{ File name: CedarRaven.mc Description: Raven LSEP (Low Speed Electronic Printer) microcode Author: Pitts PXJ , Created: March 14, 1980, Last Edited: Julio JxC , 7-Feb-85 17:07:05 Added CopyRight notice. Neil NXS , 16-Jan-85 11:59:45 Rewrite to even out overhead between scan line involving bandswitch and scan line without bandswitch. This allows a common version of microcode to drive Ravens, Banshee and Fuji model 35s, with varying clock rates. AeF AEF , 6-Feb-84 17:37:47 Move initialization of displayBase1 to start of code. AeF AEF , 13-Oct-83 11:04:28 Change references for IOPage move. CrF CRF, 21-Oct-82 18:25:35 Rewrite vertical registration code to allow shorter delay between line sync and first data bit sent. AeF AEF , August 3, 1982 1:54 PM Change CSB Offset names AeF AEF , July 6, 1982 11:42 AM Move WakeupMaskOffset definition to Dandelion.df RXG , July 30, 1981 11:51 PM: added ClrDPRq & PCtl← after endBand so that hardware fix to allow slower LSEP's works. (Note that the click at endBand can be eliminated if the click before uses nibCarry.) PXO , March 23, 1981 5:25 PM: Remove some unneeded CANCELBR's PXJ , November 25, 1980 2:14 PM Fiala, 26-Aug-86 11:19:15 Change IOPageHigh to cedarIOPageHigh two places; added defn's for RavenRunOffset and RavenOverrunOffset to Dandelion.dfn. } { Copyright (C) 1980, 1981, 1982, 1983, 1984, 1985 by Xerox Corporation. All rights reserved. } { WARNING: this microcode is extremely timing dependent. Don't attempt to change it without fully understanding the timing constraints. Notes: 1. There is a parameterized delay loop that controls the amount of JYW video data between the rising edge of the line sync signal and the first actual video data bit (pixel) sent to the printer. This delay is adjusted by the operator to align the printed image with the paper on a particular printing device. The unit of delay at the Face level is 16 pixels. After line sync is sensed, a 16-bit counter is enabled in the DLion Raven Controller. This counter is clocked by the video data clock from the printer, and is used to cause a wakeup every 16 pixels. 2. There are six DLion clicks of overhead every time we switch from one band of 16 scan lines to the next band. The hardware wakeup register remains set after a data wakeup until the microcode executes a ClrDPRq. By not executing a ClrDPRq in the six click band overhead code, it is possible to get those six clicks to run in six consecutive rounds, taking only 2.057 microseconds each, rather than 16*printerDataRate each (for Raven each full tick of the video clock is .278 microseconds, so data wakeups happen every 4.48 microseconds) if we waited for the next data wakeup each time. There is an "Overhead" loop to equalize the band switch/non band switch time between line sync and the registartion delay loop. 3. This overhead must be accounted for as part of the registartion delay, as the Raven Spec calls for a minimum delay of 70 pixels. For Raven the ratio of rounds to data wakeups is close to 2 to 1, so the six rounds of band overhead get counted as three data wakeups, or two counts through the delay loop. This means the minimum delay for Raven is three data wakeups, or about 48 pixels, counting the delay loop exit click. For Banshee the ratio is about 7 to 2, (pixel clock is .444 microseconds, data wakeups happen every 7.104 microseconds) so minimum delay is 32 pixels. This is acceptable within the allowances of the Banshee registation switches. Due to the design of the DLion Raven Controller, the first data wakeup comes about 4 pixels after line sync, so the actual minimum delay possible is 4 pixels longer. } { Printer Control register on X-Bus ~ClearLineActive 8 zero inhibits data wakeups and forces video to all JYW until next line sync ForceRequest 4 forces printer wakeups every round ~VideoWhite 2 zero forces video to JYW EnablePrinter 1 enables printer and memory refresh task wakeups } Set[EndActiveScanCtl, 1]; Set[EndBandScanCtl, 5]; Set[StartScanCtl, 9]; Set[StartActiveCtl, 0B]; {Raven CSB (read once per page, immediately after code detects run#0) run non-zero=> index of 1st band record (displayBase0 relative), read then zeroed overrun non-zero=> over ran band buffer interruptMask Mesa process stuff bandSize number of lines in a band active scan number of words in line buffer actually sent to printer line size number of words in line buffer tab number of words skipped from line sync before sending image to printer scans number of blank lines sent to printer at start of page} {Raven band record pointer real address of band (displayBase1 relative) next index of next band record (displayBase0 relative) status 0 => empty, #0 => full, <0 => last band} SetTask[1]; StartAddress[PrinterInit]; PrinterInit: PCtl← EndActiveScanCtl, CancelDisp[$]{wakeup on next line sync} ,c1; nextPage: displayBase0 ← cedarIOPageHigh, ClrDPRq {band buffer in first 64K} ,c2; rD0 ← uIOPage ,c3; pageGap: MAR← [displayBase0, rD0 + RavenRunOffset] {wait for run # 0} ,c1; MDR← 0, CancelPgCross[$, 0], LOOPHOLE[wok] {clear immediately} ,c2; rD1← MD ,c3; uThis← rD1, ZeroBr ,c1; rD1← 0A, BRANCH[startPage, $] {rD1← AltUaddr index} ,c2; PCtl← EndActiveScanCtl, GOTO[pageGap] ,c3; startPage: Noop ,c3; {clear overrun} rD0 ← MAR ← [displayBase0, rD0 + RavenOverrunOffset] ,c1; MDR ← 0, CancelPgCross[$, 0], LOOPHOLE[wok] ,c2; displayBase1 ← 0 ,c3; loadParam: MAR← [displayBase0, rD0+1], rD0← rD0+1 {load parameters} ,c1; uScans← rD0, CancelPgCross[$] ,c2; rD0← MD ,c3; Ybus← rD1, rD1← rD1+1, NibCarryBr, AltUaddr ,c1; uyParamBase← rD0, BRANCH[$, fixUTabCnt] ,c2; rD0← uScans, GOTO[loadParam] ,c3; fixUTabCnt: rD1← uTabCnt ,c3; rD1← rD1-3 {for compatability with previous versions} ,c1; uTabCnt← rD1 ,c2; skipLines: rD0← rD0-1, ZeroBr {loaded uScans last} ,c3; BRANCH[$, firstBand] {line sync wakeup} ,c1; PCtl← EndActiveScanCtl, GOTO[skipLines] ,c2; firstBand: GOTO[thisBand] {avoid allocation constraints} ,c2; nextBand: Noop ,c2; thisBand: rD0← uThis ,c3; MAR← [displayBase0, rD0], rD0← rD0+1 {line sync wakeup} ,c1; CancelPgCross[$] ,c2; rD1← MD {pointer to band} ,c3; MAR← [displayBase0, rD0], rD0← rD0+1 ,c1; uLineAddress← rD1, CancelPgCross[$] ,c2; rD1← MD {next band} ,c3; MAR← [displayBase0, rD0], uBandStatus← rD0 ,c1; {save address for later} uThis← rD1, CancelPgCross[$] ,c2; rD1← MD {status of this band} ,c3; []← rD1, ZeroBr ,c1; rD1← uLineAddress, BRANCH[$, RavenOverrun] ,c2; rD0← uBandSize ,c3; uLineCount← rD0, GOTO[startLine1] ,c1; startLine: PCtl← EndActiveScanCtl ,c1; rD0← uLineSize ,c2; rD1← uLineAddress ,c3; rD1← rD1+rD0 {line sync wakeup} ,c1; uLineAddress← rD1 ,c2; rD0← 3 {3 clicks more overhead if band switch} ,c3; overhead: Noop ,c1; rD0← rD0-1, ZeroBr {equalize band switch overhead} ,c2; BRANCH[overhead, $] ,c3; Noop ,c1; startLine1: PCtl← StartScanCtl ,c2; rD0← uTabCnt ,c3; idle: rD0← rD0-1, ZeroBr {tab in to active area} ,c1; BRANCH[$, startActive], ClrDPRq ,c2; Noop, GOTO[idle] ,c3; startActive: PCtl← StartActiveCtl {stop forcing video JYW } ,c3; scan: MAR← [displayBase1, rD1+0], ClrDPRq ,c1; MDR← rD0{= zero}, rD1← rD1+1, PgCarryBr ,c2; POData← MD, BRANCH[scan, endLine] ,c3; endLine: rD1← uLineCount, ClrDPRq {play out last word} ,c1; rD1← rD1-1, ZeroBr ,c2; uLineCount← rD1, BRANCH[startLine, endBand] ,c3; endBand: Noop ,c1; PCtl← EndBandScanCtl ,c2; rD0← uBandStatus ,c3; MAR← [displayBase0, rD0+0] {mark band empty} ,c1; MDR← rD1 {... and get next band} ,c2; rD1← MD {negative=> last band} ,c3; rD0← uWP ,c1; rD0← uInterruptMask or rD0, MesaIntRq ,c2; uWP← rD0 {initiate band complete interrupt} ,c3; Noop ,c1; Noop ,c2; endBand1: []← rD1, NegBr {neg => last band done} ,c3; PCtl← EndActiveScanCtl, BRANCH[nextBand, nextPage] ,c1; RavenOverrun: rD1← uBandStatus ,c3; rD1← rD1-2 ,c1; uThis ← rD1 {back up pointer} ,c2; rD0← uIOPage ,c3; MAR← [displayBase0, rD0 + RavenOverrunOffset] {set overrun} ,c1; rD1← MDR ← 1, CancelPgCross[endBand1, 0], LOOPHOLE[wok] ,c2; {Memory Refresh Task} SetTask[3]; StartAddress[RefreshGo]; Set[HalfFieldSize, 0E0]; RefreshGo: uClockBits ← 0, CancelDisp[$] ,c1; displayBase2 ← cedarIOPageHigh ,c2; RefreshField: rD2 ← uIOPage ,c3; MAR ← [displayBase2, rD2 + WakeupMaskOffset] ,c1; Noop, CancelPgCross[$, 0] ,c2; rD2 ← MD ,c3; uPWakeup ← rD2 ,c1; Noop ,c2; Noop ,c3; {This code looks this way because uWP must be updated within one click.} rD2 ← uWP ,c1; rD2 ← uPWakeup or rD2, MesaIntRq ,c2; uWP ← rD2 ,c3; rD2 ← LShift1 HalfFieldSize ,c1; uRefreshLine ← rD2 ,c2; ClrRefRq ,c3; Refresh: rD2 ← uClockLow, Refresh ,c1; rD2 ← rD2 + 1, CarryBr ,c2; uRefreshTemp ← rD2, BRANCH[$, RefreshCarry] ,c3; Refresh ,c1; Noop ,c2; Refresh1: uClockLow ← rD2 ,c3; rD2 ← uRefreshLine, ZeroBr ,c1; rD2 ← rD2 - 1, BRANCH[$, RefreshField] ,c2; uRefreshLine ← rD2, ClrRefRq, GOTO[Refresh] ,c3; RefreshCarry: rD2 ← uClockHigh, Refresh ,c1; rD2 ← rD2 + 1 ,c2; Noop ,c3; uClockHigh ← rD2 ,c1; rD2 ← uRefreshTemp, GOTO[Refresh1] ,c2;