// Sun-Proms.bcpl -- Sun 3 MB Ethernet board Proms
// Last modified February 10, 1984  4:00 PM by Boggs
// Sun-Rev-B & CadLink-Rev-0 boards require a hardware change: (TX Multibus/Fifo interlock)
//  isolate U308 pin 4; connect it to U308 pin 25 (P.Read/)
// Other known hardware fixes:
//  isolate U511 pin 4; connect it to U111 pin 12 (T.AbortAck/)
//  isolate U513 pin 2; connect it to U317 pin 9 (TX.Jam)

external [ Ws; OpenFile; Puts; Closes; Allocate; Free; sysZone ]

static [ memory; mbFile ]

structure String [ length byte; char↑1,1 byte ]

manifest [ high = 1; low = 0 ]

//-----------------------------------------------------------------------------------------
let SunProms() be
//-----------------------------------------------------------------------------------------
[
mbFile = OpenFile("Sun-Proms.mb")

DoMemory("TX", 256, 8, TX)
DoMemory("NewTX", 256, 8, NewTX)
DoMemory("Ctrl", 32, 40, Ctrl)

Puts(mbFile, 0)  //0 = end of file
Closes(mbFile)
]

//-----------------------------------------------------------------------------------------
and DoMemory(name, nAddr, nData, Proc) be
//-----------------------------------------------------------------------------------------
// nAddr is number of addresses
// nData is number of output bits
[
Ws("*N"); Ws(name)

Puts(mbFile, 4)  //4 = define memory
memory = memory +1
Puts(mbFile, memory)
Puts(mbFile, nData)
if name>>String.length gr 1 then
   for i = 1 to name>>String.length-1 by 2 do
      Puts(mbFile, name>>String.char↑i lshift 8 + name>>String.char↑(i+1))
Puts(mbFile, (name>>String.length & 1) eq 0? 0,
 name>>String.char↑(name>>String.length) lshift 8)

Puts(mbFile, 2)  //2 = set current memory
Puts(mbFile, memory)
Puts(mbFile, 0)  //location counter

let data = Allocate(sysZone, (nData+15)/16)
for addr = 0 to nAddr-1 do
   [
   Puts(mbFile, 1)  //1 = memory contents
   Puts(mbFile, 0)  //source line number
   Proc(addr, data)
   for i = 0 to (nData+15)/16 -1 do Puts(mbFile, data!i)
   ]
Free(sysZone, data)
]

//-----------------------------------------------------------------------------------------
and TX(addr, data) be
//-----------------------------------------------------------------------------------------
[
structure Addr:
   [
   blank bit 8
   state bit 2
   timeout bit
   inhibit bit
   ready bit
   wrd bit
   jam bit
   carrier bit
   ]

structure Data:
   [
   nextState bit 2
   notIdle bit
   idle bit
   blank bit
   load bit
   notGo bit
   go bit
   blank bit 8
   ]

manifest
   [
   stateIdle = 0
   stateData = 1
   stateCRC = 2
   ]

let state = addr<<Addr.state
let timeout = addr<<Addr.timeout eq high
let inhibit = addr<<Addr.inhibit eq high
let ready = addr<<Addr.ready eq high
let wrd = addr<<Addr.wrd eq high
let jam = addr<<Addr.jam eq high
let carrier = addr<<Addr.carrier eq high

let nextState = state
let idle, load, go = false, false, false

switchon state into
   [
   case stateIdle:
      [
      if not ready % carrier then idle = true
      if ready & not carrier then nextState = stateData //aquire Ether
      endcase
      ]
   case stateData:
      [
      go = not jam
      if not jam & wrd & ready then load = true
      if wrd & inhibit then nextState = stateCRC	//end of data
      if wrd & not ready then nextState = stateIdle	//data late
      if jam % timeout then nextState = stateIdle	//catastrophe
      endcase
      ]
   case stateCRC:
      [
      go = not jam
      if wrd then nextState = stateIdle		//end of CRC
      if jam % timeout then nextState = stateIdle	//catastrophe
      endcase
      ]
   default: nextState = stateIdle
   ]

data>>Data.nextState = nextState
data>>Data.notIdle = idle? low, high
data>>Data.idle = idle? high, low
data>>Data.load = load? high, low
data>>Data.notGo = go? low, high
data>>Data.go = go? high, low
]

//-----------------------------------------------------------------------------------------
and NewTX(addr, data) be
//-----------------------------------------------------------------------------------------
[
structure Addr:
   [
   blank bit 8
   state bit 2
   timeout bit
   inhibit bit
   ready bit
   wrd bit
   jam bit
   carrier bit
   ]

structure Data:
   [
   nextState bit 2
   notIdle bit
   idle bit
   done bit		//low true
   load bit
   abrt bit		//low true
   go bit
   blank bit 8
   ]

manifest
   [
   stateIdle = 0
   stateData = 1
   stateCRC = 2
   ]

let state = addr<<Addr.state
let timeout = addr<<Addr.timeout eq high
let inhibit = addr<<Addr.inhibit eq high
let ready = addr<<Addr.ready eq high
let wrd = addr<<Addr.wrd eq high
let jam = addr<<Addr.jam eq high
let carrier = addr<<Addr.carrier eq high

let nextState = state
let idle, load, go = false, false, false

switchon state into
   [
   case stateIdle:
      [
      if ready & not carrier & not inhibit then nextState = stateData //aquire Ether
      endcase
      ]
   case stateData:
      [
      go = not jam
      if not jam & not inhibit & wrd & ready then load = true
      if wrd & inhibit then nextState = stateCRC	//end of data
      if wrd & not ready then nextState = stateIdle	//data late
      if jam % timeout then nextState = stateIdle	//catastrophe
      endcase
      ]
   case stateCRC:
      [
      go = not jam
      if wrd then nextState = stateIdle		//end of CRC
      if jam % timeout then nextState = stateIdle	//catastrophe
      endcase
      ]
   default: nextState = stateIdle
   ]

data>>Data.nextState = nextState
data>>Data.notIdle = nextState ne stateIdle? low, high
data>>Data.idle = nextState eq stateIdle? high, low
data>>Data.done = (nextState eq stateIdle & not jam)? low, high
data>>Data.load = load? high, low
data>>Data.abrt = jam? low, high
data>>Data.go = go? high, low
]

//-----------------------------------------------------------------------------------------
and Ctrl(addr, data) be
//-----------------------------------------------------------------------------------------
[
structure Addr:
   [
   blank bit 11
   Instr bit 5
   ]

structure Data:
   [
   BufWE bit
   RBufCE bit		//low true
   TBufCE bit		//low true
   CRead bit		//low true
   PWrite bit		//low true
   PRead bit		//low true
   TWrite bit		//low true
   RRead bit		//low true
   RxOverflow bit
   TxTimeout bit
   RxInterrupt bit
   QEmpty bit
   Ack bit		//low true
   Resume bit
   CarryIn bit
   DestHigh bit
   DestLow bit 2
   Function bit 3
   Source bit 3
   BReg bit 4
   AReg bit 4
   Branch bit 3
   Next bit 5		//msb is low true
   blank bit 8
   ]

manifest
   [
   // instruction addresses
   Init1 = 0
   Init2 = 1
   Init3 = 2
   RxEnd1 = 3
   RxEnd2 = 4
   RxEnd3 = 5
   RxEnd4 = 6
   RxReady1 = 7
   RxReady2 = 10b
   PortReadReq1 = 11b
   PortReadReq2 = 12b
   PortWriteReq1 = 13b
   PortStart = 20b
   PortStart1 = 14b
   PortStart2 = 15b
   PortStart3 = 21b
   PortStart4 = 16b
   TxStart = 17b
   TxAbort1 = 22b
   TxTimer = 23b
   TxTimer1 = 24b
   TxTimer2 = 25b
   TxTimer3 = 26b
   TxTimer4 = 27b

   // Startup addresses
   RxReady = 30b
   RxEnd = 31b
   TxReady = 32b
   TxAbort = 33b
   PortWriteReq = 34b
   PortReadReq = 35b
   Init = 36b
   Idle = 37b

   // Register names
   spare = 0
   rBase = 1
   rGet = 2
   rPut = 3
   count = 4
   tBase = 5
   tGet = 6
   tPut = 7
   retry = 8
   timer = 9
   random = 10

   // Branch Condition Codes (CC)
   Never = 0
   NonZero = 1
   Zero = 2
   NoCarry8 = 3
   Positive = 4
   Negative = 5
   Equal = 6
   Always = 7

   // 2901 Functions
   RplusS = 0
   SminusR = 1
   RminusS = 2
   RorS = 3
   RandS = 4
   NotRandS = 5
   RxorS = 6
   RxnorS = 7
   
   // 2901 Sources
   AQ = 0
   AB = 1
   ZQ = 2
   ZB = 3
   ZA = 4
   DA = 5
   DQ = 6
   DZ = 7

   // 2901 Destinations
   QReg = 0
   Nop = 1
   RamA = 2
   RamF = 3
   RamQD = 4
   RamD = 5
   RamQU = 6
   RamU = 7
   ]

let Next, Branch = 0, Never
let CarryIn, Function, Source = nil, nil, nil
let AReg, BReg, Destination = nil, nil, nil
let Resume, Ack, QEmpty = false, false, false
let RxInterrupt, TxTimeout, RxOverflow = false, false, false
let RRead, TWrite, PRead, PWrite, CRead = false, false, false, false, false
let TBufCE, RBufCE, BufWE = false, false, false

switchon addr<<Addr.Instr into
   [
   case Init:		//tGet ← -1
      [
      CarryIn = low; Function = RxnorS; Source = AB
      AReg = tGet; BReg = tGet; Destination = RamF
      Next = Init1
      endcase
      ]
   case Init1:		//tPut ← 0
      [
      CarryIn = low; Function = RandS; Source = ZB
      AReg = spare; BReg = tPut; Destination = RamF
      Next = Init2
      endcase
      ]
   case Init2:		//rGet ← 0
      [
      CarryIn = low; Function = RandS; Source = ZB
      AReg = spare; BReg = rGet; Destination = RamF
      Next = Init3
      endcase
      ]
   case Init3:		//rPut ← 0; Goto(RxEnd1)
      [
      CarryIn = low; Function = RandS; Source = ZB
      AReg = spare; BReg = rPut; Destination = RamF
      Next = RxEnd1
      endcase
      ]

   case RxEnd:		//Test(rPut-rGet) mod 2↑11; SetRxOverflow(EQ); Return(EQ); Ack
      [
      CarryIn = low; Function = RminusS; Source = AB
      AReg = rPut; BReg = rGet; Destination = Nop
      RxOverflow, Ack = true, true
      Branch = Equal
      Next = RxEnd1
      endcase
      ]
   case RxEnd1:		//Latch ← Count; Count ← 0
      [
      CarryIn = low; Function = RandS; Source = ZB
      AReg = count; BReg = count; Destination = RamA
      Next = RxEnd2
      endcase
      ]
   case RxEnd2:		//RxBuf[rBase] ← Latch
      [
      CarryIn = low; Function = RorS; Source = ZB
      AReg = spare; BReg = rBase; Destination = Nop
      RBufCE, BufWE, CRead = true, true, true
      Next = RxEnd3
      endcase
      ]
   case RxEnd3:		//rBase ← rPut; RxBuf[rPut] ← Latch; QEmpty
      [
      CarryIn = low; Function = RorS; Source = ZA
      AReg = rPut; BReg = rBase; Destination = RamF
      RBufCE, BufWE, CRead = true, true, true
      QEmpty = true
      Next = RxEnd4
      endcase
      ]
   case RxEnd4:		//rPut ← rPut+1; Return
      [
      CarryIn = high; Function = RplusS; Source = ZB
      AReg = spare; BReg = rPut; Destination = RamF
      Branch = Always
      endcase
      ]

   case RxReady:	//Test(rPut-rGet) mod 2↑11; SetRxOverflow(EQ); Return(EQ); Ack
      [
      CarryIn = low; Function = RminusS; Source = AB
      AReg = rPut; BReg = rGet; Destination = Nop
      RxOverflow, Ack = true, true
      Branch = Equal
      Next = RxReady1
      endcase
      ]
   case RxReady1:	//count ← count+1
      [
      CarryIn = high; Function = RplusS; Source = ZB
      AReg = spare; BReg = count; Destination = RamF
      Next = RxReady2
      endcase
      ]
   case RxReady2:	//RxBuf[rPut] ← Rx; rPut ← rPut+1; Return
      [
      CarryIn = high; Function = RplusS; Source = ZA
      AReg = rPut; BReg = rPut; Destination = RamA
      RBufCE, BufWE, RRead = true, true, true
      Branch = Always
      endcase
      ]

   case PortReadReq:	//Port ← RxBuf[rGet]
      [
      CarryIn = low; Function = RorS; Source = ZB
      AReg = spare; BReg = rGet; Destination = Nop
      RBufCE, PWrite = true, true
      Next = PortReadReq1
      endcase
      ]
   case PortReadReq1:	//Test(rGet-Rbase) mod 2↑11; ClearRxInterrupt(EQ); Return(EQ)
      [
      CarryIn = low; Function = RminusS; Source = AB
      AReg = rGet; BReg = rBase; Destination = Nop
      RxInterrupt = true
      Branch = Equal
      Next = PortReadReq2
      endcase
      ]
   case PortReadReq2:	//rGet ← rGet+1; Goto(Idle)
      [
      CarryIn = high; Function = RplusS; Source = ZB
      AReg = spare; BReg = rGet; Destination = RamF
      Next = Idle
      endcase
      ]

   case PortWriteReq:	//Test(tPut); TxBuf[tPut] ← Port; Return(Z); Resume(PortStart)
      [
      CarryIn = low; Function = RorS; Source = ZB
      AReg = PortStart; BReg = tPut; Destination = Nop
      TBufCE, BufWE, PRead = true, true, true
      Resume = true
      Branch = Zero
      Next = PortWriteReq1
      endcase
      ]
   case PortWriteReq1:	//tPut ← tPut-1; Return(NZ); Goto(TxStart); Resume(PortStart3)
      [
      CarryIn = low; Function = SminusR; Source = ZB
      AReg = PortStart3; BReg = tPut; Destination = RamF
      Resume = true
      Branch = NonZero
      Next = TxStart
      endcase
      ]

   case PortStart:	//tBase ← DataBus
      [
      CarryIn = low; Function = RorS; Source = DZ
      AReg = spare; BReg = tBase; Destination = RamF
      Next = PortStart1
      endcase
      ]
   case PortStart1:	//tPut ← tBase
      [
      CarryIn = low; Function = RorS; Source = ZA
      AReg = tBase; BReg = tPut; Destination = RamF
      Next = PortStart2
      endcase
      ]
   case PortStart2:	//mask ← 0	(mask is in Q register)
      [
      CarryIn = low; Function = RandS; Source = ZQ
      AReg = spare; BReg = spare; Destination = QReg
      Next = PortStart3
      endcase
      ]
   case PortStart3:	//retry ← 0
      [
      CarryIn = low; Function = RandS; Source = ZB
      AReg = spare; BReg = retry; Destination = RamF
      Next = PortStart4
      endcase
      ]
   case PortStart4:	//timer ← 0; Return
      [
      CarryIn = low; Function = RandS; Source = ZB
      AReg = spare; BReg = timer; Destination = RamF
      Branch = Always
      endcase
      ]

   case TxStart:	//tGet ← tBase; Goto(TxReady)
      [
      CarryIn = low; Function = RorS; Source = ZA
      AReg = tBase; BReg = tGet; Destination = RamF
      Next = TxReady
      endcase
      ]

   case TxReady:	//Tx ← TxBuf[tGet]; tGet ← tGet-1; Ack; Goto(Idle)
      [
      CarryIn = low; Function = SminusR; Source = ZA
      AReg = tGet; BReg = tGet; Destination = RamA
      TBufCE, TWrite = true, true
      Ack = true
      Next = Idle
      endcase
      ]

   case TxAbort:	//mask ← ShiftLeft(mask); Ack
      [
      CarryIn = low; Function = RorS; Source = ZQ
      AReg = spare; BReg = spare; Destination = RamQU
      Ack = true
      Next = TxAbort1
      endcase
      ]
   case TxAbort1:	//retry ← random & mask; Return(NZ); Goto(TxStart)
      [
      CarryIn = low; Function = RandS; Source = AQ
      AReg = random; BReg = retry; Destination = RamF
      Branch = NonZero
      Next = TxStart
      endcase
      ]

   case Idle:		//random ← random+1; Return(NCY8); Goto(TxTimer)
      [
      CarryIn = high; Function = RplusS; Source = ZB
      AReg = spare; BReg = random; Destination = RamF
      Branch = NoCarry8
      Next = TxTimer
      endcase
      ]

   case TxTimer:	//Test(tGet); Return(Neg)
      [
      CarryIn = low; Function = RorS; Source = ZB
      AReg = spare; BReg = tGet; Destination = Nop
      Branch = Negative
      Next = TxTimer1
      endcase
      ]
   case TxTimer1:	//timer ← timer-1; Return(Z); SetTxTimeout(Z); Resume(TxTimer4)
      [
      CarryIn = low; Function = SminusR; Source = ZB
      AReg = TxTimer4; BReg = timer; Destination = RamF
      Resume, TxTimeout = true, true
      Branch = Zero
      Next = TxTimer2
      endcase
      ]
   case TxTimer2:	//Test(retry); Return(Z)
      [
      CarryIn = low; Function = RorS; Source = ZB
      AReg = spare; BReg = retry; Destination = Nop
      Branch = Zero
      Next = TxTimer3
      endcase
      ]
   case TxTimer3:	//Retry ← Retry-1; Return(NZ); Goto(TxStart)
      [
      CarryIn = low; Function = SminusR; Source = ZB
      AReg = spare; BReg = retry; Destination = RamF
      Branch = NonZero
      Next = TxStart
      endcase
      ]
   case TxTimer4:	//tGet ← -1; Return
      [
      CarryIn = low; Function = RxnorS; Source = AB
      AReg = tGet; BReg = tGet; Destination = RamF
      Branch = Always
      endcase
      ]
   ]

data>>Data.BufWE = BufWE? high, low
data>>Data.RBufCE = RBufCE? low, high
data>>Data.TBufCE = TBufCE? low, high
data>>Data.CRead = CRead? low, high
data>>Data.PWrite = PWrite? low, high
data>>Data.PRead = PRead? low, high
data>>Data.TWrite = TWrite? low, high
data>>Data.RRead = RRead? low, high
data>>Data.RxOverflow = RxOverflow? high, low
data>>Data.TxTimeout = TxTimeout? high, low
data>>Data.RxInterrupt = RxInterrupt? high, low
data>>Data.QEmpty = QEmpty? high, low
data>>Data.Ack = Ack? low, high
data>>Data.Resume = Resume? high, low
data>>Data.CarryIn = CarryIn
data>>Data.DestHigh = Destination rshift 2
data>>Data.DestLow = Destination
data>>Data.Function = Function
data>>Data.Source = Source
data>>Data.BReg = BReg
data>>Data.AReg = AReg
data>>Data.Branch = Branch
data>>Data.Next = Next xor 20b
]