// EtherRoms.bcpl -- Model 1
// Last modified 27-Aug-81 15:44:35 by Taft

get "DoradoProms.defs"

external EtherProms

//----------------------------------------------------------------------------
let EtherProms(mem) be
//----------------------------------------------------------------------------
[
let buff = vec 300
if StEq(mem, "DskEth") then mem!0 = DoAll

if StEq(mem, "EtherFifo") & ICtype eq MC10149 then  // define the Fifo Prom
   [
   for adr = 0 to 255 do buff!adr = EtherFifo(adr)
   Header("EtherFifo", 4, buff, 256, 0)
   PromCommand("Eth-l10")
   Header("EtherFifo", 4, buff, 256, 0)  // the second one is identical
   PromCommand("Eth-l15")
   ]

if StEq(mem, "EtherPD") & ICtype eq MC10149 then  // define the PD Prom
   [
   for adr = 0 to 255 do buff!adr = EtherPD(adr)
   Header("EtherPD", 4, buff, 256, 0)
   PromCommand("Eth-h22")
   ]

if StEq(mem, "EtherRcvr") & ICtype eq MC10149 then  // define the Rcvr Prom
   [
   for adr = 0 to 255 do buff!adr = EtherRcvr(adr)
   Header("EtherRcvr", 12, buff, 256, 0)
   PromCommand("Eth-h09") // command to blow the left nibble
   PromCommand("Eth-h10", "4")  // command to blow the middle nibble
   PromCommand("Eth-h11", "8")  // command to blow the right nibble
   ]

if StEq(mem, "EtherXmtr") & ICtype eq MC10149 then  // define the Xmtr Prom
   [
   for adr = 0 to 255 do buff!adr = EtherXmtr(adr)
   Header("EtherXmtr", 12, buff, 256, 0)
   PromCommand("Eth-h14") // command to blow the left nibble
   PromCommand("Eth-h15", "4")  // command to blow the middle nibble
   PromCommand("Eth-h16", "8")  // command to blow the right nibble
   ]
]

//----------------------------------------------------------------------------
and EtherFifo(input) = valof
//----------------------------------------------------------------------------
// An MCM10150 or MCM10149 (either type will work)
// The transmitter fifo and the receiver fifo each use one of these proms.
[
manifest
   [
   // The correspondence between voltage levels and bits in the .MB file:
   high = 1; low = 0
   ]

structure Input:
   [
   blank bit 8
   write bit 4		// msb is A0 pin 4
   read bit 4		// msb is A4 pin 10
   ]

structure Output:
   [
   full bit		// Q0 pin 15
   notFull bit
   empty bit
   notEmpty bit
   blank bit 12
   ]

let write = input<<Input.write
let read = input<<Input.read

let output = nil
output<<Output.full = ((read-1) & 17b) eq write? high, low
output<<Output.notFull = ((read-1) & 17b) eq write? low, high
output<<Output.empty = read eq write? high, low
output<<Output.notEmpty = read eq write? low, high
resultis output
]

//----------------------------------------------------------------------------
and EtherPD(input) = valof
//----------------------------------------------------------------------------
// An MCM10150 or MCM10149 (either type will work)
[
manifest
   [
   // The correspondence between voltage levels and bits in the .MB file:
   high = 1; low = 0
   // pdEvent values
   noEvent = low lshift 1 + low
   collision = low lshift 1 + high
   dataZero = high lshift 1 + low
   dataOne = high lshift 1 + high
   // cntCntrl values
   count = high; load = low
   ]

structure Input:
   [
   blank bit 8
   pdCarrier bit	// A0 pin 4
   newData bit
   oldData bit
   timer bit 4		// msb is A3 pin 9
   reportCollisions bit
   ]

structure Output:
   [
   carrier bit		// Q0 pin 15
   event bit 2
   cntCtrl bit
   blank bit 12
   ]

let pdCarrier = input<<Input.pdCarrier ne 0
let newData = input<<Input.newData ne 0
let oldData = input<<Input.oldData ne 0
let timer = input<<Input.timer
let reportCollisions = input<<Input.reportCollisions ne 0

let carrier = pdCarrier
let event = noEvent
let cntCtrl = count

test pdCarrier
   ifso test oldData ne newData
      ifso switchon timer into
         [
         case 0 to 1:  // too many transitions
            [
            if reportCollisions then event = collision
            endcase
            ]
         case 2 to 4: endcase  // setup transition
         case 5 to 9:  // data transition
            [
            cntCtrl = load
            event = newData? dataOne, dataZero
            endcase
            ]
         case 10 to 15:  // too few transitions
            [
            cntCtrl = load
            event = reportCollisions? collision, newData? dataOne, dataZero
            endcase
            ]
         ]
      ifnot if timer ge 12 then  // jam or end of packet
         [
         carrier = newData? high, low
         cntCtrl = load
         ]
   ifnot if oldData ne newData then  // first transition of new packet
      [
      cntCtrl = load
      carrier = high
      event = newData? dataOne, collision
      ]

let output = nil
output<<Output.carrier = carrier
output<<Output.event = event
output<<Output.cntCtrl = cntCtrl
resultis output
]

//----------------------------------------------------------------------------
and EtherRcvr(input) = valof
//----------------------------------------------------------------------------
// Three MCM10150s or MCM10149s (either type will work)
[
manifest
   [
   // The correspondence between voltage levels and bits in the .MB file:
   high = 1; low = 0
   // srCtrl values
   srLoad = low lshift 1 + low
   srShift = high lshift 1 + low  // shift left, count down
   srHold = high lshift 1 + high
   // state values
   idle = 0; maybe = 1; full = 2; imip = 3
   // pdEvent values
   noEvent = 0; collision = 1; dataZero = 2; dataOne = 3
   ]

structure Input:
   [
   blank bit 8
   currentState bit 3	// rxState.0 is A0 pin 4
   rxCollision bit
   pdCarrier bit
   pdEvent bit 2	// pdEvent.0 is A5 pin 6
   rxSRFull bit		// *** Low True ***
   ]

structure Output:
   [
   nextState bit 3	// rxState.0 is Q0 pin 15
   rxCollision bit
   rxEOP bit		// Q0 pin 15
   rxSync bit		// *** Low True ***
   rxIncTrans bit
   rxCRCReset bit
   rxCRCClk bit		// Q0 pin 15
   rxData bit
   rxSRCtrl bit 2	// rxSRCtrl.0 is Q2 pin 12
   blank bit 4
   ]

let currentState = input<<Input.currentState
let rxCollision = input<<Input.rxCollision ne 0
let pdCarrier = input<<Input.pdCarrier ne 0
let pdEvent = input<<Input.pdEvent
let rxSRFull = input<<Input.rxSRFull eq 0

//rxCollision, rxIncTrans, rxEOP, and rxSync are treated as booleans and
// converted to the correct voltage level at the end of this procedure.
//rxCRCReset, rxCRCClk and rxData are treated as voltage levels throughout.

let nextState = currentState
let preCollision = rxCollision
let rxEOP = false
let rxSync = false
let rxIncTrans = false
let rxCRCReset = low
let rxCRCClk = low
let rxData = low
let rxSRCtrl = srHold

switchon currentState into
   [
   case idle:
      [
      //In this state we are waiting for the beginning of a packet.
      //Assertion: all paths to this state have done a rxCtrl = srLoad
      //in order to reset the bit counter.
      rxCRCReset = high
      preCollision = false
      if pdCarrier then
         [
         nextState = maybe
         switchon pdEvent into
            [
            case collision:
            case dataZero:
               [
               //Getting dataZero means we are out of sync with
               // the phase decoder.  This may happen just after power-on.
               //Getting collision means the phase decoder missed the first
               // bit of the packet (a common failure of Ethernets which
               // are too long, aggravated by the too-short preamble).
               preCollision = true
               endcase
               ]
            case dataOne:
               [
               //All Ethernet packets are preceeded by a degenerate
               // preamble consisting of a single one bit.
               //Absense of carrier and then a one bit marks the
               // beginning of a packet.
               //The bit is clocked into the CRC register but not
               // into the receiver SR.
               rxCRCReset = low
               rxCRCClk = high
               rxData = high
               endcase
               ]
            ]
         ]
      endcase
      ]

   case maybe:
      [
      //In this state we are in the first part of a packet.  We have
      // not put anything into the Fifo, so if things go sour, we can
      // just go back to the idle state without having to notify people
      // down the pipe.
      //Collisions will generally cause very short (less then 16 bit)
      // packets or at least packets with phase encoding violations
      // (too many or too few transitions).
      //If carrier drops and the phase decoder has reported a collision,
      // then its reportCollisions input must be true, which implies that the
      // microcode wants to see collisions, so report status for the fragment.
      //If carrier drops but the PD hasn't reported a collision, then
      // reportCollisions is probably false, so just go back to the
      // idle state without putting anything into the pipe.
      test pdCarrier
         ifnot
            [
            // We lost carrier.
            // This was probably a runt packet caused by a collision.
            // Note that we discard the data.
            if rxCollision then
               [
               rxIncTrans = true
               rxEOP = true
               rxSync = true
               ]
            rxSRCtrl = srLoad
            nextState = idle
            ]
         ifso switchon pdEvent into
            [
            case collision:
               [
               preCollision = true
               endcase
               ]
            case dataZero:
            case dataOne:
               [
               rxSRCtrl = srShift
               rxCRCClk = high
               rxData = pdEvent eq dataOne? high, low
               if rxSRFull then
                  [
                  //The receiver SR is full - dump it into the Fifo.
                  nextState = full
                  rxSync = true
                  ]
               endcase
               ]
            ]
      endcase
      ]

   case full:
      [
      //In this state we are at the end of a word.
      //When a packet ends, the phase decoder will drop carrier.
      //If this happens when the shift register is full,
      // then we assume that it is the normal end of a packet
      // (if it isn't the CRC will probably be wrong).
      //If carrier drops when the shift register is not full (states
      // IMIP or Maybe), then the packet is damaged so the incomplete
      // transmission status bit is set.
      test pdCarrier
         ifnot  //carrier dropped
            [
            nextState = idle
            rxSRCtrl = srLoad
            rxIncTrans = false
            rxEOP = true
            rxSync = true
            ]
         ifso switchon pdEvent into
            [
            case collision:
               [
               preCollision = true
               endcase
               ]
            case dataZero:
            case dataOne:
               [
               rxSRCtrl = srShift
               rxCRCClk = high
               rxData = pdEvent eq dataOne? high, low
               nextState = imip
               endcase
               ]
            ]
      endcase
      ]

   case imip:
      [
      //In this state, we are in the middle of a word
      test pdCarrier
         ifnot  //carrier dropped
            [
            nextState = idle
            rxSRCtrl = srLoad
            rxIncTrans = true
            rxEOP = true
            rxSync = true
            ]
         ifso switchon pdEvent into
            [
            case collision:
               [
               preCollision = true
               endcase
               ]
            case dataZero:
            case dataOne:
               [
               rxSRCtrl = srShift
               rxCRCClk = high
               rxData = pdEvent eq dataOne? high, low
               if rxSRFull then
                  [
                  //The receiver SR is full - dump it into the Fifo.
                  nextState = full
                  rxSync = true
                  ]
               endcase
               ]
            ]
      endcase
      ]
   default:	//unused states go to idle
      [
      nextState = idle
      rxSRCtrl = srLoad
      endcase
      ]
   ]

let output = nil
output<<Output.nextState = nextState
output<<Output.rxCollision = preCollision? high, low
output<<Output.rxEOP = rxEOP? high, low
output<<Output.rxSync = rxSync? low, high	//low true
output<<Output.rxIncTrans = rxIncTrans? high, low
output<<Output.rxCRCReset = rxCRCReset
output<<Output.rxCRCClk = rxCRCClk
output<<Output.rxData = rxData
output<<Output.rxSRCtrl = rxSRCtrl
resultis output
]

//----------------------------------------------------------------------------
and EtherXmtr(input) = valof
//----------------------------------------------------------------------------
//Three MCM10150s or MCM10149s (either type will work)
[
manifest
   [
   //the correspondence between voltage levels and bits in the .MB file:
   high = 1; low = 0
   //srCtrl values
   srLoad = low lshift 1 + low
   srShift = high lshift 1 + low  //shift left, count down
   srHold = high lshift 1 + high
   //state values
   idle = 0; mark = 1; data = 2; preCRC = 3; crc = 4; postCRC = 5
   ]

structure Input:
   [
   blank bit 8
   currentState bit 3	//TxState.0 is A0 pin 4
   gotTxBit bit
   txStop bit		//TxEOP & TxFifoEmpty
   txAbort bit		//TxCollision % TxDataLate % TxFifoPE % TxOff **Low
   txStart bit		//(TxFifoFull % TxEOP) & PDCarrier'
   txSREmpty bit	//*** Low True ***
   ]

structure Output:
   [
   nextState bit 3	//preTxState.0 is Q0 pin 15
   txCRCEnbl bit
   txCRCClk bit	//Q0 pin 15
   txGone bit
   txGo bit
   txData bit
   txSRCtrl bit 2	//txSRCtrl.0 is Q0 pin 15
   spare bit 2
   blank bit 4
   ]

let currentState = input<<Input.currentState
let gotTxBit = input<<Input.gotTxBit ne 0
let txStop = input<<Input.txStop ne 0
let txAbort = input<<Input.txAbort eq 0	//*** low true
let txStart = input<<Input.txStart ne 0
let txSREmpty = input<<Input.txSREmpty eq 0  //*** low true

//txGone and txGo are treated as booleans
//txCRCEnbl, txCRCClk, and txData are treated as voltages

let nextState = currentState
let txCRCEnbl = low
let txCRCClk = low
let txGone = txAbort
let txGo = false
let txData = low
let txSRCtrl = srHold

test txAbort
   ifso nextState = idle
   ifnot switchon currentState into
      [
      case idle:
         [
         //In this state the transmitter is shut down.
         if txStart then
            [
            //The Fifo is full or as full as it is going to get
            // (i.e. the packet is < the length of the Fifo), and there
            // is no carrier present so start transmitting.
            //A one bit (the 'mark' bit) is inserted before the first
            // user data bit to allow the receiver to aquire bit phase.
            nextState = mark
            txGo = true		//enable the phase encoder
            ]
         endcase
         ]
      case mark:
         [
         //In this state we are wire ORing a one onto the output of the
         // shift register to form the mark bit, and waiting for the
         // phase encoder to acknowledge it.
         txGo = true
         txData = high
         if gotTxBit then
            [
            //The Phase encoder has acked the mark bit.
            nextState = data
            txCRCClk = high
            txSRCtrl = srLoad
            ]
         endcase
         ]
      case data:
         [
         //In this state we are sending user data to the phase encoder.
         //Note: txCRCClk and txSRCtrl are set several times.
         txGo = true
         if gotTxBit then
            [
            //The phase encoder has acked a bit.  Clock the bit into
            // the CRC and shift the next bit into place.
            txCRCClk = high
            txSRCtrl = srShift
            if txSREmpty then
               [
               //That bit was the last one in the register.
               //Load instead of shifting.
               txSRCtrl = srLoad
               if txStop then
                  [
                  //That was the last bit of the last word of user data.
                  //Supply the CRC output to the phase encoder.
                  //We DO want to shift TxSR so that a zero comes out
                  // because the CRC output is wire ORed to it.
                  txSRCtrl = srShift
                  nextState = preCRC
                  ]
               ]
            ]
         endcase
         ]

      case preCRC:
         [
         //We stay in this state for one Dorado cycle.
         //The purpose of this state is to predecrement the counter so that
         // we are notified of the end of the packet one bit time early.
         //This will also shift the shift register, but that's unimportant
         // since we are supplying the CRC output to the phase encoder.
         //However it is important that the shift register produce zeros
         // whenever we shift it since the CRC output is wire ORed to it.
         //Fortunately this is the case.
         txGo = true
         txSRCtrl = srShift
         txCRCEnbl = high
         nextState = crc
         endcase
         ]
      case crc:
         [
         //In this state we are sending CRC data to the phase encoder.
         txGo = true
         txCRCEnbl = high
         if gotTxBit then
            [
            //The phase encoder has acked another CRC bit.
            txCRCClk = high
            txSRCtrl = srShift
            if txSREmpty then
               [
               //That was an ack for the next to last CRC bit.
               txGo = false
               nextState = postCRC
               ]
            ]
         endcase
         ]
      case postCRC:
         [
         //In this state we are waiting for the phase encoder to acknowledge
         // that it has read the last bit of the CRC.
         txCRCEnbl = high
         if gotTxBit then
            [
            //As we transition to the idle state, generate txGone
            // for one cycle.  This will clear txEOP and wakeup the
            // microcode for the last time.
            txGone = true
            nextState = idle
            ]
         endcase
         ]
      default:
         [
         nextState = idle
         endcase
         ]
      ]

let output = nil
output<<Output.nextState = nextState
output<<Output.txCRCEnbl = txCRCEnbl
output<<Output.txCRCClk = txCRCClk
output<<Output.txGone = txGone? high, low
output<<Output.txGo = txGo? high, low
output<<Output.txData = txData
output<<Output.txSRCtrl = txSRCtrl
resultis output
]