107 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			107 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""Tests for the implementation of SYS_KaratsubaPrepare_54"""
 | 
						|
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
 | 
						|
 | 
						|
MAX_CYCLES = 54
 | 
						|
 | 
						|
 | 
						|
SYS_DIR = (pathlib.Path(__file__).parent / ".." / "sys").resolve()
 | 
						|
SCRIPT = SYS_DIR / "ROM.asm.py"
 | 
						|
 | 
						|
 | 
						|
def setup_module():
 | 
						|
    global vars
 | 
						|
    """Load the Emulator from the ROM 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)
 | 
						|
 | 
						|
 | 
						|
def setup_function():
 | 
						|
    RAM[vars.sysFn : vars.sysFn + 2] = asm.symbol("SYS_KaratsubaPrepare_54").to_bytes(
 | 
						|
        2, "little"
 | 
						|
    )
 | 
						|
    RAM[vars.vTicks] = 75
 | 
						|
    Emulator.next_instruction = "SYS"
 | 
						|
    Emulator.AC = 270 - max(14, MAX_CYCLES // 2)
 | 
						|
 | 
						|
 | 
						|
def _sign_extend(byte_):
 | 
						|
    if byte_ & 0x80:
 | 
						|
        return ~0xFF | byte_
 | 
						|
    return byte_
 | 
						|
 | 
						|
 | 
						|
_unsigned_words = st.integers(min_value=0, max_value=1 << 16 - 1)
 | 
						|
 | 
						|
 | 
						|
@given(a=_unsigned_words, b=_unsigned_words)
 | 
						|
def test_karatsuba_prepare(a, b):
 | 
						|
    setup_function()  # Because Hypothesis calls us repeatedly, and the function under test changes sysFn etc.
 | 
						|
    RAM[vars.sysArgs : vars.sysArgs + 2] = a.to_bytes(2, "little", signed=False)
 | 
						|
    RAM[vars.vAC : vars.vAC + 2] = b.to_bytes(2, "little", signed=False)
 | 
						|
 | 
						|
    cycles = 10  # 9 Cycles from previous next, plus one, because, I dunno, but you do.
 | 
						|
    cycles += Emulator.run_to("NEXT")
 | 
						|
 | 
						|
    assert abs((a & 0xFF) - (a >> 8)) == RAM[vars.sysArgs + 0]
 | 
						|
    assert abs((b >> 8) - (b & 0xFF)) == RAM[vars.sysArgs + 1]
 | 
						|
    assert ((a >> 8) > (a & 0xFF)) ^ ((b & 0xFF) > (b >> 8)) == RAM[vars.vAC]
 | 
						|
    assert int.from_bytes(
 | 
						|
        RAM[vars.sysFn : vars.sysFn + 2], "little", signed=False
 | 
						|
    ) == asm.symbol("SYS_MultiplyBytes_120")
 | 
						|
    assert RAM[vars.vAC] == RAM[vars.vAC + 1]
 | 
						|
    assert cycles == MAX_CYCLES
 | 
						|
    assert cycles == _sign_extend(Emulator.AC) * -2
 | 
						|
 | 
						|
 | 
						|
def test_timing_both_overflow():
 | 
						|
    """Follow the routine through, checking the timing comments
 | 
						|
 | 
						|
    This follows the case where both subtractions result in a negative number:
 | 
						|
    the high byte of vAC is bigger than the low, and
 | 
						|
    the low byte of sysArgs[0:2] is bigger than the high.
 | 
						|
    """
 | 
						|
    RAM[vars.sysArgs : vars.sysArgs + 2] = 0x00FF.to_bytes(2, "little", signed=False)
 | 
						|
    RAM[vars.vAC : vars.vAC + 2] = 0xFF00.to_bytes(2, "little", signed=False)
 | 
						|
    # fmt: off
 | 
						|
    cycles = 9  # On entry to SYS, 9 cycles have already elapsed
 | 
						|
    cycles += Emulator.run_to("SYS_KaratsubaPrepare_54");           assert 14 == cycles        # noqa: E702, E241, E272
 | 
						|
    cycles += Emulator.run_to("sysKaratsubaPrepare#18");            assert 17 == cycles        # noqa: E702, E241, E272
 | 
						|
    cycles += Emulator.run_to("sysKaratsubaPrepare#21");            assert 20 == cycles        # noqa: E702, E241, E272
 | 
						|
    cycles += Emulator.run_to("sysKaratsubaPrepare#35");            assert 34 == cycles        # noqa: E702, E241, E272
 | 
						|
    cycles += Emulator.run_to("REENTER");                           assert 51 == cycles        # noqa: E702, E241, E272
 | 
						|
    cycles += Emulator.run_to("NEXT");                              assert 53 == cycles        # noqa: E702, E241, E272
 | 
						|
    # fmt: on
 | 
						|
 | 
						|
 | 
						|
def test_timing_neither_overflow():
 | 
						|
    """Follow the routine through, checking the timing comments
 | 
						|
 | 
						|
    This follows the case where both subtractions result in a positive number:
 | 
						|
    the low byte of vAC is bigger than the high, and
 | 
						|
    the high byte of sysArgs[0:2] is bigger than the low.
 | 
						|
    """
 | 
						|
    RAM[vars.sysArgs : vars.sysArgs + 2] = 0xFF00.to_bytes(2, "little", signed=False)
 | 
						|
    RAM[vars.vAC : vars.vAC + 2] = 0x00FF.to_bytes(2, "little", signed=False)
 | 
						|
    # fmt: off
 | 
						|
    cycles = 9  # On entry to SYS, 9 cycles have already elapsed
 | 
						|
    cycles += Emulator.run_to("SYS_KaratsubaPrepare_54");           assert 14 == cycles        # noqa: E702, E241, E272
 | 
						|
    cycles += Emulator.run_to("sysKaratsubaPrepare#21");            assert 20 == cycles        # noqa: E702, E241, E272
 | 
						|
    cycles += Emulator.run_to("sysKaratsubaPrepare#35");            assert 34 == cycles        # noqa: E702, E241, E272
 | 
						|
    cycles += Emulator.run_to("REENTER");                           assert 51 == cycles        # noqa: E702, E241, E272
 | 
						|
    cycles += Emulator.run_to("NEXT");                              assert 53 == cycles        # noqa: E702, E241, E272
 | 
						|
    # fmt: on
 |