comodore-iec-emu/tests/test_decode.py
2026-06-18 22:25:42 +02:00

117 lines
3.8 KiB
Python

"""Command decoding, PETSCII glyphs, and data-byte rendering (PLAN.md Phase 0)."""
from __future__ import annotations
import pytest
from iecpoc import decode, device
from iecpoc.petscii import CAT_CONTROL, CAT_GRAPHIC, CAT_PRINTABLE, to_glyph
# --- command decoding -------------------------------------------------------
@pytest.mark.parametrize(
"value, mnemonic, primary, secondary",
[
(0x24, decode.LISTEN, 4, None), # LISTEN 4
(0x20, decode.LISTEN, 0, None), # LISTEN 0
(0x3E, decode.LISTEN, 30, None), # LISTEN 30 (top of range)
(0x3F, decode.UNLISTEN, None, None),
(0x48, decode.TALK, 8, None), # TALK 8
(0x5F, decode.UNTALK, None, None),
(0x60, decode.SECOND, None, 0), # DATA/reopen SA=0
(0x6F, decode.SECOND, None, 15),
(0xE0, decode.CLOSE, None, 0),
(0xEF, decode.CLOSE, None, 15),
(0xF0, decode.OPEN, None, 0),
(0xFF, decode.OPEN, None, 15),
],
)
def test_decode_command(value, mnemonic, primary, secondary):
cmd = decode.decode_command(value)
assert cmd.value == value
assert cmd.mnemonic == mnemonic
assert cmd.primary == primary
assert cmd.secondary == secondary
def test_targets_address():
assert decode.decode_command(0x24).targets(4) is True
assert decode.decode_command(0x24).targets(8) is False
# UNLISTEN has no primary address, targets nobody specifically
assert decode.decode_command(0x3F).targets(4) is False
def test_describe():
assert decode.decode_command(0x24).describe() == "LISTEN 4"
assert decode.decode_command(0x3F).describe() == "UNLISTEN"
assert decode.decode_command(0xF0).describe() == "OPEN SA=0"
assert decode.decode_command(0xE0).describe() == "CLOSE SA=0"
def test_decode_command_range_check():
with pytest.raises(ValueError):
decode.decode_command(0x100)
# --- PETSCII ----------------------------------------------------------------
def test_petscii_letters_and_digits():
for ch in "HELLO WORLD0123456789":
g = to_glyph(ord(ch))
assert g.category == CAT_PRINTABLE
assert g.text == ch
def test_petscii_commodore_glyphs():
assert to_glyph(0x5C).text == "£"
assert to_glyph(0x5E).text == ""
assert to_glyph(0x5F).text == ""
def test_petscii_control_codes():
assert to_glyph(0x0D) == to_glyph(0x0D)
cr = to_glyph(0x0D)
assert cr.category == CAT_CONTROL
assert cr.text == "<CR>"
assert to_glyph(0x12).text == "<RVS-ON>"
# unknown control code falls back to the hex form
assert to_glyph(0x07).category == CAT_CONTROL
assert to_glyph(0x07).text == "<$07>"
def test_petscii_graphics_render_as_dot():
g = to_glyph(0x70) # graphics range in the upper-case set
assert g.category == CAT_GRAPHIC
assert g.text == "."
assert not g.printable
def test_petscii_range_check():
with pytest.raises(ValueError):
to_glyph(256)
# --- data-byte rendering ----------------------------------------------------
def test_describe_data_printable():
assert decode.describe_data(0x48) == "$48 'H'"
def test_describe_data_control():
assert decode.describe_data(0x0D) == "$0D <CR>"
# --- event description ------------------------------------------------------
def test_describe_event_atn_released_addressed():
rec = device.IecRecord(device.KIND_EVENT, device.EV_ATN_RELEASED,
device.FLAG_ADDRESSED, 0)
assert decode.describe_event(rec) == "released -> LISTENER"
def test_describe_event_atn_released_not_addressed():
rec = device.IecRecord(device.KIND_EVENT, device.EV_ATN_RELEASED, 0, 0)
assert decode.describe_event(rec) == "released -> not addressed -> IDLE"
def test_describe_event_idle():
rec = device.IecRecord(device.KIND_EVENT, device.EV_IDLE, 0, 0)
assert decode.describe_event(rec) == "waiting for ATN"