Compare commits

...

26 Commits

Author SHA1 Message Date
KrisKennaway 9a82e6f676
Merge d4459daa40 into 95e152d6cb 2024-02-23 02:02:49 -08:00
Mike Naberezny 95e152d6cb Add changelog entry for b710c742ac 2023-11-18 17:04:42 -08:00
Mike Naberezny d547dbc07c Test with Python 3.12 on CI 2023-11-18 16:43:21 -08:00
Mike Naberezny 9ae3871388 Fix running Python 3.4 and 3.5 on CI 2023-11-18 16:34:11 -08:00
Mike Naberezny 55eef25998 Fix running Python 2 on CI 2023-11-18 15:06:23 -08:00
Mike Naberezny db247b9765 Revert "Remove support for Python 2"
This reverts commit ca02d12fc6.
2023-11-18 15:03:03 -08:00
kris d4459daa40 Tweak wording 2019-08-20 23:04:46 +01:00
kris d6ccff26ad Rename to decimal_flags_use_adjusted_results 2019-08-20 23:02:42 +01:00
kris 5b28c007d9 More cleanup to functional tests. Automatically skip the Klaus
Dormann ones if not present.
2019-08-20 23:02:23 +01:00
kris 7d5a29cbaa Oh actually the remaining test case did pass, I just had the wrong
completion PC

Streamline test harness a bit
2019-08-20 21:45:37 +01:00
kris 803632c15a Add instructions on how to assemble the binary test cases 2019-08-20 21:18:15 +01:00
kris 3468cdce1c Simplify and clean up 2019-08-20 21:17:33 +01:00
kris 3d30544b95 Clean up binary test execution framework 2019-08-20 21:17:13 +01:00
kris e0f56e48e0 Switch to cc65 versions of these sources, courtesy of github user
amb5l (https://github.com/Klaus2m5/6502_65C02_functional_tests/pull/8)

Minor modification to BRK when test completes instead of using 65C02
STP.
2019-08-20 21:14:18 +01:00
kris 0e7066a4e1 Clean up code, tests still equally working (but still not yet the
klaus 65C02 one)
2019-08-20 19:58:31 +01:00
kris 254b2fb167 Test cases pass except for devices/65C02_extended_opcodes_test_modified.bin 2019-08-20 14:59:43 +01:00
kris 8b9cf7db69 Revert "Revert "Add support for optionally tracing execution""
This reverts commit fab1174abb.
2019-08-20 14:22:57 +01:00
kris fab1174abb Revert "Add support for optionally tracing execution"
This reverts commit 5e7ade9cb9.
2019-08-20 14:16:10 +01:00
kris 430fec1e28 Merge remote-tracking branch 'upstream/master' 2019-08-20 14:12:24 +01:00
kris 9b5e1f3945 Move two BCD test cases from the common 65x02 test cases to
6502-specific.  The handling of invalid BCD arguments differs between
6502 and 65C02 and these are testing 6502-specific behaviour.
2017-07-22 23:42:29 +01:00
kris 4f52c95511 Add functional test cases that exhaustively test BCD mode on 6502 and
65C02 including invalid BCD arguments.

These are

; Written by Bruce Clark.  This code is public domain.
; see http://www.6502.org/tutorials/decimal_mode.html

and obtained via Klaus Dormann's
https://github.com/Klaus2m5/6502_65C02_functional_tests
2017-07-22 23:40:05 +01:00
kris 666cd9cd99 Use the decimally adjusted aluresult to compute the value of flags.
This fixes various bugs with ADC/SBC in decimal mode found by Klaus
Dormann's test suite.
2017-05-11 22:43:08 +01:00
kris 32896cc139 Support tracing execution for particular PC values. Use this to
trace the execution of the currently failing decimal-mode ADC test,
see https://github.com/mnaberez/py65/issues/33
2017-05-11 22:01:33 +01:00
kris 5e7ade9cb9 Add support for optionally tracing execution 2017-05-11 21:55:29 +01:00
User Apple2 33b48ffbfb Merge branch 'master' of https://github.com/mnaberez/py65 2017-05-10 23:19:44 +00:00
kris 7fec386d42 Test harness for executing Klaus Dormann's 6502-based test suites, see
https://github.com/Klaus2m5/6502_65C02_functional_tests for source
and binary images (GPL'ed so not committing here)

The 65C02 one currently is not passing.
2017-05-10 23:16:23 +01:00
19 changed files with 1886 additions and 101 deletions

View File

@ -2,13 +2,72 @@ name: Run all tests
on: [push, pull_request]
env:
PIP: "env PIP_DISABLE_PIP_VERSION_CHECK=1
PYTHONWARNINGS=ignore:DEPRECATION
pip --no-cache-dir"
jobs:
build:
tests_py27:
runs-on: ubuntu-20.04
container: python:2.7
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v3
- name: Show Python version
run: python -V
- name: Install dependencies
run: $PIP install setuptools
- name: Run the tests
run: python setup.py test -q
build_py34:
runs-on: ubuntu-20.04
container: python:3.4
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v3
- name: Show Python version
run: python -V
- name: Install dependencies
run: $PIP install setuptools
- name: Run the tests
run: python setup.py test -q
build_py35:
runs-on: ubuntu-20.04
container: python:3.5
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v3
- name: Show Python version
run: python -V
- name: Install dependencies
run: $PIP install setuptools
- name: Run the tests
run: python setup.py test -q
build_py3x:
strategy:
fail-fast: false
matrix:
python-version: [3.6, 3.7, 3.8, 3.9, "3.10", 3.11]
os: [ubuntu-20.04, windows-2019]
python-version: [3.6, 3.7, 3.8, 3.9, "3.10", 3.11, 3.12]
os: [ubuntu-20.04]
runs-on: ${{ matrix.os }}
@ -23,5 +82,8 @@ jobs:
- name: Show Python version
run: python -V
- name: Install dependencies
run: $PIP install setuptools
- name: Run the tests
run: python setup.py test -q

View File

@ -1,9 +1,6 @@
2.0.0.dev0 (Next Release)
1.2.0.dev0 (Next Release)
-------------------------
- Support for some older Python versions has been dropped. Py65
now requires Python 3.6 or later.
- Fixed a bug with character input that would cause characters to be
dropped when pasting in larger amounts of text. This makes it possible
to paste programs into EhBASIC and Taliforth. Patch by SamCoVT.
@ -22,6 +19,8 @@
- Fixed assembly and disassembly of 65C02 instruction $64 (``STZ $12``).
Patch by Patrick Surry.
- Removed use of the ``asyncore`` module deprecated in Python 3.10.
1.1.0 (2018-07-01)
------------------

View File

@ -20,9 +20,9 @@ Installation
Py65 packages are `available <http://pypi.python.org/pypi/py65>`_ on the
Python Package Index (PyPI). You download them from there or you can
use ``pip3`` to automatically install or upgrade Py65::
use ``pip`` to install Py65::
$ pip3 install -U py65
$ pip install setuptools py65
Devices
-------

View File

@ -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:

View File

@ -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)

View File

@ -22,8 +22,6 @@ import shlex
import sys
import traceback
from urllib.request import urlopen
from py65.devices.mpu6502 import MPU as NMOS6502
from py65.devices.mpu65c02 import MPU as CMOS65C02
from py65.devices.mpu65org16 import MPU as V65Org16
@ -34,6 +32,11 @@ from py65.utils import console
from py65.utils.conversions import itoa
from py65.memory import ObservableMemory
try:
from urllib2 import urlopen
except ImportError: # Python 3
from urllib.request import urlopen
class Monitor(cmd.Cmd):
Microprocessors = {'6502': NMOS6502, '65C02': CMOS65C02,

Binary file not shown.

View 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

View 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

Binary file not shown.

View 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

View 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

View 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

View 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)

View 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')

View 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')

View File

@ -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

View File

@ -2,11 +2,13 @@ import unittest
import sys
import os
import tempfile
from io import StringIO
from py65.monitor import Monitor
try:
from StringIO import StringIO
except ImportError: # Python 3
from io import StringIO
class MonitorTests(unittest.TestCase):

View File

@ -3,9 +3,14 @@ __version__ = '2.0.0.dev0'
import sys
py_version = sys.version_info[:2]
PY3 = py_version[0] == 3
if py_version < (3, 6):
raise RuntimeError('On Python 3, Py65 requires Python 3.6 or later')
if PY3:
if py_version < (3, 4):
raise RuntimeError('On Python 3, Py65 requires Python 3.4 or later')
else:
if py_version < (2, 7):
raise RuntimeError('On Python 2, Py65 requires Python 2.7 or later')
from setuptools import setup, find_packages
@ -19,13 +24,18 @@ CLASSIFIERS = [
'Natural Language :: English',
'Operating System :: POSIX',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Assembly',
'Topic :: Software Development :: Assemblers',
'Topic :: Software Development :: Disassemblers',