gigatron/rom/Contrib/psr/multiply/test/test_quarter_square.py
2025-01-28 19:17:01 +03:00

142 lines
4.4 KiB
Python

import math
import os.path
import pathlib
from importlib import reload
from types import SimpleNamespace
from hypothesis import given
from hypothesis import strategies as st
import asm
from gtemu import RAM, Emulator
SRC_DIR = (pathlib.Path(__file__).parent / ".." / "src").resolve()
SCRIPT = SRC_DIR / "quarter-square.asm.py"
def setup_module():
global vars
"""Load the Emulator from the multiplication script"""
reload(asm)
name, _ = os.path.splitext(os.path.basename(SCRIPT))
script_globals = {"__file__": str(SCRIPT.absolute()), "__name__": name}
with SCRIPT.open("rb") as file:
exec(compile(file.read(), SCRIPT, "exec"), script_globals)
Emulator.load_rom_from_asm_module()
vars = SimpleNamespace(**script_globals)
_bytes = st.integers(min_value=0, max_value=255)
@given(value=_bytes)
def test_low_byte_lookup(value):
"""Lookup of the low-byte of a quarter square should work"""
Emulator.Y = asm.symbol("Quarter-squares lookup table") >> 8
Emulator.AC = value
Emulator.next_instruction = "low-byte table entry"
cycles = Emulator.run_to("low-byte return point")
assert int(math.floor((value**2) / 4)) & 0xFF == Emulator.AC
assert vars.cost_of_low_byte_table_entry == cycles
@given(value=_bytes)
def test_high_byte_lookup(value):
"""Lookup of the high-byte of a quarter square should work"""
RAM[vars.high_byte_action] = asm.symbol("high-byte action.store")
Emulator.AC = value
Emulator.Y = asm.symbol("Quarter-squares lookup table") >> 8
Emulator.next_instruction = "table entry"
cycles = Emulator.run_to("high-byte action.store")
assert int(math.floor((value**2) / 4)) >> 8 == Emulator.AC
assert vars.cost_of_high_byte_table_entry == cycles
_seven_bit_integers = st.integers(min_value=0, max_value=127)
@given(a=_seven_bit_integers, b=_seven_bit_integers)
def test_both_byte_lookup(a, b):
"""Lookup of both bytes of a quarter square should work
The multiplication routine actually adds 1 to the result,
so storing it, so check for that.
"""
RAM[vars.a] = a
RAM[vars.b] = b
Emulator.next_instruction = "multiply 7x7"
expected = int(math.floor(((a + b) ** 2) / 4)) + 1
cycles = Emulator.run_to(asm.symbol(".after-first-lookup") + 2)
assert expected == int.from_bytes(
RAM[vars.result : vars.result + 2], "little", signed=False
)
assert cycles == vars.cost_after_first_lookup + 2
assert asm.symbol("Quarter-squares lookup table") >> 8 == Emulator.Y
@given(
a=_seven_bit_integers,
b=_seven_bit_integers,
previous_value=st.integers(min_value=0, max_value=65025),
)
def test_subtract_quarter_square(a, b, previous_value):
"""to done should subtract the"""
RAM[vars.a] = a
RAM[vars.b] = b
RAM[vars.result : vars.result + 2] = (previous_value + 1).to_bytes(
2, "little", signed=False
)
Emulator.Y = asm.symbol("Quarter-squares lookup table") >> 8
Emulator.next_instruction = asm.symbol(".after-first-lookup") + 2
expected = (previous_value - math.floor((a - b) ** 2 / 4)) & 0xFFFF
cycles = Emulator.run_to(asm.symbol("done"))
assert expected == int.from_bytes(
RAM[vars.result : vars.result + 2], "little", signed=False
)
assert asm.symbol("Quarter-squares lookup table") >> 8 == Emulator.Y
assert vars.cost_of_7bit_multiply - vars.cost_after_first_lookup - 2 == cycles
@given(a=_seven_bit_integers, b=_seven_bit_integers)
def test_multiplication_7(a, b):
"""Multiplication of two seven-bit integers should work"""
RAM[vars.a] = a
RAM[vars.b] = b
Emulator.next_instruction = "multiply 7x7"
cycles = Emulator.run_to("done")
result = int.from_bytes(RAM[vars.result : vars.result + 2], "little", signed=False)
assert a * b == result
assert vars.cost_of_7bit_multiply == cycles
@given(a=_bytes, b=_bytes)
def test_multiplication_8(a, b):
"""Multiplication of two eight-bit integers should work"""
Emulator.reset()
RAM[vars.a] = a
RAM[vars.b] = b
Emulator.next_instruction = "multiply 8x8"
expected_saving = (
vars.no_msb_cost_saving
if a < 128 and b < 128
else vars.one_msb_cost_saving
if a < 128 or b < 128
else 0
)
cycles = Emulator.run_to("done")
result = int.from_bytes(RAM[vars.result : vars.result + 2], "little", signed=False)
assert a * b == result
assert vars.cost_of_8bit_multiply - expected_saving == cycles