102 lines
3.2 KiB
Python
102 lines
3.2 KiB
Python
import os.path
|
|
import pathlib
|
|
from importlib import reload
|
|
from types import SimpleNamespace
|
|
|
|
import gcl0x as gcl
|
|
from hypothesis import given
|
|
from hypothesis import strategies as st
|
|
|
|
import asm
|
|
from gtemu import RAM, Emulator
|
|
|
|
ROOT = (pathlib.Path(__file__).parent / "..").resolve()
|
|
SCRIPT = ROOT / "sys" / "ROM.asm.py"
|
|
GCL = ROOT / "mandelbrot" / "Mandelbrot.gcl"
|
|
|
|
|
|
def setup_module():
|
|
global vars, symbol_table, execution_address
|
|
"""Load the Emulator from the ROM script and Mandelbrot
|
|
"""
|
|
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)
|
|
|
|
# This sequence of calls is roughly taken from compilegcl.py
|
|
old_rom_size = asm._romSize
|
|
program = gcl.Program("Mandelbrot", forRom=False)
|
|
user_code = asm.symbol("userCode")
|
|
user_vars = asm.symbol("userVars")
|
|
program.org(user_code)
|
|
asm.align(1)
|
|
asm.zpReset(user_vars)
|
|
with GCL.open("r", encoding="utf-8") as fp:
|
|
for line in fp.readlines():
|
|
program.line(line)
|
|
program.end()
|
|
asm.end()
|
|
|
|
# Copy the resulting data straight into RAM, in the appropriate blocks
|
|
data = asm.getRom1()[old_rom_size:]
|
|
index, more_data = 0, bool(data)
|
|
while more_data:
|
|
start_address = int.from_bytes(data[index : index + 2], "big", signed=False)
|
|
index += 2
|
|
size = data[index] or 256
|
|
index += 1
|
|
chunk = data[index : index + size]
|
|
index += size
|
|
RAM[start_address : start_address + len(chunk)] = chunk
|
|
more_data = data[index]
|
|
execution_address = program.execute
|
|
symbol_table = program.vars
|
|
|
|
|
|
def _prepare_for_vcpu_execution_at(execution_address):
|
|
Emulator.AC = 127
|
|
Emulator.next_instruction = "ENTER"
|
|
RAM[vars.vReturn] = vars.videoZ & 0xFF
|
|
RAM[vars.vCpuSelect] = Emulator.next_instruction >> 8
|
|
|
|
Emulator.vLR = execution_address
|
|
Emulator.vPC = execution_address & 0xFF00 | (execution_address - 2) & 0xFF
|
|
|
|
|
|
def _call_vcpu_function(variable):
|
|
return_point = Emulator.vPC & 0xFF00 | (Emulator.vPC + 2) & 0xFF
|
|
Emulator.AC = symbol_table[variable]
|
|
Emulator.next_instruction = "CALL"
|
|
Emulator.step_vcpu() # Execute CALL
|
|
Emulator.run_vcpu_to(return_point)
|
|
|
|
|
|
def _write_word(address, value, *, signed):
|
|
RAM[address : address + 2] = value.to_bytes(2, "little", signed=signed)
|
|
|
|
|
|
def _read_word(address, *, signed):
|
|
return int.from_bytes(RAM[address : address + 2], "little", signed=signed)
|
|
|
|
|
|
_signed_ints_with_12bit_range = st.integers(
|
|
min_value=-(1 << 11), max_value=(1 << 11) - 1
|
|
)
|
|
|
|
|
|
@given(a=_signed_ints_with_12bit_range, b=_signed_ints_with_12bit_range)
|
|
def test_MulShift8(a, b):
|
|
_prepare_for_vcpu_execution_at(execution_address)
|
|
Emulator.run_vcpu_to(0x300) # Run the first page, so the variable is defined
|
|
_write_word(vars.sysFn, asm.symbol("SYS_MultiplyBytes_120"), signed=False)
|
|
_write_word(symbol_table["A"], a, signed=True)
|
|
_write_word(symbol_table["B"], b, signed=True)
|
|
|
|
_call_vcpu_function("MulShift8")
|
|
|
|
assert int(a * b / 256) == _read_word(vars.vAC, signed=True)
|