mirror of
https://github.com/mnaberez/py65.git
synced 2024-06-08 03:29:32 +00:00
Compare commits
28 Commits
9a82e6f676
...
518629b629
Author | SHA1 | Date | |
---|---|---|---|
|
518629b629 | ||
|
74e2576894 | ||
|
9455a5c70e | ||
|
1870d65982 | ||
|
8dce37f6b8 | ||
|
85ed46fd68 | ||
|
2b837bbd4f | ||
|
6ccdbe9c07 | ||
|
d4459daa40 | ||
|
d6ccff26ad | ||
|
5b28c007d9 | ||
|
7d5a29cbaa | ||
|
803632c15a | ||
|
3468cdce1c | ||
|
3d30544b95 | ||
|
e0f56e48e0 | ||
|
0e7066a4e1 | ||
|
254b2fb167 | ||
|
8b9cf7db69 | ||
|
fab1174abb | ||
|
430fec1e28 | ||
|
9b5e1f3945 | ||
|
4f52c95511 | ||
|
666cd9cd99 | ||
|
32896cc139 | ||
|
5e7ade9cb9 | ||
|
33b48ffbfb | ||
|
7fec386d42 |
33
.github/workflows/main.yml
vendored
33
.github/workflows/main.yml
vendored
|
@ -15,17 +15,20 @@ jobs:
|
|||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Show Python version
|
||||
run: python -V
|
||||
|
||||
- name: Install dependencies
|
||||
run: $PIP install setuptools
|
||||
run: $PIP install setuptools pexpect
|
||||
|
||||
- name: Run the tests
|
||||
run: python setup.py test -q
|
||||
|
||||
- name: Run the end-to-end tests
|
||||
run: END_TO_END=1 python setup.py test -q
|
||||
|
||||
build_py34:
|
||||
runs-on: ubuntu-20.04
|
||||
container: python:3.4
|
||||
|
@ -34,16 +37,24 @@ jobs:
|
|||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
# does not work with actions/checkout@v4:
|
||||
# /usr/bin/docker exec 289170dbefc90d2ba94a09f3dcb70c42c1e1b29a5e877cd533b26ebf73ca82fa sh -c "cat /etc/*release | grep ^ID"
|
||||
# /__e/node20/bin/node: /lib/x86_64-linux-gnu/libm.so.6: version `GLIBC_2.27' not found (required by /__e/node20/bin/node)
|
||||
# /__e/node20/bin/node: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.28' not found (required by /__e/node20/bin/node)
|
||||
# /__e/node20/bin/node: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.25' not found (required by /__e/node20/bin/node)
|
||||
|
||||
- name: Show Python version
|
||||
run: python -V
|
||||
|
||||
- name: Install dependencies
|
||||
run: $PIP install setuptools
|
||||
run: $PIP install setuptools pexpect
|
||||
|
||||
- name: Run the tests
|
||||
run: python setup.py test -q
|
||||
|
||||
- name: Run the end-to-end tests
|
||||
run: END_TO_END=1 python setup.py test -q
|
||||
|
||||
build_py35:
|
||||
runs-on: ubuntu-20.04
|
||||
container: python:3.5
|
||||
|
@ -51,17 +62,20 @@ jobs:
|
|||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Show Python version
|
||||
run: python -V
|
||||
|
||||
- name: Install dependencies
|
||||
run: $PIP install setuptools
|
||||
run: $PIP install setuptools pexpect
|
||||
|
||||
- name: Run the tests
|
||||
run: python setup.py test -q
|
||||
|
||||
- name: Run the end-to-end tests
|
||||
run: END_TO_END=1 python setup.py test -q
|
||||
|
||||
build_py3x:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
@ -72,10 +86,10 @@ jobs:
|
|||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
|
@ -83,7 +97,10 @@ jobs:
|
|||
run: python -V
|
||||
|
||||
- name: Install dependencies
|
||||
run: $PIP install setuptools
|
||||
run: $PIP install setuptools pexpect
|
||||
|
||||
- name: Run the tests
|
||||
run: python setup.py test -q
|
||||
|
||||
- name: Run the end-to-end tests
|
||||
run: END_TO_END=1 python setup.py test -q
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
1.2.0.dev0 (Next Release)
|
||||
1.3.0.dev0 (Next Release)
|
||||
-------------------------
|
||||
|
||||
1.2.0 (2024-04-12)
|
||||
------------------
|
||||
|
||||
- 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.
|
||||
|
||||
- Fixed interactive assembly on Python 3.
|
||||
|
||||
- Fixed regular expression warnings on Python 3.12.
|
||||
|
||||
- The ``fill`` command in the monitor now shows an error message if an
|
||||
address or value is out of range.
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2008-2018, Mike Naberezny and contributors.
|
||||
Copyright (c) 2008-2024, Mike Naberezny and contributors.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
|
|
@ -49,7 +49,7 @@ copyright = u'2008-%d, Mike Naberezny and contributors' % year
|
|||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '1.1.0.dev0'
|
||||
version = '1.3.0.dev0'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = version
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ class Assembler:
|
|||
and parsing the address part using AddressParser. The result of
|
||||
the normalization is a tuple of two strings (opcode, operand).
|
||||
"""
|
||||
statement = ' '.join(str.split(statement))
|
||||
statement = ' '.join(statement.split())
|
||||
|
||||
# normalize target in operand
|
||||
match = self.Statement.match(statement)
|
||||
|
|
21
py65/compat.py
Normal file
21
py65/compat.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
import sys
|
||||
|
||||
PY2 = sys.version_info[0] == 2
|
||||
|
||||
if PY2:
|
||||
unicode = unicode
|
||||
|
||||
def as_string(s, encoding='utf-8'):
|
||||
if isinstance(s, unicode):
|
||||
return s
|
||||
else:
|
||||
return s.decode(encoding)
|
||||
|
||||
else:
|
||||
unicode = str
|
||||
|
||||
def as_string(s, encoding='utf-8'):
|
||||
if isinstance(s, str):
|
||||
return s
|
||||
else:
|
||||
return s.decode(encoding)
|
|
@ -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)
|
||||
|
|
|
@ -163,8 +163,8 @@ class Monitor(cmd.Cmd):
|
|||
result = cmd.Cmd.onecmd(self, line)
|
||||
except KeyboardInterrupt:
|
||||
self._output("Interrupt")
|
||||
except Exception as e:
|
||||
error = ''.join(traceback.format_exception(e))
|
||||
except Exception:
|
||||
error = ''.join(traceback.format_exception(*sys.exc_info()))
|
||||
self._output(error)
|
||||
|
||||
if not line.startswith("quit"):
|
||||
|
@ -239,7 +239,7 @@ class Monitor(cmd.Cmd):
|
|||
line = command
|
||||
break
|
||||
|
||||
pattern = '^%s\s+' % re.escape(shortcut)
|
||||
pattern = r'^%s\s+' % re.escape(shortcut)
|
||||
matches = re.match(pattern, line)
|
||||
if matches:
|
||||
start, end = matches.span()
|
||||
|
@ -580,7 +580,7 @@ class Monitor(cmd.Cmd):
|
|||
if args == '':
|
||||
return
|
||||
|
||||
pairs = re.findall('([^=,\s]*)=([^=,\s]*)', args)
|
||||
pairs = re.findall(r'([^=,\s]*)=([^=,\s]*)', args)
|
||||
if pairs == []:
|
||||
return self._output("Syntax error: %s" % args)
|
||||
|
||||
|
|
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
|
||||
|
||||
|
|
88
py65/tests/end_to_end.py
Normal file
88
py65/tests/end_to_end.py
Normal file
|
@ -0,0 +1,88 @@
|
|||
import os
|
||||
import signal
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from py65.compat import unicode
|
||||
|
||||
# end-to-test tests are slow so only run them when asked
|
||||
if 'END_TO_END' in os.environ:
|
||||
if sys.platform == "win32":
|
||||
raise NotImplementedError()
|
||||
else:
|
||||
import pexpect
|
||||
BaseTestCase = unittest.TestCase
|
||||
else:
|
||||
BaseTestCase = object
|
||||
|
||||
|
||||
class EndToEndTests(BaseTestCase):
|
||||
|
||||
def _spawn(self):
|
||||
mon = pexpect.spawn(
|
||||
sys.executable,
|
||||
['-u', '-m', 'py65.monitor'],
|
||||
encoding='utf-8'
|
||||
)
|
||||
mon.expect_exact(unicode("Py65 Monitor"))
|
||||
self.addCleanup(mon.kill, signal.SIGINT)
|
||||
return mon
|
||||
|
||||
def test_putc(self):
|
||||
mon = self._spawn()
|
||||
|
||||
mon.sendline(unicode("add_label f001 putc"))
|
||||
|
||||
mon.sendline(unicode("a c000 lda #'H"))
|
||||
mon.sendline(unicode("a c002 sta putc"))
|
||||
mon.sendline(unicode("a c005 lda #'I"))
|
||||
mon.sendline(unicode("a c007 sta putc"))
|
||||
mon.sendline(unicode("a c00a brk"))
|
||||
|
||||
mon.sendline(unicode("g c000"))
|
||||
mon.expect_exact(unicode("HI"))
|
||||
mon.sendline(unicode("q"))
|
||||
|
||||
def test_getc(self):
|
||||
mon = self._spawn()
|
||||
|
||||
mon.sendline(unicode("add_label f004 getc"))
|
||||
|
||||
mon.sendline(unicode("a c000 ldx #0"))
|
||||
mon.sendline(unicode("a c002 lda getc"))
|
||||
mon.sendline(unicode("a c005 beq c002"))
|
||||
mon.sendline(unicode("a c007 cmp #'!"))
|
||||
mon.sendline(unicode("a c009 bne c00c"))
|
||||
mon.sendline(unicode("a c00b brk"))
|
||||
mon.sendline(unicode("a c00c sta 1000,x"))
|
||||
mon.sendline(unicode("a c00f inx"))
|
||||
mon.sendline(unicode("a c010 jmp c002"))
|
||||
|
||||
mon.sendline(unicode("g c000"))
|
||||
mon.send(unicode("HELLO!"))
|
||||
mon.expect_exact(unicode("6502:"))
|
||||
mon.sendline(unicode("m 1000:1004"))
|
||||
mon.expect_exact(unicode("48 45 4c 4c 4f"))
|
||||
|
||||
def test_assemble_interactive(self):
|
||||
mon = self._spawn()
|
||||
|
||||
mon.sendline(unicode("assemble 0"))
|
||||
mon.expect_exact(unicode("$0000"))
|
||||
|
||||
mon.sendline(unicode("lda $1234"))
|
||||
mon.expect_exact(unicode("ad 34 12"))
|
||||
|
||||
mon.expect_exact(unicode("$0003"))
|
||||
mon.sendline(unicode("sta $4567"))
|
||||
mon.expect_exact(unicode("8d 67 45"))
|
||||
|
||||
mon.sendline(unicode("invalid"))
|
||||
mon.expect_exact(unicode("?Syntax"))
|
||||
|
||||
mon.sendline()
|
||||
mon.sendline(unicode("quit"))
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -61,7 +61,7 @@ class AddressParser(object):
|
|||
return self.labels[num]
|
||||
|
||||
else:
|
||||
matches = re.match('^([^\s+-]+)\s*([+\-])\s*([$+%]?\d+)$', num)
|
||||
matches = re.match(r'^([^\s+-]+)\s*([+\-])\s*([$+%]?\d+)$', num)
|
||||
if matches:
|
||||
label, sign, offset = matches.groups()
|
||||
|
||||
|
@ -88,7 +88,7 @@ class AddressParser(object):
|
|||
"""Parse a string containing an address or a range of addresses
|
||||
into a tuple of (start address, end address)
|
||||
"""
|
||||
matches = re.match('^([^:,]+)\s*[:,]+\s*([^:,]+)$', addresses)
|
||||
matches = re.match(r'^([^:,]+)\s*[:,]+\s*([^:,]+)$', addresses)
|
||||
if matches:
|
||||
start, end = map(self.number, matches.groups(0))
|
||||
else:
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import sys
|
||||
|
||||
from py65.compat import as_string
|
||||
|
||||
if sys.platform[:3] == "win":
|
||||
import msvcrt
|
||||
|
||||
|
@ -24,10 +26,7 @@ if sys.platform[:3] == "win":
|
|||
is available. Does not echo the character. The stdin argument is
|
||||
for function signature compatibility and is ignored.
|
||||
"""
|
||||
c = msvcrt.getch()
|
||||
if isinstance(c, bytes): # Python 3
|
||||
c = c.decode('latin-1')
|
||||
return c
|
||||
return as_string(msvcrt.getch())
|
||||
|
||||
def getch_noblock(stdin):
|
||||
""" Read one character from the Windows console without blocking.
|
||||
|
@ -36,8 +35,8 @@ if sys.platform[:3] == "win":
|
|||
available, an empty string is returned.
|
||||
"""
|
||||
if msvcrt.kbhit():
|
||||
return getch(stdin)
|
||||
return ''
|
||||
return as_string(getch(stdin))
|
||||
return u''
|
||||
|
||||
else:
|
||||
import termios
|
||||
|
@ -157,7 +156,7 @@ else:
|
|||
# use select to make sure there is at least one char to read.
|
||||
rd,wr,er = select([stdin], [], [], 0.01)
|
||||
if rd != []:
|
||||
char = stdin.read(1)
|
||||
char = as_string(stdin.read(1))
|
||||
except KeyboardInterrupt:
|
||||
# Pass along a CTRL-C interrupt.
|
||||
raise
|
||||
|
@ -180,7 +179,7 @@ else:
|
|||
# use select to make sure there is at least one char to read.
|
||||
rd,wr,er = select([stdin], [], [], 0.01)
|
||||
if rd != []:
|
||||
char = stdin.read(1)
|
||||
char = as_string(stdin.read(1))
|
||||
except KeyboardInterrupt:
|
||||
# Pass along a CTRL-C interrupt.
|
||||
raise
|
||||
|
@ -200,6 +199,7 @@ def line_input(prompt='', stdin=sys.stdin, stdout=sys.stdout):
|
|||
is useful in modes like the interactive assembler.
|
||||
"""
|
||||
stdout.write(prompt)
|
||||
stdout.flush()
|
||||
line = ''
|
||||
while True:
|
||||
char = getch(stdin)
|
||||
|
@ -211,6 +211,7 @@ def line_input(prompt='', stdin=sys.stdin, stdout=sys.stdout):
|
|||
line = line[:-1]
|
||||
stdout.write("\r%s\r%s%s" %
|
||||
(' ' * (len(prompt + line) + 5), prompt, line))
|
||||
stdout.flush()
|
||||
elif code == 0x1b: # escape
|
||||
pass
|
||||
else:
|
||||
|
|
Loading…
Reference in New Issue
Block a user