PCjs Machines

Home of the original IBM PC emulator for browsers.

Logo

6502 BCD Tests

This program from 6502.org verifies 6502 Decimal Mode behavior.

;
; The following code comes from:
;
;	http://www.6502.org/tutorials/decimal_mode.html
;
; and is built with:
;
;	ca65 bcd.asm -I ../.. -l -o bcd.obj
;	ld65 bcd.obj -o bcd.bin --target none
;
; An ASCII hex file which the OSI machine-language monitor can load
; is produced with the following commands:
;
;	echo -n ".0200/" > bcd.txt
;	hexdump -e '1/1 "%02X\n"' -v bcd.bin >> bcd.txt
;	echo -n ".0200" >> bcd.txt
;
		.include "sim.inc"

VALID_BCD_ONLY		=	0
VALID_FLAGS_ONLY	=	0

;
; 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
		SIM_MSG "Testing %A"
        AND #$0F  ; [1] see text
        .if VALID_BCD_ONLY <> 0
		CMP #$0A
		BCS NEXT2
		.endif
		STA N2L
        LDA N2    ; N2H = N2 & $F0
        AND #$F0  ; [2] see text
        .if VALID_BCD_ONLY <> 0
		CMP #$A0
		BCS NEXT2
		.endif
        STA N2H
        ORA #$0F  ; N2H+1 = (N2 & $F0) + $0F
        STA N2H+1
LOOP2:  LDA N1    ; N1L = N1 & $0F
;;		SIM_MSG " with %A"
        AND #$0F  ; [3] see text
        .if VALID_BCD_ONLY <> 0
		CMP #$0A
		BCS NEXT1
		.endif
        STA N1L
        LDA N1    ; N1H = N1 & $F0
        AND #$F0  ; [4] see text
        .if VALID_BCD_ONLY <> 0
        CMP #$A0
		BCS NEXT1
		.endif
        STA N1H
        JSR ADD
        JSR A6502
        JSR COMPARE
        BNE ERR
        JSR SUB
        JSR S6502
        JSR COMPARE
        BNE ERR
NEXT1:  INC N1    ; [5] see text
        BNE LOOP2 ; loop through all 256 values of N1
NEXT2:  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
        SIM_MSG "PASS"
        BEQ DONE
ERR:	SIM_MSG "FAIL"
DONE:   SIM_HLT

; 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
        .if VALID_FLAGS_ONLY = 0
        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
        .endif
        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

		.data
AR:		.byte	0
CF:		.byte	0		; predicted carry result
DA:		.byte	0
DNVZC:	.byte	0
ERROR:	.byte	0
HA:		.byte	0
HNVZC:	.byte	0
N1:		.byte	0
N1H:	.byte	0
N1L:	.byte	0
N2:		.byte	0
N2L:	.byte	0
NF:		.byte	0
VF:		.byte	0
ZF:		.byte	0
N2H:	.word	0