;
; 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<