EikonixImpl.mesa
Copyright (C) 1985, 1986, Xerox Corporation. All rights reserved.
Last edited by Tim Diebert: May 14, 1986 11:22:50 am PDT
Hal Murray, June 6, 1986 0:43:03 am PDT
DIRECTORY
Basics USING [BITAND, BITSHIFT, BytePair],
Eikonix,
EikonixProtocol USING [BitsPerPoint, Color, Command, EikonixCommand, Height, Width, SampleLength, cedarScannerSocket],
IO USING [Close, GetChar, Error, Flush, PutFR, rope, PutChar, STREAM],
LupineRuntime USING [BindingError],
Multibus USING [Block, RawAddress, RawRead, RawReadBlock, RawWrite],
MultibusRpcControl USING [ImportInterface, UnimportInterface],
Process USING [Yield],
Pup USING [Address, Socket],
PupStream USING [Create, StreamClosing, CloseReason, -- Timeout, -- waitForever],
PupName USING [Error, NameLookup],
Real USING [RoundC],
Rope USING [ROPE]
;
EikonixImpl: CEDAR PROGRAM
IMPORTS Basics, IO, LupineRuntime, Multibus, MultibusRpcControl, Process, PupName, PupStream, Real
EXPORTS Eikonix
~ BEGIN OPEN Eikonix, EikonixProtocol;
ROPE: TYPE ~ Rope.ROPE;
STREAM: TYPE ~ IO.STREAM;
GainFactor: BYTE12 = 0800H;
DefaultServer: ROPE ← "York";
EikonixError: PUBLIC ERROR [ec: ROPE] = CODE;
Open: PUBLIC PROC [serverName: ROPE] RETURNS [h: Eikonix.Handle] = BEGIN
ENABLE PupStream.StreamClosing => MakePupStreamError [h, why, text];
socket: Pup.Socket ← LOOPHOLE[EikonixProtocol.cedarScannerSocket];
commPortAddress: Pup.Address ← PupName.NameLookup[serverName, socket
! PupName.Error => ERROR EikonixError[ec: text]];
h ← NEW [HandleRecord];
h.serverName ← serverName;
h.stream ← PupStream.Create[commPortAddress, PupStream.waitForever, PupStream.waitForever];
MultibusRpcControl.ImportInterface[interfaceName: [NIL, serverName]
! LupineRuntime.BindingError => CONTINUE];
h.open ← TRUE;
END;
Close: PUBLIC PROC [h: Eikonix.Handle] = BEGIN
IF h = NIL OR NOT h.open THEN RETURN;
TurnOffLights[h ! EikonixError => CONTINUE];
h.stream.Close[ ! PupStream.StreamClosing, IO.Error => CONTINUE];
MultibusRpcControl.UnimportInterface[ ! LupineRuntime.BindingError => CONTINUE];
h.open ← FALSE;
END;
SelectWheel: PUBLIC PROC[h: Eikonix.Handle, color: EikonixProtocol.Color] = BEGIN
ENABLE PupStream.StreamClosing => MakePupStreamError [h, why, text];
SendCommand[h, SetFilter];
PutWord[h, LOOPHOLE [color]];
h.stream.Flush[];
IF GetResponse[h] # SetFilter THEN ERROR EikonixError["Bad return command."];
END;
dc, g: NBuffer ← NIL;
LoadNormalizer: PUBLIC PROC [h: Handle, darkCurrent: NBuffer, gain: NBuffer] = BEGIN
ENABLE PupStream.StreamClosing => MakePupStreamError [h, why, text];
SendCommand[h, LoadNormalizer];
FOR i: CARDINAL IN [0 .. EikonixProtocol.Width) DO
PutWord[h, Basics.BITSHIFT[darkCurrent[i], 4]];
ENDLOOP;
FOR i: CARDINAL IN [0 .. EikonixProtocol.Width) DO
PutWord[h, Basics.BITSHIFT[gain[i], 4]];
ENDLOOP;
h.stream.Flush[];
dc ← darkCurrent; g ← gain;
IF GetResponse[h] # LoadNormalizer THEN ERROR EikonixError["Bad return command."];
END;
LineConsumerProc: TYPE ~
PROC [scanNumber: LineIndex, b: Buffer, clientData: REF ANYNIL] RETURNS [Buffer];
Buffer: TYPE ~ REF BufferRecord;
BufferRecord: TYPE ~ RECORD [pixels: SEQUENCE cnt: CARDINAL OF BYTE12];
SampleScan: PUBLIC PROC
[h: Handle, b: Buffer, p: LineConsumerProc, bits: EikonixProtocol.BitsPerPoint ← bp8,
clientData: REF ANYNIL] = BEGIN
ENABLE PupStream.StreamClosing => MakePupStreamError [h, why, text];
scanCount: LineCount ~ EikonixProtocol.Height/EikonixProtocol.SampleLength;
pixelCount: PixelCount ~ EikonixProtocol.Width/EikonixProtocol.SampleLength;
IF NOT CheckOnLine[h] THEN
{Close[h ! EikonixError => CONTINUE]; ERROR EikonixError["Scanner not online."]};
SendCommand[h, SampleScan];
PutWord[h, LOOPHOLE [bits]];
h.stream.Flush[];
IF GetResponse[h] # SampleScan THEN ERROR EikonixError["Bad return command."];
IF b.cnt < pixelCount THEN b ← NEW [BufferRecord[pixelCount]];
FOR i: CARDINAL IN [0 .. scanCount) DO
pix: LineIndex ←
IF i * EikonixProtocol.SampleLength < EikonixProtocol.Height
THEN i * EikonixProtocol.SampleLength ELSE EikonixProtocol.Height - 1;
CollectSampleLine[h, b, pixelCount, bits];
b ← p[pix, b, clientData];
ENDLOOP;
END;
CollectSampleLine: PROC [h: Handle, b: Buffer, pixelCount: PixelCount,
bits: EikonixProtocol.BitsPerPoint ← bp8] = BEGIN
w: WORD;
IF b.cnt < pixelCount THEN b ← NEW [BufferRecord[pixelCount]];
IF bits = bp12
THEN FOR i: PixelCount IN [0 .. pixelCount) DO
w ← GetWord[h];
b[i] ← LOOPHOLE[Basics.BITAND[w, 0FFFH]];
ENDLOOP
ELSE FOR i: PixelCount IN [0 .. pixelCount) DO
b[i] ← LOOPHOLE[h.stream.GetChar[]];
ENDLOOP;
END;
Scan: PUBLIC PROC [h: Handle, lineStart: LineIndex, pixelStart: PixelIndex,
lineCount: LineCount, pixelCount: PixelCount, b: Buffer, p: LineConsumerProc,
bits: EikonixProtocol.BitsPerPoint ← bp8, clientData: REF ANYNIL] = BEGIN
ENABLE PupStream.StreamClosing => MakePupStreamError [h, why, text];
IF NOT CheckOnLine[h] THEN
{Close[h ! EikonixError => CONTINUE]; ERROR EikonixError["Scanner not online."]};
IF b.cnt < pixelCount THEN b ← NEW [BufferRecord[pixelCount]];
SendCommand[h, ScanPage];
PutWord[h, lineStart];
PutWord[h, pixelStart];
PutWord[h, lineCount];
PutWord[h, pixelCount];
PutWord[h, LOOPHOLE [bits]];
h.stream.Flush[];
IF GetResponse[h] # ScanPage THEN ERROR EikonixError["Bad return command."];
IF lineCount # GetWord[h] THEN ERROR EikonixError["Bad line count."];
IF pixelCount # GetWord[h] THEN ERROR EikonixError["Bad pixel count."];
FOR i: CARDINAL IN [lineStart .. lineStart + lineCount) DO
CollectLine[h, b, pixelCount, bits, pixelStart];
b ← p[i - lineStart, b, clientData];
ENDLOOP;
END;
normalize: BOOLFALSE;
CollectLine: PROC [h: Handle, b: Buffer, pixelCount: PixelCount,
bits: EikonixProtocol.BitsPerPoint ← bp8, pixelStart: CARDINAL] = TRUSTED BEGIN
st: IO.STREAM = h.stream;
p: LONG POINTER TO BYTE12;
odd: BOOL;
pc: PixelCount ← IF (odd ← pixelCount MOD 2 # 0) THEN pixelCount - 1 ELSE pixelCount;
IF b.cnt < pixelCount THEN b ← NEW [BufferRecord[pixelCount]];
p ← @b[0];
IF bits = bp12
THEN FOR i: PixelCount IN [0 .. pixelCount) DO
IF normalize
THEN {
buf: LONG CARDINAL ← Basics.BITAND [GetByteSwapWord[h], 0FFFH];
buf2: LONG CARDINAL;
IF buf < dc[i+pixelStart]
THEN buf2 ← 0
ELSE buf2 ← (buf - dc[i+pixelStart]) * LONG[g[i+pixelStart]];
buf2 ← buf2/2048;
IF buf2 > 4095 THEN buf2 ← 4095;
p^ ← buf2}
ELSE p^ ← Basics.BITAND[GetByteSwapWord[h], 0FFFH];
-- I am not sure why but the data is always in lo pos.
p ← p + SIZE[BYTE12];
ENDLOOP
ELSE BEGIN
FOR i: PixelCount IN [0 .. pc) DO
ch: CHAR;
IF i MOD 2 = 0
THEN BEGIN
ch ← IO.GetChar[st];
p^ ← LOOPHOLE[IO.GetChar[st], BYTE12];
END
ELSE p^ ← LOOPHOLE[ch, BYTE12];
p ← p + SIZE[BYTE12];
ENDLOOP;
IF odd THEN p^ ← LOOPHOLE[IO.GetChar[st], BYTE12];
END;
ELSE BEGIN
FOR i: PixelCount IN [0 .. pixelCount) DO
p^ ← LOOPHOLE[IO.GetChar[st], BYTE12];
p ← p + SIZE[BYTE12];
ENDLOOP;
END;
END;
ScanForMinMax: PUBLIC PROC [h: Handle, lineStart: LineIndex, pixelStart: PixelIndex,
lineCount: LineCount, pixelCount: PixelCount] RETURNS [min: BYTE12, minScan: LineIndex, minPixel: PixelIndex, max: BYTE12, maxScan: LineIndex, maxPixel: PixelIndex] = BEGIN
ENABLE PupStream.StreamClosing => MakePupStreamError [h, why, text];
IF NOT CheckOnLine[h] THEN
{Close[h ! EikonixError => CONTINUE]; ERROR EikonixError["Scanner not online."]};
SendCommand[h, ScanMaxMin];
PutWord[h, lineStart];
PutWord[h, pixelStart];
PutWord[h, lineCount];
PutWord[h, pixelCount];
h.stream.Flush[];
IF GetResponse[h] # ScanMaxMin THEN ERROR EikonixError["Bad return command."];
min ← GetWord[h];
minScan ← GetWord[h];
minPixel ← GetWord[h];
max ← GetWord[h];
maxScan ← GetWord[h];
maxPixel ← GetWord[h];
END;
bitsForLow: [0..0FFFh] ← 500H;
bitsForHi: [0..0FFFh] ← 000H;
reBit: [0..1] ← 1;
SetIntegrationTime: PUBLIC PROC[h: Eikonix.Handle, t: IntegrationTime,
bits: EikonixProtocol.BitsPerPoint ← bp8] = BEGIN
ENABLE PupStream.StreamClosing => MakePupStreamError [h, why, text];
bytes: Basics.BytePair;
cmd: EikonixProtocol.EikonixCommand;
i: INTEGERLOOPHOLE[t];
i ← 0 - i;
bytes ← LOOPHOLE[i];
WriteFCR[0100h];
cmd ← [reg: ITimeLow, bits: Basics.BITOR[bitsForLow, bytes.low]];
SetDeviceCommand[h, cmd];
cmd ← [reg: ITimeHigh, readBack: 1, bits: bytes.high];
cmd ←IF bits = bp8
THEN [reg: ITimeHigh, readBack: 1, bits: bytes.high]
ELSE [reg: ITimeHigh, readBack: 0, bits: Basics.BITOR[400H, bytes.high]];
SetDeviceCommand[h, cmd];
WriteFCR[0100h];
UNTIL Basics.BITAND[ReadGeneralStatus[], 8000h] = 0 DO
Process.Yield[];
ENDLOOP;
END;
GetIntegrationTime: PUBLIC PROC[h: Eikonix.Handle] RETURNS[t: IntegrationTime] = BEGIN
ENABLE PupStream.StreamClosing => MakePupStreamError [h, why, text];
cmd: EikonixProtocol.EikonixCommand ← [reg: ITimeLow, readBack: 1];
i: INTEGER; w: WORD;
SetDeviceCommand[h, cmd];
w ← GetStatus[h];
UNTIL Basics.BITAND[w, 2] = 2 DO Process.Yield[]; w ← GetStatus[h]; ENDLOOP;
i ← LOOPHOLE[GetDeviceStatus[h]];
i ← 0 - i;
t ← LOOPHOLE[i];
END;
TurnOnLights: PUBLIC PROC [h: Eikonix.Handle] = BEGIN
ENABLE PupStream.StreamClosing => MakePupStreamError [h, why, text];
SendCommand[h, TurnOnLights];
h.stream.Flush[];
IF GetResponse[h] # TurnOnLights THEN ERROR EikonixError["Bad return command."];
END;
TurnOffLights: PUBLIC PROC [h: Eikonix.Handle] = BEGIN
ENABLE PupStream.StreamClosing => MakePupStreamError [h, why, text];
SendCommand[h, TurnOffLights];
h.stream.Flush[];
IF GetResponse[h] # TurnOffLights THEN ERROR EikonixError["Bad return command."];
END;
CheckOnLine: PUBLIC PROC [h: Eikonix.Handle] RETURNS [BOOL] = BEGIN
w: WORD;
SetDeviceCommand[h, [reg: Console, readBack: 1, bits: 0]];
w ← GetStatus[h];
UNTIL Basics.BITAND[w, 2] = 2 DO Process.Yield[]; w ← GetStatus[h]; ENDLOOP;
w ← GetDeviceStatus[h];
RETURN [Basics.BITAND[w, 20H] = 20H];
END;
CalibrateDarkCurrent: PUBLIC PROC
[server: ROPENIL, t: IntegrationTime] RETURNS [dc: Eikonix.NBuffer] = BEGIN
dcScans: CARDINAL = EikonixProtocol.Height;
h: Handle ← Open [(IF server = NIL THEN (server ← DefaultServer) ELSE server)];
gain: Eikonix.NBuffer ← NEW[Eikonix.NBufferRecord];
dc ← NEW[Eikonix.NBufferRecord];
dc^ ← ALL[0];
gain^ ← ALL[GainFactor];
IF NOT CheckOnLine[h] THEN
{Close[h ! EikonixError => CONTINUE]; ERROR EikonixError["Scanner not online."]};
SelectWheel[h, Opaque];
SetIntegrationTime[h, t];
LoadNormalizer[h, dc, gain];
dc ← GetCal[h, dc];
LoadNormalizer[h, dc, gain];
Close[h ! EikonixError => CONTINUE];
END;
GetCal: PUBLIC PROC
[h: Handle, oldBuf: Eikonix.NBuffer ← NIL] RETURNS [buf: Eikonix.NBuffer] = BEGIN
ENABLE PupStream.StreamClosing => MakePupStreamError [h, why, text];
b: Buffer ← NEW [BufferRecord[EikonixProtocol.Width]];
array: REF ARRAY [0 .. 2048) OF INTNEW[ARRAY [0 .. 2048) OF INT];
proc: LineConsumerProc = BEGIN
PROC [scanNumber: LineIndex, b: Buffer, clientData: REF ANYNIL] RETURNS [Buffer];
FOR i: CARDINAL IN [0 .. 2048) DO
array^[i] ← array^[i] + b[i];
ENDLOOP;
Process.Yield[];
RETURN [b];
END;
array^ ← ALL[0];
buf ← IF oldBuf = NIL THEN NEW [Eikonix.NBufferRecord] ELSE oldBuf;
Scan[h, 512, 0, 1024, 2048, b, proc, bp12];
FOR i: CARDINAL IN [0 .. 2048) DO
buf[i] ← array[i] / 1024;
ENDLOOP;
END;
GetCal: PUBLIC PROC
[h: Handle, oldBuf: Eikonix.NBuffer ← NIL] RETURNS [buf: Eikonix.NBuffer] = BEGIN
ENABLE PupStream.StreamClosing => MakePupStreamError [h, why, text];
buf ← IF oldBuf = NIL THEN NEW [Eikonix.NBufferRecord] ELSE oldBuf;
SendCommand[h, ComputeCal];
h.stream.Flush[];
IF GetResponse[h ! PupStream.Timeout => RETRY] # ComputeCal THEN ERROR EikonixError["Bad return command."];
FOR i: CARDINAL IN [0 .. EikonixProtocol.Width) DO
buf[i] ← GetWord[h ! PupStream.Timeout => RETRY];
ENDLOOP;
END;
CalibrateLightsOn: PUBLIC PROC
[server: ROPENIL, t: IntegrationTime, color: EikonixProtocol.Color ← Clear,
dc: Eikonix.NBuffer] RETURNS [gain: Eikonix.NBuffer] = BEGIN
h: Handle ← Open [(IF server = NIL THEN (server ← DefaultServer) ELSE server)];
buffer: REF ARRAY PixelIndex OF REALNEW [ARRAY PixelIndex OF REALALL[0.0]];
max: REAL ← 0.0;
gain ← NEW[Eikonix.NBufferRecord];
gain^ ← ALL[GainFactor];
IF NOT CheckOnLine[h] THEN
{Close[h ! EikonixError => CONTINUE]; ERROR EikonixError["Scanner not online."]};
LoadNormalizer[h, dc, gain];
SelectWheel[h, color];
SetIntegrationTime[h, t];
TurnOnLights[h];
gain ← GetCal[h, gain];
TurnOffLights[h];
FOR i: CARDINAL IN [0 .. EikonixProtocol.Width) DO
buffer[i] ← gain[i];
ENDLOOP;
Buffer at this point contains the Nk' for all ks.
max ← 0.0;
FOR i: CARDINAL IN [0 .. EikonixProtocol.Width) DO
max ← MAX [max, (4095.0 * (buffer[i]/(4095.0 - dc[i])))];
ENDLOOP;
FOR i: CARDINAL IN [0 .. EikonixProtocol.Width) DO
mumble: REAL ← max/buffer[i]*800H;
IF mumble < 0.0 OR mumble > 4095.0 THEN mumble ← 4095.0;
gain[i] ← Real.RoundC[mumble];
ENDLOOP;
LoadNormalizer[h, dc, gain];
Close[h ! EikonixError => CONTINUE];
END;
Calibrate: PUBLIC PROC
[server: ROPENIL, t: IntegrationTime, color: EikonixProtocol.Color ← Clear]
RETURNS [dc, gain: Eikonix.NBuffer] = BEGIN
dc ← CalibrateDarkCurrent[server, t];
gain ← CalibrateLightsOn[server, t, color, dc];
END;
ComputeCorrectedCalibrate: PUBLIC PROC [dc, gain: Eikonix.NBuffer, min, max: BYTE12]
RETURNS [newDc, newGain: Eikonix.NBuffer] = BEGIN
minReal: REAL ← min;
maxReal: REAL ← max;
newDc ← NEW[Eikonix.NBufferRecord];
newGain ← NEW[Eikonix.NBufferRecord];
newDc^ ← ALL[0];
newGain^ ← ALL[GainFactor];
FOR i: CARDINAL IN PixelIndex DO
v: REAL ← maxReal/(gain[i]/2048.0) + dc[i]; -- 800H
ir: REAL;
newDc[i] ← Real.RoundC[minReal/(gain[i]/2048.0) + dc[i]]; -- 800H
ir ← (4095.0/(v - newDc[i])) * 800H;
IF ir > 4095.0 THEN ERROR EikonixError["Gain greater than 2 needed"];
newGain[i] ← Real.RoundC[ir];
ENDLOOP;
END;
Internal Procs
SendCommand: PROC [h: Eikonix.Handle, cmd: EikonixProtocol.Command] = BEGIN
h.stream.PutChar['\000];
h.stream.PutChar[LOOPHOLE[cmd]];
END;
GetResponse: PROC [h: Eikonix.Handle] RETURNS [EikonixProtocol.Command] = BEGIN
[] ← h.stream.GetChar[];
RETURN [LOOPHOLE[h.stream.GetChar[], EikonixProtocol.Command]];
END;
SetCommand: PROC [h: Eikonix.Handle, cmd: WORD] = BEGIN
ENABLE PupStream.StreamClosing => MakePupStreamError [h, why, text];
SendCommand[h, SetCommandReg];
PutWord[h, LOOPHOLE [cmd]];
h.stream.Flush[];
IF GetResponse[h] # SetCommandReg THEN
ERROR EikonixError["Bad return command."];
END;
SetDeviceCommand: PROC [h: Eikonix.Handle, cmd: EikonixCommand] = BEGIN
ENABLE PupStream.StreamClosing => MakePupStreamError [h, why, text];
SendCommand[h, SetDeviceCommandReg];
PutWord[h, LOOPHOLE [cmd]];
h.stream.Flush[];
IF GetResponse[h] # SetDeviceCommandReg THEN
ERROR EikonixError["Bad return command."];
END;
GetStatus: PROC [h: Eikonix.Handle] RETURNS [WORD] = BEGIN
ENABLE PupStream.StreamClosing => MakePupStreamError [h, why, text];
SendCommand[h, GetStatus];
h.stream.Flush[];
IF GetResponse[h] # GetStatus THEN ERROR EikonixError["Bad return command."];
RETURN [GetWord[h]];
END;
GetDeviceStatus: PROC [h: Eikonix.Handle] RETURNS [WORD] = BEGIN
ENABLE PupStream.StreamClosing => MakePupStreamError [h, why, text];
SendCommand[h, GetDeviceStatus];
h.stream.Flush[];
IF GetResponse[h] # GetDeviceStatus THEN ERROR EikonixError["Bad return command."];
RETURN [GetWord[h]];
END;
SetFCR: PROC [h: Eikonix.Handle, cmd: WORD] = BEGIN
ENABLE PupStream.StreamClosing => MakePupStreamError [h, why, text];
SendCommand[h, SetFCR];
PutWord[h, LOOPHOLE [cmd]];
h.stream.Flush[];
IF GetResponse[h] # SetFCR THEN
ERROR EikonixError["Bad return command."];
END;
SetDMAData: PROC [h: Eikonix.Handle, word: WORD] = BEGIN
ENABLE PupStream.StreamClosing => MakePupStreamError [h, why, text];
SendCommand[h, SetDMAData];
PutWord[h, LOOPHOLE [word]];
h.stream.Flush[];
IF GetResponse[h] # SetDMAData THEN
ERROR EikonixError["Bad return command."];
END;
PutWord: PROC [h: Eikonix.Handle, word: WORD] = BEGIN
md: MACHINE DEPENDENT RECORD [ a, b: CHAR] ← LOOPHOLE [word];
h.stream.PutChar[md.a];
h.stream.PutChar[md.b];
RETURN;
END;
GetWord: PROC [h: Eikonix.Handle] RETURNS [WORD] = BEGIN
md: MACHINE DEPENDENT RECORD [ a, b: CHAR];
md.a ← h.stream.GetChar[];
md.b ← h.stream.GetChar[];
RETURN [LOOPHOLE[md, WORD]];
END;
GetByteSwapWord: PROC [h: Eikonix.Handle] RETURNS [WORD] = BEGIN
md: MACHINE DEPENDENT RECORD [ a, b: CHAR];
md.b ← h.stream.GetChar[];
md.a ← h.stream.GetChar[];
RETURN [LOOPHOLE[md, WORD]];
END;
MakePupStreamError: PROC
[ h: Eikonix.Handle, why: PupStream.CloseReason, text: ROPE] = BEGIN
codeRope: ROPEIO.PutFR["Error from %g; %s ",
IO.rope[h.serverName], IO.rope[SELECT why FROM
localClose => "Local Close",
localAbort => "Local Abort",
remoteClose => "Remote Close",
noRouteToNetwork => "No route",
transmissionTimeout => "Timeout",
ENDCASE => text]];
ERROR EikonixError[ec: codeRope];
END;
ReadGeneralStatus: PUBLIC PROC [] RETURNS [WORD] = TRUSTED BEGIN
RETURN [Read[LOOPHOLE[0400000H+(0E004H / 2)]]];
END;
ReadDeviceStatus: PUBLIC PROC [] RETURNS [WORD] = TRUSTED BEGIN
RETURN [Read[LOOPHOLE[0400000H+(0E006H / 2)]]];
END;
WriteGeneralCommand: PUBLIC PROC [word: WORD] = TRUSTED BEGIN
Write[LOOPHOLE[0400000H+(0E000H / 2)], word];
END;
WriteDeviceCommand: PUBLIC PROC [word: WORD] = TRUSTED BEGIN
Write[LOOPHOLE[0400000H+(0E002H / 2)], word];
END;
WriteFCR: PUBLIC PROC [word: WORD] = TRUSTED BEGIN
Write[LOOPHOLE[0400000H+(0E00EH / 2)], word];
END;
Read: PUBLIC PROC [add: Multibus.RawAddress] RETURNS [word: WORD] = TRUSTED BEGIN
foo: Basics.BytePair ← Multibus.RawRead[add];
bar: Basics.BytePair;
bar.high ← foo.low; bar.low ← foo.high;
RETURN [LOOPHOLE[bar]];
END;
LineRec: TYPE ~ RECORD[SEQUENCE cnt: CARDINAL OF WORD];
ReadBuffer: PUBLIC PROC [add: Multibus.RawAddress] RETURNS [w: REF LineRec] = TRUSTED BEGIN
block: Multibus.Block ← Multibus.RawReadBlock[add];
w ← NEW[LineRec[2048]];
FOR i: CARDINAL IN [0 .. 2048) DO
foo: Basics.BytePair ← LOOPHOLE[block[i]];
bar: Basics.BytePair;
bar.high ← foo.low; bar.low ← foo.high;
w[i] ← LOOPHOLE[bar];
ENDLOOP;
END;
Write: PUBLIC PROC [add: Multibus.RawAddress, word: WORD] = TRUSTED BEGIN
foo: Basics.BytePair ← LOOPHOLE[word];
bar: Basics.BytePair;
bar.high ← foo.low; bar.low ← foo.high;
Multibus.RawWrite[bar, add];
END;
END.....