First public commit.
This commit is contained in:
parent
2a48f52979
commit
4bac9d83ed
288 changed files with 18417 additions and 1076 deletions
53
lenser/apple/dsk.py
Normal file
53
lenser/apple/dsk.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
"""Write a bootable Apple II .dsk (DOS 3.3 sector order, 143360 bytes).
|
||||
|
||||
The .dsk stores sectors in DOS *logical* order, but the Disk II boot ROM reads
|
||||
*physical* sectors, so we place each chunk of the bitmap at the logical slot that
|
||||
maps to the physical sector our loader will read -- making the loader's
|
||||
physical-sequential reads come out contiguous in memory.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
# DOS 3.3 physical-sector -> logical-sector (the .dsk read interleave).
|
||||
PHYS2LOG = [0, 7, 14, 6, 13, 5, 12, 4, 11, 3, 10, 2, 9, 1, 8, 15]
|
||||
|
||||
SECTOR = 256
|
||||
SPT = 16
|
||||
TRACKS = 35
|
||||
DISK_SIZE = TRACKS * SPT * SECTOR # 143360
|
||||
|
||||
|
||||
class DskError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
def _offset(track: int, phys_sector: int) -> int:
|
||||
return (track * SPT + PHYS2LOG[phys_sector]) * SECTOR
|
||||
|
||||
|
||||
def build_boot_dsk(boot: bytes, payload: bytes) -> bytes:
|
||||
"""boot = assembled boot0 (origin $0800); payload = data pages the loader reads
|
||||
in order: track 0 sectors 1-15, then whole tracks 1, 2, ... (physical order)."""
|
||||
if len(payload) % SECTOR:
|
||||
raise DskError("payload must be a whole number of 256-byte sectors")
|
||||
npages = len(payload) // SECTOR
|
||||
disk = bytearray(DISK_SIZE)
|
||||
disk[0:SECTOR] = (bytes(boot) + bytes(SECTOR))[:SECTOR] # boot0 at T0 phys 0
|
||||
|
||||
assigns = [(0, p) for p in range(1, 16)] # track 0 (skip boot)
|
||||
track = 1
|
||||
while len(assigns) < npages:
|
||||
assigns += [(track, p) for p in range(16)]
|
||||
track += 1
|
||||
if track > TRACKS:
|
||||
raise DskError("payload exceeds disk capacity")
|
||||
for page, (trk, phys) in enumerate(assigns[:npages]):
|
||||
off = _offset(trk, phys)
|
||||
disk[off:off + SECTOR] = payload[page * SECTOR:(page + 1) * SECTOR]
|
||||
return bytes(disk)
|
||||
|
||||
|
||||
def write_dsk(path: str, data: bytes) -> str:
|
||||
with open(path, "wb") as f:
|
||||
f.write(data)
|
||||
return path
|
||||
Loading…
Add table
Add a link
Reference in a new issue