; ; 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 load_data_direct = 1 ;NMI & IRQ are tested with a feedback register ;emulators diag register - set i_drive = 0 for a latch (74HC573) I_port = $bffc ;feedback port address I_ddr = 0 ;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 = $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 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 zero_page = $a ;data_segment memory start address, 4 consecutive Bytes required data_segment = $200 ;code_segment memory start address code_segment = $400 ;report errors through I/O channel (0=use standard self trap loops, 1=include ;report.i65 as I/O channel) 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 carry equ %00000001 ;flag bits in status zero equ %00000010 intdis equ %00000100 decmode equ %00001000 break equ %00010000 reserv equ %00100000 overfl equ %01000000 minus equ %10000000 fc equ carry fz equ zero fzc equ carry+zero fv equ overfl fvz equ overfl+zero fn equ minus fnc equ minus+carry fnz equ minus+zero fnzc equ minus+zero+carry fnv equ minus+overfl fao equ break+reserv ;bits always on after PHP, BRK fai equ fao+intdis ;+ forced interrupt disable m8 equ $ff ;8 bit mask 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 org zero_page ;BRK, IRQ, NMI test interrupt save zpt irq_a ds 1 ;a register irq_x ds 1 ;x register irq_f ds 1 ;flags nmi_a ds 1 ;a register nmi_x ds 1 ;x register nmi_f ds 1 ;flags zp_bss ;fixed stack locations lst_f equ $1fe ;last flags before interrupt lst_a equ $1ff ;last accumulator before interrupt org data_segment ;concurrent NMI, IRQ & BRK test result nmi_count ds 1 ;lowest number handled first, $ff = never irq_count ds 1 ;separation-1 = instructions between interrupts brk_count ds 1 ;expected interrupt mask I_src ds 1 ;bit: 0=BRK, 1=IRQ, 2=NMI data_bss code org code_segment start cld lda #0 ;clear expected interrupts for 2nd run sta I_src ldx #$ff 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<