"""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 == "" assert to_glyph(0x12).text == "" # 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 " # --- 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"