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 . ; 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< 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< 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< 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< 7 ;set both NMI & IRQ ora #(1< lda I_port ;turn off interrupt by bit 080c : 297e > and #I_filter-(1< sta I_port I_clr NMI_bit 0811 : adfcbf > lda I_port ;turn off interrupt by bit 0814 : 297d > and #I_filter-(1< 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< else 0826 : 0901 > ora #(1< 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< else 0858 : 0901 > ora #(1< 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< else 0899 : 0901 > ora #(1< 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< else 08d6 : 0901 > ora #(1< 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< else 09c2 : 0902 > ora #(1< 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< else 09f4 : 0902 > ora #(1< 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< else 0a35 : 0902 > ora #(1< 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< else 0a72 : 0902 > ora #(1< 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< 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< 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< 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< 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).