// DCPProms.bcpl -- Dicentra Central Processor Proms
// Last modified November 28, 1982  5:35 AM by Boggs

external [ Ws; OpenFile; Puts; Closes; TruncateDiskStream; CallSwat; Multibus ]

static [ memory; mbFile ]

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

manifest [ high = 1; low = 0 ]

//-----------------------------------------------------------------------------------------
let DCPProms() be
//-----------------------------------------------------------------------------------------
[
mbFile = OpenFile("DCPProms.mb")

DoMemory("Stack", 512, 8, Stack)
DoMemory("IB", 512, 8, IB)
DoMemory("ALUHigh", 512, 8, ALUHigh)
DoMemory("FX", 512, 8, FX)
DoMemory("FYZ", 512, 8, FYZ)
DoMemory("FY1", 512, 8, FY1)
DoMemory("FY2", 512, 8, FY2)
DoMemory("FZ1", 512, 8, FZ1)
DoMemory("FZ2", 512, 8, FZ2)
DoMemory("Trap", 512, 8, Trap)
DoMemory("Multibus", 512, 24, Multibus)

Puts(mbFile, 0)  //0 = end of file
TruncateDiskStream(mbFile)
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)

// Write Asciz string to word stream.  What a pain!
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 = vec 1; if nData gr 32 then CallSwat("increase data vector size")
for addr = 0 to nAddr-1 do
   [
   Puts(mbFile, 1)  //1 = memory contents
   Puts(mbFile, 0)  //0 = source line number (not used)
   Proc(addr, data)
   for i = 0 to (nData+15)/16 -1 do Puts(mbFile, data!i)
   ]
]

//-----------------------------------------------------------------------------------------
and Stack(addr, data) be
//-----------------------------------------------------------------------------------------
[
structure Addr:
   [
   blank bit 7
   StackP bit 4
   PopX bit
   PopZ bit
   PushX bit
   PushY bit
   PushZ bit
   ]

structure Data:
   [
   StackTrap bit
   blank bit 3
   StackP bit 4
   blank bit 8
   ]

let PopX = addr<<Addr.PopX eq high
let PopZ = addr<<Addr.PopZ eq high
let PushX = addr<<Addr.PushX eq high
let PushY = addr<<Addr.PushY eq high
let PushZ = addr<<Addr.PushZ eq high
let StackP = addr<<Addr.StackP

let Pop = PopX % PopZ
let Push = PushX % PushY % PushZ

let StackTrap = Pop & StackP eq 0 %
   Push & StackP eq 15 %
   PopX & Push & StackP eq 0 %
   Push & PopZ & StackP eq 15 %
   PopX & PopZ & (StackP eq 0 % StackP eq 1) %
   PopX & PopZ & Push & (StackP eq 0 % StackP eq 1)

if Pop then StackP = StackP -1
if Push then StackP = StackP +1

data>>Data.StackP = StackP
data>>Data.StackTrap = StackTrap? high, low
]

//-----------------------------------------------------------------------------------------
and IB(addr, data) be
//-----------------------------------------------------------------------------------------
[
manifest
   [
   // instruction buffer states
   ibEmpty = 0
   ibByte = 1
   ibFull = 2
   ibWord = 3
   ]

structure Addr:
   [
   blank bit 7
   IBDisp bit
   ReadIB bit
   WriteIB bit
   IBPtrGetsWord bit
   IBPtrGetsByte bit
   EnC2Funs bit
   Interrupt bit
   InIBPtr bit 2
   unused bit
   ]

structure Data:
   [
   GoodIBDisp bit
   IBEmpty bit		//low true
   IBRefillTrap bit
   ReadIB0 bit		//low true
   ReadIB1 bit		//low true
   WriteIBFront bit
   OutIBPtr bit 2
   blank bit 8
   ]

let IBDisp = addr<<Addr.IBDisp eq high
let ReadIB = addr<<Addr.ReadIB eq high
let WriteIB = addr<<Addr.WriteIB eq high
let IBPtrGetsWord = addr<<Addr.IBPtrGetsWord eq high
let IBPtrGetsByte = addr<<Addr.IBPtrGetsByte eq high
let EnC2Funs = addr<<Addr.EnC2Funs eq high
let Interrupt = addr<<Addr.Interrupt eq high
let InIBPtr = addr<<Addr.InIBPtr

let IBEmpty = false
let IBRefillTrap = false
let GoodIBDisp = false
let ReadIB0 = false
let ReadIB1 = false
let WriteIBFront = false
let OutIBPtr = InIBPtr

let advance = false

// IBRefillTrap causes a trap to one of four locations executed in Cycle 1:
//	3400b or 3000b	Interrupt
//	2400b		IB refill; IB not empty
//	2000b		IB refill; IB is empty
if IBDisp & EnC2Funs & not ReadIB & not WriteIB & not IBPtrGetsWord then
   test (Interrupt % InIBPtr ne ibFull) & not IBPtrGetsByte
      ifso IBRefillTrap = true
      ifnot [ GoodIBDisp = true; advance = true ]

// ReadIB puts IBFront onto the X bus and advances IBPtr.
// Since there are always at least 2 bytes in the buffer when we begin
//  executing a Mesa instruction, ReadIB can't trap.
// Note that "ReadIB, IBDisp" is not legal (its a Noop).
if ReadIB & not IBDisp & not WriteIB & not IBPtrGetsWord & not IBPtrGetsByte then
   advance = true

if advance then
   [
   WriteIBFront = true
   ReadIB0 = (InIBPtr & 1) eq 0  //ReadIB0 is the inverse of IBPtr.1
   ReadIB1 = not ReadIB0
   OutIBPtr = selecton InIBPtr into
      [
      case ibFull: ibWord
      case ibWord: ibByte
      case ibByte: ibEmpty
      case ibEmpty: ibEmpty
      ]
   ]

// Unless we are refilling due to a jump instruction, IBPtr will be ibByte or ibEmpty.
//  If it's ibEmpty then we need to write IBFront from IB0.
//  If it's ibByte then we shouldn't write IBFront.
// If we are refilling due to a jump instruction, IBPtr value is unknown.
//  It will be specified by a IBPtr← in this cycle or the next.
// Note that "WriteIB, ReadIB" is not legal (it's a Noop).
if WriteIB & not IBDisp & not ReadIB test InIBPtr eq ibEmpty
   ifso
      [
      WriteIBFront = true
      test IBPtrGetsByte
         ifso [ ReadIB1 = true; OutIBPtr = ibByte ]
         ifnot [ ReadIB0 = true; OutIBPtr = ibWord ]
      ]
   ifnot OutIBPtr = ibFull

// IBPtr←byte or IBPtr←word always come in cycle 1 immediately after a WriteIB.
// They load IBFront with the even or odd dest byte and set up IBPtr.
if IBPtrGetsWord & not IBDisp & not ReadIB & not WriteIB & not IBPtrGetsByte then
   [
   WriteIBFront = true
   ReadIB0 = true
   OutIBPtr = ibWord
   ]
if IBPtrGetsByte & not IBDisp & not ReadIB & not WriteIB & not IBPtrGetsWord then
   [
   WriteIBFront = true
   ReadIB1 = true
   OutIBPtr = ibByte
   ]

data>>Data.IBEmpty = OutIBPtr eq ibEmpty? low, high
data>>Data.IBRefillTrap = IBRefillTrap? high, low
data>>Data.GoodIBDisp = GoodIBDisp? high, low
data>>Data.ReadIB0 = ReadIB0? low, high
data>>Data.ReadIB1 = ReadIB1? low, high
data>>Data.WriteIBFront = WriteIBFront? high, low
data>>Data.OutIBPtr = OutIBPtr
]

//-----------------------------------------------------------------------------------------
and ALUHigh(addr, data) be
//-----------------------------------------------------------------------------------------
[
structure Addr:
   [
   blank bit 7
   paS0 bit
   paS1 bit
   paS2 bit
   paF1 bit
   paF2 bit
   pMem bit
   Cycle3 bit
   Cycle2 bit
   blank bit
   ]

structure Data:
   [
   aSh0 bit
   aSh1 bit
   aSh2 bit
   aFh1 bit
   aFh2 bit
   ReadMD bit		//low true
   blank bit 2
   blank bit 8
   ]

let paS0 = addr<<Addr.paS0 eq high
let paS1 = addr<<Addr.paS1 eq high
let paS2 = addr<<Addr.paS2 eq high
let paF1 = addr<<Addr.paF1 eq high
let paF2 = addr<<Addr.paF2 eq high
let pMem = addr<<Addr.pMem eq high
let Cycle3 = addr<<Addr.Cycle3 eq high
let Cycle2 = addr<<Addr.Cycle2 eq high

data>>Data.aSh0 = (pMem & Cycle3? false, paS0)? high, low
data>>Data.aSh1 = (pMem & Cycle3? true, paS1)? high, low
data>>Data.aSh2 = (pMem & Cycle3? true, paS2)? high, low
data>>Data.aFh1 =(pMem & Cycle3? true, paF1)? high, low
data>>Data.aFh2 =(pMem & Cycle3? true, paF2)? high, low
data>>Data.ReadMD = pMem & Cycle2? low, high
]

//-----------------------------------------------------------------------------------------
and FX(addr, data) be
//-----------------------------------------------------------------------------------------
[
structure Addr:
   [
   blank bit 7
   pfX bit 4
   blank bit 5
   ]

structure Data:
   [
   blank bit
   WriteRH bit
   Shift bit		//low true
   CycleX bit		//low true
   CInGetsPC16X bit	//low true
   MapRefX bit		//low true
   PopX bit
   PushX bit
   blank bit 8
   ]

let FX = addr<<Addr.pfX

data>>Data.WriteRH = FX eq 9? high, low
data>>Data.Shift = FX eq 10? low, high
data>>Data.CycleX = FX eq 11? low, high
data>>Data.CInGetsPC16X = FX eq 12? low, high
data>>Data.MapRefX = FX eq 13? low, high
data>>Data.PopX = FX eq 14? high, low
data>>Data.PushX = FX eq 15? high, low
]

//-----------------------------------------------------------------------------------------
and FYZ(addr, data) be
//-----------------------------------------------------------------------------------------
[
structure Addr:
   [
   blank bit 7
   pfS01 bit 2
   pfS23 bit 2
   pfZ01 bit 2 =
      [
      pfZ0 bit
      pfZ1 bit
      ]
   pfY0 bit
   pfY1 bit
   blank bit
   ]

structure Data:
   [
   Zero bit		//low true
   IB bit		//low true
   Rot bit		//low true
   Byte bit		//low true
   Const bit		//low true
   FourBitBr bit	//low true
   TwoBitBr bit		//low true
   OneBitBr bit		//low true
   blank bit 8
   ]

let pfS01 = addr<<Addr.pfS01
let pfS23 = addr<<Addr.pfS23
let pfZ01 = addr<<Addr.pfZ01
let pfZ0 = addr<<Addr.pfZ0 eq high
let pfY0 = addr<<Addr.pfY0 eq high
let pfY1 = addr<<Addr.pfY1 eq high

data>>Data.Zero = (pfS23 eq 1 % (pfS23 eq 3 & pfZ0))? low, high
data>>Data.IB = (pfS23 eq 3 & pfZ01 eq 3)? low, high
data>>Data.Rot = (pfS23 eq 0 & pfZ01 eq 3)? low, high
data>>Data.Byte = pfS01 eq 3? low, high
data>>Data.Const = pfS23 eq 1? low, high
data>>Data.FourBitBr = (pfS01 eq 0 & pfY0 & not pfY1)? low, high
data>>Data.TwoBitBr = (pfS01 eq 0 & pfY0)? low, high
data>>Data.OneBitBr = (pfS01 eq 0 & not pfY0)? low, high
]

//-----------------------------------------------------------------------------------------
and FY1(addr, data) be
//-----------------------------------------------------------------------------------------
[
structure Addr:
   [
   blank bit 7
   pfY bit 4
   pfS01 bit 2
   blank bit 3
   ]

structure Data:
   [
   WriteDebA bit	//low true
   WriteExtCtrl bit
   ClrIntTrap bit	//low true
   IBDisp bit
   SetInterrupt bit
   WriteStkP bit
   WriteIB bit
   CycleY bit		//low true
   blank bit 8
   ]

let pfY = addr<<Addr.pfY
let pfS01 = addr<<Addr.pfS01

data>>Data.WriteDebA = (pfS01 eq 2 & pfY eq 0)? low, high
data>>Data.WriteExtCtrl = (pfS01 eq 2 & pfY eq 1)? high, low
data>>Data.ClrIntTrap = (pfS01 eq 1 & pfY eq 2)? low, high
data>>Data.IBDisp = (pfS01 eq 1 & pfY eq 3)? high, low
data>>Data.SetInterrupt = (pfS01 eq 1 & pfY eq 4)? high, low
data>>Data.WriteStkP = (pfS01 eq 1 & pfY eq 5)? high, low
data>>Data.WriteIB = (pfS01 eq 1 & pfY eq 6)? high, low
data>>Data.CycleY = (pfS01 eq 1 & pfY eq 7)? low, high
]

//-----------------------------------------------------------------------------------------
and FY2(addr, data) be
//-----------------------------------------------------------------------------------------
[
structure Addr:
   [
   blank bit 7
   pfY bit 4
   pfS01 bit 2
   blank bit 3
   ]

structure Data:
   [
   blank bit
   MapRefY bit		//low true
   blank bit
   PushY bit
   IORefY bit		//low true
   WriteBank bit
   BHEN bit
   RawRef bit
   blank bit 8
   ]

let pfY = addr<<Addr.pfY
let pfS01 = addr<<Addr.pfS01

data>>Data.MapRefY = (pfS01 eq 1 & pfY eq 9)? low, high
data>>Data.PushY = (pfS01 eq 1 & pfY eq 11)? high, low
data>>Data.IORefY = ((pfS01 eq 1 % pfS01 eq 2) & pfY eq 12)? low, high
data>>Data.WriteBank = (pfS01 eq 1 & pfY eq 13)? high, low
data>>Data.BHEN = (pfS01 eq 2 & (pfY eq 12 % pfY eq 14 % pfY eq 15))? low, high
data>>Data.RawRef = ((pfS01 eq 1 % pfS01 eq 2) & pfY eq 14)? high, low
]

//-----------------------------------------------------------------------------------------
and FZ1(addr, data) be
//-----------------------------------------------------------------------------------------
[
structure Addr:
   [
   blank bit 7
   pfZ bit 4
   pfS23 bit 2
   blank bit 3
   ]

structure Data:
   [
   ADR0 bit
   IBPtrGetsByte bit
   IBPtrGetsWord bit
   CInGetsPC16Z bit 	//low true
   IORefZ bit		//low true
   PopZ bit
   PushZ bit
   AltUAddr bit
   blank bit 8
   ]

let pfZ = addr<<Addr.pfZ
let pfS23 = addr<<Addr.pfS23

data>>Data.ADR0 = (pfS23 eq 3 & pfZ eq 0)? high, low
data>>Data.IBPtrGetsByte = (pfS23 eq 0 & pfZ eq 1)? high, low
data>>Data.IBPtrGetsWord = (pfS23 eq 0 & pfZ eq 2)? high, low
data>>Data.CInGetsPC16Z = (pfS23 eq 0 & pfZ eq 3)? low, high
data>>Data.IORefZ = (pfS23 eq 3 & pfZ eq 7)? low, high
data>>Data.PopZ = (pfS23 eq 0 & pfZ eq 5)? high, low
data>>Data.PushZ = (pfS23 eq 0 & pfZ eq 6)? high, low
data>>Data.AltUAddr = (pfS23 eq 0 & pfZ eq 7)? high, low
]

//-----------------------------------------------------------------------------------------
and FZ2(addr, data) be
//-----------------------------------------------------------------------------------------
[
structure Addr:
   [
   blank bit 7
   pfZ bit 4
   pfS23 bit 2
   blank bit 3
   ]

structure Data:
   [
   ReadDebB bit		//low true
   ReadExtStat bit	//low true
   ReadMisc bit		//low true
   ReadRH bit		//low true
   blank bit
   ReadIB bit
   blank bit
   IBHigh bit		//low true
   blank bit 8
   ]

let pfZ = addr<<Addr.pfZ
let pfS23 = addr<<Addr.pfS23

data>>Data.ReadDebB = (pfS23 eq 3 & pfZ eq 8)? low, high
data>>Data.ReadExtStat = (pfS23 eq 3 & pfZ eq 6)? low, high
data>>Data.ReadMisc = (pfS23 eq 3 & pfZ eq 10)? low, high
data>>Data.ReadRH = (pfS23 eq 3 & pfZ eq 11)? low, high
data>>Data.ReadIB = (pfS23 eq 3 & pfZ eq 13)? high, low
data>>Data.IBHigh = (pfS23 eq 3 & pfZ eq 15)? low, high
]

//-----------------------------------------------------------------------------------------
and Trap(addr, data) be
//-----------------------------------------------------------------------------------------
[
manifest
   [
   // These bits appear inverted on X[8-9] when fZ = XLow←Misc.
   // If X[8-9] = zero then a control store parity error occured.
   stateIBEmpty = 0
   stateStack = 1
   stateInit = 2
   stateNoTrap = 3
   stateParity = 3
   ]

structure Addr:
   [
   blank bit 7
   CurrentState bit 2
   TrapIn bit
   IBEmptyTrap bit	//low true
   StackTrap bit
   InitTrap bit		//low true
   ParityTrap bit
   ClrIntTrap bit	//low true
   Cycle1 bit
   ]

structure Data:
   [
   NextState bit 2
   Trap bit
   ParityLED bit	//low true
   ForceBank0 bit
   blank bit 3
   blank bit 8
   ]

let CurrentState = addr<<Addr.CurrentState
let IBEmptyTrap = addr<<Addr.IBEmptyTrap eq low
let StackTrap = addr<<Addr.StackTrap eq high
let InitTrap = addr<<Addr.InitTrap eq low
let ParityTrap = addr<<Addr.ParityTrap eq high
let ClrIntTrap = addr<<Addr.ClrIntTrap eq low
let Cycle1 = addr<<Addr.Cycle1 eq high
let TrapIn = addr<<Addr.TrapIn eq high

let NextState = CurrentState
let Trap = false

// The Trap machine only remembers one trap.  If multiple traps occur,
//  only one of them is reported.  The priority of reporting is:
//	InitTrap - highest priority
//	ParityTrap
//	StackTrap
//	IBEmptyTrap - lowest priority
// If a trap occurs in an instruction which clears traps, the new trap still takes.
// The instruction at location 0 must read the trap type (XBus←Misc) and
//  clear the trap (ClrIntTrap), or else another trap will occur.

// InitTrap causes a continuous Trap regardless of the cycle.
// The CP repeatedly executes the instruction at location 0.
// The Multibus FSM loops in Cycle 1.
// The control store bank is forced to 0.
// When InitTrap goes away, the processor takes off from location 0 in cycle 1.

// The instruction at location 0, which the CP repeatedly executes while
//  Init is asserted, contains fY = ClrIntTrap.  Unless inhibited, this will
//  cause the trap machine to forget its mission 3 cycles before the processor
//  is finally released from its chains.  TrapIn detects this peculiar
//  combination and remembers it long enough to inhibit ClrIntTrap until the last
//  time that that magic instruction at location 0 is executed.

if ClrIntTrap & CurrentState eq stateInit & (TrapIn % not Cycle1) then ClrIntTrap = false

if ClrIntTrap then NextState = stateNoTrap
if IBEmptyTrap & CurrentState eq stateNoTrap then NextState = stateIBEmpty
if StackTrap & (CurrentState eq stateNoTrap % CurrentState eq stateIBEmpty) then
   NextState = stateStack
if ParityTrap & CurrentState ne stateInit then NextState = stateParity
if InitTrap then NextState = stateInit

// Trap must be asserted during Cycle 2, so we must decide to assert it during Cycle 1.
// A Trap causes the address loaded into NIA at the end of cycle 2 to be zero.
// The instruction at location 0 is fetched in cycle 3 and executed in cycle 1.

if Cycle1 & (NextState ne stateNoTrap % ParityTrap) then Trap = true

// ParityTrap is a level (JK-FF) rather than a 1-cycle pulse like the rest of the traps.
// This is fortunate since the trap mechanism has only 2 state bits but has 5 states:
//	NoTrap, IBEmpty, Stack, Parity, Init.

// InitTraps and ParityTraps go to location 0 in bank 0.
// All other traps go to location 0 in the current bank.
// This trap handler might just pass control to the trap handler in bank 0.

data>>Data.NextState = NextState
data>>Data.ParityLED = ParityTrap? low, high
data>>Data.ForceBank0 = (Trap & ParityTrap) % InitTrap? high, low
data>>Data.Trap = Trap? high, low
]