apple2-go/cpu/6502_interrupt_test.lst

1375 lines
72 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

AS65 Assembler for R6502 [1.42]. Copyright 1994-2007, Frank A. Kingswood Page 1
---------------------------------------------------- 6502_interrupt_test.a65 -----------------------------------------------------
1025 lines read, no errors in pass 1.
;
; 6 5 0 2 I N T E R R U P T T E S T
;
; Copyright (C) 2013 Klaus Dormann
;
; This program is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, either version 3 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program. If not, see <http://www.gnu.org/licenses/>.
; This program is designed to test IRQ and NMI of a 6502 emulator. It requires
; an internal or external feedback register to the IRQ & NMI inputs
;
; version 15-aug-2014
; contact info at http://2m5.de or email K@2m5.de
;
; assembled with AS65 from http://www.kingswood-consulting.co.uk/assemblers/
; command line switches: -l -m -s2 -w -h0
; | | | | no page headers in listing
; | | | wide listing (133 char/col)
; | | write intel hex file instead of binary
; | expand macros in listing
; generate pass2 listing
;
; No IO - should be run from a monitor with access to registers.
; To run load intel hex image with a load command, than alter PC to 400 hex and
; enter a go command.
; Loop on program counter determines error or successful completion of test.
; Check listing for relevant traps (jump/branch *).
;
; Debugging hints:
; Most of the code is written sequentially. if you hit a trap, check the
; immediately preceeding code for the instruction to be tested. Results are
; tested first, flags are checked second by pushing them onto the stack and
; pulling them to the accumulator after the result was checked. The "real"
; flags are no longer valid for the tested instruction at this time!
; If the tested instruction was indexed, the relevant index (X or Y) must
; also be checked. Opposed to the flags, X and Y registers are still valid.
;
; versions:
; 19-jul-2013 1st version distributed for testing
; 16-aug-2013 added error report to standard output option
; 15-aug-2014 added filter to feedback (bit 7 will cause diag stop in emu)
; C O N F I G U R A T I O N
;
;ROM_vectors MUST be writable & the I_flag MUST be alterable
;load_data_direct (0=move from code segment, 1=load directly)
;loading directly is preferred but may not be supported by your platform
;0 produces only consecutive object code, 1 is not suitable for a binary image
0001 = load_data_direct = 1
;NMI & IRQ are tested with a feedback register
;emulators diag register - set i_drive = 0 for a latch (74HC573)
bffc = I_port = $bffc ;feedback port address
0000 = I_ddr = 0 ;feedback DDR address, 0 = no DDR
0001 = I_drive = 1 ;0 = totem pole, 1 = open collector
0000 = IRQ_bit = 0 ;bit number of feedback to IRQ
0001 = NMI_bit = 1 ;bit number of feedback to NMI, -1 if not available
007f = I_filter = $7f ;filtering bit 7 = diag stop
;typical IO chip port B - set i_drive = 0 to avoid pullup resistors
;I_port = $bfb2 ;feedback port address
;I_ddr = $bfb3 ;feedback DDR address, 0 = no DDR
;I_drive = 1 ;0 = totem pole, 1 = open collector
;IRQ_bit = 0 ;bit number of feedback to IRQ
;NMI_bit = 1 ;bit number of feedback to NMI, -1 if not available
;I_filter = $ff ;no bits filtered
;decimal mode flag during IRQ, NMI & BRK
0000 = D_clear = 0 ;0 = not cleared (NMOS), 1 = cleared (CMOS)
;configure memory - try to stay away from memory used by the system
;zero_page memory start address, 6 consecutive Bytes required
0000 = zero_page = $0
;data_segment memory start address, 4 consecutive Bytes required
0200 = data_segment = $200
;code_segment memory start address
0800 = code_segment = $800
;report errors through I/O channel (0=use standard self trap loops, 1=include
;report.i65 as I/O channel)
0000 = report = 0
noopt ;do not take shortcuts
;macros for error & success traps to allow user modification
;example:
;trap macro
; jsr my_error_handler
; endm
;trap_eq macro
; bne skip\?
; trap ;failed equal (zero)
;skip\?
; endm
;
; my_error_handler should pop the calling address from the stack and report it.
; putting larger portions of code (more than 3 bytes) inside the trap macro
; may lead to branch range problems for some tests.
if report = 0
trap macro
jmp * ;failed anyway
endm
trap_eq macro
beq * ;failed equal (zero)
endm
trap_ne macro
bne * ;failed not equal (non zero)
endm
; please observe that during the test the stack gets invalidated
; therefore a RTS inside the success macro is not possible
success macro
jmp * ;test passed, no errors
endm
endif
if report = 1
trap macro
jsr report_error
endm
trap_eq macro
bne skip\?
trap ;failed equal (zero)
skip\?
endm
trap_ne macro
beq skip\?
trap ;failed not equal (non zero)
skip\?
endm
; please observe that during the test the stack gets invalidated
; therefore a RTS inside the success macro is not possible
success macro
jsr report_success
endm
endif
0001 = carry equ %00000001 ;flag bits in status
0002 = zero equ %00000010
0004 = intdis equ %00000100
0008 = decmode equ %00001000
0010 = break equ %00010000
0020 = reserv equ %00100000
0040 = overfl equ %01000000
0080 = minus equ %10000000
0001 = fc equ carry
0002 = fz equ zero
0003 = fzc equ carry+zero
0040 = fv equ overfl
0042 = fvz equ overfl+zero
0080 = fn equ minus
0081 = fnc equ minus+carry
0082 = fnz equ minus+zero
0083 = fnzc equ minus+zero+carry
00c0 = fnv equ minus+overfl
0030 = fao equ break+reserv ;bits always on after PHP, BRK
0034 = fai equ fao+intdis ;+ forced interrupt disable
00ff = m8 equ $ff ;8 bit mask
00fb = m8i equ $ff&~intdis ;8 bit mask - interrupt disable
;macros to set status
push_stat macro ;setting flags in the processor status register
lda #\1
pha ;use stack to load status
endm
set_stat macro ;setting flags in the processor status register
lda #\1
pha ;use stack to load status
plp
endm
if load_data_direct = 1
data
else
bss ;uninitialized segment, copy of data at end of code!
endif
0000 = org zero_page
;BRK, IRQ, NMI test interrupt save
0000 : zpt
0000 : 00 irq_a ds 1 ;a register
0001 : 00 irq_x ds 1 ;x register
0002 : 00 irq_f ds 1 ;flags
0003 : 00 nmi_a ds 1 ;a register
0004 : 00 nmi_x ds 1 ;x register
0005 : 00 nmi_f ds 1 ;flags
0006 : zp_bss
;fixed stack locations
01fe = lst_f equ $1fe ;last flags before interrupt
01ff = lst_a equ $1ff ;last accumulator before interrupt
0200 = org data_segment
;concurrent NMI, IRQ & BRK test result
0200 : 00 nmi_count ds 1 ;lowest number handled first, $ff = never
0201 : 00 irq_count ds 1 ;separation-1 = instructions between interrupts
0202 : 00 brk_count ds 1
;expected interrupt mask
0203 : 00 I_src ds 1 ;bit: 0=BRK, 1=IRQ, 2=NMI
0204 : data_bss
code
0800 = org code_segment
0800 : d8 start cld
0801 : a900 lda #0 ;clear expected interrupts for 2nd run
0803 : 8d0302 sta I_src
0806 : a2ff ldx #$ff
0808 : 9a txs
;initialize I/O for report channel
if report = 1
jsr report_init
endif
; load system vectors
if load_data_direct != 1
ldx #5
ld_vect lda vec_init,x
sta vec_bss,x
dex
bpl ld_vect
endif
; IRQ & NMI test - requires a feedback register
if I_drive > 1
ERROR ;invalid interrupt drive!
endif
if NMI_bit < 0
if I_drive = 0 ;totem pole (push/pull, 0 -> I_port to force interrupt)
I_set macro ibit ;ibit = interrupt bit
lda I_port ;turn on interrupt by bit
and #I_filter-(1<<\1)
plp ;set flags
pha ;save to verify
php
sta I_port ;interrupt next instruction plus outbound delay
endm
I_clr macro ibit ;ibit = interrupt bit
lda I_port ;turn off interrupt by bit
and #I_filter
ora #(1<<ibit)
sta I_port
endm
I_clr IRQ_bit ;turn off IRQ
if I_ddr != 0 ;with DDR
lda I_ddr ;set DDR for IRQ to enabled
and #I_filter
ora #(1<<IRQ_bit)
sta I_ddr
endif
else ;open collector, 0 -> I_DDR or I_port to force interrupt
if I_ddr != 0 ;with DDR
I_set macro ibit ;ibit = interrupt bit
lda I_ddr ;turn on interrupt by bit
and #I_filter
ora #(1<<\1)
plp ;set flags
pha ;save to verify
php
sta I_ddr ;interrupt next instruction plus outbound delay
endm
I_clr macro ibit ;ibit = interrupt bit
lda I_ddr ;turn off interrupt by bit
and #I_filter-(1<<ibit)
sta I_ddr
endm
I_clr IRQ_bit ;turn off IRQ
lda I_port ;precharge IRQ
and #I_filter-(1<<IRQ_bit)
sta I_port
else ;no DDR
I_set macro ibit ;ibit = interrupt bit
lda I_port ;turn on interrupt by bit
and #I_filter
ora #(1<<\1)
plp ;set flags
pha ;save to verify
php
sta I_port ;interrupt next instruction plus outbound delay
endm
I_clr macro ibit ;ibit = interrupt bit
lda I_port ;turn off interrupt by bit
and #I_filter-(1<<ibit)
sta I_port
endm
I_clr IRQ_bit ;turn off IRQ
endif
endif
else
if I_drive = 0 ;totem pole (push/pull, 0 -> I_port to force interrupt)
I_set macro ibit ;ibit = interrupt bit
lda I_port ;turn on interrupt by bit
if ibit > 7 ;set both NMI & IRQ
and #I_filter-(1<<IRQ_bit|1<<NMI_bit)
else
and #I_filter-(1<<\1)
endif
plp ;set flags
pha ;save to verify
php
sta I_port ;interrupt next instruction plus outbound delay
endm
I_clr macro ibit ;ibit = interrupt bit
lda I_port ;turn off interrupt by bit
and #I_filter
ora #(1<<ibit)
sta I_port
endm
I_clr IRQ_bit ;turn off IRQ & NMI
I_clr NMI_bit
if I_ddr != 0 ;with DDR
lda I_ddr ;set DDR for IRQ & NMI to enabled
and #I_filter
ora #(1<<IRQ_bit|1<<NMI_bit)
sta I_ddr
endif
else ;open collector, 0 -> I_DDR or I_port to force interrupt
if I_ddr != 0 ;with DDR
I_set macro ibit ;ibit = interrupt bit
lda I_ddr ;turn on interrupt by bit
and #I_filter
if ibit > 7 ;set both NMI & IRQ
ora #(1<<IRQ_bit|1<<NMI_bit)
else
ora #(1<<\1)
endif
plp ;set flags
pha ;save to verify
php
sta I_ddr ;interrupt next instruction plus outbound delay
endm
I_clr macro ibit ;ibit = interrupt bit
lda I_ddr ;turn off interrupt by bit
and #I_filter-(1<<ibit)
sta I_ddr
endm
I_clr IRQ_bit ;turn off IRQ & NMI
I_clr NMI_bit
lda I_port ;precharge IRQ & NMI
and #I_filter-(1<<IRQ_bit|1<<NMI_bit)
sta I_port
else ;no DDR
I_set macro ibit ;ibit = interrupt bit
lda I_port ;turn on interrupt by bit
and #I_filter
if ibit > 7 ;set both NMI & IRQ
ora #(1<<IRQ_bit|1<<NMI_bit)
else
ora #(1<<\1)
endif
plp ;set flags
pha ;save to verify
php
sta I_port ;interrupt next instruction plus outbound delay
endm
I_clr macro ibit ;ibit = interrupt bit
lda I_port ;turn off interrupt by bit
and #I_filter-(1<<ibit)
sta I_port
endm
I_clr IRQ_bit ;turn off IRQ & NMI
0809 : adfcbf > lda I_port ;turn off interrupt by bit
080c : 297e > and #I_filter-(1<<IRQ_bit )
080e : 8dfcbf > sta I_port
I_clr NMI_bit
0811 : adfcbf > lda I_port ;turn off interrupt by bit
0814 : 297d > and #I_filter-(1<<NMI_bit)
0816 : 8dfcbf > sta I_port
endif
endif
endif
; IRQ integrity test
; test for clear flags seen in IRQ vector
0819 : a902 lda #2 ;set expected interrupt source IRQ
081b : 8d0302 sta I_src
push_stat 0
081e : a900 > lda #0
0820 : 48 > pha ;use stack to load status
I_set IRQ_bit
0821 : adfcbf > lda I_port ;turn on interrupt by bit
0824 : 297f > and #I_filter
> if IRQ_bit > 7 ;set both NMI & IRQ
> ora #(1<<IRQ_bit|1<<NMI_bit)
> else
0826 : 0901 > ora #(1<<IRQ_bit)
> endif
0828 : 28 > plp ;set flags
0829 : 48 > pha ;save to verify
082a : 08 > php
082b : 8dfcbf > sta I_port ;interrupt next instruction plus outbound delay
082e : ea nop ;allow 6 cycles for interrupt to trip
082f : ea nop
0830 : ea nop
0831 : ad0302 lda I_src
trap_ne ;IRQ timeout
0834 : d0fe > bne * ;failed not equal (non zero)
0836 : ba tsx
0837 : e0fd cpx #$ff-2 ;original accu & flags remain on stack
trap_ne ;returned SP
0839 : d0fe > bne * ;failed not equal (non zero)
083b : a502 lda irq_f ;flags seen in IRQ vector
if D_clear = 1
and #decmode
trap_ne ;D-flag not cleared
lda irq_f
eor lst_f ;turn off unchanged bits
and #m8-fai-decmode ;mask untested other flags
trap_ne ;other flags (N,V,Z,C) changed
else
083d : 4dfe01 eor lst_f ;turn off unchanged bits
0840 : 29cb and #m8-fai ;mask untested other flags
trap_ne ;other flags (N,V,Z,C,D) changed
0842 : d0fe > bne * ;failed not equal (non zero)
endif
0844 : a2ff ldx #$ff ;reset stack pointer
0846 : 9a txs
; test all other registers
0847 : a249 ldx #'I'
0849 : a052 ldy #'R'
084b : a902 lda #2 ;set expected interrupt source IRQ
084d : 8d0302 sta I_src
push_stat 0
0850 : a900 > lda #0
0852 : 48 > pha ;use stack to load status
I_set IRQ_bit
0853 : adfcbf > lda I_port ;turn on interrupt by bit
0856 : 297f > and #I_filter
> if IRQ_bit > 7 ;set both NMI & IRQ
> ora #(1<<IRQ_bit|1<<NMI_bit)
> else
0858 : 0901 > ora #(1<<IRQ_bit)
> endif
085a : 28 > plp ;set flags
085b : 48 > pha ;save to verify
085c : 08 > php
085d : 8dfcbf > sta I_port ;interrupt next instruction plus outbound delay
0860 : 88 dey ;Y count will fail, if instructions are skipped
0861 : 88 dey
0862 : 88 dey
0863 : 88 dey
0864 : 08 php ;check processor status later
0865 : e04a cpx #('I'+1) ;returned registers OK?
trap_ne ;returned X
0867 : d0fe > bne * ;failed not equal (non zero)
0869 : c04b cpy #('R'-7)
trap_ne ;returned Y
086b : d0fe > bne * ;failed not equal (non zero)
086d : c951 cmp #'Q'
trap_ne ;returned A
086f : d0fe > bne * ;failed not equal (non zero)
0871 : ba tsx
0872 : e0fc cpx #$ff-3
trap_ne ;returned SP
0874 : d0fe > bne * ;failed not equal (non zero)
0876 : 68 pla ;flags
0877 : 4dfe01 eor lst_f
087a : 297d and #$ff-fnz ;ignore flags changed by dey
trap_ne ;returned flags
087c : d0fe > bne * ;failed not equal (non zero)
087e : a500 lda irq_a ;accu seen in IRQ vector
0880 : cdff01 cmp lst_a
trap_ne ;IRQ A received
0883 : d0fe > bne * ;failed not equal (non zero)
0885 : a2ff ldx #$ff ;reset stack pointer
0887 : 9a txs
; repeat with reversed registers
0888 : a2b6 ldx #$ff-'I'
088a : a0ad ldy #$ff-'R'
088c : a902 lda #2 ;set expected interrupt source IRQ
088e : 8d0302 sta I_src
push_stat $ff-intdis
0891 : a9fb > lda #$ff-intdis
0893 : 48 > pha ;use stack to load status
I_set IRQ_bit
0894 : adfcbf > lda I_port ;turn on interrupt by bit
0897 : 297f > and #I_filter
> if IRQ_bit > 7 ;set both NMI & IRQ
> ora #(1<<IRQ_bit|1<<NMI_bit)
> else
0899 : 0901 > ora #(1<<IRQ_bit)
> endif
089b : 28 > plp ;set flags
089c : 48 > pha ;save to verify
089d : 08 > php
089e : 8dfcbf > sta I_port ;interrupt next instruction plus outbound delay
08a1 : 88 dey ;Y count will fail, if instructions are skipped
08a2 : 88 dey
08a3 : 88 dey
08a4 : 88 dey
08a5 : 08 php ;check processor status later
08a6 : e0b7 cpx #($ff-'I'+1) ;returned registers OK?
trap_ne ;returned X
08a8 : d0fe > bne * ;failed not equal (non zero)
08aa : c0a6 cpy #($ff-'R'-7)
trap_ne ;returned Y
08ac : d0fe > bne * ;failed not equal (non zero)
08ae : c951 cmp #'Q'
trap_ne ;returned A
08b0 : d0fe > bne * ;failed not equal (non zero)
08b2 : ba tsx
08b3 : e0fc cpx #$ff-3
trap_ne ;returned SP
08b5 : d0fe > bne * ;failed not equal (non zero)
08b7 : 68 pla ;flags
08b8 : 4dfe01 eor lst_f
08bb : 297d and #$ff-fnz ;ignore flags changed by dey
trap_ne ;returned flags
08bd : d0fe > bne * ;failed not equal (non zero)
08bf : a500 lda irq_a ;accu seen in IRQ vector
08c1 : cdff01 cmp lst_a
trap_ne ;IRQ A received
08c4 : d0fe > bne * ;failed not equal (non zero)
08c6 : a2ff ldx #$ff ;reset stack pointer
08c8 : 9a txs
; retest for set flags seen in IRQ vector
08c9 : a902 lda #2 ;set expected interrupt source IRQ
08cb : 8d0302 sta I_src
push_stat $ff-intdis
08ce : a9fb > lda #$ff-intdis
08d0 : 48 > pha ;use stack to load status
I_set IRQ_bit
08d1 : adfcbf > lda I_port ;turn on interrupt by bit
08d4 : 297f > and #I_filter
> if IRQ_bit > 7 ;set both NMI & IRQ
> ora #(1<<IRQ_bit|1<<NMI_bit)
> else
08d6 : 0901 > ora #(1<<IRQ_bit)
> endif
08d8 : 28 > plp ;set flags
08d9 : 48 > pha ;save to verify
08da : 08 > php
08db : 8dfcbf > sta I_port ;interrupt next instruction plus outbound delay
08de : ea nop ;allow 6 cycles for interrupt to trip
08df : ea nop
08e0 : ea nop
08e1 : ad0302 lda I_src
trap_ne ;IRQ timeout
08e4 : d0fe > bne * ;failed not equal (non zero)
08e6 : ba tsx
08e7 : e0fd cpx #$ff-2 ;original accu & flags remain on stack
trap_ne ;returned SP
08e9 : d0fe > bne * ;failed not equal (non zero)
08eb : a502 lda irq_f ;flags seen in IRQ vector
if D_clear = 1
and #decmode
trap_ne ;D-flag not cleared
lda irq_f
eor lst_f ;turn off unchanged bits
and #m8-fai-decmode ;mask untested other flags
trap_ne ;other flags (N,V,Z,C) changed
else
08ed : 4dfe01 eor lst_f ;turn off unchanged bits
08f0 : 29cb and #m8-fai ;mask untested other flags
trap_ne ;other flags (N,V,Z,C,D) changed
08f2 : d0fe > bne * ;failed not equal (non zero)
endif
08f4 : a2ff ldx #$ff ;reset stack pointer
08f6 : 9a txs
; BRK integrity test
; test for clear flags seen in IRQ vector
08f7 : a901 lda #1 ;set expected interrupt source BRK
08f9 : 8d0302 sta I_src
set_stat 0
08fc : a900 > lda #0
08fe : 48 > pha ;use stack to load status
08ff : 28 > plp
0900 : 48 pha ;save entry registers
0901 : 08 php
0902 : 00 brk
0903 : ea nop ;should not be executed
0904 : ea nop ;allow 6 cycles for interrupt to trip
0905 : ea nop
0906 : ea nop
0907 : ad0302 lda I_src
trap_ne ;IRQ timeout
090a : d0fe > bne * ;failed not equal (non zero)
090c : ba tsx
090d : e0fd cpx #$ff-2 ;original accu & flags remain on stack
trap_ne ;returned SP
090f : d0fe > bne * ;failed not equal (non zero)
0911 : a502 lda irq_f ;flags seen in IRQ vector
if D_clear = 1
and #decmode
trap_ne ;D-flag not cleared
lda irq_f
eor lst_f ;turn off unchanged bits
and #m8-fai-decmode ;mask untested other flags
trap_ne ;other flags (N,V,Z,C) changed
else
0913 : 4dfe01 eor lst_f ;turn off unchanged bits
0916 : 29cb and #m8-fai ;mask untested other flags
trap_ne ;other flags (N,V,Z,C,D) changed
0918 : d0fe > bne * ;failed not equal (non zero)
endif
091a : a2ff ldx #$ff ;reset stack pointer
091c : 9a txs
; test all other registers
091d : a242 ldx #'B'
091f : a052 ldy #'R'
0921 : a901 lda #1 ;set expected interrupt source BRK
0923 : 8d0302 sta I_src
set_stat 0
0926 : a900 > lda #0
0928 : 48 > pha ;use stack to load status
0929 : 28 > plp
092a : 48 pha ;save entry
092b : 08 php
092c : 00 brk
092d : 88 dey ;should not be executed
092e : 88 dey ;Y count will fail, if return address is wrong
092f : 88 dey
0930 : 88 dey
0931 : 88 dey
0932 : 08 php ;check processor status later
0933 : e043 cpx #('B'+1) ;returned registers OK?
trap_ne ;returned X
0935 : d0fe > bne * ;failed not equal (non zero)
0937 : c04b cpy #('R'-7)
trap_ne ;returned Y
0939 : d0fe > bne * ;failed not equal (non zero)
093b : c94b cmp #'K'
trap_ne ;returned A
093d : d0fe > bne * ;failed not equal (non zero)
093f : ba tsx
0940 : e0fc cpx #$ff-3
trap_ne ;returned SP
0942 : d0fe > bne * ;failed not equal (non zero)
0944 : 68 pla ;flags
0945 : 4dfe01 eor lst_f
0948 : 297d and #$ff-fnz ;ignore flags changed by dey
trap_ne ;returned flags
094a : d0fe > bne * ;failed not equal (non zero)
094c : a500 lda irq_a ;accu seen in IRQ vector
094e : cdff01 cmp lst_a
trap_ne ;IRQ A received
0951 : d0fe > bne * ;failed not equal (non zero)
0953 : a2ff ldx #$ff ;reset stack pointer
0955 : 9a txs
; repeat with reversed registers
0956 : a2bd ldx #$ff-'B'
0958 : a0ad ldy #$ff-'R'
095a : a901 lda #1 ;set expected interrupt source BRK
095c : 8d0302 sta I_src
set_stat $ff
095f : a9ff > lda #$ff
0961 : 48 > pha ;use stack to load status
0962 : 28 > plp
0963 : 48 pha ;save entry registers
0964 : 08 php
0965 : 00 brk
0966 : 88 dey ;should not be executed
0967 : 88 dey ;Y count will fail, if return address is wrong
0968 : 88 dey
0969 : 88 dey
096a : 88 dey
096b : 08 php ;check processor status later
096c : e0be cpx #($ff-'B'+1) ;returned registers OK?
trap_ne ;returned X
096e : d0fe > bne * ;failed not equal (non zero)
0970 : c0a6 cpy #($ff-'R'-7)
trap_ne ;returned Y
0972 : d0fe > bne * ;failed not equal (non zero)
0974 : c94b cmp #'K'
trap_ne ;returned A
0976 : d0fe > bne * ;failed not equal (non zero)
0978 : ba tsx
0979 : e0fc cpx #$ff-3
trap_ne ;returned SP
097b : d0fe > bne * ;failed not equal (non zero)
097d : 68 pla ;flags
097e : 4dfe01 eor lst_f
0981 : 297d and #$ff-fnz ;ignore flags changed by dey
trap_ne ;returned flags
0983 : d0fe > bne * ;failed not equal (non zero)
0985 : a500 lda irq_a ;accu seen in IRQ vector
0987 : cdff01 cmp lst_a
trap_ne ;IRQ A received
098a : d0fe > bne * ;failed not equal (non zero)
098c : a2ff ldx #$ff ;reset stack pointer
098e : 9a txs
; retest for set flags seen in IRQ vector
098f : a901 lda #1 ;set expected interrupt source BRK
0991 : 8d0302 sta I_src
set_stat $ff
0994 : a9ff > lda #$ff
0996 : 48 > pha ;use stack to load status
0997 : 28 > plp
0998 : 48 pha ;save entry registers
0999 : 08 php
099a : 00 brk
099b : ea nop ;should not be executed
099c : ea nop ;allow 6 cycles for interrupt to trip
099d : ea nop
099e : ea nop
099f : ad0302 lda I_src
trap_ne ;IRQ timeout
09a2 : d0fe > bne * ;failed not equal (non zero)
09a4 : ba tsx
09a5 : e0fd cpx #$ff-2 ;original accu & flags remain on stack
trap_ne ;returned SP
09a7 : d0fe > bne * ;failed not equal (non zero)
09a9 : a502 lda irq_f ;flags seen in IRQ vector
if D_clear = 1
and #decmode
trap_ne ;D-flag not cleared
lda irq_f
eor lst_f ;turn off unchanged bits
and #m8-fai-decmode ;mask untested other flags
trap_ne ;other flags (N,V,Z,C) changed
else
09ab : 4dfe01 eor lst_f ;turn off unchanged bits
09ae : 29cb and #m8-fai ;mask untested other flags
trap_ne ;other flags (N,V,Z,C,D) changed
09b0 : d0fe > bne * ;failed not equal (non zero)
endif
09b2 : a2ff ldx #$ff ;reset stack pointer
09b4 : 9a txs
if NMI_bit < 0
; test IRQ with interrupts disabled
ldx #0
lda #0
sta I_src
push_stat intdis
I_set IRQ_bit ;IRQ pending
inx
inx
inx
ldx #0
lda #2 ;now re-enable IRQ
sta I_src
cli
inx
inx
inx
lda I_src ;test IRQ done?
trap_ne
ldx #$ff ;purge stack
txs
ldx #0 ;now overlap IRQ & BRK
lda #3
sta I_src
lda #$ff ;measure timing
sta nmi_count
sta irq_count
sta brk_count
push_stat 0
I_set IRQ_bit ;trigger IRQ
else
; NMI integrity test
; test for clear flags seen in NMI vector
09b5 : a904 lda #4 ;set expected interrupt source NMI
09b7 : 8d0302 sta I_src
push_stat 0
09ba : a900 > lda #0
09bc : 48 > pha ;use stack to load status
I_set NMI_bit
09bd : adfcbf > lda I_port ;turn on interrupt by bit
09c0 : 297f > and #I_filter
> if NMI_bit > 7 ;set both NMI & IRQ
> ora #(1<<IRQ_bit|1<<NMI_bit)
> else
09c2 : 0902 > ora #(1<<NMI_bit)
> endif
09c4 : 28 > plp ;set flags
09c5 : 48 > pha ;save to verify
09c6 : 08 > php
09c7 : 8dfcbf > sta I_port ;interrupt next instruction plus outbound delay
09ca : ea nop ;allow 6 cycles for interrupt to trip
09cb : ea nop
09cc : ea nop
09cd : ad0302 lda I_src
trap_ne ;NMI timeout
09d0 : d0fe > bne * ;failed not equal (non zero)
09d2 : ba tsx
09d3 : e0fd cpx #$ff-2 ;original accu & flags remain on stack
trap_ne ;returned SP
09d5 : d0fe > bne * ;failed not equal (non zero)
09d7 : a505 lda nmi_f ;flags seen in NMI vector
if D_clear = 1
and #decmode
trap_ne ;D-flag not cleared
lda nmi_f
eor lst_f ;turn off unchanged bits
and #m8-fai-decmode ;mask untested other flags
trap_ne ;other flags (N,V,Z,C) changed
else
09d9 : 4dfe01 eor lst_f ;turn off unchanged bits
09dc : 29cb and #m8-fai ;mask untested other flags
trap_ne ;other flags (N,V,Z,C,D) changed
09de : d0fe > bne * ;failed not equal (non zero)
endif
09e0 : a2ff ldx #$ff ;reset stack pointer
09e2 : 9a txs
; test all other registers
09e3 : a24e ldx #'N'
09e5 : a04d ldy #'M'
09e7 : a904 lda #4 ;set expected interrupt source NMI
09e9 : 8d0302 sta I_src
push_stat 0
09ec : a900 > lda #0
09ee : 48 > pha ;use stack to load status
I_set NMI_bit
09ef : adfcbf > lda I_port ;turn on interrupt by bit
09f2 : 297f > and #I_filter
> if NMI_bit > 7 ;set both NMI & IRQ
> ora #(1<<IRQ_bit|1<<NMI_bit)
> else
09f4 : 0902 > ora #(1<<NMI_bit)
> endif
09f6 : 28 > plp ;set flags
09f7 : 48 > pha ;save to verify
09f8 : 08 > php
09f9 : 8dfcbf > sta I_port ;interrupt next instruction plus outbound delay
09fc : 88 dey ;Y count will fail, if instructions are skipped
09fd : 88 dey
09fe : 88 dey
09ff : 88 dey
0a00 : 08 php ;check processor status later
0a01 : e04f cpx #('N'+1) ;returned registers OK?
trap_ne ;returned X
0a03 : d0fe > bne * ;failed not equal (non zero)
0a05 : c046 cpy #('M'-7)
trap_ne ;returned Y
0a07 : d0fe > bne * ;failed not equal (non zero)
0a09 : c949 cmp #'I'
trap_ne ;returned A
0a0b : d0fe > bne * ;failed not equal (non zero)
0a0d : ba tsx
0a0e : e0fc cpx #$ff-3
trap_ne ;returned SP
0a10 : d0fe > bne * ;failed not equal (non zero)
0a12 : 68 pla ;flags
0a13 : 4dfe01 eor lst_f
0a16 : 297d and #$ff-fnz ;ignore flags changed by dey
trap_ne ;returned flags
0a18 : d0fe > bne * ;failed not equal (non zero)
0a1a : a503 lda nmi_a ;accu seen in NMI vector
0a1c : cdff01 cmp lst_a
trap_ne ;NMI A received
0a1f : d0fe > bne * ;failed not equal (non zero)
0a21 : a2ff ldx #$ff ;reset stack pointer
0a23 : 9a txs
; repeat with reversed registers
0a24 : a2b1 ldx #$ff-'N'
0a26 : a0b2 ldy #$ff-'M'
0a28 : a904 lda #4 ;set expected interrupt source NMI
0a2a : 8d0302 sta I_src
push_stat $ff-intdis
0a2d : a9fb > lda #$ff-intdis
0a2f : 48 > pha ;use stack to load status
I_set NMI_bit
0a30 : adfcbf > lda I_port ;turn on interrupt by bit
0a33 : 297f > and #I_filter
> if NMI_bit > 7 ;set both NMI & IRQ
> ora #(1<<IRQ_bit|1<<NMI_bit)
> else
0a35 : 0902 > ora #(1<<NMI_bit)
> endif
0a37 : 28 > plp ;set flags
0a38 : 48 > pha ;save to verify
0a39 : 08 > php
0a3a : 8dfcbf > sta I_port ;interrupt next instruction plus outbound delay
0a3d : 88 dey ;Y count will fail, if instructions are skipped
0a3e : 88 dey
0a3f : 88 dey
0a40 : 88 dey
0a41 : 08 php ;check processor status later
0a42 : e0b2 cpx #($ff-'N'+1) ;returned registers OK?
trap_ne ;returned X
0a44 : d0fe > bne * ;failed not equal (non zero)
0a46 : c0ab cpy #($ff-'M'-7)
trap_ne ;returned Y
0a48 : d0fe > bne * ;failed not equal (non zero)
0a4a : c949 cmp #'I'
trap_ne ;returned A
0a4c : d0fe > bne * ;failed not equal (non zero)
0a4e : ba tsx
0a4f : e0fc cpx #$ff-3
trap_ne ;returned SP
0a51 : d0fe > bne * ;failed not equal (non zero)
0a53 : 68 pla ;flags
0a54 : 4dfe01 eor lst_f
0a57 : 297d and #$ff-fnz ;ignore flags changed by dey
trap_ne ;returned flags
0a59 : d0fe > bne * ;failed not equal (non zero)
0a5b : a503 lda nmi_a ;accu seen in NMI vector
0a5d : cdff01 cmp lst_a
trap_ne ;NMI A received
0a60 : d0fe > bne * ;failed not equal (non zero)
0a62 : a2ff ldx #$ff ;reset stack pointer
0a64 : 9a txs
; retest for set flags seen in NMI vector
0a65 : a904 lda #4 ;set expected interrupt source NMI
0a67 : 8d0302 sta I_src
push_stat $ff-intdis
0a6a : a9fb > lda #$ff-intdis
0a6c : 48 > pha ;use stack to load status
I_set NMI_bit
0a6d : adfcbf > lda I_port ;turn on interrupt by bit
0a70 : 297f > and #I_filter
> if NMI_bit > 7 ;set both NMI & IRQ
> ora #(1<<IRQ_bit|1<<NMI_bit)
> else
0a72 : 0902 > ora #(1<<NMI_bit)
> endif
0a74 : 28 > plp ;set flags
0a75 : 48 > pha ;save to verify
0a76 : 08 > php
0a77 : 8dfcbf > sta I_port ;interrupt next instruction plus outbound delay
0a7a : ea nop ;allow 6 cycles for interrupt to trip
0a7b : ea nop
0a7c : ea nop
0a7d : ad0302 lda I_src
trap_ne ;NMI timeout
0a80 : d0fe > bne * ;failed not equal (non zero)
0a82 : ba tsx
0a83 : e0fd cpx #$ff-2 ;original accu & flags remain on stack
trap_ne ;returned SP
0a85 : d0fe > bne * ;failed not equal (non zero)
0a87 : a505 lda nmi_f ;flags seen in NMI vector
if D_clear = 1
and #decmode
trap_ne ;D-flag not cleared
lda nmi_f
eor lst_f ;turn off unchanged bits
and #m8-fai-decmode ;mask untested other flags
trap_ne ;other flags (N,V,Z,C) changed
else
0a89 : 4dfe01 eor lst_f ;turn off unchanged bits
0a8c : 29cb and #m8-fai ;mask untested other flags
trap_ne ;other flags (N,V,Z,C,D) changed
0a8e : d0fe > bne * ;failed not equal (non zero)
endif
0a90 : a2ff ldx #$ff ;reset stack pointer
0a92 : 9a txs
; test IRQ & NMI with interrupts disabled
0a93 : a200 ldx #0
0a95 : a904 lda #4 ;set expected interrupt NMI only
0a97 : 8d0302 sta I_src
push_stat intdis
0a9a : a904 > lda #intdis
0a9c : 48 > pha ;use stack to load status
I_set 8 ;both interrupts pending
0a9d : adfcbf > lda I_port ;turn on interrupt by bit
0aa0 : 297f > and #I_filter
> if 8 > 7 ;set both NMI & IRQ
0aa2 : 0903 > ora #(1<<IRQ_bit|1<<NMI_bit)
> else
> ora #(1<<8 )
> endif
0aa4 : 28 > plp ;set flags
0aa5 : 48 > pha ;save to verify
0aa6 : 08 > php
0aa7 : 8dfcbf > sta I_port ;interrupt next instruction plus outbound delay
0aaa : e8 inx
0aab : e8 inx
0aac : e8 inx
0aad : ad0302 lda I_src ;test NMI done?
trap_ne
0ab0 : d0fe > bne * ;failed not equal (non zero)
0ab2 : a200 ldx #0
0ab4 : a902 lda #2 ;now re-enable IRQ
0ab6 : 8d0302 sta I_src
0ab9 : 58 cli
0aba : e8 inx
0abb : e8 inx
0abc : e8 inx
0abd : ad0302 lda I_src ;test IRQ done?
trap_ne
0ac0 : d0fe > bne * ;failed not equal (non zero)
0ac2 : a2ff ldx #$ff ;purge stack
0ac4 : 9a txs
;test overlapping NMI, IRQ & BRK
0ac5 : a200 ldx #0
0ac7 : a907 lda #7
0ac9 : 8d0302 sta I_src
0acc : a9ff lda #$ff ;measure timing
0ace : 8d0002 sta nmi_count
0ad1 : 8d0102 sta irq_count
0ad4 : 8d0202 sta brk_count
push_stat 0
0ad7 : a900 > lda #0
0ad9 : 48 > pha ;use stack to load status
I_set 8 ;trigger NMI + IRQ
0ada : adfcbf > lda I_port ;turn on interrupt by bit
0add : 297f > and #I_filter
> if 8 > 7 ;set both NMI & IRQ
0adf : 0903 > ora #(1<<IRQ_bit|1<<NMI_bit)
> else
> ora #(1<<8 )
> endif
0ae1 : 28 > plp ;set flags
0ae2 : 48 > pha ;save to verify
0ae3 : 08 > php
0ae4 : 8dfcbf > sta I_port ;interrupt next instruction plus outbound delay
endif
0ae7 : 00 brk
0ae8 : e8 inx
0ae9 : e8 inx
0aea : e8 inx
0aeb : e8 inx
0aec : e8 inx
0aed : e8 inx
0aee : e8 inx
0aef : e8 inx
0af0 : ad0302 lda I_src ;test all done?
;may fail due to a bug on a real NMOS 6502 - NMI could mask BRK
trap_ne ;lost an interrupt
0af3 : d0fe > bne * ;failed not equal (non zero)
; S U C C E S S ************************************************
; -------------
success ;if you get here everything went well
0af5 : 4cf50a > jmp * ;test passed, no errors
; -------------
; S U C C E S S ************************************************
; check data_segment +0 to +2 for sequence of concurrent interrupts
; e.g. 0x200 = NMI, 0x201 = IRQ, 0x202 = BRK, lower values = earlier
0af8 : 4c0008 jmp start ;run again
; manual tests for the WAI opcode of the 65c02
wai macro
db $cb ;WAI opcode
endm
; requires single step operation, report = 0
; set PC to the 1st instruction of the test
; step to the WAI opcode, then manually tie the IRQ input low
; continue to step until you see the PC advance, then remove IRQ
; allow the routine to complete.
; WAI with interrupts disabled
0afb : a2ff ldx #$ff
0afd : 9a txs
0afe : a003 ldy #3
0b00 : a900 lda #0 ;IRQ not expected
0b02 : 8d0302 sta I_src
set_stat intdis
0b05 : a904 > lda #intdis
0b07 : 48 > pha ;use stack to load status
0b08 : 28 > plp
wai
0b09 : cb > db $cb ;WAI opcode
0b0a : 88 dey
0b0b : 88 dey
0b0c : 88 dey
trap_ne ;skipped opcodes!
0b0d : d0fe > bne * ;failed not equal (non zero)
success
0b0f : 4c0f0b > jmp * ;test passed, no errors
; WAI with interrupts enabled
0b12 : a2ff ldx #$ff
0b14 : 9a txs
0b15 : a007 ldy #7
0b17 : a902 lda #2 ;IRQ expected
0b19 : 8d0302 sta I_src
set_stat 0
0b1c : a900 > lda #0
0b1e : 48 > pha ;use stack to load status
0b1f : 28 > plp
wai
0b20 : cb > db $cb ;WAI opcode
0b21 : 88 dey
0b22 : 88 dey
0b23 : 88 dey
0b24 : ad0302 lda I_src
trap_ne ;IRQ vector not called
0b27 : d0fe > bne * ;failed not equal (non zero)
0b29 : 88 dey
trap_ne ;skipped opcodes!
0b2a : d0fe > bne * ;failed not equal (non zero)
success
0b2c : 4c2c0b > jmp * ;test passed, no errors
; manual test for the STP opcode of the 65c02
stp macro
db $db ;STP opcode
endm
; set PC to the 1st instruction of the test, then run
0b2f : ea nop
0b30 : ea nop
stp ;expected end of operation
0b31 : db > db $db ;STP opcode
0b32 : ea nop
0b33 : ea nop
trap ;overran STP
0b34 : 4c340b > jmp * ;failed anyway
;end of manual tests
;---------------------------------------------------------------------------
;trap in case of unexpected IRQ, NMI, BRK, RESET - IRQ, NMI, BRK test target
0b37 : 88 dey
0b38 : 88 dey
0b39 : nmi_trap
if NMI_bit < 0
dey
dey
dey
trap ;unexpected NMI
else
0b39 : 08 php ;either SP or Y count will fail, if we do not hit
0b3a : 88 dey
0b3b : 88 dey
0b3c : 88 dey
0b3d : 8503 sta nmi_a ;save regsters during NMI
0b3f : 8604 stx nmi_x
0b41 : 68 pla
0b42 : 48 pha
0b43 : 8505 sta nmi_f
0b45 : ad0302 lda I_src ;NMI expected?
0b48 : 2904 and #4
trap_eq ;unexpexted NMI - check stack for conditions
0b4a : f0fe > beq * ;failed equal (zero)
0b4c : 68 pla ;test I-flag was set
0b4d : 48 pha
0b4e : 2904 and #intdis
trap_eq ;I-flag not set
0b50 : f0fe > beq * ;failed equal (zero)
0b52 : 68 pla ;return with other flags reversed
0b53 : 49c3 eor #m8-fai-decmode
0b55 : 48 pha
0b56 : ba tsx
0b57 : bd0201 lda $102,x ;test break on stack
0b5a : 2910 and #break
trap_ne ;unexpected B-flag! - this may fail on a real 6502
0b5c : d0fe > bne * ;failed not equal (non zero)
;due to a hardware bug on concurrent BRK & NMI
0b5e : ad0302 lda I_src ;mark expected NMI has occured
0b61 : 29fb and #$ff-4
0b63 : 8d0302 sta I_src
I_clr NMI_bit
0b66 : adfcbf > lda I_port ;turn off interrupt by bit
0b69 : 297d > and #I_filter-(1<<NMI_bit)
0b6b : 8dfcbf > sta I_port
0b6e : a604 ldx nmi_x
0b70 : e8 inx
0b71 : 8e0002 stx nmi_count
0b74 : a949 lda #'I' ;mark (NM)I
0b76 : 28 plp ;should be reversed by rti
0b77 : 40 rti
endif
0b78 : res_trap
trap ;unexpected RESET
0b78 : 4c780b > jmp * ;failed anyway
0b7b : 88 dey
0b7c : 88 dey
0b7d : irq_trap ;BRK & IRQ test
0b7d : 08 php ;either SP or Y count will fail, if we do not hit
0b7e : 88 dey
0b7f : 88 dey
0b80 : 88 dey
0b81 : 8500 sta irq_a ;save registers during IRQ/BRK
0b83 : 8601 stx irq_x
0b85 : 68 pla
0b86 : 48 pha
0b87 : 8502 sta irq_f
0b89 : ad0302 lda I_src ;IRQ expected?
0b8c : 2903 and #3
trap_eq ;unexpexted IRQ/BRK - check stack for conditions
0b8e : f0fe > beq * ;failed equal (zero)
0b90 : 68 pla ;test I-flag was set
0b91 : 48 pha
0b92 : 2904 and #intdis
trap_eq ;I-flag not set
0b94 : f0fe > beq * ;failed equal (zero)
0b96 : 68 pla ;return with other flags reversed
0b97 : 49c3 eor #m8-fai-decmode
0b99 : 48 pha
0b9a : ba tsx
0b9b : bd0201 lda $102,x ;test break on stack
0b9e : 2910 and #break
0ba0 : d021 bne brk_trap
0ba2 : ad0302 lda I_src ;IRQ expected?
0ba5 : 2902 and #2
trap_eq ;unexpexted IRQ - check stack for conditions
0ba7 : f0fe > beq * ;failed equal (zero)
0ba9 : ad0302 lda I_src ;mark expected IRQ has occured
0bac : 29fd and #$ff-2
0bae : 8d0302 sta I_src
I_clr IRQ_bit
0bb1 : adfcbf > lda I_port ;turn off interrupt by bit
0bb4 : 297e > and #I_filter-(1<<IRQ_bit)
0bb6 : 8dfcbf > sta I_port
0bb9 : a601 ldx irq_x
0bbb : e8 inx
0bbc : 8e0102 stx irq_count
0bbf : a951 lda #'Q' ;mark (IR)Q
0bc1 : 28 plp ;should be reversed by rti
0bc2 : 40 rti
0bc3 : brk_trap
0bc3 : ad0302 lda I_src ;break expected?
0bc6 : 2901 and #1
trap_eq ;unexpected BRK - check stack for conditions
0bc8 : f0fe > beq * ;failed equal (zero)
0bca : ad0302 lda I_src ;mark expected BRK has occured
0bcd : 29fe and #$ff-1
0bcf : 8d0302 sta I_src
0bd2 : a601 ldx irq_x
0bd4 : e8 inx
0bd5 : 8e0202 stx brk_count
0bd8 : a500 lda irq_a
0bda : a94b lda #'K' ;mark (BR)K
0bdc : 28 plp ;should be reversed by rti
0bdd : 40 rti
if report = 1
rep_int = 1
include "report.i65"
endif
;system vectors
if (load_data_direct = 1)
fffa = org $fffa
fffa : 390b dw nmi_trap
fffc : 780b dw res_trap
fffe : 7d0b dw irq_trap
else
vec_init
vec_bss equ $fffa
dw nmi_trap
dw res_trap
dw irq_trap
endif
fffa = end start
No errors in pass 2.
Wrote binary from address $0000 through $ffff.
Total size 65536 bytes.
Program start address is at $0800 (2048).