   ttl   Mini Winchester Disk Bootstrap

* Performs all functions of the standard bootstrap.
* Loads selectively over the ether or off local disk.
* Local disk is specified by asking to load from
* a pseudo-ether-station with address $80.

* Copyright 1984 Rainer W Thonnes * All rights reserved *

   bin

* 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

* Other definitions

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

* Disk definitions

   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

*
* 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
   beq diskload
   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

* Disk handling routines

diskwait move.w diskcsr,d0
   btst #7,d0               loop while busy
   bne diskwait
   move.w diskcsr,d0
   btst #0,d0
   bne diskw1
   rts                      return if no error
diskw1 lea diskmerr,a0
   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

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 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

diskload movem.l d0/a0,-(sp)
   bsr diskreset
   lea -512(sp),sp
   move.l sp,a0
   clr.l d0
   bsr diskread
   moveq #127,d0
   clr.l d1
   move.l sp,a0
dl1 add.l (a0)+,d1
   dbra d0,dl1
   bne diskerr
   tst.l -4(a0)
   bne diskerr
   clr.l d0
   move.l 512+4(sp),a1
   lea 8(sp),a0
dl2 move.l -4(a0),d1
   beq diskerr
   bsr.s strc
   beq diskfound
   add.l #511,d1
   and.l #-512,d1
   add.l d1,d0
   lea 24(a0),a0
   bra dl2
diskfound move.l -4(a0),d1
   lea 512(sp),sp
   move.l (sp),a0
   move.l a0,4(sp)
   move.l d1,(sp)
dl3 bsr diskread
   add.l #512,d0
   sub.l #512,d1
   bgt dl3
   movem.l (sp)+,d0/a0
   add.l d0,a0
   rts
diskerr lea 512(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
   ds.w 0

* Compare (eq/neq) strings A0 and A1
* with 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
*strc1 move.b (a0)+,d1
*     or.b #32,d1
*     cmp.b (a1)+,d1
*     dbne d0,strc1      * second matches first!32 ->
*     beq.s strc9        * count exhausted, strings equal =>
*     eor.b #32,d1
*     cmp.b -1(a1),d1
*     dbne d0,strc1      * second matches first&\32 ->
strc9 movem.l (sp)+,d0-d2/a0-a1
     rts

* 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
   move.w diskcsr,d0    for keen-to-interrupt minidisk

* 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,'Mini Winchester Disk Boot Firmware, 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
   bsr readcc
   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 9,'fmac:nsys'

   org deffile
   dc.l defaultfile
   dc.b $16,$05,$84
   dc.b $80
   dc.l $800000
   dc.l $c00000

   end
