mirror of
https://github.com/irmen/ksim65.git
synced 2024-06-08 04:29:30 +00:00
tweak cpu irq handling, updating Klaus Dormann's functional irq tests
This commit is contained in:
parent
de60698349
commit
30b164bb6d
|
@ -110,12 +110,8 @@ open class Cpu6502 : BusComponent() {
|
|||
return State(regA.toShort(), regX.toShort(), regY.toShort(), regSP, status, regPC, totalCycles)
|
||||
}
|
||||
|
||||
// has an interrupt been requested?
|
||||
protected enum class Interrupt {
|
||||
IRQ, NMI
|
||||
}
|
||||
|
||||
protected var pendingInterrupt: Interrupt? = null
|
||||
protected var pendingIRQ = false
|
||||
protected var pendingNMI = false
|
||||
|
||||
// data byte from the instruction (only set when addr.mode is Accumulator, Immediate or Implied)
|
||||
protected var fetchedData: Int = 0
|
||||
|
@ -261,26 +257,26 @@ open class Cpu6502 : BusComponent() {
|
|||
*/
|
||||
override fun clock() {
|
||||
if (instrCycles == 0) {
|
||||
if (pendingInterrupt != null) {
|
||||
if (pendingIRQ || pendingNMI) {
|
||||
// NMI or IRQ interrupt.
|
||||
// handled by the BRK instruction logic.
|
||||
currentOpcode = 0
|
||||
currentInstruction = instructions[0]
|
||||
} else {
|
||||
// no interrupt, fetch next instruction from memory
|
||||
currentOpcodeAddress = regPC
|
||||
currentOpcode = read(regPC)
|
||||
currentInstruction = instructions[currentOpcode]
|
||||
regPC++
|
||||
handleInterrupt()
|
||||
return
|
||||
}
|
||||
|
||||
// tracing and breakpoint handling
|
||||
tracing?.invoke(snapshot().toString())
|
||||
breakpoints[regPC]?.let {
|
||||
if (breakpoint(it)) return
|
||||
}
|
||||
// no interrupt, fetch next instruction from memory
|
||||
currentOpcodeAddress = regPC
|
||||
currentOpcode = read(regPC)
|
||||
currentInstruction = instructions[currentOpcode]
|
||||
|
||||
if (currentOpcode == 0x00) breakpointForBRK?.let {
|
||||
if (breakpoint(it)) return
|
||||
}
|
||||
// tracing and breakpoint handling
|
||||
tracing?.invoke(snapshot().toString())
|
||||
breakpoints[regPC]?.let {
|
||||
if (breakpoint(it)) return
|
||||
}
|
||||
|
||||
if (currentOpcode == 0x00) breakpointForBRK?.let {
|
||||
if (breakpoint(it)) return
|
||||
}
|
||||
|
||||
regPC++
|
||||
|
@ -330,11 +326,11 @@ open class Cpu6502 : BusComponent() {
|
|||
}
|
||||
|
||||
fun nmi() {
|
||||
pendingInterrupt = Interrupt.NMI
|
||||
pendingNMI = true
|
||||
}
|
||||
|
||||
fun irq() {
|
||||
if (!regP.I) pendingInterrupt = Interrupt.IRQ
|
||||
if (!regP.I && !pendingNMI) pendingIRQ = true
|
||||
}
|
||||
|
||||
protected fun getFetched() =
|
||||
|
@ -1077,19 +1073,31 @@ open class Cpu6502 : BusComponent() {
|
|||
}
|
||||
|
||||
protected open fun iBrk() {
|
||||
// handle BRK ('software interrupt') or a real hardware IRQ
|
||||
if (pendingInterrupt != null) {
|
||||
pushStackAddr(regPC-1)
|
||||
} else {
|
||||
regPC++
|
||||
pushStackAddr(regPC)
|
||||
}
|
||||
regP.B = pendingInterrupt == null
|
||||
// handle BRK ('software interrupt')
|
||||
regPC++
|
||||
if(pendingNMI)
|
||||
return // if an NMI occurs during BRK, the BRK won't get executed on 6502 (65C02 fixes this)
|
||||
pushStackAddr(regPC)
|
||||
regP.B = true
|
||||
pushStack(regP)
|
||||
regP.I = true // interrupts are now disabled
|
||||
// NMOS 6502 doesn't clear the D flag (CMOS 65C02 version does...)
|
||||
regPC = readWord(if (pendingInterrupt == Interrupt.NMI) NMI_vector else IRQ_vector)
|
||||
pendingInterrupt = null
|
||||
regPC = readWord(IRQ_vector)
|
||||
}
|
||||
|
||||
protected open fun handleInterrupt() {
|
||||
// handle NMI or IRQ -- very similar to the BRK opcode above
|
||||
pushStackAddr(regPC-1)
|
||||
regP.B = false
|
||||
pushStack(regP)
|
||||
regP.I = true // interrupts are now disabled
|
||||
// NMOS 6502 doesn't clear the D flag (CMOS 65C02 version does...)
|
||||
regPC = readWord(if (pendingNMI) NMI_vector else IRQ_vector)
|
||||
|
||||
if(pendingNMI)
|
||||
pendingNMI = false
|
||||
else
|
||||
pendingIRQ = false
|
||||
}
|
||||
|
||||
protected fun iBvc() {
|
||||
|
|
|
@ -23,14 +23,14 @@ class Cpu65C02 : Cpu6502() {
|
|||
when (waiting) {
|
||||
Wait.Normal -> super.clock()
|
||||
Wait.Waiting -> {
|
||||
if (pendingInterrupt != null) {
|
||||
if (pendingNMI || pendingIRQ) {
|
||||
// continue execution after hardware interrupt
|
||||
waiting = Wait.Normal
|
||||
instrCycles = 1
|
||||
}
|
||||
}
|
||||
Wait.Stopped -> {
|
||||
if (pendingInterrupt != null) {
|
||||
if (pendingNMI || pendingIRQ) {
|
||||
// jump to reset vector after hardware interrupt
|
||||
regPC = readWord(RESET_vector)
|
||||
}
|
||||
|
@ -625,20 +625,29 @@ class Cpu65C02 : Cpu6502() {
|
|||
/* ff */ Instruction("bbs7", AddrMode.Zpr, 5)).toTypedArray()
|
||||
|
||||
override fun iBrk() {
|
||||
// handle BRK ('software interrupt') or a real hardware IRQ
|
||||
val nmi = pendingInterrupt == Interrupt.NMI
|
||||
if (pendingInterrupt != null) {
|
||||
pushStackAddr(regPC-1)
|
||||
} else {
|
||||
regPC++
|
||||
pushStackAddr(regPC)
|
||||
}
|
||||
regP.B = pendingInterrupt == null
|
||||
// handle BRK ('software interrupt')
|
||||
regPC++
|
||||
pushStackAddr(regPC)
|
||||
regP.B = true
|
||||
pushStack(regP)
|
||||
regP.I = true // interrupts are now disabled
|
||||
regP.D = false // this is different from NMOS 6502
|
||||
regPC = readWord(if (nmi) NMI_vector else IRQ_vector)
|
||||
pendingInterrupt = null
|
||||
regPC = readWord(IRQ_vector)
|
||||
}
|
||||
|
||||
override fun handleInterrupt() {
|
||||
// handle NMI or IRQ -- very similar to the BRK opcode above
|
||||
pushStackAddr(regPC-1)
|
||||
regP.B = false
|
||||
pushStack(regP)
|
||||
regP.I = true // interrupts are now disabled
|
||||
regP.D = false // this is different from NMOS 6502
|
||||
regPC = readWord(if (pendingNMI) NMI_vector else IRQ_vector)
|
||||
|
||||
if(pendingNMI)
|
||||
pendingNMI = false
|
||||
else
|
||||
pendingIRQ = false
|
||||
}
|
||||
|
||||
override fun iBit() {
|
||||
|
|
|
@ -1,7 +1,367 @@
|
|||
AS65 Assembler for R6502 [1.42]. Page 1
|
||||
--------------------------- 6502_decimal_test.a65 ----------------------------
|
||||
AS65 Assembler for R6502 [1.42]. Copyright 1994-2007, Frank A. Kingswood Page 1
|
||||
----------------------------------------------------- 6502_decimal_test.a65 ------------------------------------------------------
|
||||
|
||||
356 lines read, no errors in pass 1.
|
||||
; Verify decimal mode behavior
|
||||
; Written by Bruce Clark. This code is public domain.
|
||||
; see http://www.6502.org/tutorials/decimal_mode.html
|
||||
;
|
||||
; Returns:
|
||||
; ERROR = 0 if the test passed
|
||||
; ERROR = 1 if the test failed
|
||||
; modify the code at the DONE label for desired program end
|
||||
;
|
||||
; This routine requires 17 bytes of RAM -- 1 byte each for:
|
||||
; AR, CF, DA, DNVZC, ERROR, HA, HNVZC, N1, N1H, N1L, N2, N2L, NF, VF, and ZF
|
||||
; and 2 bytes for N2H
|
||||
;
|
||||
; Variables:
|
||||
; N1 and N2 are the two numbers to be added or subtracted
|
||||
; N1H, N1L, N2H, and N2L are the upper 4 bits and lower 4 bits of N1 and N2
|
||||
; DA and DNVZC are the actual accumulator and flag results in decimal mode
|
||||
; HA and HNVZC are the accumulator and flag results when N1 and N2 are
|
||||
; added or subtracted using binary arithmetic
|
||||
; AR, NF, VF, ZF, and CF are the predicted decimal mode accumulator and
|
||||
; flag results, calculated using binary arithmetic
|
||||
;
|
||||
; This program takes approximately 1 minute at 1 MHz (a few seconds more on
|
||||
; a 65C02 than a 6502 or 65816)
|
||||
;
|
||||
|
||||
; Configuration:
|
||||
0000 = cputype = 0 ; 0 = 6502, 1 = 65C02, 2 = 65C816
|
||||
0000 = vld_bcd = 0 ; 0 = allow invalid bcd, 1 = valid bcd only
|
||||
0001 = chk_a = 1 ; check accumulator
|
||||
0000 = chk_n = 0 ; check sign (negative) flag
|
||||
0000 = chk_v = 0 ; check overflow flag
|
||||
0000 = chk_z = 0 ; check zero flag
|
||||
0001 = chk_c = 1 ; check carry flag
|
||||
|
||||
end_of_test macro
|
||||
brk
|
||||
; db $db ;execute 65C02 stop instruction
|
||||
endm
|
||||
|
||||
bss
|
||||
0000 = org 0
|
||||
; operands - register Y = carry in
|
||||
0000 = N1 ds 1
|
||||
0001 = N2 ds 1
|
||||
; binary result
|
||||
0002 = HA ds 1
|
||||
0003 = HNVZC ds 1
|
||||
;04
|
||||
; decimal result
|
||||
0004 = DA ds 1
|
||||
0005 = DNVZC ds 1
|
||||
; predicted results
|
||||
0006 = AR ds 1
|
||||
0007 = NF ds 1
|
||||
;08
|
||||
0008 = VF ds 1
|
||||
0009 = ZF ds 1
|
||||
000a = CF ds 1
|
||||
000b = ERROR ds 1
|
||||
;0C
|
||||
; workspace
|
||||
000c = N1L ds 1
|
||||
000d = N1H ds 1
|
||||
000e = N2L ds 1
|
||||
000f = N2H ds 2
|
||||
|
||||
code
|
||||
0200 = org $200
|
||||
0200 : a001 TEST ldy #1 ; initialize Y (used to loop through carry flag values)
|
||||
0202 : 840b sty ERROR ; store 1 in ERROR until the test passes
|
||||
0204 : a900 lda #0 ; initialize N1 and N2
|
||||
0206 : 8500 sta N1
|
||||
0208 : 8501 sta N2
|
||||
020a : a501 LOOP1 lda N2 ; N2L = N2 & $0F
|
||||
020c : 290f and #$0F ; [1] see text
|
||||
if vld_bcd = 1
|
||||
cmp #$0a
|
||||
bcs NEXT2
|
||||
endif
|
||||
020e : 850e sta N2L
|
||||
0210 : a501 lda N2 ; N2H = N2 & $F0
|
||||
0212 : 29f0 and #$F0 ; [2] see text
|
||||
if vld_bcd = 1
|
||||
cmp #$a0
|
||||
bcs NEXT2
|
||||
endif
|
||||
0214 : 850f sta N2H
|
||||
0216 : 090f ora #$0F ; N2H+1 = (N2 & $F0) + $0F
|
||||
0218 : 8510 sta N2H+1
|
||||
021a : a500 LOOP2 lda N1 ; N1L = N1 & $0F
|
||||
021c : 290f and #$0F ; [3] see text
|
||||
if vld_bcd = 1
|
||||
cmp #$0a
|
||||
bcs NEXT1
|
||||
endif
|
||||
021e : 850c sta N1L
|
||||
0220 : a500 lda N1 ; N1H = N1 & $F0
|
||||
0222 : 29f0 and #$F0 ; [4] see text
|
||||
if vld_bcd = 1
|
||||
cmp #$a0
|
||||
bcs NEXT1
|
||||
endif
|
||||
0224 : 850d sta N1H
|
||||
0226 : 204c02 jsr ADD
|
||||
0229 : 20d302 jsr A6502
|
||||
022c : 20c602 jsr COMPARE
|
||||
022f : d01a bne DONE
|
||||
0231 : 209002 jsr SUB
|
||||
0234 : 20dc02 jsr S6502
|
||||
0237 : 20c602 jsr COMPARE
|
||||
023a : d00f bne DONE
|
||||
023c : e600 NEXT1 inc N1 ; [5] see text
|
||||
023e : d0da bne LOOP2 ; loop through all 256 values of N1
|
||||
0240 : e601 NEXT2 inc N2 ; [6] see text
|
||||
0242 : d0c6 bne LOOP1 ; loop through all 256 values of N2
|
||||
0244 : 88 dey
|
||||
0245 : 10c3 bpl LOOP1 ; loop through both values of the carry flag
|
||||
0247 : a900 lda #0 ; test passed, so store 0 in ERROR
|
||||
0249 : 850b sta ERROR
|
||||
024b : DONE
|
||||
end_of_test
|
||||
024b : 00 > brk
|
||||
> ; db $db ;execute 65C02 stop instruction
|
||||
|
||||
|
||||
; Calculate the actual decimal mode accumulator and flags, the accumulator
|
||||
; and flag results when N1 is added to N2 using binary arithmetic, the
|
||||
; predicted accumulator result, the predicted carry flag, and the predicted
|
||||
; V flag
|
||||
;
|
||||
024c : f8 ADD sed ; decimal mode
|
||||
024d : c001 cpy #1 ; set carry if Y = 1, clear carry if Y = 0
|
||||
024f : a500 lda N1
|
||||
0251 : 6501 adc N2
|
||||
0253 : 8504 sta DA ; actual accumulator result in decimal mode
|
||||
0255 : 08 php
|
||||
0256 : 68 pla
|
||||
0257 : 8505 sta DNVZC ; actual flags result in decimal mode
|
||||
0259 : d8 cld ; binary mode
|
||||
025a : c001 cpy #1 ; set carry if Y = 1, clear carry if Y = 0
|
||||
025c : a500 lda N1
|
||||
025e : 6501 adc N2
|
||||
0260 : 8502 sta HA ; accumulator result of N1+N2 using binary arithmetic
|
||||
|
||||
0262 : 08 php
|
||||
0263 : 68 pla
|
||||
0264 : 8503 sta HNVZC ; flags result of N1+N2 using binary arithmetic
|
||||
0266 : c001 cpy #1
|
||||
0268 : a50c lda N1L
|
||||
026a : 650e adc N2L
|
||||
026c : c90a cmp #$0A
|
||||
026e : a200 ldx #0
|
||||
0270 : 9006 bcc A1
|
||||
0272 : e8 inx
|
||||
0273 : 6905 adc #5 ; add 6 (carry is set)
|
||||
0275 : 290f and #$0F
|
||||
0277 : 38 sec
|
||||
0278 : 050d A1 ora N1H
|
||||
;
|
||||
; if N1L + N2L < $0A, then add N2 & $F0
|
||||
; if N1L + N2L >= $0A, then add (N2 & $F0) + $0F + 1 (carry is set)
|
||||
;
|
||||
027a : 750f adc N2H,x
|
||||
027c : 08 php
|
||||
027d : b004 bcs A2
|
||||
027f : c9a0 cmp #$A0
|
||||
0281 : 9003 bcc A3
|
||||
0283 : 695f A2 adc #$5F ; add $60 (carry is set)
|
||||
0285 : 38 sec
|
||||
0286 : 8506 A3 sta AR ; predicted accumulator result
|
||||
0288 : 08 php
|
||||
0289 : 68 pla
|
||||
028a : 850a sta CF ; predicted carry result
|
||||
028c : 68 pla
|
||||
;
|
||||
; note that all 8 bits of the P register are stored in VF
|
||||
;
|
||||
028d : 8508 sta VF ; predicted V flags
|
||||
028f : 60 rts
|
||||
|
||||
; Calculate the actual decimal mode accumulator and flags, and the
|
||||
; accumulator and flag results when N2 is subtracted from N1 using binary
|
||||
; arithmetic
|
||||
;
|
||||
0290 : f8 SUB sed ; decimal mode
|
||||
0291 : c001 cpy #1 ; set carry if Y = 1, clear carry if Y = 0
|
||||
0293 : a500 lda N1
|
||||
0295 : e501 sbc N2
|
||||
0297 : 8504 sta DA ; actual accumulator result in decimal mode
|
||||
0299 : 08 php
|
||||
029a : 68 pla
|
||||
029b : 8505 sta DNVZC ; actual flags result in decimal mode
|
||||
029d : d8 cld ; binary mode
|
||||
029e : c001 cpy #1 ; set carry if Y = 1, clear carry if Y = 0
|
||||
02a0 : a500 lda N1
|
||||
02a2 : e501 sbc N2
|
||||
02a4 : 8502 sta HA ; accumulator result of N1-N2 using binary arithmetic
|
||||
|
||||
02a6 : 08 php
|
||||
02a7 : 68 pla
|
||||
02a8 : 8503 sta HNVZC ; flags result of N1-N2 using binary arithmetic
|
||||
02aa : 60 rts
|
||||
|
||||
if cputype != 1
|
||||
; Calculate the predicted SBC accumulator result for the 6502 and 65816
|
||||
;
|
||||
02ab : c001 SUB1 cpy #1 ; set carry if Y = 1, clear carry if Y = 0
|
||||
02ad : a50c lda N1L
|
||||
02af : e50e sbc N2L
|
||||
02b1 : a200 ldx #0
|
||||
02b3 : b006 bcs S11
|
||||
02b5 : e8 inx
|
||||
02b6 : e905 sbc #5 ; subtract 6 (carry is clear)
|
||||
02b8 : 290f and #$0F
|
||||
02ba : 18 clc
|
||||
02bb : 050d S11 ora N1H
|
||||
;
|
||||
; if N1L - N2L >= 0, then subtract N2 & $F0
|
||||
; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear)
|
||||
;
|
||||
02bd : f50f sbc N2H,x
|
||||
02bf : b002 bcs S12
|
||||
02c1 : e95f sbc #$5F ; subtract $60 (carry is clear)
|
||||
02c3 : 8506 S12 sta AR
|
||||
02c5 : 60 rts
|
||||
endif
|
||||
|
||||
if cputype = 1
|
||||
; Calculate the predicted SBC accumulator result for the 6502 and 65C02
|
||||
;
|
||||
SUB2 cpy #1 ; set carry if Y = 1, clear carry if Y = 0
|
||||
lda N1L
|
||||
sbc N2L
|
||||
ldx #0
|
||||
bcs S21
|
||||
inx
|
||||
and #$0F
|
||||
clc
|
||||
S21 ora N1H
|
||||
;
|
||||
; if N1L - N2L >= 0, then subtract N2 & $F0
|
||||
; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear)
|
||||
;
|
||||
sbc N2H,x
|
||||
bcs S22
|
||||
sbc #$5F ; subtract $60 (carry is clear)
|
||||
S22 cpx #0
|
||||
beq S23
|
||||
sbc #6
|
||||
S23 sta AR ; predicted accumulator result
|
||||
rts
|
||||
endif
|
||||
|
||||
; Compare accumulator actual results to predicted results
|
||||
;
|
||||
; Return:
|
||||
; Z flag = 1 (BEQ branch) if same
|
||||
; Z flag = 0 (BNE branch) if different
|
||||
;
|
||||
02c6 : COMPARE
|
||||
if chk_a = 1
|
||||
02c6 : a504 lda DA
|
||||
02c8 : c506 cmp AR
|
||||
02ca : d006 bne C1
|
||||
endif
|
||||
if chk_n = 1
|
||||
lda DNVZC ; [7] see text
|
||||
eor NF
|
||||
and #$80 ; mask off N flag
|
||||
bne C1
|
||||
endif
|
||||
if chk_v = 1
|
||||
lda DNVZC ; [8] see text
|
||||
eor VF
|
||||
and #$40 ; mask off V flag
|
||||
bne C1 ; [9] see text
|
||||
endif
|
||||
if chk_z = 1
|
||||
lda DNVZC
|
||||
eor ZF ; mask off Z flag
|
||||
and #2
|
||||
bne C1 ; [10] see text
|
||||
endif
|
||||
if chk_c = 1
|
||||
02cc : a505 lda DNVZC
|
||||
02ce : 450a eor CF
|
||||
02d0 : 2901 and #1 ; mask off C flag
|
||||
endif
|
||||
02d2 : 60 C1 rts
|
||||
|
||||
; These routines store the predicted values for ADC and SBC for the 6502,
|
||||
; 65C02, and 65816 in AR, CF, NF, VF, and ZF
|
||||
|
||||
if cputype = 0
|
||||
|
||||
02d3 : a508 A6502 lda VF ; 6502
|
||||
;
|
||||
; since all 8 bits of the P register were stored in VF, bit 7 of VF contains
|
||||
; the N flag for NF
|
||||
;
|
||||
02d5 : 8507 sta NF
|
||||
02d7 : a503 lda HNVZC
|
||||
02d9 : 8509 sta ZF
|
||||
02db : 60 rts
|
||||
|
||||
02dc : 20ab02 S6502 jsr SUB1
|
||||
02df : a503 lda HNVZC
|
||||
02e1 : 8507 sta NF
|
||||
02e3 : 8508 sta VF
|
||||
02e5 : 8509 sta ZF
|
||||
02e7 : 850a sta CF
|
||||
02e9 : 60 rts
|
||||
|
||||
endif
|
||||
if cputype = 1
|
||||
|
||||
A6502 lda AR ; 65C02
|
||||
php
|
||||
pla
|
||||
sta NF
|
||||
sta ZF
|
||||
rts
|
||||
|
||||
S6502 jsr SUB2
|
||||
lda AR
|
||||
php
|
||||
pla
|
||||
sta NF
|
||||
sta ZF
|
||||
lda HNVZC
|
||||
sta VF
|
||||
sta CF
|
||||
rts
|
||||
|
||||
endif
|
||||
if cputype = 2
|
||||
|
||||
A6502 lda AR ; 65C816
|
||||
php
|
||||
pla
|
||||
sta NF
|
||||
sta ZF
|
||||
rts
|
||||
|
||||
S6502 jsr SUB1
|
||||
lda AR
|
||||
php
|
||||
pla
|
||||
sta NF
|
||||
sta ZF
|
||||
lda HNVZC
|
||||
sta VF
|
||||
sta CF
|
||||
rts
|
||||
|
||||
endif
|
||||
|
||||
02dc = end TEST
|
||||
|
||||
No errors in pass 2.
|
||||
Wrote binary from address $0200 through $02e9.
|
||||
Total size 234 bytes.
|
||||
|
|
|
@ -1,7 +1,367 @@
|
|||
AS65 Assembler for R6502 [1.42]. Page 1
|
||||
--------------------------- 65C02_decimal_test.a65 ---------------------------
|
||||
AS65 Assembler for R6502 [1.42]. Copyright 1994-2007, Frank A. Kingswood Page 1
|
||||
----------------------------------------------------- 65C02_decimal_test.a65 -----------------------------------------------------
|
||||
|
||||
356 lines read, no errors in pass 1.
|
||||
; Verify decimal mode behavior
|
||||
; Written by Bruce Clark. This code is public domain.
|
||||
; see http://www.6502.org/tutorials/decimal_mode.html
|
||||
;
|
||||
; Returns:
|
||||
; ERROR = 0 if the test passed
|
||||
; ERROR = 1 if the test failed
|
||||
; modify the code at the DONE label for desired program end
|
||||
;
|
||||
; This routine requires 17 bytes of RAM -- 1 byte each for:
|
||||
; AR, CF, DA, DNVZC, ERROR, HA, HNVZC, N1, N1H, N1L, N2, N2L, NF, VF, and ZF
|
||||
; and 2 bytes for N2H
|
||||
;
|
||||
; Variables:
|
||||
; N1 and N2 are the two numbers to be added or subtracted
|
||||
; N1H, N1L, N2H, and N2L are the upper 4 bits and lower 4 bits of N1 and N2
|
||||
; DA and DNVZC are the actual accumulator and flag results in decimal mode
|
||||
; HA and HNVZC are the accumulator and flag results when N1 and N2 are
|
||||
; added or subtracted using binary arithmetic
|
||||
; AR, NF, VF, ZF, and CF are the predicted decimal mode accumulator and
|
||||
; flag results, calculated using binary arithmetic
|
||||
;
|
||||
; This program takes approximately 1 minute at 1 MHz (a few seconds more on
|
||||
; a 65C02 than a 6502 or 65816)
|
||||
;
|
||||
|
||||
; Configuration:
|
||||
0001 = cputype = 1 ; 0 = 6502, 1 = 65C02, 2 = 65C816
|
||||
0000 = vld_bcd = 0 ; 0 = allow invalid bcd, 1 = valid bcd only
|
||||
0001 = chk_a = 1 ; check accumulator
|
||||
0000 = chk_n = 0 ; check sign (negative) flag
|
||||
0000 = chk_v = 0 ; check overflow flag
|
||||
0000 = chk_z = 0 ; check zero flag
|
||||
0001 = chk_c = 1 ; check carry flag
|
||||
|
||||
end_of_test macro
|
||||
brk
|
||||
; db $db ;execute 65C02 stop instruction
|
||||
endm
|
||||
|
||||
bss
|
||||
0000 = org 0
|
||||
; operands - register Y = carry in
|
||||
0000 = N1 ds 1
|
||||
0001 = N2 ds 1
|
||||
; binary result
|
||||
0002 = HA ds 1
|
||||
0003 = HNVZC ds 1
|
||||
;04
|
||||
; decimal result
|
||||
0004 = DA ds 1
|
||||
0005 = DNVZC ds 1
|
||||
; predicted results
|
||||
0006 = AR ds 1
|
||||
0007 = NF ds 1
|
||||
;08
|
||||
0008 = VF ds 1
|
||||
0009 = ZF ds 1
|
||||
000a = CF ds 1
|
||||
000b = ERROR ds 1
|
||||
;0C
|
||||
; workspace
|
||||
000c = N1L ds 1
|
||||
000d = N1H ds 1
|
||||
000e = N2L ds 1
|
||||
000f = N2H ds 2
|
||||
|
||||
code
|
||||
0200 = org $200
|
||||
0200 : a001 TEST ldy #1 ; initialize Y (used to loop through carry flag values)
|
||||
0202 : 840b sty ERROR ; store 1 in ERROR until the test passes
|
||||
0204 : a900 lda #0 ; initialize N1 and N2
|
||||
0206 : 8500 sta N1
|
||||
0208 : 8501 sta N2
|
||||
020a : a501 LOOP1 lda N2 ; N2L = N2 & $0F
|
||||
020c : 290f and #$0F ; [1] see text
|
||||
if vld_bcd = 1
|
||||
cmp #$0a
|
||||
bcs NEXT2
|
||||
endif
|
||||
020e : 850e sta N2L
|
||||
0210 : a501 lda N2 ; N2H = N2 & $F0
|
||||
0212 : 29f0 and #$F0 ; [2] see text
|
||||
if vld_bcd = 1
|
||||
cmp #$a0
|
||||
bcs NEXT2
|
||||
endif
|
||||
0214 : 850f sta N2H
|
||||
0216 : 090f ora #$0F ; N2H+1 = (N2 & $F0) + $0F
|
||||
0218 : 8510 sta N2H+1
|
||||
021a : a500 LOOP2 lda N1 ; N1L = N1 & $0F
|
||||
021c : 290f and #$0F ; [3] see text
|
||||
if vld_bcd = 1
|
||||
cmp #$0a
|
||||
bcs NEXT1
|
||||
endif
|
||||
021e : 850c sta N1L
|
||||
0220 : a500 lda N1 ; N1H = N1 & $F0
|
||||
0222 : 29f0 and #$F0 ; [4] see text
|
||||
if vld_bcd = 1
|
||||
cmp #$a0
|
||||
bcs NEXT1
|
||||
endif
|
||||
0224 : 850d sta N1H
|
||||
0226 : 204c02 jsr ADD
|
||||
0229 : 20d702 jsr A6502
|
||||
022c : 20ca02 jsr COMPARE
|
||||
022f : d01a bne DONE
|
||||
0231 : 209002 jsr SUB
|
||||
0234 : 20e002 jsr S6502
|
||||
0237 : 20ca02 jsr COMPARE
|
||||
023a : d00f bne DONE
|
||||
023c : e600 NEXT1 inc N1 ; [5] see text
|
||||
023e : d0da bne LOOP2 ; loop through all 256 values of N1
|
||||
0240 : e601 NEXT2 inc N2 ; [6] see text
|
||||
0242 : d0c6 bne LOOP1 ; loop through all 256 values of N2
|
||||
0244 : 88 dey
|
||||
0245 : 10c3 bpl LOOP1 ; loop through both values of the carry flag
|
||||
0247 : a900 lda #0 ; test passed, so store 0 in ERROR
|
||||
0249 : 850b sta ERROR
|
||||
024b : DONE
|
||||
end_of_test
|
||||
024b : 00 > brk
|
||||
> ; db $db ;execute 65C02 stop instruction
|
||||
|
||||
|
||||
; Calculate the actual decimal mode accumulator and flags, the accumulator
|
||||
; and flag results when N1 is added to N2 using binary arithmetic, the
|
||||
; predicted accumulator result, the predicted carry flag, and the predicted
|
||||
; V flag
|
||||
;
|
||||
024c : f8 ADD sed ; decimal mode
|
||||
024d : c001 cpy #1 ; set carry if Y = 1, clear carry if Y = 0
|
||||
024f : a500 lda N1
|
||||
0251 : 6501 adc N2
|
||||
0253 : 8504 sta DA ; actual accumulator result in decimal mode
|
||||
0255 : 08 php
|
||||
0256 : 68 pla
|
||||
0257 : 8505 sta DNVZC ; actual flags result in decimal mode
|
||||
0259 : d8 cld ; binary mode
|
||||
025a : c001 cpy #1 ; set carry if Y = 1, clear carry if Y = 0
|
||||
025c : a500 lda N1
|
||||
025e : 6501 adc N2
|
||||
0260 : 8502 sta HA ; accumulator result of N1+N2 using binary arithmetic
|
||||
|
||||
0262 : 08 php
|
||||
0263 : 68 pla
|
||||
0264 : 8503 sta HNVZC ; flags result of N1+N2 using binary arithmetic
|
||||
0266 : c001 cpy #1
|
||||
0268 : a50c lda N1L
|
||||
026a : 650e adc N2L
|
||||
026c : c90a cmp #$0A
|
||||
026e : a200 ldx #0
|
||||
0270 : 9006 bcc A1
|
||||
0272 : e8 inx
|
||||
0273 : 6905 adc #5 ; add 6 (carry is set)
|
||||
0275 : 290f and #$0F
|
||||
0277 : 38 sec
|
||||
0278 : 050d A1 ora N1H
|
||||
;
|
||||
; if N1L + N2L < $0A, then add N2 & $F0
|
||||
; if N1L + N2L >= $0A, then add (N2 & $F0) + $0F + 1 (carry is set)
|
||||
;
|
||||
027a : 750f adc N2H,x
|
||||
027c : 08 php
|
||||
027d : b004 bcs A2
|
||||
027f : c9a0 cmp #$A0
|
||||
0281 : 9003 bcc A3
|
||||
0283 : 695f A2 adc #$5F ; add $60 (carry is set)
|
||||
0285 : 38 sec
|
||||
0286 : 8506 A3 sta AR ; predicted accumulator result
|
||||
0288 : 08 php
|
||||
0289 : 68 pla
|
||||
028a : 850a sta CF ; predicted carry result
|
||||
028c : 68 pla
|
||||
;
|
||||
; note that all 8 bits of the P register are stored in VF
|
||||
;
|
||||
028d : 8508 sta VF ; predicted V flags
|
||||
028f : 60 rts
|
||||
|
||||
; Calculate the actual decimal mode accumulator and flags, and the
|
||||
; accumulator and flag results when N2 is subtracted from N1 using binary
|
||||
; arithmetic
|
||||
;
|
||||
0290 : f8 SUB sed ; decimal mode
|
||||
0291 : c001 cpy #1 ; set carry if Y = 1, clear carry if Y = 0
|
||||
0293 : a500 lda N1
|
||||
0295 : e501 sbc N2
|
||||
0297 : 8504 sta DA ; actual accumulator result in decimal mode
|
||||
0299 : 08 php
|
||||
029a : 68 pla
|
||||
029b : 8505 sta DNVZC ; actual flags result in decimal mode
|
||||
029d : d8 cld ; binary mode
|
||||
029e : c001 cpy #1 ; set carry if Y = 1, clear carry if Y = 0
|
||||
02a0 : a500 lda N1
|
||||
02a2 : e501 sbc N2
|
||||
02a4 : 8502 sta HA ; accumulator result of N1-N2 using binary arithmetic
|
||||
|
||||
02a6 : 08 php
|
||||
02a7 : 68 pla
|
||||
02a8 : 8503 sta HNVZC ; flags result of N1-N2 using binary arithmetic
|
||||
02aa : 60 rts
|
||||
|
||||
if cputype != 1
|
||||
; Calculate the predicted SBC accumulator result for the 6502 and 65816
|
||||
;
|
||||
SUB1 cpy #1 ; set carry if Y = 1, clear carry if Y = 0
|
||||
lda N1L
|
||||
sbc N2L
|
||||
ldx #0
|
||||
bcs S11
|
||||
inx
|
||||
sbc #5 ; subtract 6 (carry is clear)
|
||||
and #$0F
|
||||
clc
|
||||
S11 ora N1H
|
||||
;
|
||||
; if N1L - N2L >= 0, then subtract N2 & $F0
|
||||
; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear)
|
||||
;
|
||||
sbc N2H,x
|
||||
bcs S12
|
||||
sbc #$5F ; subtract $60 (carry is clear)
|
||||
S12 sta AR
|
||||
rts
|
||||
endif
|
||||
|
||||
if cputype = 1
|
||||
; Calculate the predicted SBC accumulator result for the 6502 and 65C02
|
||||
;
|
||||
02ab : c001 SUB2 cpy #1 ; set carry if Y = 1, clear carry if Y = 0
|
||||
02ad : a50c lda N1L
|
||||
02af : e50e sbc N2L
|
||||
02b1 : a200 ldx #0
|
||||
02b3 : b004 bcs S21
|
||||
02b5 : e8 inx
|
||||
02b6 : 290f and #$0F
|
||||
02b8 : 18 clc
|
||||
02b9 : 050d S21 ora N1H
|
||||
;
|
||||
; if N1L - N2L >= 0, then subtract N2 & $F0
|
||||
; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear)
|
||||
;
|
||||
02bb : f50f sbc N2H,x
|
||||
02bd : b002 bcs S22
|
||||
02bf : e95f sbc #$5F ; subtract $60 (carry is clear)
|
||||
02c1 : e000 S22 cpx #0
|
||||
02c3 : f002 beq S23
|
||||
02c5 : e906 sbc #6
|
||||
02c7 : 8506 S23 sta AR ; predicted accumulator result
|
||||
02c9 : 60 rts
|
||||
endif
|
||||
|
||||
; Compare accumulator actual results to predicted results
|
||||
;
|
||||
; Return:
|
||||
; Z flag = 1 (BEQ branch) if same
|
||||
; Z flag = 0 (BNE branch) if different
|
||||
;
|
||||
02ca : COMPARE
|
||||
if chk_a = 1
|
||||
02ca : a504 lda DA
|
||||
02cc : c506 cmp AR
|
||||
02ce : d006 bne C1
|
||||
endif
|
||||
if chk_n = 1
|
||||
lda DNVZC ; [7] see text
|
||||
eor NF
|
||||
and #$80 ; mask off N flag
|
||||
bne C1
|
||||
endif
|
||||
if chk_v = 1
|
||||
lda DNVZC ; [8] see text
|
||||
eor VF
|
||||
and #$40 ; mask off V flag
|
||||
bne C1 ; [9] see text
|
||||
endif
|
||||
if chk_z = 1
|
||||
lda DNVZC
|
||||
eor ZF ; mask off Z flag
|
||||
and #2
|
||||
bne C1 ; [10] see text
|
||||
endif
|
||||
if chk_c = 1
|
||||
02d0 : a505 lda DNVZC
|
||||
02d2 : 450a eor CF
|
||||
02d4 : 2901 and #1 ; mask off C flag
|
||||
endif
|
||||
02d6 : 60 C1 rts
|
||||
|
||||
; These routines store the predicted values for ADC and SBC for the 6502,
|
||||
; 65C02, and 65816 in AR, CF, NF, VF, and ZF
|
||||
|
||||
if cputype = 0
|
||||
|
||||
A6502 lda VF ; 6502
|
||||
;
|
||||
; since all 8 bits of the P register were stored in VF, bit 7 of VF contains
|
||||
; the N flag for NF
|
||||
;
|
||||
sta NF
|
||||
lda HNVZC
|
||||
sta ZF
|
||||
rts
|
||||
|
||||
S6502 jsr SUB1
|
||||
lda HNVZC
|
||||
sta NF
|
||||
sta VF
|
||||
sta ZF
|
||||
sta CF
|
||||
rts
|
||||
|
||||
endif
|
||||
if cputype = 1
|
||||
|
||||
02d7 : a506 A6502 lda AR ; 65C02
|
||||
02d9 : 08 php
|
||||
02da : 68 pla
|
||||
02db : 8507 sta NF
|
||||
02dd : 8509 sta ZF
|
||||
02df : 60 rts
|
||||
|
||||
02e0 : 20ab02 S6502 jsr SUB2
|
||||
02e3 : a506 lda AR
|
||||
02e5 : 08 php
|
||||
02e6 : 68 pla
|
||||
02e7 : 8507 sta NF
|
||||
02e9 : 8509 sta ZF
|
||||
02eb : a503 lda HNVZC
|
||||
02ed : 8508 sta VF
|
||||
02ef : 850a sta CF
|
||||
02f1 : 60 rts
|
||||
|
||||
endif
|
||||
if cputype = 2
|
||||
|
||||
A6502 lda AR ; 65C816
|
||||
php
|
||||
pla
|
||||
sta NF
|
||||
sta ZF
|
||||
rts
|
||||
|
||||
S6502 jsr SUB1
|
||||
lda AR
|
||||
php
|
||||
pla
|
||||
sta NF
|
||||
sta ZF
|
||||
lda HNVZC
|
||||
sta VF
|
||||
sta CF
|
||||
rts
|
||||
|
||||
endif
|
||||
|
||||
02e0 = end TEST
|
||||
|
||||
No errors in pass 2.
|
||||
Wrote binary from address $0200 through $02f1.
|
||||
Total size 242 bytes.
|
||||
|
|
|
@ -2,6 +2,10 @@ import razorvine.ksim65.Bus
|
|||
import razorvine.ksim65.components.Ram
|
||||
import razorvine.ksim65.Cpu6502
|
||||
import razorvine.ksim65.Cpu65C02
|
||||
import razorvine.ksim65.components.Address
|
||||
import razorvine.ksim65.components.BusComponent
|
||||
import razorvine.ksim65.components.MemMappedComponent
|
||||
import razorvine.ksim65.components.UByte
|
||||
import razorvine.ksim65.hexW
|
||||
import java.lang.Exception
|
||||
import kotlin.math.max
|
||||
|
@ -37,7 +41,8 @@ class Test6502Klaus2m5Functional {
|
|||
}
|
||||
|
||||
// the test is successful if address 0x3469 is reached ("success" label in source code)
|
||||
if(cpu.regPC!=0x3469) {
|
||||
val testnum = bus[0x200].toInt()
|
||||
if(cpu.regPC!=0x3469 || testnum!=0xf0) {
|
||||
println(cpu.snapshot())
|
||||
val d = cpu.disassemble(ram, max(0, cpu.regPC-20), min(65535, cpu.regPC+20))
|
||||
println(d.first.joinToString("\n"))
|
||||
|
@ -65,7 +70,9 @@ class Test6502Klaus2m5Functional {
|
|||
}
|
||||
|
||||
// the test is successful if address 0x24f1 is reached ("success" label in source code)
|
||||
if(cpu.regPC!=0x24f1) {
|
||||
val testnum = bus[0x202].toInt()
|
||||
println(testnum)
|
||||
if(cpu.regPC!=0x24f1 || testnum!=0xf0) {
|
||||
println(cpu.snapshot())
|
||||
val d = cpu.disassemble(ram, max(0, cpu.regPC-20), min(65535, cpu.regPC+20))
|
||||
println(d.first.joinToString("\n"))
|
||||
|
@ -77,10 +84,45 @@ class Test6502Klaus2m5Functional {
|
|||
fun testInterrupts6502() {
|
||||
// TODO fix this test code
|
||||
val cpu = Cpu6502()
|
||||
class Trigger(startAddress: Address, endAddress: Address) : MemMappedComponent(startAddress, endAddress) {
|
||||
var value: UByte = 0
|
||||
var lastIRQpc: Address = 0
|
||||
var lastNMIpc: Address = 0
|
||||
|
||||
override fun get(offset: Int): UByte = value
|
||||
|
||||
override fun set(offset: Int, data: UByte) {
|
||||
value = data
|
||||
when(value.toInt()) {
|
||||
1 -> {
|
||||
println("IRQ at pc ${hexW(cpu.regPC)}")
|
||||
lastIRQpc = cpu.regPC
|
||||
cpu.irq()
|
||||
}
|
||||
2 -> {
|
||||
println("NMI at pc ${hexW(cpu.regPC)}")
|
||||
lastNMIpc = cpu.regPC
|
||||
cpu.nmi()
|
||||
}
|
||||
3 -> {
|
||||
println("IRQ+NMI at pc ${hexW(cpu.regPC)}")
|
||||
lastIRQpc = cpu.regPC
|
||||
lastNMIpc = cpu.regPC
|
||||
cpu.nmi()
|
||||
cpu.irq()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun clock() {}
|
||||
override fun reset() {}
|
||||
}
|
||||
val bus = Bus()
|
||||
val ram = Ram(0, 0xffff)
|
||||
val irqtrigger = Trigger(0xbffc, 0xbffc)
|
||||
ram.load("src/test/kotlin/6502_functional_tests/bin_files/6502_interrupt_test.bin", 0)
|
||||
bus.add(cpu)
|
||||
bus.add(irqtrigger)
|
||||
bus.add(ram)
|
||||
cpu.reset()
|
||||
cpu.regPC = 0x0400
|
||||
|
@ -95,6 +137,7 @@ class Test6502Klaus2m5Functional {
|
|||
|
||||
// the test is successful if address 0x06f5 is reached ("success" label in source code)
|
||||
if(cpu.regPC!=0x06f5) {
|
||||
println("Last IRQ triggered at ${hexW(irqtrigger.lastIRQpc)} last NMI at ${hexW(irqtrigger.lastNMIpc)}")
|
||||
println(cpu.snapshot())
|
||||
val d = cpu.disassemble(ram, max(0, cpu.regPC-20), min(65535, cpu.regPC+20))
|
||||
println(d.first.joinToString("\n"))
|
||||
|
@ -113,7 +156,7 @@ class Test6502Klaus2m5Functional {
|
|||
cpu.reset()
|
||||
cpu.regPC = 0x0200
|
||||
cpu.breakpointForBRK = { cpu, address ->
|
||||
if(address in 513..2047) {
|
||||
if(address==0x024b) { // test end address
|
||||
val error=bus.read(0x000b) // the 'ERROR' variable is stored here
|
||||
if(error==0.toShort())
|
||||
throw SuccessfulTestResult()
|
||||
|
@ -148,7 +191,7 @@ class Test6502Klaus2m5Functional {
|
|||
cpu.reset()
|
||||
cpu.regPC = 0x0200
|
||||
cpu.breakpointForBRK = { cpu, address ->
|
||||
if(address in 513..2047) {
|
||||
if(address==0x024b) { // test end address
|
||||
val error=bus.read(0x000b) // the 'ERROR' variable is stored here
|
||||
if(error==0.toShort())
|
||||
throw SuccessfulTestResult()
|
||||
|
|
Loading…
Reference in New Issue
Block a user