{ CCOTCIO.TEXT --------------------------------------------------------} { { CCOTCIO -- OMNINET Commands Unit for Corvus CONCEPT { { (c) Copyright 1983 Corvus Systems, Inc. { San Jose, California { { All Rights Reserved { { v 1.0 06-20-83 LEF Original unit (from PB prototype) { v 1.1 09-14-83 LEF Add uPntr to TCparmBlk { Modifications to TCgetCounts { v 1.2 02-02-84 KB Changes to not busy wait on interrupt { routine in echo and poke. { { This UNIT contains functions and procedures which construct { Omninet commands and send them to the Omninet Transporter { driver. It also defines constants and data structures which { are useful when programming an Omninet application. Hopefully, { a Pascal programmer can use this UNIT without knowing the { details of the Transporter interface ... { {!CC}{ Corvus CONCEPT version {----------------------------------------------------------------------} {$R-} UNIT ccOTCio; INTERFACE CONST TCvers = '1.2'; TCvrs64 = 100; { $64 which is one of the Transporter versions } TCvrs8A = 138; { $8A which is one of the Transporter versions } {!CC} TCbyteLO = -128; TCbyteHI = 127; { define byte value limits } { Transporter Driver Return Codes } TCnotRdy = 21; { Transporter not ready } TCqueued = 30; { driver queued command warning } TCentUse = 52; { entry in use error } TCinvFnc = 56; { invalid function code error } { Indexes into Transporter counters } TCCmiss = 1; { missed packets (number of ADLC interrrupts) } TCCcoll = 2; { collision AVOIDANCE interrrupts } TCCintErr = 3; { unknown interrupts inside Transporter } TCCrcvErr = 4; { ADLC receive errors (CRC, overrun, etc. } TCCmaxCnt = 4; { number of Omninet transporter counters } {$P} { Transporter Return Codes } Waiting = 255; { $FF } CmdAcpt = 254; { $FE command accepted } Echoed = 192; { $C0 echo command was successful } GaveUp = 128; { $80 aborted send command after MaxRetry tries } TooLong = 129; { $81 last message sent too long for receiver } NoSockt = 130; { $82 sent to an uninitialized socket } HdrErr = 131; { $83 sender hdr length did not match receiver } BadSock = 132; { $84 invalid socket number } Inuse = 133; { $85 tried to setup receive on active socket } BadDest = 134; { $86 sent to an invalid host number } OkCode = 0; { success!!! } NoTrans = -1; { indicates that we are unable to communicate } { with Transporter - StrobeCmd failed } TYPE {!CC} TCcntBuf = array [1..TCCmaxCnt] of longint; TChosts = 0..63; TChostSet = set of TChosts; pTCbuffer = ^TCbuffer; {!CC} TCbuffer = array [0..32765] of TCbyteLO..TCbyteHI; pTCrsltRcd = ^TCrsltRcd; TCrsltRcd = record Rcode: TCbyteLO..TCbyteHI; Sourc: TCbyteLO..TCbyteHI; Len: integer; {!CC} UCdta: array [0..255] of TCbyteLO..TCbyteHI; end; pTComniCmd = ^TComniCmd; TComniCmd = record case integer of 1: (p: record RP: pTCrsltRcd; DP: pTCbuffer; LN: integer; HL: integer; end); {!CC} 2: (a: array [1..12] of TCbyteLO..TCbyteHI); end; {$P} pTCparmBlk = ^TCparmBlk; TCparmBlk = record {DRV.OMNINET} pComd: pTComniCmd; { Omninet command pointer unit } {DRV.OMNINET} pProc: pTCbuffer; { interrupt procedure ptr user } {DRV.OMNINET} pPblk: pTCparmBlk; { parameter block pointer both } pBuff: pTCbuffer; { data buffer pointer user } pRslt: pTCrsltRcd; { result record pointer both } oComd: TComniCmd; { Omninet command unit } rDone: boolean; { request complete of TRUE both } rStat: integer; { request status both } rRslt: integer; { request result code both } { {1.1} { uPntr is not used by this unit and may be {1.1} { used for any purpose by the user. uPntr is {1.1} { not documented in 09-83 documentation. {1.1} { {1.1} uPntr: longint; { user pointer user } end; VAR TCtrnVrsn: integer; { Transporter version number } TCcounts: TCcntBuf; { Transporter counters } TCadlc: integer; { status of ADLC at last receive error } TCintPtr: pTCbuffer; { interrupt processing routine pointer } {!CC} TChaveDrv: boolean; { TRUE if using Omninet driver } {----------------------------------------------------------------------} {----------------------------------------------------------------------} PROCEDURE ccOTCioInit; {----------------------------------------------------------------------} PROCEDURE ccOTCioTerm; {----------------------------------------------------------------------} PROCEDURE TCinitBlk (var p: TCparmBlk; { request parameter block } r: pTCrsltRcd; { result record pointer } b: pTCbuffer; { data buffer pointer } i: pTCbuffer); { interrupt processing pointer } {----------------------------------------------------------------------} PROCEDURE TCinterrupt (q: integer; { queue flag } s: integer; { driver status } r: pTCrsltRcd; { result record pointer } b: pTCbuffer; { data buffer pointer } p: pTCparmBlk); { request parameter block ptr } {----------------------------------------------------------------------} FUNCTION TCsetRecv (var tcp: TCparmBlk; { request parameter block } sck: integer; { socket number } dln: integer; { data length } hln: integer): { message header length } integer; { returns driver status } {----------------------------------------------------------------------} {$P} {----------------------------------------------------------------------} FUNCTION TCsndMesg (var tcp: TCparmBlk; { request parameter block } sck: integer; { socket number } dln: integer; { data length } hln: integer; { message header length } dst: integer): { destination host number } integer; { returns driver status } {----------------------------------------------------------------------} FUNCTION TCendRecv (sck: integer; { socket number } var res: integer): { result code } integer; { returns driver status } {----------------------------------------------------------------------} FUNCTION TCinitTrans (var hst: integer): { our host number } integer; { returns driver status } {----------------------------------------------------------------------} FUNCTION TCwhoAmI (var hst: integer): { our host number } integer; { returns driver status } {----------------------------------------------------------------------} FUNCTION TCechoTrans (dst: integer; { destination host number } var res: integer): { command result status } integer; { returns driver status } {----------------------------------------------------------------------} FUNCTION TCpeekTrans (adr: integer; { transporter RAM address } var val: integer): { transporter RAM value } integer; { returns driver status } {----------------------------------------------------------------------} FUNCTION TCpokeTrans (adr: integer; { transporter RAM address } val: integer; { transporter RAM value } var res: integer): { command result status } integer; { returns driver status } {----------------------------------------------------------------------} FUNCTION TCsetRetry (cnt: integer): { retry count } integer; { returns driver status } {----------------------------------------------------------------------} FUNCTION TCnetMap (var map: TChostSet): { set of host numbers } integer; { returns driver status } {----------------------------------------------------------------------} PROCEDURE TCgetCounts; {----------------------------------------------------------------------} IMPLEMENTATION {$P} CONST {!CC} RdyAdr = $30F7F; { addr of VIA register A, for OMNINET ready } {!CC} StrAdr = $30FA1; { addr of Transporter register } MaxRetry = 10; { default number of retries } { Transporter Opcodes } RecvOp = 240; { $F0 - SETUPRECV opcode } SendOp = 064; { $40 - SENDMSG opcode } InitOp = 032; { $20 - INIT opcode } EndOp = 016; { $10 - ENDRECV opcode } DebOp = 008; { $08 - PEEK/POKE opcode } EchoOp = 002; { $02 - ECHOCMD opcode } WhoOp = 001; { $01 - WHOAMI opcode } PeekOp = 000; { $00 } PokeOp = 255; { $FF } { offsets into command record for byte fields } crOPCD = 1; { opcode } crSOCK = 5; { socket number } crEDST = 5; { destination for echo commands } crPAD1 = 5; { Transporter address to peek or poke - MSB } crPAD2 = 6; { Transporter address to peek or poke - LSB } crPEPO = 7; { peek/poke designator for Deb commands } crPVAL = 8; { poke value } crHLEN = 11; { header length } crDEST = 12; { destination for sends } { Hexadecimal equivalents } h80 = 128; { $80 } h90 = 144; { $90 } hA0 = 160; { $A0 } hB0 = 176; { $B0 } hE1 = 225; { $E1 } hF800 = -2048; { $F800 } TYPE byte = TCbyteLO..TCbyteHI; VAR RecvSet: array [1..4] of boolean; { active sockets for receive } otcPB: TCparmBlk; { request parameter block for this unit } otcRR: TCrsltRcd; { result record for this unit } RRptr: pTCrsltRcd; { result record pointer for this unit } {!CC} OmniUnit: integer; { Omninet transporter driver unit number } {$P} FUNCTION OSomniDv: integer; EXTERNAL; { TranSock ------------------------------------------------------------} { Force a socket number to be in the proper format for the Transporter {----------------------------------------------------------------------} FUNCTION TranSock (sck: integer): integer; begin case sck of 1,h80: TranSock := ord (h80); 2,h90: TranSock := ord (h90); 3,hA0: TranSock := ord (hA0); 4,hB0: TranSock := ord (hB0); end; {case} end; { DrvrSock ------------------------------------------------------------} { Force a socket number to be in the proper format for the driver {----------------------------------------------------------------------} FUNCTION DrvrSock (sck: integer): integer; begin case sck of 1,h80: DrvrSock := 1; 2,h90: DrvrSock := 2; 3,hA0: DrvrSock := 3; 4,hB0: DrvrSock := 4; end; {case} end; {$P} { ExecCmd -------------------------------------------------------------} { ExecCmd attempts to invoke a transporter command by calling the { Transporter driver (if available) .... {----------------------------------------------------------------------} FUNCTION ExecCmd (cmd: integer; var tcp: TCparmBlk; fnc: integer): integer; var sr,b,i,j: integer; {1.2} { ExecCmd.StrobeCmd ---------------------------------------------------} { Strobe command address to Transporter { (used if OMNINET driver not available) {----------------------------------------------------------------------} {!CC} FUNCTION StrobeCmd (CmdAdr: pTCbuffer): boolean; {!CC} var i: integer; isready: boolean; StrAddr: ^byte; { ExecCmd.StrobeCmd.Ready ---------------------------------------------} {----------------------------------------------------------------------} {!CC} FUNCTION Ready: boolean; {!CC} var i: byte; j: integer; RdyAddr: ^byte; {!CC} begin {!CC} RdyAddr := pointer (RdyAdr); j := 10000; {!CC} repeat {!CC} i := RdyAddr^; j := j-1; {!CC} until (j = 0) or (ODD (i)); {!CC} Ready := ODD (i); {!CC} end; { ExecCmd.StrobeCmd ---------------------------------------------------} {----------------------------------------------------------------------} {!CC} begin {!CC} StrAddr := pointer (StrAdr); i := 1; {!CC} repeat {!CC} isready := ready; {!CC} if isready then StrAddr^ := CmdAdr^[i]; {!CC} i := i + 1 {!CC} until (i > 3) or (NOT isready); {!CC} StrobeCmd := isready; {!CC} end; {$P} { ExecCmd -------------------------------------------------------------} {----------------------------------------------------------------------} begin tcp.rDone := FALSE; with tcp.pComd^ do begin p.RP := tcp.pRslt; { set command record result pointer } a[crOPCD] := ord(cmd); { set command record command } p.RP^.Rcode := -1; { set waiting status {1.2} end; {1.2} {!CC} if TChaveDrv {!CC} then begin {!CC} UnitStatus (OmniUnit,tcp,fnc); {!CC} ExecCmd := IOresult; {!CC} end {!CC} else begin {!CC} if StrobeCmd (@tcp.pComd) then begin repeat until (tcp.pRslt^.Rcode <> -1); {1.2} { j := 10000; } { repeat } { j := j - 1 } { until (tcp.pComd^.p.RP^.Rcode <> -1) } { or (j = 0); } end; tcp.rDone := TRUE; tcp.rStat := 0; tcp.rRslt := tcp.pRslt^.Rcode; {1.2} {!CC} if tcp.rRslt < 0 then tcp.rRslt := tcp.rRslt+256; ExecCmd := 0; {!CC} end; end; {$P} { BusyWait ------------------------------------------------------------} { Wait until the transporter command is complete { Returns status from driver interrupt handler {----------------------------------------------------------------------} FUNCTION BusyWait (var tcp: TCparmBlk): integer; begin with tcp do begin while NOT rDone do {nothing}; BusyWait := rStat; end; end; { RCbusy ----------------------------------------------------------{1.2} { Wait until the transporter command is complete {1.2} { Returns status from driver and transporter result code {1.2} {------------------------------------------------------------------{1.2} FUNCTION RCbusy (var tcp: TCparmBlk; var r : integer): integer; {1.2} begin {1.2} with tcp do begin {1.2} rStat := r; {1.2} if (r = okcode) or (r = TCqueued) {1.2} then begin {1.2} r := okcode; {if queued then is ok} {1.2} while pRslt^.rCode = -1 do; {1.2} rRslt := pRslt^.Rcode; {1.2} {!CC} if rRslt < 0 then rRslt := rRslt + 256; {1.2} rDone := TRUE; {1.2} end {1.2} else rDone := TRUE; {1.2} RCbusy := rRslt; {1.2} end; {1.2} end; {1.2} { GetResult -----------------------------------------------------------} {----------------------------------------------------------------------} FUNCTION GetResult (var tcp: TCparmBlk; ior: integer): integer; begin if (ior = 0) OR (ior = TCqueued) then GetResult := BusyWait (tcp) else GetResult := ior; end; {$P} { TCinterrupt ---------------------------------------------------------} { Interrupt procedure used by this UNIT {----------------------------------------------------------------------} PROCEDURE TCinterrupt {(q: integer; { queue flag ) s: integer; { driver status ) r: pTCrsltRcd; { result record pointer ) b: pTCbuffer; { data buffer pointer ) p: pTCparmBlk)}; { request parameter block } begin with p^ do begin rStat := s; if (q = 0) OR (s <> 0) then begin rDone := TRUE; rRslt := r^.Rcode; {1.2} {!CC} if rRslt < 0 then rRslt := rRslt+256; end; end; end; { TCinitBlk -----------------------------------------------------------} { Initialize request control block {----------------------------------------------------------------------} PROCEDURE TCinitBlk {(var p: TCparmBlk; { request parameter block ) r: pTCrsltRcd; { result record pointer ) b: pTCbuffer; { data buffer pointer ) i: pTCbuffer)};{ interrupt processing ptr } var n: integer; begin with p do begin {!CC} pComd := @oComd; {!CC} pProc := i; {!CC} pPblk := @p; {!CC} pBuff := b; {!CC} pRslt := r; rDone := FALSE; rStat := 0; rRslt := 255; for n := 1 to 12 do oComd.a[n] := 0; end; end; {$P} { TCsetRetry ----------------------------------------------------------} {----------------------------------------------------------------------} FUNCTION TCsetRetry {(cnt: integer): { retry count ) integer}; { returns driver status } var res: integer; begin TCsetRetry := TCpokeTrans (hE1,cnt,res); end; { TCnetMap ------------------------------------------------------------} { Returns map which is a set of all of the active hosts on the local { Omninet. If the integer returned is not zero, a driver error { occurred while attempting to echo. The Max retry parameter inside { the Transporter is set to 1 to minimize the impact of all of the { unsuccessful echoes. {----------------------------------------------------------------------} FUNCTION TCnetMap {(var map: TChostSet):{ set of host numbers ) integer}; { returns driver status } var i,x,res: integer; begin x := TCsetRetry (1); x := 0; map := []; for i := 0 to 63 do if x = 0 then begin x := TCechoTrans (i,res); if res = Echoed then map := map+[i]; end; TCnetMap := x; x := TCsetRetry (MaxRetry); end; {$P} { TCgetCounts ---------------------------------------------------------} { Reads the Transporter counters and sets them to 0 {----------------------------------------------------------------------} PROCEDURE TCgetCounts; var r,x,i,val: integer; begin if TCtrnVrsn < TCvrs8A then begin { get counters from T6.4 locations... } for i := 1 to 3 do begin x := TCpeekTrans (229+i,val); TCcounts[i] := TCcounts[i] + val; x := TCpokeTrans (229+i,0,r); end; x := TCpeekTrans (233,TCadlc); x := TCpeekTrans (234,val); TCcounts[TCCrcvErr] := TCcounts[TCCrcvErr] + val; x := TCpokeTrans (234,0,r); end else begin { get counters from T8.A/T9.B locations... } {1.2} x := TCpeekTrans (229,val); {1.2} TCcounts[TCCmiss] := TCcounts[TCCmiss] + val; {1.2} x := TCpokeTrans (229,0,r); {1.2} x := TCpeekTrans (230,val); TCcounts[TCCcoll] := TCcounts[TCCcoll] + val; x := TCpokeTrans (230,0,r); x := TCpeekTrans (231,TCadlc); if TCadlc <> 0 then begin TCcounts[TCCrcvErr] := TCcounts[TCCrcvErr]+1; x := TCpokeTrans (231,0,r); end; end; end; {$P} {----------------------------------------------------------------------} { { The following procedures construct Omninet commands and sends { them to the Transporter. { {----------------------------------------------------------------------} { TCinitTrans ---------------------------------------------------------} { Initialize Transporter and determine our host number { Note: TCinitTrans must not be called from an interrupt routine. {1.2} {----------------------------------------------------------------------} FUNCTION TCinitTrans {(var hst: integer): { our host number ) integer}; { returns driver status } var p: pTCbuffer; r: integer; begin TCinitBlk (otcPB,RRptr,NIL,TCintPtr); {!CC} p := NIL; {!CC} if TChaveDrv then { clear out driver interrupt service table } {!CC} for r := 1 to 4 do UnitStatus (OmniUnit,p,128+r); r := ExecCmd (InitOp,otcPB,0); r := GetResult (otcPB,r); hst := otcPB.rRslt; TCinitTrans := r; end; { TCwhoAmI ------------------------------------------------------------} { Find out what our host number is { Note: TCwhoAmI must not be called from an interrupt routine. {1.2} {----------------------------------------------------------------------} FUNCTION TCwhoAmI {(var hst: integer): { our host number ) integer}; { returns driver status } var r: integer; begin TCinitBlk (otcPB,RRptr,NIL,TCintPtr); r := ExecCmd (WhoOp,otcPB,0); r := GetResult (otcPB,r); hst := otcPB.rRslt; TCwhoAmI := r; end; {$P} { TCechoTrans ---------------------------------------------------------} { Echo to specified Transporter {----------------------------------------------------------------------} FUNCTION TCechoTrans {(dst: integer; { destination host number ) var res: integer): { command result status ) integer}; { returns driver status } var r: integer; begin TCinitBlk (otcPB,RRptr,NIL,NIL); {1.2} otcPB.pComd^.a[crEDST] := dst; r := ExecCmd (EchoOp,otcPB,0); res := RCbusy (otcPB,r); {1.2} TCechoTrans := r; end; { TCsndMesg -----------------------------------------------------------} { Send a message to another host... {----------------------------------------------------------------------} FUNCTION TCsndMesg {(var tcp: TCparmBlk; { request parameter block ) sck: integer; { socket number ) dln: integer; { data length ) hln: integer; { message header length ) dst: integer): { destination host number ) integer}; { returns driver status } var WaitForComp: boolean; r: integer; begin tcp.rDone := FALSE; tcp.rRslt := 255; WaitForComp := (tcp.pProc = NIL); {1.2} with tcp.pComd^ do begin p.DP := tcp.pBuff; p.RP := tcp.pRslt; p.LN := dln; a[crSOCK] := ord(TranSock(sck)); a[crHLEN] := hln; a[crDEST] := dst; end; r := ExecCmd (SendOp,tcp,0); if WaitForComp then tcp.rRslt := RCbusy (tcp,r); {1.2} TCsndMesg := r; end; {$P} { TCsetRecv -----------------------------------------------------------} { Assembles a receive command and sends it to the transporter. Will { not return until the command has been accepted... {----------------------------------------------------------------------} FUNCTION TCsetRecv {(var tcp: TCparmBlk; { request parameter block ) sck: integer; { socket number ) dln: integer; { data length ) hln: integer): { message header length ) integer}; { returns driver status } var r: integer; begin with tcp.pComd^ do begin p.DP := tcp.pBuff; p.RP := tcp.pRslt; p.LN := dln; a[crSOCK] := ord(TranSock(sck)); a[crHLEN] := hln; end; { driver always waits for the result to change from FF to FE } { before returning. This was done because T6.4 transporters } { do not generate that interrupt. } r := ExecCmd (RecvOp,tcp,DrvrSock(sck)); with tcp do begin rStat := r; rRslt := pRslt^.Rcode; {1.2} {!CC} if rRslt < 0 then rRslt := rRslt+256; if rRslt = CmdAcpt then begin rDone := FALSE; RecvSet[DrvrSock(sck)] := TRUE; end else rDone := TRUE; end; TCsetRecv := r; end; { TCendRecv -----------------------------------------------------------} { reset a setup receive {----------------------------------------------------------------------} FUNCTION TCendRecv {(sck: integer; { socket number ) var res: integer): { result code ) integer}; { returns driver status } var r: integer; begin TCinitBlk (otcPB,RRptr,NIL,NIL); {1.2} otcPB.pComd^.a[crSOCK] := ord(TranSock(sck)); r := ExecCmd (EndOp,otcPB,128+DrvrSock(sck)); res := RCbusy (otcPB,r); {1.2} RecvSet[DrvrSock(sck)] := FALSE; TCendRecv := r; end; {$P} { TCpeekTrans ---------------------------------------------------------} { Read from the RAM inside the Transporter {----------------------------------------------------------------------} FUNCTION TCpeekTrans {(adr: integer; { transporter RAM address ) var val: integer): { transporter RAM value ) integer}; { returns driver status } var r: integer; begin TCinitBlk (otcPB,RRptr,NIL,NIL); {1.2} with otcPB.pComd^ do begin a[crPEPO] := ord (PeekOp); a[crPAD1] := ord (adr DIV 256); a[crPAD2] := ord (adr MOD 256); end; r := ExecCmd (DebOp,otcPB,0); with otcPB do begin rStat := r; rRslt := pRslt^.Rcode; {1.2} {!CC} if rRslt < 0 then rRslt := rRslt+256; end; val := otcPB.rRslt; TCpeekTrans := r; end; { TCpokeTrans ---------------------------------------------------------} { Write into the Transporter's RAM {----------------------------------------------------------------------} FUNCTION TCpokeTrans {(adr: integer; { transporter RAM address ) val: integer; { transporter RAM value ) var res: integer): { command result status ) integer}; { returns driver status } var r: integer; begin TCinitBlk (otcPB,RRptr,NIL,NIL); {1.2} with otcPB.pComd^ do begin a[crPAD1] := ord (adr DIV 256); a[crPAD2] := ord (adr MOD 256); a[crPEPO] := ord (PokeOp); a[crPVAL] := ord (val); end; r := ExecCmd (DebOp,otcPB,0); {1.2} res := RCbusy (otcPB,r); {1.2} TCpokeTrans := r; end; {$P} { ccOTCioInit ---------------------------------------------------------} { Initialize ccOTCio unit. This procedure could also initialize the { Transporter and poke the proper values for the Transporter parameters { which have values other than the default.................. {----------------------------------------------------------------------} PROCEDURE ccOTCioInit; var i: integer; b: boolean; begin {!CC} TCintPtr := @TCinterrupt; RRptr := @otcRR; OmniUnit := OSomniDv; {!CC} b := UnitBusy (OmniUnit); TChaveDrv := IOresult <> 0; for i := 1 to 4 do RecvSet[i] := FALSE; for i := 1 to TCCmaxCnt do TCcounts[i] := 0; i := TCpeekTrans (hF800,TCtrnVrsn); end; { ccOTCioTerm ---------------------------------------------------------} {----------------------------------------------------------------------} PROCEDURE ccOTCioTerm; var i,r,s: integer; begin for i := 1 to 4 do if RecvSet[i] then r := TCendRecv (i,s); end; end.