   ttl   APM System Primary Bootstrap

* APM System Primary Bootstrap
* (c) November 1984 All rights reserved
* Rainer W Thonnes
* Edinburgh University Computer Science Dept

* Configurable to load off remote filestore or
* one of three kinds of local disk.
* Local disk is specified by asking to load from
* a pseudo-ether-station with address $80.

* Revised version with "virtual" block disk read routine,
* which reads the specified block within one of two
* boot areas, as determined by the key switch.

   bin

   list
*disk equ 0        Remote Filestore
*disk equ '2284'   NEC 7261 with Fujitsu 160 MB
*disk equ '2294'   NEC 7261 with Fujitsu 330 MB
disk equ 'mini'   WD-1000 controller
*disk equ 'pace'   Ambit-Pace controller

* Configuration variables (in rom)

gropelimit equ  $ffc
gropestart equ  $ff8
version    equ  $ff4
defstat    equ  $ff7
deffile    equ  $ff0

* Communication variables

memtop     equ  $3ffc
membot     equ  $3ff8
freetop    equ  $3ff4
freebot    equ  $3ff0
ffile      equ  $3fac
lstat      equ  $3fa8
lport      equ  $3fa9
rstat      equ  $3faa
rport      equ  $3fab

* Local store limits

ram        equ $1000
syssp      equ $3f00

* VDU (ACIA) definitions

vdus equ $4000c1
vdud equ $4000c3
vinit equ 3
viof  equ $11
vrrfbit equ 0
vtrebit equ 1

cr  equ 13
lf  equ 10
nl  equ 10
del equ 127
esc equ 27
cscreen equ 'v'

* Ethernet definitions

eths equ $7fffc      * Status register
ethd equ $7fffd      * Data register
ethc equ $7ffff      * Control register
einit  equ $40
eiof   equ 0
etebit equ 3
erdbit equ 2
ercbit equ 1
opn equ $80
cls equ $90
rdy equ $10
stx equ $20
dtx equ $30
ack equ $c0
nak equ $50
etx equ $0b
esa equ $05
ssa equ $07

* Other definitions

arbiter equ $f00003
map0    equ $080000
mapinc  equ $100000

  ifne disk

* Disk definitions

   ifeq disk-'pace'
diskcsr equ $ed0000
   endc disk='pace'
   ifeq disk-'mini'
   org $7ffe0
diskcsr     ds.w 1
disksdh     ds.w 1
diskcylh    ds.w 1
diskcyll    ds.w 1
disksector  ds.w 1
disksectors ds.w 1
diskerror   ds.w 1
diskdata    ds.w 1
   endc disk='mini'
   ifeq (disk!'10')-'2294'
   org $70000
diskbuf ds.b 4096  * Disk buffer on controller board
diskdbr ds.b 1     * 7261 data fifo
diskcsr ds.b 1     * 7261 control reg
diskbcr ds.w 1     * board control register
   endc disk#'mini'

  endc disk#0

*
* START OF ROM CODE
*

   org 0

   dc.l syssp   * Initial stack pointer
   dc.l begin   * Initial program counter
   dc.l v2      * Interrupt and Exception vectors
   dc.l v3
   dc.l v4
 nolist
 dc.l v5
 dc.l v6
 dc.l v7
 dc.l v8
 dc.l v9
 dc.l v10
 dc.l v11
 dc.l v12
 dc.l v13
 dc.l v14
 dc.l v15
 dc.l v16
 dc.l v17
 dc.l v18
 dc.l v19
 dc.l v20
 dc.l v21
 dc.l v22
 dc.l v23
 dc.l v24
 dc.l v25
 dc.l v26
 dc.l v27
 dc.l v28
 dc.l v29
 dc.l v30
 dc.l v31
 dc.l v32
 dc.l v33
 dc.l v34
 dc.l v35
 dc.l v36
 dc.l v37
 dc.l v38
 dc.l v39
 dc.l v40
 dc.l v41
 dc.l v42
 dc.l v43
 dc.l v44
 dc.l v45
 dc.l v46
 dc.l v47
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
 dc.l v23
   list
   dc.l v23
   dc.l v23
   dc.l v23

* Miscellaneous entry points

   move.l deffile,a0 * 400: reload (default filename)
   bra    reload     * 404: reload (A0=filename)
   bra    load       * 408: load file (A0) to (A1)+
   bra    lhex       * 40c: phex
   bra    whex       * 410: phex4
   bra    bhex       * 414: phex2
   bra    nhex       * 418: phex1
   bra    psym       * 41c: printsymbol
   bra    pstring    * 420: printstring

good macro
v\1 move.l \1*4+ram,-(sp)
    rts
    endm

bad macro
v\1 bsr badvector
    dc.w \1*4
    endm

* Unacceptable vector numbers are re-cast as
* address errors at the vector address

badvector move.l a0,-(sp) * save A0
    move.l 4(sp),a0       * @ vector address .w
    move.w #$dead,6(sp)   * value for IR
    move.w (a0),4(sp)     * vector address...
    move.l (sp),a0        * restore A0
    move.w #0,2(sp)       * ...zero-extended
    move.w #$1e,(sp)      * function code SPRnot
    move.l ram+12,-(sp)   * soft address error vector
    rts

    good <2>  bus error
    good <3>  address error
    good <4>  illegal
    good <5>  zero divide
    good <6>  chk
    good <7>  trapv
    good <8>  privilege
    good <9>  trace
    good <10> A emulator
    good <11> F emulator
    bad  <12>  reserved...
    bad   13
    bad   14
    bad   15
    bad   16
    bad   17
    bad   18
    bad   19
    bad   20
    bad   21
    bad   22
    bad  <23>  ...reserved
    good <24> spurious interrupt
    good <25> autointerrupt 1...
    good  26
    good  27
    good  28
    good  29
    good  30
    good <31> ...autointerrupt 7
    good <32> trap 0...
    good  33
    good  34
    good  35
    good  36
    good  37
    good  38
    good  39
    good  40
    good  41
    good  42
    good  43
    good  44
    good  45
    good  46
    good <47> ...trap 15

* Print character D0 on VDU
* preserving registers and SR

psym cmp.b #lf,d0
   beq.s psy
   cmp.b #cr,d0
   bne.s ps0
psy eor.b #cr!!lf,d0
   bsr.s ps0
   eor.b #cr!!lf,d0
   bra ps0
ps0 btst.b #vtrebit,vdus
   beq ps0
   move.b d0,vdud
ps1 btst.b #vtrebit,vdus
   beq ps1
   rts

* Print D0 in hex

lhex swap d0
   bsr.s whex
   swap d0
whex rol.w #8,d0
   bsr.s bhex
   rol.w #8,d0
bhex rol.b #4,d0
   bsr.s nhex
   rol.b #4,d0
nhex move.l d1,-(sp)
   moveq #15,d1
   and.b d0,d1
   cmp.b #9,d1
   ble.s nh1
   addq.b #7,d1
nh1 add.b #'0',d1
   exg d0,d1
   bsr psym
   exg d0,d1
   move.l (sp)+,d1
   rts

* Print string A0 on VDU
* length-prefixed but 0-terminated

pstring movem.l d0/d1/a0,-(sp)
   move.b (a0)+,d1
   beq.s ps3
ps2 move.b (a0)+,d0
   beq.s ps3
   bsr psym
   subq.b #1,d1
   bne ps2
ps3 movem.l (sp)+,d0/d1/a0
   rts

ptext macro
\* move.l a0,-(sp)
   lea t\@,a0
   bsr pstring
   bra.s e\@
t\@ dc.b 255,\1,0
   ds.w 0
e\@ move.l (sp)+,a0
   endm

* Wait for ether station
* TX buffer to empty

flush btst.b #etebit,eths
   beq flush
   rts

* Read control character from ether station to D0

skipdc move.b ethd,d0
readcc move.b eths,d0
   btst #erdbit,d0
   bne skipdc         * Data: skip
   btst #ercbit,d0
   beq readcc         * No control: loop
   move.b ethc,d0     * Read control
   cmp.b #nak,d0      * Jump out if NAK
   beq gotnak
   rts

* Read data character from ether station

readdc move.b eths,d0
   btst #erdbit,d0
   beq readdc
   move.b ethd,d0
   rts

* Wait for RDY

rdywait bsr readcc
   cmp.b #rdy,d0
   bne rdywait
   bsr readdc
   rts

* Send DTX, wait for RDY, send STX

out bsr flush
   move.b #dtx,ethc
out1 bsr flush
   move.b lport,ethd
   bsr rdywait
   move.b #stx,ethc
   bsr flush
   move.b lport,ethd
   bsr flush
   rts

* Wait for DTX, send RDY, wait for STX

in bsr readcc
   cmp.b #dtx,d0
   bne in
   bsr readdc
   move.b #rdy,ethc
   bsr flush
   move.b lport,ethd
stxwait bsr readcc
   cmp.b #stx,d0
   bne stxwait
   bsr readdc
   rts

* Open a local port to the remote filestore

open bsr flush
   move.b #opn,ethc
   bsr out1
   move.b rstat,ethd
   bsr flush
   move.b rport,ethd
   bsr flush
   move.b #etx,ethc
   bsr flush
   rts

* Load file (A0) to (D0)+
* result: length in D0, endad in A0

load tst.b lport
  ifne disk
    beq diskload
  endc disk#0
   movem.l d1-d2/a1,-(sp)
   move.l d0,a1
   bsr out
   move.b #'Z',ethd   * Send Readfile command,
   bsr flush
   move.b #'0',ethd
   bsr flush
   moveq #0,d1
   move.b (a0)+,d1
   subq #1,d1
l1 move.b (a0)+,d0    * the filename,
   move.b d0,ethd
   bsr flush
   dbra d1,l1
   move.b #nl,ethd    * newline, and ETX.
   bsr flush
   move.b #etx,ethc
   bsr in             * Read response line packet
   bsr readdc
   cmp.b #'-',d0
   beq fserr
   bsr readcc         * skip to ETX
   moveq #0,d2        * Size counter
l2 bsr in             * Read data packet
   move #512,d1
l3 move.b eths,d0     * Wait for data or control
   btst #erdbit,d0
   bne.s l4           * data ->
   btst #ercbit,d0
   beq.s l3           * no control: loop
   move.b ethc,d0     * skip ETX
   tst d1             * was this a 512-byte block?
   beq l2             * yes: read another
   move.l d2,d0       * no: terminate
   movem.l (sp)+,d1-d2/a1
   rts
l4 move.b ethd,(a1)+  * read one character
   addq.l #1,d2       * count it
   subq #1,d1         * adjust intra-block count
   bra l3

  ifne disk                 * Disk handling routines

   ifeq disk-'pace'         * Ambit-Pace controller

diska move.b diskcsr,d1         Wait for A0
   cmp.b #$a0,d1
   bne diska
   move.b d0,diskcsr            Send command code
dw1 move.b diskcsr,d1           Wait for A1
   cmp.b #$a1,d1
   bne dw1
   move.b #$ff,diskcsr          Send FF
* and fall through to
diskw movem.l d0-d7/a0-a6,-(sp) Wait >= 50 us
   movem.l (sp)+,d0-d7/a0-a6
   movem.l d0-d7/a0-a6,-(sp)
   movem.l (sp)+,d0-d7/a0-a6
   movem.l d0-d7/a0-a6,-(sp)
   movem.l (sp)+,d0-d7/a0-a6
   rts

diskb move.b #$ff,diskcsr      Send FF
   bsr diskw                   Wait 50 us
dw2 move.b diskcsr,d1          Wait for FF
   cmp.b #$ff,d1
   bne dw2
   move.b #$fe,diskcsr         Send FE
dw3 move.b diskcsr,d0          Wait for xx&80=0
   bmi dw3
   move.b diskcsr,d0           Get status
   move.b #0,diskcsr           Send 00
   moveq #$60,d1               Check for error
   and.b d0,d1
   bne.s dw4
   rts
dw4 bsr bhex
   lea diskmerr,a0
   bsr pstring
   bra retry
diskmerr dc.b 255,' disk error',0
   ds.w 0

diskreset move.b #0,diskcsr  Send initial 00
   move.b #$42,d0            Write buffer command
   bsr diska
   move.b #$ff,diskcsr
   move.b #0,diskcsr
   move.b #0,diskcsr
   move.b #0,diskcsr
   move.b #0,diskcsr
   move.b #0,diskcsr
   move.b #4,diskcsr
   move.b #(640-1)&255,diskcsr
   move.b #(640-1)>>8,diskcsr
   move.w #512-1-16+7,d0
dil move.b #0,diskcsr
   dbra d0,dil
   bsr diskb
   move.b #2,d0              Init command
   bsr diska
   bsr diskb
   rts

diskread movem.l d0-d3,-(sp)
   move.b #$21,d0            Read disc command
   bsr diska
   move.l (sp),d0            Byte number
   moveq #9,d1
   lsr.l d1,d0               Block number
   moveq #15,d1
   and d0,d1                 Sector number
   lsr.l #4,d0
   moveq #3,d2
   and d0,d2                 Head number
   lsr.l #2,d0               Cylinder number
   move.b #0,diskcsr
   move.b d2,diskcsr
   move.b d0,diskcsr
   lsr #8,d0
   move.b d0,diskcsr
   move.b d1,diskcsr
   move.b #0,diskcsr
   move.b #0,diskcsr
   move.b #0,diskcsr
   bsr diskb
   move.b #$41,d0           Read buffer command
   bsr diska
   move.w #511,d0
drl move.b diskcsr,(a0)+
   dbra d0,drl
   bsr diskb
   movem.l (sp)+,d0-d3
   rts

   endc disk='pace'
   ifeq disk-'mini'

diskwait move.w diskcsr,d0
   btst #7,d0               loop while busy
   bne diskwait
   move.w diskcsr,d0        inspect status
   btst #0,d0
   bne diskw1
   rts                      return if no error
diskw1 lea diskmerr,a0      otherwise report error
   bsr pstring
   lea diskcsr,a0
   movep.l 1(a0),d0
   bsr lhex
   movep.l 9(a0),d0
   bsr lhex
   move.l (sp),d0
   bsr lhex
   bra retry
diskmerr dc.b 255,'Disk error ',0
   ds.w 0

* Initialise controller
diskreset move.w #$20,diskerror (precomp)
   clr.w d7
   move.w d7,disksdh          drive 0
   move.w #$13,diskcsr        restore
   bsr diskwait
   move.w d7,diskcylh
   move.w d7,diskcyll
   move.w #$70,diskcsr        seek
   bsr diskwait
   move.w #8,disksdh          drive 1
   move.w #$13,diskcsr
   bsr diskwait
   move.w d7,diskcylh
   move.w d7,diskcyll
   move.w #$70,diskcsr
   bsr diskwait
   rts

* Read 512-byte-block D0>>9 to (A0)+
diskread movem.l d0-d3,-(sp)
   moveq #9,d1
   lsr.l d1,d0
   and.l #$3fff,d0
   moveq #15,d1
   and d0,d1
   add d1,d1         sector
   lsr #4,d0
   moveq #3,d2
   and d0,d2         track
   lsr #2,d0
   clr.l d3
   move.b d0,d3      cylinder
   lsr #5,d0
   and #8,d0
   or d0,d2          sdh
   move.w d1,disksector
   move.w d2,disksdh
   move.w #0,diskcylh
   move.w d3,diskcyll
   move.w #$20,diskcsr
   bsr diskwait
   move.w #255,d0
diskr1 move.b diskdata+1,(a0)+
   dbra d0,diskr1
   add.w #1,disksector
   move.w #$20,diskcsr
   bsr diskwait
   move.w #255,d0
diskr2 move.b diskdata+1,(a0)+
   dbra d0,diskr2
   movem.l (sp)+,d0-d3
   rts

  endc disk='mini'
  ifeq (disk!'10')-'2294'

   ifeq disk-'2284'         * 10 tracks per cylinder
tracks equ 10
   endc disk='2284'
   ifeq disk-'2294'
tracks equ 16
   endc disk='2294'

parm macro
\* move.b \1,diskdbr
   endm

* Wait for controller
cwait move.b diskcsr,d0
   bmi cwait                * busy ->
   cmp.b diskcsr,d0
   bne cwait                * unstable ->
   rts

* Wait for SRQ
dwait moveq #16,d0
   and.b diskcsr,d0
   beq dwait
   move.b d0,diskcsr
   bsr cwait
   moveq #7,d0
   and.b diskdbr,d0
   rts

* Controller initialisation data
* Mode,DtlH+,DtlL,Etn,Esn,Gpl2,Mgpl1
cdata dc.b $40,$82,0,tracks-1,31,16,10
   ds.w 0

* Initialise controller
diskreset move.w #$8000,diskbcr
   moveq #-1,d0
   dbra d0,*
   move.w #0,diskbcr        * normal
   dbra d0,*
   moveq #6,d0
   lea cdata,a0
cloop parm (a0)+
   dbra d0,cloop
   move.b #$20,diskcsr      * specify
dwait7 bsr dwait
   cmp #7,d0
   bne dwait7
   move.b #$50,diskcsr      * recal
dwait8 bsr dwait
   tst d0
   bne dwait8
   rts

* Read 512-byte-block D0>>9 to (A0+)
diskread movem.l d0-d4/a1,-(sp)
   lsr.l #5,d0
   lsr.l #4,d0
   divu #32,d0
   swap d0
   move d0,d4     *sector
   clr.w d0
   swap d0
   divu #tracks,d0
   swap d0
   move d0,d3     *track
   swap d0
   move d0,d2     *cyll
   ror.w #8,d0
   move d0,d1     *cylh
   move.b #2,diskcsr     * clear fifo
   parm d1               * CylH
   rol.w #8,d0
   parm d2               * CylL
   move.b #$60,diskcsr   * Seek
dwait0 bsr dwait
   tst d0
   bne dwait0
   parm d3               * Head
   parm #0               * Flag
   parm d1               * CylH
   parm d2               * CylL
   parm d3               * Track
   parm d4               * Sector
   parm #1               * Nsectors
   move.b #$b0,diskcsr   * Read
   move.w #$1000,diskbcr * DMA in
   bsr cwait
*  clr.l d0
*  move.w d0,diskbcr
*  move.b diskcsr,d0
   move.w #0,diskbcr
   rol.w #8,d0
   move.b diskdbr,d0
   and.w #$efff,d0
   cmp.w #$4000,d0
   bne.s derr
   lea diskbuf,a1
   move.w #255,d0
mloop move.w (a1)+,(a0)+
   dbra d0,mloop
   movem.l (sp)+,d0-d4/a1
   rts
derr move.l a0,-(sp)
   move.w d0,-(sp)
   lea derrmess,a0
   bsr pstring
   move.w (sp)+,d0
   bsr whex
   moveq #' ',d0
   bsr psym
   move d1,d0
   bsr bhex
   move d2,d0
   bsr bhex
   moveq #' ',d0
   bsr psym
   move d3,d0
   bsr bhex
   moveq #' ',d0
   bsr psym
   move d4,d0
   bsr bhex
   moveq #nl,d0
   bsr psym
   bsr diskreset
   move.l (sp)+,a0
   movem.l (sp)+,d0-d4/a1
   bra diskread
derrmess dc.b 255,'Disk error ',0
   ds.w 0

   endc disk#'mini'

* Block 0 of the disk contains a directory, the
* layout of which is as follows:
* 512 bytes in all:
* 1 longword checksum (such that the sum of the 128
*                 longwords is 0, using IBM byte sex)
* 21 24-byte file records
* 1 longword trailer which must be zero, as it is
*        deemed part of a truncated 22nd file record.

* Each file record looks like this:
* 1-longword length of file (number of bytes)
* 1-byte length of filename
* up to 19 bytes filename

* The byte-position on disk of file N is the position of
* file N-1 plus the size (rounded up to the next 512
* bytes) of file N-1.  The position of the first file,
* which is the directory block, is 0, and hence the
* directory will contain an entry for itself.
* A file record beginning with a 0 longword (i.e.
* describing a null file) terminates the list of file
* records in the directory.

* Routine to load (i.e. read) a file with name A0
* into memory at location D0.
* Result: D0=length, A0=address of next free byte

diskload movem.l d0/a0,-(sp)
   bsr diskreset
   lea -1024(sp),sp       allocate space into which to read directory
   move.l sp,a0
   clr.l d0
   bsr vdiskread          read first directory block
   move.l #512,d0
   bsr vdiskread          read second directory block
   move.w #255,d0         compute checksum
   clr.l d1
   move.l sp,a0
dl1 add.l (a0)+,d1
   dbra d0,dl1
   bne cserr              checksum fails ->
*  tst.l -4(a0)
*  bne diskerr            last longword non-zero ->
igncse clr.l d0           byte offset in disk of start of next file
   move.l 1024+4(sp),a1   address of filename to be searched for
   lea 8(sp),a0           address of first filename in directory
dl2 move.l -4(a0),d1      length of first (next) file
   beq diskerr            end reached (file not found) ->
   bsr   strc             compare filenames
   beq diskfound          file found ->
   add.l #511,d1          round length to multiple of block size
   and.l #-512,d1
   add.l d1,d0            note disk byte address of next file
   lea 24(a0),a0          onto next slot in directory
   bra dl2
diskfound move.l -4(a0),d1 note size (in bytes) of file
   lea 1024(sp),sp        deallocate directory block space
   move.l (sp),a0         memory address for file
   move.l a0,4(sp)
   move.l d1,(sp)         save size for result
dl3 bsr vdiskread         read first (next) block of file
   add.l #512,d0          increment disk address
   sub.l #512,d1          decrement bytes-to-go
   bgt dl3
   movem.l (sp)+,d0/a0    restore size and start address
   add.l d0,a0            compute end address
   rts
diskerr lea 1024(sp),sp
   move.l 4(sp),a0
   bsr pstring
   lea diskmnotfound,a0
   bsr pstring
   bra retry
diskmnotfound dc.b 255,' not in boot area',0
diskmcserr    dc.b 255,'Directory checksum error',cr,lf,0
   ds.w 0
cserr lea diskmcserr,a0
   bsr pstring
   bra igncse

keyswitch equ $400031

vdiskread move.l d0,-(sp)
   moveq #1,d0
   and.b keyswitch,d0
   lsl.w #3,d0
   swap d0                 <<19
   add.l (sp),d0
   bsr diskread
   move.l (sp)+,d0
   rts

* Compare (eq/neq) strings A0 and A1
* with simplified case relaxation
strc movem.l d0-d2/a0-a1,-(sp)
     clr.w d0
     move.b (a0)+,d0
     cmp.b (a1)+,d0
     bne.s strc9        * different length =>
     subq.b #1,d0
     beq.s strc9        * both null =>
strc1 move.b (a0)+,d1
     or.b #32,d1
     move.b (a1)+,d2
     or.b #32,d2
     cmp.b d1,d2
     dbne d0,strc1
strc9 movem.l (sp)+,d0-d2/a0-a1
     rts

   endc disk#0

* Read a character from ACIA to D0

rsym btst.b #vrrfbit,vdus
   beq rsym
   moveq #127,d0
   and.b vdud,d0
   cmp.b #del,d0
   beq retry
   bra psym         * to echo
   rts

* Read a hex number from ACIA to D0
* Terminating char to D1

rhex moveq #0,d1
rh1 bsr rsym
   or.b #32,d0
   sub.b #'0',d0
   bmi.s rh3
   cmp.b #9,d0
   ble.s rh2
   sub.b #7+32,d0
   cmp.b #9,d0
   ble.s rh3
   cmp.b #15,d0
   bgt.s rh3
rh2 lsl.l #4,d1
   add.b d0,d1
   bra rh1
rh3 add.b #'0',d0
   exg d0,d1
   rts

* Read filename to string(31) somewhere
* leaving A0 pointing at it

rname move.l 0,a0
   lea 1(a0),a1
   clr.b (a0)
   moveq #31,d1
   bra.s rn1
rn2 move.b d0,(a1)+
   addq.b #1,(a0)
rn1 bsr rsym
   cmp.b #' ',d0
   dble d1,rn2
   rts

*
*  MAIN ENTRY POINT
*

begin reset
   move.b #vinit,vdus
   move.b #viof,vdus

* Intercept any nasties

   move.l #nasty,d0
   move.w #48-1,d1
   lea ram,a0
nastyloop move.l d0,(a0)+
   dbra d1,nastyloop

* Initialise ether station and ACIA

   move.b #einit,eths
   move.b #eiof,eths
   ptext <esc,'v',cr,lf,'Boot Firmware'>
  ifeq disk-'2284'
    ptext <' (M2284N)'>
  endc disk='2284'
  ifeq disk-'2294'
    ptext <' (M2294N)'>
  endc disk='2294'
  ifeq disk-'pace'
    ptext <' (Ambit)'>
  endc disk='pace'
  ifeq disk-'mini'
    ptext <' (Mini)'>
  endc disk='mini'
* ifeq disk
*   ptext <' (Remote)'>
* endc disk=0
   ptext <' Version '>
   move.b version,d0
   bsr bhex
   moveq #'/',d0
   bsr psym
   move.b version+1,d0
   bsr bhex
   moveq #'/',d0
   bsr psym
   move.b version+2,d0
   bsr bhex
   moveq #nl,d0
   bsr psym

* Set up mapping registers (transparently)

   move.l #$ff800000+map0,a0
   move.l #$00800080,d0
   moveq #7,d1
maploop move.l d0,0(a0,d0.l)
   add.l #$00100010,d0
   dbra d1,maploop

* Initialise local store

   move.l #$aaaa,d0
   move.l #48*4+ram,a0
localloop move.w d0,(a0)+
   cmp.l sp,a0
   bne localloop

* Initialise main store, break out by bus error

   move.l #breakout,ram+8
   move.l gropestart,a0
   move.l a0,membot
   move.l gropelimit,d1
   sub.l a0,d1
   lsr.l #2,d1
   subq.l #1,d1
   move.l #$aaaaaaaa,d0
mainloop move.l d0,(a0)+
   dbra d1,mainloop
   addq.w #1,d1
   subq.l #1,d1
   bpl mainloop
   bra.s maindone
breakout move.l #maindone,ram+8
   move.b #0,arbiter   * (Attempt to) clear error in arbiter
maindone move.l a0,memtop
   sub.l gropestart,a0
   move.l a0,d0
   bsr lhex
   ptext <' bytes of main store',nl>

* Establish local ether station address etc

   move.l deffile,ffile
   clr.w lstat
   clr.w rstat
   move.b defstat,rstat
   move.b #esa,ethc   * Enquire station address
ssaloop bsr readcc
   cmp.b #ssa,d0
   bne ssaloop
   bsr readdc
   move.b d0,lstat
   ptext <'Ether station '>
   move.b lstat,d0
   bsr bhex
   ptext <' firmware version '>
   move.b #8,ethc     * Set peek/poke address
   bsr flush
   move.b #4,ethd
   bsr flush
   move.b #0,ethd
   bsr flush
   move.b #13,ethc    * Peek
   bsr readcc
   bsr readdc
   bsr bhex
   moveq #'/',d0
   bsr psym
   move.b #13,ethc
   bsr readcc
   bsr readdc
   bsr bhex
   moveq #'/',d0
   bsr psym
   move.b #13,ethc
   bsr readcc
   bsr readdc
   bsr bhex
   moveq #nl,d0
   bsr psym

* If a key has been pressed, ask which filestore
* to talk to, and which files to load.  Otherwise
* use defaults

   btst.b #vrrfbit,vdus
   beq which
ask move.b vdud,d0
   ptext 'Filestore:'
   bsr rhex
   tst.w d0
   beq.s ask1          * Keep default if 0
   move.b d0,rstat
ask1 ptext 'File:'
   bsr rname
   tst.b (a0)
   beq.s which         * Keep default if null
   move.l a0,ffile

* Which filestore?

which clr.b lport      * (Default: local disk)
   tst.b rstat
   bmi.s loadsys
   cmp.b #$70,rstat
   bne.s newfilestore

* Old filestore (fixed port/client assignments)

   move.b #15,lport   * Local port 15
   moveq #-16,d0      * Remote port = local station - 16
   add.b lstat,d0
   move.b d0,rport
   bsr open
   bsr out            * Send "boot" (amnesia) command
   move.b #12,ethd
   bsr flush
   move.b #etx,ethc
   bra loadsys

* New filestore (chat to establish port number)

newfilestore move.b #1,lport
   bsr open
   bsr out
   move.b #2,ethd
   bsr flush
   move.b #etx,ethc
   bsr in
   bsr readdc
   cmp.b #'0',d0
   bmi fserr
   sub.w #'0',d0
   move.b d0,rport
   bsr readcc
   bsr open
   bra loadsys

* Now load the file

loadsys move.l ffile,a0
reload move.l #reload,ram+$20
   move.l 0,sp
   move.w #$2700,sr        * Acquire privilege
   move.l membot,freebot
   move.l memtop,freetop
   move.l #ram,d0
   bsr load

* Enter Loaded System

   move.l membot,a0
   movem.l (a0),d0-d7/a0-a6
   move.l a6,usp
   move.l ram,sp
   move.l ram+4,-(sp)
   rts

* Error reports

nasty ptext <'Unexpected exception',nl>
nloop2 moveq #7,d2
nloop1 moveq #' ',d0
   bsr psym
   move.w (sp)+,d0
   bsr whex
   cmp.l #syssp,sp
   beq retry
   dbra d2,nloop1
   moveq #nl,d0
   bsr psym
   bra nloop2

gotnak ptext <'No ACK from station '>
   move.w rstat,d0
   bsr whex
   moveq #nl,d0
   bsr psym
   bra retry

fserr bsr psym
fse1 bsr readdc
   bsr psym
   cmp.b #nl,d0
   beq.s fse2
   bra fse1
fse2 bsr readcc

* Wait for keypress before retrying

retry move.b vdud,d0
wait btst.b #vrrfbit,vdus
   beq wait
   move.l 0,sp
   move.l 4,a0
   jmp (a0)

* Configuration parameters

defaultfile dc.b 14,'mouse:boot.mob'

   org deffile
   dc.l defaultfile
   dc.b $23,$1,$86  * Version
  ifne disk
    dc.b $80
  else
    dc.b $15
  endc
   dc.l $800000
   dc.l $e00000

   end
