<> <> <<>> DIRECTORY DicentraInputOutput, Heap, Inline, Process, ProcessorFace, OthelloDefs, RavenSlotDriver, Watchdog; <<>> RavenSlotDriverImpl: MONITOR IMPORTS DicentraInputOutput, Inline, Heap, Process, ProcessorFace, OthelloDefs, 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 <> <> 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]; <> 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 <> SendCommand[getStatus]; RETURN[ReceiveStatus[]]; END; DisplayBadStatusInMP: PUBLIC PROC[s: RavenSlotDriver.PrinterStatus] = BEGIN <> MPReport[s, mpFailure]; END; PrintPage: PUBLIC ENTRY PROC [slowMargin, fastMargin: NAT, numberOfLines, scanLineLength: NAT, paperSource: RavenSlotDriver.PaperSource, paperStacking: RavenSlotDriver.PaperStacking] RETURNS [RavenSlotDriver.SuccessCode] = BEGIN <> <> <> <<>> 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 <> <> 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 <> start: CARDINAL _ slotBufferSize-scanLineWords; DicentraInputOutput.WriteBlock[from: b, to: slot+8000H+start, words: scanLineWords]; END; SkipScanLines: PROCEDURE [lines: CARDINAL] RETURNS[BOOLEAN]= { <> 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 { OthelloDefs.WriteLine["slowMargin/numberOfLines problem"]; numberOfLines _ pageWidthInches*bpi - slowMargin; }; IF fastMargin+scanLineLength > pageLengthInches*bpi THEN { IF fastMargin > pageLengthInches*bpi THEN { OthelloDefs.WriteLine["fastMargin > pageLengthInches*bpi"]; fastMargin _ scanLineLength _ 16; numberOfLines _ 0; } ELSE { OthelloDefs.WriteLine["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 _ MAX[MIN[slowMargin, pageWidthInches*bpi], 0] + 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 => OthelloDefs.WriteLine[" - Registration jam"]; fuserJam => OthelloDefs.WriteLine[" - Fuser jam"]; warming => OthelloDefs.WriteLine[" - Warming up"]; feederFault => OthelloDefs.WriteLine[" - Out of paper"]; interlockOpen => OthelloDefs.WriteLine[" - Door open"]; fuserCold => OthelloDefs.WriteLine[" - Fuser cold"]; parityError => OthelloDefs.WriteLine[" - Parity Error"]; offLine => OthelloDefs.WriteLine[" - Offline"]; ENDCASE => OthelloDefs.WriteLine[" - 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 OthelloDefs.WriteLine[" - 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 OthelloDefs.WriteLine[" - Timeout 2"]; Process.SetPriority[p]; -- allow interrupts again RETURN[error]; }; <<*** WRITE CLIENT DATA TILL EXHAUSTED ***>> IF numberOfLines=0 THEN { OthelloDefs.WriteLine[" - Empty page!"]; } ELSE FOR i: CARDINAL IN [0..numberOfLines) DO <> HyperRead[destPtr: @dataBuffer[0], sourceOffset: hyperOffset, wordCount: scanLineWords]; XFerBufferToSlot[dataBuffer]; -- copy data back to slot buffer hyperOffset _ hyperOffset + scanLineWords; IF NOT SkipScanLines[1] THEN { OthelloDefs.WriteLine[" - 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 { OthelloDefs.WriteLine[" - 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 => { OthelloDefs.WriteLine[" - Out of paper?"]; }; ENDCASE => { code:CARDINAL _ VAL[s]; DisplayBadStatusInMP[s]; -- OthelloDefs.WriteLongNumber[code]; OthelloDefs.WriteLine[" - Page didn't complete satisfactorily"]; RETURN[error]; }; ENDLOOP; MPReport[GetStatus[], warningOrOKcode]; IF warningOrOKcode=mpWarning THEN { IF lowTonerFlag THEN OthelloDefs.WriteLine[" - Page completed (Toner Low)"]; IF outputTrayFullFlag THEN OthelloDefs.WriteLine[" - Page completed (Output tray full)"]; RETURN[warning]; } ELSE { code:CARDINAL _ VAL[s]; -- OthelloDefs.WriteLongNumber[code]; OthelloDefs.WriteLine[" - Page completed OK"]; RETURN[ok]; }; END; MustBe64KWordBoundary: PROC[i: LONG CARDINAL] RETURNS[DicentraInputOutput.IOAddress] = { <> IF i MOD K64Words # 0 THEN ERROR; RETURN [LOOPHOLE[i]]; }; HyperRead: PROC [destPtr: LONG POINTER TO WORD, sourceOffset: LONG CARDINAL, wordCount: LONG CARDINAL] = BEGIN <> 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 <> Process.Pause[Process.MsecToTicks[n]] END; MPReport: PROC[s: RavenSlotDriver.PrinterStatus, offset: CARDINAL] = BEGIN <> 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]; OthelloDefs.WriteLine["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]; OthelloDefs.WriteLine["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]; OthelloDefs.WriteLine["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]; OthelloDefs.WriteLine["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]; OthelloDefs.WriteLine["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]; OthelloDefs.WriteLine["Raven not answering"]; ENDLOOP; <<>> IF checkHyper THEN { DicentraInputOutput.SetNxmExpected[TRUE]; -- don't die on non-existant mem. <> <> StripeHyper[]; DicentraInputOutput.SetNxmExpected[FALSE]; MPReport[hyperOK, mpInitialising]; Susp[500]; OthelloDefs.WriteLine["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] => { <> }; ENDCASE; END; TestProcess: PROC[] RETURNS[] = BEGIN Process.SetPriority[Process.priorityBackground]; DO s: RavenSlotDriver.PrinterStatus ; successCode: RavenSlotDriver.SuccessCode; UNTIL TestKeyPressed[] DO ENDLOOP; OthelloDefs.WriteLine["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[]; OthelloDefs.WriteLine["Raven ready for service"]; END.