mirror of
https://github.com/mnaberez/py65.git
synced 2024-06-08 03:29:32 +00:00
Compare commits
26 Commits
4d0c2f6f8e
...
9a82e6f676
Author | SHA1 | Date | |
---|---|---|---|
|
9a82e6f676 | ||
|
95e152d6cb | ||
|
d547dbc07c | ||
|
9ae3871388 | ||
|
55eef25998 | ||
|
db247b9765 | ||
|
d4459daa40 | ||
|
d6ccff26ad | ||
|
5b28c007d9 | ||
|
7d5a29cbaa | ||
|
803632c15a | ||
|
3468cdce1c | ||
|
3d30544b95 | ||
|
e0f56e48e0 | ||
|
0e7066a4e1 | ||
|
254b2fb167 | ||
|
8b9cf7db69 | ||
|
fab1174abb | ||
|
430fec1e28 | ||
|
9b5e1f3945 | ||
|
4f52c95511 | ||
|
666cd9cd99 | ||
|
32896cc139 | ||
|
5e7ade9cb9 | ||
|
33b48ffbfb | ||
|
7fec386d42 |
68
.github/workflows/main.yml
vendored
68
.github/workflows/main.yml
vendored
|
@ -2,13 +2,72 @@ name: Run all tests
|
||||||
|
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
env:
|
||||||
|
PIP: "env PIP_DISABLE_PIP_VERSION_CHECK=1
|
||||||
|
PYTHONWARNINGS=ignore:DEPRECATION
|
||||||
|
pip --no-cache-dir"
|
||||||
|
|
||||||
jobs:
|
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:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
python-version: [3.6, 3.7, 3.8, 3.9, "3.10", 3.11]
|
python-version: [3.6, 3.7, 3.8, 3.9, "3.10", 3.11, 3.12]
|
||||||
os: [ubuntu-20.04, windows-2019]
|
os: [ubuntu-20.04]
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
@ -23,5 +82,8 @@ jobs:
|
||||||
- name: Show Python version
|
- name: Show Python version
|
||||||
run: python -V
|
run: python -V
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: $PIP install setuptools
|
||||||
|
|
||||||
- name: Run the tests
|
- name: Run the tests
|
||||||
run: python setup.py test -q
|
run: python setup.py test -q
|
||||||
|
|
|
@ -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
|
- Fixed a bug with character input that would cause characters to be
|
||||||
dropped when pasting in larger amounts of text. This makes it possible
|
dropped when pasting in larger amounts of text. This makes it possible
|
||||||
to paste programs into EhBASIC and Taliforth. Patch by SamCoVT.
|
to paste programs into EhBASIC and Taliforth. Patch by SamCoVT.
|
||||||
|
@ -22,6 +19,8 @@
|
||||||
- Fixed assembly and disassembly of 65C02 instruction $64 (``STZ $12``).
|
- Fixed assembly and disassembly of 65C02 instruction $64 (``STZ $12``).
|
||||||
Patch by Patrick Surry.
|
Patch by Patrick Surry.
|
||||||
|
|
||||||
|
- Removed use of the ``asyncore`` module deprecated in Python 3.10.
|
||||||
|
|
||||||
1.1.0 (2018-07-01)
|
1.1.0 (2018-07-01)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,9 @@ Installation
|
||||||
|
|
||||||
Py65 packages are `available <http://pypi.python.org/pypi/py65>`_ on the
|
Py65 packages are `available <http://pypi.python.org/pypi/py65>`_ on the
|
||||||
Python Package Index (PyPI). You download them from there or you can
|
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
|
Devices
|
||||||
-------
|
-------
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
from py65.utils.conversions import itoa
|
from py65.utils.conversions import itoa
|
||||||
from py65.utils.devices import make_instruction_decorator
|
from py65.utils.devices import make_instruction_decorator
|
||||||
|
from py65 import disassembler
|
||||||
|
|
||||||
class MPU:
|
class MPU:
|
||||||
# vectors
|
# vectors
|
||||||
|
@ -41,6 +43,8 @@ class MPU:
|
||||||
self.memory = memory
|
self.memory = memory
|
||||||
self.start_pc = pc # if None, reset vector is used
|
self.start_pc = pc # if None, reset vector is used
|
||||||
|
|
||||||
|
self.disassembler = disassembler.Disassembler(self)
|
||||||
|
|
||||||
# init
|
# init
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
|
@ -55,8 +59,12 @@ class MPU:
|
||||||
return self.reprformat() % (indent, self.name, self.pc, self.a,
|
return self.reprformat() % (indent, self.name, self.pc, self.a,
|
||||||
self.x, self.y, self.sp, flags)
|
self.x, self.y, self.sp, flags)
|
||||||
|
|
||||||
def step(self):
|
def step(self, trace=False):
|
||||||
instructCode = self.memory[self.pc]
|
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.pc = (self.pc + 1) & self.addrMask
|
||||||
self.excycles = 0
|
self.excycles = 0
|
||||||
self.addcycles = self.extracycles[instructCode]
|
self.addcycles = self.extracycles[instructCode]
|
||||||
|
@ -315,6 +323,9 @@ class MPU:
|
||||||
self.FlagsNZ(self.a)
|
self.FlagsNZ(self.a)
|
||||||
|
|
||||||
def opADC(self, x):
|
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())
|
data = self.ByteAt(x())
|
||||||
|
|
||||||
if self.p & self.DECIMAL:
|
if self.p & self.DECIMAL:
|
||||||
|
@ -331,24 +342,45 @@ class MPU:
|
||||||
adjust1 = 6
|
adjust1 = 6
|
||||||
decimalcarry = 1
|
decimalcarry = 1
|
||||||
|
|
||||||
# the ALU outputs are not decimally adjusted
|
# The ALU outputs are not yet decimally adjusted
|
||||||
nibble0 = nibble0 & 0xf
|
nibble0 = nibble0 & 0xf
|
||||||
nibble1 = nibble1 & 0xf
|
nibble1 = nibble1 & 0xf
|
||||||
aluresult = (nibble1 << 4) + nibble0
|
aluresult = (nibble1 << 4) + nibble0
|
||||||
|
|
||||||
# the final A contents will be decimally adjusted
|
# Partial result with only low nibble decimally adjusted
|
||||||
nibble0 = (nibble0 + adjust0) & 0xf
|
nibble0 = (nibble0 + adjust0) & 0xf
|
||||||
|
halfadjresult = (nibble1 << 4) + nibble0
|
||||||
|
|
||||||
|
# the final A contents has both nibbles decimally adjusted
|
||||||
nibble1 = (nibble1 + adjust1) & 0xf
|
nibble1 = (nibble1 + adjust1) & 0xf
|
||||||
|
adjresult = (nibble1 << 4) + nibble0
|
||||||
|
|
||||||
self.p &= ~(self.CARRY | self.OVERFLOW | self.NEGATIVE | self.ZERO)
|
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
|
self.p |= self.ZERO
|
||||||
else:
|
else:
|
||||||
self.p |= aluresult & self.NEGATIVE
|
self.p |= negativeres & self.NEGATIVE
|
||||||
|
|
||||||
if decimalcarry == 1:
|
if decimalcarry == 1:
|
||||||
self.p |= self.CARRY
|
self.p |= self.CARRY
|
||||||
|
|
||||||
if (~(self.a ^ data) & (self.a ^ aluresult)) & self.NEGATIVE:
|
if (~(self.a ^ data) & (self.a ^ aluresult)) & self.NEGATIVE:
|
||||||
self.p |= self.OVERFLOW
|
self.p |= self.OVERFLOW
|
||||||
self.a = (nibble1 << 4) + nibble0
|
|
||||||
|
self.a = adjresult
|
||||||
else:
|
else:
|
||||||
if self.p & self.CARRY:
|
if self.p & self.CARRY:
|
||||||
tmp = 1
|
tmp = 1
|
||||||
|
@ -411,56 +443,78 @@ class MPU:
|
||||||
self.p |= (register_value - tbyte) & self.NEGATIVE
|
self.p |= (register_value - tbyte) & self.NEGATIVE
|
||||||
|
|
||||||
def opSBC(self, x):
|
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())
|
data = self.ByteAt(x())
|
||||||
|
|
||||||
if self.p & self.DECIMAL:
|
result = self.a + (~data & self.byteMask) + (self.p & self.CARRY)
|
||||||
halfcarry = 1
|
self.p &= ~(self.CARRY | self.ZERO | self.OVERFLOW | self.NEGATIVE)
|
||||||
decimalcarry = 0
|
if ((self.a ^ data) & (self.a ^ result)) & self.NEGATIVE:
|
||||||
adjust0 = 0
|
self.p |= self.OVERFLOW
|
||||||
adjust1 = 0
|
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)
|
def _opSBCDecimal(self, x, flags_use_adjusted_result=False):
|
||||||
if nibble0 <= 0xf:
|
"""SBC opcode in BCD mode.
|
||||||
halfcarry = 0
|
|
||||||
adjust0 = 10
|
|
||||||
nibble1 = ((self.a >> 4) & 0xf) + ((~data >> 4) & 0xf) + halfcarry
|
|
||||||
if nibble1 <= 0xf:
|
|
||||||
adjust1 = 10 << 4
|
|
||||||
|
|
||||||
# the ALU outputs are not decimally adjusted
|
See e.g. http://6502.org/tutorials/decimal_mode.html#A for details
|
||||||
aluresult = self.a + (~data & self.byteMask) + \
|
"""
|
||||||
(self.p & self.CARRY)
|
data = self.ByteAt(x())
|
||||||
|
|
||||||
if aluresult > self.byteMask:
|
# the ALU outputs are not yet decimally adjusted
|
||||||
decimalcarry = 1
|
aluresult = self.a + (~data & self.byteMask) + (self.p & self.CARRY)
|
||||||
aluresult &= self.byteMask
|
|
||||||
|
|
||||||
# but the final result will be adjusted
|
decimalcarry = aluresult > self.byteMask
|
||||||
nibble0 = (aluresult + adjust0) & 0xf
|
aluresult &= self.byteMask
|
||||||
nibble1 = ((aluresult + adjust1) >> 4) & 0xf
|
|
||||||
|
|
||||||
self.p &= ~(self.CARRY | self.ZERO | self.NEGATIVE | self.OVERFLOW)
|
al = (self.a & 0xf) - (data & 0xf) + (self.p & self.CARRY) - 1
|
||||||
if aluresult == 0:
|
|
||||||
self.p |= self.ZERO
|
if flags_use_adjusted_result: # 65C02 but not 65816
|
||||||
else:
|
a = self.a - data + (self.p & self.CARRY) - 1
|
||||||
self.p |= aluresult & self.NEGATIVE
|
if a < 0:
|
||||||
if decimalcarry == 1:
|
a -= 0x60
|
||||||
self.p |= self.CARRY
|
if al < 0:
|
||||||
if ((self.a ^ data) & (self.a ^ aluresult)) & self.NEGATIVE:
|
a -= 0x6
|
||||||
self.p |= self.OVERFLOW
|
|
||||||
self.a = (nibble1 << 4) + nibble0
|
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:
|
else:
|
||||||
result = self.a + (~data & self.byteMask) + (self.p & self.CARRY)
|
self.p |= negativeres & self.NEGATIVE
|
||||||
self.p &= ~(self.CARRY | self.ZERO | self.OVERFLOW | self.NEGATIVE)
|
if decimalcarry == 1:
|
||||||
if ((self.a ^ data) & (self.a ^ result)) & self.NEGATIVE:
|
self.p |= self.CARRY
|
||||||
self.p |= self.OVERFLOW
|
if ((self.a ^ data) & (self.a ^ aluresult)) & self.NEGATIVE:
|
||||||
data = result & self.byteMask
|
self.p |= self.OVERFLOW
|
||||||
if data == 0:
|
self.a = adjresult
|
||||||
self.p |= self.ZERO
|
|
||||||
if result > self.byteMask:
|
|
||||||
self.p |= self.CARRY
|
|
||||||
self.p |= data & self.NEGATIVE
|
|
||||||
self.a = data
|
|
||||||
|
|
||||||
def opDECR(self, x):
|
def opDECR(self, x):
|
||||||
if x is None:
|
if x is None:
|
||||||
|
|
|
@ -8,11 +8,11 @@ class MPU(mpu6502.MPU):
|
||||||
self.name = '65C02'
|
self.name = '65C02'
|
||||||
self.waiting = False
|
self.waiting = False
|
||||||
|
|
||||||
def step(self):
|
def step(self, trace=False):
|
||||||
if self.waiting:
|
if self.waiting:
|
||||||
self.processorCycles += 1
|
self.processorCycles += 1
|
||||||
else:
|
else:
|
||||||
mpu6502.MPU.step(self)
|
mpu6502.MPU.step(self, trace)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
# Make copies of the lists
|
# Make copies of the lists
|
||||||
|
@ -63,6 +63,12 @@ class MPU(mpu6502.MPU):
|
||||||
self.p |= self.ZERO
|
self.p |= self.ZERO
|
||||||
self.memory[address] = m & ~self.a
|
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
|
# instructions
|
||||||
|
|
||||||
@instruction(name="BRK", mode="imp", cycles=7)
|
@instruction(name="BRK", mode="imp", cycles=7)
|
||||||
|
|
|
@ -22,8 +22,6 @@ import shlex
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from urllib.request import urlopen
|
|
||||||
|
|
||||||
from py65.devices.mpu6502 import MPU as NMOS6502
|
from py65.devices.mpu6502 import MPU as NMOS6502
|
||||||
from py65.devices.mpu65c02 import MPU as CMOS65C02
|
from py65.devices.mpu65c02 import MPU as CMOS65C02
|
||||||
from py65.devices.mpu65org16 import MPU as V65Org16
|
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.utils.conversions import itoa
|
||||||
from py65.memory import ObservableMemory
|
from py65.memory import ObservableMemory
|
||||||
|
|
||||||
|
try:
|
||||||
|
from urllib2 import urlopen
|
||||||
|
except ImportError: # Python 3
|
||||||
|
from urllib.request import urlopen
|
||||||
|
|
||||||
class Monitor(cmd.Cmd):
|
class Monitor(cmd.Cmd):
|
||||||
|
|
||||||
Microprocessors = {'6502': NMOS6502, '65C02': CMOS65C02,
|
Microprocessors = {'6502': NMOS6502, '65C02': CMOS65C02,
|
||||||
|
|
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.ZERO)
|
||||||
self.assertEqual(0, mpu.p & mpu.CARRY)
|
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
|
# ADC Absolute, X-Indexed
|
||||||
|
|
||||||
def test_adc_bcd_off_abs_x_carry_clear_in_accumulator_zeroes(self):
|
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.ZERO)
|
||||||
self.assertEqual(0, mpu.p & mpu.CARRY)
|
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
|
# SBC Absolute, X-Indexed
|
||||||
|
|
||||||
def test_sbc_abs_x_all_zeros_and_no_borrow_is_zero(self):
|
def test_sbc_abs_x_all_zeros_and_no_borrow_is_zero(self):
|
||||||
|
@ -6054,6 +6020,42 @@ class MPUTests(unittest.TestCase, Common6502Tests):
|
||||||
mpu.step()
|
mpu.step()
|
||||||
self.assertEqual(0x3f, mpu.a)
|
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):
|
def _get_target_class(self):
|
||||||
return py65.devices.mpu6502.MPU
|
return py65.devices.mpu6502.MPU
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,13 @@ import unittest
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from io import StringIO
|
|
||||||
|
|
||||||
from py65.monitor import Monitor
|
from py65.monitor import Monitor
|
||||||
|
|
||||||
|
try:
|
||||||
|
from StringIO import StringIO
|
||||||
|
except ImportError: # Python 3
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
|
|
||||||
class MonitorTests(unittest.TestCase):
|
class MonitorTests(unittest.TestCase):
|
||||||
|
|
||||||
|
|
14
setup.py
14
setup.py
|
@ -3,9 +3,14 @@ __version__ = '2.0.0.dev0'
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
py_version = sys.version_info[:2]
|
py_version = sys.version_info[:2]
|
||||||
|
PY3 = py_version[0] == 3
|
||||||
|
|
||||||
if py_version < (3, 6):
|
if PY3:
|
||||||
raise RuntimeError('On Python 3, Py65 requires Python 3.6 or later')
|
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
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
@ -19,13 +24,18 @@ CLASSIFIERS = [
|
||||||
'Natural Language :: English',
|
'Natural Language :: English',
|
||||||
'Operating System :: POSIX',
|
'Operating System :: POSIX',
|
||||||
'Programming Language :: Python',
|
'Programming Language :: Python',
|
||||||
|
'Programming Language :: Python :: 2',
|
||||||
|
'Programming Language :: Python :: 2.7',
|
||||||
'Programming Language :: Python :: 3',
|
'Programming Language :: Python :: 3',
|
||||||
|
'Programming Language :: Python :: 3.4',
|
||||||
|
'Programming Language :: Python :: 3.5',
|
||||||
'Programming Language :: Python :: 3.6',
|
'Programming Language :: Python :: 3.6',
|
||||||
'Programming Language :: Python :: 3.7',
|
'Programming Language :: Python :: 3.7',
|
||||||
'Programming Language :: Python :: 3.8',
|
'Programming Language :: Python :: 3.8',
|
||||||
'Programming Language :: Python :: 3.9',
|
'Programming Language :: Python :: 3.9',
|
||||||
'Programming Language :: Python :: 3.10',
|
'Programming Language :: Python :: 3.10',
|
||||||
'Programming Language :: Python :: 3.11',
|
'Programming Language :: Python :: 3.11',
|
||||||
|
'Programming Language :: Python :: 3.12',
|
||||||
'Programming Language :: Assembly',
|
'Programming Language :: Assembly',
|
||||||
'Topic :: Software Development :: Assemblers',
|
'Topic :: Software Development :: Assemblers',
|
||||||
'Topic :: Software Development :: Disassemblers',
|
'Topic :: Software Development :: Disassemblers',
|
||||||
|
|
Loading…
Reference in New Issue
Block a user