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 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 ← 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: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] =
{
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]]
MPReport:
PROC[s: RavenSlotDriver
.PrinterStatus, offset:
CARDINAL] =
BEGIN
Display status and status code in Dicentra MP
ProcessorFace.SetMP[LOOPHOLE[s, CARDINAL]+offset];
END;
MSecs100WaitProcess: PROCESS ← NIL;
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:
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.
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:
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;
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.