-- RavenSlotDriverImpl.mesa -- Copyright (C) 1984, Xerox Corporation. All rights reserved. -- Mik Lamming, December 9, 1983 1: 25 pm -- Michael Plass, September 14, 1984 11:01:25 am PDT -- DIRECTORY DicentraInputOutput, Heap, Inline, Process, ProcessorFace, PDQueue, RavenSlotDriver, Watchdog; -- RavenSlotDriverImpl: MONITOR IMPORTS DicentraInputOutput, Inline, Heap, Process, ProcessorFace, PDQueue, Watchdog EXPORTS RavenSlotDriver = BEGIN -- ** Dicentra hyperspace configuration constants hyperStart: DicentraInputOutput.IOAddress = MustBe64KWordBoundary[100000H]; hyperEndPlus1: DicentraInputOutput.IOAddress = LOOPHOLE[200000H]; -- ** Raven constants bpi: CARDINAL = 384; pageLengthInches: CARDINAL = 12; -- ample slop - 11 would probably do pageWidthInches: CARDINAL = 8; white: CARDINAL = 0; -- ** Slot paraphenalia slot: Slot = LOOPHOLE[LONG[4000H]]; -- controller IO address Slot: TYPE = LONG POINTER TO SlotRec; SlotRec: TYPE = RECORD [ printerCSR: CARDINAL, marginCount: CARDINAL, scanCount: CARDINAL, uARTModeOrCmd: CARDINAL, uARTData: CARDINAL]; slotBufferSize: CARDINAL = 2048; -- ** Slot command codes feedPageCommand: CARDINAL = 40H; txOffRxOnCommand: CARDINAL = 014H; resetCommand: CARDINAL = 40H; -- internal reset to go into 'Mode' mode configureUART: CARDINAL = 03BH; -- 1 stop bit, even parity, 7 bits, 64X clock rxEnable: CARDINAL = 015H; -- Error reset, Recv enable startSlot: CARDINAL = 01H; stopSlot: CARDINAL = 0; getStatus: CARDINAL = 20H; -- ** Status masks txReady: CARDINAL = 1; rxReady: CARDINAL = 2; bufferEmpty: CARDINAL = 80H; bufferSize: CARDINAL = 512; -- PowerOfTwoAtLeast[(bpi*pageLengthInches)/16]; Buffer: TYPE = LONG POINTER TO BufferRecord; BufferRecord: TYPE = ARRAY [0..bufferSize) OF WORD; dataBuffer: Buffer ← Heap.systemZone.NEW[BufferRecord]; whiteBuffer: Buffer ← Heap.systemZone.NEW[BufferRecord]; -- ** Maintenance panel codes mpInitialising: NAT = 600; initUart: RavenSlotDriver.PrinterStatus = VAL[0]; fillingHyper: RavenSlotDriver.PrinterStatus = VAL[1]; patterningHyper: RavenSlotDriver.PrinterStatus = VAL[2]; checkingHyper: RavenSlotDriver.PrinterStatus = VAL[3]; hyperOK: RavenSlotDriver.PrinterStatus = VAL[4]; errorHiDigits: RavenSlotDriver.PrinterStatus = VAL[13]; errorLoDigits: RavenSlotDriver.PrinterStatus = VAL[14]; mpOK: NAT = 700; mpWarning: NAT = 800; mpFailure: NAT = 1300; warningOrOKcode: NAT ← mpOK; -- ** Miscellaneous dross K64Words: LONG CARDINAL = LONG[64]*1024; -- *** PUBLIC PROCEDURES AND VARIABLES *** -- FallenOffTheEndOfHyperspace: PUBLIC ERROR = CODE; BadParameters: PUBLIC ERROR = CODE; -- outputTrayFullFlag: PUBLIC BOOLEAN ← FALSE; lowTonerFlag: PUBLIC BOOLEAN ← FALSE; HyperStore: PUBLIC PROC [ sourcePtr: LONG POINTER TO WORD, destOffset: LONG CARDINAL, wordCount: LONG CARDINAL] = BEGIN -- The destination area is referred to by an offset from the beginning of hyperspace. -- The microcode that implements WriteHyper has a feature that prevents it writing data across a 64K hyperspace boundary. Thats why some transfers have to be done in two parts. IF LOOPHOLE[hyperStart+destOffset+wordCount, LONG CARDINAL] > LOOPHOLE[hyperEndPlus1, LONG CARDINAL] THEN ERROR FallenOffTheEndOfHyperspace; WHILE wordCount>0 DO toNextBoundary: LONG CARDINAL ← K64Words - Inline.LowHalf[destOffset]; -- use Inline.LowHalf above because MOD on LONG CARDINAL generates bad code xferSize: CARDINAL ← CARDINAL[MIN[wordCount, toNextBoundary, K64Words-1]]; DicentraInputOutput.WriteHyper[from: sourcePtr, to: hyperStart+destOffset, words: xferSize]; sourcePtr ← sourcePtr + xferSize; destOffset ← destOffset + xferSize; wordCount ← wordCount - xferSize; ENDLOOP; END; GetStatus: PUBLIC PROC[] RETURNS[s: RavenSlotDriver.PrinterStatus] = BEGIN -- Returns the Raven status. Note the extra status code statusRQtimeout which gets returned if the Raven failed to answer the request for status. SendCommand[getStatus]; RETURN[ReceiveStatus[]]; END; DisplayBadStatusInMP: PUBLIC PROC[s: RavenSlotDriver.PrinterStatus] = BEGIN -- show the Raven status with a 1300 code MPReport[s, mpFailure]; END; PrintPage: PUBLIC ENTRY PROC [slowMargin, fastMargin: NAT, numberOfLines, scanLineLength: NAT, paperSource: RavenSlotDriver.PaperSource, paperStacking: RavenSlotDriver.PaperStacking] RETURNS [RavenSlotDriver.SuccessCode] = BEGIN -- Returns when page is complete or an unrecoverable error has occurred. -- If error use LastStatus to find out why. -- If warning investigate outputTrayFullFlag and lowTonerFlag -- deviceSlowMargin: CARDINAL = 100; deviceFastMargin: CARDINAL = 400; s: RavenSlotDriver.PrinterStatus; scanLineWords: CARDINAL; hyperOffset: LONG CARDINAL ← 0; choppedLineLength: NAT ← (scanLineLength/16)*16; -- SLOT needs multiple of 16 p: CARDINAL ← Process.GetPriority[]; -- don't get interrupted while writing FeedPage: PROC[] RETURNS[RavenSlotDriver.PrinterStatus]= BEGIN -- Feed a page and return useful status bytes: -- warming, feederFault, registrationJam, fuserJam, noExit, interlockOpen, fuserCold, parityError, illegalCharacter, illegalSequence, noPaper, pageSync, goingOffLine, offLine, statusOverRun commandByte: CARDINAL ← feedPageCommand + (IF paperSource=bottom THEN 0 ELSE 16) + (IF paperStacking=aligned THEN 0 ELSE 1); SendCommand[commandByte]; DO SELECT s ← ReceiveStatus[] FROM -- status bytes that we don't need to worry about IN [key0..keyOffLine], lowToner, -- ReceiveStatus[] has set lowTonerFlag outputTrayFull, -- ReceiveStatus[] has set lowTonerFlag displayAcknowledge, pageAtOutputTray, goingToSleep, feeding, readyToFeed => NULL; ENDCASE => EXIT; -- everything else is serious ENDLOOP; RETURN [s]; END; XFerBufferToSlot: PROC[b: Buffer] RETURNS[] = BEGIN -- Move scanLineWords into the Slot buffer in IO space using microcode loop start: CARDINAL ← slotBufferSize-scanLineWords; DicentraInputOutput.WriteBlock[from: b, to: slot+8000H+start, words: scanLineWords]; END; SkipScanLines: PROCEDURE [lines: CARDINAL] RETURNS[BOOLEAN]= { -- Wait for Slot buffer to empty lines times - Returns FALSE if a timeout occured. BufferEmpty: PROCEDURE RETURNS [BOOLEAN] = { x: CARDINAL ← DicentraInputOutput.Input[@slot.printerCSR]; RETURN[Inline.BITAND[x, bufferEmpty] # 0]; }; FOR i: CARDINAL IN [0..lines) DO count: CARDINAL ← 65000; WHILE NOT BufferEmpty[] DO count ← count - 1; IF count=0 THEN RETURN[FALSE]; ENDLOOP; ENDLOOP; RETURN[TRUE]; -- ok exit }; -- *** CHECK FOR REASONABLE PARAMETERS *** IF slowMargin+numberOfLines > pageWidthInches*bpi THEN { PDQueue.LogMessage["Raven slowMargin/numberOfLines problem"]; numberOfLines ← pageWidthInches*bpi - slowMargin; }; IF fastMargin+scanLineLength > pageLengthInches*bpi THEN { IF fastMargin > pageLengthInches*bpi THEN { PDQueue.LogMessage["Raven fastMargin > pageLengthInches*bpi"]; fastMargin ← scanLineLength ← 16; numberOfLines ← 0; } ELSE { PDQueue.LogMessage["Raven fastMargin+scanLineLength > pageLengthInches*bpi"]; choppedLineLength ← ((pageLengthInches*bpi-fastMargin)/16)*16; }; }; scanLineWords ← scanLineLength/16; -- -- *** SET UP MARGIN DIMENSIONS *** DicentraInputOutput.Output[-(fastMargin+deviceFastMargin), @slot.marginCount]; DicentraInputOutput.Output[-(choppedLineLength), @slot.scanCount]; slowMargin ← MIN[slowMargin, pageWidthInches*bpi] + deviceSlowMargin; -- add client margin to device margin -- *** START PAGE ROLLING *** IF (s ← FeedPage[]) # pageSync THEN { -- Feed a page (or tell client about problem) DisplayBadStatusInMP[s]; SELECT s FROM registrationJam => PDQueue.LogMessage["Raven - Registration jam"]; fuserJam => PDQueue.LogMessage["Raven - Fuser jam"]; warming => PDQueue.LogMessage["Raven - Warming up"]; feederFault => PDQueue.LogMessage["Raven - Out of paper"]; interlockOpen => PDQueue.LogMessage["Raven - Door open"]; fuserCold => PDQueue.LogMessage["Raven - Fuser cold"]; parityError => PDQueue.LogMessage["Raven - Parity Error"]; offLine => PDQueue.LogMessage["Raven - Offline"]; ENDCASE => PDQueue.LogMessage["Raven - Can't feed page"]; RETURN[error]; }; MPReport[s, warningOrOKcode]; -- *** WRITE WHITE INTO MARGIN *** Process.SetPriority[7]; -- don't get interrupted XFerBufferToSlot[whiteBuffer]; -- prime slot buffer A with white DicentraInputOutput.Output[startSlot, @slot.printerCSR]; -- crank up slot IF NOT SkipScanLines[1] THEN { -- wait for first buffer to go out PDQueue.LogMessage["Raven - Timeout 1"]; Process.SetPriority[p]; -- allow interrupts again RETURN[error]; }; XFerBufferToSlot[whiteBuffer]; -- prime slot buffer B with white IF NOT SkipScanLines[slowMargin-1] THEN { -- freewheel thru A and B till margin out PDQueue.LogMessage["Raven - Timeout 2"]; Process.SetPriority[p]; -- allow interrupts again RETURN[error]; }; -- *** WRITE CLIENT DATA TILL EXHAUSTED *** IF numberOfLines=0 THEN { PDQueue.LogMessage["Raven - Empty page!"]; } ELSE FOR i: CARDINAL IN [0..numberOfLines) DO -- get a scan lines worth of data from hyper to my buffer HyperRead[destPtr: @dataBuffer[0], sourceOffset: hyperOffset, wordCount: scanLineWords]; XFerBufferToSlot[dataBuffer]; -- copy data back to slot buffer hyperOffset ← hyperOffset + scanLineWords; IF NOT SkipScanLines[1] THEN { PDQueue.LogMessage["Raven - Timeout 3"]; Process.SetPriority[p]; -- allow interrupts again RETURN[error]; } ENDLOOP; -- *** FINISH OFF WITH SOME WHITE SPACE AGAIN *** XFerBufferToSlot[whiteBuffer]; IF NOT SkipScanLines[1] THEN { PDQueue.LogMessage["Raven - Timeout 4"]; Process.SetPriority[p]; -- allow interrupts again RETURN[error]; }; XFerBufferToSlot[whiteBuffer]; -- fill other buffer and freewheel to end DicentraInputOutput.Output[stopSlot, @slot.printerCSR]; -- stop slot Process.SetPriority[p]; -- allow interrupts again -- *** WAIT FOR PAGE TO LAND IN OUTPUT HOPPER *** DO s ← ReceiveStatus[]; SELECT s FROM pageAtOutputTray => EXIT; IN [key0..keyOffLine], lowToner, -- ReceiveStatus[] has set lowTonerFlag outputTrayFull, -- ReceiveStatus[] has set lowTonerFlag displayAcknowledge, goingToSleep, feeding, readyToFeed, statusRQtimeout => NULL; feederFault => { PDQueue.LogMessage["Raven - Out of paper?"]; }; ENDCASE => { code: CARDINAL ← VAL[s]; DisplayBadStatusInMP[s]; -- OthelloDefs.WriteLongNumber[code]; PDQueue.LogMessage["Raven - Page didn't complete satisfactorily"]; RETURN[error]; }; Process.Pause[Process.MsecToTicks[200]]; ENDLOOP; MPReport[GetStatus[], warningOrOKcode]; IF warningOrOKcode=mpWarning THEN { IF lowTonerFlag THEN PDQueue.LogMessage["Raven - Page completed (Toner Low)"]; IF outputTrayFullFlag THEN PDQueue.LogMessage["Raven - Page completed (Output tray full)"]; RETURN[warning]; } ELSE { code: CARDINAL ← VAL[s]; -- OthelloDefs.WriteLongNumber[code]; PDQueue.LogMessage["Raven - Page completed OK"]; RETURN[ok]; }; END; MustBe64KWordBoundary: PROC[i: LONG CARDINAL] RETURNS[DicentraInputOutput.IOAddress] = { -- Used to check configuration values at start up IF i MOD K64Words # 0 THEN ERROR; RETURN [LOOPHOLE[i]]; }; HyperRead: PROC [destPtr: LONG POINTER TO WORD, sourceOffset: LONG CARDINAL, wordCount: LONG CARDINAL] = BEGIN -- The destination area is referred to by an offset from the beginning of hyperspace. Has to cope with microcode feature which will not transfer across 64K boundaries WHILE wordCount>0 DO toNextBoundary: LONG CARDINAL ← K64Words - Inline.LowHalf[sourceOffset]; xferSize: CARDINAL ← CARDINAL[MIN[wordCount, toNextBoundary, K64Words-1]]; IF Inline.HighHalf[hyperStart+sourceOffset] # Inline.HighHalf[hyperStart+sourceOffset+xferSize-1] THEN ERROR; DicentraInputOutput.ReadHyper[from: hyperStart+sourceOffset, to: destPtr, words: xferSize]; destPtr ← destPtr + xferSize; sourceOffset ← sourceOffset + xferSize; wordCount ← wordCount - xferSize; ENDLOOP; END; Susp: PROC[n: CARDINAL] RETURNS[] = BEGIN -- Suspend for 'n' milliseconds Process.Pause[Process.MsecToTicks[n]] END; MPReport: PROC[s: RavenSlotDriver.PrinterStatus, offset: CARDINAL] = BEGIN -- Display status and status code in Dicentra MP ProcessorFace.SetMP[LOOPHOLE[s, CARDINAL]+offset]; END; MSecs1000WaitProcess: PROCESS ← NIL; SendCommand: PROCEDURE [command: CARDINAL] = { XmitReady: PROCEDURE RETURNS [BOOLEAN] = { x: CARDINAL← DicentraInputOutput.Input[@slot.uARTModeOrCmd]; RETURN[Inline.BITAND[x, txReady] # 0]; }; IF MSecs1000WaitProcess # NIL THEN [] ← JOIN MSecs1000WaitProcess; MSecs1000WaitProcess ← NIL; -- turn receiver off, transmitter on DicentraInputOutput.Output[01H, @slot.uARTModeOrCmd]; WHILE NOT XmitReady[] DO ENDLOOP; DicentraInputOutput.Output[command, @slot.uARTData]; WHILE NOT XmitReady[] DO ENDLOOP; -- turn transmitter off, receiver on DicentraInputOutput.Output[txOffRxOnCommand, @slot.uARTModeOrCmd]; MSecs1000WaitProcess ← FORK Susp[1000]; }; ReceiveStatus: PROCEDURE RETURNS [s: RavenSlotDriver.PrinterStatus] = { RecReady: PROCEDURE RETURNS [BOOLEAN] = { bits: CARDINAL ← DicentraInputOutput.Input[@slot.uARTModeOrCmd]; RETURN[Inline.BITAND[bits, rxReady] # 0]; }; timeOut: LONG CARDINAL ← 100000; WHILE NOT RecReady[] DO timeOut ← timeOut - 1; IF timeOut=0 THEN RETURN[statusRQtimeout]; ENDLOOP; s ← DicentraInputOutput.Input[@slot.uARTData]; SELECT s FROM lowToner => { lowTonerFlag ← TRUE; warningOrOKcode ← mpWarning; }; outputTrayFull => { outputTrayFullFlag ← TRUE; warningOrOKcode ← mpWarning; }; ENDCASE => { lowTonerFlag ← outputTrayFullFlag ← FALSE; warningOrOKcode ← mpOK; }; RETURN[s]; }; Init: PROC[checkHyper: BOOLEAN←TRUE] RETURNS[] = BEGIN StripeHyper: PROC = BEGIN buffer: ARRAY [0..4) OF PACKED ARRAY [0..3840) OF BOOLEAN; p: POINTER ← @buffer; offset: LONG CARDINAL ← 0; FOR i: NAT IN [0..3840) DO buffer[0][i] ← ((i MOD 38) + i / 100) > 37; ENDLOOP; buffer[1] ← buffer[2] ← buffer[3] ← buffer[0]; MPReport[fillingHyper, mpInitialising]; WHILE offset < hyperEndPlus1-hyperStart DO wordCount: CARDINAL ← CARDINAL[MIN[(hyperEndPlus1-hyperStart)-offset, 3840/4]]; HyperStore[sourcePtr: p, destOffset: offset, wordCount: wordCount]; offset ← offset + wordCount; ENDLOOP; END; FillHyper: PROC [pattern: CARDINAL] = BEGIN buffer: ARRAY [0..256) OF CARDINAL ← ALL[pattern]; offset: LONG CARDINAL ← 0; MPReport[fillingHyper, mpInitialising]; PDQueue.LogMessage["Raven Initialising memory"]; WHILE offset < hyperEndPlus1-hyperStart DO wordCount: CARDINAL ← CARDINAL[MIN[(hyperEndPlus1-hyperStart)-offset, 256]]; HyperStore[sourcePtr: @buffer[0], destOffset: offset, wordCount: wordCount]; offset ← offset + wordCount; ENDLOOP; END; PatternHyper: PROC [pattern: CARDINAL] = BEGIN buffer: ARRAY [0..99) OF CARDINAL; offset: LONG CARDINAL ← 0; mod: CARDINAL ← 0; MPReport[patterningHyper, mpInitialising]; PDQueue.LogMessage["Raven Initialising memory"]; WHILE offset < hyperEndPlus1-hyperStart DO wordCount: CARDINAL ← CARDINAL[MIN[(hyperEndPlus1-hyperStart)-offset, 99]]; FOR j: CARDINAL IN [0..wordCount) DO mod ← mod + 1; IF mod = 10 THEN {pattern ← pattern * 13 + 5; mod ← 0}; buffer[j] ← pattern; ENDLOOP; HyperStore[sourcePtr: @buffer[0], destOffset: offset, wordCount: wordCount]; offset ← offset + wordCount; ENDLOOP; END; CheckHyper: PROC [pattern: CARDINAL] = BEGIN buffer: ARRAY [0..101) OF CARDINAL; fromHyper: ARRAY [0..101) OF CARDINAL; offset: LONG CARDINAL ← 0; mod: CARDINAL ← 0; MPReport[checkingHyper, mpInitialising]; PDQueue.LogMessage["Raven Checking memory"]; WHILE offset < hyperEndPlus1-hyperStart DO wordCount: CARDINAL ← CARDINAL[MIN[(hyperEndPlus1-hyperStart)-offset, 101]]; FOR j: CARDINAL IN [0..wordCount) DO mod ← mod + 1; IF mod = 10 THEN {pattern ← pattern * 13 + 5; mod ← 0}; buffer[j] ← pattern; ENDLOOP; HyperRead[destPtr: @fromHyper[0], sourceOffset: offset, wordCount: wordCount]; FOR i: NAT IN [0..wordCount) DO IF fromHyper[i] # buffer[i] THEN DO p: CARDINAL ← Process.GetPriority[]; Process.SetPriority[7]; PDQueue.LogMessage["Raven Problem with memory"]; MPReport[errorHiDigits, mpInitialising]; THROUGH [1..100000] DO ENDLOOP; ProcessorFace.SetMP[NAT[(offset+i)/1000]]; THROUGH [1..100000] DO ENDLOOP; MPReport[errorLoDigits, mpInitialising]; THROUGH [1..100000] DO ENDLOOP; ProcessorFace.SetMP[NAT[(offset+i) MOD 1000]]; THROUGH [1..100000] DO ENDLOOP; Process.SetPriority[p]; ERROR; ENDLOOP; ENDLOOP; offset ← offset + wordCount; ENDLOOP; END; -- ***** Initialize the UART ***** -- MPReport[initUart, mpInitialising]; PDQueue.LogMessage["Raven Initialising SLOT UART"]; DO s: RavenSlotDriver.PrinterStatus; DicentraInputOutput.Output[0H, @slot.uARTModeOrCmd]; DicentraInputOutput.Output[0H, @slot.uARTModeOrCmd]; DicentraInputOutput.Output[0H, @slot.uARTModeOrCmd]; DicentraInputOutput.Output[resetCommand, @slot.uARTModeOrCmd]; DicentraInputOutput.Output[configureUART, @slot.uARTModeOrCmd]; DicentraInputOutput.Output[rxEnable, @slot.uARTModeOrCmd]; IF (s ← GetStatus[])#statusRQtimeout THEN EXIT; DisplayBadStatusInMP[s]; PDQueue.LogMessage["Raven not answering"]; ENDLOOP; -- IF checkHyper THEN { DicentraInputOutput.SetNxmExpected[TRUE]; -- don't die on non-existant mem. -- PatternHyper[103]; -- CheckHyper[103]; StripeHyper[]; DicentraInputOutput.SetNxmExpected[FALSE]; MPReport[hyperOK, mpInitialising]; Susp[500]; PDQueue.LogMessage["Raven Memory checks out OK"]; FOR i: CARDINAL IN [0..bufferSize] DO whiteBuffer[i] ← white; ENDLOOP; }; END; testCopies: CARDINAL ← 0; TestKeyPressed: ENTRY PROC[] RETURNS[yes: BOOLEAN ← FALSE] = BEGIN s: RavenSlotDriver.PrinterStatus; SELECT s ← ReceiveStatus[] FROM keyTest => yes←TRUE; IN [key0..key9] => { -- testCopies ← testCopies*10+CARDINAL[s-key0] MOD 100; }; ENDCASE; END; TestProcess: PROC[] RETURNS[] = BEGIN Process.SetPriority[Process.priorityBackground]; DO s: RavenSlotDriver.PrinterStatus ; successCode: RavenSlotDriver.SuccessCode; UNTIL TestKeyPressed[] DO ENDLOOP; PDQueue.LogMessage["Raven Printing test page"]; successCode ← PrintPage[0, 0, 3072, 3840, top, aligned]; SELECT successCode FROM warning => NULL; error => { p: CARDINAL ← Process.GetPriority[]; Process.SetPriority[7]; WHILE (s←GetStatus[])#standBy DO DisplayBadStatusInMP[s]; ENDLOOP; Process.SetPriority[p]; -- allow interrupts again }; ENDCASE => NULL; -- ok ENDLOOP; END; -- tester: PROCESS; Watchdog.Deactivate[]; Init[]; -- tester ← FORK TestProcess[]; PDQueue.LogMessage["Raven ready for service"]; END.