8.2 KiB
Space Taxi port plan
Roadmap for bringing the JoeyLib port to functional parity with the C64 original. Sequenced so each phase delivers something testable on its own and unblocks the next. Each phase ends with a user-test checkpoint -- regressions must be caught before moving on.
Workflow per subsystem
Same loop every time, so it stays cheap to iterate:
- Find -- grep the live RAM dump for relevant register writes
(
$DC00,$D015,$D400-$D418,$07F8-$07FF, etc.) or usecallGraph.pyto find routines touching specific addresses. - Read -- pull the routine into a window via
disLive.py <dump> <out> <start> <end>, study the annotated listing. - Document -- append behavior summary to
MECHANICS.mdin plain English. Add new labels/vars tostuff/spacetaxi/labels.txt. - Reannotate --
python3 disLive.py mem0000-level1.bin live-level1.lstto regenerate the (annotated) listing from the dump + labels.txt in one step. The .lst file is the only readable form -- labels are baked in directly. - Port -- update the matching JoeyLib C file
(
stEngine.c,stPassenger.c,stRender.c,stAudio.c,stLevel.c,spacetaxi.c). - Build + ship --
make -f make/dos.mk EXAMPLE=spacetaxifirst, then once stable also amiga / atarist / iigs. - User test -- run
scripts/run-dos.sh staxi, report behavior. Do not start the next phase before this.
Phase 0 -- foundation (DONE)
- Disassembly toolchain in
stuff/spacetaxi/ - Full live-RAM listing + annotated variant
MECHANICS.mdbaseline- Asset pipeline (
tools/assetbake/assetbake.py, extractor realigned) - Bugfixes: sprite size in tile units, palette clobber, tile-bank load clamp, fire-not-thrust, palette/sheet collision
- Template-velocity physics in
stEngine.c
Phase 1 -- physics + collision (NEXT)
Make the cab fly correctly. Highest impact: nothing else works until the player can hover, land, and crash.
| Task | Investigate | Update |
|---|---|---|
| 1.1 | Trace $7D95/96 gravity word writers -- when is gravity on? |
stEngine.c: gate gravity on the same conditions |
| 1.2 | Find writers of $721D (player state byte). High bit = "dying". What sets it? |
stEngine.c: crash trigger logic |
| 1.3 | Trace phase-2 handler at $6B24 -- this is what runs after $6A72 advances. Likely the crash / fall-to-ground sequence |
New stEngine.c crash-anim path |
| 1.4 | Find pad-vs-wall predicate. Hardware $D01F says "we hit bg"; some code looks at char codes near taxi feet to decide PAD vs WALL |
stLevel.c: pad detection by tile code |
| 1.5 | Confirm screen-edge wrap at $6AED -- when X-velocity carries the taxi off the right edge, does it wrap or stop? |
stEngine.c: edge behavior |
Checkpoint: cab flies smoothly, can land on a pad, crashes hard.
Phase 2 -- visuals + animation
Make the screen match the original's look frame-by-frame.
| Task | Investigate | Update |
|---|---|---|
| 2.1 | Confirm taxi has one cel only. The 2 writes to $07F8 (taxi sprite ptr) -- are they per-level (color swap) or per-frame? Read those sites |
stRender.c: reduce ST_TAXI_CEL_COUNT to 1, kill thrustFrame |
| 2.2 | What renders the engine flame under the taxi during thrust? Either a second sprite or a tile overwrite | stRender.c: flame overlay |
| 2.3 | Passenger walk: trace updates to passenger sprite Y/X in the sprite state table (NOT $D004-$D00D; the table memory) |
stPassenger.c: walk frame timing |
| 2.4 | Title-screen layout: the C64 title positions logo / credits / instructions at specific char rows. The extractor pulls the screen layout; check the .txt against the disassembly's title-init routine to confirm we render the same layout | assets/levels/title.txt, stRender.c title overlay |
Checkpoint: the title screen, gameplay, and game-over screens each look like the C64.
Phase 3 -- audio
Authentic feel needs the SID engine. JoeyLib audio is per-platform, so the abstraction is "SFX event id" -- the SID details only land in the DOS / Amiga / ST tone-generator backend.
| Task | Investigate | Update |
|---|---|---|
| 3.1 | Music engine: voice 1+2 melodic table. Find where tunes are stored, how the row pointer advances, how note-to-freq lookup works | stAudio.c: music playback state machine |
| 3.2 | Thrust SFX: confirmed voice 1 freq sweep from $721B. Find writer of $721B (start of thrust) and what triggers it to stop |
stAudioSfxThrust envelope |
| 3.3 | Crash SFX: voice 3 noise burst. LDA #$81 STA $D412 ... LDA #$80. Find duration / pitch envelope |
stAudioSfxCrash |
| 3.4 | Land SFX: a successful landing must trigger some audible cue. Trace from collision dispatch when sprite-bg collision is at low vy | stAudioSfxLand |
| 3.5 | Door / passenger SFX: passenger enters/exits the cab on a pad. Probably also voice 3 noise or a brief tone | new SFX |
Checkpoint: thrust, land, crash, and at least one music loop all play audibly on DOS. Approximate timbres OK; pitch + envelope should roughly match.
Phase 4 -- game logic
Make the game progress. State machine, scoring, lives.
| Task | Investigate | Update |
|---|---|---|
| 4.1 | Passenger spawn: when does fare N+1 appear after fare N is delivered? Look for writes to the passenger-active flags in the state table | stPassenger.c |
| 4.2 | Patience timer: a passenger has a countdown. Find the per-frame decrement and what happens at zero (passenger leaves? lose pad?) | stPassenger.c |
| 4.3 | Scoring: base fare from $7D..., tip math for time-on-board. BCD arithmetic on a 4-byte score |
stHud.c, score storage |
| 4.4 | Level transitions: $7215 == $19 is the "level complete" sentinel. What writes that, and how does the game pick the next level? |
spacetaxi.c main switch |
| 4.5 | Lives: where is the lives counter? How does the game decide game-over? | stEngine.c crash branch, spacetaxi.c |
Checkpoint: can complete a level, advance, lose all lives, see game-over, restart from title.
Phase 5 -- level data
Match the original's 25 levels (or however many ship in the disk
image; verify from SPACETAX.D64).
| Task | Investigate | Update |
|---|---|---|
| 5.1 | Level table layout in RAM: where are per-level pad coords stored? Per-level music ID? Per-level taxi spawn? Per-level fare list? | stLevel.c STL1 format |
| 5.2 | Extract every level from the D64 image (we already have level1 / level2 / title dumps; need a way to dump every level state). Either replay through the game in VICE and snapshot each level, or read the level table once and reconstruct | extractor: bulk-export |
| 5.3 | Verify the title screen extraction matches the original byte-for-byte (chars + colors) | title.dat regenerate |
Checkpoint: all original levels playable, named correctly, ordered correctly.
Phase 6 -- cross-platform parity
DOS lands first. Then bring the other three up.
| Target | Specific work |
|---|---|
| Amiga | Verify build still runs; PT replayer for music; joystick |
| Atari ST | Same; YM2149 for SFX, MOD for music |
| IIgs | Resolve ROOT-segment limit so the binary fits; wire asset |
| pipeline into the disk packager (3 options in earlier note); | |
| Ensoniq sound match |
Checkpoint: each of the four targets boots, shows title, plays at least one level end-to-end.
Working notes
- All chat-side analysis must stay in plain English -- disassembly
excerpts go in local files (
MECHANICS.md,labels.txt), not pasted back into the session. - One subsystem per session is the right cadence. Trying to "do them all at once" without user-test feedback compounds errors.
- Keep this plan up to date: as each task lands, mark it DONE with the commit SHA or date, and add new tasks discovered during the work.