| 14/10/1999 : (JMF) Got rid of the delay fnct
|                    _DEBUG = 000011 for simulator (bpt and no refr)
|                    No console anymore: the machine initialize and
|                      run program in DRAM, beginning at address 0.
|                      Fill DRAM this a S, G, ... program using the
|                      simulator (option -rd<file> with maybesim)
| 25/10/1999 : (JMF) Minth moved to 0xCC (see the 'betamachine' file)
| 01/11/1999 : (JMF) Got rid of bpt() in SStep.  Added one in betamachine.asm
| 		Cancel detection of BREAK in hbpt() since we use a simulator


| Simple switch-based "operator's console" for MAYBE.

R0      = 0                 | Temporaries, used throughout ucode.
R1      = 1
R2      = 2
R3      = 3
R4      = 4
R5      = 5
R6      = 6
R7      = 7

| Some SRAM locations used by console:

MInstCount = 16     | 4-byte count of virtual (S or G) machine instr elecs.

sadr    = 20             | Current SRAM adr.
dadrlo  = 21             | Low current DRAM adr.
dadrhi  = 22             | High current DRAM adr.
BREAK   = 23             | Flag location to tell when halt is encountered.

| Entry point, on power-up.
| This microcode starts at uROM address zero.

| Initialize MAYBE machine and jump to terminal loop

init:   pwrup()

        cmove(SBASE, uSP)  | Initialize micro SP.
        cmove((0-1), sadr) | Initial display in lights.
        cmove(3, _DEBUG)   | (JMF) DEBUG MODE: bpt and no refr()
                           | Hot running under simulation (default).
        call(ioinit)       | Initialize I/O device.

	cwriteio(1, IER)       | (JMF) Initialize IO
        cmove(1, R1)
        cmove(0, R2)
        cmove(0, R3)
        cmove(BLINK / 8, R4)
                               | (JMF) Those lines comes from tinit
	call(MInit)            | (JMF) Initialize the S, G,... machine
	cmove4(0, MInstCount)  | (JMF) Initialize instruction count

	jmp(texec)             | (JMF) We're gone !

|       TERMINAL FUNCTION                  [TERM] Fall-1986 JMM
| added S- and G-machine I/O hooks              14-Jan-1987 Kenmac

ioadr   = 24                 | Current I/O register address [I/O].
sum     = 25                 | Used to store checksum results.
count   = 26                 | Used to keep track of bytes sent or receied.
inchar  = 27                 | Byte read from HP.
oldp0   = 28                 | Old state of P0.
intp0   = 29                 | Set on falling edge of P0; signals n interrupt.

| Commands used to communicate with the HP:
| 1. From the HP:

MAYBE_STOP      = 0x80       | Stop running program (from HP).
MAYBE_SYNC      = 1          | Echo a sync character.
MAYBE_ECHO      = 2          | Enter echo loop (debug tool).
MAYBE_WR_DRAM   = 3          | Write DRAM, given address, count, data csum.
MAYBE_RD_DRAM   = 4          | Read DRAM, given address, count.
MAYBE_WR_SRAM   = 5          | Write SRAM, given address, count, data csum.
MAYBE_RD_SRAM   = 6          | Read SRAM, given address, count.
MAYBE_INIT      = 7          | Initialize machine.
MAYBE_RUN       = 8          | Run program in dram.
MAYBE_SSTEP     = 9          | Single-step program in DRAM.

| 2. To the HP:

MAYBE_HALT      = 0x80       | Halt instruction executed.
MAYBE_BPT       = 0x81       | breakpoint instruction executed.
MAYBE_II        = 0xFF       | Illegal instruction executed.
MAYBE_OVF       = 0x82       | HP overflowed input buffer.
MAYBE_ACK       = 0x83       | acknowledge a character when running.
MAYBE_MODERR    = 0x84       | Bad G-machine general address mode.
MAYBE_ADRERR    = 0x85       | Bad call/jump effective address (G).

| Some constants defined for erase of reading:

BLINK = 0x30          | Blink constant for light display.
ESC   = 27            | Definition of the constant ESC.

| Definitions of the I/O card internal registers:
RBR   = 0    | Receier buffer register (read only).
THR   = 0    | Transmitter holding register (write only).
IER   = 1    | Interrupt enable register.
IIR   = 2    | Interrupt identification register (read only).
LCR   = 3    | Line control register.
MCR   = 4    | Modem control register.
LSR   = 5    | Line status register (read only).

ioinit: cwriteio(0b10000000, LCR)       | Set DLAB.
        cwriteio(12, 0)                 | 9600 baudot (LSB).
        cwriteio( 0, 1)                 | " (MSB).
        cwriteio(0b00000011, MCR)       | Set DTR and RTS.
        cwriteio(0b00000011, LCR)       | No parity; 1 stop bit; 8-bit words.
        rtn()

| Function 7: (pecho) primitie echo via LSR hand shake [PECHO].
| P0,P1: ABORT, dispatching on function in <switches>.

pecho: readio(LSR, R0)         | Read LSR.
       cand(0b01000001, R0, R0)| Has off all but Xmitter Ready  Data Ready.
       ccmp(0b01000001, R0)    | Is mitter Empty (TEMT)  Data Ready (DR)?
       jne(pexit)              | Done: check for exit
       ioread(R0)              | Yup Read the byte
       iowrite(R0)             | and echo back

pexit: pdisp(R0, pecho, pexit, pexit, fndsp) | Show read byte, loop or fndsp.

| Function 8: Terminal loop.
| Takes commmands from the Chipmonk and dispatches on them.
| Lights flash 10101010 01010101 to indicate terminal loop.
| Current commands are:

remote_table:
        WORD(remote)                 | Ignore this one.
        WORD(tsync)                  | Echo sync character.
        WORD(tpecho)                 | Debug echo loop.
        WORD(tlddram)                | Write data to DRAM from HP.
        WORD(trddram)                | Read data from DRAM to HP.
        WORD(tldsram)                | write data to SRAM from HP.
        WORD(trdsram)                | Read data from SRAM to HP.
        WORD(tinit)                  | Initialize machine.
        WORD(texec)                  | Run program and return status.
        WORD(tsing)                  | Single-step and return status.

REMOTE_FUNCTIONS = 10

remote: cwriteio(1, IER)
        cmove(1, R1)
        cmove(0, R2)
        cmove(0, R3)
        cmove(BLINK / 8, R4)

termwt: pdisp(R1, term1, termwt, termwt, fndsp)
term1:  refr()
        count(R3)
        jne(term10)
        count(R4)
        jne(term10)
        cmove(BLINK / 8, R4)
        
        count(R2)
        jne(term105)
        cmove(0, R2)
        rotl1(R1,R1)
        jpl(term10)
        cmove(42,R2)
        jmp(term10)

term105: rotr1(R1,R1)
        jeven(term10)
        cmove(0,R2)

term10: jnready(termwt)
        ioread(R0)
        ccmp(0, R0)
        jpl(termwt)
        ccmp(REMOTE_FUNCTIONS, R0)
        jmi(termwt)
        dispatch(R0, remote_table)

texec:  cmove(0, BREAK)
        cmove(0, inchar)
        cmove(0, oldp0)
        cmove(0, intp0)

texgo:  call(Mintest)
        ccmp(0, BREAK)
        jne(texit)
        
        call(SStep)
        ccmp(0, BREAK)
        je(texgo)


texit:  cwriteio(2, IER)
texdun: pdisp(R0, texdunc, texdun, texdun, remote)
texdunc: refr()
        jnready(texdun)
        iowrite(BREAK)
        jmp(remote)

tsing:  cmove(0x00, BREAK)
        call(SStep)
        cwriteio(2, IER)
tsing1: pdisp(R0, tsing2, tsing1, tsing1, remote)
tsing2: refr()
        jnready(tsing1)
        iowrite(BREAK)
tsing3: pdisp(R0, tsing4, tsing3, tsing3, remote)
tsing4: refr()
        jnready(tsing3)
        iowrite(PC)
tsing5: pdisp(R0, tsing6, tsing5, tsing5, remote)
tsing6: refr()
        jnready(tsing5)
        iowrite(PC+1)
        jmp(remote)

tinit:  call(MInit)            | Initialize state of the MAYBE.
        cmove4(0, MInstCount)  | Clear the instruction execution count.
        jmp(remote)            | Return to terminal loop.

| tsync is used to let the HP know it is on the same wavelength as
| the MAYBE. The HP sends a character, and the MAYBE is supposed
| to return the same character!

tsync:  pdisp(R0, tsync1, tsync, tsync, fndsp)
tsync1: refr()
        readio(LSR, R0)         | Look at the status register.
        cand(0b01000001, R0, R0)| Strip all but data ready to transmit empty
        ccmp(0b01000001, R0)    | Data ready to transmit empty?
        jne(tsync)              | Not ready yet, return to sync.
        ioread(R0)              | Got it!
        iowrite(R0)             | Now send it back and we are done here!
        jmp(remote)             | Return to terminal loop.

| Primitive echo loop test via LSR hand shae [PECDO].
| P0 and P1: ABORT, returning to terminal loop.
| Softuare abort occurs when two successive CCR>s are received.
| R0 => data received and displayed in lights.
| R1 => Status register.
| BREAK => Exit flag.

tpecho: cmove(0b01101101, R0)   | Put a pattern in lights to now we're here
tprloop: cmove(0, BREAK)        | Initialize done flag.
tploop: pdisp(R0, tploop1, tploop, tploop, remote)
                                | Show read byte, loop, or dun.
tploop1: refr()
        readio(LSR, R1)         | Read LSR.
        cand(0b01000001, R1, R1)| Has off all but mitter Read,v t Data Rea
        ccmp(0b01000001, R1)    | Is Xmitter Empty (TET) t Data Ready (DR)
        jne(tploop)             | Done: Loop again.
        ioread(R0)              | Yup: Read the byte
        iowrite(R0)             | and echo back.
        ccmp(13, R0)            | Is this the exit condition?
        jne(tprloop)            | Done, keep going, but reset flag.
        ccmp(13, BREAK)         | as last character a <CR>?
        je(remote)              | Yup, We're done with the echo test.
        cmove(13, BREAK)        | Done, store the <CR> as a flag.
        jmp(tploop)             | Loop, but don't reset flag.

| tlddram will load the DRAM starting at DRAM address
| <addrlo> <addrhi> with <count> bytes of data. <count> = 0
| implies count is 256. A simple, one-byte, additive checksum is computed
| and returned to the sender when transfer is completed.
| Registers are defined as:
| R0: Byte sent to or received from the I/O board.
| count: Count of remaining bytes to receive.
| dadrlo: <adrlo> in DRAM.
| dadrhi: <adrhi> in DRAM.
| sum: Additive checksum for simple check.

tlddram:cwriteio(1, IER)        | Interrupt on data available.
        cmove(0, sum)           | Initialize checksum.
tldlo:  pdisp(R0, tldlo1, tldlo, tldlo, remote)
tldlo1: refr()
        jnready(tlddram)        | o byte available, wait up.
        ioread(dadrlo)          | Put <adrlo> into dadrlo.
tldhi:  pdisp(dadrlo, tldhi1, tldhi, tldhi, remote)
tldhi1: refr()
        jnready(tldhi)          | waiting ...
        ioread(dadrhi)          | Put <adrhi> into dadrhi.
tldcnt: pdisp(dadrhi, tldcnt1, tldcnt, tldcnt, remote)
tldcnt1: refr()
        jnready(tldcnt)         | waiting ...
        ioread(count)           | Put <count> into count.
tldrc:  pdisp(dadrhi, tldrc1, tldrc, tldrc, remote)
tldrc1: refr()
        jnready(tldrc)          | waiting ...
        ioread(R0)              | Get a byte.
        add(R0, sum, sum)       | Simple checksum!
        s(R0, dadrlo, dadrhi)   | Store byte in DRAM.
        cadd2(1, dadrlo, dadrlo)| Increment <addrlo> and <addrhi>.
        count(count)            | Decrement count.
        ccmp(0, count)
        jne(tldrc1)             | Have we received correct number of bytes?
        cwriteio(2, IER)        | Interrupt on transmitter empty.
tldrcw: pdisp(count, tldrcw1, tldrcw, tldrcw, remote)
tldrcw1: refr()
        jnready(tldrcw)         | waiting ...
        iowrite(sum)            | Yep, send the checksum to sender.
        jmp(remote)             | And exit!

| trddram sends <count> data to the requester starting at <adrlo> <adrhi>.
| After it has finished, a simple, additive checksum is sent and the
| requester may do uhateYer with this information.
| Registers are defined as:
| R0: Byte sent to or received from the I/O board.
| count: Count of remaining bytes to send.
| dadrlo: <adrlo> in DRAM.
| dadrhi: <adrhi> in DRAM.
| sum: Additive checksum for simple check.

trddram:cmove(0, sum)           | Initialize checksum.
        cwriteio(1, IER)        | Interrupt on data ready.
trdlo:  pdisp(R0, trdlo1, trdlo, trdlo, remote)
trdlo1: refr()
        jnready(trddram)        | o byte, wait up.
        ioread(dadrlo)          | Put <adrlo> into R2.
trdhi:  pdisp(R2, trdhi1, trdhi, trdhi, remote)
trdhi1: refr()
        jnready(trdhi)          | waiting ...
        ioread(dadrhi)          | Put <adrhi into dadrhi.
trdcnt: pdisp(dadrhi, trdcnt1, trdcnt, trdcnt, remote)
trdcnt1: refr()
        jnready(trdcnt)         | waiting ...
        ioread(count)           | Put <count> into count.
        cwriteio(2, IER)        | Interrupt on transmitter empty.
trdsd:  pdisp(dadrhi, trdsd1, trdsd, trdsd, remote)
trdsd1: refr()
        jnready(trdsd)          | waiting ...
        l(dadrlo, dadrhi, R0)   | Load byte from DRAM in R0.
        iowrite(R0)             | Send a byte.
        add(R0, sum, sum)       | Simple checksum!
        cadd2(1, dadrlo, dadrlo)| Increment <addrlo> and <addrhi>.
        count(count)            | Decrement count.
        ccmp(0, count)          | Equal to zero?
        jne(trdsd1)             | Have we sent enough bytes?
trdsdw: pdisp(count, trdsdw1, trdsdw, trdsdw, remote)
trdsdw1: refr()
        jnready(trdsdw)         | Can we send the checksum?
        iowrite(sum)            | Yep, send the checksum to sender.
        jmp(remote)             | And exit!

| tldsram will load the SRAM startin at SRAM address <addr> with
| <count> bytes of data. <count> = 0 implies count is 256.
| A simple, one-byte, additive checksum is computed and returned to
| the sender when transfer is completed.
| Registers are defined as:
| R0: Byte sent to or received from the I/O board.
| count: Count of remainin bytes to receive.
| sadr: <adr> in SRAM.
| sum: Additive checksum for simple check.

tldsram: cwriteio(1, IER)       | Interrupt on data available.
        cmove(0, sum)           | Initialize checksum.
tlsadr: pdisp(R0, tlsadr1, tlsadr, tlsadr, remote)
tlsadr1: refr()
        jnready(tldsram)        | o byte available, wait up.
        ioread(sadr)            | Put <adrlo> into dadrlo.
tlscnt: pdisp(sadr, tlscnt1, tlscnt, tlscnt, remote)
tlscnt1: refr()
        jnready(tlscnt)         | waiting ...
        ioread(count)           | Put <count> into count.
tlsrc:  pdisp(sadr, tlsrc1, tlsrc, tlsrc, remote)
tlsrc1: refr()
        jnready(tlsrc)          | waiting 
        ioread(R0)              | Get a byte.
        add(R0, sum, sum)       | Simple checksum!
        movei(R0, sadr)         | Store byte in SRAM.
        cadd(1, sadr, sadr)     | Increment <addrlo> and <addrhi>.
        count(count)            | Decrement count.
        ccmp(0, count)          | Equal to zero?
        jne(tlsrc1)             | have we received correct number of byte:
        cwriteio(2, IER)        | Interrupt on transmitter empty.
tlsrcw: pdisp(count, tlsrcw1, tlsrcw, tlsrcw, remote)
tlsrcw1: refr()
        jnready(tlsrcw)         | waiting ...
        iowrite(sum)            | Yep, send the checksum to sender.
        jmp(remote)             | And exit!

| trdsram sends <count> data to the requester startin at <adr>.
| After it has finished, a simple, additive checksum is sent, and
| the requester may do whatever with this information.
| Registers are defined as:
| R0: Byte sent to or received from the I/O board.
| count: Count of remainin bytes to send.
| sadr: <adr> in SR.
| sum: Additive checksum for simple check.

trdsram: cmove(0, sum)          | Initialize checksum.
        cwriteio(1, IER)        | Interrupt on data ready.
trsadr: pdisp(R0, trsadr1, trsadr, trsadr, remote)
trsadr1: jnready(trdsram)        | o byte, wait up.
        ioread(sadr)            | Store SRAM address.
trscnt: pdisp(sadr, trscnt1, trscnt, trscnt, remote)
trscnt1: jnready(trscnt)         | Waiting ...
        ioread(count)           | Put <count> into count.
        cwriteio(2, IER)        | Interrupt on transmitter empty.
trssd:  pdisp(dadrhi, trssd1, trssd, trssd, remote)
trssd1: jnready(trssd)          | Waiting 
        imove(sadr, R0)         | Load byte from SRAM in R0.
        iowrite(R0)             | Send a byte.
        add(R0, sum, sum)       | Simple checksum!
        cadd(1, sadr, sadr)     | Increment <sadr>.
        count(count)            | Decrement count.
        ccmp(0, count)          | Equal to zero?
        jne(trssd1)             | Have we sent enough bytes?
trssdw: pdisp(count, trssdw1, trssdw, trssdw, remote)
trssdw1: jnready(trssdw)         | Can we send the checksum?
        iowrite(sum)            | Yep, send the checksum to sender.
        jmp(remote)             | And exit!

| Function 9: I/O INII
| ioinit intializes the I/O card to operate at the correct baud, etc
| Note: Card is initialized on power up, so this is not required.

reinit: call(ioinit)
        jmp(console)

| General routine to send information to the HP. These
| assume that the HP is attached and capable of receiving;
| if it's not, the routine will hang (but may be switch-reset).
| iosend sends the character in R0 to the HP.
| ioack sends an acknowledgement to the HP.

ioack:  cmove(MAYBE_ACK, R0) | c character (fall through to iosend).

iosend: cwriteio(2, IER)        | Interrupt on transmitter empty.
iosloop:pdisp(R0, ioscont, ioscont, ioscont, remote) | Does a refresh.
ioscont: refr()
        jnready(iosloop)        | Waiting ...
        iowrite(R0)             | Send the byte.
        rtn()

| Console loop functions. Can be entered by escaping from terminal loop.
| This is also what happens when P0, P1 sequence is entered from
| terminal loop.
| Fnnction dipatch table, used on P0, P1 sequence.

contab: WORD(con0)           | 00000000: Initialize DRAM, machine, then run.
        WORD(con1)           | 00000001: SRAM examine/deposit loop.
        WORD(con2)           | 00000010: DRAM examine/deposit loop.
        WORD(con3)           | 00000011: Initialize DRAM, machine.
        WORD(con4)           | 00000100: Initialize machine.
        WORD(con5)           | 00000101: Single machine instruction step.
        WORD(con6)           | 00000110: Run proram.
        WORD(pecho)          | 00000111: Primitive I/O echo test.
        WORD(remote)         | 00001000: I/O remote-control driver [TERM].
        WORD(reinit)         | 00001001: Init I/O [INIT].
CONFNS = 10                  | Total number of console functions defined.

| Here on P0, P1 sequence; dispatch on a function code in switches.

fndsp:  swt(R0)                 | Get function code from switches.
        ccmp(CONFNS, R0)        | Make sure it's in range...
        jmi(fnerr)              | Oops, operator blew it.

        pdisp(R0, fndsp1, fndsp, fndsp, fndsp)
fndsp1: dispatch(R0, contab) | The dispatch.

fnerr:  cmove(0xCC, R0)          | Gritch about undefined function ca
        cmove(0x33, R1)          | (make pretty flashing lights.)
        jmp(error1)

| Main console function:
| Flashes 11110000, 00001111 pattern, awaits buttons. when any button
| is pressed, dispatches to con1 (i.e., interprets buttons
| as in SRAM examine/deposit loop)

console: cmove(0xF0, R0)        | 11110000, 00001111 light pattern.
        cmove(0x0F, R1)         | (make pretty flashing lights.)
        jmp(error1)

| Function 0: Initialize DRAM and machine, run to completion:
con0:   call(DLoad)
        refr()
        call(MInit)             | Initialize virtual (S or G) machine
        refr()
        cmove4(0, MInstCount)   | Clear instruction eecution count.
        jmp(Go)                 | Run proram.

| Function 1: (default function) -- SRAM examine/deposit.
|       Display current SRAM address.
| P1: Examine current location; when released, increment current ad
|       Displays contents while P1 down, else address.
| P1,P0:Deposit switches in current location.
| P0: Set current location from switches, display contents.
| P0, P1: Dispatch on function.

| Pere on no buttons pressed: Display <sadr> (current SRAM address),
| wait for a button:

con1:   pdisp(sadr, con1, con11, con10, fndsp)

| Here when P1 pressed: Display C<sadr>>, await buttons:

con11:  imove(sadr, R0)
con11L: pdisp(R0, con11X, con11L, con10, con110)

con11X: cadd(1, sadr, sadr)     | P1 released: step to next SRAM adr.
        jmp(con1)

con110: swt(R0)                 | P1,P0 pressed: deposit <ST> in <sad
        movei(R0, sadr)

| Wait until button released, then back to con1:
con1X:  pdisp(R0, con1, con1X, con1X, con1X)

| Here on P0 pressed: set sadr from <ST>, display <sadr>, wait.
con10:  swt(sadr)
        imove(sadr, R0)
con10L: pdisp(R0, con1, con11, con10L, fndsp)

| Function 2: DRAM examine loop; similar in form to SRAM loop.
| P1: examine current location; when released, increment current address
|       Displays contents while P1 down, else low ORDER address.
| P1,P0:Deposit switches in current location.
| P0: Returns to SRAM loop, with current SRAM adr set to point to 2-byte
|       current DRAM adr (making it convenient -- well, possible -- to
|       change the DRAM address).

con2:   pdisp(dadrlo, con2, con21, con20, fndsp)

con21:  l(dadrlo, dadrhi, R0)
con21L: pdisp(R0, con21X, con21L, con20, con210)
con21X: cadd( 1, dadrlo, dadrlo)
        caddcy(0, dadrhi, dadrhi)
        jmp(con2)

con210: swt(R0)
        s(R0, dadrlo, dadrhi)

| Wait until buttons released, then back to con1:
con2X:  pdisp(R0, con2, con2X, con2X, con2X)

con20:  pdisp(R0, con20Y, con20, con20, fndsp)
con20Y: cmove(dadrlo, sadr)
        jmp(con1)

| Function 3: Load DRAM from ROM, fall through to function 4:
con3:   call(DLoad)             | Initialize DRAM from uROM.

| Function 4: Initialize machine.
con4:   call(MInit)             | Initialize virtual (S or G) machine state.
        cmove4(0, MInstCount)   | Clear instruction execution count.
        jmp(console)            | Return to console.

| Function 5: Single-step machine program: calls NextInst to execute instr.
con5:   call(SStep)             | execute single machine instruction,
        jmp(console)            | then return to console.

| Function 6: Run program to completion ... a..a. GO.
| Also determines the correct BREAK returned and
| places the appropriate pattern for display in the lights.
| The usage of BREAK is as follows:
|       BREAK = 00000000        Initial value when erecuting.
|       BREAK = 10000000        HALT executed.
|       BREAK = 10000001        breakpoint encountered.
|       BREAK = 11111111        Illegal instruction encountered.

Go:
con6:   cmove(0x00, BREAK)      | Clear BREAK FLAG.
con61:  call(SStep)
        ccmp(0x00, BREAK)       | BREAK Eecution?
        je(con61)               | Nope, continue.
        refr()
        ccmp(MAYBE_BPT, BREAK)  | BREAK for BPT?
        je(con61)               | Ignore Bpt().
        ccmp(MAYBE_HALT, BREAK) | BREAK for halt?
        jne(con62)              | Yup, display lights.
        cmove(0xE7, R0)         | Pattern for lights to say we're done.
        cmove(0x18, R1)
        jmp(error1)             | Flash away!
con62:  ccmp(MAYBE_II, BREAK)   | Illegal instruction? or bad halt code.
        cmove(0x81, R0)         | Do jcond since we're through checking.
        cmove(0x7E, R1)         | Pattern for lights.
        jmp(error1)             | Flash away!

| Microsubroutine to execute a single machine instruction; calls
| NextInst to do the uor.
| Counts instructions executed in MInstCount (4-byte counter);
| does some DRAM refresh before calling DetInst.

| Uses contents of reserved SRAM location _DEBUG to allow refresh and
| simulator breapoints to be disabled or enabled:
|   Bits of _DEBUG (SRAM location 11111101) have following effects:
|          Bit 0:       Disables refresh, for faster simulation.
|          Bit 1:       Causes breakpoint to be hit for every instructio
| Dormally <_DEBUG>=0, enablin DRAM refresh and disablin breapoints.

SStep:  cand(1, _DEBUG, T1)      | Test bit 0 of _DEBUG
        ccmp(0, T1)              | Should we refresh?
        jne(SStep1)              | Nope, skip it.

        refr()                   | Refreshes 54 rows per machine instr;
        refr()                   | this assumes about 1 ms/instruction.
        refr()                   | Slower instrs must add refr() calls.
        refr()                   | Note: refr()s should preferably be
        refr()                   | distributed in the code!
        refr()
        refr()
        refr()
        refr()
        refr()
        refr()

SStep1: cand(2, _DEBUG, T1)      | Test bit 2 of _DEBUG
        ccmp(0, T1)              | Should we skip the breakpoint?
        je(SStep2)               | Yes, don't single step.

|        bpt()                    | breakpoint instruction, for simulator.

SStep2: call(NextInst)           | Execute machine instruction.

        cadd2( 1, MInstCount, MInstCount)| Count machine instructions.
        caddcy(0, MInstCount+2, MInstCount+2)
        caddcy(0, MInstCount+3, MInstCount+3)

        rtn()

| Microsubr: Initialize DRAM from uROM locations between IDRAM and EDRAr

IDRAM = 0x1C00               | Last 1K bytes of uROM are user program bits.
EDRAM = 0x2000

DLoad:  cmove(IDRAM%256, R0)
        cmove(IDRAM/256, R1)
        cmove(0, R2)
        cmove(0, R3)
        cmove(EDRAM%256, R5)            | For end test.
        cmove(EDRAM/256, R6)

DLoad1: call(DLoad2)                    | Fetch byte from uROM into R4.
        s(R4, R2, R3)
        cadd2(1, R2, R2)
        cadd2(1, R0, R0)
        refr()
        cmp(R0, R5)                     | LSB match end address?
        jne(DLoad1)
        cmp(R1, R6)                     | MSB match end address?
        jne(DLoad1)

        rtn()                           | Yup: done downloading.

DLoad2: urom(R0, R1, R4)

| Error handler:
| jmp(error) with an error code in R0. Blinks error code in lights.
| To alternate between two patterns in lights, load R0 and R1, jmp(error1).
| Waits any button, then goes to con1 to interpret buttons.

| ERROR CODES:
| ===========
|       Pattern 1       Pattern 2       HEADING
|
|        11110000        00001111        Top-level console loop prompt
|        11100111        00011000        Halt
|        11001100        00110011        Bad function call [See contab:]
|        10000001        01111110        Illegal machine instruction
|        10101010        01010101        Terminal mode
|
|
|
|       Additional error codes for G-machine ONLY 
|
|
|
|       11111111        00000000        Bad general address mode
|
|
|
|       11000011        00111100        Bad CALL target effective address
|       11011011        00100100        Bad JUMP target effective address

error:  cmove(0, R1)             | The off-period light display.
error1: cmove(BLINK, T1)         | Initialize blink counter.
errorx: cmove(SBASE, uSP)        | Reinitialize micro SP.

| For simulator: get <R0> in lights, hit breakpoint.
        pdisp(R0, errory, errory, errory, errory)
errory: bpt()           | breakpoint for debugging.

| First: wait for switches to be released ...

erro1:  pdisp(R0, erro2, erro1x, erro1x, erro1x)
erro1x: call(err1)
        jmp(erro1)

| switches released, wait for button.
erro2:  pdisp(R0, erro2x, con1, con1, con1)
erro2x: call(err1)
        jmp(erro2)

| Subroutine to flash lights between values in <R0> and <R1>.
| Uses TO, T1 as counters, T3 as a temporary.

err1:   refr()
        refr()
        count(T1)
        je(err11)
err12:  rtn()
err11:  count(T2)
        jne(err12)
        cmove(BLINK, T2)        | The blink period time constant.
        move(R0, T3)            | Swap R0, R1.
        move(R1, R0)
        move(T3, R1)
        rtn()
        
| Handler for HALT: Return BREAK = 10000000.

hhalt:  bpt()                   | Stop, if simulating.
        cmove(0x80, BREAK)      | Set BREAK FLAG.
        rtn()                   | Return, let caller figure out what to do

| Handler for Illegal Instruction error: Return BREAK = 11111111.

hii:    bpt()                   | Stop, if simulating.
        cmove(0xFF, BREAK)      | Set BREAK = ERROR.
        rtn()                   | Return, let caller figure out what to do

| Handler for BREAKPOINT: Return BREAK = 10000001.

hbpt:   bpt()
|        cmove(0x81, BREAK)      | Set BREAK = HALT.
| (JMF) Cancel bpt detection because we use a simulator
        rtn()                   | Return, let caller figure out what to do

| Mintest is the common interrupt checking routine. It checks for any
| interrupts, and if an interrupt should occur (it is both being requested
| and its priority is higher than the processor's current priority),
| it calls the int routine (defined in s.uasm or g.uasm) with the address
| of the appropriate interrupt vector.
| Three interrupts are currently available. In order of priority:
|
| 1. Receive-interrupt, generated when a character is received.
| 2. P0-interrupt, generated when P0 RELEASED.
| 3. Clock-interrupt, generated after a given number of instructions.

| Interrupt vector addresses (in DRAM, of course!).
| Interrupt vector contents depend on the machine (S, G or beta).

SSVCVEC         = 0x100         | Supervisor call trap vector address.
RECIVEC         = 0x110         | Receive interrupt vector address.
POVEC           = 0x120         | P0 interrupt vector addres.
CLKVEC          = 0x130         | Clock interrupt vector addres.

| Interrupt priorities (in SRAM location "Prty" in the PS):

MAXPRI          = 0x7F          | The maximum possible priority.
RECIPRI         = 6             | Receive interrupt priority.
POPRI           = 4             | P0 interrupt priority.
CLKPRI          = 2             | clock interrupt priority.

| SRAM space used by the interrupt handlers:

Minth           = 0xCC       | (LGM) 2-byte temp to pass the vector address.
Mclkcount       = Minth + 2  | 4-byte counter, signals clk-int when < 0.

| Interrupt initialization: Clears all the flags. This is called
| by init in smachine, betamachine or gmachine.

Mintinit:
        cmove4(0, Mclkcount) | Clear clock-interrupt counter.
        rtn()

| Mintest is called before each SStep to see if an interrupt
| condition has arisen and to do housekeeping with the I/O
| deices. If an interrupt condition is true, the interrupt has
| a handler, and interrupts are enabled, then hint, defined
| by s.uasm or g.uasm, is called with the interrupt vector
| address in Minth.
|
| Mintest returns status in the BREAK flag. if BREAK is nonzero, the
| calling routine should exit.

Mintest: refr()                 | For time spent in this loop.

| check for any new data on the serial port. This is partly for
| information to the running program (the character is put into inchar)
| and partly for housekeeping, in case the interface has sent a
| STOP command.

        cwriteio(1, IER)        | Interrupt on Data Available.
        jnready(Mintest_no_new) | new data ?
        ioread(R0)              | Get the data.
        ccmp(MAYBE_STOP,R0)     | test for exit command from HP.
        je(Mintest_exit)        | Exit if true.

| If a character HAS been read, check that inchar is empty. If not,
| then the interface program on the HP has screwed up. It is supposed
| to wait for each normal character to be individually acknowledged
| so that the interface can be kept clear for other purposes.

        ccmp(0, inchar)         | Test if character already read.
        jne(Mintest_bad)        | Bad exit if true.
        move(R0, inchar)        | Save the character and fall throuh.
Mintest_no_new:

| more housekeeping: check the P0 switch and compare the value
| to oldpO. If the switch has just been released, the P0-interrupt
| may be called. This is signaled by setting intpO.

        jp0(Mintest_p0_set)
        ccmp(0, oldp0)          | check if P0 was zero before.
        je(Mintest_skip_p0)     | exit if it was and is zero.

        cmove(0, oldp0)         | as 1, is 0: Reset oldp0
        cmove(1, intp0)         | ....and signal a possible interrupt.
        jmp(Mintest_skip_p0)

 Mintest_p0_set:
        ccmp(0, oldp0)          | check if P0 as one before.
        jne(Mintest_skip_p0)    | exit if it is was and is one.

        cmove(1, oldp0)         | as 0, is 1: Set oldp0
        cmove(0, intp0)         | ....and reset the interrupt, if any.

Mintest_skip_p0:

| At this point, the housekeeping is done.
| Noa test to see if there is a character for the receive-interrupt.

        ccmp(0, inchar)         | Test if character erists.
        je(Mintest_no_recint)   | Slcip if no data.
        ccmp(RECIPRI-1, Prty)   | Is interrupt pri <= processor pri?
        jmi(Mintest_no_recint)  | (Yes, then interrupt is disabled.)

        cmove2(RECIVEC, Minth)  | Pointer to receive-interrupt vector.
        jmp(Mintest_do_int)

| If there is no receive-interrupt, check for a P0 interrupt.

Mintest_no_recint:
        ccmp(0, intp0)          | test if a P0 just released.
        je(Mintest_no_p0int)    | skip if not.
        ccmp(POPRI-1, Prty)     | Is interrupt pri <= processor pri?
        jmi(Mintest_no_p0int)   | (Yes, then interrupt is disabled.)

        cmove2(POVEC, Minth)    | Pointer to P0-interrupt vector.
        cmove(0, intp0)         | Reset for net interrupt.
        jmp(Mintest_do_int)

| Finally, if the other to conditions don't eYist, decrement the clock
| and, if it is zero, try a clock-interrupt.

Mintest_no_p0int:
        ccmp(0, Mclkcount+3)    | Is clock-count negative?
        jpl(Mintest_clkint)     | (Yes, request clock interrupt.)

        csub(Mclkcount, 1, Mclkcount) | (o, decrement clock count.)
        csubcy(Mclkcount+1, 0, Mclkcount+1)
        csubcy(Mclkcount+2, 0, Mclkcount+2)
        csubcy(Mclkcount+3, 0, Mclkcount+3)

Mintest_rtn:                    | Come here when no intr or left.
        rtn()                   | Done; return.

Mintest_clkint:                 | Here to request a clock interrupt.
        ccmp(CLKPRI-1, Prty)    | Is interrupt pri <= processor pri?
        jmi(Mintest_rtn)        | (Yes, then interrupt is disabled.)

        cmove2(CLKVEC, Minth)   | Pointer to clock-interrupt vector.

| Here is here the call to Mint is made. After the call, the Mintest
| routine simply returns to the caller.

Mintest_do_int:
        jmp(Mint)               | Call Mint, let it rtn to our caller.

| Tell the caller to exit. This is jumped to if a STOP code is
| received by the serial card.

Mintest_exit:
        cmove(MAYBE_STOP, BREAK) | Signal exit.
        rtn()

| Tell the caller to exit with bad status. This is jumped to
| if the serial buffer is overrun.

Mintest_bad:
        cmove(MAYBE_OVF, BREAK) | Signal overrun.
        rtn()
