Lisa_Boot_ROM_RM248

        .IF     EXTERNAL = 1
        .NOLIST
        .ENDC

        .PAGE
        .ABSOLUTE	        ;makes listing look nicer
        .PROC   LISAROM,0

        .ORG    0	        ; ORG'ED AT 0 BUT RUNS AT $00FE0000

;  Reset vectors here to pick up SP and PC values

BASE
        .WORD   $0000	        ;initial SP
        .WORD   STKBASE

        .WORD   ROMSLCT	        ;initial PC (assumes use of MMU reg 127)
        .WORD   BEGIN

;  Set up next locations for exception vectors

BUSVCT  .WORD   ROMSLCT		  ; BUS ERROR VECTOR
        .WORD   EXCPERR
ADRVCT  .WORD   ROMSLCT		  ; ADDRESS ERROR
        .WORD   EXCPERR
ILLVCT  .WORD   ROMSLCT		  ; ILLEGAL INSTRUCTION
        .WORD   EXCPERR
DIV0VCT .WORD   ROMSLCT		  ; DIVIDE BY ZERO ERROR
        .WORD   EXCPERR
CHKVCT  .WORD   ROMSLCT		  ; CHK INSTRUCTION
        .WORD   EXCPERR
TRAPVCT .WORD   ROMSLCT		  ; TRAPV INSTRUCTION
        .WORD   EXCPERR
PRIVCT  .WORD   ROMSLCT		  ; PRIVILEGE VIOLATION
        .WORD   EXCPERR
TRCVCT  .WORD   ROMSLCT		  ; TRACE OPERATION
        .WORD   EXCPERR
L10VCT  .WORD   ROMSLCT		  ; OPCODE 1010 DETECTED
        .WORD   EXCPERR
L11VCT  .WORD   ROMSLCT		  ; OPCODE 1111 DETECTED
        .WORD   EXCPERR

;------------------------------------------------------------------
;  Exception and interrupt vector handler for ROM - resets SP and
;  tries a restart
;------------------------------------------------------------------

EXCPERR MOVEA   #STKBASE,SP     ;reset stack ptr
        CLR.L   D7	        ;clear error indicator		        CHG004
        BRA     ROMTST	        ;and restart diags		        CHG004

        .PAGE

;--------------------------------------------------------------------
;  Subroutine for saving registers and stack pointers
;--------------------------------------------------------------------

SAVEREGS
        MOVE.L  SP,SUPSTK       ;save sup stack ptr
SAVEREG2
        MOVE.L  A6,A6SAV        ;save other regs (that aren't reset)
        MOVE.L  USP,A6
        MOVE.L  A6,USPSAV
        MOVE    #A6SAV,A6       ;set ptr for saving regs
        MOVEM.L D0-D7/A0-A5,-(A6)
        RTS

;  use spare bytes for message

SVCMSG  .ASCII  'SERVICE MODE'  ;				        RM000
        .BYTE   0	        ;				        RM000

        .ORG    $60

;  The next set of vectors cover spurious and autovector interrupts

SPURVCT .WORD   ROMSLCT		  ; SPURIOUS INTERRUPT
        .WORD   EXCPERR
LVL1VCT .WORD   ROMSLCT		  ; INTERNAL I/O INTERRUPTS (DISK,VERT TRACE,ETC.)
        .WORD   EXCPERR
LVL2VCT .WORD   ROMSLCT		  ; KEYBOARD INTERRUPT
        .WORD   EXCPERR
LVL3VCT .WORD   ROMSLCT		  ; I/O SLOT 2 INTERRUPT
        .WORD   EXCPERR
LVL4VCT .WORD   ROMSLCT		  ; I/O SLOT 1
        .WORD   EXCPERR
LVL5VCT .WORD   ROMSLCT		  ; I/O SLOT 0
        .WORD   EXCPERR
LVL6VCT .WORD   ROMSLCT		  ; RS-232
        .WORD   EXCPERR
LVL7VCT .WORD   ROMSLCT		  ; NMI
        .WORD   NMIEXCP		  ;				        RM010

        .IF     EXTERNAL = 1
        .LIST
        .ENDC

        .PAGE
        .ORG    $80
;-------------------------------------------------------------------------
;  Jump Table for calling by external routines
;-------------------------------------------------------------------------

JMPTBL
        JMP     DORESET	        ;go to restart point		        RM000
        JMP     INITMON	        ;jump to ROM Monitor

        .IF  USERINT = 0
        JMP     DSPMSG	        ;display a message
        .ELSE
        JMP     CONVRTD5        ;convert row ptr and display message
        .ENDC

        JMP     WRTMMU	        ;write to set of MMU registers
        JMP     PROREAD	        ;Profile read a block routine
        JMP     TWGREAD	        ;Twiggy read a sector routine

        .IF  DIAGS = 1
        JMP     RAMTEST	        ;basic memory test
        NOP		        ;				        CHG015
        RTS		        ;				        CHG015
        NOP		        ;				        CHG015
        RTS		        ;				        CHG015
        .ELSE
        NOP
        RTS
        NOP
        RTS
        NOP
        RTS
        .ENDC

        JMP     READMMU	        ;read MMU registers
        JMP     COPSCMD	        ;Send COPS command

        .IF  DIAGS = 1
        JMP     READCLK	        ;Read clock setting
        .ELSE
        NOP
        RTS
        .ENDC

        JMP     DSPDEC	        ;display hex error code in decimal
        JMP     CONSET2	        ;for setting contrast
        JMP     TONE	        ;to beep speaker
        JMP     VFYCHKSM        ;verify checksum

        .IF  ROM4K = 0
        JMP     WRTSUM	        ;rewrite parameter memory	        CHG017
        JMP     RDSERN	        ;go read system serial #
        .ENDC

;******************** Loop point for ROM test failure ********************************

SPIN    BRA.S   SPIN	        ;hang system				        CHG007

;*************************************************************************************

        .IF     EXTERNAL = 1
        .NOLIST
        .ENDC

;----------------------------------------------------------------------------
;  NMI Exception Handler						        RM010
;----------------------------------------------------------------------------

NMIEXCP CLR.B   SETUP	        ;enable memory access			        RM010
        BTST    #1,STATREG      ;parity error?				        RM010
        BNE.S   @1	        ;skip if not to ignore			        RM010
        MOVE    MEALTCH,ADRLTCH ;save address if yes			        RM010
        TST.B   PAROFF	        ;and toggle to clear error bit		        RM010
        TST.B   PARON	        ;					        RM010
@1      TST.B   SETUPON	        ;return to SETUP state			        RM010
        RTE		        ;					        RM010

        .PAGE
;----------------------------------------------------------------------------
;  First do "warm-start" no reset check - scan I/O MMU regs to see if set up
;----------------------------------------------------------------------------

BEGIN
        .IF     NORESET = 1
        MOVE    MMU126L,D0      ;check reg 126 for special I/O space
        ANDI    #$0FFF,D0       ;ignore don't care bits
        CMPI    #IOLMT2,D0      ;for no reset, 126L = $x901  (x = random value)
        BNE.S   BEGIN2	        ;skip if not set up
        ANDI    #$0FFF,MMU126B  ;else also check 126B = $x000
        BNE.S   BEGIN2

;  Check OK - set MMU for ROM access and change SETUP before vectoring

        MOVE    #MEMLMT,MMU0L   ;set low memory for r/w (to save regs,etc.)
        MOVE    #IOLMT2,MMU126L ;set for I/O space access (reset value)
        MOVE    #SPLMT,MMU127L  ;set access for ROM space
        MOVE    #0,MMU127B
        CLR.B   SETUP	        ;enable memory access

        MOVE.L  SP,SUPSTK       ;save supervisor stack ptr
        MOVEA   #STKBASE,SP     ;move stack pointer for ROM use
        BSR.S   SAVEREG2        ;save other registers

;  Restore ROM Monitor environment

        BSR4    CONSET	        ;go set default contrast
        SUBA.L  A2,A2	        ;set for no icons
        CLR.L   D0	        ; error codes
        SUBA.L  A3,A3	        ;  or messages
        BRA     INIT1	        ;exit directly to monitor (avoid resaving regs)

        .ENDC

;-------------------------------------------------------------------------
;  Do second warm-start check to see if contrast should be reset
;-------------------------------------------------------------------------

BEGIN2  CLR.L   D7	        ;clear for error use
        MOVE    MMU127L,D0      ;check reg 127 for ROM space
        ANDI    #$0FFF,D0       ;ignore don't care bits
        CMPI    #SPLMT,D0       ;expect 127L = $xF00  (x = random value)
        BNE.S   ROMTST	        ;skip if not
        ANDI    #$0FFF,MMU127B  ;else check if 127B = $x000
        BNE.S   ROMTST

;  Check OK - set MMU for I/O and ROM access and go set contrast

        BSET    #WRMSTRT,D7     ;set warm start indicator
        MOVEQ   #0,D0	        ;clear for use
        MOVE    #IOLMT,MMU126L  ;set access for I/O space
        MOVE    D0,MMU126B
        MOVE    #SPLMT,MMU127L  ;set access for ROM space

BEGIN3  RESET		        ;ensure clean I/O state for "warm-start"

        .IF  NEWLISA = 1
        BSR4    CONOFF	        ;and go disable contrast
        .ELSE
        BSR4    CONSET	        ;go set default contrast
        .ENDC


        .PAGE
;-------------------------------------------------------------------------
;  Start diagnostics - do ROM checksum test first; expected result = 0
;-------------------------------------------------------------------------

ROMTST
        .IF  DIAGS = 1

        CLR.L   D0	        ;clear for checksum use
        LEA     BASE,A0	        ;init ROM address ptrs
        LEA     LAST,A1
DOSUM   ADD     (A0)+,D0        ;read location and add to sum
        ROL     #1,D0	        ;rotate to catch multiple bit errors
        CMPA.L  A0,A1	        ;loop until done
        BNE.S   DOSUM
        ADD     (A0)+,D0        ;add checksum word
        BNE     SPIN	        ;loop if error			        CHG007
        TST.L   D7	        ;in loop mode?
        BMI.S   ROMTST	        ;restart test if yes

        .ENDC
        .PAGE
;----------------------------------------------------------------------------
;  Next do read/write and address test of MMU supervisor regs
;  Register Usage (by this routine and/or its subroutines):
;       A0 = MMU reg pointer	        D0 = test pattern
;       A1 = last MMU limit reg addr    D1 = contents read from MMU reg
;       A2 = MMU address increment      D2 = OR mask of results
;       A3 = last MMU base reg addr     D3 = pattern expected at last error
;       A4 = used for return address    D4 = final value for MMU reg
;       A5 = MMU address of last error  D5 = unused
;       A6 = used for return address    D6 = unused
;       A7 = stack pointer	        D7 = error indicator (0 = R/W error)
;----------------------------------------------------------------------------

MMUTST
        .IF     DIAGS = 1
        BSR4    MMUINIT	        ;initialize test variables
        BSR6    MMURW	        ;and go do read/write test
        BNE.S   MMUERR	        ;abort if error

        BSRS4   MMUINIT	        ;reinitialize
        BSR6    MMUACHK	        ;and do address test
        BNE.S   @2	        ;skip if error
        BRA     SETMMU	        ;else go do initial MMU setup
@2      NOT     D7	        ;set address error indicator

        .PAGE
;----------------------------------------------------------------------------
;  The following code is used to toggle every address and data line
;  going to the MMU if an error in the MMU context 0 tests is found.
;  Reset signals indicate read/write or addressing error.
;----------------------------------------------------------------------------

MMUERR  TST     D7	        ;check error type
        BEQ.S   @2
        RESET		        ;two reset signals for address error
@2      RESET		        ;only one for R/W error

;  Toggle every data and address bit

MMULP   MOVE.L  #$00028000,A0   ;set MMU limit reg start address
        MOVEQ   #1,D1	        ;and starting data pattern
        MOVEQ   #7,D2	        ;and loop count
        BSRS4   TSTLOOP	        ;go toggle for limit regs
        MOVE.L  #$00028008,A0   ;set MMU base reg start address
        MOVEQ   #5,D2	        ;and loop count
        BSRS4   TSTLOOP	        ;go test base regs

        BRA.S   MMUERR	        ;and loop indefinitely

;  Subroutine to do reg testing

TSTLOOP MOVE.L  A0,D0	        ;save starting address
REGTST  MOVE    D1,(A0)	        ;do write
        MOVE    (A0),D3	        ;then read
        LSL     #1,D1	        ;update pattern
        SWAP    D0	        ;get address
        LSL     #1,D0	        ;update and restore
        SWAP    D0
        MOVE.L  D0,A0
        SUBQ    #1,D2	        ;loop until done
        BNE.S   REGTST
        RTS4		        ;exit

        .PAGE
;----------------------------------------------------------------------------
;  Subroutine to do initial setup for MMU testing
;----------------------------------------------------------------------------

MMUINIT MOVE    #PATRN2,D0      ;set test pattern
        MOVEQ   #0,D1	        ;clear for result/error use
        MOVEQ   #0,D2	        ; use MOVEQ for speed
        MOVE.L  #ADR128K,A2     ;set up increment value
        ORI     #$0710,SR       ;set extend bit and disable interrupts
        RTS4

        .PAGE
;----------------------------------------------------------------------------
;  Subroutine to do MMU Read/Write Test for all registers in one context.
;  Zero bit set in CCR if no errors.
;----------------------------------------------------------------------------

MMURW   MOVE.L  #MMUSADRL,A0    ;SET MMU LIMIT START ADDR
        MOVE.L  #MMUEADRL,A1    ;SET MMU LIMIT END ADDR
        MOVE.L  #MMUEADRB,A3    ;SET MMU BASE END ADDR

RWCHK1  BSR4    CHKRW	        ;GO DO READ/WRITE CHECK
        NOT     D0	        ;INVERT FOR NEXT PASS
        BSRS4   CHKRW	        ;GO DO AGAIN
RWCHK2  NOT     D0	        ;INVERT BACK TO ORIGINAL PATTERN
        BSRS4   CHKRW	        ;ONE MORE TIME
RWCHK3  ROXL    #1,D0	        ;SET UP NEW PATTERN
        CMPA.L  A0,A1	        ;CHECK IF DONE
        BEQ.S   CHKBASE	        ;IF YES GO CHECK FOR BASE REG TESTING
        ADDA.L  A2,A0	        ;ELSE BUMP MMU ADDR
        BRA.S   RWCHK1

CHKBASE CMPA.L  A0,A3	        ;DONE WITH BASE?
        BEQ.S   @2	        ;EXIT IF YES
        MOVE.L  #MMUSADRB,A0    ;ELSE SET STARTING BASE ADDRESS
        MOVEA.L A3,A1	        ; AND ENDING ADDRESS
        BRA.S   RWCHK1	        ;GO CHECK BASE REGS

@2      TST     D2	        ;check for errors
        RTS6		        ;and exit test

        .PAGE
;----------------------------------------------------------------------------
;  Subroutine to do MMU address check
;  Leaves limit registers with invalid page value, base regs with 0
;----------------------------------------------------------------------------

MMUACHK MOVE.L  #MMUSADRL,A0    ;SET MMU LIMIT START ADDR
        MOVE.L  #MMUEADRL,A1    ;SET MMU LIMIT END ADDR
        MOVE.L  #MMUEADRB,A3    ;SET MMU BASE END ADDR
        MOVE    #INVPAG,D4      ;SET FINAL VALUE FOR LIMIT REGS

ACHK1   MOVE    (A0),D1	        ;READ REG
        EOR     D0,D1	        ;CHECK IF EXPECTED
        ANDI    #$0FFF,D1       ;MASK DON'T CARES
        BNE.S   MADRERR	        ;EXIT IF ERROR

MMUSET  MOVE    D4,(A0)	        ;SET FINAL REG VALUE
        ROXL    #1,D0	        ;SET UP NEW PATTERN
        CMPA.L  A0,A1	        ;CHECK IF DONE
        BEQ.S   ACHK2	        ;IF YES GO CHECK FOR BASE REG TESTING
        ADDA.L  A2,A0	        ;ELSE BUMP MMU ADDR
        BRA.S   ACHK1

ACHK2   CMPA.L  A0,A3	        ;DONE WITH BASE?
        BEQ.S   @2	        ;EXIT IF YES
        MOVE.L  #MMUSADRB,A0    ;ELSE SET STARTING BASE ADDRESS
        MOVEA.L A3,A1	        ; AND ENDING ADDRESS
        MOVEQ   #0,D4	        ;SET FINAL VALUE FOR BASE REGS
        BRA.S   ACHK1	        ;GO CHECK BASE REGS

@2      TST     D2	        ;check for errors
        RTS6		        ;and exit test

;  Handle MMU address error

MADRERR OR      D1,D2	        ;save error bits
        BRA.S   MMUSET	        ; and continue test

        .ELSE
        CLR.L   D2	        ;for error patterns
        BRA.S   SETMMU
        .ENDC

        .PAGE
;----------------------------------------------------------------------------
; Subroutine to do MMU actual read/write
;----------------------------------------------------------------------------

CHKRW   MOVE    D0,(A0)	        ;do write
        MOVE    (A0),D1	        ;read back
        EOR     D0,D1	        ;compare
        ANDI    #$0FFF,D1       ;mask don't cares
        BNE.S   RWERR	        ;skip if error
        RTS4		        ;else return

;  Error collection

RWERR   OR      D1,D2	        ;save error bits
        RTS4		        ;and return

        .PAGE
;--------------------------------------------------------------------------
;  Now do setup of MMU supervisor registers for RAM and I/O space access.
;  Also do read check after write and abort if error.
;--------------------------------------------------------------------------

;  Do origin registers first

SETMMU  MOVE.L  #MMUSADRB,A0    ;GET MMU PTR
        MOVEQ   #0,D0	        ;clear for use
        MOVEQ   #0,D1
        MOVE    D2,D4	        ;SAVE PREVIOUS RESULTS IF ANY
        MOVEQ   #0,D2
        MOVEQ   #0,D6
        MOVE.L  #ADR128K,A2     ;ADDRESS INCREMENT
        MOVE.L  #PAG128K,A3     ;SET UP BASE ADDRESS INCREMENT
        MOVEQ   #16,D6	        ;LOOP COUNT
LOADORG BSRS4   CHKRW	        ;DO WRITE/READ CHECK
        ADD.L   A3,D0	        ;COMPUTE NEXT MEMORY BASE ADDRESS
        ADDA.L  A2,A0	        ;BUMP MMU ORG PTR
        SUBQ    #1,D6
        BNE.S   LOADORG	        ;LOOP UNTIL DONE

;  Set base for I/O and special I/O space

        MOVEA.L #MMU126B,A0     ;PT TO ORG REG 126
        MOVEQ   #0,D0	        ;set data value
        BSRS4   CHKRW
        ADDA.L  A2,A0	        ;BUMP PTR TO REG 127
        BSRS4   CHKRW

;  Now do limit registers

        MOVEA.L #MMUSADRL,A0    ;GET MMU LIMIT REG PTR
        MOVE    #MEMLMT,D0      ;LIMIT FOR 128K MEMORY SEGMENTS
        MOVEQ   #0,D1	        ;use as working reg
        MOVEQ   #16,D6	        ;LOOP COUNT
LOADLMT BSRS4   CHKRW
        ADDA.L  A2,A0	        ;BUMP MMU PTR
        SUBQ    #1,D6
        BNE.S   LOADLMT	        ;LOOP UNITL DONE

;  Now do MMU limit reg setup for I/O and Special I/O access

        MOVEA.L #MMU126L,A0     ;PT TO LMT REG 126
        MOVE    #IOLMT,D0       ;SET FOR I/O SPACE, FULL ACCESS
        BSRS4   CHKRW
        ADDA.L  A2,A0	        ;BUMP PTR TO REG 127
        MOVE    #SPLMT,D0       ;SET FOR SPECIAL I/O, FULL ACCESS
        BSRS4   CHKRW

        .IF  DIAGS = 1
;  Check if errors detected

        TST     D2	        ;CHECK ERROR MASK
        BNE     MMUERR	        ;ABORT IF ERROR
        MOVE    D4,D2	        ;ELSE RESTORE PREVIOUS RESULTS
        .ENDC

        .PAGE
;--------------------------------------------------------------------------
;  Complete testing of MMU by checking other context regs.
;  Uses reg D6 for context indicator.
;--------------------------------------------------------------------------
        .IF     DIAGS = 1

MMUTST2 MOVEQ   #0,D6	        ;FOR CONTEXT INDICATOR
        BSR4    MMUINIT	        ;REINITIALIZE FOR TESTING

        TST.B   SEG1ON	        ;SET FOR CONTEXT 1
        MOVEQ   #1,D6	        ;SET CONTEXT INDICATOR

        BSR4    CONCHK	        ;CHECK IF MMU CONTEXT CHANGED
        BEQ     MMUERR2	        ;EXIT IF NO - SEG BIT ERROR

        BSR6    MMURW	        ;ELSE GO DO R/W TEST
        BNE     MMUERR2	        ;exit if error

        TST.B   SEG2ON	        ;SET FOR CONTEXT 3
        MOVEQ   #3,D6

        BSR4    CONCHK	        ;CHECK IF MMU CONTEXT CHANGED
        BEQ.S   MMUERR2	        ;EXIT IF NO - SEG BIT ERROR

        BSR6    MMURW	        ;ELSE GO TEST
        BNE.S   MMUERR2	        ;exit if error

        TST.B   SEG1OFF	        ;SET FOR CONTEXT 2
        MOVEQ   #2,D6

        BSRS4   CONCHK	        ;CHECK IF MMU CONTEXT CHANGED
        BEQ.S   MMUERR3	        ;EXIT IF NO - SEG BIT ERROR

        BSRS6   MMURW	        ;ELSE GO TEST
        BNE.S   MMUERR3	        ;exit if error

        TST.B   SEG2OFF	        ;RESET FOR CONTEXT 0 REGS

;  Now do MMU addressing check of remaining context regs

        BSR4    MMUINIT	        ;REINITIALIZE

        TST.B   SEG1ON	        ;SET FOR CONTEXT 1
        MOVEQ   #1,D6
        BSR6    MMUACHK	        ;TEST CONTEXT 1
        BNE.S   MMUERR2	        ;exit if error

        TST.B   SEG2ON	        ;TEST CONTEXT 3
        MOVEQ   #3,D6
        BSR6    MMUACHK
        BNE.S   MMUERR2	        ;exit if error

        TST.B   SEG1OFF	        ;TEST CONTEXT 2
        MOVEQ   #2,D6
        BSR6    MMUACHK
        BNE.S   MMUERR3	        ;exit if error

        TST.B   SEG2OFF	        ;RESET TO CONTEXT 0
        BRA.S   MMULPCHK        ;go check for loop mode

MMUERR2 TST.B   SEG1OFF	        ;ENSURE RESET FOR CONTEXT 0
MMUERR3 TST.B   SEG2OFF

        ROR     #4,D6	        ;get context indicator
        OR      D6,D2	        ;save with error bits (if any)
        BSET    #MMU,D7	        ;set error indicator
MMULPCHK
        TST.L   D7	        ;in loop mode?
        BMI.S   MMUTST	        ;restart full MMU test if yes
        BRA.S   START	        ;else continue to next test

        .PAGE
;-------------------------------------------------------------------------
;  Subroutine to verify context change made - does comparison to ensure
;  destruction of context 0 mapping avoided.  Zero bit set if error.
;-------------------------------------------------------------------------

CONCHK  MOVE    MMU126L,D4      ;check limit reg for I/O space
        ANDI    #$0FFF,D4       ;mask don't care
        CMPI    #IOLMT,D4       ;still in same context?
        BNE.S   CONOK	        ;exit if not

        MOVE    MMU127L,D4      ;else also check reg for ROM space
        ANDI    #$0FFF,D4       ;mask don't care
        CMPI    #SPLMT,D4       ;also set up?
        BNE.S   CONOK	        ;exit if not

        MOVE    MMU0L,D4        ;else do final check on reg for memory access
        ANDI    #$0FFF,D4
        CMPI    #MEMLMT,D4      ;return with match results to caller

CONOK   RTS4

        .ENDC

        .IF  ROM4K = 0
        .IF  DIAGS = 0
;-------------------------------------------------------------------------
;  Initialize other MMU regs for invalid access
;-------------------------------------------------------------------------

        TST.B   SEG1ON	        ;SET FOR CONTEXT 1
        MOVEQ   #1,D6	        ;SET CONTEXT INDICATOR
        BSR6    INITMMU

        TST.B   SEG2ON	        ;SET FOR CONTEXT 3
        MOVEQ   #3,D6
        BSR6    INITMMU

        TST.B   SEG1OFF	        ;SET FOR CONTEXT 2
        MOVEQ   #2,D6
        BSR6    INITMMU

        TST.B   SEG2OFF	        ;RESET FOR CONTEXT 0 REGS
        CLR.L   D6
        TST     D2	        ;CHECK IF ERRORS
        BEQ.S   START	        ;exit if ok

        ROR.L   #8,D6	        ;GET CONTEXT INDICATOR
        OR.L    D6,D2	        ;SAVE WITH ERROR BITS
        BSET    #MMU,D7	        ;SET ERROR CODE FOR LATER MESSAGE
        BEQ.S   START	        ;AND CONTINUE

        .PAGE
;-------------------------------------------------------------------------
;  Routine to init MMU regs
;-------------------------------------------------------------------------

INITMMU
        MOVE.L  #MMUSADRL,A0    ;MMU limit start addr
        MOVE.L  #MMUEADRL,A1    ;MMU limit end addr
        MOVE.L  #ADR128K,A2     ;increment value
        MOVE.L  #MMUEADRB,A3    ;MMU base end addr
        MOVE    #INVPAG,D0      ;set pattern for limit regs

RWLOOP  BSR4    CHKRW	        ;go initialize and check
        CMPA.L  A0,A1	        ;done?
        BEQ.S   CHKBASE	        ;if yes go check for base reg testing
        ADDA.L  A2,A0	        ;else bump addr
        BRA.S   RWLOOP

CHKBASE CMPA.L  A0,A3	        ;done with base?
        BEQ.S   @2	        ;exit if yes
        MOVE.L  #MMUSADRB,A0    ;else set starting base address
        MOVEA.L A3,A1	        ; and ending address
        CLR.L   D0	        ;set base value
        BRA.S   RWLOOP	        ;go do base regs

@2      RTS6		        ;AND EXIT TEST

        .ENDC		        ;{DIAGS}
        .ENDC		        ;{ROM4K}
        .PAGE
;-------------------------------------------------------------------------
;  Reset SETUP bit to enable system access and continue with testing
;-------------------------------------------------------------------------

START   CLR.B   SETUP	        ;TURN OFF SETUP TO ENTER MAP LAND ...

;----------------------------------------------------------------------------
;  Now do memory sizing - assumes 128K minimum memory increment
;  Register usage:
;       A0 = minimum physical address	        D0 = scratch use
;       A1 = maximum physical address/scratch   D1 = incr for search address
;       A2 = unused			        D2 = unused
;       A3 = next base memory addr to test      D3 = inverted sizing test pattern
;       A4 = return address		        D4 = sizing test pattern
;       A5 = unused			        D5 = retry count
;       A6 = saved error mask		        D6 = error mask
;----------------------------------------------------------------------------

MEMSIZ  CLR.L   D0	        ;setup regs for loop
        MOVE.L  D0,A0
        MOVE.L  D0,A1
        MOVE.L  D0,A3
        MOVE.L  D0,A6
        MOVEQ   #2,D1	        ;size at 128K boundaries			        RM000
        SWAP    D1	        ;						        RM000
        MOVE.L  #PATRN,D4       ;set test patterns for sizing			        CHG002
        MOVE    D4,D3	        ;use only lower word
        NOT     D3	        ;and its inverse

CHKLO   BSR4    CHKMEM	        ;first search for low memory address
        TST     D6	        ;memory found?
        BEQ.S   SAVELO	        ;yes - go save address
        NOT     D6	        ;else invert to check if all bits in error
        TST     D6	        ;if not, assume memory error
        BNE.S   @3	        ; and go save address
@2      ADDA.L  D1,A3	        ;else bump search address
        MOVEA.L A3,A1	        ;set as next working address
        CMPA.L  #MAXADR,A1      ;at max address?
        BNE.S   CHKLO	        ;continue search if not

;  No memory found - toggle LED and check for I/O board; if no I/O (bus error)	        CHG004
;  diagnostics are restarted							        CHG004

        MOVE.B  #DEFVID2,VIDLTCH ;set LED on and default video latch setting (PAGE 2F)  CHG004
        MOVE    #TNTHSEC,D0	 ;delay for .1 sec				        CHG004
@9      SUBQ    #1,D0		 ;						        CHG004
        BNE.S   @9		 ;						        CHG004
        MOVE.B  #DEFVID,VIDLTCH	 ;reset LED and leave video latch setting	        CHG004
        MOVE.L  #VIA1BASE,A0	 ;check for I/O board				        CHG004
        TST.B   (A0)		 ;bus error will occur if not installed		        CHG004

;  Go into read/write loop if no memory found but I/O installed

        BSR2    LOTONE	        ;beep speaker for error
        BSR4    CONSET	        ;set contrast
        MOVE.L  #ONEMEG-2,A0    ;set default memory address (to span both boards)       CHG002
@4      MOVE.L  D4,(A0)	        ;go into read/write loop			        CHG002
        MOVE.L  (A0),D3	        ;						        CHG002
        BRA.S   @4

;  Low memory address found - save and continue

@3      NOT     D6	        ;reinvert and
        MOVE    D6,A6	        ; save results

SAVELO  MOVEA.L A3,A0	        ;save low address
        CMPA.L  #ONEMEG,A0      ;check for min low address
        BLE.S   @1	        ;skip if OK
        MOVEA.L #ONEMEG,A0      ;else set at min value (one 512K board in slot 1)

        .PAGE
;-----------------------------------------------------------------------------
;  Now check for high memory address; search to max address of 2 meg
;-----------------------------------------------------------------------------

@1      MOVEA.L A3,A2	        ;save low address as first high address

TSTHI
        ADDA.L  D1,A3	        ;compute next 128K increment
        MOVEA.L A3,A1	        ;use as new search value
        CMPA.L  #MAXADR,A1      ;done?
        BEQ.S   SIZXIT

;  Following patch added to detect for wraparound problem of old memory boards

        MOVE.L  A0,D0	        ;check low address				        RM015
        BNE.S   CHKHI	        ;old memory boards start at address 0		        RM015
        CMP     (A1),D3	        ;are we wrapped back to already tested location?
        BEQ.S   WRAPXIT	        ;skip if yes (i.e., old memory board)		        RM015

;  Else continue with check for high address

CHKHI   BSRS4   CHKMEM	        ;go do memory search
        TST     D6	        ;any errors?
        BEQ.S   SAVEHI	        ;skip if not to save address
        NOT     D6	        ;else invert to see if all bits in error
        TST     D6
        BEQ.S   TSTHI	        ;skip if yes to ignore address
        MOVE    A6,D0	        ;else get previous results
        NOT     D6	        ;reinvert and
        OR      D6,D0	        ; add new results
        MOVE    D0,A6	        ;save for later

SAVEHI  MOVEA.L A3,A2	        ;save as new potential high address
        CLR     (A3)	        ;clear test pattern				        RM015
        BRA.S   TSTHI	        ; and continue loop

WRAPXIT CLR     (A1)	        ;clear test pattern				        RM015

SIZXIT  ADDA.L  D1,A2	        ;high address = last valid addr + 128K
        MOVEA.L A2,A1	        ;save for later use
        BRA.S   RSTMMU	        ;continue on

        .PAGE
;----------------------------------------------------------------------------
;  Subroutine to do memory check for sizing.  If error, tries successive
;  memory locations up to retry count (D5).  Returns with error mask in D6.
;----------------------------------------------------------------------------

CHKMEM  MOVEQ   #RETRYCNT,D5    ; set retry count in case of errors
        CLR     D6	        ; clear for error mask
@1      MOVE    D4,(A1)	        ; check if true data stores
        MOVE    D3,2(A1)        ; and try complement to next location
        CMP     (A1),D4
        BEQ.S   @2	        ; continue if yes
        MOVE    (A1),D0	        ; else get error bits
        EOR     D4,D0
        OR      D0,D6	        ; and save them

@2      CMP     2(A1),D3        ; check second location
        BEQ.S   @3	        ; exit if data correct
        MOVE    2(A1),D0        ; else read again
        EOR     D3,D0	        ; get error bits
        OR      D0,D6	        ; save in error mask

@3      MOVE    D4,2(A1)        ; now try in reverse order
        MOVE    D3,(A1)
        CMP     2(A1),D4        ; check second location
        BEQ.S   @4	        ; skip if OK
        MOVE    2(A1),D0        ; else save error bits
        EOR     D4,D0
        OR      D0,D6

@4      CMP     (A1),D3	        ; and check first
        BEQ.S   @5	        ; continue if yes
        MOVE    (A1),D0	        ; else get error bits
        EOR     D3,D0
        OR      D0,D6	        ; and save them

@5      TST     D6	        ; any  errors?
        BEQ.S   @6	        ; skip if no
        TST.L   (A1)+	        ; else bump search address to next pair
        SUBQ    #1,D5	        ; decr retry count
        BNE.S   @1	        ; continue until count exhausted

@6      RTS4		        ; return with results in D6

        .PAGE
;-------------------------------------------------------------------------
; Subroutine to set parms for lo tone from speaker
;-------------------------------------------------------------------------

LOTONE  MOVEQ   #$60,D0	        ;set frequency
        MOVE    #250,D1	        ;and duration
        MOVEQ   #4,D2	        ;and volume (medium)
        BSR4    TONE2	        ;go do tone
        RTS2


        .PAGE
;---------------------------------------------------------------------------
;  Now reset MMU regs according to low and high physical address
;  Remainder of MMU regs in context 0 are set to invalid page
;
;  Register Usage:
;       A0 - low physical address       D0 - scratch/value stored in base reg
;       A1 - high physical address      D1 - value stored in limit reg
;       A2 - MMU base reg ptr	        D2 - unused
;       A3 - MMU limit reg ptr	        D3 - holds base reg page incr value
;       A4 - used for return address    D4 - used for count of regs to set
;       A5 - MMU address increment      D5 - low physical page
;       A6 - not used		        D6 - high physical page
;---------------------------------------------------------------------------

;  First translate memory addresses to 512 byte page values for MMU use

RSTMMU
        MOVE.L  A0,D5	        ;GET MEMORY ADDRESS VALUES
        MOVE.L  A1,D6
        MOVEQ   #9,D0	        ;SET SHIFT COUNT
        LSR.L   D0,D5	        ;TRANSLATE TO PAGE VALUES
        LSR.L   D0,D6

;  Now initialize for MMU write operations

        MOVEA.L #MMUSADRB,A2    ;SET MMU BASE REG PTR
        MOVEA.L #MMUSADRL,A3    ;SET LIMIT REG PTR
        MOVE.L  #ADR128K,A5     ;SET INCREMENT VALUE FOR MMU ADDRESSES
        MOVE.L  #PAG128K,D3     ;SET INCR VALUE FOR BASE REG CONTENTS
        MOVEQ   #126,D4	        ;SET REG COUNT - NO RESETTING OF REGS 126,127
        MOVE    D5,D0	        ;SET BASE VALUE
        MOVE    #MEMLMT,D1      ;SET LIMIT VALUE

;  Remap MMU regs for existing memory

REMAP   BSRS4   WRTMMU	        ;REWRITE SET OF MMU REGS
        SUBQ    #1,D4	        ;DECR REG COUNT
        ADD.L   D3,D0	        ;BUMP PAGE ADDRESS
        CMP.L   D0,D6	        ;CHECK IF AT UPPER LIMIT
        BNE.S   REMAP	        ;LOOP IF NO

;  Now map remainder of regs for invalid access

        CLR     D0	        ;SET NEW BASE REG VALUE
        MOVE    #INVPAG,D1      ; AND LIMIT REG VALUE
MAPINV  BSRS4   WRTMMU	        ;GO DO REWRITE
        SUBQ.L  #1,D4	        ;DECR COUNT
        BNE.S   MAPINV	        ;LOOP UNTIL DONE

;  Finally reset video page to last page of memory

        LSR.L   #6,D6	        ;compute address for video page
        SUBQ    #1,D6	        ;decr to last page
        MOVE.B  D6,VIDLTCH      ;and set video latch

        BRA.S   MEMTST1	        ;SKIP TO NEXT TEST

        .IF     EXTERNAL = 1
        .LIST
        .PAGE
        .ENDC

;-------------------------------------------------------------------------
;  Subroutine to set MMU regs (context 0) - can also be called by external
;  routines that provide the following register inputs:
;
;       A2 = base reg address	        D0 = value for base reg
;       A3 = limit reg address	        D1 = value for limit reg
;       A5 = reg address increment
;-------------------------------------------------------------------------

WRTMMU  TST.B   SETUPON	        ;TURN SETUP ON TO ENABLE MMU ACCESS
        MOVE    D0,(A2)	        ;SET BASE REG
        MOVE    D1,(A3)	        ;SET LIMIT REG
        ADDA.L  A5,A2	        ;BUMP MMU PTRS
        ADDA.L  A5,A3
        CLR.B   SETUP	        ;BACK TO MAP LAND
        RTS4		        ;AND BACK TO CALLER

        .IF  ROM16K = 1
;-------------------------------------------------------------------------
;  Subroutine to read MMU regs - for call by external routines.
;  Inputs:
;       D2 = context to read (0-3)
;       A2 = base reg address
;       A3 = limit reg address
;       A4 = return address
;       A5 = reg address increment
;  Outputs:
;       D0 = value of base reg
;       D1 = value of limit reg
;       A2,A3 incremented by value in A5
;  Side Effects:
;       D3 trashed
;-------------------------------------------------------------------------

READMMU MOVE    #$0FFF,D3       ;set mask for result
        TST.B   SETUPON	        ;turn setup on to enable MMU access

        TST     D2	        ;check context
        BEQ.S   @9	        ;skip if context 0
        CMP.B   #1,D2	        ;context 1?
        BEQ.S   @2
        CMP.B   #2,D2	        ;context 2?
        BEQ.S   @1
        TST.B   SEG2ON	        ;must be context 3
        BRA.S   @2	        ;set both seg bits
@1      TST.B   SEG2ON	        ;set for context 2
        BRA.S   @9
@2      TST.B   SEG1ON	        ;set for context 1 and 3

;  read the regs

@9      MOVE    (A2),D0	        ;read base reg
        AND     D3,D0	        ;clear junk
        MOVE    (A3),D1	        ;read limit reg
        AND     D3,D1	        ;clear junk
        ADDA.L  A5,A2	        ;incr ptrs
        ADDA.L  A5,A3
        TST.B   SEG1OFF	        ;restore to context 0
        TST.B   SEG2OFF
        CLR.B   SETUP	        ;back to map land
        RTS4		        ;and back to caller

        .ENDC
        .IF     EXTERNAL = 1
        .NOLIST
        .ENDC

        .PAGE
;--------------------------------------------------------------------------
;  Begin memory testing by checking first 800 hex locations (2K).
;  If error here, abort other testing and go into loop since can't relay
;  meaningful results.
;--------------------------------------------------------------------------

MEMTST1 MOVEA.L A0,A2	        ;save memory lo, hi addresses
        MOVEA.L A1,A3

        .IF  DIAGS = 1

        SUBA.L  A0,A0	        ;set test addresses
        MOVEA   #LOMEM,A1       ;upper address				        RM000
        BSR4    RAMTEST	        ;go do memory test
        BEQ.S   INITMEM	        ;skip if OK

;  Error in low memory - reset video latch, beep speaker and go into R/W loop

        MOVE.L  A2,D0	        ;get low physical address
        LSR.L   #8,D0	        ;convert to page value
        LSR.L   #7,D0
        MOVE.B  D0,VIDLTCH      ;set video latch to bad area
        BSR4    CONSET	        ;ensure contrast on

        BSR2    LOTONE	        ;beep speaker twice for low memory error
        MOVE    #TNTHSEC,D0     ;delay for about 1/10 sec		        RM000
TONEDLY SUBQ    #1,D0
        BNE.S   TONEDLY
        BSR2    LOTONE	        ;do second beep

        SUBA.L  A0,A0	        ;loop at first address
        MOVE    #PATRN2,D0      ;set pattern for use
@2      MOVE    D0,(A0)
        MOVE    (A0),D1
        BRA.S   @2	        ;loop with random display on screen

        .ELSE
        MOVEQ   #0,D3	        ;set results reg
        MOVEQ   #-1,D0	        ;set for memory "clear" pattern
CLRMEM  MOVE.L  D0,(A0)+        ;do "clear"
        CMPA.L  A0,A1	        ;done?
        BNE.S   CLRMEM	        ;loop until yes

        .ENDC		        ;{DIAGS}

;  Now attempt to initialize status areas and save results

INITMEM MOVEA   #STATUS,A0      ;get ptr to start of status area	        RM000
        MOVEQ   #127,D0	        ;set count
@2      CLR.L   (A0)+	        ;clear it
        DBF     D0,@2	        ;until done

        MOVE    D3,MEMRSLT      ;save test results
        MOVE    A6,SIZRSLT      ;save sizing results
        MOVE.L  A2,MINMEM       ;save min memory address
        MOVE.L  A3,MAXMEM       ;save max memory address
        SUBA.L  A2,A3	        ;compute total memory
        MOVE.L  A3,TOTLMEM      ;and save also

        MOVE.L  #HEX32K,A0      ;compute base address for screen
        SUBA.L  A0,A3
        MOVE.L  A3,SCRNBASE     ;and save

        MOVE    D2,MMURSLT      ;and save MMU results also

        MOVE.L  #KBDQ,KBDQPTR   ;init COPS buffer pointer for later use

        .PAGE
;------------------------------------------------------------------------
; Initialize exception and trap vectors to catch unexpected errors
;------------------------------------------------------------------------

INITVCT BSR.S   SETVCTRS        ;init vectors
        BRA     SCCSET	        ;continue testing			        CHG027

;  Subroutine to set up default vectors

SETVCTRS
        LEA     MISC,A0
        SUBA.L  A1,A1
        MOVEQ   #64,D0
@1      MOVE.L  A0,(A1)+        ; fill with unknown ones
        SUBQ    #1,D0
        BGT     @1
        BSR.S   SETBUSVCT       ; then, with special ones		        RM000
        LEA     AERR,A0
        MOVE.L  A0,ADRVCTR
        LEA     IERR,A0
        MOVE.L  A0,ILLVCTR
        LEA     NMI,A0
        MOVE.L  A0,NMIVCT
        LEA     TRPERR,A0       ; same routine for line 1010 and 1111
        MOVE.L  A0,L10VCTR
        MOVE.L  A0,L11VCTR

        .IF  USERINT = 0
        MOVEQ   #FIRSTROW,D5    ;set default row and
        MOVEQ   #FIRSTCOL,D6    ; column cursor ptrs
        .ENDC

        RTS

;-----------------------------------------------------------
;  Subroutine to setup bus error vector					        RM000
;-----------------------------------------------------------

SETBUSVCT
        LEA     BERR,A3	        ;setup default vector			        RM000
        MOVE.L  A3,BUSVCTR      ;					        RM000
        RTS		        ;					        RM000

;-----------------------------------------------------------
;  Exception Handler routines
;-----------------------------------------------------------

MISC    MOVE.L  D7,D7SAV        ;save incoming value
        MOVEQ   #0,D7
        BSET    #MISEXCP,D7     ;set error indicator
        BRA.S   EXCP1

IERR    MOVE.L  D7,D7SAV        ;save incoming value
        MOVEQ   #0,D7
        BSET    #ILLEXCP,D7     ;set error indicator
        BRA.S   EXCP1

NMI     MOVE.L  D7,D7SAV        ;save incoming value
        MOVEQ   #0,D7
        BSR     TSTSTAT	        ;check status reg for parity error	        CHG015
        BNE.S   NOTPE	        ;skip if not

        BSET    #MPAR,D7        ;set error indicator
        BSR     GETPADDR        ;get and save error address		        CHG015
        TST.B   PAROFF	        ;toggle to clear error bit
        BTST    #5,D1	        ;video error?				        CHG015
        BEQ.S   @1	        ;skip if not				        CHG015
        ANDI.L  #VMSK,D1        ;mask if yes				        CHG015
@1      MOVE.L  D1,PEADDR       ;save converted error address		        CHG015
        BRA.S   EXCP1	        ;go to exit

NOTPE   BSET   #CPUINTR,D7      ;else set NMI code
        BRA.S   EXCP1	        ; and exit

TRPERR  MOVE.L  D7,D7SAV        ;save incoming value
        MOVEQ   #0,D7
        BSET    #TRPEXCP,D7     ;set error indicator
        BRA.S   EXCP1

BERR    MOVE.L  D7,D7SAV        ;save incoming value
        MOVEQ   #0,D7
        BSET    #BUSEXCP,D7     ;set error indicator
        BRA.S   EXCP0

AERR    MOVE.L  D7,D7SAV        ;save incoming value
        MOVEQ   #0,D7
        BSET    #ADREXCP,D7     ;set error indicator

EXCP0			        ; GROUP 0 EXCEPTIONS HERE
        MOVE    (SP)+,EXCFC     ; SAVE THE EXTRA DATA
        MOVE.L  (SP)+,EXCADR
        MOVE    (SP)+,EXCIR

EXCP1			        ; GROUP 1 EXCEPTIONS HERE
        MOVE    (SP)+,EXCSR     ; SAVE COMMON INFO
        MOVE.L  (SP)+,EXCPC
        MOVE    D0,EXCTYPE      ; save error type
        BRA     TSTCHK	        ; and go display error

        .PAGE
;-------------------------------------------------------------------------
;  Initialize SCC chip for Applebus use.					 CHG027
;  Bus error vector setup in case of problems.
;-------------------------------------------------------------------------

SCCSET
        LEA     NOIO,A3		      ;set bus error vector in case no IO board	 CHG027
        MOVE.L  A3,BUSVCTR	      ;						 CHG027
        BSR     RSTSCC		      ;go do setup				 CHG027

;-------------------------------------------------------------------------
; Now test VIA for parallel port and contrast latch.
; A read/write test on the timer 1 latches is done, then contrast
; is set if OK.
;-------------------------------------------------------------------------

VIA2TST
        .IF  DIAGS = 1
VIA2CHK
        .IF  ROM16K = 1
        LEA     VIA2VCT,A3      ;OK - set up special bus vector
        MOVE.L  A3,BUSVCTR

        MOVE.L  #,A0  ;set base address of timer low latch
        MOVEQ   #8,D0	        ;set offset to high latch
        BSRS6   VIATST	        ;and go do test
        BEQ.S   @2	        ;if OK, continue
        BSET    #VIA2,D7        ;else set error bit
        TST.L   D7	        ;check if in loop mode
        BMI.S   VIA2CHK	        ;restart if yes
        BRA.S   @3	        ;else skip contrast setting
        .ENDC


@2      TST.L   D7	        ;in loop mode?
        BMI.S   VIA2CHK	        ;restart if yes
        BSRS4	 CONOFF	        ;go turn off contrast

        .ENDC		        ;{DIAGS}

@3      BRA.S   SCRNTST	        ;else skip to next test

        .IF  DIAGS = 1
;------------------------------------------------------------------------
;  Bus error handler for VIA #2 use
;------------------------------------------------------------------------

VIA2VCT MOVEQ   #EVIA2,D0       ;SET ERROR CODE
        BSET    #VIA2,D7        ;set indicator
        BRA     IOVCT	        ;AND GO HANDLE I/O EXCEPTION
        .ENDC		        ;{DIAGS}

        .IF  ROM16K = 1
        .PAGE
;-------------------------------------------------------------------------
;   Subroutine to do VIA testing
;     A0 = address of first timer latch
;     D0 = offset to other latch
;-------------------------------------------------------------------------

VIATST  MOVE.L  A0,A1	        ;set up address for second latch
        ADDA.L  D0,A1
        MOVEQ   #0,D0	        ;for error use
        CLR.B   D2	        ;clear old data value
        MOVE.B	 #0,(A0)        ;and the timer latches
        MOVE.B	 #0,(A1)

        MOVE    #$FF,D3	        ;set up start value
        BSRS4   VIARW	        ;go do read/write test
        RTS6		        ;and return

;  Subroutine to do read/write test - loops thru all 256 values

VIARW   CMP.B   (A0),D2	        ;check for old values first
        BNE.S   VIAFAIL
        CMP.B   (A1),D2
        BNE.S   VIAFAIL

        MOVE.B  D3,(A0)	        ;set new value in low timer latch
        CMP.B   (A1),D2	        ;ensure high latch not affected
        BNE.S   VIAFAIL
        CMP.B   (A0),D3	        ;verify new low latch setting
        BNE.S   VIAFAIL

        MOVE.B  D3,(A1)	        ;set new value in high timer latch
        CMP.B   (A0),D3	        ;ensure low latch not affected
        BNE.S   VIAFAIL
        CMP.B   (A1),D3	        ;verify new high latch setting
        BNE.S   VIAFAIL

        MOVE.B  D3,D2	        ;the new value becomes the old
        DBF     D3,VIARW        ;loop thru all 256 values
        BRA.S   VIARWEND

VIAFAIL ADDQ    #1,D0	        ;set for error

VIARWEND TST    D0	        ;set zero bit indicator
        RTS4
        .ENDC
        .PAGE
;--------------------------------------------------------------------------
;  Subroutine to set contrast latch - sets for default, off or value in D0
;--------------------------------------------------------------------------

CONSET  MOVE.B  #$80,D0	        ;set mid range value default
        BRA.S   CONSET2

CONOFF  MOVEQ   #-1,D0	        ;set for contrast off

CONSET2			        ;for external entry
        MOVE.L  #VIA2BASE,A0    ;GET 6522 BASE ADDR
        MOVE.B  #$84,DDRB2(A0)  ;ENSURE NO STRAY DATA TO CONTRAST
        MOVE.B  #4,ORB2(A0)     ; LATCH BY DISABLING DRIVERS
        MOVE.B  #$FF,DDRA2(A0)  ;NOW SET PORT A AS OUTPUTS
        MOVE.B  D0,ORA2(A0)     ;set contrast value
        BSET    #7,ORB2(A0)     ;AND STROBE IT

        RTS4		        ;RETURN TO CALLER


        .PAGE
;-------------------------------------------------------------------------
;  Test memory to be used for screen.  The screen can then be
;  used for test icon display.  Default choice is last 32K of memory.
;  If this is invalid, do backwards scan through memory until a valid
;  area is found.
;  Assumes:  location SCRNBASE = base address of default screen (set by
;   sizing routine)
;-------------------------------------------------------------------------

SCRNTST
        .IF  DIAGS = 1

        MOVE.L  SCRNBASE,A0     ;get base address of screen
        MOVE.L  TOTLMEM,A1      ;set end address
        BSR4    RAMTEST	        ;and go do test
        BEQ.S   INVTST	        ;continue if no error
        BSET    #MEM,D7	        ;else set memory read/write error

;  save error results and then start search for good video page

        BSR     TSTINIT	        ;initialize for further testing
        BSR.S   SCRNSAV	        ;save results

        MOVE.L  SCRNBASE,A1     ;set new search end address
        MOVE.L  A1,A0
        MOVE.L  #HEX32K,A2      ;screen size is 32K
        SUBA.L  A2,A0	        ;set new start addr (end-32K)

@1      MOVEM.L A0-A2,-(SP)     ;save search addresses
        BSR4    RAMTEST	        ;go test
        MOVEM.L (SP)+,A0-A2     ;restore addresses (no effect on CCR)
        BEQ.S   SCRNOK	        ;skip if OK (non-zero CCR if error)
        MOVE.L  A0,D4	        ;else go save results
        BSR.S   SCRNSAV
        MOVE.L  A0,A1	        ;set next end
        SUBA.L  A2,A0	        ; and start addresses
        MOVE.L  A0,D0	        ;continue thru all of memory if necessary
        BGT.S   @1

SCRNERR BRA.S   INVTST	        ;continue testing, leave screen at default

SCRNOK  MOVE.L  A0,SCRNBASE     ;save new screen base
        BSR.S   SETVLTCH        ;and go set video latch
        BRA.S   INVTST	        ;and exit to next test

;-------------------------------------------------------------------------
;  Subroutine to save error results from screen test routine
;  Inputs:
;       A3 = ptr to base of save result area
;       D4 = base address of test area
;  Outputs:
;       None
;  Side Effects:
;       D1/D3-D4 trashed
;-------------------------------------------------------------------------

SCRNSAV MOVEQ   #17,D1	        ;divide base address by 128K
        LSR.L   D1,D4
        ADD     D4,D4	        ;double for word index to save area
        MOVE.L  D3,D1	        ;combine error results
        SWAP    D3
        OR      D1,D3
        OR      D3,0(A3,D4)     ;save and exit
        RTS

;-------------------------------------------------------------------------
;  Subroutine to set the video latch
;  Inputs:
;       Location SCRNBASE = logical base address for screen
;  Outputs:
;       None
;  Side Effects:
;       D0-D1 trashed
;-------------------------------------------------------------------------

SETVLTCH
        MOVE.L  SCRNBASE,D2     ;get logical screen base address
        MOVE.L  TOTLMEM,D1      ;get physical amount of memory
        SUB.L   D2,D1	        ;compute screen base offset
        MOVE.L  MAXMEM,D0       ;get max physical address
        SUB.L   D1,D0	        ;compute physical screen base address
        LSR.L   #8,D0	        ;convert to page value
        LSR.L   #7,D0
        MOVE.B  D0,VIDLTCH      ;set latch
        RTS		        ;and exit

        .PAGE
;-------------------------------------------------------------------------
;  Now check state of INVID bit to see if inverse video is installed.
;  If yes, rewrite last 4 words of screen page to avoid retrace line.
;-------------------------------------------------------------------------

INVTST
        .IF  INVERTCK = 1       ;				        CHG013

        MOVEA.L #STATREG,A5     ;set ptr to status register
        BTST    #INVIDBIT,(A5)  ;check if inverse video installed
        BNE.S   VIA1TST	        ;go on to next test if not

;  Inverse video requires zeroing of last bit in video page (32K)

        MOVE.L  SCRNBASE,A0     ;get base address of screen
        ADD.L   #HEX32K,A0      ;set ptr to end of screen
        CLR.L   -(A0)	        ;rewrite last 4 words
        CLR.L   -(A0)

        .ENDC		        ;{INVERTCK}			        CHG013
        .ENDC		        ;{DIAGS}

;-------------------------------------------------------------------------
; Continue testing by now doing COPS VIA test
;-------------------------------------------------------------------------

VIA1TST
        .IF  USERINT = 1

;  Draw desktop on screen for test icon display

        BSR     DRAWDESK        ;draw the desk
        BSR.S   DSPCPURM        ;and CPU ROM id		        CHG001

        .ENDC

        BSR4    CONSET	        ;set default contrast

        .IF  ROM16K = 1
VIA1CHK LEA     VIA1VCT,A3      ;first set up bus error vector
        MOVE.L  A3,BUSVCTR
        MOVE.L  #,A0 ;set base address of timer low latch
        MOVEQ   #2,D0	        ;set offset to high latch
        BSR6    VIATST	        ;go test
        BEQ.S   @2	        ;skip if OK
        BSET    #VIA1,D7        ;else set error bit
        TST.L   D7	        ;loop?
        BMI.S   VIA1CHK	        ;yes - test again
        BRA     TSTCHK	        ;else abort further testing

@2      TST.L   D7	        ;check for loop mode
        BMI.S   VIA1CHK
        BRA.S   COPSENBL        ;else go test COPS

;------------------------------------------------------------------------
;  Subroutine to display CPU ROM id
;------------------------------------------------------------------------

DSPCPURM
        MOVE.B  REV,D0	        ;read ROM rev			        CHG001
        MOVEQ   #ROMIDROW,D5    ;setup cursor ptrs		        CHG001
        MOVEQ   #ROMIDCOL,D6    ;				        CHG001
        BSR     DSPVAL	        ;do display			        CHG001
        RTS		        ;				        CHG001

        .ENDC
        .PAGE
;-------------------------------------------------------------------------
;  Try turning COPS on so that keyboard commands can be received
;-------------------------------------------------------------------------

COPSENBL
        LEA     COPSVCT,A3      ;set up bus error vector first
        MOVE.L  A3,BUSVCTR
        BSR.S   CPSINIT	        ;enable COPS
        BCS.S   COPSBAD	        ;skip if error
        TST.L   D7	        ;looping desired?
        BMI.S   COPSENBL        ;go repeat test

        MOVE.L  D7,D0	        ;get error indicator
        ANDI.L  #CPIOMSK,D0     ;mask off don't care bits
        BEQ     RSTSCAN	        ;continue if OK to do reset scan
        BRA     TSTCHK	        ;else go report error

COPSBAD BSET    #IOCOPS,D7      ;else set COPS error
        TST.L   D7	        ;looping desired?
        BMI.S   COPSENBL        ;go repeat test
        BRA     TSTCHK	        ;else abort further testing

;-----------------------------------------------------------------------------
;  Bus error handler for COPS testing with entry point for other I/O tests
;-----------------------------------------------------------------------------

COPSVCT MOVEQ   #EIOCOP,D0      ;SET ERROR CODE
IOVCT   BSET    #IOEXCP,D7      ;SET I/O EXCEPTION ERROR
        BRA     EXCP0	        ;AND GO HANDLE EXCEPTION

;-----------------------------------------------------------------------------
;  Subroutine to initiialize COPS interface for use
;-----------------------------------------------------------------------------

CPSINIT MOVEA.L #VIA1BASE,A0    ;GET VIA BASE ADDRESS
        MOVE.B  #$01,ACR1(A0)   ;SET PORT A LATCH ENABLE
        OR.B    #$09,PCR1(A0)   ;SET HANDSHAKE ENABLE
        MOVE.B  #$7F,IER1(A0)   ;CLEAR ALL INTRPT ENABLES
        MOVE.B  #$7F,IFR1(A0)   ;AND CLEAR FLAGS

;  Now turn COPS on, disabling mouse and NMI key

TURNON  CLR     D0	        ;SET FOR PORT ON CMD
        BSR.S   COPSCMD	        ;SEND TO COPS
        BCS.S   @1	        ;EXIT IF TIMEOUT ERROR

        MOVEQ   #$70,D0	        ;DISABLE MOUSE
        BSR.S   COPSCMD
        BCS.S   @1

        .IF  DEBUG = 0
        MOVEQ   #$50,D0	        ;disable NMI key
        BSR.S   COPSCMD
        BCS.S   @1
        MOVEQ   #$60,D0
        BSR.S   COPSCMD
        .ENDC

@1      RTS		        ;AND EXIT

        .PAGE
;-----------------------------------------------------------------------------
;  Subroutine to send cmd to COPS
;  Assumes registers:
;       D0 = cmd value
;  If COPS does not respond, timeout error indicated by setting carry bit.
;-----------------------------------------------------------------------------

COPSCMD
        MOVEM.L D0-D4/A0-A2,-(SP) ;save regs
        DISABLE		        ;disable all interrupts
        MOVEA.L #VIA1BASE,A0    ;set COPS VIA interface ptr
        MOVEA.L A0,A1	        ;save for use as port B output reg address
        MOVEA.L A0,A2
        ADDA    #DDRA1,A2       ;compute address for port A data direction reg
        MOVEQ   #$40,D2	        ;set up constants for later use
        MOVEQ   #-1,D3
        MOVEQ   #6,D4

        MOVE.B  D0,PORTA1(A0)   ;set cmd in data reg (no handshake)

;---------------------------------------------------------------------------
;  First find a ready state (CRDY low)
;  Each of the following loops take about 32 machine cycles = 6.4 us plus
;  a variable amount of time for sync with 6522 (max = 2us)
;---------------------------------------------------------------------------

        MOVE    #$061A,D1       ;set timeout for about 10 ms
@3      SUBQ    #1,D1
        BEQ.S   @1	        ;exit if timeout
        BTST    D4,(A1)	        ;else wait for "ready" (bit 6 = CRDY)
        BNE.S   @3

;  Now find the next ready state to insure enough time available for data

        MULU    #1,D0	        ;kill some time (about 15.2 us) to get
                                ; out of previous CRDY
        MOVE    #$061A,D1       ;reinit timeout count
@4      SUBQ    #1,D1
        BEQ.S   @1	        ;exit if timeout
        BTST    D4,(A1)	        ;wait for another "ready"
        BNE.S   @4

        MOVE.B  D3,(A2)	        ; ok, jam out the data

;  Now wait for CRDY high and then hold data for COPS to read

        MOVE    #$061A,D1       ;set timeout for about 10 ms
@5      SUBQ    #1,D1
        BEQ.S   @1	        ;exit if timeout
        BTST    D4,(A1)	        ;wait for "not-ready"
        BEQ.S   @5

        MOVEQ   #$A,D0	        ; force about a 40 ms
@6      SUBQ    #1,D0	        ;  delay for COPS hold time
        BGT.S   @6

        CLR.B   (A2)	        ; reset direction reg now
        MOVE.B  #$82,IER1(A0)   ;  and, enable CA1
        BRA.S   @2	        ; go to normal exit

;  Timeout occurred - set error indicator

@1      ENABLE		        ;reenable
        ORI.B   #$01,CCR        ;set carry bit
        BRA.S   @9	        ;skip to exit

@2      ENABLE		        ;restore interrupt levels
@9      MOVEM.L (SP)+,D0-D4/A0-A2 ;restore regs
        RTS		        ;and return to caller

        .PAGE
;-------------------------------------------------------------------------
;  Scan COPS for proper reset codes.  Delay added for normal COPS power-up
;  time of about 1.7 seconds.
;
;  Send reset signal and then scan keyboard/mouse interface.  First "clears"
;  COPS of any pending codes, and then issues reset.  Works via
;  a "state machine" that checks codes received and sets flags as follows:
;
;       D1 = 0 - reset signal in place
;	   = 1 - reset signal removed
;
;       D3 = 0 - no keyboard codes received => keyboard disconnected
;	   = 1 - keyboard disconnect code ($80/$FD) received
;		  => ignore, may be old keyboard
;	   = 2 - keyboard disconnect/connect codes ($80/$FD/$80/id) received
;		  => keyboard connected
;
;       D4 = 0 - no mouse codes received => mouse connected
;	   = 1 - only mouse connect code ($87) received => ignore, may be old sys
;	   = 2 - mouse connect/disconnect ($87/$07) codes received
;		  => mouse disconnected
;--------------------------------------------------------------------------

RSTSCAN
        MOVE.L  KBDQPTR,A1      ;setup buffer ptrs
        MOVEA   #QEND,A2
@1      BSR.S   GETJMP	        ;clear COPS queue, saving data
        BCC.S   @1

        BSR     RSTKBD	        ;do reset of keyboard/mouse interfaces

        .IF  ROM4K = 0
        CLR.L   D1	        ;init some flags
        CLR.L   D3
        CLR.L   D4
        BSR.S   GETJMP	        ;check for data
        BCS.S   RSTXIT	        ;exit if none (may be old keyboard)

;  State 0 - waiting for reset flag or mouse connect code

RST0    CMPI.B  #RSTCODE,D0     ;reset flag?
        BEQ.S   RST1	        ;skip if yes to state 1
        CMPI.B  #MSPLG,D0       ;mouse connect code?
        BEQ.S   RST2	        ;skip if yes to state 2
        CMPI.B  #MSUNPLG,D0     ;mouse disconnect code only?
        BNE.S   GET0
        MOVEQ   #2,D4	        ;set flag for disconnect state
GET0    BSR.S   GETJMP	        ;go get next code
        BCS.S   RSTXIT	        ;exit if no more codes
        BRA.S   RST0	        ;else continue scan loop

;  State 2 - waiting for mouse unplugged code

RST2    MOVEQ   #1,D4	        ;set flag for mouse connect received
        BSR.S   GETJMP	        ;go get next byte
        BCS.S   RSTXIT	        ;exit if none or queue full
        CMPI.B  #MSUNPLG,D0     ;mouse disconnect code?
        BNE.S   RST0	        ;no - return to state 0
        MOVEQ   #2,D4	        ;yes - set flag
        BRA.S   GET0	        ;and return to state 0

;  State 1 - waiting for reset code

RST1    BSR.S   GETJMP	        ;go get next byte
        BCS.S   RSTXIT	        ;exit if no more
        CMPI.B  #KUNPLG,D0      ;keyboard unplugged code?
        BNE.S   @1	        ;skip if not
        MOVEQ   #1,D3	        ;else set flag
        BRA.S   GET0	        ;and return to state 0

@1      CMPI.B  #$DF,D0	        ;id code?
        BHI.S   @2	        ;skip if not
        MOVE.B  D0,KEYID        ;else save for later use
        MOVEQ   #2,D3	        ;update flag
        BRA.S   GET0	        ;and return to state 0

@2      CMPI.B  #KCERR,D0       ;Keyboard COPS error?
        BNE.S   @3	        ;skip if not
        BSET    #KBDCOPS,D7     ;else set error indicator

@3      CMPI.B  #ICERR,D0       ;I/O COPS error code?
        BNE.S   @4	        ;skip if not
        BSET    #IOCOPS,D7      ;else set error indicator

@4      BRA.S   GET0	        ;continue scan from state 0

;  Insert to save code space

GETJMP  BSR.S   GETDATA	        ;go get COPS data
        RTS		        ;and return to caller

;  Reset exit - analyze results

RSTXIT  TST.B   D1	        ;reset signal lifted?
        BNE.S   @1	        ;skip if yes
        BSR     CLRRST	        ;else remove reset signal
        MOVEQ   #1,D1	        ;set "removed flag"
        BSR.S   GETDATA	        ;any data?
        BCC.S   RST0	        ;go decode if yes

@1
        .IF  FINKBD = 1
        TST.B   D3	        ;any keyboard data detected?
        BNE.S   MSCHK	        ;skip if yes - assume keybd connected
        BSET    #KBDOUT,D7      ;if none, keyboard is disconnected

MSCHK
        TST.B   D4	        ;any mouse data?
        BEQ.S   SCANXIT	        ;skip if none - mouse connected

@1      SUBQ    #1,D4	        ;flag = 1?
        BEQ.S   SCANXIT	        ;ignore if yes

        BSET    #MOUSOUT,D7     ;else mouse disconnected
        BRA.S   SCANXIT	        ;and go to exit

        .ELSE
        BRA.S   SCANXIT
        .ENDC		        ;{FINKBD}

;  Error exits - set appropriate indicator

SCANERR BSET    #IOKBD,D7       ;I/O or keyboard failure
        BRA.S   SCANXIT

IOCERR  BSET    #IOCOPS,D7      ;I/O COPS error

        .ELSE
@1      BSR.S   GETDATA	        ;clear COPS queue
        BCC.S   @1
        BSR.S   CLRRST	        ;clear reset signal
@2      BSR.S   GETDATA	        ;any data?
        BCC.S   @2	        ;clear the queue
        .ENDC		        ;{ROM4K}

SCANXIT MOVE.L  A1,KBDQPTR      ;save queue ptr for later use

        .IF  ROM4K = 0
        MOVE.L  D7,D0	        ;check error codes
        ANDI.L  #SCANMSK,D0     ; for scan errors
        TST.L   D0	        ;any found?
        BNE     TSTCHK	        ;skip if yes
        .ENDC

        BRA.S   BEEP	        ;else continue testing

        .PAGE
;-------------------------------------------------------------------------
;  Subroutine to get COPS data
;  Assumes registers:
;       D0 - scratch use        A0 - VIA address
;       D1 - unused	        A1 - Ptr to data save area
;       D2 - scratch use        A2 - Ptr to end of data area
;  Puts data in save area and also leaves in register D0.
;  Carry bit set if timeout error or keyboard queue full.
;-------------------------------------------------------------------------

GETDATA CMPA.L  A1,A2	        ;check if at end of queue
        BEQ.S   @2	        ;exit if yes
        MOVE.L  #$1FF,D2        ;else set timeout for about 5 ms
        MOVEA.L #VIA1BASE,A0    ;set COPS VIA interface ptr
@1      MOVE.B  IFR1(A0),D0     ;check if data avail
        BTST    #1,D0
        BNE.S   GETIT	        ;skip if yes
        SUBQ    #1,D2
        BNE.S   @1	        ;else continue
@2      ORI.B   #$01,CCR        ;set timeout error
        RTS

GETIT   MOVE.B  ORA1(A0),D0     ;read data
        MOVE.B  D0,(A1)+        ;save it
        RTS		        ;and exit with results

        .PAGE
;-------------------------------------------------------------------------
;  Subroutine to do reset of keyboard and mouse interfaces
;  Inputs:
;       None
;  Outputs:
;       None
;  Side Effects:
;       D0/A0 trashed
;-------------------------------------------------------------------------

RSTKBD  MOVEA.L #VIA1BASE,A0    ;set VIA ptr
        BCLR    #0,ORB1(A0)     ;set reset signal
        ORI.B   #$01,DDRB1(A0)  ;send it
        MOVE.L  #3000,D0        ;do delay for 12 ms
        BSR.S   DELAY
        RTS

;-------------------------------------------------------------------------
;  Subroutine to remove reset signal for keyboard and mouse interfaces
;  Inputs:
;       A0 = ptr to parallel port VIA (set in RSTKBD routine)
;  Outputs:
;       None
;  Side Effects:
;       D0 trashed
;-------------------------------------------------------------------------

CLRRST  BSET    #0,ORB1(A0)     ;remove reset signal
        BSR.S   KBDDELAY        ;delay for keyboard reset time
        RTS

;----------------------------------------------------------------------------
; Subroutine to delay for count in D0 (each count = 4 us).  Additional
; entry points set up for fixed delays.
;----------------------------------------------------------------------------

        .IF  ROM4K = 0
DELAY_1 MOVE.L  #TNTHSEC,D0     ;.1 second delay
        BRA.S   DELAY

DELAY5  MOVE.L  #FIVESEC,D0     ;5 second delay
        BRA.S   DELAY

KBDDELAY
        MOVE.L  #KBDDLY,D0      ;delay for COPS debounce loop
        .ENDC

DELAY   SUBQ.L  #1,D0	        ;loop until count = 0
        BNE.S   DELAY
        RTS

        .PAGE
;-------------------------------------------------------------------------
; Sound starting "tone"
;-------------------------------------------------------------------------

BEEP
        BSR.S   CLICK	        ;go click speaker
        BRA     VIDTST	        ;then go do video test

;-------------------------------------------------------------------------
; Subroutine to set parms for speaker "click"
;-------------------------------------------------------------------------

CLICK   MOVE.B  #$A0,D0	        ;set frequency
        MOVEQ   #0,D1	        ;and duration
        MOVEQ   #8,D2	        ;and volume (medium)		        RM000
                                ;then fall thru to tone routine	        RM000

;-------------------------------------------------------------------------
;  Routine to beep the speaker
;  Assumes regs set up as
;    D0 = desired frequency ($00 - $AA)
;    D1 = duration (0 = .5 msec)
;    D2 = volume (0,2,4,...,E)
;-------------------------------------------------------------------------

TONE    MOVEM.L A0/A4/D3,-(SP)  ;save regs
        BSRS4   TONE2	        ;go do tone
        MOVEM.L (SP)+,A0/A4/D3  ;restore and exit
        RTS

;  separate entry point for call without memory usage

TONE2   MOVEA.L #VIA1BASE,A0    ;set VIA ptr
        ORI.B   #$0E,DDRB1(A0)  ;set volume bits for output
        ANDI.B  #$F1,ORB1(A0)   ;clear and then
        OR.B    D2,ORB1(A0)     ; set volume bits
        ANDI.B  #$E3,ACR1(A0)   ;clear shift mode bits
        ORI.B   #$10,ACR1(A0)   ;set shift reg for continuous rotate

; check system type

        TST.B   DISKROM	        ;test for Lisa 1 board		        CHG014
        BPL.S   @3	        ;no changes if yes		        CHG014
        BTST    #SLOTMR,DISKROM ;else check if slow timers	        CHG029
        BNE.S   @3	        ;skip if yes			        CHG029
        MOVE.B  D0,D3	        ;else adjust input parm		        CHG014
        LSR.B   #2,D3	        ; by factor of .25		        CHG014
        ADD.B   D3,D0	        ;				        CHG014

@3      MOVE.B  D0,T2CL1(A0)    ;set frequency
        MOVE.B  #$0F,SHR1(A0)   ;set for square wave and trigger

;  Do time delay -  enter with count in D1 (about .5 msec per count)

@1      MOVE.W  #$00D0,D3       ;set delay constant
@2      DBF     D3,@2
        DBF     D1,@1

SILENCE ANDI.B  #$E3,ACR1(A0)   ;disable tone
        RTS4		        ;and return


        .IF  DIAGS = 1
;-------------------------------------------------------------------------
;   Routine to handle I/O board selection errors.  Does check for access
;   of other I/O board devices to try to pinpoint error.
;-------------------------------------------------------------------------

NOIO    BSET    #RS232B,D7      ;set SCC port B access error	        CHG027
        MOVE    #STKBASE,SP     ;restore stack pointer

;  try access of other I/O board devices

        LEA     NOIO2,A3        ;set up new bus error vector
        MOVE.L  A3,BUSVCTR
        MOVE.L  #VIA2BASE,A0    ;set base address		        CHG027
        TST.B   (A0)	        ;try access
        BRA     VIA2TST	        ;return to testing if OK	        CHG027

NOIO2   BSET    #VIA2,D7        ;set VIA #2 error also		        CHG027
        LEA     NOIO3,A3        ;try final access to VIA #1	        CHG027
        MOVE.L  A3,BUSVCTR
        MOVE.L  #VIA1BASE,A0    ;set base address		        CHG027
        TST.B   (A0)
        BRA.S   SCRNTST	        ;exit if OK			        CHG027

NOIO3   BSET    #CPUSEL,D7      ;most likely CPU board error

        BRA     TSTCHK	        ;go report errors		        CHG027

        .ENDC