/********************************* MFP.Z80 ************************************
                                                                
/ETHERNET DIAGNOSTIC MONITOR STATION FIRMWARE PROGRAM.
/W.I.WALKER MSC PROJECT 1984



/INITIALISATION

/Device addresses
dmaer=0    /                          er channel addr reg
dmaet=2    /                          et -------"--------
dmalr=4    /DMA registers             lr -------"--------
dmalt=6    /                          lt -------"--------
dma=8      /                          setmode/status reg
tc=16_10   /read-only (clears DMA TC interrupt)
int=16_10  /write-only interrupt enable (ltbe.lrdr.lrcr.dmatc)
lkc=16_20  /link control (read/write)
lkd=16_30  /link data
er=16_40   /read: data/control, write: filter/reset
ls=16_50   /(link status) read-only (rbf.rdr.rcr.0.0.0.0.tbe)
etd=16_50  /write-only: data into fifo
es=16_60   /read-only: (tcol.ta.tdone.rctrl.crce.rcol.ra.over)
et=16_60   /write-only: hold-off counter, reset

/Interrupt enable bits
tcintbit=8            /dma tc stop
rcintbit=4            /receive control ready
rdintbit=2            /   "    data      "
teintbit=1            /transmit empty

/DMA enable bits
erdmabit=1           /enable er channel
etdmabit=2           /  "    et    "
lrdmabit=4           /  "    lr    "
ltdmabit=8           /  "    lt    "
tcbit=16_40          /  "    tc stop

/ctrl chars
str=1                /start recording
stp=2                /stop recording
tic=3                /timing signal
stx=16_20            /start of data stream

/Miscellaneous constants
header=14
datamax=532
trailer=4
infobyte=4
maxpkt=header+datamax+trailer 
counter=maxpkt+1-1
ticsmax=10
buffsiz=16_2000
stksiz=16_200

/Variables
  area ram
  loc 16_1800
  loc stksiz+.              /locate stack
stktop:

var get,,lastget,,dbstart,,nextstrt,,nextblck,,addrsel,,recrd
var tics,miss,tflag,ints
/get: location of next byte to be sent to host
/lastget: location following current bottom of buffer
/dbstart: location at which next packet is to be buffered
/nextstrt: used when calculating next dbstart
/nextblock: location of next block to be created
/addrsel: location of start of addr selection code
/recrd: flag set to 1 if user requires recording
/tics:counter incremented when a timing signal arrives
/miss: flag set to 64 when block overwritten in buffer
/tflag: flag set to 1 when a block for timing purposes must be built
/ints: flag set to 1 when next packet is to be ignored

/diagnostics variables
/var pacr,,pact,,bytt,

dbufftop: loc buffsiz+.            /locate buffer
dbuffbot: loc maxpkt+infobyte+.    /locate buffer overflow

  area ix                  /block format
  loc 0
var blcklen,,status,time,dest,,,,,,source,,,,,,type, 

/Initial entry
  area rom
  loc 16_1000

  jp begin
var maxblcksiz,,rcvmode,stat1,port1,stat2,port2   /user specified variables
begin:
  di                                  

  ld sp,#stktop

  /set up address selection start point 
  ld a,rcvmode            /there are 5 modes for address selection
  or a; sbc a,10          /and this piece of code loads addrsel
  jp n,slall              /with the start address of appropriate
  dec a                   /selection code
  jp n,slfan0             /rcvmode=0 accept all packets
  dec a                   /rcvmode=10 accept packets going to or coming
  jp n,slfan1             /from a specified station (s-fan)
  sbc a,9                 /rcvmode=11 accept packets going to or coming
  jp n,slpnt0             /from a specfied station-port (sp-fan)
  dec a                   /rcvmode=20 accept packets transfered between
  jp n,slpnt1             /two specified stations (s-s)
  ld hl,#pnt2             /rcvmode=21 accept packets transfered between
  jp cont                 /specified station-port and station (sp-s)
slpnt1:                   /rcvmode=22 accept packets transfered between
  ld hl,#pnt1             /two specified station-ports (sp-sp)
  jp cont                 /
slpnt0:                   /
  ld hl,#pnt0             /
  jp cont                 /
slfan1:                   /
  ld hl,#fan1             /
  jp cont                 /
slfan0:                   /
  ld hl,#fan0             /
  jp cont                 /
slall:                    /
  ld hl,#addrpass         /
cont:                     /
  ld addrsel,hl           /

/inactive loop
inactive:
  in a,(ls)                /wait for a ctrl char to arrive
  and 32
  jr z,inactive
  in a,(lkc)
  xor str                  /if char = str then enable station
  jr nz,inactive           /otherwise ignore

/initialise diagnostics
/ld hl,0
/ld pacr,hl
/ld pact,hl
/ld bytt,hl

/inform host that station is enabled
  call ltwait
  ld a,str
  out (lkc),a


/initialise variables
  ld hl,#dbufftop+infobyte
  ld dbstart,hl
  ld nextstrt,hl
  ld hl,#dbufftop 
  ld get,hl       
  ld ix,get                /set up address of first block
  ld nextblck,hl  
  ld lastget,hl   
  xor a 
  ld tics,a                /set tics to 0
  ld tflag,a               /clear tflag
  ld miss,a                /  "   miss
  inc a
  ld ints,a                /set ints to 1
  ld recrd,a               /set recrd to 1

/initialise dma controller
  exx; ex af,af               /use a different register set for
                              /interrupt processing
  ld hl,dbstart               
  ld c,dmaer                  /reg c will always contain dmaer
                              /so that it need never be loaded again
  out (c),hl                  /load initial start addr
  inc c
  ld de,16_4000+counter       /load counter
  out (c),de
  dec c

  exx; ex af,af               /swop register set

/Initialise hardware
  ld a,16_80; out (er),a  /reset receiver (sets overrun),accept all packets
  in a,(er)               /(clears overrun)
  in a,(tc)               /reset DMA interrupt
  ld a,erdmabit+tcbit     /enable er channel and tc stop of dma cntrllr
  out (dma),a             /

/Set up interrupts
  ld a,tcintbit+rcintbit   /enable tc and rc interrupts
  out (int),a              /
  ld a,inttab>>8           /load high byte of interrupt table address
  ld i,a                   /into i reg
  im2                      /set interrupt mode to 2
  ei



/MAIN PROGRAM LOOP

idle:                      /loop while no blocks to be passed on to 68000.

  ld a,recrd               /if stp has been received while sending
  or a                     /last block then jmp to stprec
  jp z,stprec              /

  ld hl,nextblck           /
  ld de,get                /if nextblck and get not equal then there
  or a; sbc hl,de          /is a block to be passed on so leave theloop
  jr z,idle                /

/diagnostics
/ld hl,pact
/inc hl
/ld pact,hl

  call ltwait              /send stx to host to indicate start of block
  ld a,stx                 /
  out (lkc),a              /

  ld hl,get                
  call ltwait              /pass 1st byte of block to link data
  ld a,(hl); inc hl        /register
  out (lkd),a              /
  ld e,a                   /load low byte of blcklen field into e

  call ltwait
  ld a,(hl); inc hl          /pass 2nd byte of block to link data
  out (lkd),a                /register
  ld d,a                     /load high byte of blcklen field into d
  dec de                     /de holds count of number of bytes in this
  dec de                     /block tobe passed on to 68000.
                             /dec the 1st 2 bytes,
                             /remainderdecremented after ldrwait
send:
  call ltwait
  ld a,(hl); inc hl        /pass next byte of block to link data
  out (lkd),a              /register
  dec de                   /when de=0 then last byte of the block
  ld a,e; or 0             /has been passed on to the 68000
  jr nz,send               /
  ld a,d; or 0             /
  jr nz,send               /

  ld get,hl                /if get is not at end of buffer then return
  ld de,lastget            /to idle
  or a; sbc hl,de          /
  jp nz,idle               /
  ld hl,#dbufftop          /otherwise set get to top of the buffer
  ld get,hl                /and return to idle
  jp idle                  /

stprec:
  di                       /disable station
  call ltwait
  ld a,stp                 /inform host that station disabled
  out (lkc),a

/pass diagnostics to host
/ld c,lkd
/ld hl,#pacr
/call ltwait
/outi
/call ltwait
/outi
/ld hl,#pact
/call ltwait
/outi
/call ltwait
/outi
/ld hl,#bytt
/call ltwait
/outi
/call ltwait
/outi

  jp inactive              /wait for next ctrl char to be received


/INTERRUPT ROUTINES

/receiver done interrupt
interd:
  exx; ex af,af        /swop register sets so that main program registers
                       /are not corrupted
  xor a; out (dma),a   /disable dma 
  in a,(er)            /clear interrupt
  ld a,ints            /if ints=0 then packet received is valid so
  and 1                /jump to valid
  ld b,a               /clear b if jumping to valid
  jp z,valid           /

  xor a; ld ints,a     /next packet should be ok so clear ints
addrfail:
  ld a,tflag           /if tflag is set then an empty timing
  xor 0                /block should be created to keep timing accurate
  jp nz,tblck          /if packet invalid or addresses fail selection and
  ld hl,dbstart        /tflag not set then buffer next packet at same location
  call resetdma
  
  exx; ex af,af; ei; ret /return to main program

valid:
  in a,(es)            /read ether status and only keep
  and 12               /crce and col bits
  or b                 /b will set dma o/r bit if such an event has occurred
  ld b,a               /this byte will become the block status field

  and 8                /check crce bit of etherstatus
  jr nz,chkaddr        /if it is set then skip 'rece' check

/'rece' check
  ld a,0               /test redundent bytes in packet header
  push ix              /if any byte is not zero then a hardware
  pop hl               /error,probably during dma,transfer has
  ld de,infobyte+2     /occured.
  or a; adc hl,de      /
  ld e,(hl); xor e     /
  jp nz,dmaerr         /
  inc hl               /
  ld e,(hl); xor e     /
  jp nz,dmaerr         /
  inc hl               /
  ld e,(hl); xor e     /
  jp nz,dmaerr         /
  inc hl               /
  ld e,(hl); xor e     /
  jp nz,dmaerr         /
  inc hl; inc hl       /
  inc hl               /
  ld e,(hl); xor e     /
  jp nz,dmaerr         /
  inc hl               /
  ld e,(hl); xor e     /
  jp nz,dmaerr         /
  inc hl               /
  ld e,(hl); xor e     /
  jp nz,dmaerr         /
  inc hl               /
  ld e,(hl); xor e     /
  jp nz,dmaerr         /
                       
chkaddr:               /jump to the addr checking algorithm corresponding
  ld hl,addrsel        /to the receiving mode selected
  jp (hl)

fan0:
  ld a,stat1
  ld e,a
  ld a,source           /if source = specified addr then pass
  xor e
  jp z,addrpass  
  ld a,dest             /if dest =   -----------"------------
  xor e
  jp z,addrpass  
  ld a,dest             /if dest =   broadcast addr then pass
  xor 0
  jp z,addrpass  
  jp addrfail           /otherwise fail

fan1:
  ld de,stat1
  ld hl,source
  or a; sbc hl,de
  jp z,addrpass         /if source =specified addr then pass
  ld hl,dest
  or a; sbc hl,de
  jp z,addrpass         /if dest =specified addr then pass
  ld a,dest
  xor 0
  jp nz,addrfail /otherwise if dest addr is not a broadcast then fail
  ld a,e
  xor 0
  jp z,addrpass  /if dest addr is a broadcast and the specified port 
  jp addrfail    /is port0 then pass otherwise fail

pnt0:
  ld a,stat1
  ld e,a
  ld a,stat2
  ld l,a
  ld a,source           /if source = 1st specified addr
  xor e                 /then test dest    *JHB*
  jp z,dest20           
  ld a,source           /if source = 2nd specified addr
  xor l                 /then continue otherwise fail
  jp nz,addrfail 
  ld a,dest             /if source = 2nd addr and 
  xor e                 /dest = 1st addr or
  jp z,addrpass   
  ld a,dest             /broadcast addr then pass
  xor 0
  jp z,addrpass  

  jp addrfail           /otherwise fail
dest20:
  ld a,dest             /if source = 1st addr and
  xor l                 /dest =2nd addr or
  jp z,addrpass  
  ld a,dest             /broadcast addr then pass
  xor 0
  jp z,addrpass  
  jp addrfail           /otherwise fail

pnt1:                   /stat1 is sp specified
                        /stat2 is s  specified
  ld hl,stat1           
  ld de,source
  or a; sbc hl,de
  jp z,dest21              /if source=1st addr then check dest
  ld a,stat2               /otherwise check source against 2nd addr
  xor d
  jp nz,addrfail           /if source not = to either addr then fail
  ld hl,stat1              /check dest against 1st addr
  ld de,dest               /
  or a; sbc hl,de          /
  jp z,addrpass            /pass if source=2nd addr and dest =1st addr
  ld a,d                   /check dest for broadcast
  xor 0
  jp nz,addrfail    
  ld a,port1               /if dest is bcast then check that port0 is
  xor 0                    /the port specified by user
  jp z,addrpass     
  jp addrfail
dest21:                    /check dest against 2nd addr
  ld a,stat2
  ld d,dest
  xor d
  jp z,addrpass            /pass if source=1st addr and dest=2nd addr
  ld a,d                   /check dest for bcast 
  xor 0
  jp z,addrpass
  jp addrfail

pnt2:                     /stat1 and stat2 are sp specified
  ld hl,stat1             /*JHB*
  ld de,source
  or a; sbc hl,de
  jp z,dest22            /if source=1st addr then test dest
  ld hl,stat2
  or a; sbc hl,de
  jp nz,addrfail         /if source not = 1st or 2nd addr then fail
  ld hl,stat1
  ld de,dest
  or a; sbc hl,de
  jp z,addrpass          /if source=2nd addr and dest=1st addr then pass
  ld a,d                 /check dest for bcast
  xor 0
  jp nz,addrfail
  ld a,port1             /check that port specified is port0
  xor 0
  jp z,addrpass
  jp addrfail
dest22:
  ld hl,stat2
  ld de,dest
  or a; sbc hl,de
  jp z,addrpass          /if source=1st addr and dest=2nd addr then pass
  ld a,d                 /check dest for bcast
  xor 0
  jp nz,addrfail
  ld a,port2             /check that port specified is port0
  xor 0
  jp z,addrpass
  jp addrfail

dmaerr:
  ld a,b              /set rece bit in status field
  or 16               /
  ld b,a              /
  ld hl,header+infobyte /only include info bytes and header in the block
                        /load hl with proposed next dma start addr
  ld blcklen,hl       /
  ld de,dbstart       /
  or a; adc hl,de     /
  ld nextstrt,hl      /

addrpass:
  ld a,miss            /if miss=0 then do not set miss bit
  xor 0; jr z,noloss   /

  ld a,time            /but if miss is set then a block is being
  ld d,a               /overwritten. the timing field of the overwritten
  ld a,tics            /block must be added to the current value
  add a,d              /of tics.
  ld time,a            /
  ld a,status          /the fifo o/f bit must be preserved
  and 1                /
  or b                 /
  or 64                /the miss bit in the status field must be set
  ld status,a          /
  ld b,a               /
  jp stsdone           

noloss:
  ld a,tics            /load time field and status field
  ld time,a            /
  ld a,b               /
  ld status,a          /

stsdone:
  xor a                 /clear tflag and miss 
  ld tflag,a            /
  ld miss,a             /
  ld tics,a             /clear tics
  ld a,b                /if dmaerr or empty bits set then blcklen
  and 144               /has already been calculated and dma start proposed
  jr nz,chk1strt        /so jump to check start code

  in hl,(c)             /now calculate where the next packet to be received
  ld de,infobyte-2      /is to be stored:add infobyte sub checksum
  or a; adc hl,de       /
  ld nextstrt,hl        /
  ld de,dbstart         /calc length of block and set blcklen to it
  or a; sbc hl,de       /
  ld blcklen,hl         /
  ld de,maxblcksiz      /check if block is longer than the max block length
  or a; sbc hl,de       /if it is not then blcklen is ok so check nextstrt.
  jp n,chk1strt         /

  ld blcklen,de         /otherwise reset blcklen to maxblcksiz
  ld hl,dbstart         /and reset nextstrt
  or a; adc hl,de       /
  ld nextstrt,hl        /

chk1strt:
  ld de,nextstrt           /first check for bottom of the buffer
  ld hl,#dbuffbot+infobyte /if nextstrt < dbuffbot then ok
  or a; sbc hl,de          /
  jp p,chk2strt            /

  ld hl,nextstrt           /else set a marker (lastget) so that main
  ld de,infobyte           /program loop knows when it must start
  or a; sbc hl,de          /transfering bytes from the top of the buffer
  ld lastget,hl            /
  ld hl,#dbufftop+infobyte /and set nextstrt to top of the buffer
  ld nextstrt,hl           /

chk2strt:
  ld de,nextstrt        /now test whether a packet loaded from the next
  ld hl,get             /start addr could possibly overwrite any
  or a; sbc hl,de       /data not yet passed on to the 68000
  jp n,rset1dma         /if get < next start addr then not possible to overwrite

  ld de,maxpkt+infobyte /if get >= next start addr then
  or a; sbc hl,de       /if distance between next start and get is
  jp p,rset1dma         / > max packet length then not possible to overwrite

  ld a,64               /if the distance is < max packetsize then do not
  ld miss,a             /change dbstart (thus overwritting the
  ld hl,dbstart         /last packet received) and set miss flag
  jp rset2dma

rset1dma:

/diagnostics
/ld hl,pacr
/inc hl
/ld pacr,hl

  ld hl, nextstrt      /reset dbstart and preserve it in hl
  ld dbstart,hl
  push hl              
  ld de,infobyte       
  or a; sbc hl,de      
  ld nextblck,hl       /set nextblck and ix reg to start of next block
  push hl              /
  pop ix               /
  pop hl               /
rset2dma:
  call resetdma        /

  exx; ex af,af; ei; ret  /return to main program


/link receiver control ready interrupt
intrcr:
  exx; ex af,af        
                       
  in a,(lkc)           /read char received 
  cp tic               /jmp to piece of code that handles
  jr z,cctic           /response to the char
  cp stp               /
  jr z,ccstp           /
                       /if char not recognised then ignore it
  exx; ex af,af; ei; ret  /return from interrupt

/timing char received
cctic:
  ld a,tics            /increment tics
  inc a                /
  ld tics,a            /
  xor ticsmax          /if tics=maxtics then a block must be built
  jr nz,rtn            /otherwise return from interrupt

  in a,(es)            /if ra bit in ether status reg is set (receiver 
  and 2                /is active) then jump to setflag
  jp nz,setflg         /

  xor a; out (dma),a    /freeze the dma 
  in de,(c)             /if dbstart (in hl) and contents of dma addr reg are
  or a; sbc hl,de       /different then a packet has just been received
  ld hl,dbstart         /
  jp nz,retqck          /but not yet processed so jump to retqck 

tblck:                  /create a small block for timing purposes only
  ld de,infobyte        /load blcklen with infobyte as these will be the
  ld blcklen,de         /only bytes in the block
  or a; adc hl,de       /(hl=dbstart,de=infobyte).propose next dma start addr
  ld nextstrt,hl        /so that timing block has only infobytes
  ld b,160              /set empty and timing bits for the status field
  jp addrpass           /set status and time fields

retqck:
  ld a,erdmabit+tcbit   /reenable dma
  out (dma),a           /
setflg:
  ld a,1               /set tflag so that next packet to be processed
  ld tflag,a            /will have timing bit in the status field set
rtn:
  exx; ex af,af; ei; ret 

/recording to be stopped
ccstp:
  xor a                  /set recrd to 0
  ld recrd,a             /

  exx; ex af,af; ei; ret


/dma completion interrupt
intdtc:
  exx; ex af,af         
                       /disable dma not required as this intrpt does it anyway
  in a,(tc)            /clear interrupt
  ld a,ints            /if ints=1 then packet is not valid so jump
  and 1                /to addrfail
  jp nz,addrfail       /

  ld a,1                /set ints to 1
  ld ints,a             /so that next packet(end of this one) will be ignored
  ld b,2                /b will be used to set dma o/r bit in status field
  jp valid              /now that b is set process as normal packet


/fifo overflow interrupt
intovf:
  exx; ex af,af        
  xor a; out (dma),a   /disable dma
  in a,(er)            /clear interrupt
  ld a,ints            /if ints=1 then packet is not valid so jump to addrfail
  and 1                /
  jp nz,addrfail       /
                       /create a small block to indicate packets lost
  ld a,1               /set ints to 1
  ld ints,a            /so that next packet received will be ignored
  ld de,infobyte       /load blcklen with infobyte as they will be the only
  ld blcklen,de        /bytes in this block
  or a; adc hl,de      /(hl=dbstart) propose next dma start addr so that this
  ld nextstrt,hl       /block only contains infobytes
  ld b,129             /b will be used to set fifo o/f and empty bits
                       /in status field
  jp addrpass          /set status field bits and check next dma start addr


/Unsolicited interrupts
intspu:
  halt



/SUBROUTINES

ltwait:
  in a,(ls)              /if tbe bit in link status register
  rra                    /set then another character can be 
  jr nc,ltwait           /sent so return from routine

/diagnostics
/push hl
/ld hl,bytt
/inc hl
/ld bytt,hl
/pop hl

  ret

resetdma:
  out (c),hl              /load initial start addr
  inc c
  ld de,16_4000+counter
  out (c),de              /load counter
  ld a,erdmabit+tcbit
  out (dma),a             /enable er channel and tc stop
  dec c
  ret


/(256-byte-aligned) interrupt table
  block 256-.&255
inttab:
  w intovf     /ovf
  w interd     /erd
  w intspu     /etd
  w intdtc     /dtc
  w intrcr     /rcr
  w intspu     /rdr
  w intspu     /tbe
  w intspu     /spu

  end
