RavenSlotDMADriverImpl.mesa
Mik Lamming, December 2, 1983 9: 05 am
DIRECTORY
DicentraInputOutput,
Heap,
Inline,
Process,
ProcessorFace,
OthelloDefs,
RavenSlotDriver,
Watchdog;
RavenSlotDMADriverImpl: 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 [
marginCount: CARDINAL,
scanCount: CARDINAL,
uARTData: CARDINAL,
uARTModeOrCmd: CARDINAL,
printerCSR: 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;
outputTrayFullFlag: PUBLIC BOOLEANFALSE;
lowTonerFlag: PUBLIC BOOLEANFALSE;
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: CARDINALCARDINAL[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 ← scanLineLength/16;
hyperOffset: LONG CARDINAL ← 0;
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
};
p: CARDINAL ← Process.GetPriority[]; -- don't get interrupted while writing
IF numberOfLines=0 THEN {
OthelloDefs.WriteLine[" - Empty page!"];
RETURN[ok];
};
*** SET UP MARGIN DIMENSIONS ***
DicentraInputOutput.Output[-(fastMargin+deviceFastMargin), @slot.marginCount];
DicentraInputOutput.Output[-(scanLineLength), @slot.scanCount];
slowMargin ← slowMargin + 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 ***
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 {
OthelloDefs.WriteLine[" - Timeout 3"];
Process.SetPriority[p]; -- allow interrupts again
RETURN[error]; -- white into margin
}
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:CARDINALVAL[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:CARDINALVAL[s];
-- OthelloDefs.WriteLongNumber[code];
OthelloDefs.WriteLine[" - 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: CARDINALCARDINAL[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;
MSecs100WaitProcess: PROCESSNIL;
SendCommand: PROCEDURE [command: CARDINAL] = {
XmitReady: PROCEDURE RETURNS [BOOLEAN] = {
x: CARDINAL← DicentraInputOutput.Input[@slot.uARTModeOrCmd];
RETURN[Inline.BITAND[x, txReady] # 0];
};
IF MSecs100WaitProcess # NIL THEN [] ← JOIN MSecs100WaitProcess;
MSecs100WaitProcess ← 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];
MSecs100WaitProcess ← FORK Susp[100];
};
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: BOOLEANTRUE] 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: CARDINALCARDINAL[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 CARDINALALL[pattern];
offset: LONG CARDINAL ← 0;
MPReport[fillingHyper, mpInitialising];
OthelloDefs.WriteLine["Initialising memory"];
WHILE offset < hyperEndPlus1-hyperStart DO
wordCount: CARDINALCARDINAL[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: CARDINALCARDINAL[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: CARDINALCARDINAL[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.
PatternHyper[103];
CheckHyper[103];
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:BOOLEANFALSE] = 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;
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.