Atari ST Book: BIOS/XBIOS ROM listing

Atari ST ROM fully commented BIOS/XBIOS ROM listing for TOS 2.06, TOS 3.06 and – especially – the ST-Book ROM. I tried to collect the original comments from the Atari developers, when available. Also available on GitHub.

ROM_TOS206  EQU 1                               ; regular TOS 2.06 ROM
ROM_TOS306  EQU 0                               ; regular TOS 2.06 ROM
ROM_STBOOK  EQU 0                               ; ROM for the ST Book (variation of TOS 2.06)

osroml:     bra.s     reseth                    ; os_entry - BRA to reset handler
IF ROM_TOS306
            DC.W      $0306                     ; os_version - TOS version number
ELSE
            DC.W      $0206                     ; os_version - TOS version number
ENDIF
            DC.L      reseth                    ; os_reseth -> reset handler
            DC.L      osroml                    ; os_beg -> base of OS
            DC.L      endos                     ; os_end -> end BIOS/GEMDOS/VDI ram usage
            DC.L      reseth                    ; os_exec/os_rsv1 << unused, reserved >>
            DC.L      gem_mupb                  ; os_magic -> GEM memory usage param. block
IF ROM_STBOOK
            DC.L      $03101992                 ; os_date - Date of system build ($YYYYMMDD)
ELIF ROM_TOS206
            DC.L      $11141991                 ; os_date - Date of system build ($YYYYMMDD)
ELIF ROM_TOS306
            DC.L      $09241991                 ; os_date - Date of system build ($YYYYMMDD)
ENDIF
            DC.W      $0000                     ; os_conf - OS configuration bits
IF ROM_STBOOK
            DC.W      $186a                     ; os_dosdate - DOS-format date of system build
ELIF ROM_TOS206
            DC.W      $176e                     ; os_dosdate - DOS-format date of system build
ELIF ROM_TOS306
            DC.W      $1738                     ; os_dosdate - DOS-format date of system build
ENDIF
            DC.L      ospool_base               ; p_root -> base of OS pool
            DC.L      kb_shift                  ; pkbshift -> keyboard shift state variable
            DC.L      gemdos_pid                ; p_run -> GEMDOS PID of current process
            DC.L      $000000                   ; p_rsv2 << unused, reserved >>

*+
* [ROM based system]
* reseth - System reset handler
*
*  Gains control of the system upon power-up reset,
*  or when the RESET button is pressed,
*  or after a really messy system crash.
*
*-
reseth:     move      #$2700,sr                 ; super mode, no interrupts
IF ROM_TOS306
            move.w    #$100,(fifo).w
            move.w    #0,(fifo).w
ENDIF
            reset                               ; reset hardware

*+
*  [ROM based system]
*  Check for a diagnostic cartridge;
*  if one is inserted, load a return address
*  into A6 and jump into the cat's entry point.
*
*-
            cmpi.l    #$fa52235f,(cartbase).l   ; is the magic number there?
            bne.s     reset1                    ; (no)
            lea       reset1(pc),a6             ; a6 -> return address
            jmp       (cartbase+4).l            ; execute diagnostic cartridge

*+
*  [ROM based system]
*  Is this is a warm reset, setup the memory
*  controller configuration register so that
*  the reset-bailout vector has something to
*  stand on ....
*
*-
reset1:

IF ROM_TOS306
            move.l    #$808,d0
            movec     d0,cacr

            moveq     #0,d0                     ; set vector base to address 0
            movec     d0,vbr
            pmove     ($e35fe4).l,tc            ; 0L - disable PMMU
            pmove     ($e35fe4).l,tt0           ; 0L
            pmove     ($e35fe4).l,tt1           ; 0L
            frestore  ($e35fe4).l               ; 0L - clear FPU

            btst      #0,(scu_gp1).w            ; memconfig valid?
            beq.s     reset3                    ; (no)
ENDIF

            lea       ret_1(pc),a6              ; load return addr
            bra       val_memval                ; check memory configuration validity
ret_1:      bne.s     reset3                    ; (invalid -- don't set anything up)
            move.b    (memcntlr).w,(memconf).w  ; initialize memory controller
reset2:

IF ROM_STBOOK
            move.w    (STConfig).w,d0           ; Configuration on ST Book
            cmp.b     #$fc,d0                   ; power pressed while the ST Book is closed?
            beq.s     reset2b                   ; yes => do not execute reset vector
            move.w    (tt_mcu+4).l,d0           ; ST Book: ???
            and.b     #6,d0                     ; check bit 1 & 2
            bne.s     reset2c
reset2b:    clr.l     (resvalid).l
reset2c:
ENDIF

*+
*  [ROM based system]
*  RESET bailout vector check.
*  Check to make sure we have a clean, well-bred
*  bailout vector. The high byte must be zero,
*  it must be even, and cannot be entirely zero.
*
*-
            cmpi.l    #$31415926,(resvalid).w   ; is the resvalid the magic number?
            bne.s     reset3                    ; (no)
            move.l    (resvector).w,d0          ; d0 = reset bailout vector
IF !ROM_TOS306
            tst.b     (resvector).w             ; bits 24..31 must be zero
            bne.s     reset3                    ; (they aren't, so punt)
ENDIF
            btst      #0,d0                     ; the vector must be even
            bne.s     reset3                    ; (it isn't, so punt)
            movea.l   d0,a0                     ; a0 -> reset handler
            lea       reset2(pc),a6             ; a6 -> return address
            jmp       (a0)                      ; execute reset bailout

*+
*  Initialize PSG output ports.
*  Make port A and B output-only,
*  initialize floppy select lines (so
*  that none are selected)
*-
reset3:     lea       (psgsel).w,a0             ; a0 -> giselect, giwrite-2
            move.b    #7,(a0)                   ; set porta & portb to output
            move.b    #$c0,2(a0)
            move.b    #14,(a0)                  ; deselect disks
            move.b    #7,2(a0)

*+
*  Determine 50hz or 60hz.
*  The hardware RESETs to 60hz. Check a bit in the
*  ROM configuration byte to see if we have to twiddle
*  the hardware into 50hz mode.
*
*-
IF ROM_TOS306
            move.b    #1,(v_shf_mod).w          ; Switch to 640x200x2
ELSE
            btst      #0,(osroml+29).l          ; check bit: configured for 50hz?
            beq.s     notpal                    ; (nope -- we're good ol' NTSC)
            lea       ret_1b(pc),a6
            bra       waitvbl                   ; a short delay for PAL
ret_1b:     move.b    #2,(synmod).w             ; yes -- twiddle to 50hz
notpal:
IF ROM_STBOOK
            move.b    #2,(v_shf_mod).w          ; Switch to 640x400x1
            move.b    #$80,(lcdPowerControl).w  ; LCD display on
            tst.w     (tt_mcu).l                ; ST Book: ???
ENDIF
ENDIF

*+
*  Initialize palette registers to
*  their default values
*
*-
            lea       (palette).w,a1            ; a1 -> hardware reg
            move.w    #15,d0                    ; setup 16 colors
            lea       colors(pc),a0             ; a0 -> table of default colors
sysic1:     move.w    (a0)+,(a1)+               ; copy palette assignment
            dbra      d0,sysic1                 ; (loop for more colors)

*+
*  On a ROM system, put the screen (temporarily)
*  at $10000, so icon-drawing routines won't
*  blow any any system variables.
*-
            move.b    #1,(v_bas_h).w            ; set high ptr
            clr.b     (v_bas_m).w               ; set low ptr

*+
*  [ROM based system]
*  Determine how much memory there is, and initialize
*  the memory controller configuration register
*
*-
IF ROM_TOS306
            btst      #0,(scu_gp1).w            ; memconfig valid?
            beq.s     checkmem                  ; (no)
ENDIF
            move.b    (memcntlr).w,d6           ; d6 = memory controller configuration
IF ROM_TOS306
            move.b    d6,(memconf).w            ; setup memory controller configuration register
ENDIF
            move.l    (phystop).w,d5            ; d5 -> (possible) top of physical mem
            lea       ret_2(pc),a6              ; load return address
            bra       val_memval                ; check if the memory configuration is valid
ret_2:      beq       reset4

IF ROM_STBOOK
*--- init vars + hardware:
            move.b    #%1010,d6                 ; setup controller for 2Mb/2Mb - only valid configuration on a ST Book
            move.b    d6,(memconf).w            ; setup memory controller

*--- write test-pattern to determine memory configuration:
            move.l    #$400000,d5               ; d5 -> physical top of memory (4Mb)
            move.l    #$6161964,d0              ; 16th June 1964...
            move.l    #$3251987,d1              ; 25th March 1987...
            move.l    d0,($300010).l
            move.l    d1,($300014).l
            cmp.l     ($300010).l,d0            ; test if 4MB is actually installed
            bne.s     ret_2a
            cmp.l     ($300014).l,d1
            beq.s     ret_2b
ret_2a:     move.l    #$100000,d5               ; d5 -> physical top of memory (1Mb)

ret_2b:     lea       ($8000).l,sp

ELIF ROM_TOS206
* First we try to configure the memory controller

            clr.w     d6
            move.b    #$a,(memconf).w           ; default: setup controller for 2Mb/2Mb

            movea.w   #$8,a0
            lea       ($200008).l,a1            ; + 2Mb
            clr.w     d0
chkpatloop: move.w    d0,(a0)+                  ; fill 512-8 bytes with a test pattern
            move.w    d0,(a1)+
            add.w     #$fa54,d0
            cmpa.w    #$200,a0
            bne.s     chkpatloop

            move.b    #90,(v_bas_l).w           ; wrote low byte of video address
            tst.b     (v_bas_m).w               ; touch the middle byte (this should reset the low byte)
            move.b    (v_bas_l).w,d0
            cmp.b     #90,d0                    ; low byte not reset?
            bne.s     chkmem1
            clr.b     (v_bas_l).w               ; try a different low byte value
            tst.w     (palette).w               ; touch the color palette
            tst.b     (v_bas_l).w               ; low byte changed?
            bne.s     chkmem1
            move.l    #$40000,d7                ; 256Kb offset
            bra.s     chkmem1b
chkmem1:    move.l    #$200,d7                  ; 512 byte offset
chkmem1b:   move.l    #$200000,d1               ; 2Mb = maximum size per bank

chkmemloop: lsr.w     #2,d6                     ; shift memory configuration down by a bank (bank 1 is in bits 0..1, bank 0 is in bits 2..3)

            movea.l   d7,a0                     ; + 512/256Kb bytes
            addq.l    #8,a0
            lea       chkmem3(pc),a4
            bra       memchk
chkmem3:    beq.s     chkmem7                   ; bank is not working =>

            movea.l   d7,a0
            adda.l    d7,a0                     ; + 1024/512Kb byte
            addq.l    #8,a0
            lea       chkmem4(pc),a4
            bra       memchk
chkmem4:    beq.s     chkmem6                   ; bank has 512Kb of memory =>

            movea.w   #$8,a0                    ; + 0 bytes
            lea       chkmem5(pc),a4
            bra       memchk
chkmem5:    bne.s     chkmem7                   ; bank is empty =>

            addq.w    #4,d6                     ; 4+4 = 1000 2Mb bank size
chkmem6:    addq.w    #4,d6                     ; 4   = 0100 512Kb bank size
chkmem7:    sub.l     #$200000,d1               ; - 2Mb
            beq.s     chkmemloop
            move.b    d6,(memconf).w            ; set memory configuration

ELIF ROM_TOS306
checkmem:
            move.w    #5,d6
            move.b    #$a,(memconf).w
            moveq     #0,d0
            move.l    d0,($1008).w
            move.l    d0,($100c).w
            move.l    #$6161964,d0
            move.l    d0,($0008).w
            cmp.l     ($1008).w,d0
            bne.s     checkmem1
            move.l    #$4251987,d0
            move.l    d0,($000c).w
            cmp.l     ($100c).w,d0
            beq.s     checkmem2
checkmem1:  move.w    #$a,d6
checkmem2:  move.b    d6,(memconf).w
ENDIF

IF ROM_TOS206 || ROM_TOS306
* memory is configured, set the stack pointer
            lea       ($8000).l,sp

* test the memory in 128Mb blocks to find out the maximum installed
* physical memory. A failure (bus error or a memory checksum error)
* terminates the search.
            movea.l   (busexception).w,a4
            lea       chkmemx(pc),a0            ; if a bus error occurs, we are done, too
            move.l    a0,(busexception).w
            move.w    #$fb55,d3
            move.l    #$20000,d7                ; 128Mb is the page size for testing
            movea.l   d7,a0                     ; 128Mb is the minimum physical memory
chkmem8:    movea.l   a0,a1                     ; test just below the current top of memory
            move.w    d0,d2
            moveq     #$2a,d1                   ; test 43 words
chkmem9:    move.w    d2,-(a1)                  ; write test pattern
            add.w     d3,d2
            dbra      d1,chkmem9

            movea.l   a0,a1
            moveq     #$2a,d1                   ; check 43 words
chkmem10:   cmp.w     -(a1),d0                  ; compare the test pattern
            bne.s     chkmemx                   ; test failed => we are done
            clr.w     (a1)                      ; erase memory after testing
            add.w     d3,d0
            dbra      d1,chkmem10

            adda.l    d7,a0                     ; advance to the next page
            bra.s     chkmem8

chkmemx:    suba.l    d7,a0                     ; the last page didn't work
            move.l    a0,d5                     ; end of the physical memory
            move.l    a4,(busexception).w
ENDIF

*+
*  [ROM based system]
*  Clear memory from $400 to 'd5' (phystop)
*-
            movea.w   #etv_timer,a0             ; start at the beginning
            move.l    d5,d4                     ; where to end
            moveq     #0,d0
clm_1:      move.l    d0,(a0)+                  ; initialize all the memory
            move.l    d0,(a0)+
            move.l    d0,(a0)+
            move.l    d0,(a0)+
            cmpa.l    d4,a0                     ; done?
            bne.s     clm_1                     ; (loop for more bytes)

*+
*  [ROM based system]
*  Indicate that memory has successfully
*  been sized and tested.  Set two variables
*  to magic values ...
*
*-
            move.b    d6,(memcntlr).w           ; save configuration byte
            move.l    d5,(phystop).w            ; save physical top-of-memory
            move.l    #$752019f3,(memvalid).w   ; indicate memory was configured
            move.l    #$237698aa,(memval2).w    ; ditto (paranoia variable)
            move.l    #$5555aaaa,(memval3).w    ; ditto #2 (paranoia variable)
IF ROM_TOS306
            move.l    #fchkmemex,(busexception).w
            move.w    #0,(tt_mcu+224).l
fchkmemex:

* test the memory in 128Mb blocks to find out the maximum installed
* physical fast mem. A failure (bus error or a memory checksum error)
* terminates the search.
            move.l    #fchkmemx,(busexception).w    ; if a bus error occurs, we are done, too
            move.w    #$fb55,d3
            moveq     #0,d0
            move.l    #$20000,d7                ; 128Mb is the page size for testing
            movea.l   #$1020000,a0              ; start address of fast mem area + 128Mb
fchkmem8:   movea.l   a0,a1                     ; test just below the current top of memory
            move.w    d0,d2
            moveq     #$2a,d1                   ; test 43 words
fchkmem9:   move.w    d2,-(a1)                  ; write test pattern
            add.w     d3,d2
            dbra      d1,fchkmem9

            movea.l   a0,a1
            moveq     #$2a,d1                   ; check 43 words
fchkmem10:  cmp.w     -(a1),d0                  ; compare the test pattern
            bne.s     fchkmemx                  ; test failed => we are done
            clr.w     (a1)                      ; erase memory after testing
            add.w     d3,d0
            dbra      d1,fchkmem10

            adda.l    d7,a0                     ; advance to the next page
            bra.s     fchkmem8

fchkmemx:   suba.l    d7,a0                     ; the last page didn't work
            cmpa.l    #$1000000,a0              ; it failed right at the beginning of the fast mem?
            bne.s     fchkmemno                 ; (no)
            suba.l    a0,a0                     ; then we have no fast mem
fchkmemno:  move.l    a0,d5                     ; end of the fast mem
            move.l    d5,(ramtop).w             ; top of fast mem
            move.l    #$1357bd13,(ramvalid).w   ; ramtop is valid (ramvalid == RAMMAGIC)
            bset      #0,(scu_gp1).w            ; memconfig is valid
ELSE
            clr.l     (ramtop).l                ; no FASTRAM available
            move.l    #$1357bd13,(ramvalid).w   ; ramtop is valid (ramvalid == RAMMAGIC)
ENDIF

reset4:     movea.l   #_supstk+2048,sp          ; setup supervisor stack

IF ROM_STBOOK
*+
*  Initialize interrupt vectors
*
*  The exception vectors are pointed to a cold boot (_coldboot)
*  during startup.
*
*  Trap 2 and Divide-by-zero are pointed at an rte
*
*  The HBLANK, VBLANK, line 1001 [someday: line 1111), trap 13, trap 14,
*  and "extended" trap vectors are initialized appropriately.
*
*-
            lea       _rte(pc),a3               ; a3 -> handy RTE
            lea       _rts(pc),a4               ; a4 -> handy RTS

*--- setup 64 vectors:
            lea       (_coldboot).l,a1          ; a1 -> during boot all exception trigger a coldboot, which erases all memory and resets
            lea       (busexception).w,a0       ; a0 -> interrupt RAM
            move.w    #$3f,d0                   ; d0 = count
sei1:       move.l    a1,(a0)+                  ; write vector
            dbra      d0,sei1                   ; (loop to write more vectors)
            move.l    a3,(divzeroexception).w   ; divide-by-zero vector -> rte

            move.l    a3,(Level7IRQ).w          ; level #7 interrupt -> rte (power exception in a ST Book)
            moveq     #6,d0
            lea       (Level1IRQ_TTVME).w,a1
sei2:       move.l    #_rte,(a1)+               ; level #1 ... level #6 to RTE
            dbra      d0,sei2

*--- install OS interrupt vectors:
            move.l    #vbl,(Level4IRQ_VBL).w    ; vblank handler
            move.l    #hbl,(Level2IRQ_HBL).w    ; hblank handler
            move.l    a3,(trap_aesvdi).w        ; (empty) trap#2 handler
            move.l    #trp13h,(trap_bios).w     ; trap #13 handler
            move.l    #trp14h,(trap_xbios).w    ; trap #14 handler
            move.l    #line1010,(lineAexception).w ;line 1010 handler
            move.l    a4,(etv_timer).w          ; default timer-tick vector -> rts
            move.l    #_critich,(etv_critic).w  ; default critical error handler
            move.l    a4,(etv_term).w           ; default terminal vector -> rts

*+
*  Setup the vblank deferred vector list.
*  (This data structor is ugly,
*   but we seem to be stuck with it).
*
*-
            lea       (_vbl_list).w,a0          ; a0 -> default list of vbl locs
            move.l    a0,(_vblqueue).w          ; install ptr to them
            move.w    #7,d0                     ; clear vbl vectors
avbl:       clr.l     (a0)+                     ; one at a time
            dbra      d0,avbl

            lea       (tconstat).l,a0
            movea.w   #xconstat,a1
            moveq     #31,d0
tconl:      move.l    (a0)+,(a1)+
            dbra      d0,tconl

            movea.l   (busexception).w,a0
            movea.l   sp,a1
            move.l    #vmeinit,(busexception).w
            move.b    #$40,(vme_mask).w         ; Enable IRQ6 from VMEBUS/MFP
            move.b    #$14,(sys_mask).w         ; VSYNC & HSYNC enable in the VME Bus System Control Unit
vmeinit:    move.l    a0,(busexception).w
            movea.l   a1,sp
ENDIF

            clr.b     ($aa6).l
IF ROM_STBOOK
            sf        (STEFlag).l               ; ST Book is not an Atari STE
ELSE
            movea.l   sp,a6
            move.l    #mwinitex,(busexception).w
            clr.w     (sndmactl).w
            st        ($a02).l                  ; microwire interface available
            lea       mwinitdata(pc),a0
            move.w    (a0)+,(mwmask).w
            bra.s     mwinit3

mwinitdata: DC.W      $0ffe                     ; LMC 1992 mask
            DC.W      $09d1                     ; 10-011-101000-1 = LCM - Master Volume - 0 db volume (max) - end-bit
            DC.W      $0aa9                     ; 10-101-010100-1 = LCM - Left channel volume - 0 db volume (max) - end-bit
            DC.W      $0a29                     ; 10-100-010100-1 = LCM - Right channel volume - 0 db volume (max) - end-bit
            DC.W      $090d                     ; 10-010-000110-1 = LCM - Trebble control - 0 db (linear) - end-bit
            DC.W      $088d                     ; 10-001-000110-1 = LCM - Bass control - 0 db (linear) - end-bit
            DC.W      $0803                     ; 10-000-000001-1 = LCM - Mixer - DMA + YM2149 - end-bit
            DC.W      $0000                     ; 0 = end of list

mwinit1:    move.w    d0,(mwdata).w
mwinit2:    tst.w     (mwdata).w
            bne.s     mwinit2
mwinit3:    move.w    (a0)+,d0
            bne.s     mwinit1
mwinitex:   movea.l   a6,sp

IF ROM_TOS206
            move.b    #90,(v_bas_l).w           ; write low byte of video address
            tst.b     (v_bas_m).w               ; access the medium byte
            move.b    (v_bas_l).w,d0            ; (which should reset the low byte on an STE!)
            cmp.b     #90,d0                    ; reset?
            bne.s     nostedetect               ; yes => STE detected
            clr.b     (v_bas_l).w               ; clear the low byte again
            tst.w     (palette).w               ; access the color palette
            tst.b     (v_bas_l).w
nostedetect:sne       (STEFlag).l               ; <>0 => no STE hardware available
ELSE
            sf        (STEFlag).l               ; no STE hardware available
ENDIF
ENDIF

*+
*  Clear OS bss (from 'endosbss' to 'ostext')
*-
            movea.l   #ostext,a1                ; a1 -> end
            movea.l   #endosbss,a0              ; a0 -> start

*--- common code to clear memory:
            moveq     #0,d0                     ; quick zero
clrm_1:     move.w    d0,(a0)+                  ; clobber a word
            cmpa.l    a0,a1                     ; at end?
            bne.s     clrm_1                    ; (no -- loop for more words)

IF ROM_TOS306
            bsr       setupPMMU                 ; setup PMMU translation table
ENDIF
*+
*  Setup display base,
*  clear display memory.
*
*-
            movea.l   (phystop).w,a0            ; video_base = phystop - 0x8000
IF !ROM_TOS306
            suba.l    #$8000,a0                 ; screen size = 32kb
            move.w    #$7ff,d1                  ; d1 = # 16-byte chunks to zero
ELSE
            suba.l    #$25900,a0                ; screen size = 154kb
            move.w    #$258f,d1                 ; d1 = # 16-byte chunks to zero
ENDIF
            move.l    a0,(_v_bas_adr).w
            move.b    (_v_bas_adr+1).w,(v_bas_h).w ;load high addr
            move.b    (_v_bas_adr+2).w,(v_bas_m).w ;load low (really, medium) addr
            moveq     #0,d0                     ; quick zero
clrm_2:     move.l    d0,(a0)+                  ; zero a longword
            move.l    d0,(a0)+                  ; zero a longword
            move.l    d0,(a0)+                  ; zero a longword
            move.l    d0,(a0)+                  ; zero a longword
            dbra      d1,clrm_2                 ; (loop for more longwords)

*+
*  Initialize all kinds of OS variables
*
*-

*--- OS parameters:
            movea.l   osroml+20(pc),a0          ; get pointer to magic
            cmpi.l    #$87654321,(a0)           ; is the magic there?
            beq.s     usem
            lea       (osroml+8).l,a0           ; yes -- use numbers there
usem:       move.l    4(a0),(end_os).w          ; init end-of-OS pointer
            move.l    8(a0),(exec_os).w         ; init default-shell pointer

*--- Disk vectors:
            move.l    #_dskinit,(hdv_init).w    ; initialization
            move.l    #_floprw,(hdv_rw).w       ; read/write absolute sectors
            move.l    #_getbpb,(hdv_bpb).w      ; media change inquiry
            move.l    #_mediach,(hdv_mediach).w ; get BIOS parameter block
            move.l    #_boot,(hdv_boot).w       ; boot-from-device

            move.l    #_lstostat,(prv_lsto).w
            move.l    #_lstout,(prv_lst).w
            move.l    #_auxostat,(prv_auxo).w
            move.l    #_auxout,(prv_aux).w
            move.l    #_scrdmp,(scr_dump).w

*--- Randoms:
            move.l    (_v_bas_adr).w,(_memtop).w ;_memtop = _v_bas_ad
            move.l    (end_os).w,(_membot).w    ; set bottom of memory (for DOS)
            move.w    #8,(nvbls).w              ; default number of vbl queue entries
            st        (_fverify).w              ; enable write-verify
            move.w    #3,(seekrate).w           ; set default seek-rate
            move.l    #_diskbuf,(_dskbufp).w    ; set pointer to disk buffer
            move.w    #-1,(_prtcnt).w           ; initialize print-count
            move.l    #osroml,(_sysbase).w      ; -> base of OS
            move.l    #savend,(savptr).w        ; register-save pointer for traps 13&14
            move.l    #_rts,(swv_vec).w         ; ignore monitor changes for now
            clr.l     (_drvbits).w              ; remove all drives
            move.l    #keybellsnd,(bell_hook).w
            move.l    #keyclicksnd,(kcl_hook).w

            bsr       sysbase2ram               ; copy sysbase into RAM and patch os_conf to contain os_dosdate

*--- Cookies:
            lea       (cookieBuffer).l,a0
            move.l    a0,(_p_cookies).w
            move.l    #'_CPU',(a0)+
            moveq     #0,d1                     ; 68000 CPU
            movea.w   #illegalexception,a2
            movea.l   (a2),a3
            movea.l   sp,a1
            move.l    #cookieCPU,(a2)
            move      ccr,d0                    ; first exists in the 68010
            moveq     #10,d1                    ; 68010 CPU
            extb.l    d0                        ; first exists in the 68020
            moveq     #20,d1                    ; 68020 CPU
            movec     cacr,d0
            bset      #9,d0
            movec     d0,cacr
            movec     cacr,d0
            bclr      #9,d0
            beq.s     cookieCPU
            moveq     #30,d1                    ; 68030 CPU
            movec     d0,cacr
cookieCPU:  movea.l   a1,sp
            move.l    a3,(a2)
            move.l    d1,(a0)+
            sne       (_longframe+1).w          ; 68010+ have a longer interrupt stack frame
IF ROM_STBOOK
            move.l    #'_VDO',(a0)+             ; setup VDO cookie: Video hardware
            move.l    #$10001,(a0)+             ; 1,1 => Atari STE, ST-Book
            move.l    #'_MCH',(a0)+             ; setup MCH cookie: Machine type
            move.l    #$10001,(a0)+             ; 1,1 => Atari STE, ST-Book

            move.b    #$7f,d0
            tst.b     (STEFlag).l
            bne.s     cookieSTE
            move.l    #'_SWI',(a0)+             ; setup SWI cookie: DIP configuration switches
            moveq     #0,d0
            move.w    (STConfig).w,d0
            lsr.w     #8,d0
            move.l    d0,(a0)+                  ; all DIP switches as a bit mask 0..7
cookieSTE:  moveq     #3,d1                     ; bit 0: PSG, bit 1: 8-bit DMA
            move.l    #'_SND',(a0)+             ; setup SND cookie: Sound hardware
            btst      #7,d0                     ; DIP switch 7 on?
            bne.s     cookieSND                 ; (punt)
            bclr      #1,d1                     ; no 8-bit DMA sound
cookieSND:  move.l    d1,(a0)+
            btst      #6,d0                     ; DIP switch 6 on?
            bne.s     cookieFDC                 ; (punt - no HD floppy)
            move.b    #8,(dsb0).l               ; select HD density for drive A
            move.l    #'_FDC',(a0)+             ; setup FDC cookie: Floppy disk controller
            move.l    #$1415443,(a0)+           ; 'FDC' | (1 << 24)
cookieFDC:  move.l    #'_FPU',(a0)+             ; Setup FPU cookie: Type of the FPU
            moveq     #0,d7                     ; 0 = no FPU
            suba.w    #$24,sp
            move.l    (lineFexception).w,(sp)
            move.l    (coprocexception).w,4(sp)
            move.l    #cookieFPU,(lineFexception).w
            move.l    #cookieFPU,(coprocexception).w
            lea       8(sp),a1
            movea.w   #ffcp_unorderedcond,a2
            move.l    #cookieFPU2,d0
            move.l    (a2),(a1)+
            move.l    d0,(a2)+
            move.l    (a2),(a1)+
            move.l    d0,(a2)+
            move.l    (a2),(a1)+
            move.l    d0,(a2)+
            move.l    (a2),(a1)+
            move.l    d0,(a2)+
            move.l    (a2),(a1)+
            move.l    d0,(a2)+
            move.l    (a2),(a1)+
            move.l    d0,(a2)+
            move.l    (a2),(a1)+
            move.l    d0,(a2)+
            clr.l     -(sp)
            movea.l   sp,a2
            frestore  (sp)
cookieFPU2: move.l    #$20000,d7                ; 0x20000 = 68881 or 68882 as co-processor (Exact type unknown)
cookieFPU:  movea.l   a2,sp
            addq.w    #4,sp
            move.l    (sp)+,(lineFexception).w
            move.l    (sp)+,(coprocexception).w
            move.l    (sp)+,(ffcp_unorderedcond).w
            move.l    (sp)+,(ffcp_inexactresult).w
            move.l    (sp)+,(ffcp_divzero).w
            move.l    (sp)+,(ffcp_underflow).w
            move.l    (sp)+,(ffcp_operanderror).w
            move.l    (sp)+,(ffcp_inexactresult).w
            move.l    (sp)+,(ffcp_divzero).w
            move.l    d7,(a0)+
            movea.l   (busexception).w,a1
            movea.l   sp,a2
            move.l    #cookieFPU3,(busexception).w
            move.w    (FPStat).w,d0
            bset      #0,-3(a0)                 ; SFP004 present
cookieFPU3: move.l    a1,(busexception).w
            movea.l   a2,sp
ELSE

IF ROM_TOS206
            tst.b     (STEFlag).l               ; no STE hardware available?
            beq.s     cookieMCH                 ; (correct)
            move.l    #'_VDO',(a0)+
            clr.l     (a0)+                     ; 0,0 = Atari ST (260 ST, 520 ST, 1040 ST, Mega ST, ...)
            move.l    #'_MCH',(a0)+
            clr.l     (a0)+                     ; 0,0 = Atari ST
            bra.s     cookieSWI

cookieVDO:  move.l    #'_VDO',(a0)+
            move.l    #$10000,(a0)+             ; 1,0 = STE Shifter

            move.l    #$10000,d0                ; x = $00 = regular STE
            movea.l   (busexception).w,a1
            movea.l   sp,a2
            move.l    #cookieMCH1,(busexception).w
            tst.b     (scu_gp1).w
            move.w    #$10,d0                   ; x = $10 = Mega STE (with SCSI)
            bra.s     cookieMCH2
cookieMCH1: clr.w     d0
            movea.l   a2,sp
            move.l    #cookieMCH2,(busexception).w
            tst.b     (ide_stat2).l
            move.w    #8,d0                     ; x = $08 = STE with IDE (unknown machine)
cookieMCH2: move.l    a1,(busexception).w
            movea.l   a2,sp
            move.l    #'_MCH',(a0)+
            move.l    d0,(a0)+                  ; 1,x = STE (520, 1040, 2080, 4160, Mega STE, ST Book)

ELIF ROM_TOS306
            move.l    #'_VDO',(a0)+
            move.l    #$20000,(a0)+             ; 2,0 = TT Shifter
            move.l    #'_MCH',(a0)+
            move.l    #$20000,(a0)+             ; 2,0 = TT
ENDIF

cookieSWI:  move.b    #$7f,d0
            tst.b     (STEFlag).l               ; no STE hardware available?
            bne.s     cookieSND2                ; (correct)
            move.l    #'_SWI',(a0)+             ; setup SWI cookie: DIP configuration switches
            moveq     #0,d0
            move.w    (STConfig).w,d0
            lsr.w     #8,d0
            move.l    d0,(a0)+

cookieSND2: moveq     #3,d1                     ; bit 0: PSG, bit 1: 8-bit DMA
            move.l    #'_SND',(a0)+             ; setup SND cookie: Sound hardware
            btst      #7,d0                     ; DIP switch 7 on?
            bne.s     cookieSND3                ; (punt)
            bclr      #1,d1                     ; no 8-bit DMA sound
cookieSND3: move.l    d1,(a0)+

            btst      #6,d0                     ; DIP switch 6 on?
            bne.s     cookieFDC2                ; (punt - no HD floppy)
            move.b    #8,(dsb0).l               ; select HD density for drive A
            move.l    #'_FDC',(a0)+             ; setup FDC cookie: Floppy disk controller
            move.l    #$1415443,(a0)+           ; 'FDC' | (1 << 24)

cookieFDC2:
            move.l    #'_FPU',(a0)+             ; Setup FPU cookie: Type of the FPU
            movea.l   (lineFexception).w,a1
            movea.l   (coprocexception).w,a2
            movea.l   sp,a3
            move.l    #cookieFPUex,(lineFexception).w
            move.l    #cookieFPUex,(coprocexception).w
            fmove.l   d0,fp0
            move.l    #$20000,(a0)+             ; 6888x present
            bra.s     cookieFPUe2
cookieFPUex:clr.l     (a0)+
cookieFPUe2:move.l    a1,(lineFexception).w
            move.l    a2,(coprocexception).w
            movea.l   a3,sp
            movea.l   (busexception).w,a1
            movea.l   sp,a2
            move.l    #cookieFPUe3,(busexception).w
            move.w    (FPStat).w,d0
            bset      #0,-3(a0)                 ; SFP004 present
cookieFPUe3:move.l    a1,(busexception).w
            movea.l   a2,sp

IF ROM_TOS306
            tst.l     (ramtop).w                ; fast mem installed?
            beq.s     nfrbcookie                ; (no)
            move.l    #'_FRB',(a0)+             ; allocate FRB cookie for DMA buffer
            move.l    (_membot).w,d0
            move.l    d0,(a0)+                  ; buffer adress
            add.l     #$10000,d0                ; 64kb size
            move.l    d0,(_membot).w            ; adjust memory base
            move.l    d0,(end_os).w
nfrbcookie:
ENDIF

ENDIF
            clr.l     (a0)+                     ; terminate cookie list
            move.l    #$10,(a0)+                ; 16 available slots in the cookie store

IF !ROM_STBOOK
*+
*  Initialize interrupt vectors
*
*  The exception vectors are pointed to a cold boot (_coldboot)
*  during startup.
*
*  Trap 2 and Divide-by-zero are pointed at an rte
*
*  The HBLANK, VBLANK, line 1001 [someday: line 1111), trap 13, trap 14,
*  and "extended" trap vectors are initialized appropriately.
*
*-
            lea       _rte(pc),a3               ; a3 -> handy RTE
            lea       _rts(pc),a4               ; a4 -> handy RTS

*--- setup 62 vectors:
            lea       _term(pc),a1              ; _term -> draw number of bombs
IF !ROM_TOS306
            adda.l    #$2000000,a1              ; exception number in top 8 bits of the address
ENDIF
            lea       (busexception).w,a0       ; a0 -> interrupt RAM
            move.w    #$3d,d0                   ; d0 = count
sei1:       move.l    a1,(a0)+                  ; write vector
IF !ROM_TOS306
            adda.l    #$1000000,a1              ; increment the exception number
ENDIF
            dbra      d0,sei1                   ; (loop to write more vectors)
            move.l    a3,(divzeroexception).w   ; divide-by-zero vector -> rte

            move.l    a3,(Level7IRQ).w          ; level #7 interrupt -> rte (power exception in a ST Book)
            moveq     #6,d0
            lea       (Level1IRQ_TTVME).w,a1
sei2:       move.l    #_rte,(a1)+               ; level #1 ... level #6 to RTE
            dbra      d0,sei2

*--- install OS interrupt vectors:
            move.l    #vbl,(Level4IRQ_VBL).w    ; vblank handler
            move.l    #hbl,(Level2IRQ_HBL).w    ; hblank handler
            move.l    a3,(trap_aesvdi).w        ; (empty) trap#2 handler
            move.l    #trp13h,(trap_bios).w     ; trap #13 handler
            move.l    #trp14h,(trap_xbios).w    ; trap #14 handler
            move.l    #line1010,(lineAexception).w ;line 1010 handler
            move.l    a4,(etv_timer).w          ; default timer-tick vector -> rts
            move.l    #_critich,(etv_critic).w  ; default critical error handler
            move.l    a4,(etv_term).w           ; default terminal vector -> rts

*+
*  Setup the vblank deferred vector list.
*  (This data structor is ugly,
*   but we seem to be stuck with it).
*
*-
            lea       (_vbl_list).w,a0          ; a0 -> default list of vbl locs
            move.l    a0,(_vblqueue).w          ; install ptr to them
            move.w    #7,d0                     ; clear vbl vectors
avbl:       clr.l     (a0)+                     ; one at a time
            dbra      d0,avbl

            lea       (tconstat).l,a0
            movea.w   #xconstat,a1
            moveq     #31,d0
tconl:      move.l    (a0)+,(a1)+
            dbra      d0,tconl

            movea.l   (busexception).w,a0
            movea.l   sp,a1
            move.l    #vmeinit,(busexception).w
            move.b    #$40,(vme_mask).w         ; Enable IRQ6 from VMEBUS/MFP
            move.b    #$14,(sys_mask).w         ; VSYNC & HSYNC enable in the VME Bus System Control Unit
vmeinit:    move.l    a0,(busexception).w
            movea.l   a1,sp
ENDIF

*+
*  "The other half" of the BIOS handles character I/O,
*  call its initialization hoook.
*  (It can "never fail". This will get interesting
*  if we ever to a detachable keyboard ....)
*-
            bsr       initmfp

            move.w    #$400,d0                  ; Delay Mode, /50 Prescale, data = 0 (about 10us delay)
            bsr       mfpdelay
            move.l    #setikbd,-(sp)
            move.w    #1,-(sp)
            jsr       (ikbdws).l                ; reset ikbd
            addq.l    #6,sp
            move.w    #$700,d0                  ; Delay Mode, /200 Prescale, data = 0 (about 40us delay)
            move.w    #14,d1                    ; 15 * 40us = 600us delay
ikbddelay:  bsr       mfpdelay
            dbra      d1,ikbddelay

*+
*  Fire up %%2 cartridges
*
*-
            moveq     #2,d0                     ; bit# = 2
            bsr       cartscan                  ; execute cartridge aps
IF ROM_STBOOK
            bsr       cartscan_STBOOK_EXTROM

            move.b    #$80,(lcdPowerControl).w  ; LCD display on
            move.b    #$80,(lcdPowerControlShadow).w ; LCD display shadow register
            moveq     #2,d1                     ; Switch to 640x400x1
            lea       setvb1(pc),a6
            bra       waitvbl
setvb1:     move.b    d1,(v_shf_mod).w          ; set rez hardware register

ELIF ROM_TOS206
            moveq     #0,d1                     ; Switch to 320x200x4
            btst      #7,($fffffa01).w          ; monochrome monitor connected?
            bne.s     nomonomon                 ; (no)
            moveq     #2,d1                     ; Switch to 640x400x1
nomonomon:
            lea       setvb1(pc),a6
            bra       waitvbl
setvb1:     move.b    d1,(v_shf_mod).w          ; set rez hardware register

ELIF ROM_TOS306
            moveq     #4,d1                     ; Switch to 640x480x4
            btst      #7,($fffffa01).w          ; monochrome monitor connected?
            bne.s     nomonomon                 ; (no)
            moveq     #6,d1                     ; Switch to 1280x960x1
nomonomon:
            move.b    d1,(shift_tt).w
ENDIF
            move.b    d1,(sshiftmd).w           ; set rez shadow

            bsr       blittest
            jsr       ($e072aa).l               ; linaA blitter/no-blitter table init
            jsr       (esc_init).l              ; clear screen, initialize cursor
            move.l    #reseth,(swv_vec).w       ; RESET system on monitor change
            move.w    #1,(vblsem).w             ; enable vblank processing

*+
*  [1] Fire up %%0 cartridges
*  [2] Enable interrupts
*  [3] Fire up %%1 cartridges
*
*-
            clr.w     d0                        ; magic bit# = 0
            bsr       cartscan                  ; execute cartridge aps
IF ROM_STBOOK
            bsr       cartscan_STBOOK_EXTROM
ENDIF
            move      #$2300,sr                 ; Enable interrupts, go to IPL 3

            moveq     #1,d0                     ; magic bit# = 1
            bsr       cartscan                  ; execute cartridge aps
IF ROM_STBOOK
            bsr       cartscan_STBOOK_EXTROM

            move.l    (_hz_200).w,d0
            addq.l    #3,d0
resDelayL:  cmp.l     (_hz_200).w,d0            ; a short delay of 15-20ms
            bhi.s     resDelayL
            clr.b     (kb_shift).l              ; reset keyboard shift state
ENDIF

            move.l    #privinstr_exception,(privexception).w ;Emulate the MOVE SR so it works in usermode on a 68020+
            bra       resetcont

*+
* Flush instruction and data cache (68020+)
*-
flushCaches:move      sr,-(sp)
            ori       #$700,sr
            movec     cacr,d0
            or.l      #$808,d0                  ; CI (Clear Instruction Cache), DI (Clear Data Cache)
            movec     d0,cacr
            move      (sp)+,sr
            rts

*+
* Emulate the MOVE SR,<ea> so it works in usermode on 68010+
*-
privinstr_exception:movem.l d0-d2,-(sp)
            move.l    a1,-(sp)
            move.l    a0,-(sp)
            movea.l   $16(sp),a0                ; a0 -> pc
            move.w    (a0),d0                   ; d0 -> current opcode
            move.w    d0,d1                     ; d1 -> save current opcode
            and.w     #$ffc0,d0
            cmp.w     #$40c0,d0                 ; move SR,<ea>?
            bne       privinstr_exception_term  ; (no -- punt)

            move.l    #$30004e71,(endosbss).w   ; move.w d0,d0, nop
            move.l    #'NqNu',(endosbss+2*2).w  ; nop, rts
            move.w    d1,d0
            and.w     #7,d0                     ; <ea> register (bit 0..2)
            lsl.w     #8,d0
            lsl.w     #1,d0                     ; move into bit 9..11 of the destination <ea>
            or.w      d0,(endosbss).w
            move.w    d1,d0
            and.w     #$38,d0                   ; <ea> mode (bit 3..5)
            lsl.w     #3,d0                     ; move into bit 6..8 of the destination <ea>
            or.w      d0,(endosbss).w           ; generate a MOVE D0,<ea> from the MOVE SR,<ea> opcode

            moveq     #2,d2                     ; opcode size = 2
            cmp.w     #$180,d0                  ; <ea> == d8(An,Xn)?
            beq       privinstr_exception_term  ; not supported -> _term
            tst.w     d0                        ; <ea> == Dn?
            beq.s     privinstr_exception_dn
            cmp.w     #$140,d0                  ; <ea> == d16(An)?
            beq.s     privinstr_exception_d16
            cmp.w     #$1c0,d0                  ; <ea> == (xxx).w or (xxx).l?
            bne.s     privinstr_exception_other

* <ea> = (xxx).l or (xxx).w
            and.w     #7,d1                     ; register == 0 => (xxx).w
            beq.s     privinstr_exception_abs16
            addq.w    #2,d2                     ; it is (xxx).l -> opcode size += 2
            move.w    4(a0),(endosbss+2*2).w    ; move lower word of the absolute address
privinstr_exception_abs16:addq.w #2,d2          ; opcode size += 2
            move.w    2(a0),(endosbss+2).w      ; move upper word of the absolute address
            bra.s     privinstr_exception_ea    ; -> regular destination ea

* <ea> = d16(An)
privinstr_exception_d16:addq.w #2,d2            ; opcode size += 2
            move.w    2(a0),(endosbss+2).w      ; move d16 offset
privinstr_exception_other:and.w #7,d1           ; d1 = An from the destination <ea>
            cmp.w     #7,d1                     ; relative to A7?
            bne.s     privinstr_exception_ea    ; no -> regular destination ea

* <ea> = d16(A7) or d8(A7,Dn)
* Special case because A7 has to be the USP, instead of the current SSP (in A7)
            move      usp,a1                    ; have to use the USP, otherwise we wouldn't have gotten the exception
            andi.w    #$f3ff,(endosbss).w       ; convert A7-relative into A1-relative destination <ea>
            add.l     d2,$16(sp)                ; pc += opcode size
IF ROM_TOS306
            bsr       flushCaches
ENDIF
            move.w    $14(sp),d0                ; d0 = SR
            jsr       (endosbss).w              ; execute: MOVE D0,d(A1,Dn); NOP; RTS or MOVE D0,d(A1); NOP; RTS
            move      a1,usp                    ; restore USP, but shouldn't have changed anyway
            movea.l   (sp)+,a0                  ; restore registers
            movea.l   (sp)+,a1
            movem.l   (sp)+,d0-d2
            rte                                 ; continue execution

* <ea> = Dn
privinstr_exception_dn:add.l d2,$16(sp)         ; pc += opcode size
            ori.w     #$10,(endosbss).w         ; source ea = (A0)
IF ROM_TOS306
            bsr       flushCaches
ENDIF
            lea       20(sp),a0                 ; point to SR register to SSP
            movem.l   8(sp),d0-d2               ; restore d0-d2
            jsr       (endosbss).w              ; execute: MOVE (A0),Dn; NOP; NOP; RTS
            movea.l   (sp)+,a0                  ; restore registers
            movea.l   (sp)+,a1
            adda.w    #$c,sp                    ; skip d0-d2, because they have already been restored
            rte                                 ; continue execution

privinstr_exception_ea:add.l d2,$16(sp)         ; pc += opcode size
IF ROM_TOS306
            bsr       flushCaches
ENDIF
            movea.l   (sp)+,a0
            movea.l   (sp)+,a1
            move.w    12(sp),d0                 ; d0 = SR
            jsr       (endosbss).w              ; execute: MOVE D0,<ea>; ...; RTS
            movem.l   (sp)+,d0-d2               ; restore registers
            rte                                 ; continue execution

privinstr_exception_term:movea.l (sp)+,a0       ; restore registers
            movea.l   (sp)+,a1
            movem.l   (sp)+,d0-d2
            jmp       (_term).l                 ; illegal instruction => _term

*+
* Load shell (if _cmdload is nonzero)
* or execute GEM in ROM
*-
resetcont:  jsr       (osi).l                   ; initialize DOS

*--- set the current system time and date
            move.w    (osroml+30).l,(systemDate).l ;use BIOS time as current time
            jsr       (readCurrentTime).l       ; set current time to RTC time
            beq.s     notimechip                ; (no RTC)
            bsr       readIKBDTime              ; read time from the keyboard controller
            swap      d0
            tst.b     d0
            beq.s     notimechip                ; (not valid either)
            move.w    d0,(systemDate).l         ; update system time
            swap      d0
            move.w    d0,(systemTime).l

notimechip: clr.b     (tacr).w
            bclr      #5,(iera).w               ; disable Timer A interrupts

IF ROM_TOS306
            move.l    #$3111,d0
            movec     d0,cacr
ENDIF

            movea.l   #atari_image,a0           ; draw Atari Logo on the screen
            movea.l   (_v_bas_adr).w,a1
            move.b    (sshiftmd).w,d0
            cmp.b     #2,d0                     ; 640 x 400 (bw)
            beq.s     drawLogobw                ; (yes)
            cmp.b     #6,d0                     ; 1280 x 960 (bw)
            beq.s     drawLogobw                ; (yes)
IF ROM_TOS306
            adda.w    #4*320,a1
ELSE
            adda.w    #4*160,a1
ENDIF
            move.w    #86-1,d0                  ; 86 lines
drawLogoc1: moveq     #6-1,d1                   ; 6 words per raster line
drawLogoc2: move.w    (a0)+,d2                  ; draw 4 plane Atari Logo
            move.w    d2,(a1)+
            move.w    d2,(a1)+
            move.w    d2,(a1)+
            move.w    d2,(a1)+
            dbra      d1,drawLogoc2
IF ROM_TOS306
            adda.w    #320-6*8,a1
ELSE
            adda.w    #160-6*8,a1
ENDIF
            dbra      d0,drawLogoc1
            bra.s     drawLogo3b
drawLogobw:
IF ROM_TOS306
            adda.w    #4*160,a1
ELSE
            adda.w    #4*80,a1
ENDIF
            move.w    #86-1,d0                  ; 86 lines
drawLogobw2:moveq     #12-1,d1                  ; 12 bytes per raster line
drawLogobw3:move.b    (a0)+,(a1)+               ; draw 1 plane Atari Logo
            dbra      d1,drawLogobw3
IF ROM_TOS306
            adda.w    #160-12,a1
ELSE
            adda.w    #80-12,a1
ENDIF
            dbra      d0,drawLogobw2

drawLogo3b: moveq     #32+7,d7                   ; y = 7
            tst.b     (sshiftmd).w
            bne.s     drawLogo4
            moveq     #32+12,d7                 ; y = 12
drawLogo4:  move.l    #$30002,d6
            move.w    #$1b,-(sp)
            move.l    d6,-(sp)
            trap      #$d                       ; Bconout(2, ESC)
            move.w    #$59,4(sp)
            move.l    d6,(sp)
            trap      #$d                       ; Bconout(2, 'Y')
            move.w    d7,4(sp)
            move.l    d6,(sp)
            trap      #$d                       ; Bconout(2, ' '+0)
            move.w    #$20,4(sp)
            move.l    d6,(sp)
            trap      #$d                       ; Bconout(2, ' '+y)
            addq.w    #6,sp

IF !ROM_TOS306
            cmpi.l    #$3e80,(_hz_200).l        ; system running for >80s?
            bcc       ptch_term                 ; (then no ROM CRC check)
ENDIF

*--- Check the ROM CRC
checkROM:
IF ROM_STBOOK
            move.l    #$3fffe,d7                ; offset to ROM CRC (last 2 bytes)
            move.w    #0,d6                     ; number of banks = 1
ELIF ROM_TOS206
            move.l    #$1fffe,d7                ; offset to ROM CRC (last 2 bytes)
            move.w    #1,d6                     ; number of banks = 2
ELIF ROM_TOS306
            move.l    #$1fffe,d7                ; offset to ROM CRC (last 2 bytes)
            move.w    #3,d6                     ; number of banks = 4
ENDIF
            movea.l   #osroml,a5                ; ROM base address
checkROMl:
IF ROM_STBOOK
            move.w    #1,-(sp)                  ; checksum over every byte
ELIF ROM_TOS206
            move.w    #2,-(sp)                  ; checksum over every other byte
ELIF ROM_TOS306
            move.w    #4,-(sp)                  ; checksum over every 4th byte
ENDIF
            move.l    d7,-(sp)                  ; number of bytes
            move.l    a5,-(sp)                  ; buffer address
            bsr       calccrc
            adda.w    #10,sp
            movea.l   a5,a0
IF ROM_STBOOK
            adda.l    d7,a0
ELIF ROM_TOS206
            adda.l    #$3fffc,a0
ELIF ROM_TOS306
            adda.l    #$7fff8,a0
ENDIF
            move.b    (a0),d1                   ; high byte of CRC
            lsl.w     #8,d1
IF ROM_STBOOK
            move.b    1(a0),d1                  ; low byte of CRC
ELIF ROM_TOS206
            move.b    2(a0),d1                  ; low byte of CRC
ELIF ROM_TOS306
            move.b    4(a0),d1                  ; low byte of CRC
ENDIF
            cmp.w     d1,d0                     ; CRC identical?
            bne.s     checkROMbad               ; (no)
            addq.l    #1,a5                     ; next bank
            dbra      d6,checkROMl
            bra.s     ptch_term

badCRCtext: DC.B      'WARNING: BAD ROM CRC IN CHIP ',0
badCRCtextB:DC.B      '.\r\n',0

checkROMbad:move.l    a5,d5
            pea       (badCRCtext).l
            move.w    #9,-(sp)
            trap      #1
            move.b    #'E',d0                   ; 'E' - even
            btst      #0,d5
            beq.s     checkROMbad2
            move.b    #'O',d0                   ; 'O' - odd
checkROMbad2:move.w   d0,2(sp)
            move.w    #2,(sp)
            trap      #1
IF ROM_TOS306
            move.b    #'E',d0                   ; 'E' - even
            btst      #1,d5
            beq.s     checkROMbad3
            move.b    #'O',d0                   ; 'O' - odd
checkROMbad3:move.w   d0,2(sp)
            move.w    #2,(sp)
            trap      #1
ENDIF
            move.l    #badCRCtextB,2(sp)
            move.w    #9,(sp)
            trap      #1
            addq.w    #6,sp
            addq.l    #1,a5
            dbra      d6,checkROMl

* if no monochrome is active, holding down the alternate key forces
* to 320x200x4 instead of 640x480x4 for ST compatibility
IF ROM_TOS306
            cmpi.b    #6,(sshiftmd).w           ; TT high?
            beq.s     ttlowreschk               ; (yes)
            move.l    #$bffff,-(sp)
            trap      #$d                       ; Kbshift()
            addq.w    #4,sp
            btst      #3,d0                     ; alternate pressed?
            beq.s     ttlowreschk               ; (no)
            clr.w     -(sp)
            pea       ($ffffffff).w
            pea       ($ffffffff).w
            move.w    #5,-(sp)
            trap      #$e                       ; switch to low-res
            adda.w    #$c,sp
            move.l    #$808,d0
            movec     d0,cacr
ttlowreschk:
ENDIF

* During boot till this point any exception triggers a _coldboot, which
* erases the first MB and resets. From now own we point the exceptions
* to _term, which draws the bombs and terminates the currently running app
ptch_term:

IF ROM_STBOOK
            move.l    #$1000000,d1              ; d1 -> exception number
            lea       _term(pc),a0              ; new exception vector
            adda.l    d1,a0                     ; add the exception number into the upper 8 bits (2 = bus error)
            adda.l    d1,a0
            lea       (busexception).w,a1       ; start with the bus error exception
            move.w    #$3f,d0
            move.l    #_coldboot,d2             ; old exception vector
ptch_term1: cmp.l     (a1)+,d2                  ; is it pointing to _coldboot?
            bne.s     ptch_term2                ; no -> ignore It
            move.l    a0,$fffc(a1)              ; point it to _term
ptch_term2: adda.l    d1,a0                     ; increment the exception number in the upper 8 bits
            dbra      d0,ptch_term1             ; next vector ->
ENDIF

            bsr       _dskboot                  ; attempt to boot from disk
            bsr       mtest                     ; memory test and attempt to boot from DMA
            bsr       runresapps                ; execute resident applications in memory

            tst.w     (_cmdload).l              ; load shell from disk?
            beq.s     st_1                      ; (no -- execute GEM in ROM)
            bsr       _auto
            move.l    #osroml,(_sysbase).l      ; -> base of OS
            pea       nullenv(pc)               ; null environment string
            pea       nullenv(pc)               ; null argument string
            pea       cmdname(pc)               ; push shell filename
            clr.w     -(sp)                     ; load-and-go flavor of execute
            bra.s     st_x                      ; exec shell ("never return")

*--- bring up GEM:
st_1:       bsr       _auto                     ; do auto-exec
IF ROM_STBOOK
            bsr       _auto_ROMDISK
ENDIF
            move.l    #osroml,(_sysbase).l      ; -> base of OS

*--- kludge up an enviroment string
            lea       orig_env(pc),a0           ; kludge up an environment string, a0 -> original environment string
            movea.l   #the_env,a1               ; a1 -> place to put it
st_2:       cmpi.b    #'#',(a0)                 ; look for drive# character
            bne.s     st_3                      ; (not it)
            movea.l   a1,a2                     ; a2 -> place to put drive#
st_3:       move.b    (a0)+,(a1)+               ; copy a byte
            bpl.s     st_2                      ; copy while not end-of-string
            move.b    (_bootdev).l,d0           ; compute drive#, and shove it
            add.b     #$41,d0                   ; into the env string at the
            move.b    d0,(a2)                   ; appropriate spot

*--- kludge up an enviroment string:
            pea       (the_env).l               ; push address of environment string
            pea       (nullenv).l               ; no arguments
            pea       nullenv(pc)               ; null shell name (in ROM, after all)
            move.w    #5,-(sp)                  ; createPSP flavor of exec
            move.w    #$4b,-(sp)                ; exec function#
            trap      #1                        ; get pointer to PSP
            adda.w    #14,sp                    ; (clean up cruft)
            movea.l   d0,a0                     ; a0 -> PSP
            move.l    (exec_os).l,8(a0)         ; stuff saddr of GEM in PSP
            pea       (the_env).l               ; our environment string
            move.l    a0,-(sp)                  ; push addr of PSP
            pea       nullenv(pc)               ; null filename
            move.w    #4,-(sp)                  ; just-go
st_x:       move.w    #$4b,-(sp)                ; function = exec
            trap      #1                        ; do it
            adda.w    #14,sp                    ; cleanup stack

*+
* When startup fails (or if the exec returns,
* which "cannot happen") fake a system reset:
*-
            jmp       (reseth).l                ; back to the beginning...

*+
* Default enviroment string
* Cannot be more than 20 chars long without modifying
* the declaration for the_env,
* Any char >= $80 terminates the string (and is included in it)
* The last '#' character is replaced by the boot drive's name (A, B, ...)
*-
orig_env:   DC.B      'PATH=',0                 ; default pathname
            DC.B      '#:\',0                   ; is the boot device
            DC.B      $00                       ; terminate env string
            DC.B      $ff                       ; end of the env string (for our copy)

cmdname:    DC.B      'COMMAND.PRG',0           ; shell name
gemname:    DC.B      'GEM.PRG',0               ; desktop name
            DC.B      0
            DC.B      0

setikbd:    DC.B      $80,$01

*+
* _dskboot - boot (or return diagnostics)
* Passed:       nothing
* Returns:      D0.W = error number (if nonzero)
*-
_dskboot:   moveq     #3,d0                     ; %%3 ap cart
            bsr       cartscan
IF ROM_STBOOK
            bsr       cartscan_STBOOK_EXTROM
ENDIF
            movea.l   (hdv_boot).w,a0           ; go through boot vector
            jsr       (a0)
            tst.w     d0                        ; any errors?
            bne.s     dskb1                     ; (yes -- punt)
            movea.l   (_dskbufp).w,a0           ; a0 -> disk buffer
            jsr       (a0)                      ; execute boot sector (it might return)
dskb1:      rts                                 ; return status

mtest:      move.l    #80*200,d7                ; 80s
            cmp.l     (_hz_200).l,d7            ; is the system running for > 80s?
            bcs       dmaBoot                   ; (no memory test)
            movea.w   #0,a5                     ; done status = false
mtestloop:  cmpa.w    #0,a5                     ; memtest done?
            bne       mtest4                    ; (yes)
            bsr       memtest
            movea.w   d0,a5                     ; memtest done?
            beq       mtest5                    ; (no)
            move.w    #$1b,-(sp)
            move.l    #$30002,-(sp)
            trap      #$d                       ; Bconout(2, ESC)
            move.w    #'p',4(sp)
            move.l    #$30002,(sp)
            trap      #$d                       ; Bconout(2, 'p') Reverse video
            move.w    #$1b,4(sp)
            move.l    #$30002,(sp)
            trap      #$d                       ; Bconout(2, ESC)
            move.w    #'w',4(sp)
            move.l    #$30002,(sp)
            trap      #$d                       ; Bconout(2, 'w') Discard end of line
            move.l    #200,d5                   ; 200 = 1s
            move.w    #79,d4                    ; 79
            tst.b     (sshiftmd).l
            bne.s     mtest2
            move.l    #2*200,d5                 ; 2s
            moveq     #39,d4                    ; 39
mtest2:     move.l    d5,d6
            move.l    d4,d3
mtestsp:    move.w    #' ',4(sp)                ; d3 + 1 spaces
            move.l    #$30002,(sp)
            trap      #$d                       ; Bconout(2, ' ')
            dbra      d4,mtestsp
            move.w    #$d,4(sp)
            move.l    #$30002,(sp)
            trap      #$d                       ; Bconout(2, 13) CR
            subq.l    #1,d3
mtestsp2:   move.w    #' ',4(sp)                ; d3 spaces
            move.l    #$30002,(sp)
            trap      #$d                       ; Bconout(2, ' ')
            dbra      d3,mtestsp2
            move.w    #$1b,4(sp)
            move.l    #$30002,(sp)
            trap      #$d                       ; Bconout(2, ESC)
            move.w    #'q',4(sp)
            move.l    #$30002,(sp)
            trap      #$d                       ; Bconout(2, 'q') Normal video
            addq.l    #6,sp
mtest4:     cmp.l     (_hz_200).l,d6
            bhi.s     mtest5
            move.w    #$1b,-(sp)
            move.l    #$30002,-(sp)
            trap      #$d                       ; Bconout(2, ESC)
            move.w    #'K',4(sp)
            move.l    #$30002,(sp)
            trap      #$d                       ; Bconout(2, 'K') Clear to eol
            move.w    #8,4(sp)
            move.l    #$30002,(sp)
            trap      #$d                       ; Bconout(2, 8) Backspace
            addq.w    #6,sp
            add.l     d5,d6
mtest5:     cmpa.w    #0,a5                     ; memtest done?
            beq.s     mtest6                    ; (no)
            cmp.l     (_hz_200).l,d7
            bls.s     mtest8
mtest6:
IF ROM_STBOOK
            move.w    (tt_mcu+4).l,d0
            not.w     d0
            and.w     #$c,d0                    ; check bit 2 & 3
            bne.s     mtest7
ENDIF
            move.l    #$10002,-(sp)
            trap      #$d                       ; Bconstat(2) - key pressed?
            addq.l    #4,sp
            tst.l     d0
            beq       mtestloop                 ; (no - continue)
            move.l    #$20002,-(sp)
            trap      #$d                       ; Bconin(2) - read key
            addq.l    #4,sp
mtest7:     cmpa.w    #0,a5                     ; memtest done?
            bne.s     mtest8                    ; (yes)
            bsr       memtestabort
            move.l    d7,(_hz_200).l
            bra.s     dmaBoot
mtest8:     move.l    d7,(_hz_200).l
            move.w    #$d,-(sp)
            move.l    #$30002,-(sp)
            trap      #$d                       ; Bconout(2, 13) CR
            move.w    #$1b,4(sp)
            move.l    #$30002,(sp)
            trap      #$d                       ; Bconout(2, ESC)
            move.w    #'K',4(sp)
            move.l    #$30002,(sp)
            trap      #$d                       ; Bconout(2, 'K') Clear to eol
            addq.l    #6,sp

*--- boot from DMA device
dmaBoot:
IF ROM_STBOOK
            clr.w     (the_env).l               ; index to the dmaDevList
ELIF ROM_TOS206
            moveq     #$10,d4
ELIF ROM_TOS306
            jsr       (_scsisubb).l

            moveq     #8,d4
            move.b    (scu_gp1).w,d0
            and.w     #$f8,d0
            move.w    d0,(defaultBootDevice).w
            bne.s     dmaBoot2
            pea       (defaultBootDevice).w
            move.w    #2,-(sp)
            clr.l     -(sp)
            jsr       (_NVMaccess).l
            adda.w    #$a,sp
            tst.w     d0
            beq.s     dmaBoot2
ENDIF
            clr.w     (defaultBootDevice).w
dmaBoot2:   move.w    #1,d1                     ; d1 -> 2 tries per device
dmaBoot3:   move.w    d1,-(sp)
IF ROM_STBOOK
            move.w    (the_env).l,d4
            move.b    dmaDevList(pc,d4.w),d4
ENDIF
            move.w    d4,-(sp)                  ; pdev
            move.l    (_dskbufp).w,-(sp)        ; buf = _dskbufp
            move.w    #1,-(sp)                  ; count = 1
            clr.l     -(sp)                     ; sectnum = 0
            jsr       (_dmaread).l              ; read first sector of this device
            adda.w    #12,sp
            move.w    (sp)+,d1
            tst.l     d0                        ; read successful?
            beq.s     dmaBoot4                  ; yes ->
            addq.l    #1,d0                     ; error == time out?
            dbeq      d1,dmaBoot3               ; timeout or another try left? ->
            bra.s     dmaexecl                  ; try next device
dmaBoot4:   movea.l   (_dskbufp).w,a0
            move.w    #$ff,d0                   ; 256 word checksum over the boot sector
            moveq     #0,d1
dmaBoot5:   add.w     (a0)+,d1
            dbra      d0,dmaBoot5
            cmp.w     #$1234,d1                 ; checksum == 0x1234?
            beq.s     dmaexec                   ; execute this valid boot sector ->
dmaexecl:
IF ROM_STBOOK
            move.w    (the_env).l,d4
            addq.w    #1,d4                     ; increment next device
            move.w    d4,(the_env).l
            cmpi.b    #$ff,dmaDevList(pc,d4.w)  ; end of the device list?
            bne.s     dmaBoot2                  ; no -> continue with the next one
ELSE
            addq.w    #1,d4
            move.w    d4,d0
            and.w     #7,d0
            bne.s     dmaBoot2
            cmp.w     #8,d4
            beq.s     dmaBootx
            moveq     #0,d4
            bra.s     dmaBoot2
dmaBootx:
ENDIF
            rts                                 ; no valid boot sector found -> return

IF ROM_STBOOK
dmaDevList: DC.B      $10,$11,$00,$01,$02,$03,$04,$05 ;boot order of DMA devices, $ff terminates the list
            DC.B      $06,$07,$ff
ENDIF

dmaexec:    movea.l   (_dskbufp).w,a0           ; pointer to the boot sector buffer
            move.l    #'DMAr',d3                ; d3 -> 'DMAr' magic
            move.w    d4,d7                     ; d4 -> pdev
            asl.w     #5,d7                     ; d7 = 0
            move.w    (defaultBootDevice).w,d5  ; defaultBootDevice = 0
IF !ROM_STBOOK
            move.l    d4,-(sp)
ENDIF
            move.l    (hdv_rw).l,-(sp)          ; save read sector function pointer
            jsr       (a0)                      ; execute boot sector
            move.l    (sp)+,d0
IF !ROM_STBOOK
            move.l    (sp)+,d4
ENDIF
            cmp.l     (hdv_rw).w,d0             ; did the read sector function change?
            beq.s     dmaexecl                  ; no -> no device driver was loaded -> continue to load boot sectors
            rts                                 ; a new device driver was loaded -> return


*+
* cartscan - scan cartridge memory for runable applications
* Passed:       d0 = bit# to test in application's initialization vector
* Returns:      after all applications have been examined
* Uses:         a0,d0
*-
IF ROM_STBOOK
cartscan_STBOOK_EXTROM:lea (STBOOK_EXTROM).l,a0
            bra.s     cartscan2
ENDIF

cartscan:   lea       (cartbase).l,a0           ; scan cartridge memory for runable applications, a0 -> cartridge memory
cartscan2:  cmpi.l    #$abcdef42,(a0)+          ; correct magic number?
            bne.s     ca_r                      ; (no, so return)
ca_1:       btst      d0,4(a0)                  ; test bit in MSB of INIT address
            beq.s     ca_2                      ; (not set, so don't execute)
            movem.l   d0-d7/a0-a6,-(sp)         ; save everything
            move.l    4(a0),d0                  ; d0 -> initialization address
            and.l     #$ffffff,d0               ; mask out bit# in the top 8 bits of the address
            movea.l   d0,a0
            jsr       (a0)                      ; call cartridge application
            movem.l   (sp)+,d0-d7/a0-a6         ; restore everything
ca_2:       tst.l     (a0)                      ; test link address
            movea.l   (a0),a0                   ; a0 -> next header (or NULL)
            bne.s     ca_1                      ; loop on next header
ca_r:       rts

_rts:       rts


*+
* memchk - check pattern written to memory
*       Passed:         d1.l = offset
*                       a0 = base of pattern ($1f8 bytes long)
*                       a5 -> return address
*
*       Returns:        EQ: the pattern matched
*                       NE: the pattern didn't match
*
*       Uses:           d0.w, a1
*       Called-by:      Coldstart memory-sizing routine.
*-
memchk:     adda.l    d1,a0                     ; a0 -> memory to check
            clr.w     d0                        ; zap pattern seed
            lea       $1f8(a0),a1               ; a1 -> ending address
memchk1:    cmp.w     (a0)+,d0                  ; match?
            bne.s     memchkr                   ; (no -- return NE)
            add.w     #$fa54,d0                 ; yes -- bump pattern
            cmpa.l    a0,a1                     ; matched entire pattern?
            bne.s     memchk1                   ; (no)
memchkr:    jmp       (a4)                      ; "return" to caller


*+
* val_memval - test memory configuration validation
*  Passed:      a6 -> return addressd
*  Returns:     a5 -> 0 (quick zeropage)
*               EQ: memory setup OK
*               NE: memory never configured succesfully
*
*-
val_memval: cmpi.l    #$752019f3,(memvalid).w   ; test memory configuration validation, check first magic number
            bne.s     val_mr                    ; (mismatched -- return NE)
            cmpi.l    #$237698aa,(memval2).w    ; check one more (for paranoia)
            bne.s     val_mr                    ; (mismatched -- return NE)
            cmpi.l    #$5555aaaa,(memval3).w    ; check a third time (for more paranoia)
val_mr:     jmp       (a6)                      ; return EQ/NE


*+
* Default palette assignments.
*  Sort of corresponding to the GSX spec.
*-
colors:     DC.W      $0fff                     ; 0 white
            DC.W      $0f00                     ; 1 red
            DC.W      $00f0                     ; 2 green
            DC.W      $0ff0                     ; 3 yellow
            DC.W      $000f                     ; 4 blue
            DC.W      $0f0f                     ; 5 magenta
            DC.W      $00ff                     ; 6 cyan
            DC.W      $0555                     ; 7 "low white"
            DC.W      $0333                     ; 8 grey
            DC.W      $0f33                     ; 9 light red
            DC.W      $03f3                     ; 10 light green
            DC.W      $0ff3                     ; 11 light yellow
            DC.W      $033f                     ; 12 light blue
            DC.W      $0f3f                     ; 13 light magenta
            DC.W      $03ff                     ; 14 light cyan
            DC.W      $0000                     ; 15 black

*+
* hbl - force caller to IPL
* Oh-well:      "Yeah, it sucks, but it works" (--lt)
*
* Note:         Hacks caller's IPL to 3 (if it was 0). This is
*               a kludge against fascist programs and certain
*               debuggers that insist on starting processes up
*               at IPL 0.
*
*-
hbl:        move.w    d0,-(sp)                  ; save d0
            move.w    2(sp),d0                  ; get pushed SR
            and.w     #$700,d0                  ; strip crufty bits
            bne.s     hbl_r                     ; not IPL 0, so punt
            ori.w     #$300,2(sp)               ; force caller to IPL 3
hbl_r:      move.w    (sp)+,d0                  ; restore d0, back to victim
            rte

*+
* vbl - vertical black interrupt handler
*
*-
vbl:        addq.l    #1,(_frclock).l           ; bump frame clock
            subq.w    #1,(vblsem).l             ; P(vblsem) -- vblank locked?
            bmi       vblret

            movem.l   d0-d7/a0-a6,-(sp)         ; save registers
            addq.l    #1,(_vbclock).l           ; bump unblocked-frame Clock

*------ Call deferred interrupt vectors
            move.w    (nvbls).l,d7              ; d7 = # of deferred vblank vectors
IF ROM_STBOOK
            beq.s     vbl12                     ; (punt if no vectors)
ELSE
            beq       vbl12                     ; (punt if no vectors)
ENDIF
            subq.l    #1,d7                     ; turn into DBRA count
            movea.l   (_vblqueue).w,a0          ; a0 -> vectors
vbl10:      movea.l   (a0)+,a1                  ; a1 -> deferred vector
            cmpa.w    #0,a1                     ; if(a1 == NULL) continue;
            beq.s     vbl11
            movem.l   d7-d7/a0,-(sp)            ; save registers
            jsr       (a1)                      ; call routine
            movem.l   (sp)+,d7-d7/a0            ; restore registers
vbl11:      dbra      d7,vbl10                  ; loop for more vectors

IF ROM_TOS206 || ROM_TOS306
            move.b    (gpip).w,d1
            tst.b     ($a02).l                  ; microwire interface available?
            beq.s     vbl11b                    ; (no)
            move      sr,-(sp)
            ori       #$700,sr
vbl11a:     move.b    (sndmactl+1).w,d0
            move.b    (gpip).w,d1
            btst      #7,d1
            sne       d1
            move.b    (gpip).w,d2
            btst      #7,d2
            sne       d2
            cmp.b     d1,d2
            bne.s     vbl11a
            cmp.b     (sndmactl+1).w,d0
            bne.s     vbl11a
            move      (sp)+,sr
            btst      #0,d0
            beq.s     vbl11b
            not.b     d1
vbl11b:

IF ROM_TOS206
            move.b    (v_shf_mod).w,d0
            and.b     #3,d0
            cmp.b     #2,d0
            bge.s     vbl11c
ELIF ROM_TOS306
            move.b    (shift_tt).w,d0
            and.b     #7,d0
            cmp.b     #6,d0
            beq.s     vbl11c
ENDIF
            btst      #7,d1
            bne.s     vbl11e
            bsr       _wvbl
IF ROM_TOS206
            move.b    #2,d0
ELIF ROM_TOS306
            move.b    #6,d0
ENDIF
            bra.s     vbl11d
vbl11c:     btst      #7,d1
            beq.s     vbl11e
            move.b    (defshiftmd).w,d0
IF ROM_TOS206
            cmp.b     #2,d0
            blt.s     vbl11d
ELIF ROM_TOS306
            cmp.b     #6,d0
            bne.s     vbl11d
ENDIF
            clr.b     d0
vbl11d:
            move.b    d0,(sshiftmd).w
IF ROM_TOS206
            move.b    d0,(v_shf_mod).w
ELIF ROM_TOS306
            move.b    (shift_tt).w,d1
            and.b     #$f8,d1
            or.b      d0,d1
            move.b    d1,(shift_tt).w
ENDIF

            movea.l   (swv_vec).w,a0
            jsr       (a0)
vbl11e:
ENDIF
            jsr       (blink).l                 ; blink cursor

*--- reload color palettes
            tst.l     (colorptr).w              ; reload color palettes, if(colorptr != NULL)....
            beq.s     vbl1
            movea.l   (colorptr).w,a0           ; a0 -> user's color base
            lea       (palette).w,a1            ; a1 -> hardware palette base
            move.w    #7,d1                     ; d0 = count
vbl2:       move.l    (a0)+,(a1)+               ; load a palette
            dbra      d1,vbl2                   ; ...and repeat
            clr.l     (colorptr).w              ; zap colorptr

*--- reload display base register
vbl1:       tst.l     (screenpt).w              ; reload display base register, if(screenpt == NULL) don't
            beq.s     vbl5
            move.l    (screenpt).w,(_v_bas_adr).w ;set OS variable
            move.b    (_v_bas_adr+2).w,(v_bas_m).w ;load "middle" pointer
            move.b    (_v_bas_adr+1).w,(v_bas_h).w ;load "high" pointer
            move.b    (_v_bas_adr+3).w,(v_bas_l).w ;load "low" pointer

*------ Floppy drive-select timeout
vbl5:       bsr       _flopvbl                  ; (no args)

vbl12:      tst.w     (_prtcnt).w               ; monitor screen dump flag - printscreen active?
            bne.s     no_print                  ; no

*+
* printScreen
*
* We re-enable vblanks here, until the printScreen finishes -
*
*-
            bsr       _dumpit                   ; dump screen

no_print:   movem.l   (sp)+,d0-d7/a0-a6         ; restore registers & return (and a handy RTE)
vblret:     addq.w    #1,(vblsem).l             ; V(vblsem) [release vblank]
_rte:       rte


*+
* wvbl - wait for next vblank
* Passed:       nothing
* Returns:      at beginning of next vblank
* Uses:         D0
*-
wvbl:       move      sr,-(sp)                  ; save psw
            andi      #$fbff,sr                 ; enable vbl interrupts
            move.l    (_frclock).l,d0           ; d0 = frame clock
wvbl1:      cmp.l     (_frclock).l,d0           ; wait for clock to change
            beq.s     wvbl1
            move      (sp)+,sr                  ; then restore psw & return
            rts


*+
* _critic - critical error handler binding for C
* Falls-into:   _critich
* (screwy way to save two bytes....)
*
*-
crit_err:   move.l    (etv_critic).l,-(sp)      ; jump through critic vector

*+
* _critich - default critical error handler
* Loads -1 into D0 and returns
*
*-
_critich:   moveq     #-1,d0                    ; default return value = ERROR
            rts                                 ; return to trap invoker


*+
* trp13h - GEMDOS BIOS trap handler (trap 13)
* trp14h - Atari BIOS extensions (trap 14)
* traph  - trap handler
*
* On the stack:
*       From super-             From user
*       visor mode:             mode:
*       -----------             ------------
*       N(sp) args              N(usp) args
*       6(sp) func#             6(usp) func#
*       2(sp) ret               2(ssp) ret
*        (sp) SR                 (ssp) SR
*
* Returns:      anything in D0
* Uses:         d0-d2/a0-a2
* Keeps:        C registers
*
* Notes         BIOS traps are re-entrant to 'nlevels' (declared near the
*               beginning of this file).  Attempts to recurse more than
*               'nlevels' will probably result in a crash.
*
*               BIOS calls may be made from user mode.  (This differs from
*               the current GEMDOS spec, which states that BIOS traps are
*               available from supervisor mode only).
*
*-
trp14h:     lea       trp14tab(pc),a0           ; a0 -> trap14 jump table
            bra.s     traph
trp13h:     lea       trp13tab(pc),a0           ; a0 -> trap13 jump table
traph:      movea.l   (savptr).l,a1             ; a1 - registers save area
            move.w    (sp)+,d0                  ; pop SR and save it
            move.w    d0,-(a1)                  ; (need in D0 for user-mode test)
            move.l    (sp)+,-(a1)               ; save return addr
IF !ROM_TOS306
            tst.w     (_longframe).w
            beq.s     traph2
ENDIF
            tst.w     (sp)+
traph2:     movem.l   d3-d7/a3-a7,-(a1)         ; save C registers + super stack
            move.l    a1,(savptr).l             ; update save-area pointer

* make sure we have the right stack, call function:
            btst      #13,d0                    ; was in user mode?
            bne.s     b_supr                    ; (was in super: use super stack)
            move      usp,sp                    ; use user stack
b_supr:     move.w    (sp)+,d0                  ; get function#
            cmp.w     (a0)+,d0                  ; out of range?
            bge.s     b_exit                    ; (yes, so punt)
            move.w    d0,d1
            lsl.w     #2,d1                     ; turn D0 into longword index
            move.l    (a0,d1.w),d1              ; get pointer to function handler
IF ROM_TOS306
            bclr      #0,d1                     ; clear the indirect but set the old status in Z
            movea.l   d1,a0
            beq.s     b_1                       ; points to code
ELSE
            movea.l   d1,a0                     ; (quick and dirty test-for-negative)
            bpl.s     b_1                       ; points to code
ENDIF
            movea.l   (a0),a0                   ; indirect through RAM...
b_1:        suba.l    a5,a5                     ; a5 -> zero page
            jsr       (a0)                      ; call BIOS function

* restore registers, cleanup stack and return:
b_exit:     movea.l   (savptr).l,a1             ; a1 -> register save area
            movem.l   (a1)+,d3-d7/a3-a7         ; restore C registers + super stack
IF !ROM_TOS306
            tst.w     (_longframe).w
            beq.s     trph3
ENDIF
            clr.w     -(sp)
trph3:      move.l    (a1)+,-(sp)               ; push return address
            move.w    (a1)+,-(sp)               ; push old SR
            move.l    a1,(savptr).l             ; update save-pointer
            rte                                 ; return to caller


*------ jump table for GEMDOS functions:
trp13tab:   DC.W      $000c                     ; number of entries in jump table
            DC.L      _get_mpb
            DC.L      bconstat
            DC.L      bconin
            DC.L      bconout
IF ROM_TOS306
            DC.L      hdv_rw | 1
ELSE
            DC.L      hdv_rw
ENDIF
            DC.L      _setexc
            DC.L      _tickcal
IF ROM_TOS306
            DC.L      hdv_bpb | 1
ELSE
            DC.L      hdv_bpb
ENDIF
            DC.L      bcostat
IF ROM_TOS306
            DC.L      hdv_mediach | 1
ELSE
            DC.L      hdv_mediach
ENDIF
            DC.L      _drvmap
            DC.L      _shift

*------ jump table for Atari BIOS extensions:
trp14tab:
IF ROM_TOS306
            DC.W      $0060                     ; number of entry points
ELSE
            DC.W      $0041                     ; number of entry points
ENDIF
            DC.L      initmous
            DC.L      _rts
            DC.L      _physbase
            DC.L      _logbase
            DC.L      _getrez
            DC.L      _setscreen
            DC.L      _setpalette
            DC.L      _setcolor
            DC.L      _floprd
            DC.L      _flopwr
            DC.L      _flopfmt
            DC.L      _getdsb
            DC.L      midiws
            DC.L      mfpint
            DC.L      iorec
            DC.L      rsconf
            DC.L      keytrans
            DC.L      _rand
            DC.L      _proto_bt
            DC.L      _flopver
            DC.L      _dumpit
            DC.L      _cursconf
            DC.L      settime
            DC.L      gettime
            DC.L      bioskeys
            DC.L      ikbdws
            DC.L      jdisint
            DC.L      jenabint
            DC.L      giaccess
            DC.L      offgibit
            DC.L      ongibit
            DC.L      xbtimer
            DC.L      dosound
            DC.L      setprt
            DC.L      ikbdvecs
            DC.L      kbrate
            DC.L      _prtblk
            DC.L      wvbl
            DC.L      supexec
            DC.L      puntaes
            DC.L      _rts
            DC.L      floprate
            DC.L      _dmaread
            DC.L      _dmawrite
            DC.L      _bconmap
            DC.L      _rts
IF ROM_TOS306
            DC.L      _NVMaccess
ELSE
            DC.L      _rts
ENDIF
IF ROM_STBOOK || ROM_TOS206
            DC.L      _Waketime
ELSE
            DC.L      _rts
ENDIF
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      blitmode
IF ROM_TOS306
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts

            DC.L      _EsetShift
            DC.L      _EgetShift
            DC.L      _EsetBank
            DC.L      _EsetColor
            DC.L      _EsetPalette
            DC.L      _EgetPalette
            DC.L      _EsetGrey
            DC.L      _EsetSmear

            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts

* this routine seems to be unused?
_e00fae:    btst      #0,1(sp)                  ; read?
            bne.s     _hdv_rw_ptc               ; (no) => no patch necessary, write doesn't need to clear the cache
            move.l    #_hdv_rw_ptt,(sp)         ; patch return address
_hdv_rw_ptc:movea.l   (hdv_rw).w,a0
            jmp       (a0)

_hdv_rw_ptt:move.l    d0,-(sp)
            bsr       _flush_cache              ; flush the CPU cache
            move.l    (sp)+,d0
            jmp       (b_exit).l                ; exit trap
ENDIF

*+
* supexec - execute some code in supervisor mode
*
*-
supexec:    movea.l   4(sp),a0                  ; a0 -> code
            jmp       (a0)                      ; execute it


*+
* Character device I/O
*
* No check is made for "bogus" device numbers.  A wierd device
* number will result in a crash.
*
*-
bconstat:   lea       (xconstat).w,a0           ; a0 -> stat table
            moveq     #0,d1
            bra.s     chsw
bconin:     lea       (xconin).w,a0             ; a0 -> input table
            moveq     #4,d1
            bra.s     chsw
bcostat:    lea       (xcostat).w,a0            ; a0 -> ostat table
            moveq     #8,d1
            bra.s     chsw
bconout:    lea       (xconout).w,a0            ; a0 -> output table
            moveq     #$c,d1
chsw:       move.w    4(sp),d0                  ; get device number
            cmp.w     #5,d0
            bls.s     chsw2
            subq.l    #6,d0
            cmp.w     (bdevcount).w,d0
            bcc.s     chsw1
            movea.l   (bdevtabptr).w,a0
            asl.w     #3,d0
            adda.w    d0,a0
            add.w     d0,d0
            adda.w    d0,a0
            movea.l   (a0,d1.w),a0
            jmp       (a0)

chsw1:      moveq     #0,d0
            rts

chsw2:      lsl.w     #2,d0                     ; turn into longword index
            movea.l   (a0,d0.w),a0              ; get address of handler
            jmp       (a0)                      ; jump to it

*+
* Jump tables for
*       0 - lst: (printer)
*       1 - aux: (rs232)
*       2 - con: (screen)
*       3 - Atari midi
*       4 - Atari keyboard (output only)
*       5 - raw console output (bypass vt52 pressure cooker)
*
* No range checking is performed.  If a bogus device number
* is passed to the BIOS' character I/O handler, the system
* will crash to become funky duex.
*
*-
tconstat:   DC.L      _rts
            DC.L      auxistat
            DC.L      constat
            DC.L      midstat
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
tconin:     DC.L      _lstin
            DC.L      auxin
            DC.L      conin
            DC.L      midin
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
tcostat:    DC.L      _lstostat
            DC.L      _auxostat
            DC.L      conoutst
            DC.L      ikbdost
            DC.L      midiost
            DC.L      _rts
            DC.L      _rts
            DC.L      _rts
tconout:    DC.L      _lstout
            DC.L      _auxout
            DC.L      conout
            DC.L      midiwc
            DC.L      ikbdwc
            DC.L      _asc_out
            DC.L      _rts
            DC.L      _rts

*+
* _drvmap - return "active drive" bit vector
* Passed:       nothing
* Returns:      D0.L = a bit vector of live (rwabs'able) block device
*
*-
_drvmap:    move.l    (_drvbits).w,d0
            rts


*+
* _shift - get/set keyboard shift state
* Synopsis:     LONG _shift(bits)
*               WORD bits
*
* Returns:      D0.B = shift/alt/ctl/shift' bits
*
* Note:         Since the shift bits are changed at interrupt
*               level, any set from a get of the shift state
*               must be done as a critical section.
*
*-
_shift:     moveq     #0,d0
            move.b    (kb_shift).w,d0
            move.w    4(sp),d1
            bmi.s     shifr
            move.b    d1,(kb_shift).w
shifr:      rts


*+
* _get_mpb - return initial memory parameter block
* Synopsis:     _get_mpb(mpb)
*               MPB *mpb;
*
* Returns:      The properly initialized MPB.
*               The MPB points to an MD somewhere in BSS. The MD /must/
*               be in RAM since DOS will modify it.
*-
_get_mpb:   movea.l   4(sp),a0                  ; a0 -> MPB
            lea       (themd).w,a1              ; a1 -> MD

*--- initialize MPB:
            move.l    a1,(a0)                   ; mp_mfl = &themd;
            clr.l     4(a0)                     ; mp_mal = NULL;
            clr.l     8(a0)                     ; mp_rover = NULL;

*---- initialize MD:
            clr.l     (a1)                      ; m_link = NULL;
            move.l    (_membot).w,4(a1)         ; m_start = _membot;
            move.l    (_memtop).w,d0            ; m_length = _memtop - membot;
            sub.l     (_membot).w,d0
            move.l    d0,8(a1)
            clr.l     $c(a1)                    ; m_own = NULL;

IF ROM_TOS306
            cmpi.l    #$1357bd13,(ramvalid).l   ; fast mem available?
            bne.s     _get_mpbret               ; (no)
            cmpi.l    #$1000000,(ramtop).l      ; fast mem installed?
            bls.s     _get_mpbret               ; (no)
            lea       ($0a02).w,a2              ; themd_tt
            move.l    a2,(a1)                   ; themd.m_link = &themd_tt
            clr.l     (a2)                      ; m_link = NULL
            move.l    #$1000001,4(a2)           ; m_start = base address of fast mem | 1
            move.l    (ramtop).l,d0
            sub.l     #$1000000,d0
            move.l    d0,8(a2)                  ; m_length = size of fast mem
            clr.l     $c(a2)                    ; m_own = NULL
_get_mpbret:
ENDIF
            rts

*+
* _setexc - set exception vector
* Synopsis:     setexc(vecno, addr)
*               If 'addr' < 0, the vector is not set.
*
*               Extended vectors ($100 though $107) are located in the
*               first eight longwords of BSS, at $400. This is for
*               convienience -- they could really be located anywhere.
*
* Returns:      D0.L = original vector value
*
*-
_setexc:    move.w    4(sp),d0                  ; d0 = vector #
            lsl.w     #2,d0                     ; turn into longword index
            suba.l    a0,a0
            lea       (a0,d0.w),a0              ; a0 -> vector
            move.l    (a0),d0                   ; d0 = current vector address
            move.l    6(sp),d1                  ; d1 = what_to_change_it_to
            bmi.s     setex1                    ; punt if (d1 < 0)
            move.l    d1,(a0)                   ; set vector address
setex1:     rts

*+
* _tickcal - return system timer calibration value (in ms)
*
*-
_tickcal:   moveq     #0,d0                     ; cast to unsigned longword
            move.w    (_timr_ms).w,d0           ; get calibration
            rts

*+
* _physbase - get physical display base
*
*-
_physbase:  moveq     #0,d0                     ; cleanup pointer-to-be
            move.b    (v_bas_h).w,d0            ; load and shift bits 16..23
            lsl.w     #8,d0
            move.b    (v_bas_m).w,d0            ; load and shift bits 8..15
            lsl.l     #8,d0
            tst.b     (STEFlag).l
            bne.s     _physbase2
            move.b    (v_bas_l).w,d0
_physbase2: rts                                 ; return pointer in d0

*+
* _logbase -get logical display base
*
*-
_logbase:   move.l    (_v_bas_adr).w,d0         ; set software shadow
            rts

*+
* _getrez - get current screen rez
*-
_getrez:    moveq     #0,d0                     ; cleanup dirty bits
IF ROM_STBOOK
            move.b    (sshiftmd).w,d0           ; get screen rezolution
            and.b     #3,d0                     ; strip garbage bits
ELIF ROM_TOS206
            move.b    (v_shf_mod).w,d0          ; get screen rezolution
            and.b     #3,d0                     ; strip garbage bits
ELIF ROM_TOS306
            move.b    (shift_tt).w,d0
            and.b     #7,d0
ENDIF
            rts                                 ; return rez

*+
* _setscreen - set screen location(s), rez
*       _setscreen(logicalLoc, physicalLoc, rez)
*       LONG logicalLoc, physicalLoc;
*       WORD rez;
*
*-

*--- set logical location:
_setscreen: tst.l     4(sp)                     ; if(logloc < 0) then ignore it
            bmi.s     f5a
            move.l    4(sp),(_v_bas_adr).w      ; set software pointer from logloc

*--- set physical location:
f5a:        tst.l     8(sp)                     ; if(physloc < 0) then ignore it
            bmi.s     f5b
            move.b    9(sp),(v_bas_h).w         ; set bits 16..23 of hardware pointer
            move.b    $a(sp),(v_bas_m).w        ; set bits 8..15 of hardware pointer
            move.b    $b(sp),(v_bas_l).w        ; set bits 0..7 of hardware pointer

*--- set screen resolution (clears the screen, clobbers the cursor):
f5b:        tst.w     $c(sp)                    ; if(rez < 0) then ignore it
            bmi.s     f5r
            bsr       _wvbl                     ; wait for start of vertical-blank
            move.b    $d(sp),(sshiftmd).w       ; set software shadow
IF ROM_TOS306
            move.b    (shift_tt).w,d0
            and.b     #$f8,d0
            or.b      $d(sp),d0
            move.b    d0,(shift_tt).w
ELSE
            move.b    (sshiftmd).w,(v_shf_mod).w ;set hardware location
ENDIF
            clr.w     (vblsem).w                ; disable vblank processing
            jsr       (esc_init).l              ; re-initialize glass tty routines
            move.w    #1,(vblsem).w             ; re-enable vblanks
f5r:        rts

*+
* _setpalette - set palette (on next vblank)
*       _setpalette(LONG palettePtr)
*
*-
_setpalette:move.l    4(sp),(colorptr).w        ; set software pointer
            rts                                 ; (updated by vbl handler)

*+
* _setcolor - set single color, return old color
*       _setcolor(WORD colorNum, WORD colorValue)
*
*-
_setcolor:  move.w    4(sp),d1                  ; get color number
            add.w     d1,d1                     ; turn into word index
            and.w     #$1f,d1                   ; force color range (prevent buserr)
            lea       (palette).w,a0            ; a0 -> base of palette memory
            move.w    (a0,d1.w),d0              ; return old color
            tst.b     (STEFlag).l
            beq.s     _setc0a
            and.w     #$777,d0                  ; mask dirty bits
            bra.s     _setc0b
_setc0a:    and.w     #$fff,d0
_setc0b:    tst.w     6(sp)                     ; if new color is <0, don't set it
            bmi.s     _setc1                    ; (punt)
            move.w    6(sp),(a0,d1.w)           ; set new color
_setc1:     rts

*+
* puntaes - throw-away AES, restart the system
*  Passed:      nothing
*  Uses:        everything
*  Returns:     if AES was already thrown away
*
*-
puntaes:    movea.l   osroml+20(pc),a0          ; get pointer to magic
            cmpi.l    #$87654321,(a0)           ; is the magic still there?
            bne.s     paes1                     ; no -- just return
            cmpa.l    (phystop).w,a0            ; is it in ROM?
            bge.s     paes1                     ; yes -- we can't do anything about it
            clr.l     (a0)                      ; clobber AES!
            bra       reseth                    ; restart the system
paes1:      rts

*+
* _term - terminate current process
* Called-by:    Uncaught traps (bus errors, and so on)
* Saves:        processor state (in a bailout area)
*
*-
_term:
IF !ROM_TOS306
            jsr       (savp_2).l                ; stack PC
savp_2:
ENDIF
            move.l    (sp)+,(proc_pc).w         ; save bogus PC + exception number
            movem.l   d0-d7/a0-a7,(proc_regs).w ; common registers
IF ROM_TOS306
            move.l    2(sp),(proc_pc).w
            move.w    6(sp),d0
            and.w     #$fff,d0
            asr.w     #2,d0
            move.b    d0,(proc_pc).w
ENDIF
            move      usp,a0                    ; save USP
            move.l    a0,(proc_usp).w
            moveq     #15,d0                    ; save 16 words off top of
            lea       (proc_stk).w,a0           ; the stack (enough for
            movea.l   sp,a1                     ; any possible 680000 exception)
savp_1:     move.w    (a1)+,(a0)+               ; save a word
            dbra      d0,savp_1
            move.l    #$12345678,(proc_lives).w ; set magic number (procdump lives)

*--- draw an appropriate number of 'shrooms on the screen:
            moveq     #0,d1
            move.b    (proc_pc).w,d1
            subq.w    #1,d1                     ; 2 for bus error, 3 for address, etc.
            bsr.s     do_shroom
            move.l    #savend,(savptr).w        ; clobber BIOS top level
            move.w    #-1,-(sp)                 ; "error" return condition
            move.w    #$4c,-(sp)                ; GEMDOS function #$4C
            trap      #1                        ; "terminate process"
            bra       reseth                    ; on return, reset system
*+
* do_shroom - draw little mushroom clouds on the screen
*  Passed:      d1.w = #shrooms to draw (DBRA count)
*  Returns:     some shrooms on display
*  Uses:        d0-d7/a0-a2
*
*  Discussion:  The graphics ain't all that great.   And this is silly.
*
*-
do_shroom:  move.b    (sshiftmd).w,d7
IF ROM_TOS306
            and.w     #7,d7
ELSE
            and.w     #3,d7
ENDIF
            add.w     d7,d7                     ; d7 = rez index
            moveq     #0,d0
            move.b    (v_bas_h).w,d0
            lsl.w     #8,d0
            move.b    (v_bas_m).w,d0
            lsl.l     #8,d0
            tst.b     (STEFlag).l
            bne.s     dmste
            move.b    (v_bas_l).w,d0
dmste:      movea.l   d0,a0                     ; a0 -> base of mem to draw at
            cmp.w     #6,d7
            blt.s     dm00
            adda.l    #768*100,a0
            bra.s     dm01
dm00:       adda.w    #160*100,a0
dm01:       lea       (mushroom).l,a1           ; a1 -> source from
            move.w    #15,d6                    ; d6 = scanline count
dm0:        move.w    d1,d2                     ; d3 = # to draw on this line
            movea.l   a0,a2                     ; safe ptr to beg of line
dm1:        move.w    mcount(pc,d7.w),d5        ; d5 = #words to replicate
dm2:        move.w    (a1),(a0)+                ; draw a word
            dbra      d5,dm2                    ; (complete single shroom)
            dbra      d2,dm1                    ; another, on the same line
            addq.w    #2,a1                     ; next source word
            adda.w    mwidth(pc,d7.w),a2        ; next dest line
            movea.l   a2,a0
            dbra      d6,dm0                    ; (loop for next line)
            moveq     #30-1,d7
dm3:        bsr       wvbl
            dbra      d7,dm3
            rts                                 ; byebye

mcount:     DC.W      $0003,$0001,$0000,$0000
            DC.W      $0003,$0000,$0000,$0007
mwidth:     DC.W      $00a0,$00a0,$0050,$0000
            DC.W      $0140,$0000,$00a0,$0140

*+
* _fastcpy - "fast" 512-byte copy
* Synopsis:     fastcpy(src, dest)
*
*               Used by _rwabs to fake disk DMA to odd addresses.  Therefore,
*               disk I/O on odd addresses is very slow.  Lose, lose.
*
*-
_fastcpy:   movea.l   4(sp),a0                  ; a0 -> src
            movea.l   8(sp),a1                  ; a1 -> dest
            move.w    #$3f,d0                   ; d0 = move count (64*8 = 512)
fast1:      move.b    (a0)+,(a1)+               ; copy 8 bytes at a time
            move.b    (a0)+,(a1)+               ; to minimize loop overhead
            move.b    (a0)+,(a1)+
            move.b    (a0)+,(a1)+
            move.b    (a0)+,(a1)+
            move.b    (a0)+,(a1)+
            move.b    (a0)+,(a1)+
            move.b    (a0)+,(a1)+
            dbra      d0,fast1
            rts

*+
* Go through hard-disk initialization vector
*
*-
_hinit:     move.l    (hdv_init).l,-(sp)
            rts

autopath:   DC.B      '\AUTO\*.PRG',0
            DC.W      $1234,$5678,$9abc,$def0

*+
* _auto - exec auto-startup files in the appropriate subdirectory
* _auto1 - exec (with filename args)
* Passed:       a0 -> full filespec (pathname)
*               a1 -> filename part of filespec
*               _drvbits: bit vector of active drives
*               _bootdev: contains device to exec from
*
* Returns:      nothing
*
* Note:         If _drvbits%%_bootdev is zero, _auto simply quits (since
*               the device isn't active....)
*
* Uses:         everything
*-
_auto:      move.l    #$bffff,-(sp)
            trap      #$d
            addq.l    #4,sp
            btst      #2,d0                     ; control key pressed?
            bne.s     autoq                     ; (then ignore the AUTO folders)
IF !ROM_TOS306
            move.l    (_drvbits).w,d0           ; d0 - active dev vector
            move.w    (_bootdev).l,d1           ; d1 - dev# to exec from
            btst      d1,d0                     ; is the dev alive?
            beq.s     autoq                     ; (no -- so punt)
ENDIF
            lea       autopath(pc),a0           ; -> path
            lea       autopath+6(pc),a1         ; -> filename
_auto1:     move.l    (sp)+,(autoret).l         ; return addr (used by execlr)
            move.l    a0,(pathname).w           ; setup filename/pathname ptrs
            move.l    a1,(filename).w
IF ROM_TOS306
            move.l    (_drvbits).w,d0           ; d0 - active dev vector
            move.w    (_bootdev).l,d1           ; d1 - dev# to exec from
            btst      d1,d0                     ; is the dev alive?
            beq.s     autoq                     ; (no -- so punt)
ENDIF
            lea       nullenv(pc),a0            ; a0 -> \0\0
            move.l    a0,-(sp)                  ; null enviorment
            move.l    a0,-(sp)                  ; null command tail
            move.l    a0,-(sp)                  ; null shell name
            move.w    #5,-(sp)                  ; Create-PSP subfunction
            move.w    #$4b,-(sp)                ; exec function#
            trap      #1                        ; do DOS CALL
            adda.w    #16,sp
            movea.l   d0,a0                     ; a0 -> PSP
            move.l    #fauto,8(a0)              ; initial PC -> autoexec prog
            move.l    a3,-(sp)                  ; null enviroment
            move.l    d0,-(sp)                  ; -> PSP
            move.l    a3,-(sp)                  ; null shell name
            move.w    #4,-(sp)                  ; just-go
            move.w    #$4b,-(sp)                ; function = exec
            trap      #1                        ; do it
            adda.w    #16,sp                    ; cleanup stack & goodbye
            move.l    (autoret).l,-(sp)
autoq:      rts


*+
* ST Book has another ROMDISK in the 2nd 256kb of the ROM
* which is mapped as drive 'P'.
*-
IF ROM_STBOOK
_auto_ROMDISK:lea     autopathROM(pc),a0        ; -> path
            lea       autopathROM+8(pc),a1      ; -> filename
            bra.s     _auto1

autopathROM:DC.B      'P:\AUTO\*.PRG',0
            DC.W      $1234,$5678,$9abc,$def0
ENDIF

*+
* fauto - exec'd by _auto to do autostartup
*
* Passed:       pathname -> path part of filespec
*               filename -> file path of filespec
*
*-
fauto:      clr.l     -(sp)                     ; get into super mode
            move.w    #$20,-(sp)
            trap      #1
            addq.w    #6,sp                     ; cleanup
            movea.l   d0,a4                     ; a4 -> saved super stack

*---- free up some memory
            movea.l   4(sp),a6                  ; a6 -> base page
            lea       $100(a6),sp               ; sp -> new, safer addr
            move.l    #$100,-(sp)               ; keep $100 (just the basepage)
            move.l    a6,-(sp)                  ; -> start of mem to keep
            clr.w     -(sp)                     ; junk word
            move.w    #$4a,-(sp)                ; setblock(...)
            trap      #1
            addq.w    #6,sp
            tst.w     d0
            bne.s     au_dn                     ; punt on error

            move.w    #7,-(sp)                  ; find r/o+hidden+system files
            move.l    (pathname).l,-(sp)        ; -> filename (on input)
            move.w    #$4e,-(sp)                ; searchFirst
            moveq     #8,d7                     ; d7 = cleanup amount
au1:        pea       (autodma).l               ; setup DTA (for search)
            move.w    #$1a,-(sp)
            trap      #1                        ; search first/search next
            addq.w    #6,sp
            trap      #1
            adda.w    d7,sp                     ; cleanup stack
            tst.w     d0                        ; test for match
            bne.s     au_dn                     ; (no match -- quit)

*--- construct filename from path and the name we just found:
            movea.l   (pathname).l,a0           ; copy pathname
            movea.l   (filename).l,a2           ; a2 -> end+1 of pathname
            lea       (autoname).l,a1
au3:        move.b    (a0)+,(a1)+               ; copy path part of name
            cmpa.l    a0,a2                     ; finished?
            bne.s     au3                       ; (no)
            lea       (autodma+30).l,a0         ; copy fname to end of pathname
au2:        move.b    (a0)+,(a1)+
            bne.s     au2
            pea       nullenv(pc)               ; null enviroment
            pea       nullenv(pc)               ; no command tail
            pea       (autoname).l              ; -> file to exec
            clr.w     -(sp)                     ; load-and-go
            move.w    #$4b,-(sp)                ; exec(...)
            trap      #1
            adda.w    #16,sp
            moveq     #2,d7                     ; reset cleanup amount
            move.w    #$4f,-(sp)                ; searchNext
            bra.s     au1

*+
* The first GEMDOS process can never terminate.
* This is not a good feature.
* Kludge around it - re-initialize the stack
* and return to the guy who called us to begin with.
*
*-
au_dn:      lea       (_supstk+2048).l,sp       ; setup supervisor stack
            move.l    (autoret).l,-(sp)         ; get return addr
            rts                                 ; just jump there ...


*+
* _dumpit: dump screen
*
*-
_dumpit:    movea.l   (scr_dump).l,a0
            jsr       (a0)
            move.w    #-1,(_prtcnt).l
            rts


*+
* _scrdmp - printScreen(), font-end to _prtblk()
*  Passed:      nothing
*  Returns:     nothing
*  Uses:        everything
*-
_scrdmp:    move.l    (_v_bas_adr).w,(p_blkptr).w ;-> screen mem
            clr.w     (p_offset).w              ; offset = 0
            clr.w     d0
            move.b    (sshiftmd).w,d0           ; get w & h
            move.w    d0,(p_srcres).w
            add.w     d0,d0
            lea       reztab(pc),a0
            move.w    (a0,d0.w),(p_width).w     ; set display width, height
            move.w    6(a0,d0.w),(p_height).w
            clr.w     (p_left).w                ; left = right = 0
            clr.w     (p_right).w
            move.l    #$ffff8240,(p_colpal).w   ; -> hardware palettes
            clr.w     (p_masks).w               ; default mask ptr

* draft or final mode
            move.w    (pconfig).w,d1            ; p_dstres = pconfig%%3
            lsr.w     #3,d1
            and.w     #1,d1
            move.w    d1,(p_dstres).w

* printer or rs232 port
            move.w    (pconfig).w,d1            ; p_port = pconfig%%4
            move.w    d1,d0
            lsr.w     #4,d0
            and.w     #1,d0
            move.w    d0,(p_port).w

* select printer flavor
            and.w     #7,d1                     ; p_type = ptype[pconfig & 7]
            move.b    ptype(pc,d1.w),d0
            move.w    d0,(p_type).l

* do it
            pea       (p_blkptr).w              ; -> beginning of parameter area
            move.w    #1,(_prtcnt).w
            bsr       _prtblk                   ; print it (finally)
            move.w    #-1,(_prtcnt).l
            addq.w    #4,sp                     ; cleanup stack
            rts                                 ; and return


*---- screen resolution table (pixel) for printScreen
reztab:     DC.W      $0140,$0280,$0280,$00c8
            DC.W      $00c8,$0190

*--- printer flavors (based on low 3 bits of pconfig)
ptype:      DC.B      $00,$02,$01,$ff,$03,$ff,$ff,$ff

*--- what it is:
mushroom:   DC.W      $0600,$2900,$0080,$4840
            DC.W      $11f0,$01f0,$07fc,$0ffe
            DC.W      $0dfe,$1fff,$1fef,$0fee
            DC.W      $0fde,$07fc,$03f8,$00e0

*+
* waitvbl - wait for the beam inside the vertical blank area
*-
IF ROM_TOS306
waitvbl:    clr.b     (tbcr).w
            clr.b     (tbdr).w
            move.b    #8,(tbcr).w
waitvbl2:   tst.b     (tbdr).w
            beq.s     waitvbl2
            jmp       (a6)
ELSE
waitvbl:    lea       (tbdr).w,a0               ; a0 -> timer B data register
            lea       (tbcr).w,a1               ; a1 -> timer B control register
            bclr      #0,(iera).w               ; disable IRQ of timer B
            moveq     #1,d4                     ; wait for the timer to expire
            clr.b     (a1)                      ; stop timer B
            move.b    #$f0,(a0)                 ; event every 240 scan lines
            move.b    #8,(a1)                   ; timer b: event count mode (HBL)
waitvbl2:   cmp.b     (a0),d4                   ; wait for HBL 239 scan lines to pass
            bne.s     waitvbl2
waitvbl3:   move.b    (a0),d4
            move.w    #615,d3                   ; wait till we are inside the vbl area
waitvbl4:   cmp.b     (a0),d4
            bne.s     waitvbl3
            dbra      d3,waitvbl4
            move.b    #$10,(a1)                 ; timer b: reset
            jmp       (a6)
ENDIF

_wvbl:      bra       wvbl

*+
* runresapps - search address space for 512-byte resident code chunks
*
*-
runresapps: movea.l   (phystop).l,a0            ; start at the top of the address space
runresappsl:suba.w    #$200,a0
            cmpa.w    #$400,a0                  ; reach the lower bottom?
            bls.s     runresappsx               ; (bail out)
            cmpi.l    #$12123456,(a0)           ; check for magic
            bne.s     runresappsl               ; (no magic -> next block)
            cmpa.l    4(a0),a0                  ; second long is equal the base address of the block?
            bne.s     runresappsl               ; (no -> next block)
            clr.w     d0
            movea.l   a0,a1
            move.w    #$ff,d1                   ; checksum over 256 words
runresappsc:add.w     (a1)+,d0                  ; checksum over the block
            dbra      d1,runresappsc
            cmp.w     #$5678,d0                 ; magic checksum?
            bne.s     runresappsl               ; (no -> next block)
            move.l    a0,-(sp)                  ; save current address
            jsr       8(a0)                     ; call code block
            movea.l   (sp)+,a0                  ; continue with next block
            bra.s     runresappsl
runresappsx:rts


gettime:    lea       (readRTCTime).l,a3
            lea       (readIKBDTime).l,a4
            bra.s     timecode
settime:    move.w    4(sp),(systemDate).l
            move.w    6(sp),(systemTime).l
            lea       (writeRTCTime).l,a3
            lea       (writeIKBDTime).l,a4
timecode:   bsr       checkRTC
            bcc.s     timecodertc
            movea.l   a4,a3
timecodertc:jmp       (a3)


*+
* sysbase2ram - copy sysbase into memory and patch it
*-
sysbase2ram:lea       osroml(pc),a0
            lea       (ramsysbase).l,a1
            moveq     #$2f,d0
sysbase2raml:move.b   (a0,d0.w),(a1,d0.w)       ; copy 48 bytes into memory
            dbra      d0,sysbase2raml
            move.w    sysbase2ramjmp(pc),$fffa(a1) ;copy JMP instruction just before
            move.l    4(a1),$fffc(a1)           ; copy ROM reseth into this JMP
            move.w    sysbase2rambra(pc),(a1)   ; patch BRA at the beginning of sysbase copy to hit the JMP
            move.w    $1e(a1),$1c(a1)           ; copy os_date into os_conf
            move.l    a1,(_sysbase).l           ; set new sysbase pointer
            rts

sysbase2ramjmp:jmp    ($0000).w                 ; destination address is patched to reseth address

sysbase2rambra:bra.s  sysbase2ramjmp            ; branch onto the JMP

*+
* blitmode - enabled/disable/test the blitter
*
* Returns: d0.w - bit 0 - blitter enabled
*                 bit 1 - blitter available
*-
blitmode:   bsr.s     blittest                  ; test if blitter is installed
            move.w    d0,d4                     ; d4 = blitter status
            move.w    d0,d5
            lsr.w     #1,d5
            or.w      #$fffe,d5                 ; d5 = blitter enable mask
            jsr       ($e0732a).l
            move.w    d0,d3                     ; d3 = blitter active
            move.w    4(sp),d0                  ; new blitter status
            bmi.s     blitmodex                 ; <0 just return the status
            and.w     d5,d0                     ; mask blitter status out
            or.w      d4,d0                     ; or'd blitter available status
            jsr       ($e07302).l               ; disable/enable blitter
blitmodex:  move.w    d3,d0
            rts

*+
* blittest - test if the blitter is installed
*-
blittest:   move      sr,d1
            move.w    #0,d0                     ; d0 = 0 -> blitter not installed
            suba.l    a0,a0
            movea.l   sp,a2
            ori       #$700,sr
            movea.l   8(a0),a1
            move.l    #blittestx,8(a0)
            tst.w     $8a00(a0)
            moveq     #2,d0                     ; d0 = 2 -> blitter installed
blittestx:  move.l    a1,8(a0)
            move      d1,sr
            movea.l   a2,sp
            rts

*+
* delay for a little bit via timer a in the mfp
*-
mfpdelay:   bsr.s     mfpdelaysetup
IF !ROM_TOS306
mfpdelayl:  btst      #5,(ipra).w               ; did timer a fire?
            beq.s     mfpdelayl                 ; wait more if not
            clr.b     (tacr).w                  ; stop timer a
ELSE
mfpdelayl:  btst      #5,(iprb_TT).w            ; did timer a fire?
            beq.s     mfpdelayl                 ; wait more if not
ENDIF
            rts

*+
* setup timer a in the mfp
*-
mfpdelaysetup:
IF !ROM_TOS306
            move    sr,-(sp)
            ori       #$700,sr
            clr.b     (tacr).w                  ; timer A control register
            bclr      #5,(iera).w               ; disable timer A
            move.b    #$df,(ipra).w             ; clear a pending timer A interrupt
            bclr      #5,(imra).w               ; mask the timer A interrupt
            bset      #5,(iera).w               ; enable timer A
            move      (sp)+,sr
            move.b    d0,(tadr).w               ; set timer a data register
            ror.w     #8,d0
            move.b    d0,(tacr).w               ; set timer a control register
            rol.w     #8,d0
ELSE
            movem.w   d0-d1,-(sp)
            move      sr,-(sp)
            ori       #$700,sr
            move.b    (tcdcr_TT).w,d1
            and.b     #$f,d1
            move.b    d1,(tcdcr_TT).w
            bclr      #5,(ierb_TT).w
            move.b    #$df,(iprb_TT).w
            bclr      #5,(imrb_TT).w
            bset      #5,(ierb_TT).w
            move.b    d0,(tcdr_TT).w
            lsr.w     #4,d0
            and.b     #$f0,d0
            or.b      d0,d1
            move.b    d1,(tcdcr_TT).w
            move      (sp)+,sr
            movem.w   (sp)+,d0-d1
            rts
ENDIF
            rts

memclear:   movea.l   4(sp),a0                  ; a0 -> start address of the block
            movea.l   8(sp),a1                  ; a1 -> end address of the block
            movem.l   d3-d7/a3,-(sp)
            moveq     #0,d1
            moveq     #0,d2
            moveq     #0,d3
            moveq     #0,d4
            moveq     #0,d5
            moveq     #0,d6
            moveq     #0,d7
            movea.w   d7,a3
            move.l    a0,d0
            btst      #0,d0
            beq.s     memclear1
            move.b    d1,(a0)+                  ; clear a single byte to even-align
memclear1:  move.l    a1,d0
            sub.l     a0,d0
            and.l     #$ffffff00,d0
            beq.s     memclearb
            lea       (a0,d0.l),a0
            movea.l   a0,a2
            lsr.l     #8,d0
memclear256:movem.l   d1-d7/a3,-(a2)            ; clear 256 bytes
            movem.l   d1-d7/a3,-(a2)
            movem.l   d1-d7/a3,-(a2)
            movem.l   d1-d7/a3,-(a2)
            movem.l   d1-d7/a3,-(a2)
            movem.l   d1-d7/a3,-(a2)
            movem.l   d1-d7/a3,-(a2)
            movem.l   d1-d7/a3,-(a2)
            subq.l    #1,d0
            bne.s     memclear256
memclearb:  cmpa.l    a0,a1                     ; end address reached?
            beq.s     memcleare
            move.b    d1,(a0)+                  ; clear a single byte
            bra.s     memclearb
memcleare:  movem.l   (sp)+,d3-d7/a3
            rts

*+
* setup PMMU translation table
*-
IF ROM_TOS306
setupPMMU:  lea       ($700).l,a0
            lea       ($e363f0).l,a1            ; translation table
            move.w    #$3f,d0                   ; 64 descriptors
setupPMMUl: move.l    (a1)+,(a0)+
            dbra      d0,setupPMMUl

* PMMU Table:
* $x.......
*   $700: $00000742,$10000001,$20000001,$30000001,$40000001,$50000001,$60000001,$70000001,
*   $720: $80000041,$90000041,$A0000041,$B0000041,$C0000041,$D0000041,$E0000041,$00000782,

* $0.......
*   $740: $000007C2,$01000001,$02000001,$03000001,$04000001,$05000001,$06000001,$07000001,
*   $760: $08000001,$09000001,$0A000001,$0B000001,$0C000001,$0D000001,$0E000001,$0F000001,

* $Fx......
*   $780: $00000041,$01000041,$02000041,$03000041,$04000041,$05000041,$06000041,$07000041,
*   $7a0: $08000041,$09000041,$0A000041,$0B000041,$0C000041,$0D000041,$0E000041,$000007C2,

* $00...... or $FF......
*   $7c0: $00000001,$00100001,$00200001,$00300001,$00400001,$00500001,$00600001,$00700001,
*   $7e0: $00800001,$00900001,$00A00001,$00B00001,$00C00001,$00D00001,$00E00001,$00F00041,

* The purpose of the PMMU is to disable caching in the area of the address space that doesn't contain ram:
* $00F00000..$00FFFFFF
* $80000000..$EFFFFFFF
* $F0000000..$FEFFFFFF
* $FFF00000..$FFFFFFFF

            pmove     ($e364f0).l,crp           ; CPU Root Pointer: $80000002,$00000700: 4 byte descriptor, at $700, lower limit: $00000
            pmove     ($e364f8).l,tc            ; Translation Control Register: $80F04445: MMU enabled, pagesize = 15, bits: 4,4,4,5
            pmove     ($e364fc).l,tt0           ; Transparent Translation Register 0: $017E8107
            pmove     ($e36500).l,tt1           ; Transparent Translation Register 1: $807E8507
            rts
ENDIF

*+
* calccrc - calc a crc
*-
calccrc:    movea.l   4(sp),a0                  ; a0 -> buffer start
            move.l    8(sp),d2                  ; d2 -> number of bytes
            movea.w   $c(sp),a1                 ; a1 -> step offset through the buffer
            clr.w     d0
            clr.w     d1
            clr.w     d3
            lea       (crcTable).l,a2
calccrcl:   move.w    d0,d1
            lsl.w     #8,d0
            lsr.w     #8,d1
            move.b    (a0),d3
            adda.l    a1,a0
            eor.b     d3,d1
            add.w     d1,d1
            move.w    (a2,d1.w),d4
            eor.w     d4,d0
            subq.l    #1,d2
            bne.s     calccrcl
            rts

IF ROM_TOS306
_EsetShift: bsr       _wvbl
            moveq     #0,d0
            move.w    (shift_tt).w,-(sp)
            move.w    6(sp),(shift_tt).w
            move.w    (shift_tt).w,d0
            and.w     #7,d0
            move.b    d0,(sshiftmd).w
            clr.w     (vblsem).w
            jsr       (esc_init).l
            move.w    #1,(vblsem).w
            move.w    (sp)+,d0
            rts

_EgetShift: moveq     #0,d0
            move.w    (shift_tt).w,d0
            rts

_EsetBank:  moveq     #0,d0
            move.w    (shift_tt).w,d0
            and.w     #$f,d0
            tst.w     4(sp)
            bmi.s     _EsetBankx
            move.b    5(sp),(shift_tt+1).w
_EsetBankx: rts

_EsetColor: moveq     #0,d0
            lea       (TT_col).w,a0
            move.w    4(sp),d0
            and.w     #$ff,d0
            add.w     d0,d0
            adda.w    d0,a0
            move.w    (a0),d0
            and.w     #$fff,d0
            move.w    6(sp),d1
            bmi.s     _EsetColorx
            move.w    d1,(a0)
_EsetColorx:rts

_EsetPalette:move.w   4(sp),d0
            and.w     #$ff,d0
            movea.w   d0,a0
            adda.w    a0,a0
            sub.w     #$100,d0
            neg.w     d0
            move.w    6(sp),d1
            cmp.w     d0,d1
            ble.s     _EsetPal2
            move.w    d0,d1
_EsetPal2:  movea.l   8(sp),a1
            lea       $8400(a0),a0
            bra.s     _EsetPal4
_EsetPal3:  move.w    (a1)+,(a0)+
_EsetPal4:  dbra      d1,_EsetPal3
            rts

_EgetPalette:move.w   4(sp),d0
            and.w     #$ff,d0
            movea.w   d0,a0
            adda.w    a0,a0
            sub.w     #$100,d0
            neg.w     d0
            move.w    6(sp),d1
            cmp.w     d0,d1
            ble.s     _EgetPal2
            move.w    d0,d1
_EgetPal2:  movea.l   8(sp),a1
            lea       $8400(a0),a0
            bra.s     _EgetPal4
_EgetPal3:  move.w    (a0)+,(a1)+
_EgetPal4:  dbra      d1,_EgetPal3
            rts

_EsetGrey:  moveq     #0,d0
            move.b    (shift_tt).w,d1
            move.b    d1,d0
            lsr.b     #4,d0
            and.b     #1,d0
            bclr      #4,d1
            tst.w     4(sp)
            beq.s     _EsetGrey2
            bmi.s     _EsetGreyr
            bset      #4,d1
_EsetGrey2: move.b    d1,(shift_tt).w
_EsetGreyr: rts

_EsetSmear: moveq     #0,d0
            move.b    (shift_tt).w,d1
            move.b    d1,d0
            add.b     d0,d0
            subx.w    d0,d0
            neg.w     d0
            bclr      #7,d1
            tst.w     4(sp)
            beq.s     _EsetSmear2
            bmi.s     _EsetSmearr
            bset      #7,d1
_EsetSmear2:move.b    d1,(shift_tt).w
_EsetSmearr:rts
ENDIF

**************************************************************************
* DMAREAD.S from the AHDI source                                         *
**************************************************************************
*+
* dmaread() - read from a DMA device.
* dmawrite() - write to a DMA device.
*
* LONG  sectnum     $4(sp).l
* WORD  count       $8(sp).w
* BYTE  *buf;       $a(sp).l    $b(sp)=high $c(sp)=mid $d(sp)=low
* WORD  pdev;       $e(sp).w
*
* Returns 0 if successful.
* Returns a negative number if failure:
*   EUNDEV (-15L) if unknown device (bad dev number)
*   EREADF (-11L) if read failure
*   EWRITF (-10L) if write failure
*          (-1L)  if timed-out
*
* Comments:
*   ACSI, SCSI and IDE-AT drives are supported.
*-
_dmaread:   move.b    #0,(rwflag).l             ; rwflag = 0 => a read
            move.w    #$08,d1                   ; d1 = opcode for READ
            bra.s     dmarw
_dmawrite:  move.b    #1,(rwflag).l             ; rwflag = 1 => a write
            move.w    #$0a,d1                   ; d1 = opcode for WRITE
dmarw:      cmpi.w    #$10,$e(sp)               ; unit# > IDE-AT unit# 16?
            bhi       undev                     ; if so, return unknow device
            cmpi.w    #$f,$e(sp)                ; an IDE-AT unit?
            bhi.s     ide0                      ; if so, talk IDE-AT
* else talk ACSI or SCSI
            lea       (_cmdblk).l,a0            ; a0 -> beginning of command block
            move.b    d1,(a0)+                  ; byte 0 = opcode
            move.b    5(sp),(a0)+               ; byte 1 = msb of logical block addr
            move.b    6(sp),(a0)+               ; byte 2 = logical block addr
            move.b    7(sp),(a0)+               ; byte 3 = lsb of logical block addr
            move.b    9(sp),(a0)+               ; byte 4 = transfer length (in blocks)
            clr.b     (a0)                      ; byte 5 = control byte
            move.w    $e(sp),d0                 ; d0 = physical unit number
IF ROM_TOS306
            moveq     #0,d1
            move.w    8(sp),d1
            lsl.l     #8,d1
            add.l     d1,d1
ENDIF
            moveq     #6,d2                     ; d2 = length of command
            movea.l   $a(sp),a0                 ; a0 = buffer
            tst.b     (rwflag).l                ; read or write?
            bne.s     rw0                       ; it's a write
            bsr       _dorcmd                   ; send a receive data command
            bra       rw1
rw0:        bsr       wracsi                    ; send a write data command
            bra       rw1                       ; branch will take place for non-IDE drives
*
* Before doing anything with the IDE bus, make sure it's there.
* (The label noide is before at just because it fits better.)
*
noide:      movea.l   a1,sp                     ; got the bus error on IDEASR;
            move.l    a0,(busexception).w       ; restore vector & sp, return EUNDEV
            bra       undev

ide0:       movea.l   (busexception).w,a0
            movea.l   sp,a1
            move.l    #noide,(busexception).w
            tst.b     (ide_stat2).l             ; read a register to probe for IDE bus
            movea.l   a1,sp                     ; hey! no bus error!
            move.l    a0,(busexception).w       ; restore vector & sp and continue.
            move.b    $f(sp),d0                 ; IDE unit#
            bsr       _iderdy                   ; wait for drive to be ready
IF ROM_TOS306
            beq       undev                     ; if drive never gets ready, return with unknown device else
ELSE
            beq.s     undev                     ; if drive never gets ready, return with unknown device else
ENDIF
            moveq     #2,d0                     ; D_IDENTIFY delay required by Conner drives
            add.l     (_hz_200).w,d0            ; between power on and identify()
ide1:       cmp.l     (_hz_200).w,d0
            bcc.s     ide1

            pea       (sbuf).l                  ; scratch buffer for drive parameters
            move.w    $12(sp),-(sp)             ; IDE unit#
            bsr       _identify                 ; identify()
            addq.w    #6,sp                     ; clean up stack
            tst.w     d0                        ; successful?
IF ROM_TOS306
            bmi       rwend                     ; if timed-out, return
ELSE
            bmi.s     rwend                     ; if timed-out, return
ENDIF
            bne.s     rw2                       ; if error, return with error code else can do read or write
            moveq     #2,d0                     ; D_IDENTIFY delay required by Conner drives
            add.l     (_hz_200).w,d0            ; between identify() and r/w
ide4:       cmp.l     (_hz_200).w,d0
            bcc.s     ide4

IF ROM_TOS306
            lea       (sbuf).l,a0
            move.w    $c(a0),-(sp)              ; # of sectors per track
            move.w    6(a0),-(sp)               ; # of heads
            move.w    $12(sp),-(sp)             ; physical unit #
            bsr       _initdevpar
            addq.w    #6,sp
            tst.w     d0
            bmi.s     rwend
            bne.s     rw2
            lea       (sbuf).l,a0
ELSE
            pea       (sbuf).l                  ; beginning of _identify() data
            bsr       _gcparm                   ; get drive current parameters
            addq.w    #4,sp                     ; clean up stack
ENDIF
            move.w    $e(sp),-(sp)              ; physical unit #
            move.l    $c(sp),-(sp)              ; buffer
            move.w    $e(sp),-(sp)              ; count
            move.l    $c(sp),-(sp)              ; logical block address
IF ROM_TOS306
            move.w    $c(a0),-(sp)              ; # of sectors per track
            move.w    6(a0),-(sp)               ; # of heads
ELSE
            move.w    d2,-(sp)                  ; # sectors per track
            move.w    d1,-(sp)                  ; # data heads
ENDIF
            tst.b     (rwflag).l                ; read or write?
            bne.s     ide2                      ; (write)
            bsr       _ideread                  ; read sectors
            bra.s     ide3
ide2:       bsr       _idewrite                 ; write sectors
ide3:       adda.w    #16,sp                    ; clean up stack
rw1:
IF ROM_TOS306
            move      sr,-(sp)
            ori       #$700,sr
            movec     cacr,d1
            ori.w     #$808,d1
            movec     d1,cacr
            move      (sp)+,sr
ENDIF
            tst.w     d0                        ; successful?
            ble.s     rwend                     ; if no error or timed-out, return else
rw2:        moveq     #-11,d0                   ; assume it's a read error EREADF
            tst.b     (rwflag).l                ; read or write?
            beq.s     rwend                     ; if read, done
            moveq     #-10,d0                   ; else, it's a write error EWRITF
rwend:      rts

undev:      moveq     #-15,d0                   ; EUNDEV
            bra.s     rwend

*+
* dorcmd() - send a command which will receive data from the target
*
* Passed:
*   d0.w = physical unit number
*   d2.w = command length (NCMD or LCMD)
*   a0.l = buffer address
*-
_dorcmd:
IF ROM_TOS306
            cmp.w     #7,d0
            bls.s     _dorcmd2
            bsr       _rcvscsi
            rts
_dorcmd2:   bsr       _rcvacsi                  ; it's an ACSI device
ELSE
            bsr.s     _rcvacsi                  ; it's an ACSI device
ENDIF
            rts

*+
* dowcmd() - send a command which will write data to the target
*
* Passed:
*   d0.w = physical unit number
*   d2.w = command length (NCMD or LCMD)
*   a0.l = buffer address
*-
wracsi:
IF ROM_TOS306
            cmp.w     #7,d0
            bls.s     wracsi2
            bsr       _wrtscsi
            rts
wracsi2:
ENDIF
            bsr       _wrtacsi                  ; it's an ACSI device
            rts

*+
* LONG _qdone() - Wait for command byte handshake
* LONG _fdone() - Wait for operation complete
* Passed:   nothing
*
* Returns:  EQ: no time-out
*       MI: time-out condition
*
* Uses:     D0
*
*-
_fdone:     move.l    #600,d0                   ; ACLTMOUT
            bra.s     qd0

_qdone:     moveq     #20,d0                    ; ACSTMOUT

qd0:
IF ROM_TOS306
            move.l    d0,-(sp)                  ; save timeout value
            moveq     #2,d0                     ; busy-wait delay for slow ACSI
            add.l     (_hz_200).w,d0            ; minimum 20 microsec.
sdelay:     cmp.l     (_hz_200).w,d0
            bge.s     sdelay
            move.l    (sp)+,d0                  ; restore timeout value
ENDIF
            add.l     (_hz_200).w,d0
qd1:        cmp.l     (_hz_200).w,d0            ; time-out?
            bcs.s     qdq                       ; (i give up, return NE)
            btst      #5,(gpip).w               ; interrupt?
            bne.s     qd1                       ; (not yet)
            moveq     #0,d0                     ; return EQ (no time-out)
            rts

qdq:        moveq     #-1,d0
            rts

*+
* Wait for end of SASI command
*
* Passed:   d1 value to be written to wdl
*
* Returns:  EQ: success (error code in D0.W)
*       MI: time-out (-1 in D0.W)
*       NE: failure (SASI error code in D0.W)
*
* Uses:     d0
*-
_endcmd:    bsr.s     _fdone                    ; wait for operation complete
            bmi.s     endce                     ; (timed-out, so complain)
IF ROM_TOS306
            move      sr,-(sp)
            ori       #$700,sr
            movec     cacr,d0
            ori.w     #$800,d0
            movec     d0,cacr
            move      (sp)+,sr
ENDIF
            move.w    d1,(fifo).w
            move.w    (diskctl).w,d0            ; get the result
            and.w     #$ff,d0                   ; (clean it up) if non-0 should do a RequestSense command to learn more
endce:      move.l    (_hz_200).w,(lastacstm).l ; update controller last accessed time
            addq.l    #2,(lastacstm).l          ; lastacstm = _hz_200 + 2;
            rts

*+
*  Handle command time-out;
*  Unlock DMA chip and return completion status;
*-
_hdone:     move.w    #$80,(fifo).w             ; Landon's code seems to presume we put $80 there
IF ROM_TOS306
            clr.w     (flock).w                 ; NOW, signal that we are done
ELSE
            sf        (flock).w                 ; NOW, signal that we are done
ENDIF
            rts

*+
* delay()
*   5 - 10ms kludge delay for message byte sent back by controller.
*-
_delay:     move.l    d0,-(sp)                  ; preserve d0
            move.l    (lastacstm).l,d0          ; d0 = controller last accessed time
wait:       cmp.l     (_hz_200).w,d0            ; while (_hz_200 <= lastacstm)
            bcc.s     wait                      ; wait()
            move.l    (sp)+,d0                  ; restore d0
            rts

*+
* rcvacsi() - send a ACSI command which receives data from target.
*
* Passed:
*   d0.w = physical unit number
*   d2.w = command length (NCMD or LCMD)
*   a0.l = buffer address
*-
_rcvacsi:   movea.l   (busexception).w,a1       ; check to see if the ACSI bus actually exists! Needed for prototype PADs
            movea.l   sp,a2
            move.l    #noacsi,(busexception).w
            tst.w     (fifo).w                  ; harmless or bus erorr
            move.l    a1,(busexception).w       ; no bus error - restore & continue
            movea.l   a2,sp
            st        (flock).w                 ; lock FIFO
            bsr.s     _delay                    ; delay if necessary
            movea.w   #diskctl,a1               ; a1 = pointer to DMA chip
            bsr       setadma                   ; set DMA pointer
            move.w    #$190,2(a1)               ; WDL   ; toggle DMA chip to direction
            bsr       rstdelay                  ; delay
            move.w    #$90,2(a1)                ; WDL   ;  for receiving data
            bsr       rstdelay                  ; delay
            bsr       setacnt                   ; set DMA count
            lea       (_cmdblk).l,a0            ; a0 = address of command block
            moveq     #0,d1                     ; direction of DMA is IN
            bsr.s     sblkacsi                  ; send the command block
raend:      bra.s     _hdone                    ; cleanup after IRQ
*+
* wrtacsi() - send an ACSI command which will write data to the target
*
* Passed:
*   d0.w = physical unit number
*   d2.w = command length (NCMD or LCMD)
*   a0.l = buffer address
*-
*
_wrtacsi:   movea.l   (busexception).w,a1       ; check to see if the ACSI bus actually exists! Needed for prototype PADs
            movea.l   sp,a2
            move.l    #noacsi,(busexception).w
            tst.w     (diskctl).w               ; harmless or bus erorr
            move.l    a1,(busexception).w       ; no bus error - restore & continue
            movea.l   a2,sp
            st        (flock).w                 ; lock FIFO
            bsr.s     _delay
            movea.w   #diskctl,a1               ; a1 = pointer to DMA chip
            bsr.s     setadma                   ; set DMA pointer
            move.w    #$90,2(a1)                ; WDL   ; toggle DMA chip for "send"
            bsr       rstdelay                  ; delay
            move.w    #$190,2(a1)               ; WDL
IF ROM_TOS306
            bsr       rstdelay                  ; delay
ELSE
            bsr.s     rstdelay                  ; delay
ENDIF
            bsr.s     setacnt                   ; set DMA count
            move.l    #$100,d1                  ; d1 = direction of DMA is OUT
            bsr.s     sblkacsi                  ; send the command block
waend:      bra       _hdone                    ; cleanup after IRQ
* You get to this label if there is a bus error when probing for
* the ACSI bus: it's meant for prototype PADs, which don't
* have an ACSI DMA chip.
noacsi:     moveq     #-15,d0                   ; return EUNDEV
            movea.l   a2,sp
            move.l    a1,(busexception).w
            rts

*+
* sblkacsi() - send command block
*
* Passed:
*   d0.w = physical unit number
*   d1.l = direction of DMA ($0000 for IN or $0100 for OUT)
*   d2.w = command length (NCMD or LCMD)
*   a1.l = pointer to DMA chip
*
* Returns:
*   d0.l =  0 if successful
*   d0.l = -1 if time-out
*
* Trashes:
*   d0, d1, d2, a2
*-
sblkacsi:   move.b    #$88,d1                   ; next byte is the opcode
            move.w    d1,2(a1)                  ; WDL
            move.b    #$8a,d1                   ; following bytes are operands
            lea       (_cmdblk).l,a2            ; a2 = address of command block
* integrate unit # into cmd blk
            lsl.b     #5,d0                     ; shift unit number into place
            or.b      d0,(a2)                   ; first command byte = unit # | opcode
* control byte is sent seperately
            subq.w    #2,d2                     ; and dbra likes one less
sa1:        swap      d1                        ; d1.hw = operand
            move.b    (a2)+,d1                  ; d1.lw = tells controller next byte
            swap      d1                        ; is an operand
            move.l    d1,(a1)                   ; WDCWDL
            bsr       _qdone
            bmi.s     sbaend                    ; if time-out, returns
            dbra      d2,sa1                    ; else send rest of command block
            move.w    d1,2(a1)                  ; WDL - get ready to send control byte
            move.b    #0,d1                     ; signal sending control byte
            swap      d1                        ; d1.hw = control byte
            move.b    (a2),d1                   ; d1.lw = tells controller it's end
            swap      d1                        ; of command
            move.l    d1,(a1)                   ; send it
            move.b    #$8a,d1                   ; d1 = wdl value
            bsr       _endcmd                   ; wait for command completion
sbaend:     rts                                 ; heading home

*+
* setadma() - set the ACSI DMA pointer
*
* Passed:
*   a0.l = buffer address
*-
setadma:    move.l    a0,-(sp)                  ; move it on stack
            move.b    3(sp),(dmalow).w          ; set low-byte of address
            move.b    2(sp),(dmamid).w          ; set mid-byte of address
            move.b    1(sp),(dmahigh).w         ; set high-byte of address
            addq.l    #4,sp                     ; clean up stack
            rts


*+
* setacnt() - set the ACSI DMA counter
*
* Passed:
*   a1.l = pointer to DMA chip
*-
setacnt:
IF ROM_TOS306
            move.l    d1,-(sp)
            lsr.l     #8,d1
            add.l     d1,d1
            move.w    d1,(a1)
            move.l    (sp)+,d1
ELSE
            move.w    #$ff,(a1)                 ; set the ACSI DMA counter to 255 512-byte-blocks
ENDIF
            rts


*   After talking to the DMA chip in a way that may reset it,
* we need a 8 8Mhz clocks (ie. 1 microsec) delay, before we can
* talk to the chip again.
rstdelay:   tst.b     (gpip).w                  ; delay for 1 microsec
            tst.b     (gpip).w                  ; this amounts to 16 16Mhz clocks
            tst.b     (gpip).w
            tst.b     (gpip).w
            rts

IF ROM_TOS306
_rcvscsi:   move.l    a0,-(sp)
            andi.w    #7,d0
            bsr.s     _scsicmd
            movea.l   (sp)+,a0
            tst.w     d0
            bmi.s     _rcvscsir
            movea.l   a0,a1
            movea.w   #s_data,a2
            move.b    #1,(s_tcr).w
            move.b    (s_inircv).w,d0
_rcvscsil:  bsr       _scsidelay250ms
            bsr       _scsiwait
            bmi.s     _rcvscsir
            btst      #3,$a(a2)
            beq       _scsiaction
            move.b    (a2),(a1)+
            bsr       _scsidone
            bra.s     _rcvscsil
_rcvscsir:  rts

_wrtscsi:   andi.w    #7,d0
            move.l    a0,-(sp)
            move.w    d2,-(sp)
            bsr.s     _scsicmd
            move.w    (sp)+,d2
            movea.l   (sp)+,a0
            tst.w     d0
            bmi.s     _wrtscsir
            movea.l   a0,a1
            movea.w   #s_data,a2
            move.b    #0,(s_tcr).w
            move.b    (s_inircv).w,d0
_wrtscsil:  bsr       _scsidelay250ms
            bsr       _scsiwait
            bmi.s     _wrtscsir
            btst      #3,$a(a2)
            beq       _scsiaction
            move.b    (a1)+,(a2)
            bsr       _scsidone
            bra.s     _wrtscsil
_wrtscsir:  rts

_scsicmd:   move.l    d2,-(sp)
            move.w    d0,-(sp)
            bsr.s     _scsido
            addq.w    #2,sp
            move.l    (sp)+,d2
            tst.w     d0
            bmi.s     _scsicmdr
            move.b    #2,(s_tcr).w
            move.b    #1,(s_icr).w
            lea       ($a96).l,a1
            subq.w    #1,d2
            bsr       _scsidelay250ms
_scsicmdl:  move.b    (a1)+,d0
            bsr       _scsisuba
            tst.w     d0
            bmi.s     _scsicmdr
            dbra      d2,_scsicmdl
            moveq     #0,d0
_scsicmdr:  rts

_scsido:    bsr       _scsidelay250ms
_scsidow:   btst      #6,(s_idstat).w
            beq.s     _scsido2
            cmp.l     (a0),d1
            bhi.s     _scsidow
            bra.s     _scsidoerr
_scsido2:   move.b    #0,(s_tcr).w
            move.b    #0,(s_idstat).w
            move.b    #$c,(s_icr).w
            clr.w     d0
            move.w    4(sp),d1
            bset      d1,d0
            move.b    d0,(s_data).w
            move.b    #$d,(s_icr).w
            andi.b    #$fe,(s_mode).w
            andi.b    #$f7,(s_icr).w
            bsr       _scsidelay250ms
_scsidow2:  btst      #6,(s_idstat).w
            bne.s     _scsidook
            cmp.l     (a0),d1
            bhi.s     _scsidow2
_scsidoerr: moveq     #-1,d0
            bra.s     _scsidowr
_scsidook:  clr.w     d0
_scsidowr:  move.b    #0,(s_icr).w
            rts

_scsiaction:bsr       _scsidelay250ms
            move.b    #3,(s_tcr).w
            move.b    (s_inircv).w,d0
            bsr.s     _scsiwait
            bmi.s     _scsiactionr
            moveq     #0,d0
            move.b    (s_data).w,d0
            bsr       _scsidelay250ms
            move.l    d0,-(sp)
            bsr.s     _scsidone
            tst.w     d0
            beq.s     _scsiaction3
_scsiaction2:addq.l    #4,sp
            bra.s     _scsiactionr
_scsiaction3:bsr.s     _scsidelay250ms
            bsr.s     _scsiwait
            bmi.s     _scsiaction2
            move.b    (s_data).w,d0
            bsr.s     _scsidone
            tst.w     d0
            bmi.s     _scsiaction2
            move.l    (sp)+,d0
_scsiactionr:rts

_scsiwait:  btst      #5,(s_idstat).w
            bne.s     _scsiwaitok
            cmp.l     (a0),d1
            bhi.s     _scsiwait
            moveq     #-1,d0
            bra.s     _scsiwaitr
_scsiwaitok:moveq     #0,d0
_scsiwaitr: rts

_scsidone:  ori.b     #$11,(s_icr).w
_scsidonel: btst      #5,(s_idstat).w
            beq.s     _scsidoneok
            cmp.l     (a0),d1
            bhi.s     _scsidonel
            moveq     #-1,d0
            bra.s     _scsidoner
_scsidoneok:moveq     #0,d0
_scsidoner: andi.b    #$ef,(s_icr).w
            rts

_scsisuba:  move.w    d0,-(sp)
            bsr.s     _scsiwait
            bmi.s     _scsisuba2
            move.b    1(sp),(s_data).w
            bsr.s     _scsidone
_scsisuba2: addq.l    #2,sp
            rts

_scsisubb:  move.b    #$80,(s_icr).w
            bsr.s     _scsidelay250ms
_scsisubbl: cmp.l     (a0),d1
            bhi.s     _scsisubbl
            move.b    #0,(s_icr).w
            bsr.s     _scsidelay1s
_scsisubbl2:cmp.l     (a0),d1
            bhi.s     _scsisubbl2
            rts

_scsidelay250ms:
            movea.w   #_hz_200,a0
            moveq     #51,d1
            add.l     (a0),d1
            rts

_scsidelay1s:
            movea.w   #_hz_200,a0
            move.l    #201,d1
            add.l     (a0),d1
            rts
ENDIF

*+
* Wait for status to come back
*-
w4int:      move.l    #10*200,d0                ; d0 = time-out limit (D_WORST)
            add.l     (_hz_200).w,d0            ; d0 = expiration time
wi0:        btst      #5,(gpip).w               ; interrupt?
            beq.s     wi0                       ; if so, out of the loop
            cmp.l     (_hz_200).w,d0            ; time-out?
            bhi.s     wi0                       ; if not, wait some more
            moveq     #-1,d0                    ; else, return time-out
            bra.s     wi3
wi0:        moveq     #0,d0                     ; clear d0
            move.b    (ide_comst).l,d0          ; d0.b = status returned
            btst      #0,d0                     ; any error?
            bne.s     wi2                       ; if yes, return error code
            btst      #3,d0                     ; else DRQ?
            bne.s     wi3                       ; if so, just return
            moveq     #0,d0                     ; else return OK
            bra.s     wi3
wi2:        move.b    (ide_param).l,d0          ; else d0.b = error bits
wi3:        rts                                 ; return status or error code

*+
* ideread() - reads from 1 to 256 sectors as specified in the Task File,
*       beginning at the specified sector.
*      - sector count equal to 0 requests 256 sectors.
*
* ideread(nhd, nspt, sectnum, count, buf, pdev)
* WORD  nhd;        4(sp).w     ; # of data heads on pdev
* WORD  nspt;       6(sp).w     ; # of physical sectors per track
* LONG  sectnum;    8(sp).l     ; logical block address
* WORD  count;      $c(sp).w    ; # of sectors to read
* BYTE  *buf;       $e(sp).l    ; $f(sp)=high $10(sp)=mid $11(sp)=low
* WORD  pdev;       $12(sp).w   ; physical device number
*-
_ideread:   bsr       set_dhcs                  ; set physical address
            movea.l   $e(sp),a0                 ; a0 -> buffer to read into
            move.b    $d(sp),(ide_seccn).l      ; set sector count
            move.w    $c(sp),d1                 ; d1.w = # of sectors to read
            subq.w    #1,d1                     ; dbra likes one less
            move.b    #0,(ide_stat2).l          ; enable interrupt
            move.b    #$20,(ide_comst).l        ; set command code IDEREAD
ider0:      bsr.s     w4int                     ; wait for interrupt
            tst.w     d0                        ; successful?
            bmi.s     ider1                     ; if timed-out, return
            btst      #3,d0                     ; DRQ?
            beq.s     ider1                     ; if not, return
            bsr       readbuf                   ; fill sector buffer
            dbra      d1,ider0                  ; more to read?
            moveq     #0,d0                     ; everything's fine
ider1:      rts

*+
* idewrite() - writes from 1 to 256 sectors as specified in the Task File,
*       beginning at the specified sector.
*       - sector count equal to 0 requests 256 sectors.
*
* idewrite(nhd, nspt, sectnum, count, buf, pdev)
* WORD  nhd;        4(sp).w     ; # of data heads on pdev
* WORD  nspt;       6(sp).w     ; # of physical sectors per track
* LONG  sectnum;    8(sp).l     ; logical block address
* WORD  count;      $c(sp).w    ; # sectors to read
* BYTE  *buf;       $e(sp).l    ; $f(sp)=high $10(sp)=mid $11(sp)=low
* WORD  pdev;       $12(sp).w   ; physical device number
*-
_idewrite:  bsr.s     set_dhcs                  ; set physical address
            movea.l   $e(sp),a0                 ; a0 -> buffer to write from
            move.b    $d(sp),(ide_seccn).l      ; set sector count
            move.w    $c(sp),d1                 ; d1.w = # of sectors to read
            subq.w    #1,d1                     ; dbra likes one less
            move.b    #0,(ide_stat2).l          ; enable interrupt
            move.b    #$30,(ide_comst).l        ; set command code IDEWRITE
idew0:      btst      #3,(ide_stat2).l          ; DRQ?
            beq.s     idew0                     ; if not, wait longer
idew1:      bsr       wrtbuf                    ; fill sector buffer
            bsr       w4int                     ; wait for interrupt
            tst.w     d0                        ; successful?
            bmi.s     idew2                     ; if timed-out, return
            btst      #3,d0                     ; DRQ?
            beq.s     idew2                     ; if not, return
            dbra      d1,idew1                  ; else go transfer data
            moveq     #0,d0                     ; everything's fine
idew2:      rts

*+
* set_dhcs() - convert a logical block address into a physical address.
*        - set drive #, head #, cylinder # and sector # in task file.
*
* Passed:
*   8(sp).w = nhd = # of data heads
*   $a(sp).w = nspt = # of physical sectors per track
*   $c(sp).l = logical block address
*   $16(sp).w = physical unit #
*-
set_dhcs:   move.l    $c(sp),d1                 ; d1.l = logical block address
            move.w    8(sp),d2                  ; d2.w = # of data heads
            move.w    $a(sp),d0                 ; d0.w = # of physical sectors per track
            mulu.w    d0,d2                     ; d2.l = # of sectors per cylinder = # heads * # of sectors per track
            divu.w    d2,d1                     ; d1.w = cylinder # = log block addr / #spc
            move.b    d1,(ide_zylow).l          ; set cylinder low
            lsr.l     #8,d1                     ; d1.b = cylinder high
            move.b    d1,(ide_zyhig).l          ; set cylinder high
            lsr.l     #8,d1                     ; d1.l = sector # within the cyl
            divu.w    d0,d1                     ; d1.w = head # = sector # within cyl / #spt
            move.w    $16(sp),d0                ; d0.w = physical unit #
            andi.b    #7,d0                     ; mask off flags from physical unit #
            lsl.b     #4,d0                     ; shift unit # to place
            or.b      d0,d1                     ; or in drive #
            move.b    d1,(ide_headn).l          ; set drive and head #
            swap      d1                        ; d1.w = sector # (base 0)
            addq.w    #1,d1                     ; = sector # + 1 (base 1)
            move.b    d1,(ide_stars).l          ; set sector #
            rts

*+
* identify() - allows the Host to receive parameter information from
*          the drive.
*
* identify(pdev, buf)
* WORD  pdev;   4(sp).w     ; physical unit #
* BYTE  *buf;   6(sp).l     ; buffer to put data
*-
IF ROM_TOS306
_initdevpar:move.w    4(sp),d0                  ; d0 = physical unit #
            andi.b    #7,d0                     ; mask off flags (if any)
            lsl.w     #4,d0                     ; shift unit # to place
            move.b    d0,(ide_headn).l          ; set drive #
            move.w    6(sp),d0
            subq.b    #1,d0
            or.b      d0,(ide_headn).l          ; # of heads - 1
            move.b    9(sp),(ide_seccn).l       ; # of sectors per track
            move.b    #0,(ide_stat2).l          ; enable interrupt
            move.b    #$91,(ide_comst).l        ; set command code INITIALIZE DEVICE PARAMETERS
            bra       w4int
ENDIF

*+
* identify() - allows the Host to receive parameter information from
*          the drive.
*
* identify(pdev, buf)
* WORD  pdev;   4(sp).w     ; physical unit #
* BYTE  *buf;   6(sp).l     ; buffer to put data
*-
_identify:  move.w    4(sp),d0                  ; d0 = physical unit #
            andi.b    #7,d0                     ; mask off flags (if any)
            lsl.b     #4,d0                     ; shift unit # to place
            move.b    d0,(ide_headn).l          ; set drive #
            movea.l   6(sp),a0                  ; a0 -> buffer
            move.b    #0,(ide_stat2).l          ; enable interrupt
            move.b    #$ec,(ide_comst).l        ; set command code IDENTIFY
            bsr       w4int                     ; wait for interrupt
            tst.w     d0                        ; successful?
            bmi.s     id0                       ; if timed-out, return
            btst      #3,d0                     ; DRQ?
            beq.s     id0                       ; if not, return with error
            bsr.s     readbuf                   ; read data
            moveq     #0,d0                     ; everything's fine
id0:        rts

*+
* readbuf() - reads 512 bytes (128 longs) of data into sector buffer.
*
* Passed:
*   a0.l = buffer to store data read from sector buffer
*-
readbuf:
IF ROM_TOS306
            moveq     #$7f,d0                   ; d0 = (# of words of data to read) - 1
            lea       (ide_datar).l,a1          ; a1 -> data bus
rb0:        move.l    (a1),(a0)+                ; read data from bus
            dbra      d0,rb0                    ; repeat until all done
ELSE
            moveq     #$1f,d0                   ; d0 = (# of words of data to read / 8) - 1
            lea       (ide_datar).l,a1          ; a1 -> data bus
rb0:        move.w    (a1),(a0)+                ; read data from bus
            move.w    (a1),(a0)+                ; read data from bus
            move.w    (a1),(a0)+                ; read data from bus
            move.w    (a1),(a0)+                ; read data from bus
            move.w    (a1),(a0)+                ; read data from bus
            move.w    (a1),(a0)+                ; read data from bus
            move.w    (a1),(a0)+                ; read data from bus
            move.w    (a1),(a0)+                ; read data from bus
            dbra      d0,rb0                    ; repeat until all done
ENDIF
            rts

*+
* wrtbuf() - writes 512 bytes (128 longs) of data to sector buffer.
*
* Passed:
*   a0.l = buffer with data to write to sector buffer
*-
wrtbuf:
IF ROM_TOS306
            moveq     #$7f,d0                   ; d0 = (# of words of data to read) - 1
            lea       (ide_datar).l,a1          ; a1 -> data bus
wb0:        move.l    (a0)+,(a1)                ; write data to bus
            dbra      d0,wb0                    ; repeat until all done
ELSE
            moveq     #$1f,d0                   ; d0 = (# of words of data to read / 8) - 1
            lea       (ide_datar).l,a1          ; a1 -> data bus
wb0:        move.w    (a0)+,(a1)                ; write data to bus
            move.w    (a0)+,(a1)                ; write data to bus
            move.w    (a0)+,(a1)                ; write data to bus
            move.w    (a0)+,(a1)                ; write data to bus
            move.w    (a0)+,(a1)                ; write data to bus
            move.w    (a0)+,(a1)                ; write data to bus
            move.w    (a0)+,(a1)                ; write data to bus
            move.w    (a0)+,(a1)                ; write data to bus
            dbra      d0,wb0                    ; repeat until all done
ENDIF
            rts

*+
* _iderdy() - test if the IDE drive is ready
*
* Passed:
*   d0.b = IDE drive unit #
*
* Returns: 0 - if drive is NOT ready
*      1 - if drive is ready
*-
_iderdy:    andi.b    #7,d0                     ; mask off flags (if any)
            lsl.b     #4,d0                     ; shift unit # to place
            move.b    d0,(ide_headn).l          ; set drive #
IF ROM_TOS306
            move.l    #5*200,d0                 ; set up timer IDERDY
            add.l     (_hz_200).w,d0
ir0:        btst      #6,(ide_stat2).l
            bne.s     ir1
ELSE
            move.b    #$50,d1                   ; ready status
            move.l    #5*200,d0                 ; set up timer IDERDY
            add.l     (_hz_200).w,d0
ir0:        cmp.b     (ide_stat2).l,d1          ; is drive ready and not busy?
            beq.s     ir1                       ; if so, return with drive ready
ENDIF
            cmp.l     (_hz_200).w,d0            ; time-out yet?
            bcc.s     ir0                       ; if not, wait longer
            moveq     #0,d0                     ; else return drive NOT ready
            rts

ir1:        moveq     #1,d0                     ; else, drive is ready
            rts

*+
* gcparm() - get current drive parameters
*
* gcparm(buf)
* char  *buf;   $4(sp).l    /* -> data returned by identify() */
*
* Returns:
*   d0.w = # of default cylinders
*   d1.w = # of default heads
*   d2.w = # of default sectors per track
*-
IF !ROM_TOS306
_gcparm:    movea.l   4(sp),a0                  ; a0 -> data buffer
            adda.w    #$50,a0                   ; a0 -> where Conner model number is (CONMDL)
            move.l    a0,-(sp)
            pea       (cp2024).l
            move.w    #6,-(sp)
            bsr.s     strcmp                    ; compare model# with "CP2024"
            adda.w    #$a,sp                    ; clean up stack
            tst.w     d0                        ; is unit the CP2024 (Kato 20Mb)?
            bne.s     gcp0                      ; if not, handle the normal way else return default values of CP2024
            move.w    #$267,d0                  ; d0.w = # of cylinders (CP20NCYL)
            move.w    #4,d1                     ; d1.w = # of heads (CP20NHEAD)
            move.w    #$11,d2                   ; d2.w = # of spt (CP20NSPT)
            bra.s     gcpend
gcp0:       movea.l   4(sp),a0
            move.w    2(a0),d0                  ; d0.w = # of cylinders
            move.w    6(a0),d1                  ; d1.w = # of heads
            move.w    $c(a0),d2                 ; d2.w = # of sectors per track
gcpend:     rts

conner:     DC.B      'Conner',0
            DC.B      $00
cp2024:     DC.B      'CP2024',0
            DC.B      $00

*+
* strcmp() - compare two strings
*
* Passed:
*   4(sp).w  = n (# of bytes to compare)
*   6(sp).l  = address of first string
*   10(sp).l = address of second string
*
* Returns:
*   d0.w = 0    if first n bytes of the 2 strings are the same
*        = non-0    otherwise
*-
strcmp:     movem.l   d1/a0-a1,-(sp)            ; save registers d1, a0 and a1
            move.w    $10(sp),d1                ; d1 = byte count
            subq.w    #1,d1                     ; dbra likes one less
            movea.l   $12(sp),a0                ; a0 -> string 1
            movea.l   $16(sp),a1                ; a1 -> string 2
            moveq     #1,d0                     ; assume strings are not the same
str0:       cmpm.b    (a0)+,(a1)+               ; characters the same?
            bne.s     str1                      ; if not, return
            dbra      d1,str0                   ; else compare next character
            moveq     #0,d0                     ; the strings are the same
str1:       movem.l   (sp)+,d1/a0-a1            ; restore registers d1, a0 and a1
            rts
ENDIF

**************************************************************************
*                                                                        *
**************************************************************************
IF ROM_TOS306
readCurrentTime:
            bsr       checkRTC
            bcs.s     readCurrentTime2
            move.b    #$d,(rtcadd).w
            move.b    (rtcdat).w,d0
            btst      #7,d0
            bne.s     readCurrentTime2
            move.l    #$12c80000,-(sp)          ; default date: July 4th, 1989, 12:00am
            bsr       writeRTCTime
            addq.w    #4,sp
readCurrentTime:
            bsr.s     readRTCTime
            cmp.l     #-1,d0
            beq.s     readCurrentTime2
            moveq     #0,d0
readCurrentTime2:
            rts

readRTCTime:bsr       checkRTC
            bcs.s     readCurrentTime2
            move.b    #$d,(rtcadd).w
            btst      #7,(rtcdat).w
            beq       readRTCTimeErr
            move      sr,d2
            move.w    d2,d0
            or.w      #$700,d0
readRTCTime3:move.b    #$a,(rtcadd).w
            btst      #7,(rtcdat).w
            bne.s     readRTCTime3
            moveq     #0,d0
            move.l    d0,d1
            move.b    #0,(rtcadd).w
            move.b    (rtcdat).w,d0
            asr.w     #1,d0
            move.b    #2,(rtcadd).w
            move.b    (rtcdat).w,d1
            bfins     d1,d0{21:6}
            move.b    #4,(rtcadd).w
            move.b    (rtcdat).w,d1
            bfins     d1,d0{16:5}
            move.b    #7,(rtcadd).w
            move.b    (rtcdat).w,d1
            bfins     d1,d0{11:5}
            move.b    #8,(rtcadd).w
            move.b    (rtcdat).w,d1
            bfins     d1,d0{7:4}
            move.b    #9,(rtcadd).w
            move.b    (rtcdat).w,d1
            sub.b     #12,d1
            bfins     d1,d0{0:7}
            move      d2,sr
            move      sr,d2
            ori       #$700,sr
            move.w    d0,(systemDate).l
            swap      d0
            move.w    d0,(systemTime).l
            swap      d0
            move      d2,sr
            rts

readRTCTimeErr:
            moveq     #-1,d0
            rts

writeRTCTime:bsr       checkRTC
            bcs       readCurrentTime2
            move.l    4(sp),d0
            move.b    #$b,(rtcadd).w
            move.b    #$80,(rtcdat).w
            move.b    #$a,(rtcadd).w
            move.b    #$2a,(rtcdat).w
            move.b    #$b,(rtcadd).w
            move.b    #$8e,(rtcdat).w
            move.b    #0,(rtcadd).w
            bfextu    d0{27:5},d1
            add.b     d1,d1
            move.b    d1,(rtcdat).w
            move.b    #2,(rtcadd).w
            bfextu    d0{21:6},d1
            move.b    d1,(rtcdat).w
            move.b    #4,(rtcadd).w
            bfextu    d0{16:5},d1
            move.b    d1,(rtcdat).w
            move.b    #7,(rtcadd).w
            bfextu    d0{11:5},d1
            move.b    d1,(rtcdat).w
            move.b    #8,(rtcadd).w
            bfextu    d0{7:4},d1
            move.b    d1,(rtcdat).w
            move.b    #9,(rtcadd).w
            bfextu    d0{0:7},d1
            add.b     #$c,d1
            move.b    d1,(rtcdat).w
            move.b    #$b,(rtcadd).w
            move.b    #$e,(rtcdat).w
            rts

checkRTC:   movea.l   sp,a0
            movea.l   (busexception).w,a1
            move.l    #checkRTC2,(busexception).w
            move.b    #0,(rtcadd).w
            move.b    (rtcdat).w,d0
            move.l    a1,(busexception).w
            andi      #$fe,ccr
            rts
checkRTC2:  movea.l   a0,sp
            move.l    a1,(busexception).w
            ori       #1,ccr
            rts



_NVMaccess: moveq     #-5,d0
            move.w    4(sp),d1                  ; op = 0: read NVRAM
            beq.s     _NVMread
            cmp.w     #2,d1                     ; op = 2: initialize NVRAM and update checksum
            beq.s     _NVMinit
            bhi.s     _NVMaccessr               ; (unknown op)

* write into NVRAM and update the checksum
            bsr.s     _NVMvalid                 ; op = 1: write NVRAM
            tst.w     d0
            bne.s     _NVMaccessr
            movea.l   $a(sp),a0                 ; buffer
            bra.s     _NVMwrt2
_NVMwrtl:   move.b    d1,(a1)                   ; select register
            move.b    (a0)+,(a2)                ; write value
            addq.w    #1,d1                     ; next reg
_NVMwrt2:   dbra      d2,_NVMwrtl
            bsr       _NVMchksum                ; calculate the checksum
            move.b    #$3f,(a1)
            move.b    d0,(a2)                   ; checksum into reg 63
            not.b     d0
            move.b    #$3e,(a1)
            move.b    d0,(a2)                   ; inverted checksum into reg 62
            moveq     #0,d0                     ; (ok)
_NVMaccessr:rts

* read from NVRAM and also validate the checksum
_NVMread:   bsr.s     _NVMvalid
            cmp.w     #-5,d0                    ; argument error?
            beq.s     _NVMaccessr               ; (fail)
            movea.l   $a(sp),a0                 ; buffer
            bra.s     _NVMread2
_NVMreadl:  move.b    d1,(a1)                   ; select register
            move.b    (a2),(a0)+                ; read value
            addq.w    #1,d1                     ; next reg
_NVMread2:  dbra      d2,_NVMreadl
            rts

* erase NVRAM and update the checksum
_NVMinit:   lea       (rtcadd).w,a1
            lea       (rtcdat).w,a2
            moveq     #0,d0                     ; fill value = 0
            moveq     #14,d1
            moveq     #50-1,d2                  ; fill 48 bytes plus checksum
_NVMinitl:  move.b    d1,(a1)                   ; select register
            move.b    d0,(a2)                   ; clear
            addq.w    #1,d1                     ; next reg
            dbra      d2,_NVMinitl
            move.b    #$3e,(a1)                 ; inverted checksum of 0 is 0xFF
            move.b    #$ff,(a2)
            rts

* validate checksum in NVRAM and function parameters
_NVMvalid:  bsr.s     _NVMchksum                ; calculate the checksum
            move.b    d0,d1
            moveq     #-12,d0                   ; default: checksum error
            move.b    #$3f,(a1)                 ; register 63
            cmp.b     (a2),d1                   ; contains the checksum
            bne.s     _NVMvalidr                ; (error)
            not.b     d1
            move.b    #$3e,(a1)                 ; register 62
            cmp.b     (a2),d1                   ; contains the inverted checksum
            bne.s     _NVMvalidr                ; (error)
            moveq     #-5,d0                    ; default: argument error
            move.w    $a(sp),d1                 ; count
            cmp.w     #48,d1                    ; more than 48 bytes?
            bcc.s     _NVMvalidr                ; (error)
            move.w    $c(sp),d2                 ; start
            bmi.s     _NVMvalidr                ; negative => error
            add.w     d1,d2                     ; start + count
            cmp.w     #48,d2                    ; >48
            bhi.s     _NVMvalidr                ; (error)
            moveq     #0,d0                     ; no error
_NVMvalidr: move.w    $c(sp),d2                 ; d2: count
            move.w    $a(sp),d1
            add.w     #14,d1                    ; d1: first register
            rts

_NVMchksum: lea       (rtcadd).w,a1
            lea       (rtcdat).w,a2
            moveq     #0,d0                     ; checksum = 0
            moveq     #14,d1                    ; NVRAM starts at register 14
            moveq     #48-1,d2                  ; 48 bytes
_NVMcheckl: move.b    d1,(a1)                   ; select register
            add.b     (a2),d0                   ; sum values to checksum
            addq.w    #1,d1
            dbra      d2,_NVMcheckl
            rts

ELSE

readCurrentTime:bsr.s checkRTC
            bcs.s     readCurrentTime2
            bsr.s     readRTCTime
            cmp.l     #-1,d0
            beq.s     readCurrentTime2
            moveq     #0,d0
readCurrentTime2:rts

IF ROM_STBOOK
checkRTC:
            movea.w   #rtc_sec,a0
            move.b    #$00,$1d(a0)              ; Clear RTC Test register
            move.b    #$0c,$1f(a0)              ; Reset 1 Hz & 16 Hz alarm pulse
            move.b    #$08,$1b(a0)              ; Clock Start, Alarm off, Bank 0
            moveq     #0,d0                     ; The RTC is always available
            rts
ELSE
checkRTC:   movea.w   #rtc_sec,a0
            move.l    (busexception).w,d2
            movea.l   sp,a2
            move.l    #checkRTC2,(busexception).w
            bset      #0,$1b(a0)
            move.l    d2,(busexception).w
            move.w    #$a05,d0
            movep.w   d0,5(a0)
            movep.w   5(a0),d1
            and.w     #$f0f,d1
            cmp.w     d0,d1
            bne.s     checkRTC3
            move.b    #1,1(a0)
            bclr      #0,$1b(a0)
            move.b    #0,$1d(a0)
            rts
checkRTC2:  movea.l   a2,sp
            move.l    d2,(busexception).w
checkRTC3:  ori       #1,ccr                    ; RTC not available
            rts
ENDIF

readRTCTime:bsr.s     checkRTC
            bcs       readRTCTimeErr
            lea       (rtcbufa).w,a1
            lea       (rtcbufb).w,a2
            bsr       readRTCTimeMask
readRTCTime2:exg      a1,a2
            bsr       readRTCTimeMask
            moveq     #$c,d0
readRTCTime3:move.b   (a1,d0.w),d1
            cmp.b     (a2,d0.w),d1
            bne.s     readRTCTime2
            dbra      d0,readRTCTime3
            moveq     #0,d0
            move.b    $b(a1),d0
            mulu.w    #10,d0
            add.b     $c(a1),d0
            asr.w     #1,d0
            move.w    d0,d1
            moveq     #0,d0
            move.b    9(a1),d0
            mulu.w    #10,d0
            add.b     $a(a1),d0
            asl.w     #5,d0
            add.w     d0,d1
            moveq     #0,d0
            move.b    7(a1),d0
            mulu.w    #10,d0
            add.b     8(a1),d0
            asl.w     #8,d0
            asl.w     #3,d0
            add.w     d0,d1
            swap      d1
            moveq     #0,d0
            move.b    4(a1),d0
            mulu.w    #10,d0
            add.b     5(a1),d0
            move.w    d0,d1
            moveq     #0,d0
            move.b    2(a1),d0
            mulu.w    #10,d0
            add.b     3(a1),d0
            asl.w     #5,d0
            add.w     d0,d1
            moveq     #0,d0
            move.b    (a1),d0
            mulu.w    #10,d0
            add.b     1(a1),d0
            asl.w     #8,d0
            asl.w     #1,d0
            add.w     d0,d1
            move      sr,d2
            ori       #$700,sr
            move.w    d1,(systemDate).l
            swap      d1
            move.w    d1,(systemTime).l
            move      d2,sr
            move.l    d1,d0
            rts

readRTCTimeMask:moveq #$c,d0
            moveq     #1,d1
readRTCTimeMask2:move.b (a0,d1.w),d2
            and.b     #$f,d2
            move.b    d2,(a1,d0.w)
            addq.w    #2,d1
            dbra      d0,readRTCTimeMask2
            rts

readRTCTimeErr:moveq  #-1,d0
            rts

writeRTCTime:bsr      checkRTC
            bcs       no_RTC_found
            lea       (rtcbufa).w,a1
            movea.w   4(sp),a2
            bsr       calcRTCWeekday
            move.b    d0,6(a1)
            move.w    6(sp),d1
            move.w    d1,d0
            and.l     #$1f,d0
            add.w     d0,d0
            divu.w    #10,d0
            move.b    d0,$b(a1)
            swap      d0
            move.b    d0,$c(a1)
            move.w    d1,d0
            lsr.w     #5,d0
            and.l     #$3f,d0
            divu.w    #10,d0
            move.b    d0,9(a1)
            swap      d0
            move.b    d0,$a(a1)
            lsr.w     #8,d1
            lsr.w     #3,d1
            ext.l     d1
            divu.w    #10,d1
            move.b    d1,7(a1)
            swap      d1
            move.b    d1,8(a1)
            move.w    4(sp),d1
            move.w    d1,d0
            and.l     #$1f,d0
            divu.w    #10,d0
            move.b    d0,4(a1)
            swap      d0
            move.b    d0,5(a1)
            move.w    d1,d0
            lsr.w     #5,d0
            and.l     #$f,d0
            divu.w    #10,d0
            move.b    d0,2(a1)
            swap      d0
            move.b    d0,3(a1)
            lsr.w     #1,d1
            lsr.w     #8,d1
            ext.l     d1
            move.l    d1,d2
            divu.w    #10,d1
            move.b    d1,(a1)
            swap      d1
            move.b    d1,1(a1)
            divu.w    #4,d2
            swap      d2
            move.b    #$e,$1f(a0)
            bset      #0,$1b(a0)
            move.b    #1,$15(a0)
            move.b    d2,$17(a0)
            bclr      #0,$1b(a0)
            bclr      #3,$1b(a0)
            moveq     #$c,d0
            moveq     #1,d1
writeRTCTime2:move.b  (a1,d0.w),(a0,d1.w)
            addq.w    #2,d1
            dbra      d0,writeRTCTime2
            bset      #3,$1b(a0)
            moveq     #0,d0
            rts

no_RTC_found:moveq    #-1,d0
            rts

calcRTCWeekday:moveq  #2,d2
            move.w    a2,d0
            lsr.w     #8,d0
            lsr.w     #1,d0
            add.w     d0,d2
            move.w    d0,d1
            lsr.w     #2,d1
            add.w     d1,d2
            move.w    a2,d1
            lsr.w     #5,d1
            and.w     #$f,d1
            and.w     #3,d0
            bne.s     calcRTCWeekday2
            cmp.w     #2,d1
            bhi.s     calcRTCWeekday2
            subq.w    #1,d2
calcRTCWeekday2:subq.w #1,d1
            add.w     d1,d1
            add.w     daysOffsetToMonths(pc,d1.w),d2
            move.w    a2,d1
            and.w     #$1f,d1
            add.w     d1,d2
            divu.w    #7,d2
            swap      d2
            moveq     #0,d0
            move.w    d2,d0
            rts

daysOffsetToMonths:DC.W $0000,$001f,$003b,$005a
            DC.W      $0078,$0097,$00b5,$00d4
            DC.W      $00f3,$0111,$0130,$014e

_Waketime:  move.l    4(sp),d0
            beq       clearRTCWaketime
            cmp.l     #-1,d0
            beq       readRTCWaketime
            cmp.l     #1,d0
            beq       enableRTCWaketime
            bsr       readRTCTime
            cmp.l     #-1,d0
            beq       enableRTCWaketimeDone
            move.l    4(sp),d1
            and.b     #$e0,d0
            and.b     #$e0,d1
            cmp.l     d1,d0                     ; is the new wakeup time already in the past?
            bcc.s     _Waketime_ret1
            move.l    (waketimeAlarm).l,d2
            beq.s     _Waketime2
            cmp.l     d2,d0
            bhi.s     _Waketime2
            cmp.l     d2,d1
            bcc.s     _Waketime_ret2
_Waketime2: movea.l   d0,a0
            move.l    d1,d2
            and.l     #$1fffff,d2
            and.l     #$1fffff,d0
            cmp.l     d2,d0                     ; new wakeup time is _now_?
            beq.s     _Waketime3
            move.l    d1,(waketimeAlarm).l
            moveq     #0,d0
            rts

_Waketime3: and.l     #$1f0000,d2
            cmp.l     #$10000,d2
            beq.s     _Waketime_ret5
            and.l     #$ffe0ffff,d1
            or.l      #$10000,d1
            move.l    d1,(waketimeAlarm).l
            moveq     #4,d0
            rts

_Waketime_ret5:move.l a0,d0
            add.l     #$10000,d0
            move.l    d0,(waketimeAlarm).l
            moveq     #5,d0
            rts

_Waketime_ret1:moveq  #1,d0
            rts

_Waketime_ret2:moveq  #2,d0
            rts

readRTCWaketime:move.l (waketimeAlarm).l,d0
            rts

clearRTCWaketime:bsr  checkRTC
            bcs       no_RTC_found
            bclr      #2,$1b(a0)                ; RTC Alarm off
            move.b    #$d,$1f(a0)               ; Reset 1Hz, 16 Hz and Alarm
            clr.l     (waketimeAlarm).l
            moveq     #3,d0
            rts

enableRTCWaketime:bsr readRTCTime
            cmp.l     #-1,d0
            beq       enableRTCWaketimeDone
            move.l    (waketimeAlarm).l,d1
            beq       enableRTCWaketimeDone
            and.b     #$e0,d1
            move.l    d1,d2
            and.b     #$e0,d0
            cmp.l     d1,d0                     ; is the new wakeup time already in the past?
            bhi.s     enableRTCWaketimeDone
            bne.s     enableRTCWaketime2
            and.b     #$1f,d2
            cmp.b     #7,d2
            bcs.s     enableRTCWaketime3
enableRTCWaketime2:movea.w #rtc_sec,a0
            bset      #0,$1b(a0)                ; RTC select Bank 1
            move.l    (waketimeAlarm).l,d1
            move.w    d1,d0
            lsr.w     #5,d0                     ; Minutes
            and.l     #$3f,d0
            divu.w    #10,d0
            move.b    d0,7(a0)                  ; RTC 1-minute alarm register
            swap      d0
            move.b    d0,5(a0)                  ; RTC 10-minute alarm register
            move.w    d1,d0
            lsr.w     #8,d0
            lsr.w     #3,d0                     ; Hours
            ext.l     d0
            divu.w    #10,d0
            move.b    d0,$b(a0)                 ; RTC 1-hour alarm register
            swap      d0
            move.b    d0,9(a0)                  ; RTC 10-hour alarm register
            move.l    d1,d0
            swap      d0
            and.w     #$1f,d0                   ; Days
            ext.l     d0
            divu.w    #10,d0
            move.b    d0,$11(a0)                ; RTC 1-hour alarm register
            swap      d0
            move.b    d0,$f(a0)                 ; RTC 10-day alarm register
            movea.w   (waketimeAlarm).l,a2
            bsr       calcRTCWeekday
            move.b    d0,$d(a0)                 ; RTC day-of-the-week alarm register
            bset      #2,$1b(a0)                ; RTC Alarm on
            bclr      #0,$1b(a0)                ; RTC select Bank 0
enableRTCWaketimeDone:moveq #0,d0
            rts

enableRTCWaketime3:clr.l (waketimeAlarm).l
            moveq     #-1,d0
            rts
ENDIF

**************************************************************************
*                                                                        *
*                cp/m-68k atari rbp bios                                 *
*                basic input/output subsystem                            *
*                copyright 1984, atari corporation                       *
*                all rights reserved.                                    *
*                atari confidential                                      *
*                                                                        *
**************************************************************************
**************************************************************************
*                                                                        *
*        convert ikbd real-time clock format to jdos format              *
*                                                                        *
**************************************************************************
jdostime:   lea       (clkrec).w,a0
            bsr       bcdbin
            subi.b    #80,d0                    ; adjust so that 1980 => 0 for time base
            move.b    d0,d2
            asl.l     #4,d2

            bsr       bcdbin
            add.b     d0,d2
            asl.l     #5,d2

            bsr       bcdbin
            add.b     d0,d2
            asl.l     #5,d2

            bsr       bcdbin
            add.b     d0,d2
            asl.l     #6,d2

            bsr       bcdbin
            add.b     d0,d2
            asl.l     #5,d2

            bsr       bcdbin

            lsr.b     #1,d0                     ; adjust to provide two second increments...
            add.b     d0,d2                     ; ...another @!#%@#$% kludge, thank you !
            move.l    d2,(datetime).w
            move.b    #0,(newtod).w             ; clear handshaking flag
            rts


readIKBDTime:move.b   #$ff,(newtod).w
            move.b    #$1c,d1
            bsr       ikbdput
            movea.l   (_hz_200).w,a0
            adda.w    #200,a0
            moveq     #0,d0
readIKBDTime2:cmpa.l  (_hz_200).w,a0
            bcs.s     readIKBDTimex
            tst.b     (newtod).w
            bne.s     readIKBDTime2
            move.l    (datetime).w,d0
readIKBDTimex:rts

writeIKBDTime:move.l  4(sp),(newtime).w
            lea       (kmbuf).l,a0
            move.l    (newtime).w,d2
            move.b    d2,d0
            andi.b    #$1f,d0
            asl.b     #1,d0
            bsr.s     binbcd
            lsr.l     #5,d2
            move.b    d2,d0
            andi.b    #$3f,d0
            bsr.s     binbcd
            lsr.l     #6,d2
            move.b    d2,d0
            andi.b    #$1f,d0
            bsr.s     binbcd
            lsr.l     #5,d2
            move.b    d2,d0
            andi.b    #$1f,d0
            bsr.s     binbcd
            lsr.l     #5,d2
            move.b    d2,d0
            andi.b    #$f,d0
            bsr.s     binbcd
            lsr.l     #4,d2
            move.b    d2,d0
            andi.b    #$7f,d0
            bsr.s     binbcd
            addi.b    #$80,(a0)
            move.b    #$1b,d1
            bsr       ikbdput
            moveq     #5,d3
            lea       (oclkrec).l,a2
            bsr       ikbdstr
            move.b    #$1c,d1
            bsr       ikbdput
            rts

**************************************************************************
*                                                                        *
*                  convert a byte from binary to bcd format              *
*                                                                        *
*       entry:     d0.l  - value                                         *
*                                                                        *
**************************************************************************
binbcd:     moveq     #0,d1
            move.b    d0,d1
            divs.w    #10,d1
            asl.w     #4,d1
            move.w    d1,d0
            swap      d1
            add.w     d1,d0
            move.b    d0,-(a0)                  ; transfer to output clock buffer
            rts

**************************************************************************
*                                                                        *
*                  convert a byte from bcd format to binary              *
*                                                                        *
*       entry:     a0.l  - pointer to byte                               *
*                                                                        *
**************************************************************************
bcdbin:     move.b    (a0)+,d0                  ; get bcd byte
            move.b    d0,d1
            and.w     #$0f,d0                   ; dump high nibble
            and.w     #$f0,d1                   ; isolate high nibble
            asr.w     #4,d1                     ; dump low nibble
            mulu.w    #10,d1                    ; high nibble * 10
            add.w     d1,d0                     ; + low nibble
            rts

**************************************************************************
*                                                                        *
*                  midi output status                                    *
*                                                                        *
*       entry:                                                           *
*                                                                        *
*       word       midiost()                                             *
*                                                                        *
*       returns true/okay to send = -1,  false/not ready = 0             *
*                                                                        *
**************************************************************************
midiost:    moveq     #-1,d0                    ; pre-set to true
            move.b    (midictl).w,d2            ; grab midi status
            btst      #1,d2
            bne.s     midiox                    ; status okay to send
            moveq     #0,d0                     ; status not okay
midiox:     rts

**************************************************************************
*                                                                        *
*                  write char to midi port                               *
*                                                                        *
*       entry:                                                           *
*                                                                        *
*       void       midiwc(chr)                                           *
*       word       chr                                                   *
*                                                                        *
**************************************************************************
midiwc:     move.w    6(sp),d1
midiput:    lea       (midictl).w,a1            ; point to midi register base
midput1:    move.b    (a1),d2                   ; grab midi status
            btst      #1,d2
            beq.s     midput1
            move.b    d1,2(a1)
            rts                                 ; done for now

**************************************************************************
*                                                                        *
*                  put string to midi routine                            *
*                                                                        *
*       entry:                                                           *
*                                                                        *
*       void       midiws(size,ptr)                                      *
*       word       size                                                  *
*       long       ptr                                                   *
*                                                                        *
**************************************************************************
midiws:     moveq     #0,d3
            move.w    4(sp),d3                  ; get size of string buffer - 1
            movea.l   6(sp),a2                  ; get string address
midp1:      move.b    (a2)+,d1
            bsr.s     midiput
            dbra      d3,midp1
            rts

**************************************************************************
*                                                                        *
*                  get midi receiver buffer status                       *
*                                                                        *
*       entry:                                                           *
*                                                                        *
*       word       midistat()                                            *
*                                                                        *
*       -1 signifies true/okay  0 - signifies false/no characters        *
*                                                                        *
**************************************************************************
midstat:    lea       (mbufrec).w,a0            ; point to midi i/o bufrec
            lea       (midictl).w,a1            ; point to midi register base
            moveq     #-1,d0                    ; set result to true
            lea       6(a0),a2
            lea       8(a0),a3
            cmpm.w    (a3)+,(a2)+               ; atomic buffer empty test
            bne.s     midist1                   ; branch of not, assume d0 is "clr.w"'ed
            moveq     #0,d0                     ; set result to false
midist1:    rts

**************************************************************************
*                                                                        *
*                  getchar routine for midi port                         *
*                                                                        *
*       this routine transfers characters from a input queue that is     *
*       filled by an automatic interrupt routine.  the interrupt         *
*       routine handles the actual transfer of the character from the    *
*       i/o port.                                                        *
*                                                                        *
*       entry:                                                           *
*                                                                        *
*       long       midiin()                                              *
*                                                                        *
*       long data returned represents upper three bytes of time stramp   *
*       and least significant byte as data                               *
*                                                                        *
**************************************************************************
* assume that a0/a1 are inited by the midstat call for the rest of
* this routine.

midin:      bsr.s     midstat                   ; see if key pressed
            tst.w     d0
            beq.s     midin                     ; wait until byte comes in
            move      sr,-(sp)                  ; protect this upcoming test
            ori       #$700,sr
            move.w    6(a0),d1                  ; get current head pointer offset from buffer
            cmp.w     8(a0),d1                  ; head=tail?
            beq.s     mwi2                      ; yes

* check for wrap of pointer

            addq.w    #1,d1                     ; i=j+1
            cmp.w     4(a0),d1                  ; ? i>= current bufsiz?
            bcs.s     mwi1                      ; no...
            moveq     #0,d1                     ; wrap pointer
mwi1:       movea.l   (a0),a1                   ; get base address of buffer
            and.l     #$ffff,d1
IF !ROM_TOS306
            moveq     #0,d0
ENDIF
            move.b    (a1,d1.l),d0              ; get character
            move.w    d1,6(a0)                  ; store new head pointer to buffer record
mwi2:       move      (sp)+,sr
            rts

**************************************************************************
*                                                                        *
*                  parallel i/o port service routine                     *
*                                                                        *
*       this set of routines is for general parallel i/o                 *
*                                                                        *
*       entry to listout                                                 *
*                                                                        *
*       entry to listin                                                  *
*                                                                        *
*       exit from listin                                                 *
*                                                                        *
**************************************************************************
_lstout:    btst      #4,(pconfig).w
            bne       _auxout

            move.l    (_hz_200).w,d2            ; d2 = hz_200 - prt_to
            sub.l     (prt_to).w,d2             ; (compute time since last timeout)
            cmpi.l    #5*200,d2                 ; do "fake" timeout if we timed out within
            bcs.s     lperr                     ; the last five seconds
            move.l    (_hz_200).w,d2            ; d2 = starting time for this char
pt0:        bsr       _lstostat                 ; go get parallel port status
            tst.w     d0                        ; ...and check for high (busy)
            bne.s     pt1                       ; port is ready -- print the char

            move.l    (_hz_200).w,d3            ; d3 = hz_200 - d2
            sub.l     d2,d3
            cmpi.l    #$1770,d3                 ; check for 30 second delta
            blt.s     pt0                       ; continue of no timeout

lperr:      moveq     #0,d0                     ; return value of 0 indicates timeout
            move.l    (_hz_200).w,(prt_to).w    ; record time of last timeout
            rts

pt1:        move      sr,d3                     ; save status register
            ori       #$700,sr                  ; protect upcoming switching of the port setting
            moveq     #7,d1                     ; get current io enable register contents
            bsr       gientry
            ori.b     #$80,d0                   ; set port b for output
            move.b    #$87,d1                   ; set to write to io enable
            bsr       gientry
            move      d3,sr                     ; restore status register

            move.w    6(sp),d0                  ; retrieve byte to be sent and...
            move.b    #$8f,d1                   ; write out byte to parallel port
            bsr       gientry

            move      sr,-(sp)
            ori       #$700,sr
            bsr.s     strobeon
            bsr.s     strobeon
            bsr.s     strobeoff
            move      (sp)+,sr
            moveq     #-1,d0                    ; set d0=-1 for good transfer status
            rts


strobeoff:  moveq     #$20,d2                   ; set strobe off
            bra       onbit                     ; go set it!!
strobeon:   move.b    #$df,d2                   ; set strobe on
            bra       offbit                    ; set strobe now...


_lstin:     moveq     #7,d1                     ; get current io enable register contents
            bsr       gientry
            andi.b    #$7f,d0                   ; set port b for input
            move.b    #$87,d1                   ; set to write to io enable
            bsr       gientry

            bsr.s     strobeoff                 ; busy off!
lstibusy:   bsr.s     _lstostat                 ; go get parallel port status
            tst.w     d0                        ; ...and check for high (busy)
            bne.s     lstibusy                  ; loop till high...
            bsr.s     strobeon
            moveq     #$f,d1                    ; init to use gientry routine to read
            bra       gientry                   ; now get the byte from the parallel port
* d0.l contains the byte of data from the port
* the 'bra' is implied rts from this routine

**************************************************************************
*                                                                        *
*                  parallel port status routine                          *
*                                                                        *
**************************************************************************
_lstostat:  lea       (gpip).w,a0               ; point to mfp register base
            moveq     #-1,d0                    ; pre-init to true (parallel port ready)
            btst      #0,(a0)
            beq.s     lst1
            moveq     #0,d0                     ; parallel port busy
lst1:       rts


**************************************************************************
*                                                                        *
*                  auxillary port input status routine                   *
*                                                                        *
**************************************************************************
auxistat:   lea       (rbufrec).w,a0            ; point to rs-232 buffer record
_auxistat:  moveq     #-1,d0                    ; set result to true
            lea       6(a0),a1                  ; head
            lea       8(a0),a0                  ; tail
            cmpm.w    (a0)+,(a1)+               ; atomic buffer empty test
            bne.s     auxist1
            moveq     #0,d0                     ; set result to false
auxist1:    rts

**************************************************************************
*                                                                        *
*                  auxillary input routine                               *
*                                                                        *
**************************************************************************
auxin:      lea       (rbufrec).w,a0            ; point to rs-232 buffer record
            lea       (gpip).w,a2
_auxin:     bsr       rs232get
            move.w    d0,-(sp)
            tst.b     $20(a0)                   ; flow control active?
            beq.s     auxinrts                  ; (no)
            move.w    8(a0),d0                  ; tail
            sub.w     6(a0),d0                  ; - head
            bpl.s     auxin2                    ; underflow?
            add.w     4(a0),d0                  ; + size
auxin2:     cmp.w     $a(a0),d0
            bgt.s     auxinrts
            tst.b     $1e(a0)                   ; high-water flag already set?
            beq.s     auxinrts                  ; no...exit...
            bsr.s     auxinclrhw
auxinrts:   move.w    (sp)+,d0
            rts

auxinclrhw: clr.b     $1e(a0)                   ; clear high-water flag
            btst      #0,$20(a0)                ; is the rs232 mode xon/xoff?
            bne.s     auxinclrhw2               ; (yes)
            bra       rtson
auxinclrhw2:move.b    #$11,$21(a0)              ; "xon"
            bra.s     _auxoutwr

**************************************************************************
*                                                                        *
*                  auxillary port output status routine                  *
*                                                                        *
**************************************************************************
_auxostat:  lea       (rbufrec+14).w,a0         ; point to rs-232 buffer record
__auxostat: move.w    8(a0),d1                  ; tail
            bsr       wrapin
            moveq     #-1,d0                    ; set result to true
            cmp.w     6(a0),d1                  ; head
            bne.s     _auxostatrts
            moveq     #0,d0                     ; set result to false
_auxostatrts:rts

**************************************************************************
*                                                                        *
*                  auxillary output routine                              *
*                                                                        *
**************************************************************************
_auxout:    move.w    6(sp),d0                  ; get data
            lea       (rbufrec+14).w,a0
            bsr       rs232put                  ; exit via rs-232 output routine
            lea       (rbufrec).w,a0
            lea       (gpip).w,a2

_auxoutwr:  tst.b     $2c(a2)                   ; transmitter status
            bpl.s     _auxoutrts                ; not ready
            move      sr,-(sp)
            ori       #$700,sr
            bsr       wr_rs232
            move      (sp)+,sr
_auxoutrts: rts

**************************************************************************
*                                                                        *
*               ikbd output status                                       *
*                                                                        *
*       entry:                                                           *
*                                                                        *
*       word    ikbdost()                                                *
*                                                                        *
*       returns true/okay to send = 1,  false/not ready = 0              *
*                                                                        *
**************************************************************************
ikbdost:    moveq     #-1,d0                    ; pre-set to true
            move.b    (keyctl).w,d2             ; grab ikbd status
            btst      #1,d2
            bne.s     ikbdox                    ; status okay to send
            moveq     #0,d0                     ; status not okay
ikbdox:     rts

**************************************************************************
*                                                                        *
*               write char to ikbd port                                  *
*                                                                        *
*       entry:                                                           *
*                                                                        *
*       void    ikbdwc(chr)                                              *
*       word    chr                                                      *
*                                                                        *
**************************************************************************
ikbdwc:     move.w    6(sp),d1
ikbdput:    lea       (keyctl).w,a1             ; point to ikbd register base
ikput1:     move.b    (a1),d2                   ; grab keyboard status
            btst      #1,d2
            beq.s     ikput1
IF ROM_TOS306
            move.w    #$400,d0
            bsr       mfpdelay
ELSE
            lea       (tcdr).w,a0
            move.w    #$bf,d0
ikput2:     move.b    (a0),d2
ikput3:     cmp.b     (a0),d2
            beq.s     ikput3
            dbra      d0,ikput2
ENDIF
            move.b    d1,2(a1)                  ; write char to the ikbd port
            rts                                 ; done for now

**************************************************************************
*                                                                        *
*               put string to to ikbd routine                            *
*                                                                        *
*       entry:                                                           *
*                                                                        *
*       void    ikbdws(size,ptr)                                         *
*       word    size                                                     *
*       long    ptr                                                      *
*                                                                        *
**************************************************************************
ikbdws:
IF ROM_TOS306
            moveq     #0,d3
ENDIF
            move.w    4(sp),d3
            movea.l   6(sp),a2
ikbdstr:    move.b    (a2)+,d1
            bsr.s     ikbdput
            dbra      d3,ikbdstr
            rts


constat:    lea       (kbufrec).w,a0            ; point to ikbd buffer record
            moveq     #-1,d0                    ; set result to true
            lea       6(a0),a2                  ; head
            lea       8(a0),a3                  ; tail
            cmpm.w    (a3)+,(a2)+               ; atomic buffer empty test
            bne.s     const1                    ; branch of not, assume d0 is "clr.w"'ed
            moveq     #0,d0                     ; set result to false
const1:     rts


conin:      bsr.s     constat                   ; see if key pressed
            tst.w     d0
            beq.s     conin                     ; wait until key pressed
            move      sr,-(sp)                  ; protect this upcoming test
            ori       #$700,sr
            move.w    6(a0),d1                  ; get current head pointer offset
            cmp.w     8(a0),d1                  ; head=tail?
            beq.s     cwi2                      ; yes

* check for wrap of pointer

            addq.w    #4,d1                     ; i=h+4
            cmp.w     4(a0),d1
            bcs.s     cwi1                      ; no...
            moveq     #0,d1                     ; wrap pointer
cwi1:       movea.l   (a0),a1                   ; get base address of buffer
            and.l     #$ffff,d1
            move.l    (a1,d1.l),d0              ; get character
            move.w    d1,6(a0)                  ; store new head pointer to buffer
cwi2:       move      (sp)+,sr
            rts


conoutst:   moveq     #-1,d0
            rts                                 ; jdos requirement


***********************************************************************************
*                                                                                 *
*                routine to set up the general interrupt port registers           *
*                        (gpip,are,ddr)                                           *
*                                                                                 *
*                algorithm to set up the port                                     *
*                                                                                 *
*                1.  mask off all interrupts via the imrx register,               *
*                2.  clear all enable and pending bits in the ierx and iprx       *
*                         registers;                                              *
*                3.  check the inerrupt in-service registers and loop till        *
*                         clear;                                                  *
*                4.  init the aer register bits as desired (default = 11111111);  *
*                5.  init the ddr register bits as desired (default = 10000000);  *
*                6.  clear the gpip register;                                     *
*                7.  enable all desired interrupt enable bits;                    *
*                8.  mask on all desired interrupt mask bits;                     *
*                                                                                 *
***********************************************************************************
initmfp:    lea       (gpip).w,a0               ; init mfp address pointer
            moveq     #0,d0                     ; init to zero for clearing mfp
            movep.l   d0,0(a0)                  ; clear gpip thru iera
            movep.l   d0,8(a0)                  ; clear ierb thru isrb
            movep.l   d0,$10(a0)                ; clear isrb thru vr

            move.b    #$48,$16(a0)              ; set mfp autovector to $100 and s-bit
            bset      #2,2(a0)                  ; set cts to low to high transition

IF ROM_TOS306
            lea       (gpip_TT).w,a0            ; init mfp #2 address pointer
            moveq     #0,d0                     ; init to zero for clearing mfp
            movep.l   d0,0(a0)                  ; clear gpip thru iera
            movep.l   d0,8(a0)                  ; clear ierb thru isrb
            movep.l   d0,$10(a0)                ; clear isrb thru vr
            move.b    #$58,$16(a0)              ; set mfp autovector to $140 and s-bit
ENDIF

            clr.b     (privexception).w

* init the "c" timer
            move.w    #$1111,(tc_rot).w         ; setup bitstream for /4 on timer c interrupts
            move.w    #$14,(_timr_ms).w         ; set timer calibration value

            moveq     #2,d0                     ; set to timer C
            moveq     #$50,d1                   ; set to /64 for 200 hz tick
            move.w    #$c0,d2                   ; set to 192
            bsr       settimer                  ; setup timer and init interrupt vector.....

            lea       (timercint).l,a2          ; point to the timer C interrupt routine...
            moveq     #5,d0                     ; point the the timer C interrupt number
            bsr       initint

* init the "d" timer
            moveq     #3,d0                     ; select the d timer
            moveq     #1,d1                     ; init for /4 for 9600 baud
            moveq     #2,d2                     ; init for 9600 baud
            bsr       settimer                  ; branch to out timer initialier...

            move.b    #1,(rbufrec+34).w

* now init the 3 rs232 chip registers
            lea       (gpip).w,a0
            move.l    #$880105,d0
            movep.l   d0,$26(a0)                ; inits scr,ucr,rsr,tsr

IF ROM_TOS306
* now init the 3 rs232 chip registers in mfp #2
            lea       (gpip_TT).w,a0
            move.l    #$880105,d0
            movep.l   d0,$26(a0)                ; inits scr,ucr,rsr,tsr
            move.b    #1,(tcdcr_TT).w           ; timer c+d control register: delay 1:4
            move.b    #2,(tddr_TT).w            ; timer d data register
            move.b    #1,($1380).w              ; ???
ENDIF

* check for SCC chip
            tst.b     (STEFlag).l
            bne.s     initmfp2
            bsr       initscc

* initialize the default rs-232 control line settings
initmfp2:   bsr       dtron
            bsr       rtson

* initialize the rs-232 buffer record structure
            lea       (rbufrec).w,a0
            lea       (rs232init).l,a1
            moveq     #$23,d0
            bsr       lbmove                    ; do block move and return

IF ROM_TOS306
            lea       (ttmfp_rbufrec).w,a0
            lea       (ttrs232init).l,a1
            moveq     #$23,d0
            bsr       lbmove

*           ttrs232init:DC.L      iobuf_ttrs232i            ; ibufptr
*                       DC.W      $0100                     ; ibufsiz
*                       DC.W      $0000                     ; ibufhead
*                       DC.W      $0000                     ; ibuftail
*                       DC.W      $0080                     ; ibuflow
*                       DC.W      $00c0                     ; ibufhigh
*                       DC.L      iobuf_ttrs232o            ; obufptr
*                       DC.W      $0100                     ; obufsiz
*                       DC.W      $0000                     ; obufhead
*                       DC.W      $0000                     ; obuftail
*                       DC.W      $0080                     ; obuflow
*                       DC.W      $00c0                     ; obufhigh
*                       DC.B      $00                       ; rsrbyte
*                       DC.B      $00                       ; tsrbyte
*                       DC.B      $00                       ; rxoff
*                       DC.B      $00                       ; txoff
*                       DC.B      $01                       ; rsmode_xon
*                       DC.B      $00                       ; rsmode_filler
*                       DC.W      $01ff

ENDIF

* initialize the midi buffer record structure
            lea       (mbufrec).w,a0
            lea       (minit).l,a1
            moveq     #$d,d0
            bsr       lbmove                    ; do block move and return

            move.w    #-1,(altdigits).l         ; reset alt-numpad ascii entering
            move.l    #aciaexit,d0              ; init ikbd and midi error handler address
            move.l    d0,(vkbderr).w            ; init keyboard error handler address
            move.l    d0,(vmiderr).w            ; init midi error handler address
            move.l    #sysmidi,(midivec).w      ; point to system midi interrupt vector
            move.l    #astatusmidi,(astatusmidip).w
            move.l    #astatuskeyboard,(astatuskeyp).w
            move.l    #itsakey,(itsakeyp).w

* init the midi acia next
            move.b    #$03,(midictl).w          ; init the midi acia via master reset

* init the acia to divide by 16x clock, 8 bit data, 1 stop bit, no parity,
* rts low, transmitting interrupt disabled, receiving interrupt enabled
            move.b    #$95,(midictl).w

* initialize the keyboard acia interrupt vector exception address
            move.b    #7,(conterm).w            ; enabled keyclick, repeat key, bell functions

            move.l    #jdostime,(clkintvec).w
            move.l    #genrts,d0                ; generalized rts for ikbd subsystems
            move.l    d0,(statintvec).w
            move.l    d0,(msintvec).w           ; init user mouse interrupt adr to rts
            move.l    d0,(joyintvec).w
            bsr       initdevstables
*
* Sound routine initialization - uses the pre-init'ed d0.l=0000 !!
*
            moveq     #0,d0                     ; init 'd0' to clear sound variables
            move.l    d0,(cursnd).w             ; clear sound ptr
            move.b    d0,(timer).w              ; clear delay timer
            move.b    d0,(auxd).w               ; clear temp value
            move.l    d0,(prt_to).w             ; init printer timeout to 0

            bsr       strobeoff                 ; init strobe to off (line high!)
            move.b    #15,(cdelay1).w           ; init system default key repeat values
            move.b    #2,(cdelay2).w

* within the mouse relative routine
* initialize the ikbd buffer record structure
            lea       (kbufrec).w,a0
            lea       (kinit).l,a1
            moveq     #$d,d0
            bsr.s     lbmove                    ; do block move and return

            bsr       bioskeys                  ; point key translation address to
* the rom based translation tables

* init the acia next
            move.b    #$03,(keyctl).w           ; init the acia via master Reset

* now that the vector is initialized, we can allow interrupts to occur!
* init the acia do divice by 64 clock, 8 bit data, 1 stop bit, no parity,
* rts low, transmitting interrupt disabled, receiving interrupt enabled

            move.b    #$96,(keyctl).w

            movea.l   #mfpvectr,a3              ; point to initializing array of exception vec's
            moveq     #3,d1                     ; init branch counter/index
st1:        move.l    d1,d2
            move.l    d1,d0                     ; load in interrupt # to setup
            addi.b    #9,d0                     ; add constand to point to proper mfp interrupt
            asl.l     #2,d2
            movea.l   (a3,d2.w),a2
            bsr       initint                   ; go to service routine
            dbra      d1,st1
            lea       (midikey).l,a2
            moveq     #6,d0                     ; load in interrupt # to setup
            bsr       initint                   ; go to service routine

* initializing code which sets the enable
* and mask bits...
            lea       (ctsint).l,a2             ; point to the CTS interrupt routine...
            moveq     #2,d0                     ; point to the CTS interrrupt number
            bsr       initint

IF ROM_TOS306
            movea.l   #$e36704,a3               ; ttmfp_txerr,ttmfp_txrint,ttmfp_rxerr,ttmfp_rcvrint
            movea.w   #mfpTT_SendError,a0       ; $0164
            move.l    (a3)+,(a0)+               ; mfpTT_SendError = ttmfp_txer
            move.l    (a3)+,(a0)+               ; mfpTT_SendBufferEmpty = ttmfp_txrint
            move.l    (a3)+,(a0)+               ; mfpTT_ReceiveError = ttmfp_rxerr
            move.l    (a3)+,(a0)+               ; mfpTT_ReceiveBufferFull = ttmfp_rcvrint
            ori.b     #$1e,(iera_TT).w
            ori.b     #$1e,(imra_TT).w
ENDIF

genrts:     rts

lbmove:     move.b    (a1)+,(a0)+
            dbra      d0,lbmove
            rts

kinit:      DC.L      iobuf_keyb                ; ibufptr
            DC.W      $0100                     ; ibufsiz
            DC.W      $0000                     ; ibufhead
            DC.W      $0000                     ; ibuftail
            DC.W      $0040                     ; ibuflow
            DC.W      $00c0                     ; ibufhigh
minit:      DC.L      iobuf_midi                ; ibufptr
            DC.W      $0080                     ; ibufsiz
            DC.W      $0000                     ; ibufhead
            DC.W      $0000                     ; ibuftail
            DC.W      $0020                     ; ibuflow
            DC.W      $0060                     ; ibufhigh
rs232init:  DC.L      iobuf_rs232i              ; ibufptr
            DC.W      $0100                     ; ibufsiz
            DC.W      $0000                     ; ibufhead
            DC.W      $0000                     ; ibuftail
            DC.W      $0080                     ; ibuflow
            DC.W      $00c0                     ; ibufhigh
            DC.L      iobuf_rs232o              ; obufptr
            DC.W      $0100                     ; obufsiz
            DC.W      $0000                     ; obufhead
            DC.W      $0000                     ; obuftail
            DC.W      $0080                     ; obuflow
            DC.W      $00c0                     ; obufhigh
            DC.B      $00                       ; rsrbyte
            DC.B      $00                       ; tsrbyte
            DC.B      $00                       ; rxoff
            DC.B      $00                       ; txoff
            DC.B      $01                       ; rsmode_xon
            DC.B      $00                       ; rsmode_filler
            DC.W      $01ff

* array of exception vector addresses for the above interrupts, including
* dummy vectors that point to "rte's".

mfpvectr:   DC.L      txerror
            DC.L      txrint
            DC.L      rxerror
            DC.L      rcvrint

***********************************************************************************
*                                                                                 *
*                routine to set up a timer                                        *
*                                                                                 *
*                algorithm to init a timer                                        *
*                                                                                 *
*                1.  determine which timer and set d0.b = to timer's index value  *
*                    as shown below;                                              *
*                2.  disable the associated interrupt;                            *
*                3.  disable the timer itself via it's timer control register;    *
*                4.  initialize the timer's data register                         *
*                5.  repeat step #4 until the data register's contents are        *
*                    verified, per the errata sheet to the 68901 description;     *
*                6.  turn on the timer by using the value that you previously     *
*                    stored in d1;                                                *
*                                                                                 *
*                note:    the interrupt vector for the associated timer           *
*                         is not set in this routine, so it is the user's         *
*                         responsiblity to set it if so desired!                  *
*                                                                                 *
*                                                                                 *
*                registers used:          d0-d3/a0-a3                             *
*                registers saved:         d0-d3/a0-a3                             *
*                entry:                                                           *
*                        d0.l - timer to be set                                   *
*                                0 - timer a                                      *
*                                1 - timer b                                      *
*                                2 - timer c                                      *
*                                3 - timer d                                      *
*                        d1.b - timer's new control setting                       *
*                        d2.b - timer's data register data                        *
*                                                                                 *
*                exit:   no values to pass                                        *
*                                                                                 *
*                        d3   - used and abused by call to mskreg routine         *
*                        a0.l - set to mfp register base                          *
*                        a1.l - temporary location for a3                         *
*                        a2.l - used to pass table address to mskreg routine      *
*                        a3.l - used to pass table address to mskreg routine      *
*                                                                                 *
***********************************************************************************
settimer:   movem.l   d0-d4/a0-a3,-(sp)         ; save all registers to be messed with!!
            movea.w   #gpip,a0                  ; set mfp chip address pointer

            movea.l   #imrt,a3                  ; mask off the timer's interrupt maskable bit
            movea.l   #imrmt,a2
            bsr.s     mskreg

            movea.l   #iert,a3                  ; mask off the timer's interrupt enable bit
            movea.l   #imrmt,a2
            bsr.s     mskreg

            movea.l   #iprt,a3                  ; mask off the timer's interrupt pending bit
            movea.l   #imrmt,a2
            bsr.s     mskreg

            movea.l   #isrt,a3                  ; mask off the timer's interrupt inservice bit
            movea.l   #imrmt,a2
            bsr.s     mskreg

            movea.l   #tcrtab,a3                ; mask off the timer's control bits
            movea.l   #tcrmsk,a2
            bsr.s     mskreg

            exg       a3,a1                     ; save address pointer for restoring control

            lea       (tdrtab).l,a3             ; initialize the timer data register
            moveq     #0,d3                     ; to prevent false effective address generation
            move.b    (a3,d0.w),d3
verify:     move.b    d2,(a0,d3.w)
            cmp.b     (a0,d3.w),d2
            bne.s     verify

            exg       a3,a1                     ; grab that register address back
            or.b      d1,(a3)                   ; mask the timer control register value

            movem.l   (sp)+,d0-d4/a0-a3         ; restore all registers that were saved
            rts

***********************************************************************************
*                generalize mask register bit(s) routine                          *
*                                                                                 *
*       entry                                                                     *
*       static  d0 - contains the timer #                                         *
*               d3 - used and abused                                              *
*               d4 - used and abused                                              *
*       static  a0 - mfp register base                                            *
*               a3 - points to table of similar timer registers                   *
*       static  a2 - points to table of similar timer data registers              *
***********************************************************************************

mskreg:     bsr.s     getmask
            move.b    (a2),d3                   ; grab mask now
            and.b     d3,(a3)                   ; and have masked off the desired bit(s)
            rts

getmask:    moveq     #0,d3                     ; to prevent false effective address generation
            adda.w    d0,a3                     ; have got pointer to mfp register now
            move.b    (a3),d3                   ; now have the address offset to mfp
            add.l     a0,d3
            movea.l   d3,a3                     ; now have address pointing to desired mfp reg.
* now we get the mask to turn off interrupt
            adda.w    d0,a2                     ; have got pointer to mask now
            rts


iert:       DC.B      $06,$06,$08,$08
iprt:       DC.B      $0a,$0a,$0c,$0c
isrt:       DC.B      $0e,$0e,$10,$10
imrt:       DC.B      $12,$12,$14,$14
imrmt:      DC.B      $df,$fe,$df,$ef
tcrtab:     DC.B      $18,$1a,$1c,$1c
tcrmsk:     DC.B      $00,$00,$8f,$f8
tdrtab:     DC.B      $1e,$20,$22,$24

***********************************************************************************
*                initialize mfp interrupt via GEMDOS                              *
*                                                                                 *
*                entry                                                            *
*                                                                                 *
*                void    mfpint(numint,intvec)                                    *
*                word    numint                                                   *
*                long    intvec                                                   *
*                                                                                 *
***********************************************************************************
mfpint:     move.w    4(sp),d0
            movea.l   6(sp),a2
            andi.l    #$f,d0                    ; to ensure masking of 0-$f

***********************************************************************************
*                                                                                 *
*                routine to init an mfp associated interrupt vector               *
*                                                                                 *
*                algorithm                                                        *
*                                                                                 *
*                1.  block the interrupt via it's mask bit;                       *
*                2.  disable the interrupt's enable and pending bits;             *
*                3.  check the interrupt's in-service register and loop till      *
*                    clear;                                                       *
*                4.  init the interrupt's associated vector;                      *
*                5.  set the interrupt's enable bit;                              *
*                6.  set the interrupt's mask bit;                                *
*                                                                                 *
*                entry                                                            *
*                         d0 - contains interrupt # to aff                        *
*                         a2 - contains new vector address                        *
***********************************************************************************

initint:    movem.l   d0-d2/a0-a2,-(sp)         ; save affected registers
            bsr.s     disint                    ; disable the interrupts
            move.l    d0,d2                     ; get a copy so as to determine where to...
            asl.w     #2,d2                     ; place the a2 address into the int. vector
            addi.l    #$100,d2                  ; interrupt vector addr = (4 * int) + $000100
            movea.l   d2,a1                     ; transfer the calculated address to a register
            move.l    a2,(a1)                   ; ...that can act upon it thus<--vector init'ed
            bsr.s     enabint                   ; enable interrupts
            movem.l   (sp)+,d0-d2/a0-a2         ; restore affected registers
            rts

*************************************************************************
*                disable an mfp interrupt via GEMDOS                    *
*                                                                       *
*                entry                                                  *
*                                                                       *
*                void    jdisint(numint)                                *
*                word    numint                                         *
*                                                                       *
*************************************************************************

jdisint:    move.w    4(sp),d0
            andi.l    #$f,d0                    ; to ensure masking of 0-$f

*************************************************************************
*                interrupt disable routine                              *
*************************************************************************

disint:     movem.l   d0-d1/a0-a1,-(sp)         ; save affected registers
            lea       (gpip).w,a0               ; set mfp chip address pointer
            lea       $12(a0),a1                ; set a1 to the mskoff routine
            bsr.s     bselect                   ; generate the appropriate bit to clear
            bclr      d1,(a1)                   ; and clear the bit...
            lea       6(a0),a1                  ; set a1 for another mskoff call
            bsr.s     bselect
            bclr      d1,(a1)                   ; and clear the bit...
            lea       $e(a0),a1                 ; now set up to check for interrupts in progress
            bsr.s     bselect                   ; get proper a/b version...
            move.b    #$fe,d0
            rol.b     d1,d0
            move.b    d0,(a1)
            movem.l   (sp)+,d0-d1/a0-a1         ; restore affected registers
            rts

*************************************************************************
*                enable/re-enabled an mfp interrupt via GEMDOS          *
*                                                                       *
*                entry                                                  *
*                                                                       *
*                void    jenabint(numint)                               *
*                word    numint                                         *
*                                                                       *
*************************************************************************

jenabint:   move.w    4(sp),d0
            andi.l    #$f,d0                    ; to ensure masking of 0-$f

*************************************************************************
*                enable interrupt routine                               *
*************************************************************************

enabint:    movem.l   d0-d1/a0-a1,-(sp)         ; save affected registers
            lea       (gpip).w,a0               ; set mfp chip address pointer
            lea       6(a0),a1                  ; set up to enable the interrupt enable bit
            bsr.s     bselect
            bset      d1,(a1)                   ; and set the bit...
            lea       $12(a0),a1                ; set up to enable the interrupt mask bit
            bsr.s     bselect
            bset      d1,(a1)                   ; and set the bit...
            movem.l   (sp)+,d0-d1/a0-a1         ; restore affected registers
            rts


*************************************************************************
*                                                                       *
*       the following routine generates the appropriate bset/bclr #     *
*       for the interrupt # specified in d0.    valid interrupt #'s are *
*       0 --> 15 as shown in the 68901 chip specification.  It also     *
*       selects between the ixra and the ixrb version of the register   *
*       as is appropriate.                                              *
*                                                                       *
*       entry   d0 - contains the interrupt number                      *
*               a1 - contains the pointer to the "ixra" version of      *
*                    the interrupt byte to mask                         *
*       exit            d0 - same as upon entry                         *
*                       d1 - contains the number of the bit             *
*************************************************************************
bselect:    move.b    d0,d1                     ; copy d0 to d1 for scratch work
            cmpi.b    #8,d1                     ; see if desired int # >= 8...
            blt.s     skip0                     ; ...and branch if it ain't...
            subq.w    #8,d1                     ; adjust for using ixrb instead
            rts

skip0:      addq.l    #2,a1                     ; adjust for using ixrb instead
            rts


*************************************************************************
*                                                                       *
*               receiver buffer full interrupt routine                  *
*                                                                       *
*               grabs data from the rs-232 receiver port                *
*                                                                       *
*************************************************************************
rcvrint:    movem.l   d0-d1/a0-a2,-(sp)         ; save affected registers
            lea       (rbufrec).w,a0            ; point to current output buffer record
            lea       (gpip).w,a2               ; set mfp chip address pointer
_rcvrint:   move.b    $2a(a2),$1c(a0)           ; do the required rsr read before the udr read!
            move.b    $2e(a2),d0                ; get incoming data byte

            btst      #0,$20(a0)                ; is the rs232 mode xon/xoff?
            beq.s     ri3                       ; no... so process normally

            cmpi.b    #$13,d0                   ; is the data an "xoff" signal?
            bne.s     ri2                       ; no...now check for xon
            move.b    #$ff,$1f(a0)              ; set to halted transmission to host
            bra.s     ri8

ri2:        cmpi.b    #$11,d0                   ; is the data an "xon" signal?
            bne.s     ri3                       ; neither xon/xoff value, must be normal data...
            clr.b     $1f(a0)                   ; set to normal transmission status
            bra.s     ri7                       ; abnormal exit condition!!

ri3:        move.w    8(a0),d1                  ; get current tail pointer offset
            bsr       wrapin                    ; do wrap of input pointer if needed
            cmp.w     6(a0),d1                  ; head=tail?
            beq.s     ri8                       ; yes...exit...
            bsr       rs232put                  ; store data in ring buffer

* now check for highwater mark triggering of flow-control
            tst.b     $20(a0)                   ; flow control active?
            beq.s     ri8                       ; no...exit...
            move.w    8(a0),d0                  ; current tail pointer
            sub.w     6(a0),d0                  ; - current head
            bpl.s     ri4                       ; wrap around?
            add.w     4(a0),d0                  ; + size to correct the wrap around
ri4:        cmp.w     $c(a0),d0                 ; high-water mark not reached?
            blt.s     ri8                       ; correct...exit...
            tst.b     $1e(a0)                   ; high-water flag already set?
            bne.s     ri8                       ; yes...exit...
            move.b    #$ff,$1e(a0)              ; set high-water flag
            btst      #0,$20(a0)                ; are we using xon/xoff flow control?
            bne.s     ri6                       ; yes...
            bsr       rtsoff                    ; we're ready now for more data...yum! yum!
            bra.s     ri8
ri6:        move.b    #$13,$21(a0)              ; xoff
ri7:        tst.b     $2c(a2)                   ; transmitter status?
            bpl.s     ri8                       ; not ready
            bsr       wr_rs232
ri8:        move.b    #$ef,$e(a2)
            movem.l   (sp)+,d0-d1/a0-a2         ; restore affected registers
            rte

*************************************************************************
*                                                                       *
*               transmit buffer empty interrupt routine                 *
*                                                                       *
*************************************************************************
txrint:     movem.l   d0-d1/a0-a2,-(sp)         ; save affected registers
            lea       (gpip).w,a2               ; set mfp chip address pointer
            lea       (rbufrec).w,a0            ; point to current output buffer record
_txrint:    bsr       wr_rs232
            move.b    #$fb,$e(a2)               ; xmit buffer empty
            movem.l   (sp)+,d0-d1/a0-a2         ; restore affected registers
            rte

*************************************************************************
*                                                                       *
*               Clear-To-Send interrupt routine                         *
*                                                                       *
*************************************************************************
ctsint:     movem.l   d0-d1/a0-a2,-(sp)         ; save affected registers
            lea       (rbufrec).w,a0            ; point to current output buffer record
            lea       (gpip).w,a2               ; set mfp chip address pointer

            btst      #1,$20(a0)                ; are we using CTS/RTS flow control?
            beq.s     ctsexit                   ; no...
            btst      #2,(a2)                   ; CTS already active?
            bne.s     ctsoff                    ; yes...
            clr.b     $1f(a0)                   ; xon_flag = 0
            bset      #2,2(a2)                  ; CTS set
            tst.b     $2c(a2)                   ; transmitter status?
            bpl.s     ctsexit                   ; not empty...
            bsr.s     wr_rs232                  ; send a byte
            bra.s     ctsexit
ctsoff:     move.b    #$ff,$1f(a0)              ; xon_flag = -1
            bclr      #2,2(a2)
ctsexit:    move.b    #$fb,$10(a2)              ; clear CTS
            movem.l   (sp)+,d0-d1/a0-a2         ; restore affected registers
            rte


*************************************************************************
*               routines to handle tx or rx errors                      *
*************************************************************************
rxerror:    movem.l   d0/a0,-(sp)               ; save all registers
            lea       (gpip).w,a0               ; set mfp chip address pointer
_rxerror:   move.b    $2a(a0),(rbufrec+28).l    ; receiver status register
            move.b    $2e(a0),d0                ; dummy read of data register
            move.b    #$f7,$e(a0)
            movem.l   (sp)+,d0/a0               ; restore all registers
            rte

txerror:    move.l    a0,-(sp)
            lea       (gpip).w,a0               ; set mfp chip address pointer
_txerror:   move.b    $2c(a0),(rbufrec+29).l    ; transmitter status register
            move.b    #$fd,$e(a0)
            movea.l   (sp)+,a0                  ; restore all registers
            tst.b     (tsr).w
            rte


wr_rs232:   move.l    a0,-(sp)                  ; save all registers
            move.b    $21(a0),d0                ; send handshake byte pending?
            beq.s     wr_rs2322                 ; no...
            clr.b     $21(a0)                   ; clear handshake byte
            bra.s     wr_rs232o                 ; output the handshake byte
wr_rs2322:  move.b    $20(a0),d0                ; flow control active?
            and.b     $1f(a0),d0                ; and xon-flag
            bne.s     wr_rs2324                 ; yes...
            adda.w    #$e,a0                    ; switch to output ring buffer
            move.w    6(a0),d0                  ; head-index
            cmp.w     8(a0),d0                  ; = tail-index?
            beq.s     wr_rs2324                 ; buffer empty? => out
            bsr.s     rs232get                  ; get next character from the buffer
wr_rs232o:  tst.b     $2c(a2)                   ; transmit buffer empty?
            bpl.s     wr_rs232o                 ; no...wait...
            move.b    $2c(a2),(rbufrec+29).l    ; save transmitter status
            move.b    d0,$2e(a2)                ; output the byte
wr_rs2324:  movea.l   (sp)+,a0                  ; restore all registers
            rts

rs232put:   move.w    8(a0),d1                  ; tail-index
            bsr.s     wrapin
rs232putl:  cmp.w     6(a0),d1                  ; = head-index?
            beq.s     rs232putl                 ; buffer full...wait...
            movea.l   (a0),a1                   ; ptr to send buffer
            and.l     #$ffff,d1
            move.b    d0,(a1,d1.l)              ; byte into the send buffer
            move.w    d1,8(a0)                  ; update tail-index
            rts

rs232get:   move.w    6(a0),d1                  ; head-index
            cmp.w     8(a0),d1                  ; = tail-index?
            beq.s     rs232get                  ; empty? wait...
            bsr.s     wrapin
            movea.l   (a0),a1                   ; ptr to receive buffer
            moveq     #0,d0
            and.l     #$ffff,d1
            move.b    (a1,d1.l),d0              ; byte from the receive buffer
            move.w    d1,6(a0)                  ; update head-index
            rts

rtson:      lea       (psgsel).w,a1
            move      sr,-(sp)
            ori       #$700,sr
            move.b    #14,(a1)
            move.b    (a1),d1
            and.b     #$f7,d1                   ; clear bit 3 in port A
            move.b    d1,2(a1)
            move      (sp)+,sr
            rts

rtsoff:     lea       (psgsel).w,a1
            move      sr,-(sp)
            ori       #$700,sr
            move.b    #14,(a1)
            move.b    (a1),d1
            ori.b     #8,d1                     ; set bit 3 in port A
            move.b    d1,2(a1)
            move      (sp)+,sr
            rts

wrapin:     addq.w    #1,d1                     ; i=h+1
            cmp.w     4(a0),d1                  ; > i => current bufsiz?
            bcs.s     wi1                       ; no...
            moveq     #0,d1                     ; wrap pointer
wi1:        rts

iorec:      move.w    4(sp),d1                  ; device number
            beq.s     iorecaux                  ; rs232? =>
            asl.l     #2,d1
            move.l    devtab(pc,d1.w),d0
            rts

iorecaux:   move.l    (bsrecauxp).w,d0
            rts

devtab:     DC.L      rbufrec                   ; RS232
            DC.L      kbufrec                   ; IKBD
            DC.L      mbufrec                   ; MIDI

rsconf:     movea.l   (bsrsconfp).w,a0
            jmp       (a0)

auxrsconf:  lea       (rbufrec).w,a0            ; point to current output buffer record
            lea       (gpip).w,a2               ; set mfp chip address pointer
_auxrsconf: moveq     #0,d0
            cmpi.w    #-2,4(sp)                 ; -2 = return last baudrate
            bne.s     auxrsconf1
            move.b    $22(a0),d0                ; last baudrate
            rts

auxrsconf1: ori       #$700,sr                  ; no interrupts for now
*
*      first we grab the old ucs,rsr,tsr,scr contents
*
            movep.l   $28(a2),d7
*
*      set flow control mode(s)
*
            move.w    6(sp),d0                  ; if -1 then don't change
            cmp.w     #3,d0
            bhi.s     auxc1                     ; no =>
            bne.s     auxrsconf2
            moveq     #1,d0                     ; set flag for handshake
auxrsconf2: cmp.b     $20(a0),d0                ; state unchanged? => continue
            beq.s     auxc1
            move.w    d0,-(sp)                  ; save new handshake state
            tst.b     $1f(a0)                   ; xon flag set?
            beq.s     auxrsconf3
            clr.b     $1f(a0)                   ; clear xon flag
            bsr       wr_rs232                  ; transmit xon to continue
auxrsconf3: tst.b     $1e(a0)                   ; RTS set?
            beq.s     auxrsconf4                ; no...
            bsr       auxinclrhw                ; reenable RTS
auxrsconf4: move.w    (sp)+,d0                  ; new handshake state
            move.b    d0,$20(a0)                ; set new handshake state
*
*      set timer baud rate
*
auxc1:      tst.w     4(sp)                     ; if -1 then don't change
            bmi.s     auxc2
            bclr      #0,$2a(a2)                ; disable receiver
            bclr      #0,$2c(a2)                ; disable transmitter
            move.w    4(sp),d1                  ; new baudrate
            move.b    d1,$22(a0)                ; store for later
            move.b    baudctrl(pc,d1.w),d0      ; get baudrate control register settings mask
            move.b    bauddata(pc,d1.w),d2      ; get baudrate data register value
            andi.b    #$70,$1c(a2)
            move.b    d2,$24(a2)                ; set tuner D to new baud rate
            or.b      d0,$1c(a2)
            bset      #0,$2a(a2)                ; enable receiver
            bset      #0,$2c(a2)                ; enable transmitter

*      set rs-232 registers

auxc2:      tst.w     8(sp)                     ; if -1 then don't change
            bmi.s     auxc3
            move.b    9(sp),$28(a2)             ; set ucr
auxc3:      tst.w     $a(sp)                    ; if -1 then don't change
            bmi.s     auxc4
            move.b    $b(sp),$2a(a2)            ; set rsr
auxc4:      tst.w     $c(sp)                    ; if -1 then don't change
            bmi.s     auxc5
            move.b    $d(sp),$2c(a2)            ; set tsr
auxc5:      tst.w     $e(sp)                    ; if -1 then don't change
            bmi.s     auxc6
            move.b    $f(sp),$26(a2)            ; set scr
auxc6:      move.l    d7,d0                     ; move old contents of rs-232 registers to d0.l
            rts

* baudrate table - control register setting

baudctrl:   DC.B      $01,$01,$01,$01,$01,$01,$01,$01
            DC.B      $01,$01,$01,$01,$01,$01,$02,$02

* baudrate table - data register setting

bauddata:   DC.B      $01,$02,$04,$05,$08,$0a,$0b,$10
            DC.B      $20,$40,$60,$80,$8f,$af,$40,$60

rs232devs:  DC.L      auxistat                  ; Dev 6: "Modem 1" - ST MFP
            DC.L      auxin
            DC.L      _auxostat
            DC.L      _auxout
            DC.L      auxrsconf
            DC.L      rbufrec

            DC.L      sccbistat                 ; Dev 7: "Modem 2" - SCC Channel B
            DC.L      sccbin
            DC.L      sccbostat
            DC.L      sccbout
            DC.L      sccbrsconf
            DC.L      sccbbufrec

IF TOS_ROM306
            DC.L      ttmfp_auxistat            ; Dev 8: "Seriell 1" - TT MFP
            DC.L      ttmfp_auxin
            DC.L      ttmfp__auxostat
            DC.L      ttmfp__auxout
            DC.L      ttmfp_auxrsconf
            DC.L      ttmfp_rbufrec
ENDIF

            DC.L      sccaistat                 ; Dev 8/9: "Seriell 2" - SCC Channel A
            DC.L      sccain
            DC.L      sccaostat
            DC.L      sccaout
            DC.L      sccarsconf
            DC.L      sccabufrec

_bconmap:   moveq     #0,d0
            move.w    4(sp),d1
            move.w    (bdevmappedAux).w,d0
            cmp.w     #-1,d1
            beq.s     _bconmap_rts
            move.l    #bdevtabptr,d0
            cmp.w     #-2,d1
            beq.s     _bconmap_rts
            moveq     #0,d0
            subq.w    #6,d1
            bmi.s     _bconmap_rts
            cmp.w     (bdevcount).w,d1
            bcc.s     _bconmap_rts
            move.w    (bdevmappedAux).w,d1
            subq.w    #6,d1
            asl.w     #3,d1
            move.w    d1,d2
            add.w     d1,d1
            add.w     d1,d2
            movea.l   (bdevtabptr).w,a0
            adda.w    d2,a0
            lea       (xconstat+4).w,a1
            move.l    (a1),(a0)+
            move.l    $20(a1),(a0)+
            move.l    $40(a1),(a0)+
            move.l    $60(a1),(a0)+
            move.l    (bsrsconfp).l,(a0)+
            move.l    (bsrecauxp).w,(a0)+
            move.w    4(sp),d1
            subq.w    #6,d1
            asl.w     #3,d1
            move.w    d1,d2
            add.w     d1,d1
            add.w     d1,d2
            movea.l   (bdevtabptr).w,a0
            adda.w    d2,a0
            move.l    (a0)+,(a1)
            move.l    (a0)+,$20(a1)
            move.l    (a0)+,$40(a1)
            move.l    (a0)+,$60(a1)
            move.l    (a0)+,(bsrsconfp).w
            move.l    (a0)+,(bsrecauxp).w
            move.w    (bdevmappedAux).w,d0
            move.w    4(sp),(bdevmappedAux).w
_bconmap_rts:rts


initdevstables:lea    (bdevtable).w,a0
            move.l    a0,(bdevtabptr).w
IF TOS_ROM306
            move.w    #4,(bdevcount).w
ELSE
            move.w    #3,(bdevcount).w
            moveq     #1,d0                     ; only MFP RS232 is available
            movea.l   sp,a1
            movea.l   (busexception).w,a2
            move.l    #initdevnoscc,(busexception).w
            tst.b     (scu_gp1).w
            move.w    #3,d0                     ; also 2 SCC RS232 are available
initdevnoscc:movea.l  a1,sp
            move.l    a2,(busexception).w
            move.w    d0,(bdevcount).w
ENDIF
            lea       rs232devs(pc),a1
IF TOS_ROM306
            move.w    #$17,d0                   ; 4 devices
ELSE
            move.w    #$11,d0                   ; 3 devices
ENDIF
initdev1:   move.l    (a1)+,(a0)+
            dbra      d0,initdev1
            move.w    #6,(bdevmappedAux).w      ; Device 6 is mapped into device 1 (Aux)
            lea       (rs232devs).l,a1          ; Map MFP RS232 to dev 1
            lea       (xconstat+4).w,a0
            move.l    (a1)+,(a0)                ; Fix tconstat for dev 1
            move.l    (a1)+,$20(a0)             ; Fix tconin for dev 1
            move.l    (a1)+,$40(a0)             ; Fix tcostat for dev 1
            move.l    (a1)+,$60(a0)             ; Fix tconout for dev 1
            move.l    (a1)+,(bsrsconfp).w
            move.l    (a1)+,(bsrecauxp).w
            rts

IF ROM_TOS306
ttmfp_auxistat:
            lea       (ttmfp_rbufrec).w,a0
            bra       _auxistat
ttmfp_auxin:lea       (ttmfp_rbufrec).w,a0
            lea       (gpip_TT).w,a2
            bra       _auxin
ttmfp__auxostat:
            lea       (ttmfp_rbufrec+14).w,a0
            bra       __auxostat
ttmfp__auxout:
            move.w    6(sp),d0
            lea       (ttmfp_rbufrec+14).w,a0
            bsr       rs232put
            lea       (ttmfp_rbufrec).w,a0
            lea       (gpip_TT).w,a2
            bra       _auxoutwr
ttmfp_rcvrint:movem.l   d0-d1/a0-a2,-(sp)
            lea       (ttmfp_rbufrec).w,a0
            lea       (gpip_TT).w,a2
            bra       _rcvrint
ttmfp_txrint:movem.l   d0-d1/a0-a2,-(sp)
            lea       (gpip_TT).w,a2
            lea       (ttmfp_rbufrec).w,a0
            bra       _txrint
ttmfp_rxerr:movem.l   d0/a0,-(sp)
            lea       (gpip_TT).w,a0
            bra       _rxerror
ttmfp_txerr:move.l    a0,-(sp)
            lea       (gpip_TT).w,a0
            bra       _txerror
ttmfp_auxrsconf:lea       (ttmfp_rbufrec).w,a0
            lea       (gpip_TT).w,a2
            bra       _auxrsconf
ENDIF

sccinit:    move.b    (a1)+,d0                  ; SCC register number
            bmi.s     sccina                    ; end of the register list? => exit
            move.b    d0,(a0)                   ; select register
            move.b    (a1)+,(a0)                ; write into register
            bra.s     sccinit
sccina:     rts


* SCC initialization
scctbl:     DC.B      $04,$44                   ; x16, 1 stop, no parity
            DC.B      $01,$04
            DC.B      $02,$60
            DC.B      $03,$c0                   ; rx 8 bit, no crc, rx disabled
            DC.B      $05,$e2                   ; tx 8 bit, no crc, tx disabled, rts,dtr on
            DC.B      $06,$00
            DC.B      $07,$00
            DC.B      $09,$01
            DC.B      $0a,$00                   ; NRZ
            DC.B      $0b,$50                   ; tx & rx clk = brg, trxc=in
            DC.B      $0c,$18                   ; clock divisor for 9600 (pclk)
            DC.B      $0d,$00                   ; divisor high
            DC.B      $0e,$02                   ; BRG = PCLK
            DC.B      $0e,$03                   ; enable BRG
            DC.B      $03,$c1                   ; enable Rx, 8 bits
            DC.B      $05,$ea                   ; enable Tx, 8 bits, rts, dtr on
            DC.B      $0f,$20
            DC.B      $00,$10
            DC.B      $00,$10
            DC.B      $01,$17
            DC.B      $09,$09
            DC.B      $ff                       ; end of the register list
            DC.B      $00

* disconnect LAN from port a
nolan:      move      sr,d1
            ori       #$700,sr
            move.b    #14,(psgsel).w
            move.b    (psgsel).w,d0
            bset      #7,d0
            move.b    #14,(psgsel).w
            move.b    d0,(psgwr).w
            move      d1,sr
            rts

initscc:    movea.l   (busexception).w,a0
            movea.l   sp,a1
            move.l    #initscc_none,(busexception).w
            tst.b     (sccdat_a).w
            move.l    a0,(busexception).w
            lea       (SCC).w,a0
            lea       sccvect(pc),a1
            moveq     #15,d0
initscc2:   move.l    (a1)+,(a0)+
            dbra      d0,initscc2
IF ROM_TOS306
            clr.w     (scdmactl).w
ENDIF
            bsr.s     nolan
            lea       (sccabufrec).w,a0
            lea       sccbufinit(pc),a1
            moveq     #$25,d0
            bsr       lbmove
            lea       (sccbbufrec).w,a0
            lea       sccbufinit(pc),a1
            moveq     #$25,d0
            bsr       lbmove
            move.l    #$1434,(sccbbufrec).w
            move.l    #$1534,(sccbbufrec+14).w
            lea       (sccctl_a).w,a2
            move.b    #9,(a2)
            move.b    #$c0,(a2)
            move.w    #$104,d0                  ; Delay Mode, /4 Prescale, data = 4 (about 6us delay)
            jsr       (mfpdelay).l
            lea       (sccctl_a).w,a0
            lea       scctbl(pc),a1
            bsr       sccinit
            lea       (sccctl_b).w,a0
            lea       scctbl(pc),a1
            bsr       sccinit
            bset      #5,(vme_mask).w           ; Enable IRQ5 from VMEBUS/SCC
            rts

initscc_none:move.l   a0,(busexception).w
            movea.l   a1,sp
            rts


**************************************************************************
*                                                                        *
*                  83C30 SCC interrupt vectors                           *
*                                                                        *
**************************************************************************
sccvec_portbtrnbempty:movem.l d0-d1/a0-a2,-(sp)
            lea       (sccctl_b).w,a2
            lea       (sccbbufrec).w,a0
            bra       scc23
sccvec_portbextstchg:movem.l d0-d1/a0-a2,-(sp)
            lea       (sccctl_b).w,a2
            lea       (sccbbufrec).w,a0
            bra       scc24
sccvec_portbrecchr:movem.l d0-d1/a0-a2,-(sp)
            lea       (sccctl_b).w,a2
            lea       (sccbbufrec).w,a0
            bra.s     scc09
sccvec_portbschrrec:movem.l d0-d1/a0-a2,-(sp)
            lea       (sccctl_b).w,a2
            lea       (sccbbufrec).l,a0
            bra       scc16

sccvec_portatrnbempty:movem.l d0-d1/a0-a2,-(sp)
            lea       (sccctl_a).w,a2
            lea       (sccabufrec).w,a0
            bra       scc23
sccvec_portaextstchg:movem.l d0-d1/a0-a2,-(sp)
            lea       (sccctl_a).w,a2
            lea       (sccabufrec).w,a0
            bra       scc24
sccvec_portarecchr:movem.l d0-d1/a0-a2,-(sp)
            lea       (sccctl_a).w,a2
            lea       (sccabufrec).w,a0
            bra.s     scc09
sccvec_portaschrrec:movem.l d0-d1/a0-a2,-(sp)
            lea       (sccctl_a).w,a2
            lea       (sccabufrec).l,a0
            bra       scc16

scc09:      move.b    #8,(a2)                   ; select scc register #8
            move.b    (a2),d0                   ; read "Receive Data register"
            and.b     $23(a0),d0
            btst      #0,$20(a0)
            beq.s     scc11
            cmp.b     #$13,d0
            bne.s     scc10
            st        $1f(a0)
            bra.s     scc15
scc10:      cmp.b     #$11,d0
            bne.s     scc11
            tst.b     $1f(a0)
            sf        $1f(a0)
            bne.s     scc14
scc11:      move.w    8(a0),d1
            bsr       wrapin
            cmp.w     6(a0),d1
            beq.s     scc15
            bsr       rs232put
            tst.b     $20(a0)
            beq.s     scc15
            tst.b     $1e(a0)
            bne.s     scc15
            move.w    8(a0),d0
            sub.w     6(a0),d0
            bpl.s     scc12
            add.w     4(a0),d0
scc12:      cmp.w     $c(a0),d0
            blt.s     scc15
            st        $1e(a0)
            btst      #0,$20(a0)
            bne.s     scc13
            move.b    $1d(a0),d0
            bclr      #1,d0                     ; clear RTS bit
            move.b    d0,$1d(a0)
            move.b    #5,(a2)                   ; select scc register #5
            move.b    d0,(a2)                   ; write "Transmit Parameters and Controls" register
            bra.s     scc15
scc13:      move.b    #$13,$21(a0)
scc14:      move.b    #0,(a2)                   ; select scc register #0
            move.b    (a2),d0                   ; read register #0
            btst      #2,d0                     ; Tx Buffer Empty?
            beq.s     scc15
            bsr.s     wr_scc
scc15:      move.b    #0,(a2)                   ; select scc register #0
            move.b    #$38,(a2)                 ; write "Reset Highest IUS Command" - clear pending interrupt
            movem.l   (sp)+,d0-d1/a0-a2
            rte

scc16:      move.b    #1,(a2)                   ; select scc register #1
            move.b    (a2),d0                   ; read special receive condition status bits
            move.b    #8,(a2)                   ; select scc register #8
            move.b    (a2),d0                   ; read "Receive Data register"
            move.b    #0,(a2)                   ; select scc register #0
            move.b    #$30,(a2)                 ; write "Error Reset Command" - clear error condition
            bra.s     scc15

wr_scc:     move.l    a0,-(sp)
            tst.b     $21(a0)
            bne.s     scc19
IF ROM_STBOOK
            btst      #0,$20(a0)
            beq.s     scc18
            tst.b     $1f(a0)
            bne.s     scc22
scc18:      btst      #1,$20(a0)
            beq.s     scc19
            move.b    #0,(a2)                   ; select scc register #0
            move.b    (a2),d0                   ; read register #0
            btst      #5,d0                     ; CTS?
            beq.s     scc22
ELSE
            move.b    $20(a0),d0
            and.b     $1f(a0),d0
            bne.s     scc2
ENDIF
scc19:      move.b    #0,(a2)                   ; select scc register #0
            move.b    (a2),d0                   ; read register #0
            btst      #2,d0                     ; Tx Buffer Empty?
            beq.s     scc22
            move.b    $21(a0),d0
            beq.s     scc20
            clr.b     $21(a0)
            bra.s     scc21
scc20:      adda.w    #$e,a0
            move.w    6(a0),d0
            cmp.w     8(a0),d0
            beq.s     scc22
            bsr       rs232get
scc21:      move.b    #8,(a2)                   ; select scc register #8
            move.b    d0,(a2)                   ; write transmit buffer
scc22:      movea.l   (sp)+,a0
            rts

scc23:      move.b    #0,(a2)                   ; select scc register #0
            move.b    #$28,(a2)                 ; write "Reset Tx Int Pending"
            move.b    #0,(a2)                   ; select scc register #0
            move.b    #$38,(a2)                 ; write "Reset Highest IUS Command" - clear pending interrupt
            bsr.s     wr_scc
            movem.l   (sp)+,d0-d1/a0-a2
            rte

scc24:      btst      #1,$20(a0)
            beq.s     scc25
            move.b    #0,(a2)                   ; select scc register #0
            move.b    (a2),d0                   ; read register #0
            btst      #5,d0                     ; CTS?
            seq       $1f(a0)
            beq.s     scc25
IF ROM_STBOOK
            bsr       wr_scc
ELSE
            bsr.s     wr_scc
ENDIF
scc25:      move.b    #0,(a2)                   ; select scc register #0
            move.b    #$10,(a2)                 ; write "Reset Ext/Status Interrupts"
            move.b    #0,(a2)                   ; select scc register #0
            move.b    #$38,(a2)                 ; write "Reset Highest IUS Command" - clear pending interrupt
            movem.l   (sp)+,d0-d1/a0-a2
            rte


**************************************************************************
*                                                                        *
*                  scc BIOS callbacks                                    *
*                                                                        *
**************************************************************************
sccbistat:  lea       (sccbbufrec).w,a0
            lea       (sccctl_b).w,a2
            bra.s     sccistat
sccbin:     lea       (sccbbufrec).w,a0
            lea       (sccctl_b).w,a2
            bra.s     sccin
sccbostat:  lea       (sccbbufrec).w,a0
            lea       (sccctl_b).w,a2
            bra.s     _sccostat
sccbout:    lea       (sccbbufrec).w,a0
            lea       (sccctl_b).w,a2
            bra       _sccout
sccaistat:  lea       (sccabufrec).w,a0
            lea       (sccctl_a).w,a2
            bra.s     sccistat
sccain:     lea       (sccabufrec).w,a0
            lea       (sccctl_a).w,a2
            bra.s     sccin
sccaostat:  lea       (sccabufrec).w,a0
            lea       (sccctl_a).w,a2
            bra.s     _sccostat
sccaout:    lea       (sccabufrec).w,a0
            lea       (sccctl_a).w,a2
            bra.s     _sccout

**************************************************************************
*                                                                        *
*                  scc port input status routine                         *
*                                                                        *
**************************************************************************
sccistat:   moveq     #0,d0                     ; set result to false
            lea       6(a0),a1                  ; head
            lea       8(a0),a0                  ; tail
            cmpm.w    (a0)+,(a1)+               ; atomic buffer empty test
            beq.s     sccist1
            moveq     #-1,d0                    ; set result to true
sccist1:    rts


**************************************************************************
*                                                                        *
*                  scc port output status routine                        *
*                                                                        *
**************************************************************************
_sccostat:  move.w    8(a0),d1                  ; tail
            bsr       wrapin
            cmp.w     6(a0),d1                  ; head
            beq.s     _sccost1
            moveq     #-1,d0                    ; set result to true
            rts

_sccost1:   moveq     #0,d0                     ; set result to false
            rts


**************************************************************************
*                                                                        *
*                  scc input routine                                     *
*                                                                        *
**************************************************************************
sccin:      bsr       rs232get
            move.w    d0,-(sp)
            tst.b     $20(a0)                   ; flow control active?
            beq.s     sccinrts                  ; (no)
            tst.b     $1e(a0)                   ; high-water flag already set?
            beq.s     sccinrts                  ; no...exit...
            move.w    8(a0),d0                  ; tail
            sub.w     6(a0),d0                  ; - head
            bpl.s     sccin2                    ; underflow?
            add.w     4(a0),d0                  ; + size
sccin2:     cmp.w     $a(a0),d0
            bgt.s     sccinrts
            bsr.s     sccinclrhw
sccinrts:   move.w    (sp)+,d0
            rts

sccinclrhw: clr.b     $1e(a0)                   ; clear high-water flag
            btst      #0,$20(a0)                ; is the rs232 mode xon/xoff?
            beq.s     sccinclrhw2
            move.b    #$11,$21(a0)              ; "xon"
            bra.s     _sccoutwr
sccinclrhw2:move.b    $1d(a0),d0
            bset      #1,d0                     ; set RTS
            move.b    d0,$1d(a0)
            move.b    #5,(a2)                   ; select scc register #5
            move.b    d0,(a2)                   ; write "Transmit Parameters and Controls" register
            rts

**************************************************************************
*                                                                        *
*                  scc output routine                                    *
*                                                                        *
**************************************************************************
_sccout:    move.w    6(sp),d0                  ; get data
            adda.w    #$e,a0
            bsr       rs232put                  ; exit via rs-232 output routine
            suba.w    #$e,a0
_sccoutwr:  move.b    #0,(a2)                   ; select scc register #0
            move.b    (a2),d0                   ; read register #0
            btst      #2,d0                     ; Tx Buffer Empty?
            beq.s     _sccoutrts                ; not ready
            move      sr,-(sp)
            ori       #$700,sr
            bsr       wr_scc
            move      (sp)+,sr
_sccoutrts: rts

**************************************************************************
*                                                                        *
*                  scc rsconf() routine                                  *
*                                                                        *
**************************************************************************
sccarsconf: lea       (sccabufrec).w,a0         ; point to current output buffer record
            lea       (sccctl_a).w,a2
            bra.s     sccrsconf
sccbrsconf: lea       (sccbbufrec).w,a0         ; point to current output buffer record
            lea       (sccctl_b).w,a2

sccrsconf:  moveq     #0,d0
            cmpi.w    #-2,4(sp)                 ; -2 = return last baudrate
            bne.s     sccrsconf2
            move.b    $22(a0),d0                ; last baudrate
            rts

sccrsconf2: ori       #$700,sr                  ; no interrupts for now
*
*      first we grab the old ucs,rsr,tsr,scr contents
*
            moveq     #0,d7
            move.b    $1c(a0),d7
            asl.w     #8,d7
            swap      d7
            move.b    $1d(a0),d7
            lsr.b     #1,d7
            and.b     #4,d7
            asl.w     #8,d7
*
*      set flow control mode(s)
*
            move.w    6(sp),d0                  ; if -1 then don't change
            cmp.w     #3,d0
            bhi.s     sccrsconf6
            bne.s     sccrsconf3
            moveq     #1,d0                     ; set flag for handshake
sccrsconf3: cmp.b     $20(a0),d0                ; state unchanged? => continue
            beq.s     sccrsconf6
            tst.b     $1f(a0)                   ; xon flag set?
            beq.s     sccrsconf4
            clr.b     $1f(a0)                   ; clear xon flag
            bsr       wr_scc
sccrsconf4: tst.b     $1e(a0)                   ; RTS set?
            beq.s     sccrsconf5                ; no...
            move.w    d0,-(sp)                  ; save new handshake state
            bsr       sccinclrhw                ; reenable RTS
            move.w    (sp)+,d0                  ; new handshake state
sccrsconf5: move.b    d0,$20(a0)                ; set new handshake state
*
*      set timer baud rate
*
sccrsconf6: move.w    4(sp),d0                  ; new baudrate
            cmp.w     #$f,d0                    ; if -1 (out of range 0..15) then don't change
            bhi.s     sccrsconf7
            move.b    d0,$22(a0)                ; new baudrate
            asl.w     #1,d0
            lea       (sccbaudtab).l,a1
            move.w    (a1,d0.w),d0
            move.b    #$c,(a2)                  ; select scc register #12
            move.b    d0,(a2)                   ; write "Lower Byte of Baud Rate Generator Time Constant" register
            lsr.w     #8,d0
            move.b    #$d,(a2)                  ; select scc register #13
            move.b    d0,(a2)                   ; write "Upper Byte of Baud Rate Generator Time Constant" register
*      set rs-232 registers

sccrsconf7: move.w    8(sp),d0                  ; if -1 then don't change
            bmi.s     sccrsconf12
            move.b    d0,$1c(a0)
            move.b    d0,d1
            and.b     #$60,d1
            lsr.b     #5,d1
            moveq     #$ff,d2
            lsr.b     d1,d2
            move.b    d2,$23(a0)
            move.b    d0,d1
            and.b     #$60,d1
            beq.s     sccrsconf8
            cmp.b     #$60,d1
            bne.s     sccrsconf9
sccrsconf8: eori.b    #$60,d1
sccrsconf9: move.b    $1d(a0),d2
            and.b     #$9f,d2
            or.b      d1,d2
            move.b    d2,$1d(a0)
            move.b    #5,(a2)                   ; select scc register #5
            move.b    d2,(a2)                   ; write "Transmit Parameters and Controls" register
            asl.b     #1,d1
            or.b      #1,d1                     ; "Rx Enable"
            move.b    #3,(a2)                   ; select scc register #3
            move.b    d1,(a2)                   ; write "Receive Parameters and Control" register
            move.b    d0,d1
            and.b     #$1e,d1
            lsr.b     #1,d1                     ; mask stop bits (bit 2..3) and parity (bit 0..1)
            bclr      #1,d1
            sne       d2
            bclr      #0,d1                     ; disable parity
            bne.s     sccrsconf10
            bclr      #1,d2                     ; "odd parity"
            bra.s     sccrsconf11
sccrsconf10:bset      #1,d2                     ; "even parity"
sccrsconf11:and.b     #3,d2                     ; mask parity bits
            or.b      d2,d1
            or.b      #$40,d1                   ; "X16 Clock Mode"
            move.b    #4,(a2)                   ; select scc register #4
            move.b    d1,(a2)                   ; write "Transmit/Receive Mis- cellaneous Parameters and Modes" register

sccrsconf12:
IF ROM_TOS306
            move.w    $a(sp),d0                 ; if -1 then don't change
ELSE
            move.w    $c(sp),d0                 ; if -1 then don't change
ENDIF
            bmi.s     sccrsconf14
            btst      #3,d0
            beq.s     sccrsconf13
            bset      #4,$1d(a0)                ; set "Tx Enable"
            bne.s     sccrsconf14
            move.b    #5,(a2)                   ; select scc register #5
            move.b    $1d(a0),(a2)              ; write "Transmit Parameters and Controls" register
            bra.s     sccrsconf14
sccrsconf13:bclr      #4,$1d(a0)                ; clear "Tx Enable"
            beq.s     sccrsconf14
            move.b    #5,(a2)                   ; select scc register #5
            move.b    $1d(a0),(a2)              ; write "Transmit Parameters and Controls" register
sccrsconf14:move.l    d7,d0                     ; move old contents of rs-232 registers to d0.l
            rts

sccbaudtab: DC.W      $000b,$0018,$0032,$0044
            DC.W      $0067,$007c,$008a,$00d0
            DC.W      $01a1,$0345,$04e8,$068c
            DC.W      $074d,$08ee,$0d1a,$13a8
sccvect:    DC.L      sccvec_portbtrnbempty
            DC.L      $000000
            DC.L      sccvec_portbextstchg
            DC.L      $000000
            DC.L      sccvec_portbrecchr
            DC.L      $000000
            DC.L      sccvec_portbrecchr
            DC.L      $000000
            DC.L      sccvec_portatrnbempty
            DC.L      $000000
            DC.L      sccvec_portaextstchg
            DC.L      $000000
            DC.L      sccvec_portarecchr
            DC.L      $000000
            DC.L      sccvec_portarecchr
            DC.L      $000000
sccbufinit: DC.L      $001210                   ; ibufptr
            DC.W      $0100                     ; ibufsiz
            DC.W      $0000                     ; ibufhead
            DC.W      $0000                     ; ibuftail
            DC.W      $0080                     ; ibuflow
            DC.W      $00c0                     ; ibufhigh
            DC.L      $001310                   ; obufptr
            DC.W      $0100                     ; obufsiz
            DC.W      $0000                     ; obufhead
            DC.W      $0000                     ; obuftail
            DC.W      $0080                     ; obuflow
            DC.W      $00c0                     ; obufhigh
            DC.B      $08                       ; rsrbyte
            DC.B      $ea                       ; tsrbyte
            DC.B      $00                       ; rxoff
            DC.B      $00                       ; txoff
            DC.B      $01                       ; rsmode_xon
            DC.B      $00                       ; rsmode_filler
            DC.W      $01ff

*************************************************************************
*       this code handles the midi/keyboard interrupt exception         *
*************************************************************************
midikey:    movem.l   d0-d3/a0-a3,-(sp)         ; save all registers
keymidi:    movea.l   (astatusmidip).w,a2
            jsr       (a2)
            movea.l   (astatuskeyp).w,a2
            jsr       (a2)
            btst      #4,(gpip).w               ; check for pending interrupt occurance
            beq.s     keymidi                   ; repeat this interrupt processing
            move.b    #$bf,(isrb).w             ; clear in-service bit
            movem.l   (sp)+,d0-d3/a0-a3         ; restore all registers
            rte                                 ; go back to what was happening!

astatusmidi:lea       (mbufrec).w,a0
            lea       (midictl).w,a1
            movea.l   (vmiderr).w,a2
            bra.s     astatus2
astatuskeyboard:lea   (kbufrec).w,a0
            lea       (keyctl).w,a1
            movea.l   (vkbderr).w,a2
astatus2:   move.b    (a1),d2                   ; grab device status
            btst      #7,d2                     ; make sure it was an interrupt request
            beq.s     aciaexit                  ; nope..it's empty
            btst      #0,d2                     ; see if receiver buffer is full
            beq.s     mk1                       ; nope..it's empty
            movem.l   d2/a0-a2,-(sp)
            bsr.s     arcvrint                  ; yes...get byte
            movem.l   (sp)+,d2/a0-a2
mk1:        andi.b    #$20,d2                   ; mask off bits already tested
            beq.s     aciaexit                  ; see if any other status bits are on
            move.b    2(a1),d0                  ; grab data byte from acia data register
            jmp       (a2)                      ; yes to branch to pre-inited error subroutine

aciaexit:   rts

*************************************************************************
*       acia receiver buffer full interrupt routine                     *
*************************************************************************

arcvrint:   move.b    2(a1),d0                  ; grab data byte from acia data register
            cmpa.l    #kbufrec,a0
            bne       midibyte                  ; don't treat midi acia data as anything other than as pure data...
            tst.b     (kstate).w
            bne.s     ML3
            cmpi.b    #$f6,d0
            bcc.s     itsnotakey                ; is it a ikbd header?
            move.l    (itsakeyp).w,-(sp)        ; jump into ikbd processing function
            rts

itsnotakey: subi.b    #$f6,d0                   ; generate true index into tables now
            andi.l    #$ff,d0                   ; clear high 3 bytes for indexing
            lea       (ikbdev).l,a3             ; point to ikbd device state codes
            move.b    (a3,d0.w),(kstate).w      ; set ikbd state
            lea       (ikbdlen).l,a3            ; point to ikbd device buffer length table
            move.b    (a3,d0.w),(kindex).w      ; set ikbd device index counter
            addi.w    #$f6,d0                   ; re-constitute original value
            cmpi.b    #$f8,d0                   ; mouse position record?
            blt.s     ML8                       ; no
            cmpi.b    #$fb,d0                   ; mouse position record?
            bgt.s     ML8                       ; no
            move.b    d0,(mousebuf).w           ; store mouse byte
            rts

ML8:        cmpi.b    #$fd,d0                   ; joystick record?
            blt.s     ML8b                      ; no
            move.b    d0,(joyrec).w             ; store joystick byte
ML8b:       rts

ikbdev:     DC.B      $01,$02,$03,$03,$03,$03,$04,$05
            DC.B      $06,$07
ikbdlen:    DC.B      $07,$05,$02,$02,$02,$02,$06,$02
            DC.B      $01,$01

ML3:        cmpi.b    #6,(kstate).w
            bcc       ML35                      ; a joystick 0/1 record byte, not both!
            lea       (ikbdparams).l,a2         ; point to ikbd subsystem parameters table
            moveq     #0,d2
            move.b    (kstate).w,d2             ; load to generate longword offset
            subq.b    #1,d2                     ; d2 = (kstate-1) * 12
            asl.w     #1,d2
            add.b     (kstate).w,d2
            subq.b    #1,d2
            asl.w     #2,d2
            movea.l   (a2,d2.w),a0              ; load in subsystem's record pointer
            movea.l   4(a2,d2.w),a1             ; load in subsystem's index base+record pointer
            movea.l   8(a2,d2.w),a2             ; load in subsystem's pointer variable that contains the pointer to the subsystem's interrupt routine
            movea.l   (a2),a2                   ; get the address of the interrupt routine
            moveq     #0,d2                     ; clear out 'd2' for address manipulation
            move.b    (kindex).w,d2
            suba.l    d2,a1
            move.b    d0,(a1)                   ; store byte into the subsystems buffer
            subq.b    #1,(kindex).w             ; decrement the number of bytes in the package
            tst.b     (kindex).w                ; package received?
            bne.s     ML1                       ; not yet...
ikserve:    move.l    a0,-(sp)                  ; stuff buffer pointer to stack
            jsr       (a2)                      ; go service the subsystem interrupt routine
            addq.w    #4,sp                     ; re-adjust stack
            clr.b     (kstate).w                ; reset ikbd state
ML1:        rts

ikbdparams: DC.L      statrec
            DC.L      amrec
            DC.L      statintvec

            DC.L      amrec
            DC.L      mousebuf
            DC.L      msintvec

            DC.L      mousebuf
            DC.L      clkrec
            DC.L      msintvec

            DC.L      clkrec
            DC.L      joyrec
            DC.L      clkintvec

            DC.L      joyrec
            DC.L      joyrec+2
            DC.L      joyintvec

ML35:       move.l    #joyrec+1,d1
            add.b     (kstate).w,d1             ; kstate reflects joy0 or joy1 state
            subq.b    #6,d1
            movea.l   d1,a2                     ; create index to joyrec table for record byte
            move.b    d0,(a2)
            movea.l   (joyintvec).w,a2          ; get user's joystick interrupt routine adr
            lea       (joyrec).w,a0             ; send along address of joystick data
            bra.s     ikserve

itsakey:    move.b    (kb_shift).w,d1           ; load in kbshift for manipulation
* check the special keys
            cmpi.b    #$2a,d0                   ; left shift?
            bne.s     ari2
            bset      #1,d1
            bra       ari10
ari2:       cmpi.b    #$aa,d0
            bne.s     ari3
            bclr      #1,d1
            bra       ari10
ari3:       cmpi.b    #$36,d0                   ; right shift?
            bne.s     ari4
            bset      #0,d1
            bra.s     ari10
ari4:       cmpi.b    #$b6,d0
            bne.s     ari5
            bclr      #0,d1
            bra.s     ari10
ari5:       cmpi.b    #$1d,d0                   ; CTRL
            bne.s     ari6
            bset      #2,d1
            bra.s     ari10
ari6:       cmpi.b    #$9d,d0
            bne.s     ari7
            bclr      #2,d1
            bra.s     ari10
ari7:       cmpi.b    #$38,d0                   ; ALT
            bne.s     ari8
            bset      #3,d1
            bra.s     ari10
ari8:       cmpi.b    #$b8,d0
            bne.s     ari9
            bclr      #3,d1

            tst.w     (altdigits).l             ; ascii code via numpad active?
            bmi.s     ari10                     ; no...
            move.b    d1,(kb_shift).w           ; store shift status
            move.l    a0,-(sp)
            moveq     #0,d1                     ; no scancode
            move.w    d1,d0                     ; clear ascii code reg
            move.b    (altdigits+1).l,d0        ; take entered ascii code
            move.w    #-1,(altdigits).l         ; reset alt-numpad ascii entering
            bra       conin25

ari9:       cmpi.b    #$3a,d0                   ; CAPS LOCK
            bne.s     ari11
            btst      #0,(conterm).w
            beq.s     ari9a                     ; no click please!
            movem.l   d0-d2/a0-a2,-(sp)
            movea.l   (kcl_hook).w,a0
            jsr       (a0)
            movem.l   (sp)+,d0-d2/a0-a2
ari9a:      bchg      #4,d1                     ; toggle CAPS LOCK state
ari10:      move.b    d1,(kb_shift).w           ; restore new kbshift value
            rts                                 ; ignore CAPS LOCK break


ari11:      btst      #7,d0                     ; is it a break code?
            bne.s     ari12                     ; no... a break code!
            move.b    d0,(keyrep).w             ; save for repeat purposes
            move.b    (cdelay1).l,(keydelay1).w
            move.b    (cdelay2).l,(keydelay2).w
            bra.s     ari16
ari12:      move.b    d0,d1
            bclr      #7,d1
            cmp.b     (keyrep).w,d1
            bne.s     ari15
            moveq     #0,d1
            move.b    d1,(keyrep).w
            move.b    d1,(keydelay1).w
            move.b    d1,(keydelay2).w

ari15:      cmpi.b    #$c7,d0                   ; is it a "home" break-code?
            beq.s     ari18a                    ; yes...allow it to pass
            cmpi.b    #$d2,d0                   ; is it an "insert" break-code?
            bne       ari14                     ; no...regular break junk...
ari18a:     btst      #3,(kb_shift).w           ; early "ALT" test to prevent double "nulls"
            beq       ari14                     ; no ALT...so exit now...
ari16:      btst      #0,(conterm).w
            beq.s     ari16a                    ; no click please!
            movem.l   d0-d2/a0-a2,-(sp)
            movea.l   (kcl_hook).w,a1
            jsr       (a1)
            movem.l   (sp)+,d0-d2/a0-a2
ari16a:     move.l    a0,-(sp)                  ; store kbufrec pointer

            moveq     #0,d1
            move.b    d0,d1

default: use regular ascii table:movea.l (skeytran).w,a0
            andi.w    #$7f,d0
            btst      #4,(kb_shift).w           ; caps-lock pressed?
            beq.s     conin21
            movea.l   (skeycl).w,a0             ; use caps-lock table
conin21:    btst      #0,(kb_shift).w
            bne.s     conin22
            btst      #1,(kb_shift).w
            beq.s     conin23
conin22:    cmpi.b    #$3b,d0                   ; see if a possible function key
            bcs.s     conin22a                  ; unsigned less than lowest function scancode
            cmpi.b    #$44,d0                   ; see if a possible function key
            bhi.s     conin22a                  ; unsigned greater than highest function scan
            addi.w    #$19,d1                   ; add to change to GSX standard
            moveq     #0,d0                     ; change to GSX standard
            bra       conin25

conin22a:   movea.l   (skeyshif).w,a0           ; use the shift table
conin23:    move.b    (a0,d0.w),d0
            btst      #2,(kb_shift).w           ; is the control key down?
            beq.s     conin24a
            cmpi.b    #$d,d0                    ; is it a carriage return?
            bne.s     conin23a
            moveq     #$a,d0                    ; change to a linefeed according to GSX spec...
            beq.s     conin24x
conin23a:   cmpi.b    #$47,d1                   ; convert CONTROL-home to gsx standard
            bne.s     conin23b
            addi.w    #$30,d1                   ; by adding #$30...
            bra       conin25
conin23b:   cmpi.b    #$4b,d1                   ; convert CONTROL-left arrow to gsx standard
            bne.s     conin23c
            moveq     #$73,d1                   ; change according to gsx spec
            moveq     #0,d0
            bra       conin25
conin23c:   cmpi.b    #$4d,d1                   ; convert CONTROL-right arrow to gsx standard
            bne.s     conin24x
            moveq     #$74,d1                   ; change according to gsx spec
            moveq     #0,d0
            bra       conin25

conin24x:   cmpi.b    #$32,d0                   ; Control-M?
            bne.s     conin240
            moveq     #0,d0                     ; ascii code 0
            bra       conin25
conin240:   cmpi.b    #$36,d0                   ; Control-Shift-Right?
            bne.s     conin241
            moveq     #$1e,d0                   ; ascii code RS
            bra       conin25
conin241:   cmpi.b    #$2d,d0                   ; Control-X?
            bne.s     conin24a
            moveq     #$1f,d0                   ; ascii code US
            bra       conin25

* this routine allows entering any ascii code by using the numpad
* and holding down ALT during it.
conin24a:   btst      #3,(kb_shift).w           ; is the alt key down?
            beq       conin2x
            cmp.b     #$67,d1                   ; key 0 ... 9 on the number pad?
            bcs.s     outside                   ; no...
            cmp.b     #$70,d1
            bhi.s     outside                   ; no...
            move.w    (altdigits).l,d0          ; already entering an ascii code?
            bpl.s     conin24b                  ; yes
            moveq     #0,d0                     ; start with 0
conin24b:   mulu.w    #10,d0                    ; previous digit * 10
            ext.w     d1
            move.b    (a0,d1.w),d1              ; ascii code of current digit
            sub.b     #'0',d1                   ; convert into number
            add.b     d1,d0                     ; plus old number
            move.w    d0,(altdigits).l          ; store as new number code
            movea.l   (sp)+,a0
            rts

outside:    cmpi.b    #$62,d1                   ; is it an "alt help" signal to dump the screen?
            bne.s     alt15a                    ; no...
            addq.w    #1,(_prtcnt).w            ; yes...switch the signal flag on!...
            movea.l   (sp)+,a0                  ; restore kbufrec pointer
            bra       ari14                     ; ...and exit
*
*       check the alt-insert/alt-home key make/break combinations, first
*
alt15a:     lea       (mousekey1).l,a2          ; get pointer to first alt. mouse scancode table
            moveq     #3,d2                     ; create countdown
mkloop1:    cmp.b     (a2,d2.w),d1              ; is table's scancode value = current value?
            beq       keymaus1                  ; yes...go preprocess it...
            dbra      d2,mkloop1                ; no...loop back to check next table value

            cmpi.b    #$48,d1                   ; is it an up arrow?
            bne.s     alt11
            move.b    #0,d1                     ; x value for up arrow
            move.b    #$f8,d2                   ; y value for up arrow
            move.b    (kb_shift).w,d0           ; grab current setting
            andi.b    #3,d0                     ; KBRSH+KBLSH bits
            beq       keymaus
            move.b    #$ff,d2                   ; y value for up arrow
            bra       keymaus
alt11:      cmpi.b    #$4b,d1                   ; is it an left arrow?
            bne.s     alt12
            move.b    #0,d2                     ; x value for left arrow
            move.b    #$f8,d1                   ; y value for left arrow
            move.b    (kb_shift).w,d0           ; grab current setting
            andi.b    #3,d0                     ; KBRSH+KBLSH bits
            beq       keymaus
            move.b    #$ff,d1                   ; x value for left arrow
            bra       keymaus
alt12:      cmpi.b    #$4d,d1                   ; is it an right arrow?
            bne.s     alt13
            move.b    #8,d1                     ; x value for right arrow
            move.b    #0,d2                     ; y value for right arrow
            move.b    (kb_shift).w,d0           ; grab current setting
            andi.b    #3,d0                     ; KBRSH+KBLSH bits
            beq       keymaus
            move.b    #1,d1                     ; x value for right arrow
            bra       keymaus
alt13:      cmpi.b    #$50,d1                   ; is it an down arrow?
            bne.s     alt14
            move.b    #0,d1                     ; x value for down arrow
            move.b    #8,d2                     ; y value for down arrow
            move.b    (kb_shift).w,d0           ; grab current setting
            andi.b    #3,d0                     ; KBRSH+KBLSH bits
            beq       keymaus
            move.b    #1,d2                     ; y value for down arrow
            bra       keymaus
alt14:      btst      #2,(kb_shift).w           ; CTRL key?
            bne.s     conin24                   ; yes
            cmpi.b    #2,d1
            bcs.s     alt1                      ; not >= the '1' key scancode
            cmpi.b    #$d,d1
            bhi.s     alt1                      ; not <= the '=' key scancode
            addi.b    #$76,d1                   ; scancode is a key between '1' key and '=' key
            bra.s     alt2
alt1:       cmpi.b    #$41,d0                   ; is the key an ascii 'A' or greater?
            bcs.s     alt3                      ; no skip to check if 'a'-'z'...
            cmpi.b    #$5a,d0                   ; is the key an ascii 'Z' or less?
            bhi.s     alt3                      ; no skip to check if 'a'-'z'...
alt2:       moveq     #0,d0
            bra.s     conin25
alt3:       cmpi.b    #$61,d0                   ; is the key an ascii 'a' or greater?
            bcs.s     conin25                   ; not...skip to finish normal processing
            cmpi.b    #$7a,d0                   ; is the key an ascii 'z' or less?
            bhi.s     conin25                   ; not...skip to finish normal processing
            bra.s     alt2

conin2x:    btst      #2,(kb_shift).w           ; CTRL key?
            beq.s     conin25                   ; no
conin24:    andi.w    #$1f,d0                   ; yep, so CTRLize the key
conin25:    asl.w     #8,d1                     ; shift the scan code to the word's high byte
            add.w     d1,d0                     ; form the outgoing word

            movea.l   (sp)+,a0                  ; restore kbufrec pointer

            move.w    8(a0),d1                  ; get current tail pointer offset
            addq.w    #4,d1                     ; index = tail + 4
            cmp.w     4(a0),d1                  ; check to see if buffer should wrap
            bcs.s     ari13                     ; no...
            moveq     #0,d1                     ; wrap pointer
ari13:      cmp.w     6(a0),d1                  ; head=tail?
            beq.s     ari14                     ; yes
            movea.l   (a0),a2                   ; get buffer pointer

* move kb_shift into the upper 8 bits of the keycode
            swap      d0
            clr.w     d0
            move.b    (kb_shift).w,d0
            swap      d0
            lsl.l     #8,d0
            lsr.w     #8,d0

            move.l    d0,d2                     ; save keycode for our reset key commands
            bclr      #$1c,d2                   ; clear capslock state
            swap      d2                        ; isolate kb_shift & scancode
            cmp.w     #$c53,d2                  ; control+alt+delete?
            beq       reseth                    ; => reset
            cmp.w     #$d53,d2                  ; control+alt+rshift+delete?
            beq       _coldboot                 ; => cold boot by erasing all memory

            btst      #3,(conterm).w            ; should Bconin() to return the shift status?
            bne.s     ari13b                    ; yes...
            andi.l    #$ffffff,d0               ; no, filter it out (default case)
ari13b:     and.l     #$ffff,d1
            move.l    d0,(a2,d1.l)              ; store the data
            move.w    d1,8(a0)                  ; store the new buftail pointer
ari14:      rts

keyclicksnd:move.l    #gkeyclicksnd,(cursnd).w  ; sound data for key click
            move.b    #0,(timer).w              ; enable sound timer
            rts


midibyte:   movea.l   (midivec).w,a2            ; get contents of midivec for indirect branch
            jmp       (a2)                      ; jump to midi interrupt handler


sysmidi:    move.w    8(a0),d1                  ; get current tail pointer offset
            addq.w    #1,d1                     ; index = tail + 1
            cmp.w     4(a0),d1                  ; check to see if buffer should wrap
            bcs.s     mi13                      ; no...
            moveq     #0,d1                     ; wrap pointer
mi13:       cmp.w     6(a0),d1                  ; head=tail?
            beq.s     mi14                      ; yes
            movea.l   (a0),a2                   ; get buffer pointer
            and.l     #$ffff,d1
            move.b    d0,(a2,d1.l)              ; store the data
            move.w    d1,8(a0)                  ; store the new buftail pointer
mi14:       rts


keymaus1:   moveq     #5,d3                     ; pre-init to "keyboard" right mouse button
            btst      #4,d1                     ; see if it is a left or right button...
            beq.s     keym1                     ; it's a right button ($47/$c7)
            moveq     #6,d3                     ; it's a left button ($52/$d2)
keym1:      btst      #7,d1                     ; see if it is a make or break action
            beq.s     keym2                     ; it's a set button action (make code)
            bclr      d3,(kb_shift).w           ; it's a clear button action (break code)
            bra.s     keym3                     ; go to further pre-init action...
keym2:      bset      d3,(kb_shift).w           ; it's a set button action (set code)
keym3:      moveq     #0,d1
            moveq     #0,d2
*
* finish up at the actual pseudo mouse routine
*

keymaus:    lea       (kmbuf).w,a0              ; point to key-emulating mouse buffer
            movea.l   (msintvec).w,a2           ; grab mouse interrupt vector
            clr.l     d0
            move.b    (kb_shift).w,d0           ; get current button status
            lsr.b     #5,d0                     ; shift right button bit to 'd0'
            addi.b    #$f8,d0                   ; add relative mouse header
            move.b    d0,(a0)                   ; store in first byte of record header
            move.b    d1,1(a0)                  ; store x value in second byte of record buffer
            move.b    d2,2(a0)                  ; store y value in third byte of record buffer
            jsr       (a2)
            movea.l   (sp)+,a0                  ; restore kbufrec pointer
            rts


mousekey1:  DC.B      $47,$c7,$52,$d2

*************************************************************************
*                                                                       *
*************************************************************************
_coldboot:  move      #$2700,sr                 ; disable all IRQSR
IF ROM_STBOOK
            move.l    ($0004).w,(busexception).w ;a bus error triggers a reset
            movea.w   #addrexception,a0         ; start erasing from $c on
            moveq     #0,d0
            move.l    #$3fffc,d1                ; erase 1MB
_coldboot2: move.l    d0,(a0)+
            dbra      d1,_coldboot2
            movea.l   ($0004).w,a0              ; jump into the ROM to reset the system
            jmp       (a0)

ELIF ROM_TOS206
            move.w    #5,d0
            lea       _coldbootr(pc),a0
            movea.w   #addrexception,a1
_coldboot1: move.l    (a0)+,(a1)+               ; copy erase routine to $000c...
            dbra      d0,_coldboot1
            jmp       (addrexception).w

_coldbootr: move.l    ($0004).w,(busexception).w
            lea       _coldbootre(pc),a0
            moveq     #0,d0
_coldbootl: move.l    d0,(a0)+
            move.l    d0,(a0)+
            move.l    d0,(a0)+
            move.l    d0,(a0)+
            bra.s     _coldbootl
_coldbootre:nop

ELIF ROM_TOS306
            move.l    #$808,d0
            movec     d0,cacr

            moveq     #0,d0
            movec     d0,vbr                    ; set vector base to address 0
            pmove     ($e36738).l,tc            ; 0L - disable PMMU
            pmove     ($e36738).l,tt0           ; 0L
            pmove     ($e36738).l,tt1           ; 0L

            move.w    #11,d0
            lea       _coldbootr(pc),a0
            movea.w   #addrexception,a1
_coldboot1: move.l    (a0)+,(a1)+               ; copy erase routine to $000c...
            dbra      d0,_coldboot1
            jmp       (addrexception).w

_coldbootr: lea       _coldboott(pc),a0
            move.l    a0,(busexception).w
            lea       _coldbootre(pc),a0
            moveq     #0,d0
_coldbootl: move.l    d0,(a0)+                  ; erase ST RAM
            move.l    d0,(a0)+
            move.l    d0,(a0)+
            move.l    d0,(a0)+
            bra.s     _coldbootl
_coldboott: move.l    ($0004).w,(busexception).w
            lea       ($1000000).l,a0
_coldbootfl:move.l    d0,(a0)+                  ; erase fast RAM
            move.l    d0,(a0)+
            move.l    d0,(a0)+
            move.l    d0,(a0)+
            bra.s     _coldbootfl
_coldbootre:nop

ENDIF

*************************************************************************
*                                                                       *
*       protocol for accessing a gi sound chip register                 *
*                                                                       *
*       this bios call must be accessed in supervisor state             *
*       because it affects the 'sr' register                            *
*                                                                       *
*       entry                                                           *
*                                                                       *
*       word    giaccess(data,register)                                 *
*       word    data,register                                           *
*                                                                       *
*               data -- data register read/write date                   *
*               register -- chip register to select                     *
*               d1 = #$0000     ;selects read operation of the register *
*               d1 = #$80 .or .xx       ;selects write xx to register   *
*               example write to portb - $80 .or. $0f = $8f             *
*                                                                       *
*       exit                                                            *
*       read operations                                                 *
*       d0.b -- data register contains byte of date                     *
*       write operations                                                *
*       d0.b -- data register contains a verification of written data   *
*                                                                       *
*************************************************************************
giaccess:   move.w    4(sp),d0
            move.w    6(sp),d1
gientry:    move      sr,-(sp)
            ori       #$700,sr
            movem.l   d1-d2/a0,-(sp)            ; save affected registers
            lea       (psgsel).w,a0             ; init desired gi register addr
            move.b    d1,d2                     ; make a copy to test for read or write
            andi.b    #$f,d1                    ; turn off any extraneous bits
            move.b    d1,(a0)                   ; select register
            asl.b     #1,d2                     ; shift once for carry bit detection
            bcc.s     giread                    ; carry clear, so do a read operation
giwrit:     move.b    d0,2(a0)                  ; init the memory location
giread:     moveq     #0,d0                     ; clear our register
            move.b    (a0),d0                   ; grab the data from the gi register
            movem.l   (sp)+,d1-d2/a0            ; restore affected registers
            move      (sp)+,sr
            rts


*********************************************************************
*       routine to turn on the dtr signal                           *
*********************************************************************

dtron:      move.b    #$ef,d2
            bra.s     offbit

*********************************************************************
*                                                                   *
*       routine to set any bit in the gi port a area                *
*                                                                   *
*       entry                                                       *
*                                                                   *
*       word    ongibit(bitnum)                                     *
*       word    bitnum                                              *
*                                                                   *
*               bitnum - byte size mask with desired bit set to "1" *
*                                                                   *
*********************************************************************

ongibit:    moveq     #0,d2
            move.w    4(sp),d2
onbit:      movem.l   d0-d2,-(sp)
            move      sr,-(sp)
            ori       #$700,sr
            moveq     #14,d1                    ; get ready to read in the port a contents
            move.l    d2,-(sp)
            bsr.s     gientry                   ; go get it...
            move.l    (sp)+,d2
            or.b      d2,d0                     ; set bit(s) on
            move.b    #$8e,d1                   ; setup to write to port a
            bsr.s     gientry                   ; go set it and return
            move      (sp)+,sr
            movem.l   (sp)+,d0-d2
            rts


*********************************************************************
*                                                                   *
*       routine to clear any bit in the gi port a area              *
*                                                                   *
*       entry                                                       *
*                                                                   *
*       word    offgibit(bitnum)                                    *
*       word    bitnum                                              *
*                                                                   *
*               bitnum - byte size mask with desired bit set to "0" *
*                                                                   *
*********************************************************************

offgibit:   moveq     #0,d2
            move.w    4(sp),d2
offbit:     movem.l   d0-d2,-(sp)
            move      sr,-(sp)
            ori       #$700,sr
            moveq     #14,d1                    ; get ready to read in the port a contents
            move.l    d2,-(sp)
            bsr.s     gientry                   ; go get it...
            move.l    (sp)+,d2
            and.b     d2,d0                     ; turn bit(s) off
            move.b    #$8e,d1                   ; setup to write to port a
            bsr.s     gientry                   ; go set it and return
            move      (sp)+,sr
            movem.l   (sp)+,d0-d2
            rts


*************************************************************************
*                                                                       *
*               EXTENDED RBP BIOS TIMER INIT CALL                       *
*                                                                       *
*       entry:                                                          *
*                                                                       *
*       word    initmous(type,param,intvec)                             *
*       word    type                                                    *
*       long    param,initvec                                           *
*                                                                       *
*               type - key/abs/rel/off  mouse function requested        *
*                       4/  2/  1/  0   value                           *
*               param - address of parameter block                      *
*               intvec - mouse interrupt vector                         *
*                                                                       *
*                                                                       *
*       parameter block definition:                                     *
*                                                                       *
*       byte 0 - y=0 at top/bottom; if non-zero then y=0 at bottom      *
*               otherwise y=0 at top                                    *
*       byte 1 - parameter for set mouse buttons command                *
*       byte 2 - x threshold/scale/delta parameter                      *
*       byte 3 - y threshold/scale/delta parameter                      *
*                                                                       *
*       the following bytes are required for the absolute mode only     *
*                                                                       *
*       byte 4 - xmsb for absolute mouse maximum position               *
*       byte 5 - xlsb for absolute mouse maximum position               *
*       byte 6 - ymsb for absolute mouse maximum position               *
*       byte 7 - ylsb for absolute mouse maximum position               *
*       byte 8 - xmsb for absolute mouse initial position               *
*       byte 9 - xlsb for absolute mouse initial position               *
*       byte a - ymsb for absolute mouse initial position               *
*       byte b - ylsb for absolute mouse initial position               *
*                                                                       *
*************************************************************************

* first we determine if the init is for a absolute, relative or keycode
* mouse action.

initmous:   tst.w     4(sp)                     ; turn mouse off?
            beq.s     im1                       ; yes...disable mouse
            move.l    $a(sp),(msintvec).w       ; init the mouse interrupt vector
            movea.l   6(sp),a3
            cmpi.w    #1,4(sp)                  ; relative mouse request?
            beq.s     im2                       ; yes...
            cmpi.w    #2,4(sp)                  ; absolute mouse request?
            beq.s     im3                       ; yes...
            cmpi.w    #4,4(sp)                  ; keycode mouse request?
            beq.s     im4                       ; yes...
            moveq     #0,d0                     ; error condition returned -- improper request
            rts

im1:        moveq     #$12,d1                   ; disable mouse
            bsr       ikbdput
            move.l    #xbtexit,(msintvec).w     ; re-init the mouse interrupt vector
            bra.s     imexit

im2:        lea       (transbuf).w,a2           ; set transfer buffer pointer
            move.b    #8,(a2)+                  ; set to relative mouse
            move.b    #$b,(a2)+                 ; set relative mouse threshold x,y
            bsr.s     setmouse
            moveq     #6,d3                     ; set length of the string -1 to transfer
            lea       (transbuf).w,a2           ; set transfer buffer pointer
            bsr       ikbdstr                   ; do transfer to ikbd
            bra.s     imexit

im3:        lea       (transbuf).w,a2           ; set transfer buffer pointer
            move.b    #9,(a2)+                  ; set to absolute mouse
            move.b    4(a3),(a2)+               ; set xmsb max
            move.b    5(a3),(a2)+               ; set xlsb max
            move.b    6(a3),(a2)+               ; set ymsb max
            move.b    7(a3),(a2)+               ; set ylsb max
            move.b    #$c,(a2)+                 ; set absolute mouse scale
            bsr.s     setmouse
            move.b    #$e,(a2)+                 ; load initial absolute mouse position
            move.b    #0,(a2)+                  ; filler load
            move.b    8(a3),(a2)+               ; initial xmsb absolute mouse position
            move.b    9(a3),(a2)+               ; initial xlsb absolute mouse position
            move.b    $a(a3),(a2)+              ; initial ymsb absolute mouse position
            move.b    $b(a3),(a2)+              ; initial ylsb absolute mouse position
            moveq     #$10,d3                   ; set length of string -1 to transfer
            lea       (transbuf).w,a2           ; set transfer buffer pointer
            bsr       ikbdstr                   ; do transfer to ikbd
            bra.s     imexit

im4:        lea       (transbuf).w,a2           ; set transfer buffer pointer
            move.b    #$a,(a2)+                 ; set to mouse keycode mode
            bsr.s     setmouse
            moveq     #5,d3                     ; set length of string -1 to transfer
            lea       (transbuf).w,a2           ; set transfer buffer pointer
            bsr       ikbdstr                   ; do transfer to ikbd
imexit:     moveq     #-1,d0                    ; set to true to indicate good init
            rts


setmouse:   move.b    2(a3),(a2)+               ; set x threshold/scale/delta
            move.b    3(a3),(a2)+               ; set y threshold/scale/delta
            moveq     #$10,d1                   ; setup to determine if top/bottom
            sub.b     (a3),d1                   ; set y=0 at ?
            move.b    d1,(a2)+
            move.b    #7,(a2)+                  ; set mouse button action
            move.b    1(a3),(a2)+               ; mouse button parameter
            rts


*************************************************************************
*                                                                       *
*               EXTENDED RBP BIOS TIMER INIT CALL                       *
*                                                                       *
*       entry:                                                          *
*                                                                       *
*       void    xbtimer(id,control,data,intvec)                         *
*       word    id,control,data                                         *
*       long    intvec                                                  *
*                                                                       *
*               intvec - timer interrupt vector                         *
*               control - timer's control setting                       *
*               data - timer's data register setting                    *
*               id - timer id   a-0, b-1, c-2, d-3                      *
*                                                                       *
*       Special Note:                                                   *
*                                                                       *
*       In the interest of preserving as many features for the user     *
*       in the future, timer A should be reserved for the end-user      *
*       or independent software vendor's application program.  System   *
*       software or those application needing just a "tick" should      *
*       constrain themselves to timer C, which is adequate for delay    *
*       and other timing uses.  Future hardware may or may not bring    *
*       out the timer A input line out...giving software developers     *
*       another useful aspect of the machine to utilize.                *
*                                                                       *
*       The recommended usage of the timers is as follows:              *
*                                                                       *
*       Timer A - Reserved for end-users and stand-alone applications.  *
*       Timer B - Reserved for screen graphics, primarily.              *
*       Timer C - Reserved for system timing (GSX,GEM,DESKTOP,ET.AL).   *
*       Timer D - Reserved for baud rate control of RS-232 port,        *
*                the interrupt vector is available to anyone.           *
*                                                                       *
*************************************************************************

xbtimer:    moveq     #0,d0
            moveq     #0,d1
            moveq     #0,d2
            move.w    4(sp),d0
            move.w    6(sp),d1
            move.w    8(sp),d2
            bsr       settimer                  ; setup the timer
            tst.l     $a(sp)                    ; if >$7fffffff then skip and exit
            bmi.s     xbtexit
            movea.l   $a(sp),a2                 ; setup for initint call
            moveq     #0,d1                     ; clear long
            lea       xbtim(pc),a1              ; point to timer -> interrupt # translation tab
            andi.l    #$ff,d0                   ; mask off the highest three bytes in register
            move.b    (a1,d0.w),d0              ; setup for initint call
            bsr       initint
xbtexit:    rts

xbtim:      DC.B      $0d,$08,$05,$04

*************************************************************************
*                                                                       *
*               KEYBOARD TRANSLATION TABLE CHANGE CALL                  *
*                                                                       *
*       entry:                                                          *
*                                                                       *
*       long    keytrans(unshift,shift,capslock)                        *
*       long    unshift,shift,capslock                                  *
*                                                                       *
*               -1 signifies no change to vector                        *
*                                                                       *
*       exit:                                                           *
*               d0.l - returns pointer to beginning of                  *
*                       key translation address pointers                *
*               order of pointers is:                                   *
*               unshifted,shifted,caps-locked                           *
*               Note:  buffer space for each table should $80!!         *
*                                                                       *
*************************************************************************

keytrans:   tst.l     4(sp)
            bmi.s     kt1
            move.l    4(sp),(skeytran).w
kt1:        tst.l     8(sp)
            bmi.s     kt2
            move.l    8(sp),(skeyshif).w
kt2:        tst.l     $c(sp)
            bmi.s     kt3
            move.l    $c(sp),(skeycl).w
kt3:        move.l    #skeytran,d0
            rts


*************************************************************************
*                                                                       *
*               RESTORE BIOS KEYBOARD TRANSLATION TABLE                 *
*                                                                       *
*       entry:                                                          *
*                                                                       *
*       void    bioskeys()                                              *
*                                                                       *
*                                                                       *
*************************************************************************

bioskeys:   move.l    #gskeytran,(skeytran).w
            move.l    #gskeyshif,(skeyshif).w
            move.l    #gskeycl,(skeycl).w
IF !ROM_TOS306
            clr.b     (keyrep).w
ENDIF
            rts

*************************************************************************
*                                                                       *
*               RETURN IKBD SUBSYSTEM INTERRUPT TABLE POINTER           *
*                                                                       *
*       entry:                                                          *
*                                                                       *
*       void    dosound(ptr)                                            *
*       long    ptr     ;points to start of sound interpreter table     *
*                                                                       *
*                                                                       *
*************************************************************************

dosound:    move.l    (cursnd).w,d0             ; return current status in D0.L
            move.l    4(sp),d1                  ; if new ptr < 0, then just return
            bmi.s     ds_r                      ; (invalid ptr, so return)
            move.l    d1,(cursnd).w             ; setup new sound ptr
            clr.b     (timer).w                 ; zap sound timer register
ds_r:       rts

*************************************************************************
*                                                                       *
*               SET/RETURN PRINTER CONFIGURATION WORD                   *
*                                                                       *
*       entry:                                                          *
*                                                                       *
*       word    setptr(pconfig)                                         *
*       word    pconfig ;sets/gets printer information word             *
*                                                                       *
*                                                                       *
*************************************************************************

setprt:     move.w    (pconfig).w,d0            ; get current config word before we change it
            tst.w     4(sp)                     ; see if we don't change the word
            bmi.s     nosetp                    ; don't set printer word
            move.w    4(sp),(pconfig).w         ; set printer config word
nosetp:     rts

*************************************************************************
*                                                                       *
*               SET/RETURN KEY REPEAT VALUES                            *
*                                                                       *
*       entry:                                                          *
*                                                                       *
*       word    kbrate(initial,repeat)                                  *
*       word    initial,repeat                                          *
*                                                                       *
*       initial determines the number of 50 hz cycles to wait before    *
*       a keyrepeat is to commence.  repeat determines the interval     *
*       between keyrepeats after the initial pause.                     *
*                                                                       *
*************************************************************************

kbrate:     move.w    (cdelay1).w,d0            ; get current initial/repeat values
            tst.w     4(sp)                     ; see if we don't change the word
            bmi.s     kbrate1                   ; don't set key repeat values
            move.w    4(sp),d1                  ; set key repeat values
            move.b    d1,(cdelay1).w            ; set initial delay
            tst.w     6(sp)                     ; see if we don't change the word
            bmi.s     kbrate1                   ; don't set key repeat values
            move.w    6(sp),d1                  ; set key repeat values
            move.b    d1,(cdelay2).w            ; set subsequent delay
kbrate1:    rts


*************************************************************************
*                                                                       *
*               RETURN POINTER TO IKBD/MIDI INTERRUPT VECTORS           *
*                                                                       *
*       entry:                                                          *
*                                                                       *
*       long    ikbdvecs()                                              *
*               returns a pointer to the midi interrupt vector and      *
*               ikbd subsystem interrupt vector table.  the table       *
*               structure is as follows:                                *
*                                                                       *
*       midivec         ds.l    1       ;midi interrupt handler vector  *
*       vkbderr         ds.l    1       ;keyboard error handler address *
*       vmiderr         ds.l    1       ;midi error handler address     *
*       statintvec      ds.l    1       ;ikbd status interrupt vector   *
*       msintvec        ds.l    1       ;mouse interrupt vector         *
*       clkintvec       ds.l    1       ;realtime clk interrupt vector  *
*       joyintvec       ds.l    1       ;joystick interrupt vector      *
*                                                                       *
*       note:   msintvec is modified via the initmouse system function  *
*               call.  since gem uses this vector, modifying it can be  *
*               fatal while running under gem.  clkintvec is used by    *
*               gemdos.  its pre-inited vector must be restored for     *
*               proper gemdos operation.  Caveat hacker!                *
*                                                                       *
*                                                                       *
*************************************************************************

ikbdvecs:   move.l    #midivec,d0
            rts

*************************************************************************
*                                                                       *
*       C Timer interrupt routine to process the PSG sound table        *
*                                                                       *
*************************************************************************
*+ (lmd)
* timercint - timer c interrupt handler
* divide 200 Hz interrupt frequency to 50 hz, and do:
*       sound handler processing
*       key-repeat processing;
*       control-g bell and keyclick if enabled via sound handler
*       system timer-tick handoff
*       updates:        tc_rot (every tick)
*
*       imports:        etv_timer (timer handoff vector)
*                       _timr_ms (timer calibration value)
*
*-
timercint:  addq.l    #1,(_hz_200).l            ; increment raw tick counter
            rol       (tc_rot).l                ; rotate divisor bits
            bpl.s     t_punt                    ; if not 4th interrupt, then return
            movem.l   d0-d7/a0-a6,-(sp)

            bsr.s     sndirq                    ; process sounds

            btst      #1,(conterm).w            ; check for key repeat enabled
            beq.s     krexit                    ; not enabled

* process for repeat key function first because it can affect the sound
* table if enabled and the user is 'using'...

            tst.b     (keyrep).w
            beq.s     krexit
            tst.b     (keydelay1).w
            beq.s     kr1
            subq.b    #1,(keydelay1).w
            bne.s     krexit
kr1:        subq.b    #1,(keydelay2).w
            bne.s     krexit
            move.b    (cdelay2).w,(keydelay2).w
            move.b    (keyrep).w,d0
            lea       (kbufrec).w,a0
            bsr       ari16                     ; repeat key stroke and stuff into buffer

*+ (lmd)
* Call system timer vector
* (first guy in the system daisy-chain)
*
*-
krexit:     move.w    (_timr_ms).w,-(sp)        ; push #ms/tick
            movea.l   (etv_timer).w,a0          ; get vector
            jsr       (a0)                      ; call it
            addq.w    #2,sp                     ; cleanup stack
tick1:      movem.l   (sp)+,d0-d7/a0-a6
t_punt:     move.b    #$df,(isrb).w             ; clear the interrupt channel
            rte

*********************************************************
*
*  Quick & dirty sound stuff
*
*
*  Programmed by Dave Staugas
*                14 Mar 1985
*
*
*
*
*********************************************************
*
*
*
*
*  To start a sound, load the 32-bit address of the
*                       byte stream for that sound in 32-bit
*                       "cursnd", & zero the 8-bit "timer"
*
*
*
*
*   Sound interrupt routine
*   Called from timer C irq
*
sndirq:     movem.l   d0-d1/a0,-(sp)
            move.l    (cursnd).w,d0             ; get current sound ptr
            beq.s     snd1                      ; br to exit if zero, inactive
            movea.l   d0,a0                     ; ptr to a0
            move.b    (timer).w,d0              ; check delay timer
            beq.s     snd3                      ; br over delay timer update if not on
*
            subq.b    #1,d0                     ; tick off delay timer
            move.b    d0,(timer).w              ; save new
            bra.s     snd1                      ; skip sound update this time

snd3:       move.b    (a0)+,d0                  ; pick up next sound command
            bmi.s     snd2                      ; if minus, go do special
*
            move.b    d0,(psgsel).w             ; else, register load command--select this
            cmpi.b    #7,d0                     ; reg. 7 selected?
            bne.s     sn1                       ; br if no
*
            move.b    (a0)+,d1                  ; get data to write to reg 7
            andi.b    #$3f,d1                   ; always leave i/o port settings alone
            move.b    (psgsel).w,d0             ; get mixer contents
            andi.b    #$c0,d0                   ; mask off non-useful info...
            or.b      d1,d0                     ; generate new setting
            move.b    d0,(psgwr).w              ; write data
            bra.s     snd3                      ; go for next command

sn1:        move.b    (a0)+,(psgwr).w           ; write next byte as data directly to reg
            bra.s     snd3                      ; go for next command
*
*  special case command
*
snd2:       addq.b    #1,d0                     ; was command 255?
            bpl.s     snd5                      ; br if yes--set delay timer
*
            cmpi.b    #$81,d0                   ; was command 128 (before increment)
            bne.s     snd6                      ; br if not
*
*  command 128
*
            move.b    (a0)+,(auxd).w            ; 128--set aux data from next byte in stream
            bra.s     snd3                      ; go for next command
*
*  command > 128
*
snd6:       cmpi.b    #$82,d0                   ; command greater than 129
            bne.s     snd5                      ; br if yes--must be set timer
*
*  command 129
*
            move.b    (a0)+,(psgsel).w          ; 129--select register
            move.b    (a0)+,d0                  ; get increment step (signed)
            add.b     d0,(auxd).w               ; add to aux data
            move.b    (a0)+,d0                  ; terminating value
            move.b    (auxd).w,(psgwr).w        ; load reg from data in auxd
            cmp.b     (auxd).w,d0               ; reached end of cycle?
            beq.s     snd4                      ; br if so
*
*  still within loop, reset sound pointer to iterate for next irq
*
            subq.w    #4,a0                     ; back up sound ptr to repeat this command
            bra.s     snd4                      ; update ptr & exit
*
*  set delay timer
*
snd5:       move.b    (a0)+,(timer).w           ; set delay timer from next byter in stream
            bne.s     snd4                      ; if non-zero real delay here
            movea.w   #0,a0                     ; else, sound terminator--set ptr to null

snd4:       move.l    a0,(cursnd).w             ; update sound ptr
snd1:       movem.l   (sp)+,d0-d1/a0            ; pop stack & exit
            rts


*
* sound data...
*
*
* format:
*
*      sound data usually is found in byte pairs, the first of which is the command
*      and the second is the argument.  However, some commands take on more than
*      1 argument
*
*      cmd     function        argument(s)
*      00      load reg0       data0
*      01      load reg1       data0
*      02      load reg2       data0
*      03      load reg3       data0
*      04      load reg4       data0
*      05      load reg5       data0
*      06      load reg6       data0
*      07      load reg7       data0   note: b7 & b6 forced set for all data to r...
*      08      load reg8       data0
*      09      load reg9       data0
*      0A      load reg10      data0
*      0B      load reg11      data0
*      0C      load reg12      data0
*      0D      load reg13      data0
*
*
*      80      init temp w/    data0
*
*      81      loop defined
*              by 3 args       data0 as register to load using temp
*                              data1 as increment/decrement (signed) of temp
*                              data2 as loop terminator value of temp
*
*      82-FF   set delay
*              timer           data0 is # of counts till next update
*                                      note: if data0 = 0, sound is terminated
*
*
*
*
*********************************************************
* VT52 emulator calls for the bell sound
*********************************************************
ringbel:    btst      #2,(conterm).w            ; console bell enabled?
            beq.s     rgbel                     ; (no sound)
            movea.l   (bell_hook).l,a0
            jsr       (a0)                      ; go through the bell vector
            rts


keybellsnd: move.l    #bellsnd,(cursnd).w       ; sound data for console bell
            move.b    #0,(timer).w              ; enable sound timer
rgbel:      rts


*+
* flopini - initialize floppies
* Passed (on the stack):
*        $c(sp) devno
*        $8(sp) ->DSB
*        $4(sp) ->buffer (unused)
*        $0(sp) return address
*
* Returns:       EQ if initialization succeeded (drive attached).
*                NE if initialization failed (no drive attached).
*-
_flopini:   clr.l     (frbufcookie).l
            lea       (dsb0).l,a1               ; get ptr to correct DSB
            tst.w     $c(sp)
            beq.s     fi_1
            lea       (dsb1).l,a1
fi_1:       move.w    (seekrate).l,6(a1)        ; setup default seek rate
            move.w    #3,4(a1)                  ; default: HD mode
            moveq     #-1,d0                    ; (default error)
            clr.w     2(a1)                     ; fake clean drive
            clr.w     (frbufscnt).l
IF !ROM_TOS306
            move.w    #$ff00,2(a1)              ; default = recal drive (it's dirty)
ENDIF
IF ROM_STBOOK
            bsr.s     checkfdc
            beq.s     fi_ndrv
ENDIF
            bsr       floplock                  ; setup parameters
            bsr       select                    ; select drive an side
IF ROM_TOS306
            move.w    #$ff00,2(a1)              ; default = recal drive (it's dirty)
ENDIF
            bsr       restore                   ; attempt restore
            beq.s     fi_ok                     ; (quick exit if that won)
            moveq     #10,d7                    ; attempt seek to track 10
            bsr       hseek1                    ; (hard seek to 'd7')
            bne.s     fi_nok                    ; (failed: drive unusable)
            bsr       restore                   ; attempt restore after seek
fi_ok:      beq       flopok                    ; return OK (on win)
fi_nok:     bra       flopfail                  ; return failure
IF ROM_STBOOK
fi_ndrv:    move.w    #$0202,(diskmode).w       ; both drives are "unsure"
            moveq     #0,d0
            tst.w     (_nflops).w               ; any floppy drives installed?
            beq.s     fi_ret                    ; (no, return NO_ERROR)
            moveq     #-15,d0                   ; return "unknown device"
fi_ret:     rts

drvnrdy:    moveq     #-2,d0                    ; return DRIVE_NOT_READY
            rts
ENDIF

IF ROM_TOS306
            move.l    a0,(busexception).w
            movea.l   a1,sp
            movem.l   (sp)+,d7/a6
drvnrdy:    moveq     #-15,d0
            rts
ENDIF

*+
* checkfdc - check if the FDC track/sector register are available
* Returns:      EQ, if the FDC is not available
*               NE, if the FDC is available
*-
IF ROM_STBOOK
checkfdc:   addq.w    #1,(flock).w              ; lock floppies (allow 'recursion')
            movem.l   d0-d7/a6,-(sp)
            lea       (fifo).w,a6
            move.w    #$82,(a6)                 ; setup 1770 track register
            moveq     #0,d7
            bsr       wdiskctl
            move.w    #$84,(a6)                 ; load 1770 sector register
            moveq     #-1,d7
            bsr       wdiskctl
            move.w    #$82,(a6)                 ; read 1770 track register
            bsr       rdiskctl
            move.b    d0,d1                     ; not equal to 0?
            bne.s     checkfdc2
            move.w    #$84,(a6)                 ; read 1770 sector register
            bsr       rdiskctl
            subq.w    #1,(flock).w              ; unlock floppies
            tst.b     d1                        ; track register wrong?
            bne.s     checkfdc2
            cmp.b     #$ff,d0                   ; sector register wrong?
checkfdc2:  movem.l   (sp)+,d0-d7/a6
            eori      #4,sr                     ; flip Z bit (return NE if bit is zero)
            rts
ENDIF

*+
* floprd - read sector from floppy
* Passed (on the stack):
*       $14(sp) count
*       $12(sp) sideno
*       $10(sp) trackno
*        $e(sp) sectno
*        $c(sp) devno
*        $8(sp) ->DSB
*        $4(sp) ->buffer
*        $0(sp) return address
*
* Returns       EQ, the read won (on all sectors),
*               NE, the read failed (on some sector).
*-
_floprd:
IF ROM_STBOOK
            bsr.s     checkfdc
            beq.s     drvnrdy
ENDIF
IF ROM_TOS306
            tst.w     (_nflops).w               ; any floppy drives installed?
            beq.s     drvnrdy                   ; (no, return "unknown device")
ENDIF
            bsr       change                    ; test for disk change
            moveq     #-11,d0                   ; set default error#
            bsr       frbcheckrd
            bsr       floplock                  ; lock floppies, setup parameters
frd1:       bsr       select                    ; select drive, setup registers
            bsr       go2track                  ; seek appropriate track
            bcs       flopfail
            bne       frde                      ; retry on seek failure
frd1l:      move.w    #-1,(curr_err).l          ; set general error#
            move.w    #$190,(a6)                ; toggle DMA data direction,
            tst.b     (gpip).w
            tst.b     (gpip).w                  ; delay for 1 microsec
            tst.b     (gpip).w                  ; this amounts to 16 16Mhz clocks
            tst.b     (gpip).w
            move.w    #$90,(a6)                 ; leave hardware in READ state
            tst.b     (gpip).w
            tst.b     (gpip).w                  ; delay for 1 microsec
            tst.b     (gpip).w                  ; this amounts to 16 16Mhz clocks
            tst.b     (gpip).w
            move.w    #1,(diskctl).w            ; set sector count register
            move.w    #$80,(a6)                 ; startup 1770 "read sector" command
            move.w    #$80,d7                   ; (read single)
            bsr       wdiskctl
            move.l    (_hz_200).w,d7
            add.l     #300,d7                   ; set timeout timer

*--- Wait for read completion:
frd2:       btst      #5,(gpip).w               ; 1770 done yet?
            beq.s     frd4                      ; (yes)
            cmp.l     (_hz_200).w,d7            ; timeout reached?
            bhi.s     frd2                      ; (punt on timeout)

*---- check status after read
            move.w    #-2,(curr_err).w          ; set "timeout" error
            bsr       reset1770                 ; (clobber 1770)
            bra.s     frde                      ; (go retry)

*--- check status after read:
frd4:       move.w    #$90,(a6)                 ; examine DMA status register
            move.w    (a6),d0
            btst      #0,d0                     ; bit zero inidcates DMA ERROR
            beq.s     frde                      ; (when its zero -- retry)

            move.w    #$80,(a6)                 ; exeamine 1770 status register
            bsr       rdiskctl
            and.b     #$1c,d0                   ; check for RNF, checksum, lost-data
            bne.s     frd4                      ; (bail on error)
            move.w    #2,(retrycnt).w           ; reset retry count for next sector
            addq.w    #1,(csect).w              ; advance sector #
            addi.l    #$200,(cdma).w            ; advance buffer by 512 bytes
            subq.w    #1,(ccount).w             ; decrement sector count
            beq       flopok                    ; (done)
            bsr       select1
            bra       frd1l

frd4:       bsr.s     err_bits                  ; set error# from 1770 bits
frde:       cmpi.w    #1,(retrycnt).w           ; are we on the "middlemost" retry?
            bne.s     frd5
frde1:      bsr       reseek                    ; yes, home and reseek the head
frd5:       subq.w    #1,(retrycnt).w           ; drop retry count
            bpl       frd1                      ; (continue of any retries left)
            bra       flopfail                  ; fail when we run out of patience

*+
* err_bits - set "curr_err" according to 1770 error status
* Passed:       d0 = 1770 status
*
* Returns:      curr_err, containing current error number
*
* Uses:         d1
*-
err_bits:   moveq     #-13,d1                   ; write protect?
            btst      #6,d0
            bne.s     eb1
            moveq     #-8,d1                    ; record-not-found?
            btst      #4,d0
            bne.s     eb1
            moveq     #-4,d1                    ; CRC error?
            btst      #3,d0
            bne.s     eb1
            move.w    (def_error).w,d1          ; use default error#
eb1:        move.w    d1,(curr_err).w           ; set current error number & return
            rts

*+
* flopwr - write sector to floppy
* Passed (on the stack):
*       $14(sp) count
*       $12(sp) sideno
*       $10(sp) trackno
*        $e(sp) sectno
*        $c(sp) devno
*        $8(sp) ->DSB
*        $4(sp) ->buffer (unused)
*        $0(sp) return address
*
* Returns:      EQ, the write won (on all sectors),
*               NE, the write failed (on some sectors).
*-
_flopwr:
IF ROM_STBOOK
            bsr       checkfdc
            beq       drvnrdy
ENDIF
IF ROM_TOS306
            tst.w     (_nflops).w               ; any floppy drives installed?
            beq       drvnrdy                   ; (no, return "unknown device")
ENDIF
            bsr       change                    ; check for disk swap
            moveq     #-10,d0                   ; set default error number
            bsr       frbcheckwr
            bsr       floplock                  ; lock floppies

*+
* If the boot sector is written to,
* set the media change mode to "unsure".
* (Kludge, kludge, kludge....)
*-
            move.w    (csect).w,d0              ; sector 1
            subq.w    #1,d0
            or.w      (ctrack).w,d0             ; track 0
            or.w      (cside).w,d0              ; side 0
            bne.s     fwr1                      ; if not boot sector, then OK
            moveq     #2,d0                     ; set media change mode to unsure
            bsr       setdmode                  ; (boy, is this /ugly/)

fwr1:       bsr       select                    ; select drive
            bsr       go2track                  ; seek
            bcs       flopfail
            bne       fwre1                     ; (retry on seek failure)
fwr1a:      move.w    #-1,(curr_err).w          ; set general error#
            move.w    #$90,(a6)                 ; toggle DMA chip to clear status
            tst.b     (gpip).w
            tst.b     (gpip).w                  ; delay for 1 microsec
            tst.b     (gpip).w                  ; this amounts to 16 16Mhz clocks
            tst.b     (gpip).w
            move.w    #$190,(a6)                ; leave in WRITE mode
            tst.b     (gpip).w
            tst.b     (gpip).w                  ; delay for 1 microsec
            tst.b     (gpip).w                  ; this amounts to 16 16Mhz clocks
            tst.b     (gpip).w
            move.w    #1,d7                     ; load sector-count register
            bsr       wdiskctl
            move.w    #$180,(a6)                ; load "WRITE SECTOR" command
            move.w    #$a0,d7                   ; into 1770 cmdreg
            bsr       wdiskctl
            move.l    (_hz_200).w,d7
            add.l     #300,d7                   ; d7 = timeout timer

fwr2:       btst      #5,(gpip).w               ; done yet?
            beq.s     fwr4                      ; (yes, check status)
            cmp.l     (_hz_200).w,d7            ; timeout reached?
            bhi.s     fwr2                      ; (still tickin')
            bsr       reset1770                 ; timed out -- reset 1770
            bra.s     fwre                      ; and retry

fwr4:       move.w    #$180,(a6)                ; get 1770 status
            bsr       rdiskctl
            bsr       err_bits                  ; compute 1770 error bits
            btst      #6,d0                     ; if write protected, don't retry
            bne       flopfail                  ; (can't write, so punt)
            and.b     #$5c,d0                   ; check WriteProt+RecNtFnd+CHKSUM+LostD
            bne.s     fwre                      ; retry on error

            move.w    #2,(retrycnt).w           ; reset retry count
            addq.w    #1,(csect).w              ; bump sector number
            addi.l    #$200,(cdma).w            ; add DMA pointer for next sector
            subq.w    #1,(ccount).w             ; if(!--count) return OK;
            beq       flopok
            bsr       select1                   ; setup sector#, DMA pointer
            bra       fwr1a                     ; write next (no seek)

fwre:       cmpi.w    #1,(retrycnt).w           ; re-seek head in "middle" retry
            bne.s     fwr5                      ; (not middle retry)
fwre1:      bsr       reseek                    ; home head and seek
fwr5:       subq.w    #1,(retrycnt).w           ; decrement retry count
            bpl       fwr1                      ; loop if there's still hope
            bra       flopfail                  ; otherwise return error status
*+
* _flopfmt - format a track
* Passed (on the stack):
*       $1a(sp) initial sector data
*       $16(sp) magic number
*       $14(sp) interleave
*       $12(sp) side
*       $10(sp) track
*        $e(sp) spt
*        $c(sp) devno
*        $8(sp) pointer to state block
*        $4(sp) dma address
*        $0(sp) [return]
*
* Returns:      EQ: track successfully written.  Zero.W-terminated list of
*               bad sectors left in buffer (they might /all/ be bad.)
*
*               NE: could not write track (write-protected, drive failure,
*               or something catastrophic happened).
*-
_flopfmt:   cmpi.l    #$87654321,$16(sp)        ; check for magic# on stack
            bne       flopfail                  ; no magic, so we just saved the world
IF ROM_STBOOK
            bsr       checkfdc
            beq       drvnrdy
ENDIF
IF ROM_TOS306
            tst.w     (_nflops).w               ; any floppy drives installed?
            beq       drvnrdy                   ; (no, return "unknown device")
ENDIF
            bsr       change                    ; check for disk flip
            moveq     #-10,d0                   ; set default error number
            bsr       frbcheckfmt
            bsr       floplock
            bsr       select                    ; select drive and side
            move.w    $e(sp),(spt).w            ; save sectors-per-track
            move.w    $14(sp),(interlv).w       ; save interleave factor
            move.w    $1a(sp),(virgin).w        ; save initial sector data
            move.l    8(sp),(fmtstateblk).w

*--- put drive into "changed" mode
            moveq     #2,d0                     ; d0 = "CHANGED"
            bsr       setdmode                  ; set media change mode
            moveq     #3,d0                     ; HD mode
            cmpi.w    #$d,(spt).w               ; 13 sectors or more require HD mode
            bcc.s     flopfmt1
            moveq     #0,d0                     ; SD mode
flopfmt1:
IF ROM_STBOOK
            bsr       hdselect                  ; select SD/HD mode of floppy
ELSE
IF ROM_TOS206
            tst.b     (STEFlag).l               ; no STE hardware available?
            bne.s     flopfmt2                  ; (correct)
ENDIF
            move.w    d0,(fdccs).w
flopfmt2:
ENDIF
            move.w    d0,4(a1)

*--- seek to track (hard seek):
            bsr       hseek                     ; hard seek to 'ctrack'
            bne       flopfail                  ; (return error on seek failure)
            move.w    (ctrack).w,2(a1)          ; record current track#

*--- format track, then verify it:
            move.w    #-1,(curr_err).w          ; vanilla error mode
            bsr.s     fmtrack                   ; format track
            bne       flopfail                  ; (return error on seek failure)
            move.w    (spt).w,(ccount).w        ; set number of sectors to verify
            move.w    #1,(csect).w              ; starting sector# = 1
            bsr       verify1                   ; verify sectors

*--- if there are any bad sectors, return /that/ error...
            movea.l   (cdma).w,a2               ; a2 -> bad sector list
            tst.w     (a2)                      ; any bad sectors?
            beq       flopok                    ; no -- return OK
            move.w    #-16,(curr_err).w         ; set error number
            bra       flopfail                  ; return error

*+
* fmtrack - format a track
* Passed:       variables setup by _flopfmt
* Returns:      NE on failure, EQ on success
* Uses:         almost everything
* Called-by:    _flopfmt
*
*-
fmtrack:    move.w    #-10,(def_error).w        ; set default error number
            movea.l   (cdma).w,a2               ; a2 -> prototyping area
            movea.l   (fmtstateblk).w,a3
            moveq     #120-1,d1                 ; 120 x $4e (track leadin)
            cmpi.w    #$d,(spt).w
            bcc.s     fmtrack1
            moveq     #60-1,d1                  ; 60 x $4e (track leadin)
fmtrack1:   moveq     #$4e,d0
            bsr       wmult

            clr.w     d3                        ; interleave index = table start
            tst.w     (interlv).w               ; interleave < 0
            bmi       fmtrack2                  ; use custom interleave table ->
            moveq     #1,d3                     ; first sector = 1

*---- address mark
ot3:        move.w    d3,d4                     ; d4 = starting sector (this pass)
ot1:        moveq     #12-1,d1                  ; 12 x $00
            clr.b     d0
            bsr       wmult
            moveq     #3-1,d1                   ; 3 x $f5
            moveq     #$f5,d0
            bsr       wmult
            move.b    #$fe,(a2)+                ; $fe -- address mark intro
            move.b    (ctrack+1).w,(a2)+        ; track#
            move.b    (cside+1).w,(a2)+         ; side#
            move.b    d4,(a2)+                  ; sector#
            move.b    #2,(a2)+                  ; sector size (512)
            move.b    #$f7,(a2)+                ; write checksum

*--- gap between AM and data:
            moveq     #22-1,d1                  ; 22 x $4e
            moveq     #$4e,d0
            bsr       wmult
            moveq     #12-1,d1                  ; 12 x $00
            clr.b     d0
            bsr       wmult
            moveq     #3-1,d1                   ; 3 x $f5
            moveq     #$f5,d0
            bsr       wmult

*--- data block:
            move.b    #$fb,(a2)+                ; $fb -- data intro
            move.w    #256-1,d1                 ; 256 x virgin.W (initial sector data)
ot2:        move.b    (virgin).w,(a2)+          ; copy high byte
            move.b    (virgin+1).w,(a2)+        ; copy low byte
            dbra      d1,ot2                    ; fill 512 bytes
            move.b    #$f7,(a2)+                ; $f7 -- write checksum
            moveq     #40-1,d1                  ; 40 x $4e
            moveq     #$4e,d0
            bsr       wmult

            tst.w     (interlv).w               ; interleave < 0
            bmi       fmtrack2                  ; use custom interleave table ->
            add.w     (interlv).w,d4            ; bump sector#
            cmp.w     (spt).w,d4                ; if(d4 <= spt) then_continue;
            ble.s     ot1                       ; proto more sectors this pass
            addq.w    #1,d3                     ; bump pass start count
            cmp.w     (interlv).w,d3            ; if(d3 <= interlv) then_continue;
            ble.s     ot3

*--- end-of-track
ot5:        move.w    #2800,d1                  ; 2801 x $4e -- end of track trailer
            cmpi.w    #$d,(spt).w
            bcc.s     ot4
            move.w    #1400,d1                  ; 1401 x $4e -- end of track trailer
ot4:        moveq     #$4e,d0
            bsr       wmult

*--- setup to write the track:
            move.b    (cdma+3).w,(dmalow).w     ; load dma pointer
            move.b    (cdma+2).w,(dmamid).w
            move.b    (cdma+1).w,(dmahigh).w
            move.w    #$90,(a6)                 ; toggle R/W flag and
            tst.b     (gpip).w
            tst.b     (gpip).w                  ; delay for 1 microsec
            tst.b     (gpip).w                  ; this amounts to 16 16Mhz clocks
            tst.b     (gpip).w
            move.w    #$190,(a6)                ; select sector-count register
            tst.b     (gpip).w
            tst.b     (gpip).w                  ; delay for 1 microsec
            tst.b     (gpip).w                  ; this amounts to 16 16Mhz clocks
            tst.b     (gpip).w
            moveq     #$60,d7                   ; (absurd sector count)
            bsr       wdiskctl
            move.w    #$180,(a6)                ; select 1770 cmd register
            move.w    #$f0,d7                   ; write format_track command
            bsr       wdiskctl
            move.l    (_hz_200).w,d7
            add.l     #300,d7                   ; d7 = timeout timer

*--- wait for 1770 complete:
otw1:       btst      #5,(gpip).w               ; is 1770 done?
            beq.s     otw2                      ; (yes)
            cmp.l     (_hz_200).w,d7            ; timeout reached?
            bhi.s     otw1                      ; (still tickin')
            bsr       reset1770                 ; timed out -- reset 1770
oterr:      moveq     #1,d7                     ; return NE (error status)
            rts

fmtrack2:   cmp.w     (spt).w,d3                ; last sector reached?
            beq       ot5                       ; yes -> end of track
            move.w    d3,d6
            add.w     d6,d6
            move.w    (a3,d6.w),d4              ; pick new sector number from the table
            addq.w    #1,d3                     ; increment interleave index
            bra       ot1

*--- see if the write-track won:
otw2:       move.w    #$190,(a6)                ; check DMA status bit
            move.w    (a6),d0
            btst      #0,d0                     ; if its zero, there was a DMA error
            beq.s     oterr                     ; (so return NE)
            move.w    #$180,(a6)                ; get 1770 status
            bsr       rdiskctl
            bsr       err_bits                  ; set 1770 error bits
            and.b     #$44,d0                   ; check for writeProtect & lostData
            rts                                 ; return NE on 1770 error

*----- write 'D1+1' copies of D0.B into A2, A2+1, ...
wmult:      move.b    d0,(a2)+                  ; record byte in proto buffer
            dbra      d1,wmult                  ; (do it again)
            rts


*+
* _flopver - verify sectors on a track
*       $14(sp) count
*       $12(sp) sideno
*       $10(sp) trackno
*        $e(sp) sectno
*        $c(sp) devno
*        $8(sp) ->DSB
*        $4(sp) ->buffer (at least 1K long)
*        $0(sp) return address
*
* Returns:      NULL.W-terminated list of bad sectors in the buffer if D0 == 0,
*               OR some kind of error (D0 < 0).
*
*-
_flopver:
IF ROM_STBOOK
            bsr       checkfdc
            beq       drvnrdy
ENDIF
IF ROM_TOS306
            tst.w     (_nflops).w               ; any floppy drives installed?
            beq       drvnrdy                   ; (no, return "unknown device")
ENDIF
            bsr       change                    ; hack disk change
            moveq     #-11,d0                   ; set default error#
            bsr       frbcheckfmt
            bsr       floplock                  ; lock floppies, setup parameters
            bsr       select                    ; select floppy
            bsr       go2track                  ; go to track
            bne       flopfail                  ; (punt if that fails)
            bsr.s     verify1                   ; verify some sectors
            bra       flopok                    ; return "OK"

*+
* verify1 - verify sectors on a single track
* Passed:       csect = starting sector#
*               ccount = number of sectors to verify
*               cdma -> 1K buffer (at least)
*
* Returns:      NULL.W-terminated list of bad sectors (in the buffer)
*               (buffer+$200..buffer+$3ff used as DMA buffer)
*
* Enviroment:   Head seeked to correct track;
*               Drive and side already selected;
*               Motor should be spinning (go2track and fmttrack do this).
*
* Uses:         Almost everything.
*
* Called-by:    _flopfmt, _flopver
*
*-
verify1:    move.w    #-11,(def_error).w        ; set default error number
            movea.l   (cdma).w,a2               ; a2 -> start of bad sector list
            addi.l    #$200,(cdma).w            ; bump buffer up 512 bytes

*--- setup for (next) sector
tvrlp:      move.w    #2,(retrycnt).w           ; init sector-retry count
            move.w    #$84,(a6)                 ; load 1770 sector register
            move.w    (csect).w,d7              ; with 'csect'
            bsr       wdiskctl

*--- setup for sector read
tvr1:       move.b    (cdma+3).w,(dmalow).w     ; load dma pointer
            move.b    (cdma+2).w,(dmamid).w
            move.b    (cdma+1).w,(dmahigh).w
            move.w    #$190,(a6)                ; toggle R/W (leave in W state)
            tst.b     (gpip).w
            tst.b     (gpip).w                  ; delay for 1 microsec
            tst.b     (gpip).w                  ; this amounts to 16 16Mhz clocks
            tst.b     (gpip).w
            move.w    #$90,(a6)
            tst.b     (gpip).w
            tst.b     (gpip).w                  ; delay for 1 microsec
            tst.b     (gpip).w                  ; this amounts to 16 16Mhz clocks
            tst.b     (gpip).w
            move.w    #1,d7                     ; set DMA sector count to 1
            bsr       wdiskctl
            move.w    #$80,(a6)                 ; load 1770 command register
            move.w    #$80,d7                   ; with ReadSector command
            bsr       wdiskctl
            move.l    (_hz_200).w,d7
            add.l     #300,d7                   ; d7 = timeout timer

*--- wait for command completion
tvr2:       btst      #5,(gpip).w               ; test for 1770 done
            beq.s     tvr4                      ; (yes, it completed)
            cmp.l     (_hz_200).w,d7            ; timeout reached?
            bhi.s     tvr2                      ; (still tickin')
            bsr       reset1770                 ; reset controller and return error
            bra.s     tvre

*--- got "done" interrupt, check DMA status:
tvr4:       move.w    #$90,(a6)                 ; read DMA error status
            move.w    (a6),d0
            btst      #0,d0                     ; if DMA_ERROR is zero, then retry
            beq.s     tvre

*--- check 1770 completion status (see if it's happy):
            move.w    #$80,(a6)                 ; read 1770 status register
            bsr       rdiskctl
            bsr       err_bits                  ; set error# from 1770 register
            and.b     #$1c,d0                   ; check for record-not-found, crc-error,
            bne.s     tvre                      ; and lost data; return on error

*--- read next sector (or return if done)
tvr6:       addq.w    #1,(csect).w              ; bump sector count
            subq.w    #1,(ccount).w             ; while(--count) read_another;
            bne       tvrlp
            subi.l    #$200,(cdma).w            ; readjust DMA pointer
            clr.w     (a2)                      ; terminate bad sector list
            rts                                 ; and return EQ


*--- read failure: retry or record bad sector
tvre:       cmpi.w    #1,(retrycnt).w           ; re-seek head?
            bne.s     tvr5                      ; (no)
            bsr       reseek                    ; yes: back to home and then back
tvr5:       subq.w    #1,(retrycnt).w           ; to the current track...
            bpl       tvr1
            move.w    (csect).w,(a2)+           ; record bad sector
            bra.s     tvr6                      ; do next sector

*+
* _flopvbl - floppy vblank handler
* Deselects floppies after the motor stops
*-
_flopvbl:
IF ROM_TOS306
            tst.w     (_nflops).w               ; any floppy drives installed?
            beq       fvblr                     ; (no, return "unknown device")
ENDIF
            lea       (fifo).w,a6               ; a6 -> fifo
            st        (_motoron).w              ; assume motor is on
            tst.w     (flock).w                 ; floppies locked?
            bne.s     fvblr                     ; (yes, so don't touch them)
IF ROM_STBOOK
            bsr       checkfdc
            beq.s     fvblr
ENDIF

*--- write-protect monitor:
            move.l    (_frclock).w,d0           ; check a drive every 8 jiffies
            move.b    d0,d1                     ; (save jiffy count)
            and.b     #7,d1                     ; time yet?
            bne.s     fvbl1                     ; (no)
            move.w    #$80,(a6)                 ; select 1770 command/status register

*--- select drive, record it's WP status:
            lsr.b     #3,d0                     ; use bit 4 as drive# to check
            and.w     #1,d0
            lea       (wpstatus).w,a0
            adda.w    d0,a0
            cmp.w     (_nflops).w,d0
            bne.s     fvbl2
            clr.w     d0
fvbl2:      addq.b    #1,d0
            lsl.b     #1,d0                     ; (magic shift left)
            eori.b    #7,d0                     ; invert select bits, select side 0
            bsr       setporta                  ; set port A (d2 = old bits)
            bsr       rdiskctl                  ; get 1770 status
            move.w    d0,d1
            btst      #6,d1                     ; test Write-Protect status bit
            sne       (a0)                      ; set WP status to $00 or $FF.
            move.b    d2,d0                     ; restore old drive-select bits
            bsr       setporta

fvbl1:      move.w    (wpstatus).w,d0           ; or _wpstatus into _wplatch
            or.w      d0,(wplatch).w            ; (catch any WP transitions)

*--- floppy deselect test
            tst.w     (deselflg).w              ; floppies already deselected?
            bne.s     fvblr1                    ; (yes, so don't do it again)
            move.l    (_hz_200).w,d0
            cmp.l     (fltimeout).l,d0          ; did we hit our 5s timeout?
            bcc.s     fvbl2

            bsr       rdiskctl                  ; read 1770 status register
            btst      #7,d0                     ; is the motor still on?
            bne.s     fvblr                     ; (yes, so don't deselect)
fvbl2:      move.b    #7,d0                     ; deselect both drives
            bsr       setporta                  ; (set bits 0..3 in portA of PSG)
            move.w    #1,(deselflg).w           ; indicate floppies deselected
fvblr1:     clr.w     (_motoron).w              ; indicate motor is OFF
fvblr:      rts                                 ; back to vbl


*+
* floplock - lock floppies and setup floppy parameters
*
* Passed (on the stack):
*       $18(sp) - count.W (sector count)
*       $16(sp) - side.W (side#)
*       $14(sp) - track.W (track#)
*       $12(sp) - sect.W (sector#)
*       $10(sp) - dev.W (device#)
*        $c(sp) - obsolte.L
*         8(sp) - dma.L (dma pointer)
*         4(sp) - ret1.L (caller's return address)
*         0(sp) - ret.L (floplock's return address)
*
* Passed:       D0.W = default error number
*-
floplock:   movem.l   d3-d7/a3-a6,($168a).l     ; save C registers

            lea       (fifo).w,a6               ; a6 -> fifo
            st        (_motoron).w              ; kludge motor state = ON
            move.w    d0,(def_error).w          ; set default error number
            move.w    d0,(curr_err).w           ; set current error number
            move.w    #1,(flock).w              ; tell vbl not to touch floppies
            move.l    8(sp),(cdma).w            ; cdma -> /even/ DMA address
            move.w    $10(sp),(cdev).w          ; save devide# (0 . 1)
            move.w    $12(sp),(csect).w         ; save sector# (1 . 9, usually)
            move.w    $14(sp),(ctrack).w        ; save track# (0 . 39 .  79   )
            move.w    $16(sp),(cside).w         ; save side# (0 . 1)
            move.w    $18(sp),(ccount).w        ; save sector count (1..spt)
            move.w    #2,(retrycnt).w           ; setup retry count

*--- pick a DSB
            lea       (dsb0).w,a1
            tst.w     (cdev).w
            beq.s     flock2
            lea       (dsb1).w,a1

*--- recalibrate drive (if it needs it)
flock2:     tst.w     2(a1)                     ; if (curtrack < 0) recalibrate()
            bpl.s     flockr

            bsr       select                    ; select drive & side
            clr.w     2(a1)                     ; we're optimistic -- assume winnage
            bsr       restore                   ; attempt restore
            beq.s     flockr                    ; (it won)
            moveq     #10,d7                    ; attempt seek to track 10
            bsr       hseek1
            bne.s     flock1                    ; (failed)
            bsr       restore                   ; attempt restore again
            beq.s     flockr                    ; (it won)
flock1:     move.w    #$ff00,2(a1)              ; complete failure (what can we do?)
flockr:     rts

*+
* flopfail - unlock floppies and return error.
*
*-
flopfail:   moveq     #1,d0                     ; disk change mode = unsure
            bsr       setdmode                  ; set media change mode
            move.w    (curr_err).w,d0           ; get current error number
            ext.l     d0                        ; extent to long
            bra.s     unlok1                    ; clobber floppy lock & return

*+
* flopok - unlock floppies and return success status:
*
*-
flopok:     clr.l     d0                        ; return 0 (success)
unlok1:     move.l    d0,-(sp)                  ; (save return value)
IF ROM_TOS306
            bsr       flushCaches
ENDIF
            move.w    #$86,(a6)                 ; force WP to real-time mode
            move.w    2(a1),d7                  ; dest-track = current track
            bsr       wdiskctl
            move.w    #$10,d6                   ; cmd = seek w/o verify
            bsr       flopcmds                  ; do it

            move.l    (_hz_200).l,d0
            add.l     #5*200,d0                 ; 5s floppy deselect timeout
            move.l    d0,(fltimeout).l

            move.w    (cdev).w,d0               ; set last-access time for 'cdev'
            lsl.w     #2,d0
            lea       (_acctim).w,a0
            move.l    (_frclock).w,(a0,d0.w)
            cmpi.w    #1,(_nflops).w            ; if (nflops == 1) set other time, too
            bne.s     unlok2
            move.l    (_frclock).w,4(a0)        ; set last-access time for floppy 1

unlok2:     move.l    (sp)+,d0                  ; restore return value
            movem.l   ($168a).w,d3-d7/a3-a6     ; restore C registers
            clr.w     (flock).l                 ; unlock floppies
            bsr       frbbackcopy               ; if necessary copy FRB buffer back
            rts


*+
* hseek  - seek to 'ctrack' without verify
* hseek1 - seek to 'd7' without verify
* hseek2 - seek to 'd7' without verify, keep current error number
*
* Returns:      NE on seek failure ("cannot happen"?)
*               EQ if seek wins
*
* Uses:         d7, d6, ...
* Jumps-to:     flopcmds
* Called-by:    _flopfmt, _flopinit
*
*-
hseek:      move.w    (ctrack).l,d7             ; dest track = 'ctrack'
hseek1:     move.w    #-6,(curr_err).l          ; possible error = "seek error"
            move.w    #$86,(a6)                 ; write destination track# to data reg
            bsr       wdiskctl
            move.w    #$10,d6                   ; execute "seek" command
            bra       flopcmds                  ; (without verify...)

*+
* reseek - home head, then reseek track
* Returns:      EQ/NE on success/failure
* Falls-into:   go2track
*
*-
reseek:     move.w    #-6,(curr_err).l          ; set "seek error"
IF ROM_STBOOK
            bsr.s     restore                   ; restore head
            bne.s     go2trr                    ; (punt if home fails)
ELSE
            bsr       restore                   ; restore head
            bne       go2trr                    ; (punt if home fails)
ENDIF

            clr.w     2(a1)                     ; current track = 0
            move.w    #$82,(a6)                 ; set "current track" reg on 1770
            clr.w     d7
            bsr       wdiskctl

            move.w    #$86,(a6)                 ; seek out to track five
            move.w    #5,d7
            bsr       wdiskctl                  ; dest track = 5
            move.w    #$10,d6
            bsr.s     flopcmds                  ; seek
            bne.s     go2trr                    ; return error on seek failure
            move.w    #5,2(a1)                  ; set current track#

*+
* go2track - seek proper track
* Passed:       Current floppy parameters (ctrack, et al.)
* Returns:      EQ/NE on success/failure
* Calls:        flopcmds
*-
go2track:   move.w    #1,(fseekrtr).l           ; set seek retry count
go2track1:  move.w    #-6,(curr_err).l          ; set "seek error"
            move.w    #$86,(a6)                 ; set destination track# in
            move.w    (ctrack).w,d7             ; 1770's data register
            bsr       wdiskctl                  ; (write track#)
            moveq     #$14,d6                   ; execute 1770 "seek_with_verify"
            bsr.s     flopcmds                  ; (include seek-rate bits)
            bcs.s     go2trr
            bne.s     go2track2                 ; return error on seek failure
            and.b     #$18,d7                   ; check for RNF, CRC_error, lost_data
            beq.s     go2track3                 ; (exit on no error)
go2track2:  move.w    4(a1),d0
            and.w     #3,d0
            eori.w    #3,d0                     ; toggle between SD/HD mode for a second seek try
IF ROM_STBOOK
            move.w    d0,4(a1)
            bsr       hdselect                  ; select SD/HD mode of floppy
ELIF ROM_TOS206
            move.w    d0,4(a1)
            tst.b     (STEFlag).l               ; no STE hardware available?
            bne.s     go2track2b                ; (correct)
            move.w    d0,(fdccs).w
go2track2b:
ELIF ROM_TOS306
            move.w    d0,(fdccs).w
            move.w    d0,4(a1)
ENDIF
            subq.w    #1,(fseekrtr).l           ; --seek retry count
            bne.s     go2trr                    ; (out of retries -- punt)
            bsr.s     restore                   ; move head to track 00 to reset it
            bra.s     go2track1                 ; try again
go2track3:  move.w    (ctrack).w,2(a1)          ; update current track number
            clr.w     d7                        ; set EQ
go2trr:     rts


*+
* restore - home head
* Passed:       nothing
* Returns:      EQ/NE on success/failure
* Falls-into:   flopcmds
*-
restore:    clr.w     d6                        ; $00 = 1770 "restore" command
            bsr.s     flopcmds                  ; do restore
            bne.s     res_r                     ; punt on timeout
            btst      #2,d7                     ; test TRK00 bit
            eori      #4,ccr                    ; flip Z bit (return NE if bit is zero)
            bne.s     res_r                     ; (punt if didn't win)
            clr.w     2(a1)                     ; set current track#
res_r:      rts


*--- seek rate conversion table for HD mode
* HD is using twice the clock speed (16MHz instead of 8MHz) for the FDC,
* so the FDC has to seek "slower"
dseekrt:    DC.B      $01,$01,$00,$00           ; 6ms and 12ms -> 12ms,  2ms and 3ms -> 6ms


*+
* flopcmds - floppy command (on-in seek speed bits from database)
* Passed:       d6.w = 1770 command
* Sets-up:      seek bits (bits 0 and 1) in d6.w
* Falls-into:   flopcmds
* Returns:      EQ/NE on success/failure
*-
flopcmds:   move.w    6(a1),d0                  ; get floppy's seek rate bits
            and.w     #3,d0                     ; OR into command
            tst.w     4(a1)                     ; SD mode?
            beq.s     flopcmds1                 ; (yes)
            lea       (dseekrt).l,a0            ; use a HD-compatible seek rate table
            move.b    (a0,d0.w),d0
flopcmds1:  or.b      d0,d6
            or.b      (a1),d6                   ; (a1) == 8 for HD, with sets the verify bit in the command
IF ROM_STBOOK
            move.w    4(a1),d0                  ; get SD/HD mode
            bsr.s     hdselect                  ; select SD/HD mode of floppy
ELSE
IF ROM_TOS206
            tst.b     (STEFlag).l               ; no STE hardware available?
            bne.s     flopcmds2                 ; (correct)
ENDIF
            move.w    4(a1),(fdccs).w
flopcmds2:
ENDIF

*+
* flopcmd - execute 1770 command (with timeout)
* Passed:       d6.w = 1770 command
*
* Returns:      EQ/NE on success/failure
*               d7 = 1770 status bits
*
*-
flopcmd:    move.l    (_hz_200).w,d7            ; setup timeout (assume short)
            add.l     #300,d7
            move.w    #$80,(a6)                 ; select 1770 command register
            bsr       rdiskctl                  ; read it to clobber READY status
            btst      #7,d0                     ; is motor on?
            bne.s     flopcm                    ; (yes, keep short timeout)
            move.l    (_hz_200).w,d7            ; extra timeout for motor startup
            add.l     #600,d7
flopcm:     bsr       wdiskct6                  ; write command (in d6)
flopc1:     cmp.l     (_hz_200).w,d7            ; timeout?
            bcs.s     flopcto                   ; (yes, reset and return failure)
            btst      #5,(gpip).w               ; 1770 completion?
            bne.s     flopc1                    ; (not yet, so wait some more)
            bsr       rdiskct7                  ; return EQ + 1770 status in d7
            clr.w     d6
            rts

flopcto:    bsr.s     reset1770                 ; bash controller
            moveq     #0,d6
            subq.w    #1,d6                     ; and return NE
            rts


*+
* hdselect - select HD mode for 1.4MB floppies and SD for 720kd floppies
* Passed:          d0.w SD/HD mode
*-
IF ROM_STBOOK
hdselect:   tst.w     d0                        ; if != 0 then select HD
            bne.s     hdselect2
            clr.b     d0                        ; SD mode
            bra.s     hdselect3
hdselect2:  move.b    #$80,d0                   ; HD mode is bit 7 in PORT A on the sound chip
hdselect3:  move      sr,d2                     ; save status register
            move.b    #14,(psgsel).w            ; select port on GI chip
            move.b    (psgsel).w,d1             ; get current bits
            bclr      #7,d1                     ; clear HD bit
            or.b      d0,d1                     ; or-in our new bit
            move.b    #14,(psgsel).w            ; select port on GI chip
            move.b    d1,(psgwr).w              ; and write 'em back out there
            move      d2,sr                     ; restore status register
            rts
ENDIF

*+
* reset1770 - reset disk controller after a catastrophe
* Passed:       nothing
* Returns:      nothing
* Uses:         d7
*-
reset1770:  move.w    #$80,(a6)                 ; execute 1770 "reset" command
            move.w    #$d0,d7
            bsr       wdiskctl
IF ROM_TOS306
            move.w    d0,-(sp)
            move.w    #$114,d0
            bsr       mfpdelay
            move.w    (sp)+,d0
ELSE
            move.w    #2,d0
r1770a:     move.b    (tcdr).w,d1               ; wait for 1770 to stop convulsing
r1770b:     cmp.b     (tcdr).w,d1
            beq.s     r1770b
            dbra      d0,r1770a
ENDIF
            bsr.s     rdiskct7                  ; return 1770 status in d7
            rts


*+
* select - setup drive select, 1770 and DMA registers
* Passed:       cside, cdev
* Returns:      appropriate drive and side select
*-
select:     clr.w     (deselflg).w              ; floppies NOT deselected
            move.w    (cdev).w,d0               ; get device number
            addq.b    #1,d0                     ; add and shift to get select bits
            lsl.b     #1,d0                     ; into bits 1 and 2
            or.w      (cside).w,d0              ; or-in side number (bit 0)
            eori.b    #7,d0                     ; negate bits for funky hardware select
            and.b     #7,d0                     ; strip anything else out there
            bsr.s     setporta                  ; do drive select

            move.w    #$82,(a6)                 ; setup 1770 track register
            move.w    2(a1),d7                  ; for current track number
            bsr.s     wdiskctl

select1:    move.w    #$84,(a6)                 ; setup requested sector_number from
            move.w    (csect).w,d7              ; caller's parameters
            bsr.s     wdiskctl
            move.b    (cdma+3).w,(dmalow).w     ; setup DMA chip's DMA pointer
            move.b    (cdma+2).w,(dmamid).w
            move.b    (cdma+1).w,(dmahigh).w
            rts


*+
* setporta - set floppy select bits in PORT A on the sound chip
* Passed:       d0.b (low three bits)
* Returns:      d1 = value written to port A
*               d2 = old value read from port A
* Uses:         d1
*-
setporta:   move      sr,-(sp)                  ; save our IPL
            ori       #$700,sr                  ; start critical section
            move.b    #14,(psgsel).w            ; select port on GI chip
            move.b    (psgsel).w,d1             ; get current bits
            move.b    d1,d2                     ; save old bits for caller
            and.b     #$f8,d1                   ; strip low three bits there
            or.b      d0,d1                     ; or-in our new bits
            move.b    d1,(psgwr).w              ; and write 'em back out there
            move      (sp)+,sr                  ; restore IPL to terminate CS, return
            rts


*+
* Primitives to read/write 1770 controller chip (DISKCTL register).
*
* The 1770 can't keep up with full-tilt CPU access, so
* we have to surround read and writes with delay loops.
* This is not really as slow as it sounds.
*
*-
wdiskct6:   bsr.s     rwdelay                   ; delay
            move.w    d6,(diskctl).w            ; write d6 to diskctl
            rts

wdiskctl:   bsr.s     rwdelay                   ; delay
            move.w    d7,(diskctl).w            ; write d7 to diskctl
            rts

rdiskct7:   bsr.s     rwdelay                   ; delay
            move.w    (diskctl).w,d7            ; read diskctl into d7
            rts

rdiskctl:   bsr.s     rwdelay                   ; delay
            move.w    (diskctl).w,d0            ; read diskctl into d0
            rts


rwdelay:
IF ROM_TOS306
            move.w    d0,-(sp)
            move.w    #$119,d0
            bsr       mfpdelay
            move.w    (sp)+,d0
ELSE
            movem.l   d0-d1,-(sp)               ; save registers
            move.w    #2,d0
rwdly1:     move.b    (tcdr).w,d1               ; busy-loop: give 1770 time to settle
rwdly2:     cmp.b     (tcdr).w,d1
            beq.s     rwdly2
            dbra      d0,rwdly1
            movem.l   (sp)+,d0-d1               ; restore registers
ENDIF
            rts


*+
* change - check to see if the "right" floppy bas been inserted
* On the stack:
*       $10(sp) - dev.W (device#)
*        $c(sp) - dsb.L (pointer to Device State Block)
*         8(sp) - dma.L (dma pointer)
*         4(sp) - ret1.L (caller's return address)
*         0(sp) - ret.L (change's return address)
*
* Returns:      both media "might have changed" condition
*
* Uses:         C registers
*
*-
change:     cmpi.w    #1,(_nflops).l            ; if there are zero or two floppies
            bne.s     ch_r                      ; then do nothing (return OK)
            move.w    $10(sp),d0                ; if cdev == _curflop
            cmp.w     (_curflop).l,d0           ; (...current disk == current drive?)
            beq.s     ch_ok1                    ; then return OK (but use drive #0)

*--- ask the user to stick in the other floppy (via critical error handler)
push disk# we want inserted:move.w d0,-(sp)
            move.w    #-17,-(sp)                ; push "INSERT_A_DISK" error number
            bsr       crit_err                  ; use critical error handler and
            addq.w    #4,sp                     ; hope somebody handles it
            move.w    #$ffff,(wplatch).l        ; set "might have changed" on both drvs
            lea       (_acctim).l,a0
            clr.l     (a0)+
            clr.l     (a0)
            move.w    $10(sp),(_curflop).l      ; set current disk#
ch_ok1:     clr.w     $10(sp)                   ; use drive 0
ch_r:       rts

*+
* setdmode - set drive-change mode
* Passed:         d0.b = mode to put current drive in (0, 1, 2)
* Uses:           a0
*
*-
setdmode:   lea       (diskmode).w,a0           ; a0 -> disk mode table
            move.b    d0,-(sp)                  ; (save mode)
            move.w    (cdev).w,d0               ; d0.w = drive# (index into table)
            cmpi.w    #1,(_nflops).l
            bne.s     setdmode2
            move.w    (_curflop).l,d0
setdmode2:  move.b    (sp)+,(a0,d0.w)           ; set drive's mode
            rts


*+
* floprate - sets the seek rate of the specified floppy drive
* On the stack:
*         6(sp) - rate.w (0: 6ms, 1: 12ms, 2:2ms, 3:3ms)
*         4(sp) - dev.W (device#)
*         0(sp) - return address
*
* Returns:      prior seek rate for the specified drive
*
*-

*--- pick a DSB
floprate:   lea       (dsb0).l,a1
            tst.w     4(sp)
            beq.s     floprate2
            lea       (dsb1).l,a1
floprate2:  move.w    6(a1),d0                  ; current rate
            move.w    6(sp),d1                  ; new seek rate
            cmp.w     #-1,d1                    ; new seek rate == -1
            beq.s     floprateret               ; (just return the current rate)
            cmp.w     #-2,d1                    ; new seek rate == -2
            beq.s     flopratehd                ; (select HD floppy density)
            cmp.w     #-3,d1                    ; new seek rate == -3
            beq.s     flopratesd                ; (select SD floppy density)
            cmp.w     #-4,d1                    ; new seek rate == -4
            beq.s     floprateretdens           ; (return current density)
            move.w    d1,6(a1)                  ; set new seek rate in dsb
floprateret:ext.l     d0
            rts

flopratehd: move.b    #8,(a1)                   ; set density to HD
            moveq     #0,d0                     ; return no error
            rts

flopratesd: clr.b     (a1)                      ; set density to SD
            moveq     #0,d0                     ; return no error
            rts

floprateretdens:tst.b (a1)                      ; return current density setting
            sne       d0                        ; -1:HD, 0:SD
            ext.w     d0
            ext.l     d0
            rts


*+
* Fast RAM doesn't support DMA transfers, we use a temporary buffer
* for loads/stores from and into Fast RAM. The buffer is in _FRB cookie.
*-
frbbackcopy:move.w    (frbufscnt).l,d1          ; sectors to copy back into memory?
            beq.s     frbcheckrts               ; (no, punt)
IF ROM_TOS306
            move.l    d0,-(sp)
            bsr       flushCaches
            move.l    (sp)+,d0
ENDIF
            clr.w     (frbufscnt).l             ; reset sector flag
            movea.l   (frbufaddress).l,a0       ; requested buffer address in memory
            movea.l   (frbufcookie).l,a1        ; used DMA buffer address
frbcopya1_a0:asl.w    #5,d1
            subq.w    #1,d1
frbcopyl:   move.l    (a1)+,(a0)+
            move.l    (a1)+,(a0)+
            move.l    (a1)+,(a0)+
            move.l    (a1)+,(a0)+
            dbra      d1,frbcopyl
frbcheckrts:rts

frbcheckrd:
IF ROM_TOS306
            cmpi.l    #$a00000,8(sp)            ; beyond the 10MB limit?
ELSE
            cmpi.l    #$400000,8(sp)            ; beyond the 4MB limit?
ENDIF
            bcs.s     frbcheckrts               ; (no, punt)
            bsr.s     findFRBc
            move.l    8(sp),(frbufaddress).l    ; save buffer address
            move.l    (frbufcookie).l,8(sp)     ; inject FRB buffer into the parameter
            move.w    $18(sp),(frbufscnt).l     ; save number of sectors
            rts

frbcheckwr:
IF ROM_TOS306
            cmpi.l    #$a00000,8(sp)            ; beyond the 10MB limit?
ELSE
            cmpi.l    #$400000,8(sp)            ; beyond the 4MB limit?
ENDIF
            bcs.s     frbcheckrts               ; (no, punt)
            bsr.s     findFRBc
            movea.l   8(sp),a1                  ; get the source buffer address
            movea.l   (frbufcookie).l,a0
            move.l    a0,8(sp)                  ; inject FRB buffer into the parameter
            move.w    $18(sp),d1                ; number of sectors
            bra.s     frbcopya1_a0              ; copy D1 sectors from A1 to A0

frbcheckfmt:
IF ROM_TOS306
            cmpi.l    #$a00000,8(sp)            ; beyond the 10MB limit?
ELSE
            cmpi.l    #$400000,8(sp)            ; beyond the 4MB limit?
ENDIF
            bcs.s     frbcheckrts               ; (no, punt)
            bsr.s     findFRBc
            move.l    8(sp),(frbufaddress).l    ; save buffer address
            move.l    (frbufcookie).l,8(sp)     ; inject FRB buffer into the parameter
            move.w    #1,(frbufscnt).l          ; 1 sector
            rts

* get FRB buffer pointer from the cookies
findFRBc:   tst.l     (frbufcookie).l           ; variable already set?
            bne.s     findFRBcrts               ; (yes, return)
            movea.l   (_p_cookies).w,a0
            cmpa.w    #0,a0                     ; no cookie jar?
            beq.s     findFRBcx
findFRBcl:  tst.l     (a0)                      ; end of the cookie jar?
            beq.s     findFRBcx
            cmpi.l    #'_FRB',(a0)+
            beq.s     findFRBcf
            addq.l    #4,a0
            bra.s     findFRBcl
findFRBcf:  move.l    (a0)+,(frbufcookie).l
findFRBcrts:rts

findFRBcx:  addq.l    #8,sp                     ; go 2 return levels up
            moveq     #-12,d0                   ; return -12 if no _FRB cookie
            rts