142 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			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
 |