EthernetOneHeadDorado:
PROGRAM
IMPORTS Basics, DeviceCleanup, DoradoInputOutput, PrincOpsUtils, ProcessorFace
EXPORTS EthernetOneFace =
BEGIN OPEN EthernetOneFace;
OCSB: TYPE = LONG POINTER TO OutputControllerStatusBlock;
OutputControllerStatusBlock:
TYPE =
MACHINE
DEPENDENT
RECORD [
next: ShortIOCB,
interruptBit: WORD,
minPacketSpacing: CARDINAL,
words after here are unused by microcode
last: IOCB]; -- last IOCB on output queue, valid if next#noIOCB
ICSB: TYPE = LONG POINTER TO InputControllerStatusBlock;
InputControllerStatusBlock:
TYPE =
MACHINE
DEPENDENT
RECORD [
next: ShortIOCB,
interruptBit: WORD,
host: WORD, -- if >=0, old-style single-host receive microcode will be used.
missed: CARDINAL,
words after here (except "hosts") are unused by microcode
last: IOCB, -- last IOCB on input queue, valid if next#noIOCB
interstices:
ARRAY [6..30B)
OF
WORD←
NULL,
-- hosts start 30B beyond next
for compatibility with existing location of oCSB.
hosts: HostArray]; -- current set of hosts to which receiver code responds
IOCB: TYPE = LONG POINTER TO IOControlBlock;
ShortIOCB: TYPE = POINTER TO IOControlBlock;
IOControlBlock:
TYPE =
MACHINE
DEPENDENT
RECORD [
next: ShortIOCB,
completion: CompletionStatus,
used: CARDINAL, -- input only
load: CARDINAL, -- output only
length: CARDINAL,
buffer: LONG POINTER,
words after here are unused by microcode
direction: {input, output},
fill: [0..77777B] ← NULL];
CompletionStatus:
TYPE =
MACHINE
DEPENDENT
RECORD [
microcode (0: 0..7): {pending (0), done (1), bufferOverflow (2), loadOverflow (3)},
hardware (0: 8..15):
SELECT
OVERLAID *
FROM
input => [
rxCollision (0: 8..8),
fill1 (0: 9..9),
rxDataLate (0: 10..10),
fill2 (0: 11..11),
rxCRCError (0: 12..12),
fill3 (0: 13..14),
rxIncTrans (0: 15..15): BOOLEAN],
output => [
rxOn (0: 8..8),
txOn (0: 9..9),
loopBack (0: 10..10),
txCollision (0: 11..11),
noWakeups (0: 12..12),
txDataLate (0: 13..13),
singleStep (0: 14..14),
txFifoPE (0: 15..15): BOOLEAN],
ENDCASE];
multiHosts: CARDINAL = 177777B;
inputErrorMask: input CompletionStatus = [
microcode: pending,
hardware: input[rxCollision:
TRUE, fill1:
FALSE, rxDataLate:
TRUE, fill2:
FALSE,
rxCRCError: TRUE, fill3: FALSE, rxIncTrans: TRUE]];
outputErrorMask: output CompletionStatus = [
microcode: pending,
hardware: output[rxOn:
FALSE, txOn:
FALSE, loopBack:
FALSE, txCollision:
TRUE,
noWakeups: FALSE, txDataLate: TRUE, singleStep: FALSE, txFifoPE: TRUE]];
nullCompletion: CompletionStatus = LOOPHOLE[0];
Command: TYPE = RECORD [WORD];
enableInput: Command = [173377B];
enableOutput: Command = [047777B];
control: DoradoInputOutput.IOAddress = 16B;
noIOCB: ShortIOCB = LOOPHOLE[0];
This implementation has no concept of a "device", but something has to
be exported to EthernetOneFace (see below)
Device: TYPE = RECORD [WORD];
Shorten:
PROCEDURE [iocb:
IOCB]
RETURNS [ShortIOCB] =
INLINE
BEGIN
Maybe we should check to be sure that the high half is zero
RETURN[PrincOpsUtils.LowHalf[iocb]];
END;
EXPORTed TYPEs
DeviceHandle: PUBLIC TYPE = Device;
ControlBlockRecord: PUBLIC TYPE = IOControlBlock;
EXPORTed variables
nullDeviceHandle: PUBLIC DeviceHandle ← LOOPHOLE[123456B];
globalStateSize: PUBLIC CARDINAL ← 0;
controlBlockSize: PUBLIC CARDINAL ← SIZE[IOControlBlock];
hearSelf: PUBLIC BOOLEAN ← TRUE;
Non EXPORTed things. Note that all the state information lives in the CSBs.
oCSB: OCSB = LOOPHOLE[LONG[177610B]];
iCSB: ICSB = LOOPHOLE[LONG[177600B]];
QueueOutput:
PUBLIC
PROCEDURE [
device: Device, buffer: LONG POINTER, length: CARDINAL, cb: IOCB] =
BEGIN
cb^ ← [next: noIOCB, completion: nullCompletion, used: 0, load: 0, length: length,
buffer: buffer, direction: output];
IF oCSB.next # noIOCB THEN oCSB.last.next ← Shorten[cb];
IF oCSB.next = noIOCB
AND cb.completion = nullCompletion
THEN
BEGIN
oCSB.next ← Shorten[cb];
DoradoInputOutput.Output[enableOutput, control];
END;
oCSB.last ← cb;
END;
QueueInput:
PUBLIC
PROCEDURE [
device: Device, buffer: LONG POINTER, length: CARDINAL, cb: IOCB] =
BEGIN
cb^ ← [next: noIOCB, completion: nullCompletion, used: 0, load: 0, length: length,
buffer: buffer, direction: input];
IF iCSB.next # noIOCB THEN iCSB.last.next ← Shorten[cb];
IF iCSB.next = noIOCB
AND cb.completion = nullCompletion
THEN
iCSB.next ← Shorten[cb];
iCSB.last ← cb;
END;
GetStatus:
PUBLIC
PROCEDURE [cb:
IOCB]
RETURNS [status: Status] =
BEGIN OPEN cb.completion;
RETURN[
SELECT microcode
FROM
pending => pending,
done =>
SELECT cb.direction
FROM
input =>
IF PrincOpsUtils.
BITAND[cb.completion, inputErrorMask]=0
THEN ok
ELSE
SELECT
TRUE
FROM
rxCollision => otherError,
rxDataLate => overrun,
rxCRCError => IF rxIncTrans THEN crcAndBadAlignment ELSE crc,
rxIncTrans => badAlignmentButOkCrc,
ENDCASE => otherError,
output =>
IF PrincOpsUtils.
BITAND[cb.completion, outputErrorMask]=0
THEN ok
ELSE
SELECT
TRUE
FROM
txCollision => tooManyCollisions,
txDataLate => underrun,
txFifoPE => otherError,
ENDCASE => otherError,
ENDCASE => otherError,
bufferOverflow => packetTooLong,
loadOverflow => tooManyCollisions,
ENDCASE => otherError];
END;
GetRetries:
PUBLIC
PROCEDURE [cb:
IOCB]
RETURNS [
CARDINAL] =
BEGIN
RETURN[
SELECT cb.load
FROM
1 => 0,
3 => 1,
7 => 2,
17B => 3,
37B => 4,
77B => 5,
177B => 6,
377B => 7,
777B => 8,
1777B => 9,
3777B => 10,
7777B => 11,
17777B => 12,
37777B => 13,
77777B => 14,
177777B => 15,
ENDCASE => 16];
END;
GetPacketLength:
PUBLIC
PROCEDURE [cb:
IOCB]
RETURNS [
CARDINAL] =
BEGIN RETURN[cb.used]; END;
GetPacketsMissed:
PUBLIC
PROCEDURE [device: Device]
RETURNS [
CARDINAL] =
BEGIN RETURN[iCSB.missed]; END;
nonNullDeviceHandle: DeviceHandle = LOOPHOLE[22334]; -- # nullDeviceHandle
GetNextDevice:
PUBLIC
PROCEDURE [device: Device]
RETURNS [Device] =
BEGIN
RETURN[IF device=nullDeviceHandle THEN nonNullDeviceHandle ELSE nullDeviceHandle];
END;
GetEthernet1Address:
PUBLIC
PROCEDURE [device: Device]
RETURNS [net: [0..256), host: HostAddress] =
BEGIN
RETURN[net: 0, host: Basics.HighByte[DoradoInputOutput.Input[control]]];
END;
TurnOn:
PUBLIC
PROCEDURE [
device: Device, host: HostAddress, inInterrupt, outInterrupt: WORD,
globalState: GlobalStatePtr] =
BEGIN
DoradoInputOutput.ResetEther[];
iCSB.next ← noIOCB;
iCSB.interruptBit←inInterrupt;
iCSB.missed𡤀
iCSB.last←NIL;
If in multiHost mode, set all hosts true for promiscuous mode, or add specified
host to currently-accepted list.
IF iCSB.host#multiHosts THEN iCSB.host←host
ELSE IF host = 0 THEN iCSB.hosts←ALL[TRUE]
ELSE iCSB.hosts[host]←TRUE;
oCSB^ ← [next: noIOCB, interruptBit: outInterrupt, last:
NIL,
for now, always space output packets at least 500 microseconds apart
minPacketSpacing: 500/32];
DoradoInputOutput.Output[enableInput, control];
END;
TurnOff:
PUBLIC
PROCEDURE [device: Device] =
BEGIN DoradoInputOutput.ResetEther[]; END;
The next three procedure should probably be monitored, or be called from properly
locked places.
inputHosts is complete specification (including 0, if broadcasts are to be accepted),
of the hosts whose input packets the microcode will accept.
InputHosts:
PUBLIC
PROCEDURE [device: DeviceHandle,
inputHosts: LONG POINTER TO HostArray] = {
iCSB.hosts←inputHosts^;
iCSB.host←multiHosts; }; -- enable multicast host array
InputHost:
PUBLIC
PROCEDURE[device: DeviceHandle, host: HostAddress] = {
iCSB.host←host; }; -- disable multicast host array, specify host.
MulticastCapabilities:
PUBLIC
PROCEDURE[device: DeviceHandle]
RETURNS [ canDo: BOOLEAN, multicastsEnabled: BOOLEAN] = {
RETURN [ canDo: TRUE, multicastsEnabled: iCSB.host = multiHosts]; };