First public commit.
This commit is contained in:
parent
2a48f52979
commit
4bac9d83ed
288 changed files with 18417 additions and 1076 deletions
97
lenser/apple/viewer/assemble.py
Normal file
97
lenser/apple/viewer/assemble.py
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
"""Assemble the Apple II 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__))
|
||||
SOURCES = {"hgr": "hgr.s", "dhgr": "dhgr.s"}
|
||||
_cache: dict[tuple, bytes] = {}
|
||||
|
||||
# How long the viewer holds the picture (see apple/viewer/awyt.i).
|
||||
WAIT_MODES = {"forever": 0, "key": 1, "seconds": 2}
|
||||
SS_WAITMODE = {"key": 1, "seconds": 2, "both": 3}
|
||||
|
||||
|
||||
def build_slideshow_stub(n_images: int, advance: str = "both", seconds: int = 10,
|
||||
loop: bool = True) -> bytes:
|
||||
"""Assemble the Apple HGR slideshow boot loader (one 256-byte boot sector).
|
||||
|
||||
Reads NIMAGES * 32 sectors into the $4000 buffer and cycles them; must fit a
|
||||
single boot sector since the Disk II ROM only loads sector 0.
|
||||
"""
|
||||
import shutil as _sh
|
||||
if not _sh.which("xa"):
|
||||
raise AssemblerError("The 'xa' assembler was not found on PATH.")
|
||||
end_page = 0x40 + n_images * 0x20
|
||||
wrapper = (f"#define WAITMODE {SS_WAITMODE[advance]}\n"
|
||||
f"#define WAITSECS {max(0, min(255, int(seconds)))}\n"
|
||||
f"#define NIMAGES {n_images}\n"
|
||||
f"#define LOOPFLAG {1 if loop else 0}\n"
|
||||
f"#define ENDPAGE ${end_page:02X}\n"
|
||||
'#include "slideshow.s"\n')
|
||||
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", "-o", out, os.path.basename(wrap)],
|
||||
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:
|
||||
raw = f.read()
|
||||
finally:
|
||||
os.unlink(wrap)
|
||||
if len(raw) > 256:
|
||||
raise AssemblerError(
|
||||
f"Apple slideshow boot loader is {len(raw)} bytes, over the 256-byte "
|
||||
"boot sector")
|
||||
return raw
|
||||
|
||||
|
||||
class AssemblerError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
def have_xa() -> bool:
|
||||
return shutil.which("xa") is not None
|
||||
|
||||
|
||||
def assemble_stub(viewer_key: str, display: str = "forever", seconds: int = 0) -> bytes:
|
||||
waitmode = WAIT_MODES.get(display, 0)
|
||||
secs = max(0, min(255, int(seconds))) # 8-bit delay counter
|
||||
key = (viewer_key, waitmode, secs)
|
||||
if key in _cache:
|
||||
return _cache[key]
|
||||
if not have_xa():
|
||||
raise AssemblerError("The 'xa' assembler was not found on PATH.")
|
||||
if not os.path.exists(os.path.join(VIEWER_DIR, SOURCES[viewer_key])):
|
||||
raise AssemblerError(f"viewer source missing: {SOURCES[viewer_key]}")
|
||||
|
||||
# Wrapper sets options then includes the real source; run from VIEWER_DIR so
|
||||
# the source's #include "awyt.i" resolves (xa looks relative to cwd).
|
||||
wrapper = (f"#define WAITMODE {waitmode}\n"
|
||||
f"#define WAITSECS {secs}\n"
|
||||
f'#include "{SOURCES[viewer_key]}"\n')
|
||||
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", "-o", out, os.path.basename(wrap)],
|
||||
capture_output=True, text=True, cwd=VIEWER_DIR)
|
||||
if proc.returncode != 0:
|
||||
raise AssemblerError(
|
||||
f"xa failed for {viewer_key}:\n{proc.stdout}{proc.stderr}")
|
||||
with open(out, "rb") as f:
|
||||
raw = f.read()
|
||||
finally:
|
||||
os.unlink(wrap)
|
||||
_cache[key] = raw
|
||||
return raw
|
||||
Loading…
Add table
Add a link
Reference in a new issue