Lisa_Boot_ROM_RM248

        .PAGE
        .LIST
;-------------------------------------------------------------------------
;  All is OK - check for boot device and then do bootstrap
;-------------------------------------------------------------------------

DOBOOT  BSR     CURSORINIT      ;init cursor/mouse

BOOTCHK
        CLR.L   D0	        ;clear for use
        BTST    #ALTBOOT,D7     ;check if alternate boot command rcvd
        BEQ.S   @1	        ;skip if no
        MOVE.B  BOOTDVCE,D0     ;get alternate boot code

       .IF     BURNIN = 1
        CMP.B   #PC,D0	        ;power-cycle mode?
        BNE.S   DVCECHK	        ;if no, go determine device
        BSR     SAV2PM	        ;if yes, save in parameter memory
       .ENDC

        BRA.S   DVCECHK	        ;and go determine device

@1
        .IF USERINT = 1
        BTST    #BTMENU,D7      ;boot menu wanted?
        BNE     BOOTMENU        ;skip if yes
        .ENDC

        BSR     CHKPM	        ;next step is to check parameter memory

        .IF  AAPL = 1
        BCS     NMISET	        ;skip if not to default boot
        .ELSE
        BCC.S   @2	        ;skip if valid

;  set default boot

        TST.B   SYSTYPE	        ;else check if Lisa 1			        CHG030
        BEQ.S   @5	        ;skip if yes				        CHG030
        MOVEQ   #1,D2	        ;else set wait flag			        CHG030
        BSR     CHKPROFILE      ;and go check if hard disk attached	        CHG030
        BEQ.S   @5	        ;skip if yes to do boot from hard disk	        CHG030
        MOVEQ   #TWIG2,D0       ;else set for boot from floppy		        CHG030
        BRA.S   @3	        ;					        CHG030

@5      MOVEQ   #PROFILE,D0     ;else if not valid do default boot from Profile
        BRA.S   @3
        .ENDC

@2      MOVE.B  DVCCODE,D0      ;else read device code
        LSR.B   #4,D0	        ;rotate to low nibble
        BRA.S   DVCECHK

        .IF  NEWTWIG = 0
        BSR     EXPAND	        ;convert id to keycode
        .ENDC

@3
        .IF  ROM16K = 1
;  Do special check for Applenet and I/O test cards

        MOVE    #TSTCRD,D1      ;search first for a bootable test card
        MOVE    #TSTQUAL,D4
        BSR     SEARCH	        ;go do search
        BNE.S   DVCECHK	        ;skip if not found
        MOVE    D2,D3	        ;else save its id
        MOVE    #APPLENET,D1    ;next search for an Applenet card
        MOVE    #APPLQUAL,D4
        BSR     SEARCH
        BEQ.S   @4	        ;skip if found to boot from it (DMT need)
        MOVE    D3,D2	        ;else do boot from I/O test card (DIAG need)
        BPL.S   DVCECHK	        ; unless card boot bit not set		        CHG012
@4
        MULU    #3,D2	        ;convert to boot id
        MOVE    D2,D0	        ;and set boot id for appropriate slot
        .ENDC

;  Alternate boot desired - check which

DVCECHK MOVE.B  D0,BOOTDVCE     ;save for later reference

        .IF TWIGGY = 1
        TST.B   D0	        ;boot from upper drive?		        CHG009
        BNE.S   @1	        ;no - go to next check
        TST.B   SYSTYPE	        ;check system type		        CHG009
        BEQ.S   @11	        ;skip if Lisa 1.0		        CHG009
@10     BRA     PROBOOT	        ;else do Pepsi boot		        CHG009

@11     MOVE.B  #DRV1,D0        ;else set drive #		        CHG009
        BRA.S   @2	        ;and go do boot

@1      CMP.B   #TWIG2,D0       ;boot from lower drive?		        CHG009
        BNE.S   @3	        ;skip if no
        MOVE.B  #DRV2,D0        ;else set drive #
@2      BRA     TWGBOOT	        ;and go boot
        .ENDC

@3
        .IF PROFLE = 1
        CMP.B   #PROFILE,D0     ;boot from Profile?
        BEQ.S   @10	        ;yes - go do it			        CHG009
        .ENDC

        CMP.B   #IO1PORT2,D0    ;boot from slot 1, ports 1-2?
        BGT.S   @4	        ;skip if not
        MOVE.L  #SLOT1L,A1      ;set slot address
        BRA.S   @9	        ;and go do boot

@4      CMP.B   #IO2PORT2,D0    ;boot from slot 2, ports 1-2?
        BGT.S   @5	        ;skip if not
        MOVE.L  #SLOT2L,A1      ;set slot address
        BRA.S   @9	        ;and go do boot

@5      CMP.B   #IO3PORT2,D0    ;boot from slot 3, ports 1-2?
        BGT.S   @6	        ;skip if not
        MOVE.L  #SLOT3L,A1      ;set slot address

@9      BRA     IOSBOOT	        ;and go do boot

@6
        .IF     BURNIN = 1
        CMP.B   #PC,D0	        ;power-cycle?
        BEQ     CHKPASS	        ;go do cycling
        .ENDC

        .IF  ROM4K = 0
@7      CMP.B   #MON,D0	        ;abort boot?
        BNE.S   LSTCHK	        ;skip if no match

        .IF  USERINT = 1
        BCLR    #NORSTRT,STATFLGS ;allow restart option but
        BSET    #NOCONT,STATFLGS  ; no CONTINUE button for direct to monitor jump
        BSR     CLRMENU		  ;clear menu bar and
        BSR     MAKEPCALRT	  ; open alert box for monitor or power cycling
        .ENDC

        .IF  BURNIN = 1

;  Check if completing power-cycling operation

        .IF  DEBUG = 0
        LEA     PMERR,A3        ;set vector for parameter mem error
        MOVE.L  A3,BUSVCTR
        .ENDC

        BSR     CHKPM	        ;check validity of parameter memory
        BCS.S   PMEXIT	        ;skip if not
        MOVE.B  DVCCODE,D0      ;get boot code
        LSR.B   #4,D0	        ;shift to lower nibble

        .IF     NEWTWIG = 0
        BSR     EXPAND	        ;convert to keycode
        .ENDC

        CMPI.B  #PC,D0	        ;exiting from power-cycling?
        BNE.S   PMEXIT	        ;skip if no
        CLR.B   DVCCODE	        ;else reset boot and
        BCLR    #6,MEMCODE      ; memory test indicators

        LEA     LOOPMSG,A3      ;display loop count
        BSR     DSPMSG
        MOVE.L  #LCNTHI,A0      ;set ptr to loop count
        MOVEP   (A0),D0	        ;get it
        MOVEQ   #4,D1	        ;set # of digits to display
        BSR     OUTCHR	        ;and do display
        MOVEQ   #PCCOL,D6       ;reset col for proper left margin

        BSR     DSPCLK	        ;display final clock value
        BSR     TWGDSP	        ;and display Twiggy error count

;  Normal exit

PMEXIT
        .IF  DEBUG = 0
        BSR     SETBUSVCT       ;restore normal bus error vector        RM000
        .ENDC		        ;{debug}

        .ENDC		        ;{BURNIN}

        BRA     MONITOR	        ;and go to monitor

        .IF  BURNIN = 1
;  Bus error handler for parameter memory error

PMERR   LEA     PMMSG,A3        ;setup error message
        LEA     IOBRD,A2        ;and icon
        CLR.L   D0	        ;no error code
        BRA     INITMON	        ;exit to monitor

        .ENDC		        ;{BURNIN}
        .ENDC		        ;{ROM4K}

LSTCHK
        .IF  AAPL = 1
        CMP.B   #APPLE,D0       ;Apple boot?
        BEQ     NMISET	        ;exit if yes
        .ENDC

        .IF  USERINT = 0
        LEA     BADBOOT,A3      ;else display error msg
        BSR     DSPMSGR
        BRA     MONITOR	        ;go to monitor
        .ELSE
        BSR     SQUAWK	        ;else sound error tone
        BRA     BOOTMENU        ;and go to boot menu
        .ENDC

        .PAGE
;-------------------------------------------------------------------------
;  Subroutine to check parameter memory validity.  Calls generalized
;  verify checksum routine.
;-------------------------------------------------------------------------

CHKPM   MOVEM.L D0-D1/A0,-(SP)  ;save regs
        MOVEA.L #PMSTRT,A0      ;set starting ptr
        MOVE    #PMWRDS-1,D0    ;and # of words to check
        MOVE    D0,D1	        ;set for shared memory
        BSR.S   VFYCHKSM        ;and go do checksum
@1      MOVEM.L (SP)+,D0-D1/A0  ;restore regs
        RTS

        .IF BURNIN = 1
;-------------------------------------------------------------------------
;  Subroutine to save boot device code to parameter memory.
;-------------------------------------------------------------------------

SAV2PM  MOVEM.L D0-D1/A0,-(SP)  ;save regs

        .IF     NEWTWIG = 0
        BSR     XLATE	        ;convert keycode to boot id
        MOVE.B  D2,D0
        .ENDC

        LSL.B   #4,D0	        ;rotate device code to upper nibble
        MOVE.B  DVCCODE,D1      ;read current setting
        ANDI.B  #$0F,D1	        ;clear device indicator
        OR.B    D1,D0	        ;save other data
        MOVE.B  D0,DVCCODE      ;and write new device code

;  also set for full memory test

        BSET    #6,MEMCODE      ;ensure memory test indicator set

;  then compute new checksum

        MOVEA.L #PMSTRT,A0      ;compute new checksum
        MOVEQ   #PMWRDS-2,D0    ;leaving out checksum word
        BSR.S   WRTSUM
@2      MOVEM.L (SP)+,D0-D1/A0  ;restore regs
        RTS

;----------------------------------------------------------------------
;  Subroutine to write new checksum to parameter memory area
;----------------------------------------------------------------------

WRTSUM  MOVE    D0,D1	        ;set for shared memory
        BSR.S   VFYCHKSM
        NOT     D3	        ;compute 2's complement
        ADDQ    #1,D3
        MOVEP   D3,(A0)	        ;write as new checksum
        RTS

        .ENDC

;-------------------------------------------------------------------------
;  Subroutine to verify 16 bit checksum validity for memory contents.
;
;  Inputs Required:  A0 = starting address for verify
;		     D0 = # of words-1 to read
;		     D1 = 0 for regular memory (uses MOVE.W)
;		        = nonzero for shared memory (uses MOVEP)
;
;  Outputs:	     Carry bit set if computed checksum not 0.
;		     D2 = expected checksum  (last word read)
;		     D3 = computed checksum
;-------------------------------------------------------------------------

VFYCHKSM
        CLR.L   D2	        ;clear regs for use
        CLR.L   D3
CKLOOP  TST     D1	        ;shared memory?
        BEQ.S   @1	        ;skip if no
        MOVEP   (A0),D2	        ;else read alternate bytes
        ADDQ.L  #4,A0	        ;bump address
        BRA.S   @2	        ;skip to do checksum
@1      MOVE    (A0)+,D2        ;read words
@2      ADD     D2,D3	        ;add to computed checksum
        ROL     #1,D3	        ;rotate for better effectiveness
        DBF     D0,CKLOOP       ;loop until done
        TST     D3	        ;expected result = 0
        BEQ.S   CKXIT
        ORI.B   #$01,CCR        ;else set error indicator
CKXIT   RTS

        .IF     NEWTWIG = 0
;-------------------------------------------------------------------------
;  Subroutine to expand boot id read from parameter memory to keycode
;-------------------------------------------------------------------------

EXPAND  LEA     KEYTBL,A2       ;set ptrs to keycode table
        LEA     TBLEND,A3
        CLR.L   D2	        ;use for search id
@1      CMP.B   D2,D0	        ;check for match
        BEQ.S   @2	        ;skip if yes
        ADDQ    #1,D2	        ;incr search id
        ADDQ.L  #1,A2	        ;bump table ptr
        CMPA.L  A2,A3	        ;at end?
        BNE.S   @1	        ;loop if not
        MOVE.B  #APPLE,D0       ;else set for default boot
        BRA.S   @3	        ;and go to exit
@2      MOVE.B  (A2),D0	        ;get keycode
@3      RTS

        .ENDC
        .IF  USERINT = 1
;-------------------------------------------------------------------------
;  Subroutine to expand boot id read from parameter memory to keycode
;  Returns with keycode in D0.
;-------------------------------------------------------------------------

EXPAND  MOVEM.L D2/A2-A3,-(SP)  ;save regs
        LEA     KEYTBL,A2       ;set ptrs to keycode table
        LEA     TBLEND,A3
        CLR.L   D2	        ;use for search id
@1      CMP.B   D2,D0	        ;check for match
        BEQ.S   @2	        ;skip if yes
        ADDQ    #1,D2	        ;incr search id
        ADDQ.L  #1,A2	        ;bump table ptr
        CMPA.L  A2,A3	        ;at end?
        BNE.S   @1	        ;loop if not
        MOVEQ   #PROFILE,D0     ;else set for default boot
        BRA.S   @3	        ;and go to exit
@2      MOVE.B  (A2),D0	        ;get keycode
@3      MOVEM.L (SP)+,D2/A2-A3  ;restore regs
        RTS

;-------------------------------------------------------------------------
;  Routine to search I/O slots for specific card
;  Expects  D1 = card id to search for
;	    D4 = qualifier for search (mask)
;  Returns  CC = 0 if card found and
;	    D2 = slot #
;-------------------------------------------------------------------------

SEARCH  MOVEM.L D0/D3,-(SP)     ;save regs
        MOVEQ   #0,D2	        ;setup as slot counter
        MOVEQ   #2,D3	        ;set # of slots - 1 to check
        LEA     IO1ID,A0        ;get location of saved slot id's
@1      ADDQ    #1,D2	        ;bump slot #
        MOVE    (A0)+,D0        ;read slot id
        AND     D4,D0	        ;mask it
        CMP     D1,D0	        ;match?
        BEQ.S   @2	        ;skip if yes
        DBF     D3,@1
        TST     D2	        ;set nonzero status if no match
@2      MOVEM.L (SP)+,D0/D3     ;restore regs
        RTS		        ;exit

        .PAGE
;-------------------------------------------------------------------------
;  Routines to display boot icon menu
;-------------------------------------------------------------------------

BOOTMENU
        ANDI.B  #$0F,STATFLGS   ;initialize flags
        CLR     RectCnt	        ;clear active rectangle counter
        MOVEQ   #1,D6	        ;set min # of boot alternates	        CHG009
                                ; i.e., at least have lower drive
                                ;  to boot from
        TST.B   SYSTYPE	        ;check system type		        CHG009
        BNE.S   @10	        ;Skip if Lisa 2			        CHG009
        ADDQ    #1,D6	        ;else incr count for upper floppy drive CHG009

@10     MOVEQ   #1,D2	        ;set flag to do wait if needed	        RM011
        BSR     CHKPROFILE      ;go check for attached Profile
        BNE.S   @1	        ;skip if not there
        ADDQ    #1,D6	        ;else bump boot count

@1      CLR.L   D4	        ;set flag for no status check
        BSR     RDSLOTS	        ;go scan the I/O slots
        LEA     IO1ID,A0        ;check results
        MOVE    (A0)+,D0        ;read first ID
        BPL.S   @2	        ;skip if not bootable or not there
        MOVE.L  #SLOT1L,A2      ;set slot address
        BSR     RDSLT	        ;go check if any icons
        BCS.S   @2	        ;skip if any error
        ANDI    #$03,D3	        ;else clear don't care bits (max count = 2)
        ADD     D3,D6	        ;and add to icon count

@2      MOVE    (A0)+,D0        ;check slot 2
        BPL.S   @3	        ;skip if not bootable
        MOVE.L  #SLOT2L,A2      ;set slot address
        BSR     RDSLT	        ;go check if any icons
        BCS.S   @3	        ;skip if any error
        ANDI    #$03,D3	        ;else clear don't care bits (max count = 2)
        ADD     D3,D6	        ;and add to icon count

@3      MOVE    (A0)+,D0        ;check final slot
        BPL.S   @4
        MOVE.L  #SLOT3L,A2      ;set slot address
        BSR     RDSLT	        ;go check if any icons
        BCS.S   @4	        ;skip if any error
        ANDI    #$03,D3	        ;else clear don't care bits (max count = 2)
        ADD     D3,D6	        ;and add to icon count

;  set starting icon display address according to boot count

@4      CMP.B   #10,D6	        ;max of 10 icons in menu
        BLE.S   @5	        ;skip if OK
        MOVEQ   #10,D6	        ;else set max count

;  now display blank boot icon menu

@5      MOVEQ   #BMENUWIDTH,D0  ;set menu parameters
        MOVE.L  D6,D1	        ;get count of entries
        MULU    #BMENULEN,D1    ;length depends on number of entries
        LEA     STRTMSG,A3      ;set menu heading
        BSR     DSPMENUBOX      ;and go display the menu

        MOVE    #MENUSTRT,MenuBase ;setup base menu address
        MOVE    #MENU1MSG,IconAddr ;and display pt for first entry

;  next fill in the menu entries with icons and alternate keycodes
;  D0 set with system type

ICONCHK
        MOVE.B  SYSTYPE,D0      ;read system type	        CHG009/CHG029
        BEQ.S   @1	        ;skip if Lisa 1.0	        CHG009
        CMP.B   #3,D0	        ;else check if internal disk    CHG009/CHG029
        BNE.S   @2	        ;skip if not - no upper icon    CHG009/CHG029

        CLR.L   D2	        ;else set for no wait	        CHG009
        BSR     CHKPROFILE      ; and check if integral disk    CHG009
                                ; installed		        CHG009
        BNE.S   @2	        ;skip if not		        CHG009
        LEA     UPPER,A2        ;set icon ptr for integral disk CHG009
        BRA.S   @3	        ;and go display		        CHG009

@1      LEA     DRIVEN,A2       ;set icon ptr for drive	        CHG009
        MOVEQ   #1,D1	        ;set drive id #		        CHG009
@3      MOVEQ   #TWIG1,D2       ;set id code		        CHG009
        MOVEQ   #-1,D3	        ;set compressed icon indicator
        BSR     DSPMNTRY        ;display the entry

@2      LEA     DRIVEN,A2       ;set icon ptr for drive	        CHG009
        MOVEQ   #2,D1	        ; and drive id #	        CHG009
        MOVEQ   #TWIG2,D2       ;set id code
        MOVEQ   #-1,D3	        ;set compressed indicator
        BSR     DSPMNTRY        ;display the entry

        CLR.L   D2	        ;set flag for no wait	        RM011
        CMP.B   #3,D0	        ;skip check if internal disk    CHG009/CHG029
        BEQ.S   SCNSLTS	        ;			        CHG009/CHG029

        BSR     CHKPROFILE      ;else check if external disk attached
        BNE.S   SCNSLTS	        ;skip if not
        LEA     PROICON,A2      ;else set icon ptr for Profile
        MOVEQ   #PROFILE,D2     ;set id code
        MOVEQ   #-1,D3	        ;set compressed indicator
        BSR     DSPMNTRY        ;display the entry

;  check for bootable devices in slots (what a pain!)

SCNSLTS CLR.L   D0	        ;clear for use
        LEA     IO1ID,A0        ;set ptr for slot id
        MOVE    (A0)+,D0        ;get id
        BPL.S   CHKS2	        ;skip if not bootable or not there
        MOVE.L  #SLOT1L,A2      ;else set slot address
        MOVEQ   #1,D1	        ;set slot # for generic display if no ROM icon
        MOVEQ   #IO1PORT1,D2    ;set base boot id for slot
        BSR     CHKSLOT	        ;go check slot and display icons

CHKS2   MOVE    (A0)+,D0        ;read next id
        BPL.S   CHKS3	        ;skip if not bootable or not there
        MOVE.L  #SLOT2L,A2      ;else set slot address
        MOVEQ   #2,D1	        ;set slot # for generic display
        MOVEQ   #IO2PORT1,D2    ;set base boot id for slot
        BSR     CHKSLOT	        ;go check slot and display icons

CHKS3   MOVE    (A0)+,D0        ;read slot 3 id
        BPL.S   WT4BOOT	        ;skip if not bootable or not there
        MOVE.L  #SLOT3L,A2      ;else set slot address
        MOVEQ   #3,D1	        ;set slot # for generic display
        MOVE    #IO3PORT1,D2    ;set base boot id for slot
        BSR     CHKSLOT	        ;go check slot 3 and display icons

;---------------------------------------------------------------------------
;  Menu displayed - now wait for operator selection
;---------------------------------------------------------------------------

WT4BOOT
        BSR     CursorDisplay	 ;display cursor on screen
        BSET    #CHKCMD,STATFLGS ;set flag for CMD key check
        BSR     GETINPUT        ;go wait for user input
        BCS     GETERR	        ;skip if error
        BSR     XLATE	        ;translate and save boot id
        BSR     CursorHide      ;remove cursor from screen
        BSR     CLRDESK	        ;clear desktop
        BRA     BOOTCHK	        ;and go start boot

        .PAGE
;---------------------------------------------------------------------------
;  Routine to check for Profile attached to built-in parallel port.
;  Checks for Profile connected (OCD) and tries an initial handshake to
;  ensure the device is a Profile.
;
;  Inputs: D2 = nonzero if full wait for Profile ready should be done
;  Outputs: Zero condition code bit cleared if error
;---------------------------------------------------------------------------

CHKPROFILE
        MOVEM.L A0/D0/D2/D4/D6,-(SP)    ;save regs			        RM011
        BSR     PROINIT		        ;init for Profile access
        BNE.S   @9		        ;skip if not attached

        BSR     WFNBSY3		        ;wait for not busy		        RM000
        TST.B   D0		        ;check return code
        BEQ.S   @0		        ;skip if OK
        TST.B   D2		        ;do full wait?			        RM011
        BEQ.S   @7		        ;skip if not			        RM011

        CLR.L   D0		        ;else reset error code for retry
        BSR     WAITALRT	        ;output wait alert
        BSR     WFNBSY2		        ;try wait for normal profile boot time  CHG019
        MOVEM.L A0/D0,-(SP)	        ;save regs
        BSR     CLRDESK		        ;clear desktop
        MOVEM.L (SP)+,A0/D0	        ;restore regs
        TST.B   D0
        BNE.S   @8		        ;exit if still not ready

@0      ANDI.B  #$EF,ORB2(A0)	        ;set command = true
        BSR     WFBSY		        ;then get initial Profile response      RM016
        BNE.S   @1		        ;skip if error

        CMPI.B  #1,PORTA2(A0)	        ;check for expected '01' response
        BEQ.S   @1		        ;skip if OK
        MOVEQ   #BADRSP,D0	        ;else set error code

@1      MOVEQ   #0,D3		        ;send '0' response to reset Profile
        BSR     SENDRSP
        BSR     WFNBSY		        ;wait until command taken

@8      CLR.B   DDRA2(A0)	        ;set port A bits to input
        ORI.B   #$18,ORB2(A0)	        ;and set dir=in, cmd=false

@7      TST     D0		        ;set return code

@9      MOVEM.L (SP)+,A0/D0/D2/D4/D6    ;restore			        RM011
        RTS			        ; and exit

;---------------------------------------------------------------------------
;  Subroutine to invoke display of boot menu entry
;  Inputs:
;       D2 = boot id
;       A2 = ptr to icon
;  Outputs:
;       Location MenuBase updated with address for next menu "box"
;  Side Effects:
;       None
;---------------------------------------------------------------------------

DSPMNTRY
        MOVEM.L D0/A1,-(SP)     ;save boot id and addr ptr
        MOVE    D2,D0	        ;get boot id
        BSR     EXPAND	        ;go convert boot id to keycode
        MOVE    MenuBase,A1     ;get address for display of entry
        BSR.S   ICONMENU        ;go display in menu
        MOVEM.L (SP)+,D0/A1     ;restore boot id and addr ptr
        RTS		        ;and exit

;----------------------------------------------------------------------------
;  Subroutine to display icon menu on screen.  Creates "active rectangle
;  table" as entries are made.
;  Inputs:
;       D0 = alternate keycode
;       D3 = compressed icon indicator
;       A1 = address for start of next menu "box"
;       A2 = ptr to icon
;  Outputs:
;       A1 = ptr for display of next menu entry
;  Side Effects:
;       None
;----------------------------------------------------------------------------

ICONMENU
        MOVEM.L D0-D4/A0/A2/A5,-(SP)

;  first save icon coordinates in active rectangle table

        LEA     RectTable,A0    ;get ptr to active rect table
        MOVE    (A0),D2	        ;get current count of rect's
        MULU    #5,D2	        ;five entries per rect
        ADD     D2,D2	        ;double for word index
        ADDQ    #1,(A0)+        ;incr for new rect
        MOVE    D0,0(A0,D2.W)   ;save keycode id for new rect
        BSR     KeyToAscii      ;convert keycode to Ascii
        MOVE    D0,D4	        ;save for later display

;  compute X,Y pixel coordinates from starting address

        BSR     GETROWCOL       ;get pixel row, byte col
        MULU    #8,D6	        ;convert to pixel col
        MOVE    D6,2(A0,D2.W)   ;save upper left X
        MOVE    D5,4(A0,D2.W)   ; and Y coordinates

        MOVE    #,D0 ;width in pixels of menu entry
        ADD     D0,D6		  ;compute and save
        MOVE    D6,6(A0,D2.W)	  ; lower Y coordinate
        ADD     #,D5  ;compute and save
        MOVE    D5,8(A0,D2.W)	  ; lower X coordinate

;  now do icon display

        SUBA.L  A6,A6	        ;clear for use
        MOVE    IconAddr,A6     ;get address for icon display
        MOVE.L  A6,A1	        ;save for later use
        ADD.L   SCRNBASE,A6     ;convert to screen address
        TST.B   D3	        ;check for compressed icon
        BPL.S   @1	        ;skip if not
        BSR     DSPICON	        ;go do display

        LEA     DRIVEN,A0       ;displaying drive?		        CHG009
        CMPA.L  A0,A2	        ;				        CHG009
        BNE.S   @2	        ;skip if not			        CHG009
        MOVE.L  A1,A5	        ;else set icon display address	        CHG009
        BSR     DSPNUM	        ; and display with id #		        CHG009
        BRA.S   @2	        ;skip to continue		        CHG009

@1      BSR     DSPRGICON       ;display an uncompressed icon	        CHG008

;  now display the alternate keycode

@2      ADD     #ALTKYADDR,A1   ;set starting display pt
        BSR     GETROWCOL       ;convert to row, col
        CLR     D0	        ;first display the apple
        BSR     DSPVAL
        MOVE    D4,D0	        ;get Ascii char
        BSR     DSPVAL	        ;and display it

;  finally compute the next menu entry and icon display address

@3      MOVE    MenuBase,A1     ;get base address for the entry
        ADD     #BMenuSpc,A1    ;space to next col
        MOVE    A1,MenuBase     ;and save for next entry

        MOVE    IconAddr,A1     ;else get this icon's address
        ADD     #BMenuspc,A1    ;and bump to next spot in column
        MOVE    A1,IconAddr     ;and do update
        BRA.S   @4

@4      MOVEM.L (SP)+,D0-D4/A0/A2/A5
        RTS

;---------------------------------------------------------------------------
;  Routine to check slots for icons and do display or do generic display.
;  Inputs:
;       D0 = card id
;       D1 = slot #
;       D2 = first boot id for slot
;       A1 = address for icon display
;       A2 = slot address
;  Outputs:
;       Returns with carry bit set if error.
;  Side Effects:
;       None
;---------------------------------------------------------------------------

CHKSLOT MOVEM.L D0-D4/A0-A2,-(SP) ;save regs
        BTST    #ICBIT,D0       ;icon available?
        BNE.S   CHKICONS        ;skip if yes

;  no icons available - display slot # and display generic slot card icon

        LEA     XCARD,A2        ;point to generic icon
        MOVEQ   #-1,D3	        ;set compressed flag
        MOVE    IconAddr,A1     ;get display address for later use
        BSR     DSPMNTRY        ;go display entry

        LEA     XCARD,A2        ;set icon ptr
        MOVE.L  A1,A5	        ;get icon address
        BSR     DSPNUM	        ;display slot #
        BRA.S   CHKSXIT	        ;and exit

;  Slot has icon - read ROM and get ptr to desired icon

CHKICONS
        BSR.S   RDSLT	        ;go read slot
        BCS.S   CHKSXIT	        ;exit if error
        MOVE.L  #ADR128K-4,A1   ;set base address of I/O slot ROM code
        MOVE.L  D3,D4	        ;save icon count
        ANDI    #$03,D4	        ;isolate count (max = 2)
        MOVE.L  A2,A0	        ;get code ptr
        MOVE    (A0)+,D1        ;get icon offset
        MOVE.L  A1,A2	        ;get base address
        ADD     D1,A2	        ;add offset to set up icon ptr
        BSR     DSPMNTRY        ;display as menu entry

        SUBQ    #1,D4	        ;more than one icon?
        BEQ.S   CHKSXIT	        ;skip if not
        MOVE    (A0)+,D1        ;else get ptr to second icon
        ADDQ    #1,D2	        ;bump boot id ptr for slot
        MOVE.L  A1,A2	        ;restore base address
        ADD     D1,A2	        ;set up icon address
        BSR     DSPMNTRY        ;display as menu entry

CHKSXIT MOVEM.L (SP)+,D0-D4/A0-A2 ;restore regs and exit
        RTS

;---------------------------------------------------------------------
;  Routine to read I/O slot ROM's and get icon count if any.
;
;  Expects D0 = boot id
;	   A2 = slot address
;
;  Returns D3 = icon count
;	   A2 = address of ptr to first icon if more than one
;---------------------------------------------------------------------

RDSLT   MOVEM.L D0/D2,-(SP)     ;save boot id's
        BTST    #ICBIT,D0       ;any icons stored in ROM?
        BEQ.S   @2	        ;skip if none
        CLR.L   D4	        ;set flag for no status check
        BSR     RDIOSLT	        ;and go read ROM on slot
        BCS.S   @1	        ;skip if error

        CLR.L   D3	        ;clear for use
        MOVE    ICONPTR,D3      ;get icon ptr
        BSET    #0,D3	        ;must be odd address
        MOVE.L  #ADR128K-4,A2   ;set base address
        ADD.L   D3,A2	        ;set actual address
        MOVE.B  (A2)+,D3        ;read icon count
        BRA.S   @1

@2      MOVEQ   #1,D3	        ;set default icon count

@1      MOVEM.L (SP)+,D0/D2     ;restore boot id's
        RTS

        .ENDC		        ;{USERINT}
        .PAGE
;-------------------------------------------------------------------------
;  Do default boot from Twiggy specified drive.
;  Assumes regs:
;       D0 = drive #
;  Following assumptions are made for power-up status:
;    1) No interrupt from disk unless diskette inserted or button pushed
;-------------------------------------------------------------------------

TWGBOOT LEA     DSKVCT,A3       ; first set up bus error vector
        MOVE.L  A3,BUSVCTR

        .IF  USERINT = 0
        LEA     BOOTMSG,A3      ; output boot msg
        BSR     DSPMSGR
        .ELSE
        MOVE.B  D0,DRIVE        ;save drive id
        BSR     WAITALRT        ;display wait icon
        .ENDC

        MOVE.L  #DISKMEM,A0     ; set ptr to controller memory
        MOVE.L  #TWGHDR,A1      ; set ptr to load header
        MOVE.L  #TWGHDR+12,A2   ; and ptr to load data
        CLR.L   D1	        ; set drive/side/sector/track ptr
        ANDI.L  #$00FF,D0       ; mask off junk

        TST     D0	        ;enable only drive selected
        BNE.S   @1
        MOVE.B  #$08,CMD(A0)    ;enable drive #1
        BRA.S   @2
@1      MOVE.B  #$80,CMD(A0)    ;enable drive #2

@2      ROR.L   #8,D0	        ; get actual drive desired
        OR.L    D0,D1	        ; and save for shared mem format

        MOVE.B  #ENBLINT,(A0)   ;go do enable
        BSR     CMDCHK	        ;wait until cmd taken
        BCS     DSKTIMERR       ;skip if timeout error
        MOVE.L  #VIA1BASE,A3    ;else enable
        BCLR    #FDIR,DDRB1(A3) ; FDIR

;	 BTST	 #FDIR,(A3)	 ;FDIR present?
;	 BEQ.S	 DOREAD		 ;skip if no to do read

CLRINT  BSR     CLRFDIR	        ;clear interrupts
        BCS     DSKTIMERR       ;exit if timeout error

        .PAGE
;-----------------------------------------------------------------------------
;  Read boot data - retry on sector 0 if needed.
;-----------------------------------------------------------------------------

DOREAD
        CLR     D0	        ; set speed value
        BSR     TWGRD	        ; go read sector 0		        RM000
        BCC.S   @1	        ; skip if OK
        CMP.B   #TIMOUT,D0      ; timeout error?
        BEQ     DSKCHK	        ; exit if yes
        BRA.S   RDRETRY	        ; else go do retry

@1      MOVE    FILEID(A1),D0   ; else get file ID
        CMP     #BOOTPAT,D0     ; is it a boot file?
        BEQ.S   RDSCTR1	        ; skip if yes

;  Do retry by reading track 1 to try to get head properly aligned, then
;  retry reading track 0

RDRETRY MOVE.B  #1,D1	        ; set for track 1
        CLR     D0	        ; set speed value
        BSR     TWGRD	        ; go do read			        RM000
        BCS.S   DSKCHK	        ; exit if second error
        CLR.B   D1	        ; else retry track 0
        CLR     D0	        ; set speed value
        BSR     TWGRD	        ; go do read			        RM000
        BCS.S   DSKCHK	        ; exit if error

;  Now check again for a valid boot track

        MOVE    FILEID(A1),D0   ; get file ID
        CMP     #BOOTPAT,D0     ; is it a boot file?
        BEQ.S   RDSCTR1	        ; skip if yes
        MOVEQ   #BADTHDR,D0     ; set error code
        BRA.S   DSKCHK	        ; and exit

RDSCTR1
        .IF  NEWTWIG = 0
        ORI     #$100,D1        ; set for sector 1
        MOVE.L  #TWGHDR+268,A1  ; set new header ptr
        MOVE.L  #TWGHDR+280,A2  ; and new data ptr
        BSR.S   TWGREAD	        ; and read next sector
        BCS.S   DSKCHK
        .ENDC

        CLR.L   BOOTDATA        ;set for no error
        LEA     DSKERR2,A3      ;set up vectors in case of bad diskette
        LEA     DSKERR3,A4
        BSR     VCTRINIT
        MOVE.L  #TWGDATA,A2     ;set data ptr (software view)

;  Do keyboard/mouse reset before continuing boot process

STRTBOOT
        MOVEM.L A0/D0,-(SP)     ;save regs
        BSR     RSTKBD	        ;send reset signal
        BSR     CLRRST	        ;then clear it
        CLR.B   KBDQ	        ;clear first byte of keyboard queue
        MOVEM.L (SP)+,A0/D0     ;restore regs
        JMP     (A2)	        ;and away we go ...

        .PAGE
;-------------------------------------------------------------------------
;  Error occurred - output error message and go to monitor
;-------------------------------------------------------------------------

DSKTIMERR
        MOVEQ   #TIMOUT,D0      ;set timeout error code

DSKCHK  MOVE.B  D0,BOOTDATA     ;save the error status
        CMPI.B  #TIMOUT,D0      ;timeout?
        BEQ.S   DSKERR	        ;skip if yes
        BSR     CLRFDIR	        ;ensure intrpt cleared
        BCS.S   DSKOUT	        ;exit if error

        .IF  USERINT = 0
        CMPI.B  #NODISK,D0      ; no disk in?
        BNE.S   DSKBAD	        ; skip if no
        LEA     DSKMSG,A3       ; get msg ptr
        BSR     DSPMSGR	        ; output it
        BSR     CHKFIN	        ; go wait for FDIR
        BCS.S   DSKERR	        ; and exit if timeout
        BSR     CLRFDIR	        ;assume disk in place, clear int
        BCS.S   DSKERR	        ;skip if error
        BSR     CLAMPIT	        ;issue clamp cmd
        BCS.S   DSKERR	        ;exit if error
        CLR.B   D1	        ;reset for track 0
        BRA.S   CLRINT	        ;and go try read again
        .ENDC

DSKBAD  MOVE.B  CHKCNT(A0),BOOTDATA+2  ; and data checksum count

        .IF  NEWTWIG = 1
        MOVE.B  CHKCNT2(A0),BOOTDATA+1 ; and address checksum count
        MOVE.B  RTRYCNT(A0),BOOTDATA+3 ; and retry count
        .ENDC

DSKOUT  BSR     EJCTDSK	        ;then eject the disk

DSKDIS  BSR     DSABLDSK        ;disable both drives

DSKERR  BSR     SETBUSVCT       ; restore default bus error vector      RM000

        .IF  USERINT = 0
        LEA     BOOTERR,A3      ; and output message
        BSR     DSPMSGR
        .ELSE
        BSR     CHKDRIVE        ;go determine drive
        MOVE.B  BOOTDATA,D0     ;get error code
        CMP.B   #NODISK,D0      ;no disk error?
        BNE.S   @1	        ;skip if not

                                ;2 statements deleted		        CHG009

        LEA     INSERTD,A2      ;set icon for insert rqst	        CHG009
        BSR     DSPALRTICON     ;display basic icon		        CHG009
        MOVE    #ERRSTRT,A5     ;set display pt for id #	        CHG009
        BSR     DSPNUM	        ; and display it		        CHG009
        BRA     BFAIL2	        ;then go signal user

@1      CMP.B   #RDWRERR,D0     ;read error?
        BEQ.S   @2	        ;skip if yes
        CMP.B   #BADTHDR,D0     ;bad file id?
        BEQ.S   @2
        CMP.B   #EBOOT,D0       ;boot error?
        BNE.S   @3	        ;skip if not

@2      BSR     DSPNUMICON      ;display diskette icon with id #
        BRA.S   TBOOTERR        ;and exit

@3      LEA     IOBRD,A2        ;error must be on I/O board
        BSR     DSPERRICON      ;display icon
        .ENDC

TBOOTERR
        .IF  USERINT = 0
        BRA     DSPDVC	        ; and exit to display
        .ELSE
        BRA     BOOTFAIL        ;and go signal boot failure
        .ENDC

;--------------------------------------------------------------------------
;  Handler for Twiggy boot errors
;--------------------------------------------------------------------------

DSKERR2 BSRS4   SAVEXCP	        ;go save exception info

DSKERR3 BSRS4   BTERR	        ;regroup
        BRA.S   DSKOUT	        ;and display error

SAVEXCP
        MOVE    (SP)+,EXCFC     ;save function code
        MOVE.L  (SP)+,EXCADR    ;and address
        MOVE    (SP)+,EXCIR     ;and instruction reg
        RTS4

BTERR   MOVE    (SP)+,EXCSR     ;save status reg
        MOVE.L  (SP)+,EXCPC     ;and pc
        BSR     SAVEREGS        ;save regs
        MOVEA   #STKBASE,SP     ;reset stack pointer
        BSR     SETVCTRS        ;reinit vectors
        MOVEQ   #EBOOT,D0       ;set boot error
        MOVE.B  D0,BOOTDATA     ;and save
        RTS4

;---------------------------------------------------------------------------
;  Subroutine to disable interrupts from both Twiggy drives
;  Inputs:
;       None
;  Outputs:
;       Carry bit set if timeout error (in CMDCHK).
;  Side Effects:
;       A0 trashed (other regs trashed in CMDCHK)
;---------------------------------------------------------------------------

DSABLDSK
        MOVE.L  #DISKMEM,A0     ;set ptr to shared memory
        MOVE.B  #$88,CMD(A0)    ;disable ints from both drives
        MOVE.B  #DSABLINT,(A0)
        BSR     CMDCHK	        ;wait for command to be taken
        RTS		        ;then return

;--------------------------------------------------------------------------
;  Subroutine to determine drive # in error and get icon ptr
;  Inputs:
;       Location DRIVE = drive id # (0 or $80)
;  Outputs:
;       A2 = ptr to diskette icon
;       D1 = id #
;  Side Effects:
;       None
;--------------------------------------------------------------------------

CHKDRIVE
        LEA     DISKETTE,A2     ;set ptr for diskette icon
        MOVE.B  DRIVE,D1        ;get drive id
        TST.B   D1	        ; drive #1?
        BNE.S   @1	        ; skip if no

        .IF  USERINT = 0
        MOVEQ   #TWG1,D0        ; else set up ASCII code
        .ELSE
        MOVEQ   #1,D1	        ; else set up id code
        .ENDC

        BRA.S   @2
@1
        .IF  USERINT = 0
        MOVEQ   #TWG2,D0        ; else must be drive #2
        .ELSE
        MOVEQ   #2,D1	        ; else set up id code
        .ENDC

@2
        RTS		        ;exit


        .IF     EXTERNAL = 1
        .LIST
        .ENDC
        .PAGE
;-----------------------------------------------------------------------------
;  Read a Twiggy sector routine.  Uses hardware view of the world, with
;  12 bytes for header and 256 bytes (512 for new format) of data per sector.
;  Also assumes registers:
;    D0 = speed (for new Twiggy code)   A0 = disk shared memory address
;    D1 = drive/side/sector/track       A1 = address to load header(12 bytes)
;    D2 = timeout for read	        A2 = address to load data(256 or 512 bytes)
;    D3 = scratch		        A3 = VIA address for FDIR
;  If error, returns with carry bit set and error code in D0.
;------------------------------------------------------------------------------

TWGRD
        MOVE.L  #FDIRTIME,D2    ; set default timeout value	        RM000

TWGREAD
        .IF TWIGGY = 1
        MOVEM.L D3/A1-A4,-(SP)  ; save regs
        DISABLE		        ; disable interrupts
        BSR     CHKFDIR	        ;ensure no ints pending
        MOVEP.L D1,DRV(A0)      ; set disk ptrs
        .IF     NEWTWIG = 1
        MOVE.B  D0,SPEED(A0)    ;set speed value
        .ENDC
        MOVE.B  #READS,CMD(A0)  ; set for read operation
        MOVE.B  #EXRW,(A0)      ; and go do it
        BSR     CHKFIN	        ; wait
        BCS.S   TWGOUT	        ; exit if timeout
        MOVE.B  STAT(A0),D0     ; get disk return code
        MOVE.B  #$CC,CMD(A0)    ;clear RWTS interrupt bits
        MOVE.B  #CLRSTAT,(A0)
        BSR.S   CMDCHK	        ;wait until cmd taken
        BCS.S   TWGOUT	        ;exit if error
        TST.B   D0	        ;check status code
        BNE.S   TWGERR	        ; and exit if error

;  Read successful - transfer header and then data to main memory

        .IF  NEWTWIG = 0
        MOVE.B  #HDRLEN,D0      ; set header length (hardware view)
        MOVE.L  #HDRSTRT,A4     ; set ptr to Twiggy buffer

XFRHDR  MOVE.B  (A4),(A1)+      ; transfer bytes
        ADDQ.L  #2,A4	        ; bump ptr
        SUBQ.B  #1,D0	        ; and continue until done
        BNE.S   XFRHDR

        MOVE    #SECLEN,D0      ; next set sector length
        MOVE.L  #DSKBFR,A4      ; and set ptr to disk bfr
XFRDATA MOVE.B  (A4),(A2)+      ; do data transfer
        ADDQ.L  #2,A4	        ; bump disk ptr
        SUBQ    #1,D0	        ; and continue until done
        BNE.S   XFRDATA
        BRA.S   TWGOK	        ; and go to exit

        .ELSE
        LEA     DSKBUFF(A0),A4  ;set address for Twiggy buffer
XFRHDR  MOVEP.L (A4),D3	        ;load header bytes
        MOVE.L  D3,(A1)+
        MOVEP.L 8(A4),D3
        MOVE.L  D3,(A1)+
        MOVEP.L 16(A4),D3
        MOVE.L  D3,(A1)+

        LEA     DSKDATA(A0),A4  ;set address for data
        MOVE.W  #31,D0	        ;need to load 512 bytes
XFRDATA MOVEP.L (A4),D3	        ;load data bytes
        MOVE.L  D3,(A2)+
        MOVEP.L 8(A4),D3
        MOVE.L  D3,(A2)+
        MOVEP.L 16(A4),D3
        MOVE.L  D3,(A2)+
        MOVEP.L 24(A4),D3
        MOVE.L  D3,(A2)+
        ADD.W   #32,A4
        DBF     D0,XFRDATA      ;loop until done
        BRA.S   TWGOK	        ;and go to exit

        .ENDC

;  Error exit - set carry bit as error indicator

TWGOUT  MOVEQ   #TIMOUT,D0      ; set timeout error

TWGERR  ENABLE		        ; restore interrupt mask
        ORI.B   #$01,CCR
        BRA.S   TWGRXIT	        ; and exit

TWGOK   ENABLE		        ; restore interrupt mask
        CLR.L   D0	        ; set OK return code	        CHG025

TWGRXIT MOVEM.L (SP)+,D3/A1-A4  ; restore regs
        RTS		        ; and return to caller

        .PAGE
;-------------------------------------------------------------------------
;  Subroutine to check for disk command taken. Also does check for DSKDIAG
;  in case Twiggy controller becomes busy servicing second disk drive before
;  command is seen.  Loop takes about 12.4 us if DSKDIAG OK, else DSKDIAG
;  loop takes about 9.6 us.
;
;  Destroys register A0
;-------------------------------------------------------------------------

CMDCHK  MOVEM.L D3/A3,-(SP)     ;save regs
        MOVE.L  #CMDTIME,D3     ;set timeout for about 15 secs
        MOVE.L  #DISKMEM,A0     ;set ptr to shared memory
        MOVE.L  #VIA2BASE,A3    ;also set up to monitor DSKDIAG
        ANDI.B  #$BF,DDRB2(A3)
@1      TST.B   (A0)	        ;cmd taken when byte 0'ed
        BEQ.S   @2
@3      BTST    #DSKDIAG,IRB2(A3) ;check if controller not ready
        BNE.S   @4	        ;skip if OK
        SUBQ.L  #1,D3	        ;else loop until timeout or ready
        BNE.S   @3
        BRA.S   @5	        ;take error exit
@4      SUBQ.L  #1,D3
        BNE.S   @1	        ;loop until yes or timeout
@5      ORI.B   #$01,CCR        ;set error
@2      MOVEM.L (SP)+,D3/A3     ;restore regs
        RTS

;----------------------------------------------------------------------------
;  Subroutine to check for disk interrupt (FDIR asserted) - loop takes 8.8us
;  Destroys register D3 and A3
;----------------------------------------------------------------------------

CHKFIN
        .IF  NEWTWIG = 0
        MOVE.L  #FDIRTIME,D3    ;set fixed timeout of 2 mins
        .ENDC
        .IF  NEWTWIG = 1
        MOVE.L  D2,D3	        ;set user-supplied timeout
        .ENDC
        MOVE.L  #VIA1BASE,A3    ;set ptr for interface
@1      BTST    #4,(A3)	        ;FDIR?
        BNE.S   @2	        ;exit if yes
        SUBQ.L  #1,D3
        BNE.S   @1	        ;else loop
        ORI.B   #$01,CCR        ;set error
@2      RTS

        .PAGE
;-------------------------------------------------------------------------
;  Subroutine to eject disk
;  Assumes A0 = ptr to disk shared memory
;-------------------------------------------------------------------------

EJCTDSK
        .IF     NEWTWIG = 1
        BSR.S   CLRFDIR		 ;ensure interrupts cleared
        BCS.S   @1		 ;exit if error
        MOVE.L  #EJCTTIME,D2	 ;set eject timeout
        .ENDC
        MOVE.B  #UNCLAMP,CMD(A0) ;set up cmd
        MOVE.B  #EXRW,(A0)	 ;go do it
        BSR.S   CHKFIN		 ;wait for FDIR
        BCS.S   @1		 ;skip if error
        BSR.S   CLRFDIR		 ;clear intrpt and return
@1      RTS

;-------------------------------------------------------------------------
;  Subroutine to clear interrupt.  Waits for FDIR to go low before return.
;  Assumes A0 = ptr to disk shared memory.
;-------------------------------------------------------------------------

CLRFDIR MOVE.B  #$FF,CMD(A0)    ;clear FDIR
        MOVE.B  #CLRSTAT,(A0)
        BSR.S   CMDCHK	        ;wait until cmd taken
        MOVE.L  #VIA1BASE,A3    ;then wait for FDIR to go low
        MOVEQ   #25,D3	        ;set timeout for about 200 us
@1      BTST    #FDIR,(A3)      ;FDIR?
        BEQ.S   @2	        ;skip if none
        SUBQ    #1,D3	        ;else loop until gone or timeout
        BNE.S   @1
        ORI.B   #01,CCR	        ;set error indicator
@2      RTS

;-------------------------------------------------------------------------
;  Subroutine to ensure FDIR gone after clear status cmd
;-------------------------------------------------------------------------

CHKFDIR MOVE.L  #VIA1BASE,A3    ;set ptr for FDIR status
        BTST    #FDIR,(A3)      ;FDIR present?
        BEQ.S   @1	        ;skip if not
        BSR.S   CLRFDIR	        ;else do clear
@1      RTS		        ;and exit

        .IF  USERINT = 0
        .IF  NEWTWIG = 1
;-------------------------------------------------------------------------
;  Subroutine to issue clamp cmd
;-------------------------------------------------------------------------

CLAMPIT MOVE.B  #CLAMP,CMD(A0)  ;set cmd
        MOVE.B  #EXRW,(A0)      ;do it
        BSR     CHKFIN	        ;wait until done
        RTS

        .ENDC		        ;{NEWTWIG}
        .ENDC		        ;{USERINT}

        .IF  USERINT = 1
;-------------------------------------------------------------------------
;  Subroutine to enable display of wait icon.  Main entry point creates
;  alert box also, secondary enrty point (DSPWTICON) invokes icon display
;  only.
;  Inputs:
;       None
;  Outputs:
;       None
;  Side Effects:
;       A2,A3 trashed
;-------------------------------------------------------------------------

WAITALRT
DSPWTICON
        LEA     WAITICON,A2     ;and display wait icon
        BSR     DSPALRTICON
        RTS
        .ENDC		        ;{USERINT}

        .ENDC		        ;{TWIGGY}

;-----------------------------------------------------------------------------
;  Routine to reinit vectors before release of control to boot loader.
;  Sets all vectors other than reset and interrupts to jump to the
;  failing boot device handler.
;
;  Inputs:
;       A3 = address of boot error handler for bus/address errors
;       A4 = address of boot error handler for other exceptions
;  Outputs:
;       None
;  Side Effects:
;       A0/D1 trashed
;-----------------------------------------------------------------------------

VCTRINIT
        MOVEA   #BUSVCTR,A0     ;get ptr to vector locations	        RM000
        MOVE.L  A3,(A0)+        ;set up for bus error
        MOVE.L  A3,(A0)+        ;and address error
        MOVEQ   #20,D1	        ;# of remaining low vectors to init
@1      MOVE.L  A4,(A0)+        ;setup handler for errors up
        SUBQ    #1,D1	        ; to spurious intrpt vector
        BNE.S   @1
        MOVEA   #TRPVCT0,A0     ;next do all trap vectors	        RM000
        MOVEQ   #32,D1	        ;set count
@2      MOVE.L  A4,(A0)+        ;and do init
        SUBQ    #1,D1
        BNE.S   @2
        RTS

        .IF     EXTERNAL = 1
        .NOLIST
        .ENDC
        .PAGE
        .IF PROFLE = 1	        ;ASSEMBLE ONLY IF PROFILE CODE NEEDED
;-----------------------------------------------------------------------------
;  Routine to boot from Profile hard disk attached to parallel port
;  Sets up input parameters and then calls READ routine.  If no error
;  on return (carry not set), a jump to the loaded boot code is done.
;-----------------------------------------------------------------------------

PROBOOT
        .IF  USERINT = 0
        LEA     BOOTMSG,A3	        ; output boot msg
        BSR     DSPMSGR
        .ELSE
        BSR     WAITALRT	        ; display wait icon
        .ENDC

        MOVE.L  #HDRBUFR,A1	        ; set up ptr to save header
        MOVE.L  #HDRBUFR+20,A2	        ; ptr for data
        CLR.L   D1		        ; set sector #
        MOVE.L  #STRTIME,D2	        ; set timeout count (3 mins)
        MOVEQ   #RCNT,D3	        ; set retry count
        MOVEQ   #TCNT,D4	        ; set threshold count
        BSR     PROREAD		        ; go get data
        BCS.S   HDSKERR		        ; exit if error

;  Now verify header and if OK, jump to startup program

        MOVE    FILEID(A1),D0	        ; get file id
        CMP     #BOOTPAT,D0	        ; is it a boot block?
        BEQ.S   PBOOT		        ; continue if OK
        MOVEQ   #BADHDR,D0	        ; else set error code
        BRA.S   HDSKERR		        ; and exit

PBOOT   LEA     HDERR2,A3	        ;set up vectors in case of errors
        LEA     HDERR3,A4
        BSR     VCTRINIT

        MOVE.L  #HDRBUFR+20,A2	        ; set ptr for data
        BRA     STRTBOOT	        ; and go start execution

;  Error detected - output message and abort boot

HDSKERR
        .IF  USERINT = 0
        LEA     BOOTERR,A3	        ; get msg ptr
        BSR     DSPMSGR		        ; display error
        MOVEQ   #PRO,D0		        ; set device code
        BRA     DSPDVC		        ;  and go display

        .ELSE

        CMP.B   #3,SYSTYPE	        ;check system type	        CHG009/CHG029
        BEQ.S   @2		        ;skip if internal disk	        CHG009/CHG029
        LEA     PROICON,A2	        ;else setup Profile icon
        BRA.S   @3		        ;skip to do display	        CHG009
@2      LEA     UPPER,A2	        ;set for integral hard disk     CHG009

@3      CMP.B   #NODSK,D0	        ;Profile not attached error?
        BNE.S   @1		        ;skip if not

;  If default boot and no Profile attached, go to boot menu

        BTST    #ALTBOOT,D7	        ;is this a default boot?
        BNE.S   @1		        ;skip if not
        BSR     CLRDESK		        ;clear desktop
        BRA     LSTCHK		        ;and do beep and display the boot menu

@1      BSR     DSPERRICON	        ;display with bad mark

        .ENDC			        ;{PROFILE}

;  Sound error tones and display error code

BOOTFAIL
        BSR     DSPCODE		        ;display error code

BFAIL2
        BSR     HIPTCH		        ;startup failure causes hi,hi,hi tones
        BSR     HIPTCH
        BSR     HIPTCH
        BSR     CLRMENU		        ;clear menu bar
        ANDI.B  #$FC,STATFLGS	        ;allow CONTINUE option from boot error
        BSET    #NORSTRT,STATFLGS       ; but not restart option
        BRA     MONITOR		        ;and go to monitor

HDERR2  BSR4    SAVEXCP		        ;save exception info
HDERR3  BSR4    BTERR		        ;regroup from error
        BRA.S   HDSKERR		        ;and go display it

        .IF     EXTERNAL = 1
        .LIST
        .ENDC

        .PAGE
;-----------------------------------------------------------------------------
;  First initialize and then ensure disk is attached by checking OCD line.
;  Assumes ACR and IER registers of VIA set up by caller.  For boot, these
;  are cleared by power-up reset.
;  Register usage:
;    D0 = scratch use	        A0 = VIA address for parallel port interface
;    D1 = block to read	        A1 = address to save header
;    D2 = timeout count	        A2 = address to save data
;    D3 = retry count	        A3 = scratch
;    D4 = threshold count       A4 = unused
;  Returns:
;    D0 = error code (0 = OK)
;    D1 = error bytes (4)
;    D2 - D7 and A1 - A6 are preserved
;-----------------------------------------------------------------------------

PROREAD MOVEM.L D2-D7/A1-A6,-(SP)       ; save regs
        DISABLE			        ; ensure interrupts off
        BSR.S   PROINIT		        ; init for Profile access and check
                                        ;  if attached
        BEQ.S   CHKBSY		        ; go on if OK
        MOVEQ   #NODSK,D0	        ; else set error code and
        BRA.S   PROERR		        ; skip if error - no disk attached

;  Now check if Profile ready - wait time presently set for about 100 seconds
;  to allow enough time for normal Profile startup time of about 80 seconds

CHKBSY  BTST    #BSY,IRB2(A0)	        ; check if Profile ready (not busy)
        BNE.S   TRYRD		        ; skip if yes
        SUBQ.L  #1,D2		        ; else loop until timeout
        BNE.S   CHKBSY

        MOVEQ   #DSKBSY,D0	        ; set disk busy error code
        BRA.S   PROERR		        ; and go to error exit

;  Now start read and check status to see if OK

TRYRD   BSR     STRTRD		        ; go begin read process
        BCC.S   @1		        ; skip if OK		        CHG016
        BSR     WFNBSY		        ; else check for ready	        CHG016
        BSR     STRTRD		        ; and do retry		        CHG016
        BCC.S   @1		        ; continue if OK	        CHG016
        BSR     DOCRES		        ; else issue reset signal       CHG016
        BSR     WFNBSY		        ; wait until ready	        CHG016
        BSR     STRTRD		        ; and try one more time	        CHG016
        BCS.S   PROERR		        ; finally exit if error

@1      TST.W   STAT3		        ; check if reset error	        CHG016
        BPL.S   @2		        ; skip if not
        BSR     STRTRD		        ; else go try read again
        BCS.S   PROERR		        ; and abort if error

@2      TST.L   STATBFR		        ; check complete status
        BEQ.S   RDDATA		        ; skip if OK
        MOVE.L  STATBFR,D1	        ; else get status
        MOVE.L  D1,D0		        ; save for use
        ANDI.L  #STATMSK,D0	        ; mask don't care bits
        BEQ.S   RDDATA		        ; and continue if OK
        MOVEQ   #STATNZ,D0	        ; else set error code
        BRA.S   PROERR		        ; and go to error exit

;  All OK - go read block and transfer to memory; do as multiple moves for
;  max transfer rate.

RDDATA  MOVEQ   #-1,D0       ; set count for header read
        BSR.S   READIT		        ; go do it

        MOVEQ   #-1,D0       ; set count for data read
        MOVEA.L A2,A1		        ; set new read location
        BSR.S   READIT

        BRA.S   PROXIT		        ; and go to exit

;  Error exit - set carry bit as indicator flag

PROERR  ENABLE			        ;restore interrupt mask
        ORI.B   #$01,CCR
        BRA.S   PROXIT2

;  Normal exit - restore regs and exit

PROXIT  ENABLE			        ;restore interrupt mask
        CLR.L   D0		        ;set OK return code	        CHG025

PROXIT2 MOVEM.L (SP)+,D2-D7/A1-A6
        RTS

;----------------------------------------------------------------------------
;  Subroutine to init parallel port for Profile access.
;  Inputs:
;       None
;  Outputs:
;       D0 cleared for error use
;       A0 = VIA base address for parallel port
;       CCR zero bit set if cable connected
;  Side Effects:
;       None
;----------------------------------------------------------------------------

PROINIT
        CLR.L   D0		        ; clear for result use
        MOVE.L  #VIA1BASE,A3	        ; get kybd VIA base address		        CHG036
        ORI.B   #$A0,ORB1(A3)	        ; initialize profile-reset and parity-reset     CHG036
        ORI.B   #$A0,DDRB1(A3)	        ;  and set lines as outputs		        CHG036
        MOVE.L  #VIA2BASE,A0	        ; get paraport VIA base address
        ANDI.B  #$7B,PCR2(A0)	        ; set ctrl CA2 pulse mode/positive edge
        ORI.B   #$6B,PCR2(A0)
        MOVE.B  #$00,DDRA2(A0)	        ; set port A bits to input
        ORI.B   #$18,ORB2(A0)	        ; then set direction=in, cmd=false,	        CHG036
        ANDI.B  #$FB,ORB2(A0)	        ;  enable=true				        CHG036
        ANDI.B  #$FC,DDRB2(A0)	        ; set port B bits 0,1=in,
        ORI.B   #$1C,DDRB2(A0)	        ;  2,3,4=out
        BTST    #OCD,IRB2(A0)	        ; check OCD line
        RTS			        ; and exit

;----------------------------------------------------------------------------
;  Subroutine to read bytes from Profile.  Assumes:
;    D0 = byte count - 1
;    A0 = VIA address for parallel interface
;    A1 = memory load address
;----------------------------------------------------------------------------

READIT  MOVE.B  IRA2(A0),(A1)+	        ; read the bytes
        MOVE.B  IRA2(A0),(A1)+
        MOVE.B  IRA2(A0),(A1)+
        MOVE.B  IRA2(A0),(A1)+
        DBF     D0,READIT
        RTS

        .PAGE
;---------------------------------------------------------------------------
;  Subroutine to begin the read process.  First calls a routine that
;  gets in sync with Profile and sends read command.  Then a wait for
;  an appropriate response from Profile is executed.
;  Assumes regs:
;    D0 = scratch use	        A0 = VIA address
;    D1 = block to read	        A1 = unused
;    D2 = used for Profile cmd  A2 = unused
;    D3 = retry count	        A3 = scratch use
;    D4 = threshold count       A4 = unused
;  If error, carry bit set and error code in D0.
;---------------------------------------------------------------------------

STRTRD  MOVEA   #CMDBUFR,A3	        ; first set up command		        RM000
        MOVE.L  D1,(A3)		        ; set read command (0) and block #
        MOVE.B  D3,RETRY(A3)	        ; set retry count
        MOVE.B  D4,THRESH(A3)	        ; set threshhold for sparing
        BSR.S   STAT01		        ; get 01 byte and send read command
        BCS.S   STRTXIT		        ; exit if error

;  OK so far - go check if Profile ready to send data

        MOVEQ   #2,D2		        ; get 02 byte			        RM000
        BSR.S   FINDD2
        BCS.S   STRTXIT		        ; exit if timeout error

;  Get status bytes

GETSTAT MOVEA   #STATBFR,A3	        ; set buffer ptr		        RM000
        MOVE.B  IRA2(A0),(A3)+	        ; read and save the status
        MOVE.B  IRA2(A0),(A3)+
        MOVE.B  IRA2(A0),(A3)+
        MOVE.B  IRA2(A0),(A3)+

STRTXIT RTS			        ; return to caller

        .PAGE
;--------------------------------------------------------------------------
;  Subroutine to get in sync with Profile.
;  Input regs:
;    A0 = VIA address
;    A3 = ptr to command buffer
;  If error, returns with carry bit set and error code in D0
;--------------------------------------------------------------------------

STAT01  MOVEM.L D2/D4,-(SP)	        ; save regs
        MOVE.L  #1,D2		        ; try to find state 01
        BSR.S   FINDD2
        BCC.S   COPY6		        ; skip if OK
        CMP.B   #TMOUT,D0	        ; else check if timeout error
        BEQ.S   STATERR		        ; and exit if yes

        BSR     WFNBSY3		        ; ensure Profile ready		        RM000
        TST.B   D0		        ; check for timeout error
        BNE.S   STATERR		        ; and exit if yes

@2      BSR.S   FINDD2		        ; try to find state 01 again
        BCS.S   STATXIT		        ; exit if error again

COPY6   ANDI.B  #$F7,ORB2(A0)	        ; set dir=out
        MOVE.B  #$FF,DDRA2(A0)	        ; set port A bits to output
        MOVE.W  #PCMDSZ,D0	        ; set command size
COPY6LP MOVE.B  (A3)+,ORA2(A0)	        ; send the command bytes
        DBF     D0,COPY6LP
        ORI.B   #$08,ORB2(A0)	        ; reset dir=in
        MOVE.B  #$00,DDRA2(A0)	        ;  and set port A bits to input
        BRA.S   STATXIT		        ; then exit

STATERR ORI.B   #$01,CCR	        ; set error indicator

STATXIT MOVEM.L (SP)+,D2/D4	        ; restore regs
        RTS

        .PAGE
;------------------------------------------------------------------------------
;  Subroutine to handshake with Profile and wait for command completion.
;  Polls busy bit until it goes low (not busy).
;  Assumes regs:
;    A0 = VIA address
;    D2 = Expected response to previously issued command
;  If error, carry bit set and error code in D0.
;------------------------------------------------------------------------------

FINDD2  MOVEM.L D1-D4,-(SP)	        ; save regs
        ANDI.B  #$EF,ORB2(A0)	        ; set cmd=true
        MOVE.B  #$00,DDRA2(A0)	        ; set port A bits to input
        CLR.L   D0		        ; used for return code
        BSR.S   WFBSY		        ; wait for busy
        BNE.S   FINDERR		        ; exit if error

GETRSP  MOVE.B  PORTA2(A0),D1	        ; get response in D1 w/o handshake
        CLR.B   D3		        ; used for reply to Profile
        CMP.B   D2,D1		        ; did pippin return state requested ?
        BEQ.S   RSPOK		        ; skip if yes
        MOVEQ   #BADRSP,D0	        ; else set error code
        BRA.S   SNDR1		        ; and go send reply

RSPOK   MOVEQ   #$55,D3		        ; set up OK reply		        RM000

SNDR1   BSR.S   SENDRSP		        ; send response

        TST.B   D0		        ; check return code
        BNE.S   FINDERR		        ; skip if error

        BSR.S   WFNBSY		        ; now go wait for not busy

FINDERR MOVE.B  #$00,DDRA2(A0)	        ; reset port A bits to input
        ORI.B   #$18,ORB2(A0)	        ; and dir = in, cmd=false

        TST.B   D0		        ; check return code
        BEQ.S   FNDXIT		        ; skip if OK
        ORI.B   #$01,CCR	        ; else set error indicator

FNDXIT  MOVEM.L (SP)+,D1-D4	        ; restore regs(but don't affect CCR bits)
        RTS


;------------------------------------------------------------------------------
;  Subroutine to wait for Profile busy signal.  Polls busy bit until it
;  goes high (busy).
;  Assumes regs:
;    A0 = VIA address
;    D4 = timeout value if WFBSY1 entry point used
;  If error, error code in D0.
;------------------------------------------------------------------------------

WFBSY   MOVE    #RSPTIME,D4	        ; set response timeout = 100 msec

WFBSY1  BTST    #BSY,IRB2(A0)	        ; wait for busy
        BEQ.S   @9		        ; skip if OK
        SUBQ    #1,D4		        ; else loop until timeout
        BNE.S   WFBSY1
        MOVEQ   #TMOUT,D0	        ; set timeout error
@9      RTS

;------------------------------------------------------------------------------
;  Subroutine to send response command to Profile.
;  Assumes regs:
;    A0 = VIA address
;------------------------------------------------------------------------------

SENDRSP ANDI.B  #$E7,ORB2(A0)	        ; set dir=out, cmd=true
        MOVE.B  #$FF,DDRA2(A0)	        ; set port A bits to output
        MOVE.B  D3,PORTA2(A0)	        ; send reply(00 or 55) w/o handshake
        ORI.B   #$10,ORB2(A0)	        ; set cmd=false
        RTS

;------------------------------------------------------------------------------
;  Subroutine to wait for Profile not busy signal.  Polls busy bit until it
;  goes low (not busy).
;  Assumes regs:
;    A0 = VIA address
;    D4 = timeout value if WFNBSY1 entry point used
;  If error, D0 has error code.
;------------------------------------------------------------------------------

WFNBSY  MOVE.L  #RDTIME,D4	        ; set timeout for about 16 secs	        CHG037
        BRA.S   WFNBSY1		        ;				        CHG019

WFNBSY2 MOVE.L  #STRTIME,D4	        ; set initial Profile self-test time    CHG019
        BRA.S   WFNBSY1		        ;				        CHG019

WFNBSY3 MOVE.L  #BSYTIME,D4	        ; set timeout for about 10 ms	        RM000

WFNBSY1 BTST    #BSY,IRB2(A0)	        ; wait for not busy
        BNE.S   @9		        ; exit if OK
        SUBQ.L  #1,D4		        ; else loop until timeout
        BNE.S   WFNBSY1
        MOVEQ   #TMOUT,D0	        ; set timeout error
@9      RTS

;-----------------------------------------------------------------------------
;  Subroutine to send reset to Profile controller to enable handshake retry     CHG016
;-----------------------------------------------------------------------------

DOCRES  MOVE.L  #VIA1BASE,A3	        ;use keyboard 6522		        CHG026
        ANDI.B  #$7F,ORB1(A3)	        ;set reset signal		        CHG016/CHG026/CHG036
        BSR     DELAY_1		        ;delay for controller to get signal     CHG016
        ORI.B   #$80,ORB1(A3)	        ;remove reset signal		        CHG016/CHG026/CHG036
        BSR     DELAY_1		        ;delay for controller to respond        CHG038
        RTS			        ;and exit

        .ENDC			        ;{PROFLE}
        .IF     EXTERNAL = 1
        .NOLIST
        .ENDC
        .PAGE
;-------------------------------------------------------------------------
;  Routine to boot from I/O slot.
;  Verifies that slot has bootable card installed and then reads in ROM
;  data.  If status routine exists it is executed, else jump to boot
;  routine done if checksum OK.
;
;  Inputs:
;       D0 = boot device id ($4-$C)
;       A0 = scratch use
;       A1 = slot address
;  Outputs: (relayed to loaded boot program)
;       D0 = boot device id
;       A1 = slot address
;-------------------------------------------------------------------------

IOSBOOT MOVE.B  D0,D4	        ;save boot device id
                                ; also acts as flag for status check
        MOVE.L  BUSVCTR,A5      ;save bus error vector
        MOVE.L  SP,A6	        ;save current stack pointer

        LEA     NOCRD,A3        ;setup new bus error vector
        MOVE.L  A3,BUSVCTR
        MOVEP   (A1),D1	        ;read card id

        .IF  USERINT = 0
        LEA     BOOTMSG,A3      ;output boot msg
        BSR     DSPMSGR
        .ENDC

        TST     D1	        ;check if card installed
        BPL.S   INVID	        ;exit if not bootable
        CMP     #$FFFF,D1       ;check if special case
        BEQ.S   INVID

        .IF  USERINT = 1
        BSR     WAITALRT        ;display wait icon
        .ENDC

        MOVE.L  A1,A2	        ;get slot address
        BSR.S   RDIOSLT	        ;go check the board
        BCS.S   BADBRD	        ;exit if error

STATOK
        MOVE.B  BOOTDVCE,D0     ;setup boot device id
        MOVE.L  #BTENTRY,A2     ;and starting program address
        BRA     STRTBOOT        ;and go do boot ...

;  Error routines for I/O slot booting. Error code saved and error message output.

NOCRD   MOVEQ   #NOC,D0	        ;set error code
        MOVE.L  A5,BUSVCTR      ;restore bus error vector
        MOVE.L  A6,SP	        ;and stack ptr
        BRA.S   SENDMSG	        ;then go display msg

INVID   MOVEQ   #INV,D0	        ;set error code
        BRA.S   SENDMSG	        ;go do display

BADBRD  MOVE.B  BOOTDVCE,D4     ;restore boot device id for checking

SENDMSG
        MOVE.B  D0,BOOTDATA     ;save error code

        .IF  USERINT = 0
        LEA     BOOTERR,A3      ;set ptr to msg
        BSR     DSPMSGR	        ;and display
        MOVE.B  D4,D0	        ;set up boot device id for display
        BRA     DSPDVC	        ; and go do it
        .ELSE

;  determine which slot # being used

        LEA     XCARD,A2        ;set general I/O slot card ptr
        CMP.B   #IO1PORT2,D4    ;in slot 1 range?
        BGT.S   @1
        MOVEQ   #1,D1	        ;yes - set slot #1
        BRA.S   @3

@1      CMP.B   #IO2PORT2,D4    ;slot 2 range?
        BGT.S   @2
        MOVEQ   #2,D1	        ;set slot 2 id
        BRA.S   @3

@2      MOVEQ   #3,D1	        ;else must be slot 3

@3      BSR     DSPNUMICON      ;display error icon and slot #
        BRA     BOOTFAIL        ;and signal boot failure
        .ENDC

        .PAGE
        .IF  NEWTWIG = 1
;----------------------------------------------------------------------------
;  Routine to read ROM on an I/O slot.
;  Inputs:
;       A2 = I/O slot base address
;       D4 = nonzero if status check also to be done, else 0 for no check
;       D1 = card id if status check needed
;  Outputs:
;       Carry bit set if error, error code saved in D0, error code from
;       status check saved in BOOTDATA+1
;  Side Effects:
;       D0, A2 trashed
;----------------------------------------------------------------------------

RDIOSLT MOVEM.L D1-D3/A0-A1,-(SP) ;save regs
        MOVE.L  #ADR128K-4,A0   ;set load pt (also load id/word count)
        MOVE.L  A2,A1	        ;save slot address for later use
        CLR.L   D0	        ;clear for use
        MOVEP   4(A2),D0        ;read word count
        ADDQ    #2,D0	        ;incr for id/count fields
        CMPI    #$0FFF,D0       ;check for max count
        BHI.S   INVSUM	        ;exit if error
        CLR.L   D2	        ;clear for use
        CLR.L   D3

LOADPGM MOVEP   (A2),D2	        ;read word
        MOVE    D2,(A0)	        ;save in memory
        MOVE    (A0)+,D2        ;reread it from memory
        ADD     D2,D3	        ;add to checksum
        ROL     #1,D3	        ;rotate for better effectiveness
        ADDQ.L  #4,A2	        ;bump address ptr
        SUBQ    #1,D0
        BNE.S   LOADPGM	        ;loop until done

        MOVEP   (A2),D2	        ;read expected checksum (2 bytes)
        ADD     D2,D3	        ;add to calculated checksum
        TST     D3	        ;check for 0 result (also clears carry bit)
        BNE.S   INVSUM	        ;skip if error

;  Do status check if desired and available

        TST     D4	        ;do status check?
        BEQ.S   RDIOXIT	        ;skip if not
        BTST    #STBIT,D1       ;status routine available?
        BEQ.S   RDIOXIT	        ;skip if not

        MOVEM.L D4-D7/A2-A6,-(SP) ;save regs not already saved
        JSR     STENTRY		  ;go execute status routine
        MOVEM.L (SP)+,D4-D7/A2-A6 ;restore regs

        TST     D0	        ;check status
        BEQ.S   RDIOXIT	        ;skip if no error
        MOVE.B  D0,BOOTDATA+1   ;save card error code
        MOVEQ   #BADST,D0       ;set general error code
        BRA.S   SAVERR

INVSUM  MOVEQ   #BADSM,D0       ;set invalid checksum

SAVERR  MOVE.B  D0,BOOTDATA     ;save error code
        ORI.B   #$01,CCR        ;set error indicator

RDIOXIT MOVEM.L (SP)+,D1-D3/A0-A1 ;restore regs
        RTS

        .ENDC

        .IF     BURNIN = 1
        .PAGE
;-------------------------------------------------------------------------
;  Special code to enable burnin cycling by the ROM.  Does initialization
;  on first pass, and then causes cycling for the specified cycle count,
;  which defaults to approximately 60 minutes.  At
;  that point a branch to a system shutdown routine is performed.
;-------------------------------------------------------------------------

;  Do first pass initialization

CHKPASS CMP.B   #$01,INITFLG    ;first pass done?
        BEQ.S   CHKTIM	        ;skip if yes
CHKPAS2 MOVEA.L #INITFLG,A0     ;set ptr for other data areas
        MOVEA.L #ENDPM,A1       ;and ending ptr

CLRPM   CLR.B   (A0)	        ;do clear
        ADDQ.L  #2,A0	        ;bump ptr
        CMPA.L  A0,A1	        ;loop until done
        BNE.S   CLRPM

        MOVEQ   #1,D0
        MOVE.B  D0,INITFLG      ;set init flag
        MOVE.B  #60,CYCLVAL     ;set cycling for 60 minutes
        CLR.L   CLKDATA	        ;and init clock data area
        CLR     CLKDATA+4

;  Set clock to initial value so run can be ended at cycle count.  Sends
;  value of day=01, all other values=0 (e.g., time = 00:00:00).

        MOVEQ   #$2C,D0	        ;set up clock set cmd
        BSR     COPSCMD	        ;and send to COPS
        BCS.S   @9	        ;exit if error			        RM000
        MOVE.L  #SET1,D1        ;set up value for clock
        MOVEQ   #8,D2	        ;set "char" count
        BSR     TODSET	        ;and go do it
        BCS.S   @9	        ;				        RM000
        MOVE.L  #SET2,D1        ;set up next value for clock
        MOVEQ   #8,D2	        ;set "char" count
        BSR     TODSET	        ;and go do it
        BCS.S   @9	        ;				        RM000
        MOVEQ   #$25,D0	        ;finally set up clock enable cmd
        BSR     COPSCMD	        ;and send it
@9      BCS     SETERR1	        ;exit if error

;  Check to see if cycle count to be changed and if time data needs to be saved

CHKTIM
        .IF  USERINT = 1
        BSR     MAKEPCALRT      ;setup powercycle alert box
        .ENDC

        BTST    #MSBUTN,STATFLGS ;mouse button detected?
        BEQ.S   @3	        ;skip if no			        RM000
        MOVE.B  CYCLVAL,D0      ;read current setting		        RM000
        CMP.B   #60,D0	        ;long cycle set?		        RM000
        BNE.S   @1
        MOVEQ   #3,D0	        ;set for 3 minute cycle		        RM000
        BRA.S   @2
@1      MOVEQ   #60,D0	        ;set for 60 minute cycle	        RM000
@2      MOVE.B  D0,CYCLVAL      ;save in PM			        RM000

@3      CMP.B   #$01,TIMFLG     ;time data saved?		        RM000
        BEQ.S   TWGCHK	        ;skip if yes

        MOVE.L  HOUR,D0	        ;get minutes
        ROL.L   #4,D0
        SWAP    D0
        MOVE.B  D0,MINSAV       ;save minutes
        CLR.B   MINCNT	        ;and clear minute count
        CLR.B   CYCLCNT	        ; and cycle count
        MOVE.B  #$01,TIMFLG     ;and set flag

;  Check if time for Twiggy test (do every two minutes)

TWGCHK  CMP.B   #2,MINCNT       ;check minute counter
        BNE     WRTMSG

        CLR.B   MINCNT	        ;clear counter
        LEA     TWGMSG,A3       ;get msg ptr
        BSR     DSPMSGR	        ;and display it
        MOVEQ   #PCCOL,D6       ;reset left margin

        LEA     DSKVCT,A3       ;set up bus error vector
        MOVE.L  A3,BUSVCTR
        MOVE.L  #DISKMEM,A0     ;set ptr to shared memory
        MOVE.L  #VIA1BASE,A3
        TST.B   SYSTYPE	        ;check system type		        CHG009
        BEQ.S   @1	        ;skip if Lisa 1.0		        CHG009
        MOVEQ   #80,D4	        ;else set track count for SONY drive    CHG009
        BRA.S   @2	        ;and go test single drive	        CHG009

@1      CLR.L   D1	        ;else set for drive 1, track 0 to start
        MOVEQ   #45,D4	        ;set count (# of tracks)

;  Now do the drive test, one drive at a time

        BSR     CLRFDIR	        ;first clear interrupts
        BCS.S   TSTERR	        ;exit if error
        BSR     TWGTST	        ;go do test
        BCS.S   TSTERR
        MOVEQ   #45,D4	        ;reset track count		        CHG009

@2      CLR.L   D1	        ;set for drive 2		        CHG009
        MOVE.B  #$08,D1
        ROR.L   #4,D1
        BSR     TWGTST	        ;and do test again
        BCC.S   DISINT	        ;and continue if OK

TSTERR  LEA     TWGFAIL,A3      ;display error msg
        BSR     DSPMSGR
        MOVEQ   #PCCOL,D6       ;reset left margin
        CMP.B   #TIMOUT,D0      ;timeout error?
        BEQ     CMDERR	        ;exit testing if yes
        ADDQ.B  #1,DSKCNTL      ;else bump low error count
        BCC.S   DISINT	        ;skip if no overflow
        ADDQ.B  #1,DSKCNTH      ;else bump high counter also

;  Disable interrupt so disks can be ejected

DISINT  BSR     TWGDSP	        ;display Twiggy error count
        MOVE.B  #$88,CMD(A0)    ;set ptr for both drives
        MOVE.B  #DSABLINT,(A0)  ;send disable cmd
        BSR     CMDCHK	        ;wait until done

;  Output initial message

WRTMSG  LEA     BRNMSG,A3       ;get msg ptr
        BSR     DSPMSG	        ;and display it
        MOVE.B  CYCLVAL,D0      ;get cycling value
        BSR     DSPDEC	        ;display as decimal
        MOVEQ   #PCCOL,D6       ;set col for window limits

;  Increment loop count and display it on screen

CNTINC  ADDQ.B  #1,LCNTLO       ;inc low byte
        BCC.S   DSPTIM	        ;skip if no carry
        ADDQ.B  #1,LCNTHI       ;else inc high byte also

DSPTIM  BSR     DSPCLK	        ;go display time

;  Now check time to see if update needed

        MOVE.L  HOUR,D0	        ;get minute value
        ROL.L   #4,D0
        SWAP    D0
        CMP.B   MINSAV,D0       ;has value changed?
        BEQ.S   NOCHG	        ;skip if not
        ADDQ.B  #1,MINCNT       ;else bump minute count
        ADDQ.B  #1,CYCLCNT      ;and cycle count
        MOVE.B  D0,MINSAV       ;save new minute value

;  Delay so screen can be read

NOCHG   BSR     DELAY5	        ;delay for 5 secs

;  Check to see if run should be ended

        MOVE.B  CYCLCNT,D0      ;get cycle count
        MOVE.B  CYCLVAL,D1      ;get desired cycle value
        CMP.B   D1,D0	        ;cycle if same or greater
        BGE.S   SHUTDOWN

;  If not, cause double bus fault to restart diagnostics
;  First make parameter memory valid

        MOVE.B  #PC,D0	        ;set power-cycle boot code
        BSR     SAV2PM	        ;and go set param mem
        BSR     PROINIT	        ;check for attached hard disk   CHG019
        BNE.S   @1	        ;skip if none		        CHG019
        BSR     WFNBSY2	        ;else wait until disk ready     CHG019
@1      BRA     DORESET	        ;then go cause a system reset

;  Do soft power-off for specified cycle period

SHUTDOWN
        CLR.B   TIMFLG	        ;reset time save indicator
        MOVE.L  CLKDATA+2,D0    ;and save clock data
        MOVE.L  #CLKSAVE,A1
        MOVEP.L D0,(A1)

;  Disable Twiggy controller to avoid any RAM problems

DSCONT
        MOVE.L  #DISKMEM,A0     ;set ptr to shared memory
        MOVE.B  #DIE,(A0)       ;and send "die" cmd
        BSR     CMDCHK	        ;wait until done
        BCS.S   CMDERR	        ;exit if error

        MOVEQ   #$2D,D0	        ;enable alarm setting
        BSR     COPSCMD
        BCS.S   SETERR2

        CLR.L   D1
        MOVE.B  CYCLVAL,D1      ;get desired shutdown time
        MOVEQ   #60,D0	        ;multiply by 60 for seconds
        MULU    D0,D1
        MOVEQ   #12,D0	        ;rotate to send as alarm value
        ROL.L   D0,D1

        MOVE.L  #ALRMSAV,A1
        MOVEP.L D1,(A1)	        ;save alarm value
        MOVEQ   #5,D2	        ;5 digits for alarm value
        BSR.S   TODSET
        BCS.S   SETERR2

;  Make parameter memory valid

        MOVE.B  #PC,D0	        ;set power-cycle boot code
        BSR     SAV2PM	        ;and go set param mem

;  And finally send power-off cmd

        MOVEQ   #$23,D0	        ;set up enable/power off cmd
        BSR     COPSCMD	        ;send it
        BCS.S   SETERR2
SELF    NOP
        BRA.S   SELF	        ;goodbye ...

SETERR1 MOVEQ   #SERR1,D0       ;set error code
        BRA.S   DSPERR	        ;and go display

SETERR2 MOVEQ   #SERR2,D0       ;set error code

DSPERR
        .IF  USERINT = 0
        LEA     IOMSG,A3        ;display error
        BSR     DSPMSGR
        .ELSE
        LEA     IOBRD,A2        ;set icon ptr
        BSR     DSPERRICON      ;display it
        .ENDC

        BSR     DSPCODE
        BRA     MONITOR	        ;and exit to monitor

;  Error routine if disk cmd not taken

CMDERR  BSET    #DISK,D7        ;set error bit
        BRA     TSTCHK	        ;and exit

        .PAGE
;-------------------------------------------------------------------------
;  Subroutine to send clock data.  Assumes registers:
;       D0 = scratch use
;       D1 = clock data
;       D2 = digit count
;-------------------------------------------------------------------------

TODSET  ROL.L   #4,D1	        ;get digit
        MOVE.B  D1,D0	        ;set up for COPS as 1X
        ANDI.B  #$0F,D0	        ; where X = digit for clock
        ORI.B   #$10,D0
        BSR     COPSCMD	        ;and send it
        BCS.S   SETXIT	        ;exit if error
        SUBQ    #1,D2	        ;decr count
        BNE.S   TODSET	        ;and loop until done
SETXIT  RTS

        .PAGE
;--------------------------------------------------------------------------
;  Subroutine to do Twiggy testing
;  Expects
;       D0 = scratch use	        A0 = shared memory address
;       D1 = drive parameters	        A1 = unused
;       D2 = FDIR timeout value	        A2 = unused
;       D3 = unused		        A3 = VIA address for FDIR access
;       D4 = loop count for reads
;--------------------------------------------------------------------------

TWGTST  MOVE.B  #$88,CMD(A0)    ;enable interrupts from both drives
        MOVE.B  #ENBLINT,(A0)   ;do it
        BSR     CMDCHK	        ;wait until done
        BCS.S   TERR	        ;exit if error
        BCLR    #FDIR,DDRB1(A3) ;enable FDIR bit
        MOVE.L  #FDIRTIME,D2    ;set timeout value for FDIR

TWGLOOP MOVEP.L D1,DRV(A0)      ; set disk ptrs
        MOVE.B  #READS,CMD(A0)  ; set for read operation
        MOVE.B  #EXRW,(A0)      ; and go do it
        BSR     CHKFIN	        ; wait
        BCS.S   TOOLONG	        ; exit if timeout
        MOVE.B  STAT(A0),D0     ; get disk return code
        BSR     CLRFDIR	        ; clear interrupt indicator
        BCS.S   TOOLONG
        TST.B   D0	        ;any error?
        BNE.S   TERR	        ; and exit if error
        ADDQ    #1,D1	        ;incr track ptr
        SUBQ    #1,D4	        ;decrement count
        BNE.S   TWGLOOP	        ;loop until done
        RTS

TOOLONG MOVEQ   #TIMOUT,D0      ;set error code
TERR    ORI.B   #$01,CCR        ;set indicator

        RTS		        ;and exit

;------------------------------------------------------------------------
;  Subroutine to display clock reading as D HH MM SS
;------------------------------------------------------------------------

DSPCLK  LEA     TIMMSG,A3       ;get msg ptr
        BSR     DSPMSG	        ;and display it
        ADDQ    #1,D6	        ;add extra space
        BSR     READCLK	        ;go read clock
        MOVE.L  CLKDATA+2,D0    ;get time (minus Ey/dd digits)
        MOVE.L  #CLKSAVE,A1     ;and save it
        MOVEP.L D0,(A1)

        ROL.L   #4,D0	        ;get day value
        MOVEQ   #1,D1	        ;set # of digits to display
        BSR     OUTCH	        ;and display it
        ADDQ    #1,D6	        ;bump col ptr

        ROL.L   #8,D0	        ;get hour
        MOVEQ   #2,D1	        ;and display
        BSR     OUTCH
        ADDQ    #1,D6

        ROL.L   #8,D0	        ;display minute
        MOVEQ   #2,D1
        BSR     OUTCH
        ADDQ    #1,D6

        ROL.L   #8,D0	        ;display seconds
        MOVEQ   #2,D1
        BSR     OUTCHR

        .IF  USERINT = 1
        MOVEQ   #PCCOL,D6       ;set col for window
        .ENDC

        RTS


        .PAGE
;-------------------------------------------------------------------------
;  Subroutine to display Twiggy error count
;-------------------------------------------------------------------------

TWGDSP  MOVEM.L D0-D1/A3,-(SP)  ;save regs
        LEA     TWGRSLT,A3      ;output msg
        BSR     DSPMSG
        MOVE.L  #DSKCNTH,A3     ;set ptr to error count
        MOVEP   (A3),D0	        ;get count
        MOVEQ   #4,D1	        ;# of digits to display
        BSR     OUTCHR

        .IF  USERINT = 1
        MOVEQ   #PCCOL,D6       ;set col for window
        .ENDC

        MOVEM.L (SP)+,D0-D1/A3  ;restore and exit
        RTS

        .ENDC

        .PAGE
        .IF  USERINT = 0
;-------------------------------------------------------------------------
;  Boot failed - see if device code should be displayed
;  Assumes D0 = ASCII code to be displayed
;-------------------------------------------------------------------------

DSPDVC
        LEA     DVCMSG,A3	        ;set msg ptr
        BSR     DSPMSG		        ;and display
        BSR     DSPVAL		        ;then display device code
        ADDQ    #1,D5		        ;bump cursor ptrs to next row
        MOVEQ   #1,D6
        BRA.S   MONITOR		        ;exit to monitor

        .ENDC