mirror of
https://github.com/zellyn/go6502.git
synced 2024-11-04 16:04:39 +00:00
325 lines
7.5 KiB
Plaintext
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
|