57 lines
2.1 KiB
Python
57 lines
2.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Run the C64 Space Taxi music player and log every SID write per frame.
|
|
|
|
The player lives in the TITLE memory image at $CB00-$CFFF (its own relocated
|
|
copy; the $7500 copy is gameplay-side). Rather than hand-decode its byte
|
|
code, drive the real routines under cpu6502.py -- the same trick that
|
|
extracted the speech:
|
|
$CB00 select song ($CB01 = song number) and init (hooks the IRQ vector)
|
|
$CF52 the IRQ body: first frame calls start ($CC28), then ticks the three
|
|
voices via $CC6C, then JMPs to the KERNAL IRQ ($EA31) which is what
|
|
advances the jiffy clock $A2. We poke an RTS at $EA31 and bump $A2
|
|
ourselves, once per frame.
|
|
A song is over when all three voice stream pointers' high bytes are zero
|
|
(that is exactly what the game's play-and-wait loop at $44D5 spins on).
|
|
"""
|
|
import json, sys
|
|
sys.path.insert(0, "/home/scott/claude/joeylib/stuff/spacetaxi")
|
|
from cpu6502 import Cpu6502
|
|
|
|
RAM = bytearray(open(sys.argv[1], "rb").read()[:65536])
|
|
OUT = sys.argv[2]
|
|
MAX_FRAMES = 4000
|
|
|
|
|
|
class SidTrap(Cpu6502):
|
|
def __init__(self, ram):
|
|
super().__init__(ram)
|
|
self.log = []
|
|
self.frame = 0
|
|
|
|
def write8(self, addr, val):
|
|
a = addr & 0xFFFF
|
|
if 0xD400 <= a <= 0xD418:
|
|
self.log.append((self.frame, a - 0xD400, val & 0xFF))
|
|
super().write8(addr, val)
|
|
|
|
|
|
songs = {}
|
|
for song in range(0, 9):
|
|
ram = bytearray(RAM)
|
|
ram[0xEA31] = 0x60 # KERNAL IRQ tail -> RTS
|
|
cpu = SidTrap(ram)
|
|
cpu.call(0xCC00) # full reset first
|
|
ram[0xCB01] = song
|
|
cpu.call(0xCB00) # select + arm
|
|
for frame in range(MAX_FRAMES):
|
|
cpu.frame = frame
|
|
ram[0xA2] = (ram[0xA2] + 1) & 0xFF
|
|
cpu.call(0xCF52)
|
|
if ram[0xCB81] == 0 and ram[0xCB88] == 0 and ram[0xCB8F] == 0:
|
|
break
|
|
songs[song] = {"frames": frame + 1, "writes": cpu.log,
|
|
"ptr": ram[0xCB40 + song * 2] | (ram[0xCB41 + song * 2] << 8)}
|
|
print(f"song {song}: ptr=${songs[song]['ptr']:04X} frames={frame+1} "
|
|
f"({(frame+1)/50:.1f}s PAL) sidWrites={len(cpu.log)}")
|
|
|
|
json.dump(songs, open(OUT, "w"))
|