--last edit by Govvel on February 4, 1981 2:50 PM
--last edit by Jarvis on July 30, 1980 1:47 PM
--ast edit by Olmstead on October 31, 1980 9:07 AM
--last edit by Forrect on December 1, 1980 1:52 PM

-- To do: bury ImageCSB.lineSize into the microcode.  Re-arrange ImageCSB to make
-- fields the microcode cares about contiguous.

DIRECTORY
   DeviceCleanup USING [Item, Reason, Await],
   DLionInputOutput USING [ firstReservedPage, IOPage, ioPageRealAddrLow, SetReservedMemoryUse],
   Environment USING [PageCount, PageNumber, wordsPerPage],
   PageMap USING [Assoc, flagsClean, RealPageNumber, Value, valueVacant],
   RavenFace,
   System USING [Pulses, MicrosecondsToPulses, Microseconds, GetClockPulses],
   Utilities USING [LongPointerFromPage];
   
   RavenHeadDLion: PROGRAM
   IMPORT DLionInputOutput, DeviceCleanup, PageMap, System, Utilities
   EXPORTS RavenFace
   SHARES PageMap =
   BEGIN OPEN RavenFace;
   
   -- There is a game going on that the relative pointers "work" in relation BOTH
   -- to Physical 10000x and some virtual Base addresses.
   Index: PUBLIC TYPE -= BandRecordBase RELATIVE ORDERED POINTER TO BandRecord;
   BandRecordBase: TYPE - LONG BASE POINTER TO RECORD [UNSPECIFIED];
   Band: TYPE = BandBufferBase RELATIVE POINTER; -- real AND virtual relative pointer
   BandBufferBase: TYPE = LONG BASE POINTER TO RECORD [UNSPECIFIED];

-- raven occupies from 10x through 38x on the IOPage
imageLoc: LONG POINTER TO ImageCSB = LOOPHOLE[DLionInputOutput.IOPage + 20B]; -- FF10'x
ImageCSB: TYPE - MACHINE DEPENDENT RECORD [
run(0): Index,
overrun(1): WORD, -- zero => ok, nonzero => overrun
mask(2): CARDINAL, -- band empty wakeup mask
bandSize(3): CARDINAL, -- number of lines (pages) in band
lastStartedBand(4): Index, -- used by device cleanup to wait for printer to stop
lineSize(5): CARDINAL, -- constant = 256
tab(6): CARDINAL, -- words of pixels to skip before starting scan
scans(7): CARDINAL]; -- number of blank lines after page sync

BandRecord: TYPE = MACHINE DEPENDENT RECORD [
band(0): Band, next(1): Index, status(2): INTEGER];
firstBandRecord: Index =
LOOPHOLE [DLionInputOutput.ioPageRealAddrLow + 20B + SIZE[ImageCSB]];
bandRecordLimit: Index = firstBandRecord + MaxBands*SIZE[BandRecord];
bandRecordBase: BandRecordBase =
LOOPHOLE[imageLoc + SIZE[ImageCSB] - LOOPHOLE[firstBandRecord, CARDINAL]];

controlLoc: LONG POINTER TO CCSB = LOOPHOLE[DLionInputOutput.IOPage + 60B]; -- FF30'x
CCSB: TYPE = MACHINE DEPENDENT RECORD [
unused(0): WORD, mask(1): WORD, data(2): WORD, status(3): WORD];

statusLoc: LONG POINTER TO SCSB = LOOPHOLE[DLionInputOutput.IOPage + 64B]; -- FF34'x
SCSB: TYPE = MACHINE DEPENDENT RECORD [
overrun(0): WORD, mask(1): WORD, data(2): WORD, status(3): CARDINAL]; -- actually PrinterStatus

pagesCurrentlyInBands: Environment.PageCount ← 0;
bandBufferBase: BandBufferBase;
bandBufferBasePage: Environment.PageNumber;
nullIndex: Index = LOOPHOLE[0];

IthIndex: PROC [i:CARDINAL [0..MaxBands)] RETURNS [Index] = {
RETURN[firstBandRecord + (i*SIZE[BandRecord])]};

AllocateBands: PUBLIC PROC [
bandVirtualPageNumber: Environment.PageNumber, nBands: (0..MaxBands],
sizeEachBand: Environment.PageCount] =
BEGIN
bandBufferBase ← Utilities.LongPointerFromPage[
bandVirtualPageNumber - DLionInputOutput.firstReservedPage];
bandBufferBasePage ← bandVirtualPageNumber;
pagesCurrentlyInBands ← sizeEachBand*nBands;
DLionInputOutput.SetReservedMemoryUse[Raven, pagesCurrentlyInBands];
FOR i: CARDINAL IN [0..pagesCurrentlyInBands) DO
PageMap.Assoc[
bandVirtualPageNumber + i, [
FALSE, PageMap.flagsClean, DLionInputOutput.firstReservedPgae + i]];
LOOPHOLE[Utilities.LongPointerFromPage[bandVirtualPageNumber + i], LONG
POINTER TO ARRAY [0..Environment.wordsPerPage) OF WORD]↑← ALL[0];
ENDLOOP;
FOR i: CARDINAL IN [0..nBands) DO
bandRecordBase[IthIndex[i]] ← [
band:
LOOPHOLE[(DLionInputOutput.firstReservedPage + 
i*sizeEachBand)*Environment.wordsPerPage],
next: IthIndex[(i+1) MOD nBands], status: 0];
ENDLOOP;
imageLoc.bandSize ← sizeEachBand;
imageLoc.lastStartedBand ← nullIndex;
imageLoc.lineSize ← Environment.wordsPerPage;
END;

SetInteruptMasks: PUBLIC PROC [control, status, data: WORD] = {
controlLoc.mask ← control; statusLoc.mask ← status; imageLoc.mask ← data};

DeallocateBands: PUBLIC PROC =
BEGIN
imageLoc.lastStartedBand ← nullIndex;
FOR i: CARDINAL IN [0..pagesCurrentlyInBands) DO
PageMap.Assoc[bandBufferBasePage + i, PageMap.valueVacant] ENDLOOP;
pagesCurrentlyInBands ← 0;
DLionInputOutput.SetreservedMemoryUse[notBusy];
END;

Display: PUBLIC PROC [chat: CionsoleCharacter] = {
Send[60B + LOOPHOLE[char, WORD]]};

Feed: PUBLIC PROC [paperSource: PaperSource, paperStacking: PaperStacking] =
BEGIN
Send[
100B + (IF paperSource = botton THEN 0 ELSE 20B) +
(IF paperStacking = aligned THEN 0 ELSE 1)]
END;

SolicitStatus: PUBLIC PROC RETURNS [s: PrinterStatus] =
BEGIN
If statusLoc.overrun # 0 THEN {
statusLoc.status ← 0; statusLoc.overrun ← 0; s ← statusOverRun}
ELSE
IF statusLoc.status # 0 then {
s ← LOOPHOLE[Inline.BITAND[statusLoc.data, 177B]]; statusLoc.status ← 0}
ELSE s ← noStatus;
END;

SetScanLineLength: PUBLIC PROC [
activeWordsEachScanLine: [0..Environment.wordsPerPage]] =
BEGIN
offset: [0..Environment.wordsPerPage) =
Environments.wordsPerPage - activeWordsEachScanLine;
FOR i: Index ← firstBandRecord, i + SIZE[BandRecord] WHILE i < bandRecordLimit
DO
pageOffset: TYPE = MACHINE DEPENDENT RECORD [
page(0:0..7): [0..377B), offset(0:8..15): [0..377B)];
LOOPHOLE[bandRecordBase[i].band, pageOffset].offset ← offset;
ENDLOOP;
END;

SetPageOffsets: PUBLIC PROC [linesFromLeft, wordTabFromBottom: CARDINAL] = {
imageLoc.scans ← linesFromLeft; imageLoc.tab ← wordTabFromBottom};

ResetBands: PUBLIC PROC RETURNS [Index, LONG POINTER] =
BEGIN
imageLoc.lastStartedBand ← null Index;
FOR i: Index ← firstBandRecord, i + SIZE[BandRecord] WHILE i < bandRecordLimit
DO bandRecordBase[i].status ← 0 ENDLOOP;
RETURN[fistBandRecord, @bandBufferBase[bandRecordBase[firstBandRecord].band]]
END;

StartImage: PUBLIC PROC [band: Index] = {
imageLoc.overrun ← 0; imageLoc.run ← band};

BandOverrun: PUBLIC PROC RETURNS [BOOLEAN] = {RETURN[imageLoc.overrun # 0]};

AdvanceBand: PUBLIC PROC [currentBand: Index] RETURNS [Index, LONG POINTER] =
BEGIN
b: LONG POINTER TO BandRecord = @bandRecordBase[currentBand];
b.status ← LAST[INTEGER];
imageLoc.lastStartedBand ← currentBand;
RETURN[b.next, @bandBufferBase[bandRecordBase[b.next].band]]
END;

BAndFull: PUBLIC PROC [band: Index] RETURNS [BOOLEAN] = {
RETURN[bandRecordBase[band].status # 0]};

LastBand: PUBLIC PROC [band: Index] = {
bandRecordBase[band].status ← -1; imageLoc.lastStartedBand ← band};

-- send off a control word
Send: PROC [w: WORD] = 
BEGIN
UNTIL controlLoc.status = 0 DO --GORP??-- ENDLOOP;
controlLoc.data ← w;
controlLoc.status ← LAST[WORD]
END;

InitializeCleanUp: PUBLIC PROC =
BEGIn
item: DeviceCleanup.item;
saveCSB; ImageCSB;
maxWaitTime: System.Microseconds = LONG[27500]*(2*MaxBands);
waitTime: System.Pulses = System.MicrosecondsToPulses[m: maxWaitTime];

DO
reason: DeviceCleanup.Reason = DeviceCleanup.Await[@item];
SELECT reason FROM
turnOff =>
BEGIN
then: System.Pulses = System.GetclockPulses[];
If imageLoc.lastStartedBand # nullIndex THEN
WHILE (System.GetClockPulses[] - then) < waitTime
AND bandRecordBase[imageLoc.lastStartedBand].status # 0 DO ENDLOOP;
saveCSB ← imageLoc↑;
END;
turnOn => --restore csb; client will have to redo page.
{saveCSB.lastStartedBand ← nullIndex; imageLoc ↑ ← saveCSB};
disconnect =>
FOR i: CARDINAL IN [0..pagesCurrentlyInBands) DO
PageMap.Assoc[bandBufferBasePage + i, PageMap.valueVacant] ENDLOOP;
ENDCASE;
ENDLOOP;
END;

END...