82 lines
3 KiB
Python
82 lines
3 KiB
Python
"""Assemble the IIGS SHR boot/viewer with `xa` (origin $0800, raw bytes)."""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import tempfile
|
|
|
|
VIEWER_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
|
|
class AssemblerError(RuntimeError):
|
|
pass
|
|
|
|
|
|
def have_xa() -> bool:
|
|
return shutil.which("xa") is not None
|
|
|
|
|
|
def assemble_boot() -> bytes:
|
|
"""Return the boot0 sector (<=256 bytes) that loads the SHR block + shows it."""
|
|
if not have_xa():
|
|
raise AssemblerError("The 'xa' assembler was not found on PATH.")
|
|
with tempfile.TemporaryDirectory() as td:
|
|
out = os.path.join(td, "v.bin")
|
|
proc = subprocess.run(["xa", "-w", "-o", out, "iigs.s"],
|
|
capture_output=True, text=True, cwd=VIEWER_DIR)
|
|
if proc.returncode != 0:
|
|
raise AssemblerError(f"xa failed:\n{proc.stdout}{proc.stderr}")
|
|
with open(out, "rb") as f:
|
|
boot = f.read()
|
|
if len(boot) > 256:
|
|
raise AssemblerError(f"boot sector is {len(boot)} bytes (>256)")
|
|
return boot
|
|
|
|
|
|
SS_WAITMODE = {"key": 1, "seconds": 2, "both": 3}
|
|
|
|
|
|
def _xa_wrapper(wrapper: str, what: str) -> bytes:
|
|
if not have_xa():
|
|
raise AssemblerError("The 'xa' assembler was not found on PATH.")
|
|
with tempfile.TemporaryDirectory() as td:
|
|
out = os.path.join(td, "v.bin")
|
|
fd, wrap = tempfile.mkstemp(suffix=".s", prefix="_wrap_", dir=VIEWER_DIR)
|
|
try:
|
|
with os.fdopen(fd, "w") as f:
|
|
f.write(wrapper)
|
|
proc = subprocess.run(["xa", "-w", "-o", out, os.path.basename(wrap)],
|
|
capture_output=True, text=True, cwd=VIEWER_DIR)
|
|
if proc.returncode != 0:
|
|
raise AssemblerError(f"xa failed for {what}:\n{proc.stdout}{proc.stderr}")
|
|
with open(out, "rb") as f:
|
|
return f.read()
|
|
finally:
|
|
os.unlink(wrap)
|
|
|
|
|
|
def build_slideshow(n_images: int, advance: str = "both", seconds: int = 10,
|
|
loop: bool = True, video: str = "ntsc"):
|
|
"""Assemble the two-stage IIGS SHR slideshow.
|
|
|
|
Returns (boot_sector<=256B, stage2_bytes, stage2_pages). The boot reads
|
|
stage2 (stage2_pages sectors) into $0900, then the images into bank-0 $2000
|
|
and banks them; stage2 cycles them onto the SHR screen.
|
|
"""
|
|
speed = 8 if video != "pal" else 7 # ~1s delay outer count (fast IIGS)
|
|
stage2 = _xa_wrapper(
|
|
f"#define WAITMODE {SS_WAITMODE[advance]}\n"
|
|
f"#define WAITSECS {max(0, min(255, int(seconds)))}\n"
|
|
f"#define SPEED {speed}\n"
|
|
f"#define NIMAGES {n_images}\n"
|
|
f"#define LOOPFLAG {1 if loop else 0}\n"
|
|
'#include "slideshow_stage2.s"\n', "iigs stage2")
|
|
s2_pages = max(1, (len(stage2) + 255) // 256)
|
|
boot = _xa_wrapper(
|
|
f"#define NIMAGES {n_images}\n"
|
|
f"#define S2END ${0x09 + s2_pages:02X}\n"
|
|
'#include "slideshow_boot.s"\n', "iigs boot")
|
|
if len(boot) > 256:
|
|
raise AssemblerError(f"IIGS slideshow boot is {len(boot)} bytes (>256)")
|
|
return boot, stage2, s2_pages
|