-- Copyright (C) 1984, 1985 by Xerox Corporation. All rights reserved.
-- TestPhoneLines.mesa, HGM, 11-Dec-85 20:26:27
DIRECTORY
Environment USING [Byte],
Inline USING [BITAND, BITOR, LowHalf],
Process USING [Detach, GetPriority, Pause, Priority, priorityBackground, SecondsToTicks, SetPriority, Yield, Ticks],
ProcessOperations USING [DisableInterrupts, EnableInterrupts],
Put USING [Char, CR, Decimal, Line, LongNumber, Number, Text],
System USING [GetClockPulses, Pulses, PulsesToMicroseconds],
Window USING [Handle],
CommUtil USING [AllocateBuffers, AllocateIocbs],
DicentraInputOutput USING [
Input, InterruptLevel, IOAddress, JumpBank, Output, SetPhonelineCSB, SetWakeupMask],
MultibusAddresses USING [dialer0, dialer1, dialer2, modem0, scc0, scc1, scc2, scc3],
OthelloDefs USING [
CheckUserAbort, CommandProcessor, Confirm, IndexTooLarge,
MyNameIs, ReadChar, RegisterCommandProc],
PhoneCreate USING [CreatePhone];
TestPhoneLines: PROGRAM
IMPORTS
Inline, Process, ProcessOperations, Put, System,
CommUtil, DicentraInputOutput, OthelloDefs, PhoneCreate =
BEGIN
wordsPerBuffer: CARDINAL = 1000;
wordsToSend: CARDINAL = 350;
bytesToSend: CARDINAL = 2*wordsToSend;
bytesToRecv: CARDINAL = bytesToSend + 1; -- Half of CRC
recvBufferBytes: CARDINAL = 1000;
Buffer: TYPE = LONG POINTER TO BufferRecord;
BufferRecord: TYPE = RECORD [
next: Buffer,
iocb: IOCB,
data: ARRAY [0..wordsPerBuffer) OF WORD,
slop: ARRAY [0..10) OF WORD ];
DtrFlapper: PROCEDURE =
BEGIN
normal: WORD = 0EBH; -- DTR, 8bits/char, TxE, RTS, CRC
noDtrNoRts: WORD = 069H; -- ~DTR, 8bits/char, TxE, ~RTS, CRC
noRts: WORD = 0E9H; -- DTR, 8bits/char, TxE, ~RTS, CRC
noDtr: WORD = 06BH; -- ~DTR, 8bits/char, TxE, RTS, CRC
priority: Process.Priority = Process.GetPriority[];
pleaseStop: BOOLEAN ← FALSE;
Watch: PROCEDURE =
BEGIN
[] ← OthelloDefs.ReadChar[ ! ABORTED => CONTINUE];
pleaseStop ← TRUE;
END;
Put.Line[log, "Flapping DTR and RTS..."L];
Process.Detach[FORK Watch[]];
Process.SetPriority[Process.priorityBackground];
UNTIL pleaseStop DO
Flappem[noDtrNoRts, normal];
Flappem[noRts, normal];
Flappem[noDtr, normal];
Put.Char[log, '!];
Process.Pause[Process.SecondsToTicks[3]];
ENDLOOP;
Process.SetPriority[priority];
Put.Line[log, " done."L];
END;
Flappem: PROCEDURE [down, up: WORD] =
BEGIN
start: System.Pulses = System.GetClockPulses[];
random: LONG CARDINAL = start MOD 10000; -- 0 to 10 ms
FOR line: CARDINAL IN [0..lines) DO -- Very Quick flap
chan: LONG POINTER TO Words = channels[line];
DicentraInputOutput.Output[down, @chan.r5];
DicentraInputOutput.Output[up, @chan.r5];
ENDLOOP;
FOR line: CARDINAL IN [0..lines) DO -- Quick flap
chan: LONG POINTER TO Words = channels[line];
DicentraInputOutput.Output[down, @chan.r5];
Process.Yield[];
DicentraInputOutput.Output[up, @chan.r5];
Process.Pause[2];
ENDLOOP;
FOR line: CARDINAL IN [0..lines) DO -- Slighlty different Quick flap
chan: LONG POINTER TO Words = channels[line];
DicentraInputOutput.Output[down, @chan.r5];
ENDLOOP;
Process.Pause[2];
FOR line: CARDINAL IN [0..lines) DO
chan: LONG POINTER TO Words = channels[line];
DicentraInputOutput.Output[up, @chan.r5];
ENDLOOP;
Process.Pause[Process.SecondsToTicks[1]];
FOR line: CARDINAL IN [0..lines) DO -- Longer flap
chan: LONG POINTER TO Words = channels[line];
DicentraInputOutput.Output[down, @chan.r5];
ENDLOOP;
UNTIL System.PulsesToMicroseconds[[System.GetClockPulses[]-start]] > random DO
Process.Yield[];
ENDLOOP;
FOR line: CARDINAL IN [0..lines) DO
chan: LONG POINTER TO Words = channels[line];
DicentraInputOutput.Output[up, @chan.r5];
ENDLOOP;
END;
PushPackets: PROCEDURE =
BEGIN
buffers: ARRAY [0..lines) OF Buffer;
pleaseStop: BOOLEAN ← FALSE;
Watch: PROCEDURE =
BEGIN
[] ← OthelloDefs.ReadChar[ ! ABORTED => CONTINUE];
pleaseStop ← TRUE;
END;
Put.Line[log, "Sending packets..."L];
Process.Detach[FORK Watch[]];
TurnOn[];
FOR line: CARDINAL IN [0..lines) DO
buffers[line] ← AllocateBuffer[bytesToSend];
QueueOutput[line, buffers[line].iocb];
ENDLOOP;
UNTIL pleaseStop DO
FOR line: CARDINAL IN [0..lines) DO
iocb: IOCB ← buffers[line].iocb;
IF iocb.status # 0 THEN
BEGIN
Put.Char[log, 'A + line];
IF iocb.status = 0FFFFH THEN
goodSend[line] ← goodSend[line] + 1
ELSE
BEGIN
badSend[line] ← badSend[line] + 1;
Put.Char[log, '~];
Put.Number[log, iocb.status, [16, FALSE, TRUE, 0]];
END;
QueueOutput[line, iocb];
END;
ENDLOOP;
Process.Yield[];
ENDLOOP;
Put.Line[log, " done."L];
TurnOff[];
PrintStats[];
FOR line: CARDINAL IN [0..lines) DO
FreeBuffer[buffers[line]];
ENDLOOP;
END;
TimeSending: PROCEDURE =
BEGIN
bufferA, bufferB: ARRAY [0..lines) OF Buffer;
start, stop: System.Pulses;
ms: LONG CARDINAL;
pleaseStop: BOOLEAN ← FALSE;
Watch: PROCEDURE =
BEGIN
[] ← OthelloDefs.ReadChar[ ! ABORTED => CONTINUE];
pleaseStop ← TRUE;
END;
Put.Line[log, "Sending packets..."L];
Process.Detach[FORK Watch[]];
TurnOn[];
FOR line: CARDINAL IN [0..lines) DO
bufferA[line] ← AllocateBuffer[bytesToSend];
bufferB[line] ← AllocateBuffer[bytesToSend];
ENDLOOP;
start ← System.GetClockPulses[];
FOR line: CARDINAL IN [0..lines) DO
QueueOutput[line, bufferA[line].iocb];
QueueOutput[line, bufferB[line].iocb];
ENDLOOP;
UNTIL pleaseStop DO
FOR line: CARDINAL IN [0..lines) DO
iocb: IOCB ← bufferA[line].iocb;
IF iocb.status # 0 THEN
BEGIN
IF iocb.status = 0FFFFH THEN
BEGIN
goodSend[line] ← goodSend[line] + 1;
bytesSent[line] ← bytesSent[line] + iocb.bytes;
END
ELSE
badSend[line] ← badSend[line] + 1;
QueueOutput[line, iocb];
END;
ENDLOOP;
FOR line: CARDINAL IN [0..lines) DO
iocb: IOCB ← bufferB[line].iocb;
IF iocb.status # 0 THEN
BEGIN
IF iocb.status = 0FFFFH THEN
BEGIN
goodSend[line] ← goodSend[line] + 1;
bytesSent[line] ← bytesSent[line] + iocb.bytes;
END
ELSE
badSend[line] ← badSend[line] + 1;
QueueOutput[line, iocb];
END;
ENDLOOP;
Process.Yield[];
ENDLOOP;
stop ← System.GetClockPulses[];
ms ← System.PulsesToMicroseconds[[stop-start]]/1000;
Put.Line[log, " done."L];
Put.Line[log, "Line Bytes BPS"L];
FOR line: CARDINAL IN [0..lines) DO
Put.Number[log, line, [10, FALSE, TRUE, 4]];
Put.LongNumber[log, bytesSent[line], [10, FALSE, TRUE, 8]];
Put.LongNumber[log, bytesSent[line]*8*1000/ms, [10, FALSE, TRUE, 8]];
Put.CR[log];
ENDLOOP;
TurnOff[];
PrintStats[];
FOR line: CARDINAL IN [0..lines) DO
FreeBuffer[bufferA[line]];
FreeBuffer[bufferB[line]];
ENDLOOP;
END;
PullPackets: PROCEDURE =
BEGIN
buffers: ARRAY [0..lines) OF Buffer;
pleaseStop: BOOLEAN ← FALSE;
Watch: PROCEDURE =
BEGIN
[] ← OthelloDefs.ReadChar[ ! ABORTED => CONTINUE];
pleaseStop ← TRUE;
END;
Put.Line[log, "Receiving packets..."L];
Process.Detach[FORK Watch[]];
TurnOn[];
FOR line: CARDINAL IN [0..lines) DO
buffers[line] ← AllocateBuffer[recvBufferBytes];
QueueInput[line, buffers[line].iocb];
ENDLOOP;
UNTIL pleaseStop DO
FOR line: CARDINAL IN [0..lines) DO
iocb: IOCB ← buffers[line].iocb;
IF iocb.status # 0 THEN
BEGIN
status: WORD = Inline.BITAND[iocb.status, 0FFH];
Put.Char[log, 'a + line];
IF status = 87H THEN
goodRecv[line] ← goodRecv[line] + 1
ELSE
BEGIN
badRecv[line] ← badRecv[line] + 1;
Put.Char[log, '~];
Put.Number[log, iocb.status, [16, FALSE, TRUE, 0]];
END;
QueueInput[line, iocb];
END;
ENDLOOP;
Process.Pause[1];
ENDLOOP;
Put.Line[log, " done."L];
TurnOff[];
PrintStats[];
FOR line: CARDINAL IN [0..lines) DO
FreeBuffer[buffers[line]];
ENDLOOP;
END;
DoubleLoopBack: PROCEDURE =
BEGIN
line: Line = 0;
pleaseStop: BOOLEAN ← FALSE;
Watch: PROCEDURE =
BEGIN
[] ← OthelloDefs.ReadChar[ ! ABORTED => CONTINUE];
pleaseStop ← TRUE;
END;
sendBufferA: Buffer ← AllocateBuffer[bytesToSend];
sendBufferB: Buffer ← AllocateBuffer[bytesToSend];
recvBufferA: Buffer ← AllocateBuffer[recvBufferBytes];
recvBufferB: Buffer ← AllocateBuffer[recvBufferBytes];
recvBufferC: Buffer ← AllocateBuffer[recvBufferBytes];
sendAgain: BOOLEAN ← FALSE;
Put.Line[log, "Sending packets to myself (double buffered) on line 0."L];
Process.Detach[FORK Watch[]];
TurnOn[];
FOR i: CARDINAL IN [0..wordsToSend) DO sendBufferA.data[i] ← i*i; ENDLOOP;
FOR i: CARDINAL IN [0..wordsToSend) DO sendBufferB.data[i] ← i*i; ENDLOOP;
QueueInput[line, recvBufferA.iocb];
QueueInput[line, recvBufferB.iocb];
QueueInput[line, recvBufferC.iocb];
QueueOutput[line, sendBufferA.iocb];
QueueOutput[line, sendBufferB.iocb];
UNTIL pleaseStop DO
IF sendBufferA.iocb.status # 0 THEN ProcessSendBuffer[sendBufferA, line];
IF sendBufferB.iocb.status # 0 THEN ProcessSendBuffer[sendBufferB, line];
IF recvBufferA.iocb.status # 0 THEN ProcessRecvBuffer[recvBufferA, line, line];
IF recvBufferB.iocb.status # 0 THEN ProcessRecvBuffer[recvBufferB, line, line];
IF recvBufferC.iocb.status # 0 THEN ProcessRecvBuffer[recvBufferC, line, line];
Process.Yield[];
ENDLOOP;
Put.Line[log, " done."L];
TurnOff[];
PrintStats[];
FreeBuffer[sendBufferA];
FreeBuffer[sendBufferB];
FreeBuffer[recvBufferA];
FreeBuffer[recvBufferB];
FreeBuffer[recvBufferC];
END;
LoopBack: PROCEDURE =
BEGIN
line: Line = 0;
pleaseStop: BOOLEAN ← FALSE;
under, abort, idleAborts, extAborts: CARDINAL ← 0;
Watch: PROCEDURE =
BEGIN
[] ← OthelloDefs.ReadChar[ ! ABORTED => CONTINUE];
pleaseStop ← TRUE;
END;
sendBuffer: Buffer ← AllocateBuffer[bytesToSend];
recvBuffer: Buffer ← AllocateBuffer[recvBufferBytes];
sendAgain: BOOLEAN ← FALSE;
Put.Line[log, "Sending packets to myself on line 0."L];
Process.Detach[FORK Watch[]];
TurnOn[];
FOR i: CARDINAL IN [0..wordsToSend) DO sendBuffer.data[i] ← i*i; ENDLOOP;
QueueInput[line, recvBuffer.iocb];
QueueOutput[line, sendBuffer.iocb];
UNTIL pleaseStop DO
IF under # csb[line].tranUnderruns THEN
BEGIN
Put.Char[log, '#];
under ← under + 1;
LOOP;
END;
IF abort # csb[line].recvAborted THEN
BEGIN
Put.Char[log, '$];
abort ← abort + 1;
LOOP;
END;
IF idleAborts # csb[line].recvAbortIdle THEN
BEGIN
Put.Char[log, '%];
idleAborts ← idleAborts + 1;
LOOP;
END;
IF extAborts # csb[line].recvAbortEx THEN
BEGIN
Put.Char[log, '&];
extAborts ← extAborts + 1;
LOOP;
END;
IF sendBuffer.iocb.status # 0 THEN
BEGIN
IF ~sendAgain THEN -- Give packet time to arrive (single buffer)
BEGIN
Put.Char[log, 'A + line];
IF sendBuffer.iocb.status # 0 THEN
BEGIN
IF sendBuffer.iocb.status = 0FFFFH THEN
BEGIN
goodSend[line] ← goodSend[line] + 1;
bytesSent[line] ← bytesSent[line] + sendBuffer.iocb.bytes;
END
ELSE
BEGIN
badSend[line] ← badSend[line] + 1;
Put.Number[log, sendBuffer.iocb.status, [16, FALSE, TRUE, 0]];
END;
END;
sendAgain ← TRUE;
Process.Pause[2];
END
ELSE
BEGIN
sendAgain ← FALSE;
QueueOutput[line, sendBuffer.iocb];
END;
END;
IF recvBuffer.iocb.status # 0 THEN ProcessRecvBuffer[recvBuffer, line, line];
Process.Yield[];
ENDLOOP;
Put.Line[log, " done."L];
TurnOff[];
PrintStats[];
FreeBuffer[sendBuffer];
FreeBuffer[recvBuffer];
END;
ProcessSendBuffer: PROCEDURE [sendBuffer: Buffer, line: Line] =
BEGIN
Put.Char[log, 'A + line];
IF sendBuffer.iocb.status = 0FFFFH THEN
BEGIN
goodSend[line] ← goodSend[line] + 1;
bytesSent[line] ← bytesSent[line] + sendBuffer.iocb.bytes;
END
ELSE
BEGIN
badSend[line] ← badSend[line] + 1;
Put.Number[log, sendBuffer.iocb.status, [16, FALSE, TRUE, 0]];
END;
QueueOutput[line, sendBuffer.iocb];
END;
ProcessRecvBuffer: PROCEDURE [buffer: Buffer, line: Line, check: CARDINAL] =
BEGIN
status: WORD = Inline.BITAND[buffer.iocb.status, 0FFH];
length: CARDINAL = buffer.iocb.bytes - buffer.iocb.bytesLeft;
Put.Char[log, 'a + line];
IF status = 87H THEN
goodRecv[line] ← goodRecv[line] + 1
ELSE
BEGIN
badRecv[line] ← badRecv[line] + 1;
Put.Char[log, '~];
Put.Number[log, buffer.iocb.status, [16, FALSE, TRUE, 0]];
Put.Char[log, '.];
Put.Decimal[log, length];
Put.CR[log];
PrintBuffer[buffer, line];
END;
IF status = 87H AND length # bytesToRecv THEN
BEGIN
Put.Char[log, '(];
Put.Decimal[log, length];
Put.Char[log, ')];
PrintBuffer[buffer, line];
END;
IF status = 87H AND length = bytesToRecv THEN
BEGIN
bad: BOOLEAN ← FALSE;
FOR i: CARDINAL IN [0..2*wordsToSend) DO
data: LONG POINTER TO PACKED ARRAY [0..0) OF Environment.Byte = LOOPHOLE[@buffer.data];
IF data[i] # check * 16 THEN bad ← TRUE;
ENDLOOP;
IF bad THEN PrintBuffer[buffer, line];
END;
FOR i: CARDINAL IN [0..wordsPerBuffer) DO buffer.data[i] ← 0; ENDLOOP;
QueueInput[line, buffer.iocb];
END;
PrintBuffer: PROCEDURE [buffer: Buffer, line: CARDINAL] =
BEGIN
iocb: LONG POINTER TO ARRAY [0..0) OF WORD = LOOPHOLE[buffer.iocb];
length: CARDINAL = buffer.iocb.bytes - buffer.iocb.bytesLeft;
Put.CR[log];
Put.Text[log, "Line: "L];
Put.Number[log, line, [16, FALSE, TRUE, 0]];
Put.Text[log, ", Status: "L];
Put.Number[log, buffer.iocb.status, [16, FALSE, TRUE, 0]];
Put.CR[log];
Put.Text[log, "IOCB/ "L];
FOR i: CARDINAL IN [0..16) DO
Put.Number[log, iocb[i], [16, FALSE, TRUE, 6]];
IF i = 7 THEN Put.Text[log, "\n "L];
ENDLOOP;
FOR i: CARDINAL IN [0..(length+1)/2) DO
IF (i MOD 8) = 0 THEN
BEGIN
Put.CR[log];
Put.Number[log, i, [16, FALSE, TRUE, 4]];
Put.Text[log, "/ "L];
END;
Put.Number[log, buffer.data[i], [16, FALSE, TRUE, 5]];
IF buffer.data[i] # i*i THEN Put.Char[log, '←] ELSE Put.Char[log, ' ];
ENDLOOP;
Put.CR[log];
END;
Thrash: PROCEDURE =
BEGIN
pause: CARDINAL ← 0;
pleaseStop: BOOLEAN ← FALSE;
Watch: PROCEDURE =
BEGIN
[] ← OthelloDefs.ReadChar[ ! ABORTED => CONTINUE];
pleaseStop ← TRUE;
END;
sendBufferA: ARRAY [0..lines) OF Buffer;
sendBufferB: ARRAY [0..lines) OF Buffer;
recvBufferA: ARRAY [0..lines) OF Buffer;
recvBufferB: ARRAY [0..lines) OF Buffer;
recvBufferC: ARRAY [0..lines) OF Buffer;
Put.Line[log, "Sending and receiving lots of packets on all lines."L];
Process.Detach[FORK Watch[]];
FOR line: Line IN [0..lines) DO
sendBufferA[line] ← AllocateBuffer[500+line];
sendBufferB[line] ← AllocateBuffer[500+line];
recvBufferA[line] ← AllocateBuffer[recvBufferBytes];
recvBufferB[line] ← AllocateBuffer[recvBufferBytes];
recvBufferC[line] ← AllocateBuffer[recvBufferBytes];
ENDLOOP;
TurnOn[];
FOR line: Line IN [0..lines) DO
QueueInput[line, recvBufferA[line].iocb];
QueueInput[line, recvBufferB[line].iocb];
QueueInput[line, recvBufferC[line].iocb];
ENDLOOP;
FOR line: Line IN [0..lines) DO
QueueOutput[line, sendBufferA[line].iocb];
QueueOutput[line, sendBufferB[line].iocb];
ENDLOOP;
UNTIL pleaseStop DO
FOR line: Line IN [0..lines) DO
CheckOutput[line, sendBufferA[line]];
CheckOutput[line, sendBufferB[line]];
CheckInput[line, recvBufferA[line]];
CheckInput[line, recvBufferB[line]];
CheckInput[line, recvBufferC[line]];
ENDLOOP;
Process.Yield[];
pause ← pause + 1;
IF pause = 1000 THEN
BEGIN
Process.Pause[1]; -- Let Watchdog run
pause ← 0;
Put.Char[log, '!];
END;
ENDLOOP;
Put.Line[log, " done."L];
TurnOff[];
PrintStats[];
FOR line: Line IN [0..lines) DO
FreeBuffer[sendBufferA[line]];
FreeBuffer[sendBufferB[line]];
FreeBuffer[recvBufferA[line]];
FreeBuffer[recvBufferB[line]];
FreeBuffer[recvBufferC[line]];
ENDLOOP;
END;
CheckOutput: PROCEDURE [line: CARDINAL, buffer: Buffer] =
BEGIN
IF buffer.iocb.status = 0 THEN RETURN;
IF buffer.iocb.status = 0FFFFH THEN
BEGIN
goodSend[line] ← goodSend[line] + 1;
bytesSent[line] ← bytesSent[line] + buffer.iocb.bytes;
END
ELSE
BEGIN
badSend[line] ← badSend[line] + 1;
Put.Char[log, 'A+line];
Put.Number[log, buffer.iocb.status, [16, FALSE, TRUE, 0]];
END;
QueueOutput[line, buffer.iocb];
END;
CheckInput: PROCEDURE [line: CARDINAL, buffer: Buffer] =
BEGIN
status: WORD;
IF buffer.iocb.status = 0 THEN RETURN;
status ← Inline.BITAND[buffer.iocb.status, 0FFH];
IF status = 87H THEN goodRecv[line] ← goodRecv[line] + 1
ELSE
BEGIN
Put.Char[log, 'a+line];
Put.Number[log, buffer.iocb.status, [16, FALSE, TRUE, 0]];
badRecv[line] ← badRecv[line] + 1;
END;
QueueInput[line, buffer.iocb];
END;
PushPull: PROCEDURE =
BEGIN
line: Line = 0;
pleaseStop: BOOLEAN ← FALSE;
under, abort, idleAborts, extAborts: CARDINAL ← 0;
Watch: PROCEDURE =
BEGIN
[] ← OthelloDefs.ReadChar[ ! ABORTED => CONTINUE];
pleaseStop ← TRUE;
END;
sendBuffer: Buffer ← AllocateBuffer[bytesToSend];
recvBuffer: Buffer ← AllocateBuffer[recvBufferBytes];
Put.Line[log, "Sending and receiving packets on line 0."L];
Process.Detach[FORK Watch[]];
TurnOn[];
FOR i: CARDINAL IN [0..wordsToSend) DO sendBuffer.data[i] ← i*i; ENDLOOP;
QueueInput[line, recvBuffer.iocb];
QueueOutput[line, sendBuffer.iocb];
UNTIL pleaseStop DO
IF under # csb[line].tranUnderruns THEN
BEGIN
Put.Char[log, '#];
under ← under + 1;
LOOP;
END;
IF abort # csb[line].recvAborted THEN
BEGIN
Put.Char[log, '$];
abort ← abort + 1;
LOOP;
END;
IF idleAborts # csb[line].recvAbortIdle THEN
BEGIN
Put.Char[log, '%];
idleAborts ← idleAborts + 1;
LOOP;
END;
IF extAborts # csb[line].recvAbortEx THEN
BEGIN
Put.Char[log, '&];
extAborts ← extAborts + 1;
LOOP;
END;
IF sendBuffer.iocb.status # 0 THEN
BEGIN
Put.Char[log, 'A + line];
IF sendBuffer.iocb.status = 0FFFFH THEN
BEGIN
goodSend[line] ← goodSend[line] + 1;
bytesSent[line] ← bytesSent[line] + sendBuffer.iocb.bytes;
END
ELSE
BEGIN
badSend[line] ← badSend[line] + 1;
Put.Number[log, sendBuffer.iocb.status, [16, FALSE, TRUE, 0]];
END;
QueueOutput[line, sendBuffer.iocb];
END;
IF recvBuffer.iocb.status # 0 THEN
BEGIN
status: WORD = Inline.BITAND[recvBuffer.iocb.status, 0FFH];
Put.Char[log, 'a + line];
IF status = 87H THEN goodRecv[line] ← goodRecv[line] + 1
ELSE badRecv[line] ← badRecv[line] + 1;
QueueInput[line, recvBuffer.iocb];
END;
Process.Yield[];
ENDLOOP;
Put.Line[log, " done."L];
TurnOff[];
PrintStats[];
FreeBuffer[sendBuffer];
FreeBuffer[recvBuffer];
END;
PrintStats: PROCEDURE =
BEGIN
Put.Line[log, "PhoneLine Statistics:"L];
Put.Line[log, "Line Under Abort IAbt EAbt Missed G Snd B Snd G Rcv B Rcv"L];
FOR line: Line IN [0..lines) DO
Put.Number[log, line, [10, FALSE, TRUE, 4]];
Put.Number[log, csb[line].tranUnderruns, [10, FALSE, TRUE, 8]];
Put.Number[log, csb[line].recvAborted, [10, FALSE, TRUE, 8]];
Put.Number[log, csb[line].recvAbortIdle, [10, FALSE, TRUE, 8]];
Put.Number[log, csb[line].recvAbortEx, [10, FALSE, TRUE, 8]];
Put.Number[log, csb[line].recvMissed, [10, FALSE, TRUE, 8]];
Put.Number[log, goodSend[line], [10, FALSE, TRUE, 8]];
Put.Number[log, badSend[line], [10, FALSE, TRUE, 8]];
Put.Number[log, goodRecv[line], [10, FALSE, TRUE, 8]];
Put.Number[log, badRecv[line], [10, FALSE, TRUE, 8]];
Put.CR[log];
ENDLOOP;
Put.CR[log];
END;
commandProcessor: OthelloDefs.CommandProcessor ← [Commands];
Commands: PROCEDURE [index: CARDINAL] =
BEGIN
SELECT index FROM
0 =>
BEGIN
OthelloDefs.MyNameIs[
myNameIs: "Activate 4 phone lines"L,
myHelpIs: "Turn on the phone lines so you can talk to the rest of the world"L];
OthelloDefs.Confirm[];
PhoneCreate.CreatePhone[376B, 0, 0, NIL];
PhoneCreate.CreatePhone[376B, 0, 1, NIL];
PhoneCreate.CreatePhone[376B, 0, 2, NIL];
PhoneCreate.CreatePhone[376B, 0, 3, NIL];
END;
1 =>
BEGIN
OthelloDefs.MyNameIs[
myNameIs: "PhoneLine Double Buffered LoopBack"L,
myHelpIs: "Send packets to self on line 0"L];
DoubleLoopBack[];
END;
2 =>
BEGIN
OthelloDefs.MyNameIs[
myNameIs: "PhoneLine DTR Flapper"L,
myHelpIs: "Flap DTR and xx on all 8 lines"L];
DtrFlapper[];
END;
3 =>
BEGIN
OthelloDefs.MyNameIs[
myNameIs: "PhoneLine LoopBack"L,
myHelpIs: "Send packets to self on line 0"L];
LoopBack[];
END;
4 =>
BEGIN
OthelloDefs.MyNameIs[
myNameIs: "PhoneLine PushPull"L,
myHelpIs: "Send and Recv packets on line 0"L];
PushPull[];
END;
5 =>
BEGIN
OthelloDefs.MyNameIs[
myNameIs: "PhoneLine Recv"L,
myHelpIs: "Recv packets via Phone line"L];
PullPackets[];
END;
6 =>
BEGIN
OthelloDefs.MyNameIs[
myNameIs: "PhoneLine Send"L,
myHelpIs: "Send packets via Phone line"L];
PushPackets[];
END;
7 =>
BEGIN
OthelloDefs.MyNameIs[
myNameIs: "PhoneLine Statistics"L,
myHelpIs: "Print PhoneLine Testing statistics"L];
PrintStats[];
END;
8 =>
BEGIN
OthelloDefs.MyNameIs[
myNameIs: "PhoneLine Timing"L,
myHelpIs: "Time sending packets"L];
TimeSending[];
END;
9 =>
BEGIN
OthelloDefs.MyNameIs[
myNameIs: "PhoneLine Thrash"L,
myHelpIs: "Send+Recv (double buffered) on all 8 lines"L];
Thrash[];
END;
10 =>
BEGIN
OthelloDefs.MyNameIs[
myNameIs: "Test Bank Hopping"L,
myHelpIs: "Jump back and forth between Bank0 and Bank 1"L];
TestBankHopping[];
END;
11 =>
BEGIN
OthelloDefs.MyNameIs[
myNameIs: "Test Dialers Noisy"L,
myHelpIs: "Test Dialer - print info on errors"L];
TestDialers[FALSE];
END;
12 =>
BEGIN
OthelloDefs.MyNameIs[
myNameIs: "Test Dialers Quietly"L,
myHelpIs: "Test Dialer - ignore errors"L];
TestDialers[TRUE];
END;
13 =>
BEGIN
OthelloDefs.MyNameIs[
myNameIs: "Test Mesa Clocks"L,
myHelpIs: "Compare Process Ticker with High Res Clock"L];
TestMesaClocks[];
END;
ENDCASE => OthelloDefs.IndexTooLarge;
END;
TestBankHopping: PROCEDURE =
BEGIN
Put.Text[log, "Bank Hopping..."L];
FOR i: CARDINAL IN [0..100) DO
DicentraInputOutput.JumpBank[0];
DicentraInputOutput.JumpBank[1];
ENDLOOP;
DicentraInputOutput.JumpBank[1];
Put.Line[log, " done."L];
END;
TestDialers: PROCEDURE [silent: BOOLEAN] =
BEGIN
pleaseStop: BOOLEAN ← FALSE;
Watch: PROCEDURE =
BEGIN
[] ← OthelloDefs.ReadChar[ ! ABORTED => CONTINUE];
pleaseStop ← TRUE;
END;
Put.Text[log, "Dialing..."L];
Process.Detach[FORK Watch[]];
InitDialers[];
UNTIL pleaseStop DO
FOR dialer: CARDINAL IN [0..dialers) UNTIL pleaseStop DO
FOR i: CARDINAL IN [0..256) UNTIL pleaseStop DO
n: WORD;
SetDialerOutput[dialer, i];
n ← GetDialerInput[dialer];
Process.Yield[];
IF i # n AND ~ silent THEN
BEGIN
Put.Text[log, "Dialer mixup: dialer "L];
Put.Number[log, dialer, [16, FALSE, TRUE, 0]];
Put.Text[log, ", expected = "L];
Put.Number[log, i, [16, FALSE, TRUE, 0]];
Put.Text[log, ", found = "L];
Put.Number[log, n, [16, FALSE, TRUE, 0]];
Put.CR[log];
END;
ENDLOOP;
ENDLOOP;
Put.Char[log, '!];
ENDLOOP;
Put.Line[log, " done."L];
END;
TestMesaClocks: PROCEDURE =
BEGIN
oneSecond: Process.Ticks = Process.SecondsToTicks[1];
start, stop, elapsed: System.Pulses;
micro, max, min: LONG CARDINAL;
data: ARRAY [0..1000) OF System.Pulses;
BEGIN
Put.Line[log, "Collecting a clump of close samples..."L];
ProcessOperations.DisableInterrupts[];
FOR i: CARDINAL IN [0..100) DO
data[i] ← System.GetClockPulses[];
ENDLOOP;
ProcessOperations.EnableInterrupts[];
max ← 0;
min ← LAST[LONG CARDINAL];
start ← data[0];
FOR i: CARDINAL IN [1..100) DO
stop ← data[i];
elapsed ← LOOPHOLE[stop - start];
min ← MIN[min, elapsed];
max ← MAX[max, elapsed];
Put.LongNumber[log, stop, [16, FALSE, TRUE, 10]];
SELECT elapsed FROM
0 => Put.Text[log, " "L];
< 0300H =>
BEGIN
Put.LongNumber[log, elapsed, [16, FALSE, TRUE, 4]];
Put.Text[log, " "L];
END;
> 0FFFFFF00H =>
BEGIN
Put.LongNumber[log, -elapsed, [16, FALSE, TRUE, 4]];
Put.Text[log, "←"L];
END;
ENDCASE => Put.Text[log, " "L];
OthelloDefs.CheckUserAbort[! ABORTED => EXIT];
IF ((i+1) MOD 5) = 0 THEN Put.CR[log];
start ← stop;
ENDLOOP;
Put.Text[log, "The min separation was "L];
Put.LongNumber[log, min, [10, FALSE, TRUE, 0]];
Put.Text[log, " ticks = "L];
Put.LongNumber[log, System.PulsesToMicroseconds[[min]], [10, FALSE, TRUE, 0]];
Put.Line[log, " microseconds."L];
Put.Text[log, "The max separation was "L];
Put.LongNumber[log, max, [10, FALSE, TRUE, 0]];
Put.Text[log, " ticks = "L];
Put.LongNumber[log, System.PulsesToMicroseconds[[max]], [10, FALSE, TRUE, 0]];
Put.Line[log, " microseconds."L];
END;
BEGIN
Put.CR[log];
Put.Line[log, "Collecting a clump of 100 LOOP samples..."L];
ProcessOperations.DisableInterrupts[];
FOR i: CARDINAL IN [0..100) DO
FOR x: CARDINAL IN [0..100) DO ENDLOOP;
data[i] ← System.GetClockPulses[];
ENDLOOP;
ProcessOperations.EnableInterrupts[];
max ← 0;
min ← LAST[LONG CARDINAL];
start ← data[0];
FOR i: CARDINAL IN [1..100) DO
stop ← data[i];
elapsed ← LOOPHOLE[stop - start];
min ← MIN[min, elapsed];
max ← MAX[max, elapsed];
Put.LongNumber[log, stop, [16, FALSE, TRUE, 10]];
SELECT elapsed FROM
0 => Put.Text[log, " "L];
< 0300H =>
BEGIN
Put.LongNumber[log, elapsed, [16, FALSE, TRUE, 4]];
Put.Text[log, " "L];
END;
> 0FFFFFF00H =>
BEGIN
Put.LongNumber[log, -elapsed, [16, FALSE, TRUE, 4]];
Put.Text[log, "←"L];
END;
ENDCASE => Put.Text[log, " "L];
OthelloDefs.CheckUserAbort[! ABORTED => EXIT];
IF ((i+1) MOD 5) = 0 THEN Put.CR[log];
start ← stop;
ENDLOOP;
Put.Text[log, "The min separation was "L];
Put.LongNumber[log, min, [10, FALSE, TRUE, 0]];
Put.Text[log, " ticks = "L];
Put.LongNumber[log, System.PulsesToMicroseconds[[min]], [10, FALSE, TRUE, 0]];
Put.Line[log, " microseconds."L];
Put.Text[log, "The max separation was "L];
Put.LongNumber[log, max, [10, FALSE, TRUE, 0]];
Put.Text[log, " ticks = "L];
Put.LongNumber[log, System.PulsesToMicroseconds[[max]], [10, FALSE, TRUE, 0]];
Put.Line[log, " microseconds."L];
END;
BEGIN
Put.CR[log];
Put.Line[log, "Collecting a clump of 255 LOOP samples..."L];
ProcessOperations.DisableInterrupts[];
start ← System.GetClockPulses[];
FOR i: CARDINAL IN [0..100) DO
FOR x: CARDINAL IN [0..Inline.BITAND[255, 0FFH]) DO ENDLOOP;
data[i] ← System.GetClockPulses[];
ENDLOOP;
ProcessOperations.EnableInterrupts[];
max ← 0;
min ← LAST[LONG CARDINAL];
start ← data[0];
FOR i: CARDINAL IN [1..100) DO
stop ← data[i];
elapsed ← LOOPHOLE[stop - start];
min ← MIN[min, elapsed];
max ← MAX[max, elapsed];
Put.LongNumber[log, stop, [16, FALSE, TRUE, 10]];
SELECT elapsed FROM
0 => Put.Text[log, " "L];
< 0300H =>
BEGIN
Put.LongNumber[log, elapsed, [16, FALSE, TRUE, 4]];
Put.Text[log, " "L];
END;
> 0FFFFFF00H =>
BEGIN
Put.LongNumber[log, -elapsed, [16, FALSE, TRUE, 4]];
Put.Text[log, "←"L];
END;
ENDCASE => Put.Text[log, " "L];
OthelloDefs.CheckUserAbort[! ABORTED => EXIT];
IF ((i+1) MOD 5) = 0 THEN Put.CR[log];
start ← stop;
ENDLOOP;
Put.Text[log, "The min separation was "L];
Put.LongNumber[log, min, [10, FALSE, TRUE, 0]];
Put.Text[log, " ticks = "L];
Put.LongNumber[log, System.PulsesToMicroseconds[[min]], [10, FALSE, TRUE, 0]];
Put.Line[log, " microseconds."L];
Put.Text[log, "The max separation was "L];
Put.LongNumber[log, max, [10, FALSE, TRUE, 0]];
Put.Text[log, " ticks = "L];
Put.LongNumber[log, System.PulsesToMicroseconds[[max]], [10, FALSE, TRUE, 0]];
Put.Line[log, " microseconds."L];
END;
BEGIN
Put.CR[log];
Put.Line[log, "Scanning for hickups..."L];
FOR k: CARDINAL IN [0..1000) DO
ProcessOperations.DisableInterrupts[];
start ← System.GetClockPulses[];
FOR i: CARDINAL IN [0..100) DO
data[i] ← System.GetClockPulses[];
FOR j: CARDINAL IN [0..Inline.BITAND[k, 0FFH]) DO ENDLOOP;
ENDLOOP;
ProcessOperations.EnableInterrupts[];
max ← 0;
FOR i: CARDINAL IN [0..100) DO
stop ← data[i];
elapsed ← LOOPHOLE[stop - start];
max ← MAX[max, elapsed];
IF elapsed > 200H THEN {
Put.LongNumber[log, k, [16, FALSE, TRUE, 4]];
Put.LongNumber[log, i, [16, FALSE, TRUE, 4]];
Put.LongNumber[log, start, [16, FALSE, TRUE, 10]];
Put.LongNumber[log, stop, [16, FALSE, TRUE, 10]];
Put.LongNumber[log, elapsed, [16, FALSE, TRUE, 10]];
Put.CR[log]; };
start ← stop;
ENDLOOP;
Put.Char[log, '!];
Process.Pause[5]; -- WatchDog keeps attacking
OthelloDefs.CheckUserAbort[! ABORTED => EXIT];
ENDLOOP;
END;
Put.CR[log];
Put.Line[log, "Comparing Mesa clocks (1 sec samples)..."L];
FOR i: CARDINAL IN [0..100) DO
Process.Pause[1]; -- Wait for clock to tick
start ← System.GetClockPulses[];
Process.Pause[oneSecond];
stop ← System.GetClockPulses[];
elapsed ← LOOPHOLE[stop - start];
micro ← System.PulsesToMicroseconds[elapsed];
Put.LongNumber[log, micro, [10, FALSE, TRUE, 8]];
Put.LongNumber[log, start, [16, FALSE, TRUE, 10]];
Put.LongNumber[log, stop, [16, FALSE, TRUE, 10]];
Put.LongNumber[log, elapsed, [16, FALSE, TRUE, 10]];
Put.CR[log];
OthelloDefs.CheckUserAbort[! ABORTED => EXIT];
ENDLOOP;
Put.Line[log, " done."L];
END;
log: Window.Handle ← NIL;
-- Utlities
freeChain: Buffer ← NIL; -- Pilot dies (regionCacheFull) if we do the obvious thing
AllocateBuffer: PROCEDURE [bytes: CARDINAL] RETURNS [buffer: Buffer] =
BEGIN
IF freeChain = NIL THEN
BEGIN
buffer ← CommUtil.AllocateBuffers[SIZE[BufferRecord]];
buffer.iocb ← CommUtil.AllocateIocbs[SIZE[IOCBlock]];
END
ELSE
BEGIN
buffer ← freeChain;
freeChain ← buffer.next;
END;
buffer.next ← NIL;
buffer.iocb↑ ← [
next: NIL,
status: 0,
buffer: @buffer.data,
bytes: bytes,
bytesLeft: 0,
unused: ALL[0],
finger: NIL,
mapped: NIL,
spare: ALL[0] ];
FOR i: CARDINAL IN [0..wordsPerBuffer) DO buffer.data[i] ← 0; ENDLOOP;
END;
FreeBuffer: PROCEDURE [buffer: Buffer] =
BEGIN
buffer.next ← freeChain;
freeChain ← buffer;
END;
-- Driver sorts of things
maxLines: CARDINAL = 16;
Line: TYPE = [0..maxLines);
sendTail, recvTail: ARRAY Line OF IOCB;
goodSend: ARRAY Line OF CARDINAL ← ALL[0];
bytesSent: ARRAY Line OF LONG CARDINAL ← ALL[0];
badSend: ARRAY Line OF CARDINAL ← ALL[0];
goodRecv: ARRAY Line OF CARDINAL ← ALL[0];
bytesRecv: ARRAY Line OF LONG CARDINAL ← ALL[0];
badRecv: ARRAY Line OF CARDINAL ← ALL[0];
csb: LONG POINTER TO CSB = CommUtil.AllocateBuffers[SIZE[CSB]];
CSB: TYPE = ARRAY Line OF ControlBlock;
ControlBlock: TYPE = RECORD [
tranState: WORD,
tranIOCB: ShortIOCB,
tranIOCBMapped: LONG POINTER,
recvState: WORD,
recvIOCB: ShortIOCB,
recvIOCBMapped: LONG POINTER,
tranUnderruns: CARDINAL,
tranUnused: ARRAY [9..12) OF WORD,
recvMissed: CARDINAL,
recvAborted: CARDINAL,
recvAbortIdle: CARDINAL,
recvAbortEx: CARDINAL];
IOCB: TYPE = LONG POINTER TO IOCBlock;
ShortIOCB: TYPE = POINTER TO IOCBlock;
IOCBlock: TYPE = RECORD [
next: ShortIOCB,
status: WORD,
buffer: LONG POINTER,
bytes: CARDINAL,
bytesLeft: CARDINAL,
unused: ARRAY [6..8) OF WORD,
finger: LONG POINTER,
mapped: LONG POINTER,
spare: ARRAY [12..16) OF WORD ];
lines: CARDINAL = 8;
channels: ARRAY [0..lines) OF LONG POINTER TO Words = [
LOOPHOLE[MultibusAddresses.scc0 + 10H], -- Chan A
LOOPHOLE[MultibusAddresses.scc0 + 00H], -- Chan B
LOOPHOLE[MultibusAddresses.scc1 + 10H],
LOOPHOLE[MultibusAddresses.scc1 + 00H],
LOOPHOLE[MultibusAddresses.scc2 + 10H],
LOOPHOLE[MultibusAddresses.scc2 + 00H],
LOOPHOLE[MultibusAddresses.scc3 + 10H],
LOOPHOLE[MultibusAddresses.scc3 + 00H]];
interruptVect: ARRAY [0..lines) OF WORD = [0, 0, 10H, 10H, 20H, 20H, 30H, 30H];
baudRateNumber: ARRAY [0..lines) OF WORD = [014H, 014H, 077H, 077H, 077H, 077H, 077H, 077H];
dialers: CARDINAL = 3;
dialerAddrs: ARRAY [0..dialers) OF LONG POINTER TO Words = [
LOOPHOLE[MultibusAddresses.dialer0],
LOOPHOLE[MultibusAddresses.dialer1],
LOOPHOLE[MultibusAddresses.dialer2]];
Words: TYPE = RECORD [
r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15: WORD];
SetupTestClocks: PROCEDURE =
BEGIN
SetupTestClock[MultibusAddresses.modem0, 00BH]; -- LCa for first 2 lines at 56KB
SetupTestClock[MultibusAddresses.dialer0, 041H]; -- LCb for next 2 lines at 9600
SetupTestClock[MultibusAddresses.dialer1, 041H]; -- LCc for next 2 lines at 9600
SetupTestClock[MultibusAddresses.dialer2, 041H]; -- LCd for next 2 lines at 9600
END;
SetupTestClock: PROCEDURE [chip: DicentraInputOutput.IOAddress, ticks: CARDINAL] =
BEGIN
DicentraInputOutput.Output[001H, chip + 000H]; -- Master Interrupt Control ← Reset
DicentraInputOutput.Output[000H, chip + 000H]; -- Master Interrupt Control ← No Reset
DicentraInputOutput.Output[094H, chip + 001H]; -- Master Config Control ← Enable Ports and Cnt3
DicentraInputOutput.Output[000H, chip + 01AH]; -- Cnt3 MSB
DicentraInputOutput.Output[ticks, chip + 01BH]; -- Cnt3 LSB
DicentraInputOutput.Output[0C2H, chip + 01EH]; -- Cnt3 Mode ← Sq Out
DicentraInputOutput.Output[006H, chip + 00CH]; -- Cnt3 Command ← Go
END;
InitChip: PROCEDURE [line: Line] =
BEGIN
chan: LONG POINTER TO Words = channels[line];
chanB: LONG POINTER TO Words = channels[Inline.BITOR[line, 1]];
DicentraInputOutput.Output[002H, @chanB.r0]; -- Shift Left (ADR0 is ignored)
DicentraInputOutput.Output[0C9H, @chan.r9]; -- Hardware Reset, MIE, V, VIS
END;
InitLine: PROCEDURE [line: Line] =
BEGIN
chan: LONG POINTER TO Words = channels[line];
DicentraInputOutput.Output[interruptVect[line], @chan.r2]; -- Int Vector Base
DicentraInputOutput.Output[020H, @chan.r4]; -- SDLC
DicentraInputOutput.Output[013H, @chan.r1]; -- Rx All Int, Tx Int En, Ext Int En
DicentraInputOutput.Output[0D9H, @chan.r3]; -- 8bits/char, Hunt, CRC, RxE
DicentraInputOutput.Output[0EBH, @chan.r5]; -- DTR, 8bits/char, TxE, RTS, CRC
DicentraInputOutput.Output[07EH, @chan.r7]; -- Flag Char
DicentraInputOutput.Output[084H, @chan.r10]; -- CRC←1, Abort on Underrun
DicentraInputOutput.Output[080H, @chan.r15]; -- Enable Ext Int on Abort
DicentraInputOutput.Output[070H, @chan.r0]; -- Reset Rx CRC, Error Reset
DicentraInputOutput.Output[090H, @chan.r0]; -- Reset Tx CRC, Reset Ext/Sts Int
DicentraInputOutput.Output[008H, @chan.r11]; -- External Clocks
IF FALSE THEN
BEGIN
DicentraInputOutput.Output[baudRateNumber[line], @chan.r12]; -- Low byte of time constant
DicentraInputOutput.Output[000H, @chan.r13]; -- High byte of time constant
DicentraInputOutput.Output[003H, @chan.r14]; -- Baud Rate Gen from PClk
DicentraInputOutput.Output[050H, @chan.r11]; -- Clocks from BR Gen
END;
END;
InitDialers: PROCEDURE =
BEGIN
SetupTestClocks[];
FOR dialer: CARDINAL IN [0..dialers) DO
InitDialer[dialerAddrs[dialer]];
ENDLOOP;
END;
InitDialer: PROCEDURE [chip: DicentraInputOutput.IOAddress] =
BEGIN
DicentraInputOutput.Output[000H, chip + 023H]; -- Port A Direction, All Out
DicentraInputOutput.Output[0FFH, chip + 02BH]; -- Port B Direction, All In
END;
SetDialerOutput: PROCEDURE [dialer: CARDINAL, data: WORD] =
BEGIN
chip: DicentraInputOutput.IOAddress = dialerAddrs[dialer];
DicentraInputOutput.Output[data, chip+00DH]; -- Port A
END;
GetDialerInput: PROCEDURE [dialer: CARDINAL] RETURNS [data: WORD] =
BEGIN
chip: DicentraInputOutput.IOAddress = dialerAddrs[dialer];
data ← DicentraInputOutput.Input[chip+00EH]; -- Port B
END;
TurnOn: PROCEDURE =
BEGIN
SetupTestClocks[];
goodSend ← ALL[0];
bytesSent ← ALL[0];
badSend ← ALL[0];
goodRecv ← ALL[0];
bytesRecv ← ALL[0];
badRecv ← ALL[0];
csb↑ ← ALL[ [
tranState: 0,
tranIOCB: NIL,
tranIOCBMapped: NIL,
recvState: 0,
recvIOCB: NIL,
recvIOCBMapped: NIL,
tranUnderruns: 0,
tranUnused: ALL[0],
recvMissed: 0,
recvAborted: 0,
recvAbortIdle: 0,
recvAbortEx: 0 ] ];
FOR line: Line IN [0..lines) DO
InitChip[line];
ENDLOOP;
FOR line: Line IN [0..lines) DO
InitLine[line];
ENDLOOP;
DicentraInputOutput.SetWakeupMask[0, phoneLine];
DicentraInputOutput.SetPhonelineCSB[csb];
END;
TurnOff: PROCEDURE =
BEGIN
DicentraInputOutput.SetPhonelineCSB[NIL];
END;
QueueOutput: PROCEDURE [line: Line, iocb: IOCB] =
BEGIN
chan: LONG POINTER TO Words = channels[line];
bytes: LONG POINTER TO PACKED ARRAY [0..0) OF Environment.Byte = iocb.buffer;
iocb.status ← 0;
iocb.next ← NIL;
IF csb[line].tranIOCB = NIL THEN
BEGIN
temp: WORD ← csb[line].tranState;
IF temp # 0 THEN
BEGIN
Put.CR[log];
Put.Text[log, "Unexpected tran state = "L];
Put.Number[log, temp, [16, FALSE, TRUE, 0]];
Put.CR[log];
END;
csb[line].tranIOCB ← Inline.LowHalf[iocb];
DicentraInputOutput.Output[080H, @chan.r0]; -- Reset Tx CRC
DicentraInputOutput.Output[bytes[0], @chan.r8]; -- Send first data byte
END
ELSE
BEGIN
sendTail[line].next ← Inline.LowHalf[iocb];
IF csb[line].tranIOCB = NIL AND iocb.status = 0 THEN -- Rats, lost race
BEGIN
csb[line].tranIOCB ← Inline.LowHalf[iocb];
DicentraInputOutput.Output[080H, @chan.r0]; -- Reset Tx CRC
DicentraInputOutput.Output[bytes[0], @chan.r8]; -- Send first data byte
END;
END;
sendTail[line] ← iocb;
END;
QueueInput: PROCEDURE [line: Line, iocb: IOCB] =
BEGIN
iocb.status ← 0;
iocb.next ← NIL;
IF csb[line].recvIOCB = NIL THEN
BEGIN
csb[line].recvIOCB ← Inline.LowHalf[iocb]
END
ELSE
BEGIN
recvTail[line].next ← Inline.LowHalf[iocb];
END;
recvTail[line] ← iocb;
END;
-- Main Body
OthelloDefs.RegisterCommandProc[@commandProcessor];
END.