"""Build a ColecoVision/Adam cartridge ROM (.col) holding the Z80 viewer + image. Cartridge maps at $8000. Header: magic $55 $AA (skip the Coleco title screen and run immediately), eight pointer words, then a table of JP vectors -- the BIOS jumps to the one at $800A (the game entry). We point that at our viewer; the RST and interrupt vectors go to a stub (we run with interrupts disabled). """ from __future__ import annotations import struct from . import viewer CART_BASE = 0x8000 CART_SIZE = 0x2000 # 8 KB (viewer + 6912-byte image fit comfortably) VECTORS = 8 # entry + 7 RST/INT stubs CODE_BASE = CART_BASE + 10 + VECTORS * 3 # after magic(2)+ptrs(8)+vectors def build_rom(data: bytes, display: str = "forever", seconds: int = 0) -> bytes: if len(data) != viewer.PATTERN_BYTES + viewer.NCELLS: raise ValueError(f"unexpected image length {len(data)}") vk = dict(display=display, seconds=seconds) code = viewer.build(CODE_BASE, 0, **vk) # pass 1: measure code length data_addr = CODE_BASE + len(code) + 1 # +1 for the stub RET code = viewer.build(CODE_BASE, data_addr, **vk) # pass 2: real data address stub_addr = CODE_BASE + len(code) data_addr = stub_addr + 1 if data_addr - CART_BASE + len(data) > CART_SIZE: raise ValueError("viewer + image exceed the 8KB cartridge") header = bytearray([0x55, 0xAA]) + bytes(8) # magic + 8 pointer bytes vecs = bytearray() targets = [CODE_BASE] + [stub_addr] * (VECTORS - 1) for t in targets: vecs += bytes([0xC3]) + struct.pack(" str: with open(path, "wb") as f: f.write(rom) return path