mirror of
https://github.com/mnaberez/py65.git
synced 2024-06-08 03:29:32 +00:00
Compare commits
21 Commits
9a82e6f676
...
4d0c2f6f8e
Author | SHA1 | Date | |
---|---|---|---|
|
4d0c2f6f8e | ||
|
d4459daa40 | ||
|
d6ccff26ad | ||
|
5b28c007d9 | ||
|
7d5a29cbaa | ||
|
803632c15a | ||
|
3468cdce1c | ||
|
3d30544b95 | ||
|
e0f56e48e0 | ||
|
0e7066a4e1 | ||
|
254b2fb167 | ||
|
8b9cf7db69 | ||
|
fab1174abb | ||
|
430fec1e28 | ||
|
9b5e1f3945 | ||
|
4f52c95511 | ||
|
666cd9cd99 | ||
|
32896cc139 | ||
|
5e7ade9cb9 | ||
|
33b48ffbfb | ||
|
7fec386d42 |
|
@ -1,6 +1,8 @@
|
|||
import sys
|
||||
|
||||
from py65.utils.conversions import itoa
|
||||
from py65.utils.devices import make_instruction_decorator
|
||||
|
||||
from py65 import disassembler
|
||||
|
||||
class MPU:
|
||||
# vectors
|
||||
|
@ -41,6 +43,8 @@ class MPU:
|
|||
self.memory = memory
|
||||
self.start_pc = pc # if None, reset vector is used
|
||||
|
||||
self.disassembler = disassembler.Disassembler(self)
|
||||
|
||||
# init
|
||||
self.reset()
|
||||
|
||||
|
@ -55,8 +59,12 @@ class MPU:
|
|||
return self.reprformat() % (indent, self.name, self.pc, self.a,
|
||||
self.x, self.y, self.sp, flags)
|
||||
|
||||
def step(self):
|
||||
def step(self, trace=False):
|
||||
instructCode = self.memory[self.pc]
|
||||
if trace:
|
||||
out = str(self) + " $%04X: %s" % (
|
||||
self.pc, self.disassembler.instruction_at(self.pc)[1])
|
||||
print >> sys.stderr, "\n".join(out.split('\n'))
|
||||
self.pc = (self.pc + 1) & self.addrMask
|
||||
self.excycles = 0
|
||||
self.addcycles = self.extracycles[instructCode]
|
||||
|
@ -315,6 +323,9 @@ class MPU:
|
|||
self.FlagsNZ(self.a)
|
||||
|
||||
def opADC(self, x):
|
||||
return self._opADC(x, decimal_flags_use_adjusted_result=False)
|
||||
|
||||
def _opADC(self, x, decimal_flags_use_adjusted_result):
|
||||
data = self.ByteAt(x())
|
||||
|
||||
if self.p & self.DECIMAL:
|
||||
|
@ -331,24 +342,45 @@ class MPU:
|
|||
adjust1 = 6
|
||||
decimalcarry = 1
|
||||
|
||||
# the ALU outputs are not decimally adjusted
|
||||
# The ALU outputs are not yet decimally adjusted
|
||||
nibble0 = nibble0 & 0xf
|
||||
nibble1 = nibble1 & 0xf
|
||||
aluresult = (nibble1 << 4) + nibble0
|
||||
|
||||
# the final A contents will be decimally adjusted
|
||||
# Partial result with only low nibble decimally adjusted
|
||||
nibble0 = (nibble0 + adjust0) & 0xf
|
||||
halfadjresult = (nibble1 << 4) + nibble0
|
||||
|
||||
# the final A contents has both nibbles decimally adjusted
|
||||
nibble1 = (nibble1 + adjust1) & 0xf
|
||||
adjresult = (nibble1 << 4) + nibble0
|
||||
|
||||
self.p &= ~(self.CARRY | self.OVERFLOW | self.NEGATIVE | self.ZERO)
|
||||
if aluresult == 0:
|
||||
|
||||
if decimal_flags_use_adjusted_result: # 65C02 and 65816
|
||||
# Z and N use adjusted (i.e. decimal) result
|
||||
zerores = adjresult
|
||||
negativeres = adjresult
|
||||
else: # 6502
|
||||
# Z uses unadjusted (i.e. binary) ALU result
|
||||
zerores = aluresult
|
||||
# N effectively uses ALU result with only low nibble
|
||||
# decimally adjusted - but see here for what is really going on
|
||||
# https://atariage.com/forums/topic/163876-flags-on-decimal-mode-on-the-nmos-6502/
|
||||
negativeres = halfadjresult
|
||||
|
||||
if zerores == 0:
|
||||
self.p |= self.ZERO
|
||||
else:
|
||||
self.p |= aluresult & self.NEGATIVE
|
||||
self.p |= negativeres & self.NEGATIVE
|
||||
|
||||
if decimalcarry == 1:
|
||||
self.p |= self.CARRY
|
||||
|
||||
if (~(self.a ^ data) & (self.a ^ aluresult)) & self.NEGATIVE:
|
||||
self.p |= self.OVERFLOW
|
||||
self.a = (nibble1 << 4) + nibble0
|
||||
|
||||
self.a = adjresult
|
||||
else:
|
||||
if self.p & self.CARRY:
|
||||
tmp = 1
|
||||
|
@ -411,56 +443,78 @@ class MPU:
|
|||
self.p |= (register_value - tbyte) & self.NEGATIVE
|
||||
|
||||
def opSBC(self, x):
|
||||
self._opSBC(x, decimal_flags_use_adjusted_result=False)
|
||||
|
||||
def _opSBC(self, x, decimal_flags_use_adjusted_result):
|
||||
if self.p & self.DECIMAL:
|
||||
self._opSBCDecimal(x, decimal_flags_use_adjusted_result)
|
||||
return
|
||||
|
||||
data = self.ByteAt(x())
|
||||
|
||||
if self.p & self.DECIMAL:
|
||||
halfcarry = 1
|
||||
decimalcarry = 0
|
||||
adjust0 = 0
|
||||
adjust1 = 0
|
||||
result = self.a + (~data & self.byteMask) + (self.p & self.CARRY)
|
||||
self.p &= ~(self.CARRY | self.ZERO | self.OVERFLOW | self.NEGATIVE)
|
||||
if ((self.a ^ data) & (self.a ^ result)) & self.NEGATIVE:
|
||||
self.p |= self.OVERFLOW
|
||||
data = result & self.byteMask
|
||||
if data == 0:
|
||||
self.p |= self.ZERO
|
||||
if result > self.byteMask:
|
||||
self.p |= self.CARRY
|
||||
self.p |= data & self.NEGATIVE
|
||||
self.a = data
|
||||
|
||||
nibble0 = (self.a & 0xf) + (~data & 0xf) + (self.p & self.CARRY)
|
||||
if nibble0 <= 0xf:
|
||||
halfcarry = 0
|
||||
adjust0 = 10
|
||||
nibble1 = ((self.a >> 4) & 0xf) + ((~data >> 4) & 0xf) + halfcarry
|
||||
if nibble1 <= 0xf:
|
||||
adjust1 = 10 << 4
|
||||
def _opSBCDecimal(self, x, flags_use_adjusted_result=False):
|
||||
"""SBC opcode in BCD mode.
|
||||
|
||||
# the ALU outputs are not decimally adjusted
|
||||
aluresult = self.a + (~data & self.byteMask) + \
|
||||
(self.p & self.CARRY)
|
||||
See e.g. http://6502.org/tutorials/decimal_mode.html#A for details
|
||||
"""
|
||||
data = self.ByteAt(x())
|
||||
|
||||
if aluresult > self.byteMask:
|
||||
decimalcarry = 1
|
||||
aluresult &= self.byteMask
|
||||
# the ALU outputs are not yet decimally adjusted
|
||||
aluresult = self.a + (~data & self.byteMask) + (self.p & self.CARRY)
|
||||
|
||||
# but the final result will be adjusted
|
||||
nibble0 = (aluresult + adjust0) & 0xf
|
||||
nibble1 = ((aluresult + adjust1) >> 4) & 0xf
|
||||
decimalcarry = aluresult > self.byteMask
|
||||
aluresult &= self.byteMask
|
||||
|
||||
self.p &= ~(self.CARRY | self.ZERO | self.NEGATIVE | self.OVERFLOW)
|
||||
if aluresult == 0:
|
||||
self.p |= self.ZERO
|
||||
else:
|
||||
self.p |= aluresult & self.NEGATIVE
|
||||
if decimalcarry == 1:
|
||||
self.p |= self.CARRY
|
||||
if ((self.a ^ data) & (self.a ^ aluresult)) & self.NEGATIVE:
|
||||
self.p |= self.OVERFLOW
|
||||
self.a = (nibble1 << 4) + nibble0
|
||||
al = (self.a & 0xf) - (data & 0xf) + (self.p & self.CARRY) - 1
|
||||
|
||||
if flags_use_adjusted_result: # 65C02 but not 65816
|
||||
a = self.a - data + (self.p & self.CARRY) - 1
|
||||
if a < 0:
|
||||
a -= 0x60
|
||||
if al < 0:
|
||||
a -= 0x6
|
||||
|
||||
else: # 6502
|
||||
# Note: 65816 apparently also uses this logic instead of 65C02
|
||||
if al < 0:
|
||||
al = ((al - 0x6) & 0xf) - 0x10
|
||||
a = (self.a & 0xf0) - (data & 0xf0) + al
|
||||
if a < 0:
|
||||
a -= 0x60
|
||||
|
||||
adjresult = a & self.byteMask
|
||||
|
||||
if flags_use_adjusted_result: # 65C02 and 65816
|
||||
# Z and N use adjusted (i.e. decimal) result
|
||||
zerores = adjresult
|
||||
negativeres = adjresult
|
||||
else: # 6502
|
||||
# Z and N uses unadjusted (i.e. binary) ALU result
|
||||
zerores = aluresult
|
||||
negativeres = aluresult
|
||||
|
||||
self.p &= ~(self.CARRY | self.ZERO | self.NEGATIVE | self.OVERFLOW)
|
||||
if zerores == 0:
|
||||
self.p |= self.ZERO
|
||||
else:
|
||||
result = self.a + (~data & self.byteMask) + (self.p & self.CARRY)
|
||||
self.p &= ~(self.CARRY | self.ZERO | self.OVERFLOW | self.NEGATIVE)
|
||||
if ((self.a ^ data) & (self.a ^ result)) & self.NEGATIVE:
|
||||
self.p |= self.OVERFLOW
|
||||
data = result & self.byteMask
|
||||
if data == 0:
|
||||
self.p |= self.ZERO
|
||||
if result > self.byteMask:
|
||||
self.p |= self.CARRY
|
||||
self.p |= data & self.NEGATIVE
|
||||
self.a = data
|
||||
self.p |= negativeres & self.NEGATIVE
|
||||
if decimalcarry == 1:
|
||||
self.p |= self.CARRY
|
||||
if ((self.a ^ data) & (self.a ^ aluresult)) & self.NEGATIVE:
|
||||
self.p |= self.OVERFLOW
|
||||
self.a = adjresult
|
||||
|
||||
def opDECR(self, x):
|
||||
if x is None:
|
||||
|
|
|
@ -8,11 +8,11 @@ class MPU(mpu6502.MPU):
|
|||
self.name = '65C02'
|
||||
self.waiting = False
|
||||
|
||||
def step(self):
|
||||
def step(self, trace=False):
|
||||
if self.waiting:
|
||||
self.processorCycles += 1
|
||||
else:
|
||||
mpu6502.MPU.step(self)
|
||||
mpu6502.MPU.step(self, trace)
|
||||
return self
|
||||
|
||||
# Make copies of the lists
|
||||
|
@ -63,6 +63,12 @@ class MPU(mpu6502.MPU):
|
|||
self.p |= self.ZERO
|
||||
self.memory[address] = m & ~self.a
|
||||
|
||||
def opADC(self, x):
|
||||
return self._opADC(x, decimal_flags_use_adjusted_result=True)
|
||||
|
||||
def opSBC(self, x):
|
||||
return self._opSBC(x, decimal_flags_use_adjusted_result=True)
|
||||
|
||||
# instructions
|
||||
|
||||
@instruction(name="BRK", mode="imp", cycles=7)
|
||||
|
|
BIN
py65/tests/devices/bcd/6502_decimal_test.bin
Normal file
BIN
py65/tests/devices/bcd/6502_decimal_test.bin
Normal file
Binary file not shown.
354
py65/tests/devices/bcd/6502_decimal_test.c65
Normal file
354
py65/tests/devices/bcd/6502_decimal_test.c65
Normal file
|
@ -0,0 +1,354 @@
|
|||
; 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:
|
||||
cputype = 0 ; 0 = 6502, 1 = 65C02, 2 = 65C816
|
||||
vld_bcd = 0 ; 0 = allow invalid bcd, 1 = valid bcd only
|
||||
chk_a = 1 ; check accumulator
|
||||
chk_n = 1 ; check sign (negative) flag
|
||||
chk_v = 1 ; check overflow flag
|
||||
chk_z = 1 ; check zero flag
|
||||
chk_c = 1 ; check carry flag
|
||||
|
||||
.macro end_of_test
|
||||
BRK
|
||||
; .byte $db ;execute 65C02 stop instruction
|
||||
.endmacro
|
||||
|
||||
.ZEROPAGE
|
||||
.org 0
|
||||
; operands - register Y = carry in
|
||||
N1: .res 1,0
|
||||
N2: .res 1,0
|
||||
; binary result
|
||||
HA: .res 1,0
|
||||
HNVZC: .res 1,0
|
||||
;04
|
||||
; decimal result
|
||||
DA: .res 1,0
|
||||
DNVZC: .res 1,0
|
||||
; predicted results
|
||||
AR: .res 1,0
|
||||
NF: .res 1,0
|
||||
;08
|
||||
VF: .res 1,0
|
||||
ZF: .res 1,0
|
||||
CF: .res 1,0
|
||||
ERROR: .res 1,0
|
||||
;0C
|
||||
; workspace
|
||||
N1L: .res 1,0
|
||||
N1H: .res 1,0
|
||||
N2L: .res 1,0
|
||||
N2H: .res 2,0
|
||||
|
||||
.CODE
|
||||
.org $200
|
||||
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
|
||||
.if vld_bcd = 1
|
||||
cmp #$0a
|
||||
bcs NEXT2
|
||||
.endif
|
||||
sta N2L
|
||||
lda N2 ; N2H = N2 & $F0
|
||||
and #$F0 ; [2] see text
|
||||
.if vld_bcd = 1
|
||||
cmp #$a0
|
||||
bcs NEXT2
|
||||
.endif
|
||||
sta N2H
|
||||
ora #$0F ; N2H+1 = (N2 & $F0) + $0F
|
||||
sta N2H+1
|
||||
LOOP2: lda N1 ; N1L = N1 & $0F
|
||||
and #$0F ; [3] see text
|
||||
.if vld_bcd = 1
|
||||
cmp #$0a
|
||||
bcs NEXT1
|
||||
.endif
|
||||
sta N1L
|
||||
lda N1 ; N1H = N1 & $F0
|
||||
and #$F0 ; [4] see text
|
||||
.if vld_bcd = 1
|
||||
cmp #$a0
|
||||
bcs NEXT1
|
||||
.endif
|
||||
sta N1H
|
||||
jsr ADD
|
||||
jsr A6502
|
||||
jsr COMPARE
|
||||
bne DONE
|
||||
jsr SUB
|
||||
jsr S6502
|
||||
jsr COMPARE
|
||||
bne DONE
|
||||
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
|
||||
DONE:
|
||||
end_of_test
|
||||
|
||||
; 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
|
||||
|
||||
.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
|
||||
;
|
||||
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
|
||||
;
|
||||
COMPARE:
|
||||
.if chk_a = 1
|
||||
lda DA
|
||||
cmp AR
|
||||
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
|
||||
lda DNVZC
|
||||
eor CF
|
||||
and #1 ; mask off C flag
|
||||
.endif
|
||||
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
|
||||
|
||||
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
|
359
py65/tests/devices/bcd/6502_decimal_test.lst
Normal file
359
py65/tests/devices/bcd/6502_decimal_test.lst
Normal file
|
@ -0,0 +1,359 @@
|
|||
ca65 V2.17 - Git N/A
|
||||
Main file : 6502_decimal_test.c65
|
||||
Current file: 6502_decimal_test.c65
|
||||
|
||||
000000r 1 ; Verify decimal mode behavior
|
||||
000000r 1 ; Written by Bruce Clark. This code is public domain.
|
||||
000000r 1 ; see http://www.6502.org/tutorials/decimal_mode.html
|
||||
000000r 1 ;
|
||||
000000r 1 ; Returns:
|
||||
000000r 1 ; ERROR = 0 if the test passed
|
||||
000000r 1 ; ERROR = 1 if the test failed
|
||||
000000r 1 ; modify the code at the DONE label for desired program end
|
||||
000000r 1 ;
|
||||
000000r 1 ; This routine requires 17 bytes of RAM -- 1 byte each for:
|
||||
000000r 1 ; AR, CF, DA, DNVZC, ERROR, HA, HNVZC, N1, N1H, N1L, N2, N2L, NF, VF, and ZF
|
||||
000000r 1 ; and 2 bytes for N2H
|
||||
000000r 1 ;
|
||||
000000r 1 ; Variables:
|
||||
000000r 1 ; N1 and N2 are the two numbers to be added or subtracted
|
||||
000000r 1 ; N1H, N1L, N2H, and N2L are the upper 4 bits and lower 4 bits of N1 and N2
|
||||
000000r 1 ; DA and DNVZC are the actual accumulator and flag results in decimal mode
|
||||
000000r 1 ; HA and HNVZC are the accumulator and flag results when N1 and N2 are
|
||||
000000r 1 ; added or subtracted using binary arithmetic
|
||||
000000r 1 ; AR, NF, VF, ZF, and CF are the predicted decimal mode accumulator and
|
||||
000000r 1 ; flag results, calculated using binary arithmetic
|
||||
000000r 1 ;
|
||||
000000r 1 ; This program takes approximately 1 minute at 1 MHz (a few seconds more on
|
||||
000000r 1 ; a 65C02 than a 6502 or 65816)
|
||||
000000r 1 ;
|
||||
000000r 1
|
||||
000000r 1 ; Configuration:
|
||||
000000r 1 cputype = 0 ; 0 = 6502, 1 = 65C02, 2 = 65C816
|
||||
000000r 1 vld_bcd = 0 ; 0 = allow invalid bcd, 1 = valid bcd only
|
||||
000000r 1 chk_a = 1 ; check accumulator
|
||||
000000r 1 chk_n = 1 ; check sign (negative) flag
|
||||
000000r 1 chk_v = 1 ; check overflow flag
|
||||
000000r 1 chk_z = 1 ; check zero flag
|
||||
000000r 1 chk_c = 1 ; check carry flag
|
||||
000000r 1
|
||||
000000r 1 .macro end_of_test
|
||||
000000r 1 BRK
|
||||
000000r 1 ; .byte $db ;execute 65C02 stop instruction
|
||||
000000r 1 .endmacro
|
||||
000000r 1
|
||||
000000r 1 .ZEROPAGE
|
||||
000000r 1 .org 0
|
||||
000000 1 ; operands - register Y = carry in
|
||||
000000 1 00 N1: .res 1,0
|
||||
000001 1 00 N2: .res 1,0
|
||||
000002 1 ; binary result
|
||||
000002 1 00 HA: .res 1,0
|
||||
000003 1 00 HNVZC: .res 1,0
|
||||
000004 1 ;04
|
||||
000004 1 ; decimal result
|
||||
000004 1 00 DA: .res 1,0
|
||||
000005 1 00 DNVZC: .res 1,0
|
||||
000006 1 ; predicted results
|
||||
000006 1 00 AR: .res 1,0
|
||||
000007 1 00 NF: .res 1,0
|
||||
000008 1 ;08
|
||||
000008 1 00 VF: .res 1,0
|
||||
000009 1 00 ZF: .res 1,0
|
||||
00000A 1 00 CF: .res 1,0
|
||||
00000B 1 00 ERROR: .res 1,0
|
||||
00000C 1 ;0C
|
||||
00000C 1 ; workspace
|
||||
00000C 1 00 N1L: .res 1,0
|
||||
00000D 1 00 N1H: .res 1,0
|
||||
00000E 1 00 N2L: .res 1,0
|
||||
00000F 1 00 00 N2H: .res 2,0
|
||||
000011 1
|
||||
000011 1 .CODE
|
||||
000011 1 .org $200
|
||||
000200 1 A0 01 TEST: ldy #1 ; initialize Y (used to loop through carry flag values)
|
||||
000202 1 84 0B sty ERROR ; store 1 in ERROR until the test passes
|
||||
000204 1 A9 00 lda #0 ; initialize N1 and N2
|
||||
000206 1 85 00 sta N1
|
||||
000208 1 85 01 sta N2
|
||||
00020A 1 A5 01 LOOP1: lda N2 ; N2L = N2 & $0F
|
||||
00020C 1 29 0F and #$0F ; [1] see text
|
||||
00020E 1 .if vld_bcd = 1
|
||||
00020E 1 cmp #$0a
|
||||
00020E 1 bcs NEXT2
|
||||
00020E 1 .endif
|
||||
00020E 1 85 0E sta N2L
|
||||
000210 1 A5 01 lda N2 ; N2H = N2 & $F0
|
||||
000212 1 29 F0 and #$F0 ; [2] see text
|
||||
000214 1 .if vld_bcd = 1
|
||||
000214 1 cmp #$a0
|
||||
000214 1 bcs NEXT2
|
||||
000214 1 .endif
|
||||
000214 1 85 0F sta N2H
|
||||
000216 1 09 0F ora #$0F ; N2H+1 = (N2 & $F0) + $0F
|
||||
000218 1 85 10 sta N2H+1
|
||||
00021A 1 A5 00 LOOP2: lda N1 ; N1L = N1 & $0F
|
||||
00021C 1 29 0F and #$0F ; [3] see text
|
||||
00021E 1 .if vld_bcd = 1
|
||||
00021E 1 cmp #$0a
|
||||
00021E 1 bcs NEXT1
|
||||
00021E 1 .endif
|
||||
00021E 1 85 0C sta N1L
|
||||
000220 1 A5 00 lda N1 ; N1H = N1 & $F0
|
||||
000222 1 29 F0 and #$F0 ; [4] see text
|
||||
000224 1 .if vld_bcd = 1
|
||||
000224 1 cmp #$a0
|
||||
000224 1 bcs NEXT1
|
||||
000224 1 .endif
|
||||
000224 1 85 0D sta N1H
|
||||
000226 1 20 4C 02 jsr ADD
|
||||
000229 1 20 EB 02 jsr A6502
|
||||
00022C 1 20 C6 02 jsr COMPARE
|
||||
00022F 1 D0 1A bne DONE
|
||||
000231 1 20 90 02 jsr SUB
|
||||
000234 1 20 F4 02 jsr S6502
|
||||
000237 1 20 C6 02 jsr COMPARE
|
||||
00023A 1 D0 0F bne DONE
|
||||
00023C 1 E6 00 NEXT1: inc N1 ; [5] see text
|
||||
00023E 1 D0 DA bne LOOP2 ; loop through all 256 values of N1
|
||||
000240 1 E6 01 NEXT2: inc N2 ; [6] see text
|
||||
000242 1 D0 C6 bne LOOP1 ; loop through all 256 values of N2
|
||||
000244 1 88 dey
|
||||
000245 1 10 C3 bpl LOOP1 ; loop through both values of the carry flag
|
||||
000247 1 A9 00 lda #0 ; test passed, so store 0 in ERROR
|
||||
000249 1 85 0B sta ERROR
|
||||
00024B 1 DONE:
|
||||
00024B 1 00 end_of_test
|
||||
00024C 1
|
||||
00024C 1 ; Calculate the actual decimal mode accumulator and flags, the accumulator
|
||||
00024C 1 ; and flag results when N1 is added to N2 using binary arithmetic, the
|
||||
00024C 1 ; predicted accumulator result, the predicted carry flag, and the predicted
|
||||
00024C 1 ; V flag
|
||||
00024C 1 ;
|
||||
00024C 1 F8 ADD: sed ; decimal mode
|
||||
00024D 1 C0 01 cpy #1 ; set carry if Y = 1, clear carry if Y = 0
|
||||
00024F 1 A5 00 lda N1
|
||||
000251 1 65 01 adc N2
|
||||
000253 1 85 04 sta DA ; actual accumulator result in decimal mode
|
||||
000255 1 08 php
|
||||
000256 1 68 pla
|
||||
000257 1 85 05 sta DNVZC ; actual flags result in decimal mode
|
||||
000259 1 D8 cld ; binary mode
|
||||
00025A 1 C0 01 cpy #1 ; set carry if Y = 1, clear carry if Y = 0
|
||||
00025C 1 A5 00 lda N1
|
||||
00025E 1 65 01 adc N2
|
||||
000260 1 85 02 sta HA ; accumulator result of N1+N2 using binary arithmetic
|
||||
000262 1
|
||||
000262 1 08 php
|
||||
000263 1 68 pla
|
||||
000264 1 85 03 sta HNVZC ; flags result of N1+N2 using binary arithmetic
|
||||
000266 1 C0 01 cpy #1
|
||||
000268 1 A5 0C lda N1L
|
||||
00026A 1 65 0E adc N2L
|
||||
00026C 1 C9 0A cmp #$0A
|
||||
00026E 1 A2 00 ldx #0
|
||||
000270 1 90 06 bcc A1
|
||||
000272 1 E8 inx
|
||||
000273 1 69 05 adc #5 ; add 6 (carry is set)
|
||||
000275 1 29 0F and #$0F
|
||||
000277 1 38 sec
|
||||
000278 1 05 0D A1: ora N1H
|
||||
00027A 1 ;
|
||||
00027A 1 ; if N1L + N2L < $0A, then add N2 & $F0
|
||||
00027A 1 ; if N1L + N2L >= $0A, then add (N2 & $F0) + $0F + 1 (carry is set)
|
||||
00027A 1 ;
|
||||
00027A 1 75 0F adc N2H,x
|
||||
00027C 1 08 php
|
||||
00027D 1 B0 04 bcs A2
|
||||
00027F 1 C9 A0 cmp #$A0
|
||||
000281 1 90 03 bcc A3
|
||||
000283 1 69 5F A2: adc #$5F ; add $60 (carry is set)
|
||||
000285 1 38 sec
|
||||
000286 1 85 06 A3: sta AR ; predicted accumulator result
|
||||
000288 1 08 php
|
||||
000289 1 68 pla
|
||||
00028A 1 85 0A sta CF ; predicted carry result
|
||||
00028C 1 68 pla
|
||||
00028D 1 ;
|
||||
00028D 1 ; note that all 8 bits of the P register are stored in VF
|
||||
00028D 1 ;
|
||||
00028D 1 85 08 sta VF ; predicted V flags
|
||||
00028F 1 60 rts
|
||||
000290 1
|
||||
000290 1 ; Calculate the actual decimal mode accumulator and flags, and the
|
||||
000290 1 ; accumulator and flag results when N2 is subtracted from N1 using binary
|
||||
000290 1 ; arithmetic
|
||||
000290 1 ;
|
||||
000290 1 F8 SUB: sed ; decimal mode
|
||||
000291 1 C0 01 cpy #1 ; set carry if Y = 1, clear carry if Y = 0
|
||||
000293 1 A5 00 lda N1
|
||||
000295 1 E5 01 sbc N2
|
||||
000297 1 85 04 sta DA ; actual accumulator result in decimal mode
|
||||
000299 1 08 php
|
||||
00029A 1 68 pla
|
||||
00029B 1 85 05 sta DNVZC ; actual flags result in decimal mode
|
||||
00029D 1 D8 cld ; binary mode
|
||||
00029E 1 C0 01 cpy #1 ; set carry if Y = 1, clear carry if Y = 0
|
||||
0002A0 1 A5 00 lda N1
|
||||
0002A2 1 E5 01 sbc N2
|
||||
0002A4 1 85 02 sta HA ; accumulator result of N1-N2 using binary arithmetic
|
||||
0002A6 1
|
||||
0002A6 1 08 php
|
||||
0002A7 1 68 pla
|
||||
0002A8 1 85 03 sta HNVZC ; flags result of N1-N2 using binary arithmetic
|
||||
0002AA 1 60 rts
|
||||
0002AB 1
|
||||
0002AB 1 .if cputype <> 1
|
||||
0002AB 1 ; Calculate the predicted SBC accumulator result for the 6502 and 65816
|
||||
0002AB 1 ;
|
||||
0002AB 1 C0 01 SUB1: cpy #1 ; set carry if Y = 1, clear carry if Y = 0
|
||||
0002AD 1 A5 0C lda N1L
|
||||
0002AF 1 E5 0E sbc N2L
|
||||
0002B1 1 A2 00 ldx #0
|
||||
0002B3 1 B0 06 bcs S11
|
||||
0002B5 1 E8 inx
|
||||
0002B6 1 E9 05 sbc #5 ; subtract 6 (carry is clear)
|
||||
0002B8 1 29 0F and #$0F
|
||||
0002BA 1 18 clc
|
||||
0002BB 1 05 0D S11: ora N1H
|
||||
0002BD 1 ;
|
||||
0002BD 1 ; if N1L - N2L >= 0, then subtract N2 & $F0
|
||||
0002BD 1 ; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear)
|
||||
0002BD 1 ;
|
||||
0002BD 1 F5 0F sbc N2H,x
|
||||
0002BF 1 B0 02 bcs S12
|
||||
0002C1 1 E9 5F sbc #$5F ; subtract $60 (carry is clear)
|
||||
0002C3 1 85 06 S12: sta AR
|
||||
0002C5 1 60 rts
|
||||
0002C6 1 .endif
|
||||
0002C6 1
|
||||
0002C6 1 .if cputype = 1
|
||||
0002C6 1 ; Calculate the predicted SBC accumulator result for the 6502 and 65C02
|
||||
0002C6 1 ;
|
||||
0002C6 1 SUB2: cpy #1 ; set carry if Y = 1, clear carry if Y = 0
|
||||
0002C6 1 lda N1L
|
||||
0002C6 1 sbc N2L
|
||||
0002C6 1 ldx #0
|
||||
0002C6 1 bcs S21
|
||||
0002C6 1 inx
|
||||
0002C6 1 and #$0F
|
||||
0002C6 1 clc
|
||||
0002C6 1 S21: ora N1H
|
||||
0002C6 1 ;
|
||||
0002C6 1 ; if N1L - N2L >= 0, then subtract N2 & $F0
|
||||
0002C6 1 ; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear)
|
||||
0002C6 1 ;
|
||||
0002C6 1 sbc N2H,x
|
||||
0002C6 1 bcs S22
|
||||
0002C6 1 sbc #$5F ; subtract $60 (carry is clear)
|
||||
0002C6 1 S22: cpx #0
|
||||
0002C6 1 beq S23
|
||||
0002C6 1 sbc #6
|
||||
0002C6 1 S23: sta AR ; predicted accumulator result
|
||||
0002C6 1 rts
|
||||
0002C6 1 .endif
|
||||
0002C6 1
|
||||
0002C6 1 ; Compare accumulator actual results to predicted results
|
||||
0002C6 1 ;
|
||||
0002C6 1 ; Return:
|
||||
0002C6 1 ; Z flag = 1 (BEQ branch) if same
|
||||
0002C6 1 ; Z flag = 0 (BNE branch) if different
|
||||
0002C6 1 ;
|
||||
0002C6 1 COMPARE:
|
||||
0002C6 1 .if chk_a = 1
|
||||
0002C6 1 A5 04 lda DA
|
||||
0002C8 1 C5 06 cmp AR
|
||||
0002CA 1 D0 1E bne C1
|
||||
0002CC 1 .endif
|
||||
0002CC 1 .if chk_n = 1
|
||||
0002CC 1 A5 05 lda DNVZC ; [7] see text
|
||||
0002CE 1 45 07 eor NF
|
||||
0002D0 1 29 80 and #$80 ; mask off N flag
|
||||
0002D2 1 D0 16 bne C1
|
||||
0002D4 1 .endif
|
||||
0002D4 1 .if chk_v = 1
|
||||
0002D4 1 A5 05 lda DNVZC ; [8] see text
|
||||
0002D6 1 45 08 eor VF
|
||||
0002D8 1 29 40 and #$40 ; mask off V flag
|
||||
0002DA 1 D0 0E bne C1 ; [9] see text
|
||||
0002DC 1 .endif
|
||||
0002DC 1 .if chk_z = 1
|
||||
0002DC 1 A5 05 lda DNVZC
|
||||
0002DE 1 45 09 eor ZF ; mask off Z flag
|
||||
0002E0 1 29 02 and #2
|
||||
0002E2 1 D0 06 bne C1 ; [10] see text
|
||||
0002E4 1 .endif
|
||||
0002E4 1 .if chk_c = 1
|
||||
0002E4 1 A5 05 lda DNVZC
|
||||
0002E6 1 45 0A eor CF
|
||||
0002E8 1 29 01 and #1 ; mask off C flag
|
||||
0002EA 1 .endif
|
||||
0002EA 1 60 C1: rts
|
||||
0002EB 1
|
||||
0002EB 1 ; These routines store the predicted values for ADC and SBC for the 6502,
|
||||
0002EB 1 ; 65C02, and 65816 in AR, CF, NF, VF, and ZF
|
||||
0002EB 1
|
||||
0002EB 1 .if cputype = 0
|
||||
0002EB 1
|
||||
0002EB 1 A5 08 A6502: lda VF ; 6502
|
||||
0002ED 1 ;
|
||||
0002ED 1 ; since all 8 bits of the P register were stored in VF, bit 7 of VF contains
|
||||
0002ED 1 ; the N flag for NF
|
||||
0002ED 1 ;
|
||||
0002ED 1 85 07 sta NF
|
||||
0002EF 1 A5 03 lda HNVZC
|
||||
0002F1 1 85 09 sta ZF
|
||||
0002F3 1 60 rts
|
||||
0002F4 1
|
||||
0002F4 1 20 AB 02 S6502: jsr SUB1
|
||||
0002F7 1 A5 03 lda HNVZC
|
||||
0002F9 1 85 07 sta NF
|
||||
0002FB 1 85 08 sta VF
|
||||
0002FD 1 85 09 sta ZF
|
||||
0002FF 1 85 0A sta CF
|
||||
000301 1 60 rts
|
||||
000302 1
|
||||
000302 1 .endif
|
||||
000302 1 .if cputype = 1
|
||||
000302 1
|
||||
000302 1 A6502: lda AR ; 65C02
|
||||
000302 1 php
|
||||
000302 1 pla
|
||||
000302 1 sta NF
|
||||
000302 1 sta ZF
|
||||
000302 1 rts
|
||||
000302 1
|
||||
000302 1 S6502: jsr SUB2
|
||||
000302 1 lda AR
|
||||
000302 1 php
|
||||
000302 1 pla
|
||||
000302 1 sta NF
|
||||
000302 1 sta ZF
|
||||
000302 1 lda HNVZC
|
||||
000302 1 sta VF
|
||||
000302 1 sta CF
|
||||
000302 1 rts
|
||||
000302 1
|
||||
000302 1 .endif
|
||||
000302 1 .if cputype = 2
|
||||
000302 1
|
||||
000302 1 A6502: lda AR ; 65C816
|
||||
000302 1 php
|
||||
000302 1 pla
|
||||
000302 1 sta NF
|
||||
000302 1 sta ZF
|
||||
000302 1 rts
|
||||
000302 1
|
||||
000302 1 S6502: jsr SUB1
|
||||
000302 1 lda AR
|
||||
000302 1 php
|
||||
000302 1 pla
|
||||
000302 1 sta NF
|
||||
000302 1 sta ZF
|
||||
000302 1 lda HNVZC
|
||||
000302 1 sta VF
|
||||
000302 1 sta CF
|
||||
000302 1 rts
|
||||
000302 1
|
||||
000302 1 .endif
|
||||
000302 1
|
BIN
py65/tests/devices/bcd/65C02_decimal_test.bin
Normal file
BIN
py65/tests/devices/bcd/65C02_decimal_test.bin
Normal file
Binary file not shown.
354
py65/tests/devices/bcd/65C02_decimal_test.c65
Normal file
354
py65/tests/devices/bcd/65C02_decimal_test.c65
Normal file
|
@ -0,0 +1,354 @@
|
|||
; 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:
|
||||
cputype = 1 ; 0 = 6502, 1 = 65C02, 2 = 65C816
|
||||
vld_bcd = 0 ; 0 = allow invalid bcd, 1 = valid bcd only
|
||||
chk_a = 1 ; check accumulator
|
||||
chk_n = 1 ; check sign (negative) flag
|
||||
chk_v = 1 ; check overflow flag
|
||||
chk_z = 1 ; check zero flag
|
||||
chk_c = 1 ; check carry flag
|
||||
|
||||
.macro end_of_test
|
||||
BRK
|
||||
; .byte $db ;execute 65C02 stop instruction
|
||||
.endmacro
|
||||
|
||||
.ZEROPAGE
|
||||
.org 0
|
||||
; operands - register Y = carry in
|
||||
N1: .res 1,0
|
||||
N2: .res 1,0
|
||||
; binary result
|
||||
HA: .res 1,0
|
||||
HNVZC: .res 1,0
|
||||
;04
|
||||
; decimal result
|
||||
DA: .res 1,0
|
||||
DNVZC: .res 1,0
|
||||
; predicted results
|
||||
AR: .res 1,0
|
||||
NF: .res 1,0
|
||||
;08
|
||||
VF: .res 1,0
|
||||
ZF: .res 1,0
|
||||
CF: .res 1,0
|
||||
ERROR: .res 1,0
|
||||
;0C
|
||||
; workspace
|
||||
N1L: .res 1,0
|
||||
N1H: .res 1,0
|
||||
N2L: .res 1,0
|
||||
N2H: .res 2,0
|
||||
|
||||
.CODE
|
||||
.org $200
|
||||
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
|
||||
.if vld_bcd = 1
|
||||
cmp #$0a
|
||||
bcs NEXT2
|
||||
.endif
|
||||
sta N2L
|
||||
lda N2 ; N2H = N2 & $F0
|
||||
and #$F0 ; [2] see text
|
||||
.if vld_bcd = 1
|
||||
cmp #$a0
|
||||
bcs NEXT2
|
||||
.endif
|
||||
sta N2H
|
||||
ora #$0F ; N2H+1 = (N2 & $F0) + $0F
|
||||
sta N2H+1
|
||||
LOOP2: lda N1 ; N1L = N1 & $0F
|
||||
and #$0F ; [3] see text
|
||||
.if vld_bcd = 1
|
||||
cmp #$0a
|
||||
bcs NEXT1
|
||||
.endif
|
||||
sta N1L
|
||||
lda N1 ; N1H = N1 & $F0
|
||||
and #$F0 ; [4] see text
|
||||
.if vld_bcd = 1
|
||||
cmp #$a0
|
||||
bcs NEXT1
|
||||
.endif
|
||||
sta N1H
|
||||
jsr ADD
|
||||
jsr A6502
|
||||
jsr COMPARE
|
||||
bne DONE
|
||||
jsr SUB
|
||||
jsr S6502
|
||||
jsr COMPARE
|
||||
bne DONE
|
||||
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
|
||||
DONE:
|
||||
end_of_test
|
||||
|
||||
; 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
|
||||
|
||||
.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
|
||||
;
|
||||
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
|
||||
;
|
||||
COMPARE:
|
||||
.if chk_a = 1
|
||||
lda DA
|
||||
cmp AR
|
||||
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
|
||||
lda DNVZC
|
||||
eor CF
|
||||
and #1 ; mask off C flag
|
||||
.endif
|
||||
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
|
||||
|
||||
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
|
359
py65/tests/devices/bcd/65C02_decimal_test.lst
Normal file
359
py65/tests/devices/bcd/65C02_decimal_test.lst
Normal file
|
@ -0,0 +1,359 @@
|
|||
ca65 V2.17 - Git N/A
|
||||
Main file : 65C02_decimal_test.c65
|
||||
Current file: 65C02_decimal_test.c65
|
||||
|
||||
000000r 1 ; Verify decimal mode behavior
|
||||
000000r 1 ; Written by Bruce Clark. This code is public domain.
|
||||
000000r 1 ; see http://www.6502.org/tutorials/decimal_mode.html
|
||||
000000r 1 ;
|
||||
000000r 1 ; Returns:
|
||||
000000r 1 ; ERROR = 0 if the test passed
|
||||
000000r 1 ; ERROR = 1 if the test failed
|
||||
000000r 1 ; modify the code at the DONE label for desired program end
|
||||
000000r 1 ;
|
||||
000000r 1 ; This routine requires 17 bytes of RAM -- 1 byte each for:
|
||||
000000r 1 ; AR, CF, DA, DNVZC, ERROR, HA, HNVZC, N1, N1H, N1L, N2, N2L, NF, VF, and ZF
|
||||
000000r 1 ; and 2 bytes for N2H
|
||||
000000r 1 ;
|
||||
000000r 1 ; Variables:
|
||||
000000r 1 ; N1 and N2 are the two numbers to be added or subtracted
|
||||
000000r 1 ; N1H, N1L, N2H, and N2L are the upper 4 bits and lower 4 bits of N1 and N2
|
||||
000000r 1 ; DA and DNVZC are the actual accumulator and flag results in decimal mode
|
||||
000000r 1 ; HA and HNVZC are the accumulator and flag results when N1 and N2 are
|
||||
000000r 1 ; added or subtracted using binary arithmetic
|
||||
000000r 1 ; AR, NF, VF, ZF, and CF are the predicted decimal mode accumulator and
|
||||
000000r 1 ; flag results, calculated using binary arithmetic
|
||||
000000r 1 ;
|
||||
000000r 1 ; This program takes approximately 1 minute at 1 MHz (a few seconds more on
|
||||
000000r 1 ; a 65C02 than a 6502 or 65816)
|
||||
000000r 1 ;
|
||||
000000r 1
|
||||
000000r 1 ; Configuration:
|
||||
000000r 1 cputype = 1 ; 0 = 6502, 1 = 65C02, 2 = 65C816
|
||||
000000r 1 vld_bcd = 0 ; 0 = allow invalid bcd, 1 = valid bcd only
|
||||
000000r 1 chk_a = 1 ; check accumulator
|
||||
000000r 1 chk_n = 1 ; check sign (negative) flag
|
||||
000000r 1 chk_v = 1 ; check overflow flag
|
||||
000000r 1 chk_z = 1 ; check zero flag
|
||||
000000r 1 chk_c = 1 ; check carry flag
|
||||
000000r 1
|
||||
000000r 1 .macro end_of_test
|
||||
000000r 1 BRK
|
||||
000000r 1 ; .byte $db ;execute 65C02 stop instruction
|
||||
000000r 1 .endmacro
|
||||
000000r 1
|
||||
000000r 1 .ZEROPAGE
|
||||
000000r 1 .org 0
|
||||
000000 1 ; operands - register Y = carry in
|
||||
000000 1 00 N1: .res 1,0
|
||||
000001 1 00 N2: .res 1,0
|
||||
000002 1 ; binary result
|
||||
000002 1 00 HA: .res 1,0
|
||||
000003 1 00 HNVZC: .res 1,0
|
||||
000004 1 ;04
|
||||
000004 1 ; decimal result
|
||||
000004 1 00 DA: .res 1,0
|
||||
000005 1 00 DNVZC: .res 1,0
|
||||
000006 1 ; predicted results
|
||||
000006 1 00 AR: .res 1,0
|
||||
000007 1 00 NF: .res 1,0
|
||||
000008 1 ;08
|
||||
000008 1 00 VF: .res 1,0
|
||||
000009 1 00 ZF: .res 1,0
|
||||
00000A 1 00 CF: .res 1,0
|
||||
00000B 1 00 ERROR: .res 1,0
|
||||
00000C 1 ;0C
|
||||
00000C 1 ; workspace
|
||||
00000C 1 00 N1L: .res 1,0
|
||||
00000D 1 00 N1H: .res 1,0
|
||||
00000E 1 00 N2L: .res 1,0
|
||||
00000F 1 00 00 N2H: .res 2,0
|
||||
000011 1
|
||||
000011 1 .CODE
|
||||
000011 1 .org $200
|
||||
000200 1 A0 01 TEST: ldy #1 ; initialize Y (used to loop through carry flag values)
|
||||
000202 1 84 0B sty ERROR ; store 1 in ERROR until the test passes
|
||||
000204 1 A9 00 lda #0 ; initialize N1 and N2
|
||||
000206 1 85 00 sta N1
|
||||
000208 1 85 01 sta N2
|
||||
00020A 1 A5 01 LOOP1: lda N2 ; N2L = N2 & $0F
|
||||
00020C 1 29 0F and #$0F ; [1] see text
|
||||
00020E 1 .if vld_bcd = 1
|
||||
00020E 1 cmp #$0a
|
||||
00020E 1 bcs NEXT2
|
||||
00020E 1 .endif
|
||||
00020E 1 85 0E sta N2L
|
||||
000210 1 A5 01 lda N2 ; N2H = N2 & $F0
|
||||
000212 1 29 F0 and #$F0 ; [2] see text
|
||||
000214 1 .if vld_bcd = 1
|
||||
000214 1 cmp #$a0
|
||||
000214 1 bcs NEXT2
|
||||
000214 1 .endif
|
||||
000214 1 85 0F sta N2H
|
||||
000216 1 09 0F ora #$0F ; N2H+1 = (N2 & $F0) + $0F
|
||||
000218 1 85 10 sta N2H+1
|
||||
00021A 1 A5 00 LOOP2: lda N1 ; N1L = N1 & $0F
|
||||
00021C 1 29 0F and #$0F ; [3] see text
|
||||
00021E 1 .if vld_bcd = 1
|
||||
00021E 1 cmp #$0a
|
||||
00021E 1 bcs NEXT1
|
||||
00021E 1 .endif
|
||||
00021E 1 85 0C sta N1L
|
||||
000220 1 A5 00 lda N1 ; N1H = N1 & $F0
|
||||
000222 1 29 F0 and #$F0 ; [4] see text
|
||||
000224 1 .if vld_bcd = 1
|
||||
000224 1 cmp #$a0
|
||||
000224 1 bcs NEXT1
|
||||
000224 1 .endif
|
||||
000224 1 85 0D sta N1H
|
||||
000226 1 20 4C 02 jsr ADD
|
||||
000229 1 20 EF 02 jsr A6502
|
||||
00022C 1 20 CA 02 jsr COMPARE
|
||||
00022F 1 D0 1A bne DONE
|
||||
000231 1 20 90 02 jsr SUB
|
||||
000234 1 20 F8 02 jsr S6502
|
||||
000237 1 20 CA 02 jsr COMPARE
|
||||
00023A 1 D0 0F bne DONE
|
||||
00023C 1 E6 00 NEXT1: inc N1 ; [5] see text
|
||||
00023E 1 D0 DA bne LOOP2 ; loop through all 256 values of N1
|
||||
000240 1 E6 01 NEXT2: inc N2 ; [6] see text
|
||||
000242 1 D0 C6 bne LOOP1 ; loop through all 256 values of N2
|
||||
000244 1 88 dey
|
||||
000245 1 10 C3 bpl LOOP1 ; loop through both values of the carry flag
|
||||
000247 1 A9 00 lda #0 ; test passed, so store 0 in ERROR
|
||||
000249 1 85 0B sta ERROR
|
||||
00024B 1 DONE:
|
||||
00024B 1 00 end_of_test
|
||||
00024C 1
|
||||
00024C 1 ; Calculate the actual decimal mode accumulator and flags, the accumulator
|
||||
00024C 1 ; and flag results when N1 is added to N2 using binary arithmetic, the
|
||||
00024C 1 ; predicted accumulator result, the predicted carry flag, and the predicted
|
||||
00024C 1 ; V flag
|
||||
00024C 1 ;
|
||||
00024C 1 F8 ADD: sed ; decimal mode
|
||||
00024D 1 C0 01 cpy #1 ; set carry if Y = 1, clear carry if Y = 0
|
||||
00024F 1 A5 00 lda N1
|
||||
000251 1 65 01 adc N2
|
||||
000253 1 85 04 sta DA ; actual accumulator result in decimal mode
|
||||
000255 1 08 php
|
||||
000256 1 68 pla
|
||||
000257 1 85 05 sta DNVZC ; actual flags result in decimal mode
|
||||
000259 1 D8 cld ; binary mode
|
||||
00025A 1 C0 01 cpy #1 ; set carry if Y = 1, clear carry if Y = 0
|
||||
00025C 1 A5 00 lda N1
|
||||
00025E 1 65 01 adc N2
|
||||
000260 1 85 02 sta HA ; accumulator result of N1+N2 using binary arithmetic
|
||||
000262 1
|
||||
000262 1 08 php
|
||||
000263 1 68 pla
|
||||
000264 1 85 03 sta HNVZC ; flags result of N1+N2 using binary arithmetic
|
||||
000266 1 C0 01 cpy #1
|
||||
000268 1 A5 0C lda N1L
|
||||
00026A 1 65 0E adc N2L
|
||||
00026C 1 C9 0A cmp #$0A
|
||||
00026E 1 A2 00 ldx #0
|
||||
000270 1 90 06 bcc A1
|
||||
000272 1 E8 inx
|
||||
000273 1 69 05 adc #5 ; add 6 (carry is set)
|
||||
000275 1 29 0F and #$0F
|
||||
000277 1 38 sec
|
||||
000278 1 05 0D A1: ora N1H
|
||||
00027A 1 ;
|
||||
00027A 1 ; if N1L + N2L < $0A, then add N2 & $F0
|
||||
00027A 1 ; if N1L + N2L >= $0A, then add (N2 & $F0) + $0F + 1 (carry is set)
|
||||
00027A 1 ;
|
||||
00027A 1 75 0F adc N2H,x
|
||||
00027C 1 08 php
|
||||
00027D 1 B0 04 bcs A2
|
||||
00027F 1 C9 A0 cmp #$A0
|
||||
000281 1 90 03 bcc A3
|
||||
000283 1 69 5F A2: adc #$5F ; add $60 (carry is set)
|
||||
000285 1 38 sec
|
||||
000286 1 85 06 A3: sta AR ; predicted accumulator result
|
||||
000288 1 08 php
|
||||
000289 1 68 pla
|
||||
00028A 1 85 0A sta CF ; predicted carry result
|
||||
00028C 1 68 pla
|
||||
00028D 1 ;
|
||||
00028D 1 ; note that all 8 bits of the P register are stored in VF
|
||||
00028D 1 ;
|
||||
00028D 1 85 08 sta VF ; predicted V flags
|
||||
00028F 1 60 rts
|
||||
000290 1
|
||||
000290 1 ; Calculate the actual decimal mode accumulator and flags, and the
|
||||
000290 1 ; accumulator and flag results when N2 is subtracted from N1 using binary
|
||||
000290 1 ; arithmetic
|
||||
000290 1 ;
|
||||
000290 1 F8 SUB: sed ; decimal mode
|
||||
000291 1 C0 01 cpy #1 ; set carry if Y = 1, clear carry if Y = 0
|
||||
000293 1 A5 00 lda N1
|
||||
000295 1 E5 01 sbc N2
|
||||
000297 1 85 04 sta DA ; actual accumulator result in decimal mode
|
||||
000299 1 08 php
|
||||
00029A 1 68 pla
|
||||
00029B 1 85 05 sta DNVZC ; actual flags result in decimal mode
|
||||
00029D 1 D8 cld ; binary mode
|
||||
00029E 1 C0 01 cpy #1 ; set carry if Y = 1, clear carry if Y = 0
|
||||
0002A0 1 A5 00 lda N1
|
||||
0002A2 1 E5 01 sbc N2
|
||||
0002A4 1 85 02 sta HA ; accumulator result of N1-N2 using binary arithmetic
|
||||
0002A6 1
|
||||
0002A6 1 08 php
|
||||
0002A7 1 68 pla
|
||||
0002A8 1 85 03 sta HNVZC ; flags result of N1-N2 using binary arithmetic
|
||||
0002AA 1 60 rts
|
||||
0002AB 1
|
||||
0002AB 1 .if cputype <> 1
|
||||
0002AB 1 ; Calculate the predicted SBC accumulator result for the 6502 and 65816
|
||||
0002AB 1 ;
|
||||
0002AB 1 SUB1: cpy #1 ; set carry if Y = 1, clear carry if Y = 0
|
||||
0002AB 1 lda N1L
|
||||
0002AB 1 sbc N2L
|
||||
0002AB 1 ldx #0
|
||||
0002AB 1 bcs S11
|
||||
0002AB 1 inx
|
||||
0002AB 1 sbc #5 ; subtract 6 (carry is clear)
|
||||
0002AB 1 and #$0F
|
||||
0002AB 1 clc
|
||||
0002AB 1 S11: ora N1H
|
||||
0002AB 1 ;
|
||||
0002AB 1 ; if N1L - N2L >= 0, then subtract N2 & $F0
|
||||
0002AB 1 ; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear)
|
||||
0002AB 1 ;
|
||||
0002AB 1 sbc N2H,x
|
||||
0002AB 1 bcs S12
|
||||
0002AB 1 sbc #$5F ; subtract $60 (carry is clear)
|
||||
0002AB 1 S12: sta AR
|
||||
0002AB 1 rts
|
||||
0002AB 1 .endif
|
||||
0002AB 1
|
||||
0002AB 1 .if cputype = 1
|
||||
0002AB 1 ; Calculate the predicted SBC accumulator result for the 6502 and 65C02
|
||||
0002AB 1 ;
|
||||
0002AB 1 C0 01 SUB2: cpy #1 ; set carry if Y = 1, clear carry if Y = 0
|
||||
0002AD 1 A5 0C lda N1L
|
||||
0002AF 1 E5 0E sbc N2L
|
||||
0002B1 1 A2 00 ldx #0
|
||||
0002B3 1 B0 04 bcs S21
|
||||
0002B5 1 E8 inx
|
||||
0002B6 1 29 0F and #$0F
|
||||
0002B8 1 18 clc
|
||||
0002B9 1 05 0D S21: ora N1H
|
||||
0002BB 1 ;
|
||||
0002BB 1 ; if N1L - N2L >= 0, then subtract N2 & $F0
|
||||
0002BB 1 ; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear)
|
||||
0002BB 1 ;
|
||||
0002BB 1 F5 0F sbc N2H,x
|
||||
0002BD 1 B0 02 bcs S22
|
||||
0002BF 1 E9 5F sbc #$5F ; subtract $60 (carry is clear)
|
||||
0002C1 1 E0 00 S22: cpx #0
|
||||
0002C3 1 F0 02 beq S23
|
||||
0002C5 1 E9 06 sbc #6
|
||||
0002C7 1 85 06 S23: sta AR ; predicted accumulator result
|
||||
0002C9 1 60 rts
|
||||
0002CA 1 .endif
|
||||
0002CA 1
|
||||
0002CA 1 ; Compare accumulator actual results to predicted results
|
||||
0002CA 1 ;
|
||||
0002CA 1 ; Return:
|
||||
0002CA 1 ; Z flag = 1 (BEQ branch) if same
|
||||
0002CA 1 ; Z flag = 0 (BNE branch) if different
|
||||
0002CA 1 ;
|
||||
0002CA 1 COMPARE:
|
||||
0002CA 1 .if chk_a = 1
|
||||
0002CA 1 A5 04 lda DA
|
||||
0002CC 1 C5 06 cmp AR
|
||||
0002CE 1 D0 1E bne C1
|
||||
0002D0 1 .endif
|
||||
0002D0 1 .if chk_n = 1
|
||||
0002D0 1 A5 05 lda DNVZC ; [7] see text
|
||||
0002D2 1 45 07 eor NF
|
||||
0002D4 1 29 80 and #$80 ; mask off N flag
|
||||
0002D6 1 D0 16 bne C1
|
||||
0002D8 1 .endif
|
||||
0002D8 1 .if chk_v = 1
|
||||
0002D8 1 A5 05 lda DNVZC ; [8] see text
|
||||
0002DA 1 45 08 eor VF
|
||||
0002DC 1 29 40 and #$40 ; mask off V flag
|
||||
0002DE 1 D0 0E bne C1 ; [9] see text
|
||||
0002E0 1 .endif
|
||||
0002E0 1 .if chk_z = 1
|
||||
0002E0 1 A5 05 lda DNVZC
|
||||
0002E2 1 45 09 eor ZF ; mask off Z flag
|
||||
0002E4 1 29 02 and #2
|
||||
0002E6 1 D0 06 bne C1 ; [10] see text
|
||||
0002E8 1 .endif
|
||||
0002E8 1 .if chk_c = 1
|
||||
0002E8 1 A5 05 lda DNVZC
|
||||
0002EA 1 45 0A eor CF
|
||||
0002EC 1 29 01 and #1 ; mask off C flag
|
||||
0002EE 1 .endif
|
||||
0002EE 1 60 C1: rts
|
||||
0002EF 1
|
||||
0002EF 1 ; These routines store the predicted values for ADC and SBC for the 6502,
|
||||
0002EF 1 ; 65C02, and 65816 in AR, CF, NF, VF, and ZF
|
||||
0002EF 1
|
||||
0002EF 1 .if cputype = 0
|
||||
0002EF 1
|
||||
0002EF 1 A6502: lda VF ; 6502
|
||||
0002EF 1 ;
|
||||
0002EF 1 ; since all 8 bits of the P register were stored in VF, bit 7 of VF contains
|
||||
0002EF 1 ; the N flag for NF
|
||||
0002EF 1 ;
|
||||
0002EF 1 sta NF
|
||||
0002EF 1 lda HNVZC
|
||||
0002EF 1 sta ZF
|
||||
0002EF 1 rts
|
||||
0002EF 1
|
||||
0002EF 1 S6502: jsr SUB1
|
||||
0002EF 1 lda HNVZC
|
||||
0002EF 1 sta NF
|
||||
0002EF 1 sta VF
|
||||
0002EF 1 sta ZF
|
||||
0002EF 1 sta CF
|
||||
0002EF 1 rts
|
||||
0002EF 1
|
||||
0002EF 1 .endif
|
||||
0002EF 1 .if cputype = 1
|
||||
0002EF 1
|
||||
0002EF 1 A5 06 A6502: lda AR ; 65C02
|
||||
0002F1 1 08 php
|
||||
0002F2 1 68 pla
|
||||
0002F3 1 85 07 sta NF
|
||||
0002F5 1 85 09 sta ZF
|
||||
0002F7 1 60 rts
|
||||
0002F8 1
|
||||
0002F8 1 20 AB 02 S6502: jsr SUB2
|
||||
0002FB 1 A5 06 lda AR
|
||||
0002FD 1 08 php
|
||||
0002FE 1 68 pla
|
||||
0002FF 1 85 07 sta NF
|
||||
000301 1 85 09 sta ZF
|
||||
000303 1 A5 03 lda HNVZC
|
||||
000305 1 85 08 sta VF
|
||||
000307 1 85 0A sta CF
|
||||
000309 1 60 rts
|
||||
00030A 1
|
||||
00030A 1 .endif
|
||||
00030A 1 .if cputype = 2
|
||||
00030A 1
|
||||
00030A 1 A6502: lda AR ; 65C816
|
||||
00030A 1 php
|
||||
00030A 1 pla
|
||||
00030A 1 sta NF
|
||||
00030A 1 sta ZF
|
||||
00030A 1 rts
|
||||
00030A 1
|
||||
00030A 1 S6502: jsr SUB1
|
||||
00030A 1 lda AR
|
||||
00030A 1 php
|
||||
00030A 1 pla
|
||||
00030A 1 sta NF
|
||||
00030A 1 sta ZF
|
||||
00030A 1 lda HNVZC
|
||||
00030A 1 sta VF
|
||||
00030A 1 sta CF
|
||||
00030A 1 rts
|
||||
00030A 1
|
||||
00030A 1 .endif
|
||||
00030A 1
|
7
py65/tests/devices/bcd/README
Normal file
7
py65/tests/devices/bcd/README
Normal file
|
@ -0,0 +1,7 @@
|
|||
These .bin files were assembled with ca65 available at http://cc65.org
|
||||
|
||||
$ ca65 --cpu 6502 6502_decimal_test.c65 --listing 6502_decimal_test.lst
|
||||
$ ld65 -t none -o 6502_decimal_test.bin 6502_decimal_test.o
|
||||
|
||||
$ ca65 --cpu 65C02 65C02_decimal_test.c65 --listing 65C02_decimal_test.lst
|
||||
$ ld65 -t none -o 65C02_decimal_test.bin 65C02_decimal_test.o
|
56
py65/tests/devices/functional_tests.py
Normal file
56
py65/tests/devices/functional_tests.py
Normal file
|
@ -0,0 +1,56 @@
|
|||
"""Helpers for functional tests based on executing 65x02 object code."""
|
||||
|
||||
|
||||
class FunctionalTestExecutor(object):
|
||||
def __init__(self, mpu_class, filename, load_addr):
|
||||
self.mpu_class = mpu_class
|
||||
self.mpu = self._make_mpu()
|
||||
|
||||
object_code = bytearray(open(filename, "rb").read())
|
||||
self.write_memory(load_addr, object_code)
|
||||
|
||||
def _make_mpu(self, *args, **kargs):
|
||||
mpu = self.mpu_class(*args, **kargs)
|
||||
if 'memory' not in kargs:
|
||||
mpu.memory = 0x10000 * [0xAA]
|
||||
return mpu
|
||||
|
||||
def write_memory(self, start_address, bytes):
|
||||
self.mpu.memory[start_address:start_address + len(bytes)] = bytes
|
||||
|
||||
@staticmethod
|
||||
def never_trace_predicate(pc):
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def always_trace_predicate(pc):
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def address_completion_predicate(addrs):
|
||||
"""Terminate test when PC loops to itself or enters addrs set"""
|
||||
def completion_predicate(mpu, old_pc):
|
||||
return mpu.pc == old_pc or mpu.pc in addrs
|
||||
|
||||
return completion_predicate
|
||||
|
||||
def execute(
|
||||
self, pc, completion_predicate,
|
||||
trace_predicate=never_trace_predicate
|
||||
):
|
||||
self.mpu.pc = pc
|
||||
|
||||
while True:
|
||||
old_pc = self.mpu.pc
|
||||
self.mpu.step(trace=trace_predicate(self.mpu.pc))
|
||||
if completion_predicate(self.mpu, old_pc):
|
||||
break
|
||||
|
||||
|
||||
def trace_on_assertion(executor, *args, **kwargs):
|
||||
try:
|
||||
return executor(*args, **kwargs)
|
||||
except AssertionError:
|
||||
# Rerun with tracing
|
||||
return executor(*args, trace=True, **kwargs)
|
||||
|
76
py65/tests/devices/test_bcd_functional.py
Normal file
76
py65/tests/devices/test_bcd_functional.py
Normal file
|
@ -0,0 +1,76 @@
|
|||
"""65(c)02-based test suite for BCD implementation correctness.
|
||||
|
||||
See source code in bcd/*.c65 for more details.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
import py65.devices.mpu6502
|
||||
import py65.devices.mpu65c02
|
||||
from py65.tests.devices import functional_tests
|
||||
|
||||
|
||||
def run_bcd_test_case(mpu_class, filename, trace=False):
|
||||
executor = functional_tests.FunctionalTestExecutor(
|
||||
mpu_class, filename, load_addr=0x200)
|
||||
|
||||
# $1000: JSR $0200
|
||||
executor.write_memory(0x1000, [0x20, 0x00, 0x02])
|
||||
|
||||
# Set up BRK vector pointing to $2000 so we can trap PC
|
||||
executor.write_memory(0xfffe, [0x00, 0x20])
|
||||
|
||||
if trace:
|
||||
tracer = executor.always_trace_predicate
|
||||
else:
|
||||
tracer = executor.never_trace_predicate
|
||||
|
||||
executor.execute(
|
||||
0x1000,
|
||||
# If we are looping at the same PC, or we return
|
||||
# from the JSR, or we hit the BRK vector, then we are done.
|
||||
executor.address_completion_predicate({0x1003, 0x2000}),
|
||||
tracer
|
||||
)
|
||||
mpu = executor.mpu
|
||||
|
||||
if mpu.memory[0x0b] != 0: # Tests did not complete successfully
|
||||
# Display internal test state; read the .c65 source to understand
|
||||
# what these mean about the particular test case that failed.
|
||||
assert False, (
|
||||
"N1={:02x} N2={:02x} HA={:02x} HNVZC={:08b} DA={:02x} "
|
||||
"DNVZC={:08b} AR={:02x} NF={:08b} VF={:08b} ZF={:08b} "
|
||||
"CF={:08b}".format(
|
||||
mpu.memory[0x00], mpu.memory[0x01], mpu.memory[0x02],
|
||||
mpu.memory[0x03], mpu.memory[0x04], mpu.memory[0x05],
|
||||
mpu.memory[0x06], mpu.memory[0x07], mpu.memory[0x08],
|
||||
mpu.memory[0x09], mpu.memory[0x0a]
|
||||
))
|
||||
|
||||
|
||||
class BCDFunctionalTests(unittest.TestCase):
|
||||
|
||||
@staticmethod
|
||||
def test6502DecimalTest():
|
||||
functional_tests.trace_on_assertion(
|
||||
run_bcd_test_case,
|
||||
py65.devices.mpu6502.MPU,
|
||||
"devices/bcd/6502_decimal_test.bin"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def test65c02DecimalTest():
|
||||
functional_tests.trace_on_assertion(
|
||||
run_bcd_test_case,
|
||||
py65.devices.mpu65c02.MPU,
|
||||
"devices/bcd/65C02_decimal_test.bin"
|
||||
)
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.findTestCases(sys.modules[__name__])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(defaultTest='test_suite')
|
82
py65/tests/devices/test_klaus_dormann_functional.py
Normal file
82
py65/tests/devices/test_klaus_dormann_functional.py
Normal file
|
@ -0,0 +1,82 @@
|
|||
"""Harness for running Klaus Dormann's 65(c)02 functional test suite
|
||||
|
||||
These are quite comprehensive test suites for 65(c)02 implementation
|
||||
correctness, but they're licensed under the GPL so we cannot include
|
||||
the binary object code here (this is just the test harness for executing
|
||||
them in py65).
|
||||
|
||||
Obtain the object files from
|
||||
https://github.com/Klaus2m5/6502_65C02_functional_tests instead.
|
||||
"""
|
||||
|
||||
import os.path
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
import py65.devices.mpu6502
|
||||
import py65.devices.mpu65c02
|
||||
from py65.tests.devices import functional_tests
|
||||
|
||||
|
||||
def run_klaus_dormann_test(mpu_class, filename, load_addr, success_addr,
|
||||
trace=False):
|
||||
executor = functional_tests.FunctionalTestExecutor(
|
||||
mpu_class, filename, load_addr)
|
||||
|
||||
if trace:
|
||||
tracer = executor.always_trace_predicate
|
||||
else:
|
||||
tracer = executor.never_trace_predicate
|
||||
|
||||
executor.execute(
|
||||
0x400, executor.address_completion_predicate({success_addr}), tracer)
|
||||
|
||||
mpu = executor.mpu
|
||||
assert mpu.pc == success_addr, (
|
||||
"%s 0xb=%02x 0xc=%02x 0xd=%02x 0xf=%02x" % (
|
||||
mpu, mpu.memory[0xb], mpu.memory[0xc], mpu.memory[0xd],
|
||||
mpu.memory[0xf])
|
||||
)
|
||||
|
||||
|
||||
class KlausDormannTests(unittest.TestCase):
|
||||
"""Runs Klaus Dormann's 6502-based test suites"""
|
||||
|
||||
def test6502FunctionalTest(self):
|
||||
filename = "devices/6502_functional_test.bin"
|
||||
if not os.path.exists(filename):
|
||||
self.skipTest("%s not available, skipping")
|
||||
|
||||
functional_tests.trace_on_assertion(
|
||||
run_klaus_dormann_test,
|
||||
py65.devices.mpu6502.MPU,
|
||||
filename,
|
||||
load_addr=0x0,
|
||||
success_addr=0x3399
|
||||
)
|
||||
|
||||
def test65c02ExtendedOpcodeTest(self):
|
||||
# Modified version of 65C02_extended_opcodes_test that defines
|
||||
# rkwl_wdc_op = 0 (don't test BBR/BBS instructions, which we do not
|
||||
# implement) and many of the NOP tests for undefined opcodes which
|
||||
# are not yet implemented here.
|
||||
filename = "devices/65C02_extended_opcodes_test_modified.bin"
|
||||
|
||||
if not os.path.exists(filename):
|
||||
self.skipTest("%s not available, skipping")
|
||||
|
||||
functional_tests.trace_on_assertion(
|
||||
run_klaus_dormann_test,
|
||||
py65.devices.mpu65c02.MPU,
|
||||
filename,
|
||||
load_addr=0xa,
|
||||
success_addr=0x1570
|
||||
)
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.findTestCases(sys.modules[__name__])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main(defaultTest='test_suite')
|
|
@ -409,26 +409,6 @@ class Common6502Tests:
|
|||
self.assertEqual(0, mpu.p & mpu.ZERO)
|
||||
self.assertEqual(0, mpu.p & mpu.CARRY)
|
||||
|
||||
def test_adc_bcd_on_immediate_9c_plus_9d(self):
|
||||
mpu = self._make_mpu()
|
||||
mpu.p |= mpu.DECIMAL
|
||||
mpu.p &= ~(mpu.CARRY)
|
||||
mpu.a = 0x9c
|
||||
# $0000 ADC #$9d
|
||||
# $0002 ADC #$9d
|
||||
self._write(mpu.memory, 0x0000, (0x69, 0x9d))
|
||||
self._write(mpu.memory, 0x0002, (0x69, 0x9d))
|
||||
mpu.step()
|
||||
self.assertEqual(0x9f, mpu.a)
|
||||
self.assertEqual(mpu.CARRY, mpu.p & mpu.CARRY)
|
||||
mpu.step()
|
||||
self.assertEqual(0x0004, mpu.pc)
|
||||
self.assertEqual(0x93, mpu.a)
|
||||
self.assertEqual(0, mpu.p & mpu.NEGATIVE)
|
||||
self.assertEqual(mpu.OVERFLOW, mpu.p & mpu.OVERFLOW)
|
||||
self.assertEqual(0, mpu.p & mpu.ZERO)
|
||||
self.assertEqual(mpu.CARRY, mpu.p & mpu.CARRY)
|
||||
|
||||
# ADC Absolute, X-Indexed
|
||||
|
||||
def test_adc_bcd_off_abs_x_carry_clear_in_accumulator_zeroes(self):
|
||||
|
@ -4741,20 +4721,6 @@ class Common6502Tests:
|
|||
self.assertEqual(0, mpu.p & mpu.ZERO)
|
||||
self.assertEqual(0, mpu.p & mpu.CARRY)
|
||||
|
||||
def test_sbc_bcd_on_immediate_20_minus_0a_carry_unset(self):
|
||||
mpu = self._make_mpu()
|
||||
mpu.p |= mpu.DECIMAL
|
||||
mpu.a = 0x20
|
||||
# $0000 SBC #$00
|
||||
self._write(mpu.memory, 0x0000, (0xe9, 0x0a))
|
||||
mpu.step()
|
||||
self.assertEqual(0x0002, mpu.pc)
|
||||
self.assertEqual(0x1f, mpu.a)
|
||||
self.assertEqual(0, mpu.p & mpu.NEGATIVE)
|
||||
self.assertEqual(0, mpu.p & mpu.OVERFLOW)
|
||||
self.assertEqual(0, mpu.p & mpu.ZERO)
|
||||
self.assertEqual(mpu.CARRY, mpu.p & mpu.CARRY)
|
||||
|
||||
# SBC Absolute, X-Indexed
|
||||
|
||||
def test_sbc_abs_x_all_zeros_and_no_borrow_is_zero(self):
|
||||
|
@ -6054,6 +6020,42 @@ class MPUTests(unittest.TestCase, Common6502Tests):
|
|||
mpu.step()
|
||||
self.assertEqual(0x3f, mpu.a)
|
||||
|
||||
# Different undefined BCD behaviour between 6502 and 65C02
|
||||
|
||||
def test_adc_bcd_on_immediate_9c_plus_9d(self):
|
||||
mpu = self._make_mpu()
|
||||
mpu.p |= mpu.DECIMAL
|
||||
mpu.p &= ~(mpu.CARRY)
|
||||
mpu.a = 0x9c
|
||||
# $0000 ADC #$9d
|
||||
# $0002 ADC #$9d
|
||||
self._write(mpu.memory, 0x0000, (0x69, 0x9d))
|
||||
self._write(mpu.memory, 0x0002, (0x69, 0x9d))
|
||||
mpu.step()
|
||||
self.assertEqual(0x9f, mpu.a)
|
||||
self.assertEqual(mpu.CARRY, mpu.p & mpu.CARRY)
|
||||
mpu.step()
|
||||
self.assertEqual(0x0004, mpu.pc)
|
||||
self.assertEqual(0x93, mpu.a)
|
||||
self.assertEqual(0, mpu.p & mpu.NEGATIVE)
|
||||
self.assertEqual(mpu.OVERFLOW, mpu.p & mpu.OVERFLOW)
|
||||
self.assertEqual(0, mpu.p & mpu.ZERO)
|
||||
self.assertEqual(mpu.CARRY, mpu.p & mpu.CARRY)
|
||||
|
||||
def test_sbc_bcd_on_immediate_20_minus_0a_carry_unset(self):
|
||||
mpu = self._make_mpu()
|
||||
mpu.p |= mpu.DECIMAL
|
||||
mpu.a = 0x20
|
||||
# $0000 SBC #$00
|
||||
self._write(mpu.memory, 0x0000, (0xe9, 0x0a))
|
||||
mpu.step()
|
||||
self.assertEqual(0x0002, mpu.pc)
|
||||
self.assertEqual(0x1f, mpu.a)
|
||||
self.assertEqual(0, mpu.p & mpu.NEGATIVE)
|
||||
self.assertEqual(0, mpu.p & mpu.OVERFLOW)
|
||||
self.assertEqual(0, mpu.p & mpu.ZERO)
|
||||
self.assertEqual(mpu.CARRY, mpu.p & mpu.CARRY)
|
||||
|
||||
def _get_target_class(self):
|
||||
return py65.devices.mpu6502.MPU
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user