go6502/tests/decimal_mode.a65

325 lines
7.5 KiB
Plaintext

; Decimal mode tests from Bruce Clark's fantastic Decimal Mode
; tutorial on 6502.org. Modified very slightly to allow choosing the
; CPU architecture (6502 or 65C02) without recompiling.
; http://www.6502.org/tutorials/decimal_mode.html#B
bss
org 0
ERROR ds 1
MODE ds 1 ; 0 for 6502, 1 for 65C02
AR ds 1
CF ds 1
DA ds 1
DNVZC ds 1
HA ds 1
HNVZC ds 1
N1 ds 1
N1H ds 1
N1L ds 1
N2 ds 1
N2L ds 1
NF ds 1
VF ds 1
ZF ds 1
N2H ds 2
code
org $1000
lda MODE
bne INIT65C02
INIT6502
lda #lo(A6502)
sta ADD_ADDR + 1
lda #hi(A6502)
sta ADD_ADDR + 2
lda #lo(S6502)
sta SUB_ADDR + 1
lda #hi(S6502)
sta SUB_ADDR + 2
jmp INITDONE
INIT65C02
lda #lo(A65C02)
sta ADD_ADDR + 1
lda #hi(A65C02)
sta ADD_ADDR + 2
lda #lo(S65C02)
sta SUB_ADDR + 1
lda #hi(S65C02)
sta SUB_ADDR + 2
jmp INITDONE
INITDONE
jsr TEST
lda #0
beq * ; done
; Verify decimal mode behavior
;
; Returns:
; ERROR = 0 if the test passed
; ERROR = 1 if the test failed
;
; 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)
;
TEST ldy #1 ; initialize y (used to loop through carry flag values)
sty ERROR ; store 1 in ERROR until the test passes
lda #0 ; initialize N1 and N2
sta N1
sta N2
LOOP1 lda N2 ; N2L = N2 & $0F
and #$0F ; [1] see text
sta N2L
lda N2 ; N2H = N2 & $F0
and #$F0 ; [2] see text
sta N2H
ora #$0F ; N2H+1 = (N2 & $F0) + $0F
sta N2H+1
LOOP2 lda N1 ; N1L = N1 & $0F
and #$0F ; [3] see text
sta N1L
lda N1 ; N1H = N1 & $F0
and #$F0 ; [4] see text
sta N1H
jsr ADD
ADD_ADDR = *
jsr A6502
jsr COMPARE
bne DONE
jsr SUB
SUB_ADDR = *
jsr S6502
jsr COMPARE
bne DONE
inc N1 ; [5] see text
bne LOOP2 ; loop through all 256 values of N1
inc N2 ; [6] see text
bne LOOP1 ; loop through all 256 values of N2
dey
bpl LOOP1 ; loop through both values of the carry flag
lda #0 ; test passed, so store 0 in ERROR
sta ERROR
DONE rts
; 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
;
ADD sed ; decimal mode
cpy #1 ; set carry if Y = 1, clear carry if Y = 0
lda N1
adc N2
sta DA ; actual accumulator result in decimal mode
php
pla
sta DNVZC ; actual flags result in decimal mode
cld ; binary mode
cpy #1 ; set carry if Y = 1, clear carry if Y = 0
lda N1
adc N2
sta HA ; accumulator result of N1+N2 using binary arithmetic
php
pla
sta HNVZC ; flags result of N1+N2 using binary arithmetic
cpy #1
lda N1L
adc N2L
cmp #$0A
ldx #0
bcc A1
inx
adc #5 ; add 6 (carry is set)
and #$0F
sec
A1 ora N1H
;
; if N1L + N2L < $0A, then add N2 & $F0
; if N1L + N2L >= $0A, then add (N2 & $F0) + $0F + 1 (carry is set)
;
adc N2H,x
php
bcs A2
cmp #$A0
bcc A3
A2 adc #$5F ; add $60 (carry is set)
sec
A3 sta AR ; predicted accumulator result
php
pla
sta CF ; predicted carry result
pla
;
; note that all 8 bits of the P register are stored in VF
;
sta VF ; predicted V flags
rts
; Calculate the actual decimal mode accumulator and flags, and the
; accumulator and flag results when N2 is subtracted from N1 using binary
; arithmetic
;
SUB sed ; decimal mode
cpy #1 ; set carry if Y = 1, clear carry if Y = 0
lda N1
sbc N2
sta DA ; actual accumulator result in decimal mode
php
pla
sta DNVZC ; actual flags result in decimal mode
cld ; binary mode
cpy #1 ; set carry if Y = 1, clear carry if Y = 0
lda N1
sbc N2
sta HA ; accumulator result of N1-N2 using binary arithmetic
php
pla
sta HNVZC ; flags result of N1-N2 using binary arithmetic
rts
; 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
; 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
; Compare accumulator actual results to predicted results
;
; Return:
; Z flag = 1 (BEQ branch) if same
; Z flag = 0 (BNE branch) if different
;
COMPARE lda DA
cmp AR
bne C1
lda DNVZC ; [7] see text
eor NF
and #$80 ; mask off N flag
bne C1
lda DNVZC ; [8] see text
eor VF
and #$40 ; mask off V flag
bne C1 ; [9] see text
lda DNVZC
eor ZF ; mask off Z flag
and #2
bne C1 ; [10] see text
lda DNVZC
eor CF
and #1 ; mask off C flag
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
A6502 lda VF
;
; 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
A65C02 lda AR
php
pla
sta NF
sta ZF
rts
S65C02 jsr SUB2
lda AR
php
pla
sta NF
sta ZF
lda HNVZC
sta VF
sta CF
rts
A65816 lda AR
php
pla
sta NF
sta ZF
rts
S65816 jsr SUB1
lda AR
php
pla
sta NF
sta ZF
lda HNVZC
sta VF
sta CF
rts