1
0
mirror of https://github.com/mnaberez/py65.git synced 2024-05-31 12:41:31 +00:00

More cleanup to functional tests. Automatically skip the Klaus

Dormann ones if not present.
This commit is contained in:
kris 2019-08-20 23:02:23 +01:00
parent 7d5a29cbaa
commit 5b28c007d9
5 changed files with 214 additions and 198 deletions

View File

@ -0,0 +1,56 @@
"""Helpers for functional tests based on executing 65x02 object code."""
class FunctionalTestExecutor(object):
def __init__(self, mpu_class, filename, load_addr):
self.mpu_class = mpu_class
self.mpu = self._make_mpu()
object_code = bytearray(open(filename, "rb").read())
self.write_memory(load_addr, object_code)
def _make_mpu(self, *args, **kargs):
mpu = self.mpu_class(*args, **kargs)
if 'memory' not in kargs:
mpu.memory = 0x10000 * [0xAA]
return mpu
def write_memory(self, start_address, bytes):
self.mpu.memory[start_address:start_address + len(bytes)] = bytes
@staticmethod
def never_trace_predicate(pc):
return False
@staticmethod
def always_trace_predicate(pc):
return True
@staticmethod
def address_completion_predicate(addrs):
"""Terminate test when PC loops to itself or enters addrs set"""
def completion_predicate(mpu, old_pc):
return mpu.pc == old_pc or mpu.pc in addrs
return completion_predicate
def execute(
self, pc, completion_predicate,
trace_predicate=never_trace_predicate
):
self.mpu.pc = pc
while True:
old_pc = self.mpu.pc
self.mpu.step(trace=trace_predicate(self.mpu.pc))
if completion_predicate(self.mpu, old_pc):
break
def trace_on_assertion(executor, *args, **kwargs):
try:
return executor(*args, **kwargs)
except AssertionError:
# Rerun with tracing
return executor(*args, trace=True, **kwargs)

View File

@ -0,0 +1,76 @@
"""65(c)02-based test suite for BCD implementation correctness.
See source code in bcd/*.c65 for more details.
"""
import sys
import unittest
import py65.devices.mpu6502
import py65.devices.mpu65c02
from py65.tests.devices import functional_tests
def run_bcd_test_case(mpu_class, filename, trace=False):
executor = functional_tests.FunctionalTestExecutor(
mpu_class, filename, load_addr=0x200)
# $1000: JSR $0200
executor.write_memory(0x1000, [0x20, 0x00, 0x02])
# Set up BRK vector pointing to $2000 so we can trap PC
executor.write_memory(0xfffe, [0x00, 0x20])
if trace:
tracer = executor.always_trace_predicate
else:
tracer = executor.never_trace_predicate
executor.execute(
0x1000,
# If we are looping at the same PC, or we return
# from the JSR, or we hit the BRK vector, then we are done.
executor.address_completion_predicate({0x1003, 0x2000}),
tracer
)
mpu = executor.mpu
if mpu.memory[0x0b] != 0: # Tests did not complete successfully
# Display internal test state; read the .c65 source to understand
# what these mean about the particular test case that failed.
assert False, (
"N1={:02x} N2={:02x} HA={:02x} HNVZC={:08b} DA={:02x} "
"DNVZC={:08b} AR={:02x} NF={:08b} VF={:08b} ZF={:08b} "
"CF={:08b}".format(
mpu.memory[0x00], mpu.memory[0x01], mpu.memory[0x02],
mpu.memory[0x03], mpu.memory[0x04], mpu.memory[0x05],
mpu.memory[0x06], mpu.memory[0x07], mpu.memory[0x08],
mpu.memory[0x09], mpu.memory[0x0a]
))
class BCDFunctionalTests(unittest.TestCase):
@staticmethod
def test6502DecimalTest():
functional_tests.trace_on_assertion(
run_bcd_test_case,
py65.devices.mpu6502.MPU,
"devices/bcd/6502_decimal_test.bin"
)
@staticmethod
def test65c02DecimalTest():
functional_tests.trace_on_assertion(
run_bcd_test_case,
py65.devices.mpu65c02.MPU,
"devices/bcd/65C02_decimal_test.bin"
)
def test_suite():
return unittest.findTestCases(sys.modules[__name__])
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')

View File

@ -1,111 +0,0 @@
import unittest
import sys
import py65.devices.mpu6502
import py65.devices.mpu65c02
class BinaryObjectTests(unittest.TestCase):
"""Test cases based on executing 65x02 object code."""
# If test fails, rerun with execution tracing enabled
TRACE_TEST_FAILURE = False
def binaryObjectTestCase(self, filename, load_addr, pc, success_addr, should_trace=None):
mpu = self._make_mpu()
mpu.pc = pc
object_code = bytearray(open(filename, "r").read())
self._write(mpu.memory, load_addr, object_code)
if not should_trace:
should_trace = lambda pc: False
while True:
old_pc = mpu.pc
mpu.step(trace=should_trace(mpu.pc))
if mpu.pc == old_pc:
break
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])
# Test Helpers
def _write(self, memory, start_address, bytes):
memory[start_address:start_address + len(bytes)] = bytes
def _make_mpu(self, *args, **kargs):
mpu = self.MPU(*args, **kargs)
if 'memory' not in kargs:
mpu.memory = 0x10000 * [0xAA]
return mpu
def runBinaryTest(self, executor, filename):
try:
return executor(filename)
except AssertionError:
# Rerun with tracing
if self.TRACE_TEST_FAILURE:
return executor(filename, trace=True)
else:
raise
class FunctionalBCDTests(BinaryObjectTests):
def bcd_test_case(self, filename, trace=False):
mpu = self._make_mpu()
mpu.pc = 0x1000
object_code = bytearray(open(filename, "r").read())
self._write(mpu.memory, 0x200, object_code)
# $1000: JSR $0200
self._write(mpu.memory, 0x1000, [0x20, 0x00, 0x02])
# Set up BRK vector pointing to $2000 so we can trap PC
self._write(mpu.memory, 0xfffe, [0x00, 0x20])
def should_trace(pc):
return trace
while True:
old_pc = mpu.pc
mpu.step(trace=should_trace(mpu.pc))
# If we are looping at the same PC, or we return
# from the JSR, or we hit the BRK vector, then we are done.
if mpu.pc == old_pc or mpu.pc == 0x1003 or mpu.pc == 0x2000:
break
if mpu.memory[0x0b] != 0: # Tests did not complete successfully
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 Functional6502Tests(FunctionalBCDTests):
MPU = py65.devices.mpu6502.MPU
def test6502DecimalTest(self):
self.runBinaryTest(
self.bcd_test_case, "devices/bcd/6502_decimal_test.bin")
class Functional65C02Tests(FunctionalBCDTests):
MPU = py65.devices.mpu65c02.MPU
def test65C02DecimalTest(self):
self.runBinaryTest(
self.bcd_test_case, "devices/bcd/65C02_decimal_test.bin")
def test_suite():
return unittest.findTestCases(sys.modules[__name__])
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')

View File

@ -1,87 +0,0 @@
import unittest
import sys
import py65.devices.mpu65c02
class KlausDormannTests(unittest.TestCase):
"""Runs Klaus Dormann's 6502-based test suites"""
MPU = py65.devices.mpu65c02.MPU
def klausTestCase(self, filename, load_addr, pc, completion_criteria,
should_trace=None):
mpu = self._make_mpu()
mpu.pc = pc
object_code = bytearray(open(filename, "r").read())
self._write(mpu.memory, load_addr, object_code)
if not should_trace:
should_trace = lambda pc: False
while True:
old_pc = mpu.pc
mpu.step(trace=should_trace(mpu.pc))
if completion_criteria(mpu, old_pc):
break
return mpu
def make_completion_criteria(self, success_addr):
def completion_criteria(mpu, old_pc):
return mpu.pc == old_pc or mpu.pc == success_addr
return completion_criteria
def test6502FunctionalTest(self):
success_addr = 0x3399
completion_criteria = self.make_completion_criteria(success_addr)
mpu = self.klausTestCase(
"devices/6502_functional_test.bin", 0x0, 0x400,
completion_criteria
)
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])
)
def test65C02ExtendedOpcodesTest(self):
success_addr = 0x1570
completion_criteria = self.make_completion_criteria(success_addr)
# 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.
mpu = self.klausTestCase(
"devices/65C02_extended_opcodes_test_modified.bin", 0xa, 0x400,
completion_criteria
)
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])
)
# Test Helpers
def _write(self, memory, start_address, bytes):
memory[start_address:start_address + len(bytes)] = bytes
def _make_mpu(self, *args, **kargs):
mpu = self.MPU(*args, **kargs)
if 'memory' not in kargs:
mpu.memory = 0x10000 * [0xAA]
return mpu
def test_suite():
return unittest.findTestCases(sys.modules[__name__])
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')

View File

@ -0,0 +1,82 @@
"""Harness for running Klaus Dormann's 65(c)02 functional test suite
These are quite comprehensive test suites for 65(c)02 implementation
correctness, but they're licensed under the GPL so we cannot include
the binary object code here (this is just the test harness for executing
them in py65).
Obtain the object files from
https://github.com/Klaus2m5/6502_65C02_functional_tests instead.
"""
import os.path
import sys
import unittest
import py65.devices.mpu6502
import py65.devices.mpu65c02
from py65.tests.devices import functional_tests
def run_klaus_dormann_test(mpu_class, filename, load_addr, success_addr,
trace=False):
executor = functional_tests.FunctionalTestExecutor(
mpu_class, filename, load_addr)
if trace:
tracer = executor.always_trace_predicate
else:
tracer = executor.never_trace_predicate
executor.execute(
0x400, executor.address_completion_predicate({success_addr}), tracer)
mpu = executor.mpu
assert mpu.pc == success_addr, (
"%s 0xb=%02x 0xc=%02x 0xd=%02x 0xf=%02x" % (
mpu, mpu.memory[0xb], mpu.memory[0xc], mpu.memory[0xd],
mpu.memory[0xf])
)
class KlausDormannTests(unittest.TestCase):
"""Runs Klaus Dormann's 6502-based test suites"""
def test6502FunctionalTest(self):
filename = "devices/6502_functional_test.bin"
if not os.path.exists(filename):
self.skipTest("%s not available, skipping")
functional_tests.trace_on_assertion(
run_klaus_dormann_test,
py65.devices.mpu6502.MPU,
filename,
load_addr=0x0,
success_addr=0x3399
)
def test65c02ExtendedOpcodeTest(self):
# Modified version of 65C02_extended_opcodes_test that defines
# rkwl_wdc_op = 0 (don't test BBR/BBS instructions, which we do not
# implement) and many of the NOP tests for undefined opcodes which
# are not yet implemented here.
filename = "devices/65C02_extended_opcodes_test_modified.bin"
if not os.path.exists(filename):
self.skipTest("%s not available, skipping")
functional_tests.trace_on_assertion(
run_klaus_dormann_test,
py65.devices.mpu65c02.MPU,
filename,
load_addr=0xa,
success_addr=0x1570
)
def test_suite():
return unittest.findTestCases(sys.modules[__name__])
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')