OXYGENHUB
◆ main enhanced-x16-emulator / tests / pt_convert.py
3.6 KiB · Python · last changed chore(): initial commit 25 days ago source blame history raw
1
#!/usr/bin/env python3
2
"""Convert SingleStepTests JSON into a binary fixture. Format 2.
3
4
Avoids a JSON parser in C. Run once; the C runner walks the output with fread().
5
6
One format covers both suites. The 65C02 has no bank registers and no emulation
7
bit, so those fields carry the values a 65C816 in emulation mode would hold, and
8
the cpu field says which core to reset into.
9
10
Layout, all little-endian:
11
12
    magic 'X16P', u32 version = 2, u32 case count
13
    per case:
14
        u8  opcode
15
        u8  cpu           0 = 65C02, 1 = 65C816
16
        initial state, then u16 ram count and [u32 addr, u8 value] each
17
        final state,   then u16 ram count and [u32 addr, u8 value] each
18
        u16 cycle count, then [u32 addr, u8 value, u8 flags] each
19
20
    state: u16 pc, u16 s, u16 a, u16 x, u16 y, u8 p, u8 e, u8 dbr, u8 pbr, u16 d
21
22
Cycle flags, from the 65C816 suite's eight-character signal string:
23
24
    0x01 VDA   0x02 VPA   0x04 VPB   0x08 write
25
    0x10 E     0x20 M     0x40 X     0x80 MLB
26
27
The 65C02 suite reports only read or write, so its cycles carry VDA and VPA set
28
and the rest from the read/write word alone.
29
"""
30
31
import struct
32
33
MAGIC = b"X16P"
34
VERSION = 2
35
36
CPU_65C02 = 0
37
CPU_65816 = 1
38
39
F_VDA, F_VPA, F_VPB, F_WRITE = 0x01, 0x02, 0x04, 0x08
40
F_E, F_M, F_X, F_MLB = 0x10, 0x20, 0x40, 0x80
41
42
43
def pack_state(st, emulation_defaults):
44
    e = st.get("e", 1 if emulation_defaults else 0)
45
    return struct.pack(
46
        "<HHHHHBBBBH",
47
        st["pc"] & 0xFFFF,
48
        st["s"] & 0xFFFF,
49
        st["a"] & 0xFFFF,
50
        st["x"] & 0xFFFF,
51
        st["y"] & 0xFFFF,
52
        st["p"] & 0xFF,
53
        e & 0xFF,
54
        st.get("dbr", 0) & 0xFF,
55
        st.get("pbr", 0) & 0xFF,
56
        st.get("d", 0) & 0xFFFF,
57
    )
58
59
60
def pack_ram(ram):
61
    out = struct.pack("<H", len(ram))
62
    for addr, val in ram:
63
        out += struct.pack("<IB", (addr or 0) & 0xFFFFFF, (val or 0) & 0xFF)
64
    return out
65
66
67
def pack_cycle_flags(word):
68
    """Third element of a cycles entry, either 'read'/'write' or eight signals."""
69
    if word in ("read", "write"):
70
        flags = F_VDA | F_VPA
71
        if word == "write":
72
            flags |= F_WRITE
73
        return flags
74
    # 'dp-remx-': VDA, VPA, VPB, R/W, E, M, X, MLB, each its letter or '-'.
75
    bits = (F_VDA, F_VPA, F_VPB, 0, F_E, F_M, F_X, F_MLB)
76
    flags = 0
77
    for i, bit in enumerate(bits):
78
        if word[i] != "-":
79
            flags |= bit
80
    if word[3] == "w":
81
        flags |= F_WRITE
82
    return flags
83
84
85
def pack_cycles(cycles):
86
    out = struct.pack("<H", len(cycles))
87
    for entry in cycles:
88
        addr, val, word = entry[0], entry[1], entry[2]
89
        # Both can be null: an internal cycle drives nothing meaningful onto
90
        # the bus. VDA and VPA in the flags are what says the address is real,
91
        # so a placeholder zero here is not mistaken for address zero.
92
        out += struct.pack("<IBB", (addr or 0) & 0xFFFFFF, (val or 0) & 0xFF,
93
                           pack_cycle_flags(word))
94
    return out
95
96
97
def pack_case(opcode, cpu, case):
98
    emulation_defaults = (cpu == CPU_65C02)
99
    return (
100
        struct.pack("<BB", opcode, cpu)
101
        + pack_state(case["initial"], emulation_defaults)
102
        + pack_ram(case["initial"]["ram"])
103
        + pack_state(case["final"], emulation_defaults)
104
        + pack_ram(case["final"]["ram"])
105
        + pack_cycles(case.get("cycles", []))
106
    )
107
108
109
def write_fixture(path, entries):
110
    """Write (opcode, cpu, case) triples. Order is preserved."""
111
    blobs = [pack_case(op, cpu, c) for op, cpu, c in entries]
112
    with open(path, "wb") as fh:
113
        fh.write(MAGIC + struct.pack("<II", VERSION, len(blobs)))
114
        for b in blobs:
115
            fh.write(b)
116
    return len(blobs)