Compare commits
No commits in common. "786ff257c33a79a28e8c2f8f54b616ff7f46ee86" and "a9ccb6e5c244922ea02e0c4b8ba38d1776b0d197" have entirely different histories.
786ff257c3
...
a9ccb6e5c2
220 changed files with 1962 additions and 14167 deletions
1
.gitattributes
vendored
1
.gitattributes
vendored
|
|
@ -1 +0,0 @@
|
|||
*.png filter=lfs diff=lfs merge=lfs -text
|
||||
22
.gitignore
vendored
22
.gitignore
vendored
|
|
@ -1,22 +0,0 @@
|
|||
# Everything make writes: programs, bakes, disk images, the harness.
|
||||
build/
|
||||
|
||||
# The original game's memory image (scripts/importData.sh installs it as
|
||||
# stuff/raw.bin) and the disassembly work around it. A copy of a commercial
|
||||
# game: never committed.
|
||||
stuff/
|
||||
|
||||
# stsim's host binaries and its captured golden traces (gate.sh capture;
|
||||
# gateHashes.txt is the committed reference).
|
||||
tools/stsim/stsim
|
||||
tools/stsim/recorder
|
||||
tools/stsim/golden/
|
||||
|
||||
screenshots/
|
||||
__pycache__/
|
||||
|
||||
# Editor / OS cruft
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.DS_Store
|
||||
BIN
21_!.wav
BIN
21_!.wav
Binary file not shown.
BIN
31_1.wav
BIN
31_1.wav
Binary file not shown.
BIN
32_2.wav
BIN
32_2.wav
Binary file not shown.
BIN
33_3.wav
BIN
33_3.wav
Binary file not shown.
BIN
34_4.wav
BIN
34_4.wav
Binary file not shown.
BIN
35_5.wav
BIN
35_5.wav
Binary file not shown.
BIN
36_6.wav
BIN
36_6.wav
Binary file not shown.
BIN
37_7.wav
BIN
37_7.wav
Binary file not shown.
BIN
38_8.wav
BIN
38_8.wav
Binary file not shown.
BIN
39_9.wav
BIN
39_9.wav
Binary file not shown.
BIN
3F_x.wav
BIN
3F_x.wav
Binary file not shown.
BIN
48_H.wav
BIN
48_H.wav
Binary file not shown.
BIN
50_P.wav
BIN
50_P.wav
Binary file not shown.
BIN
54_T.wav
BIN
54_T.wav
Binary file not shown.
BIN
55_U.wav
BIN
55_U.wav
Binary file not shown.
12
MECHANICS.md
12
MECHANICS.md
|
|
@ -1,8 +1,8 @@
|
|||
# Space Taxi Mechanics (research notes from C64 disassembly)
|
||||
|
||||
Source: VICE memory dumps in `stuff/` decoded via the
|
||||
Source: VICE memory dumps in `stuff/spacetaxi/` decoded via the
|
||||
`dis6502.py` / `disLive.py` helpers. Full live-RAM listings are at
|
||||
`stuff/live-level1.lst` (40,069 instructions) and
|
||||
`stuff/spacetaxi/live-level1.lst` (40,069 instructions) and
|
||||
`live-raw.lst` (titlescreen snapshot). This doc captures observed
|
||||
behavior in plain English to drive the JoeyLib port without dragging
|
||||
the original 6502 source into chat or comments.
|
||||
|
|
@ -68,7 +68,7 @@ acceleration always applied to Y.
|
|||
### Per-frame algorithm (`$6032`, full trace) -- VERIFIED via emulator
|
||||
|
||||
Verified by running `$6032` through the 6502 emulator with various
|
||||
joystick / sprite-ptr states. See `stuff/trace.py`.
|
||||
joystick / sprite-ptr states. See `stuff/spacetaxi/trace.py`.
|
||||
|
||||
```
|
||||
1. Clear instant accel: $7148/49 = 0; $714A/4B = 0 [$6032-$603F]
|
||||
|
|
@ -1069,7 +1069,7 @@ IEC fast-loader (raw `$FFA8` CIOUT / `$FFAE` UNLSN serial traffic)
|
|||
that pulls the song file off the disk. **The music DATA is not in
|
||||
the resident program at all: each song ships as its own disk file**
|
||||
-- the `SPACETAXI 01..04` / `SPACETAXI HI` PRGs (present in
|
||||
`stuff/extract/`). Whatever song was resident when the
|
||||
`stuff/spacetaxi/extract/`). Whatever song was resident when the
|
||||
raw.bin dump was taken is what sits in its load area there.
|
||||
|
||||
Timing base: `$A2` in the tick compare is the KERNAL jiffy clock low
|
||||
|
|
@ -1770,7 +1770,7 @@ Char-code interpretation (via $4345 validation):
|
|||
- Other chars: undefined behavior
|
||||
|
||||
Verified by running `$4354` through the 6502 emulator (see
|
||||
`stuff/trace.py`) with each blob against an all-blank
|
||||
`stuff/spacetaxi/trace.py`) with each blob against an all-blank
|
||||
HUD. The actual byte-level result for each:
|
||||
|
||||
| Blob | HUD after BCD-add (chars) | Numeric effect |
|
||||
|
|
@ -2098,7 +2098,7 @@ Key gameplay-affecting observations:
|
|||
- **Border/BG colors** vary; most levels are black (0/0). Level S
|
||||
has yellow/orange (`$07/$08`), level L is dark gray on dark gray.
|
||||
|
||||
The .dat files in `generated/levels/` carry the
|
||||
The .dat files in `examples/spacetaxi/generated/levels/` carry the
|
||||
same xAccel/yAccel/xGrav/yGrav values verbatim (verified by parsing
|
||||
the binary headers).
|
||||
|
||||
|
|
|
|||
26
Makefile
26
Makefile
|
|
@ -1,26 +0,0 @@
|
|||
# Space Taxi, built with JoeyLib: a JoeyLib checkout (JOEYLIB, beside this
|
||||
# one by default) owns the toolchains, the library and every target's rules,
|
||||
# and program.mk describes the game to them.
|
||||
#
|
||||
# make every target whose toolchain is installed
|
||||
# make <port> one target: iigs amiga atarist dos x68000 dvx
|
||||
# make check build, then run the gates (scripts/verify.sh)
|
||||
# make clean remove build/
|
||||
# make help JoeyLib's project targets
|
||||
#
|
||||
# README.md covers setting up JoeyLib, importing the game data and running.
|
||||
|
||||
JOEYLIB ?= ../joeylib
|
||||
include $(JOEYLIB)/make/project.mk
|
||||
|
||||
include $(JOEY_PROJECT)/program.mk
|
||||
|
||||
# What DVX's help and resource texts call the program, and its author.
|
||||
DVX_KIND := JoeyLib game
|
||||
DVX_AUTHOR := Scott Duensing
|
||||
|
||||
include $(JOEYLIB)/make/programs.mk
|
||||
|
||||
.PHONY: check
|
||||
check: all
|
||||
JOEYLIB="$(abspath $(JOEYLIB))" $(JOEY_PROJECT)/scripts/verify.sh
|
||||
6
PLAN.md
6
PLAN.md
|
|
@ -15,7 +15,7 @@ Same loop every time, so it stays cheap to iterate:
|
|||
2. **Read** -- pull the routine into a window via `disLive.py
|
||||
<dump> <out> <start> <end>`, study the annotated listing.
|
||||
3. **Document** -- append behavior summary to `MECHANICS.md` in
|
||||
plain English. Add new labels/vars to `stuff/labels.txt`.
|
||||
plain English. Add new labels/vars to `stuff/spacetaxi/labels.txt`.
|
||||
4. **Reannotate** -- `python3 disLive.py mem0000-level1.bin
|
||||
live-level1.lst` to regenerate the (annotated) listing from
|
||||
the dump + labels.txt in one step. The .lst file is the only
|
||||
|
|
@ -25,12 +25,12 @@ Same loop every time, so it stays cheap to iterate:
|
|||
`stLevel.c`, `spacetaxi.c`).
|
||||
6. **Build + ship** -- `make -f make/dos.mk EXAMPLE=spacetaxi`
|
||||
first, then once stable also amiga / atarist / iigs.
|
||||
7. **User test** -- run `./joey-run --port dos staxi`, report
|
||||
7. **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/`
|
||||
- Disassembly toolchain in `stuff/spacetaxi/`
|
||||
- Full live-RAM listing + annotated variant
|
||||
- `MECHANICS.md` baseline
|
||||
- Asset pipeline (`tools/assetbake/assetbake.py`, extractor realigned)
|
||||
|
|
|
|||
123
README.md
123
README.md
|
|
@ -1,123 +0,0 @@
|
|||
# Space Taxi
|
||||
|
||||
A port of Space Taxi (John Kutcher, Muse Software, 1984), the Commodore 64
|
||||
game, to every machine [JoeyLib](../joeylib) targets: the Apple IIgs,
|
||||
Commodore Amiga, Atari ST, MS-DOS, Sharp X68000 and DVX. One C codebase
|
||||
builds for all six.
|
||||
|
||||
The game is a routine-for-routine re-expression of the C64 original in C,
|
||||
written from its disassembly (MECHANICS.md): the flight physics, the fare
|
||||
machine, the 24 levels' hook programs, the title and level intros, the
|
||||
high-score table and name entry, the music (captured from the game's own
|
||||
SID player) and the speech. The simulation (stSim.c) is verified tick for
|
||||
tick against VICE traces of the C64's four attract-demo rides (VERIFIED.md),
|
||||
and the gates below hold it there.
|
||||
|
||||
## Setting up
|
||||
|
||||
Space Taxi builds with JoeyLib, which owns the cross compilers, the
|
||||
library, the asset tools and every machine's build rules. Check JoeyLib out
|
||||
beside this repository and install its toolchains (its README covers the
|
||||
installer):
|
||||
|
||||
```
|
||||
games/
|
||||
joeylib/ a JoeyLib checkout, set up with ./install.sh
|
||||
spacetaxi/ this repository
|
||||
```
|
||||
|
||||
```console
|
||||
$ cd ../joeylib
|
||||
$ ./install.sh install toolchains # the cross compilers
|
||||
$ ./install.sh install emulators # to run it and for the gates
|
||||
```
|
||||
|
||||
`JOEYLIB=PATH` on the make command line (or in the environment, for the
|
||||
scripts) points at a checkout somewhere else.
|
||||
|
||||
## The game data
|
||||
|
||||
The levels and the title screen are extracted at build time from a memory
|
||||
image of the original C64 game. It is a copy of a commercial game, so it is
|
||||
not in the repository; supply your own:
|
||||
|
||||
```console
|
||||
$ scripts/importData.sh PATH...
|
||||
```
|
||||
|
||||
PATH is the image itself, a folder holding it, or a `.zip` or `.tar.gz`
|
||||
with it inside. It is recognised by its checksum and installed as
|
||||
`stuff/raw.bin`, which git ignores. Without it the game still builds; only
|
||||
the levels (and the per-level sprite banks baked from them) are left out,
|
||||
and make says so.
|
||||
|
||||
## Building
|
||||
|
||||
```console
|
||||
$ make # every machine whose toolchain is installed
|
||||
$ make dos # one: iigs amiga atarist dos x68000 dvx
|
||||
$ make clean
|
||||
```
|
||||
|
||||
Everything lands in `build/<machine>/bin/`:
|
||||
|
||||
| Machine | Program |
|
||||
|---------|---------|
|
||||
| Apple IIgs | `STAXI`, and `STAXI.2mg`: a bootable 2 MB volume (stripped GS/OS that launches the game from its own folder) |
|
||||
| Amiga | `Taxi/Taxi` with its `DATA/` |
|
||||
| Atari ST | `STAXI/STAXI.PRG` with its `DATA/` |
|
||||
| MS-DOS | `STAXI/STAXI.EXE` with its `DATA/` |
|
||||
| Sharp X68000 | `STAXI/STAXI.X` with its `DATA/` |
|
||||
| DVX | `apps/joeylib/staxi/`: `staxi.app`, its help file and `DATA/` |
|
||||
|
||||
The bakes (levels, the pre-compiled sprite cels, the speech) are made per
|
||||
machine in `build/generated/<machine>/`. JoeyLib's library is built on
|
||||
demand in the JoeyLib checkout's own `build/`.
|
||||
|
||||
## Running
|
||||
|
||||
JoeyLib's `joey-run` starts a build in the right emulator:
|
||||
|
||||
```console
|
||||
$ ../joeylib/joey-run --project . --port dos staxi
|
||||
$ ../joeylib/joey-run --project . --port amiga taxi
|
||||
$ ../joeylib/joey-run build/iigs/bin/STAXI.2mg
|
||||
$ ../joeylib/joey-run --headless --seconds 90 --snapshot taxi.png --project . --port atarist staxi
|
||||
```
|
||||
|
||||
The IIgs volume is larger than an 800 KB floppy, so MAME boots it off a
|
||||
CFFA2 card (`mame apple2gs -sl7 cffa2 -hard1 build/iigs/bin/STAXI.2mg`).
|
||||
|
||||
## The gates
|
||||
|
||||
```console
|
||||
$ make check # build, then every gate
|
||||
$ scripts/verify.sh disk # or name the ones to run
|
||||
```
|
||||
|
||||
| Gate | What it proves |
|
||||
|------|----------------|
|
||||
| `stsim` | Every hook level's 4000-tick simulation trace is identical to its golden (`tools/stsim/gate.sh`; the four C64 attract rides and thirteen synthesised ones), and every cel a ride shows is pre-compiled (`celGate.sh`) |
|
||||
| `harness` | The screens above the simulation -- the high-score table, name entry, its save -- on JoeyLib's host BLANK port (`tools/staxiharness`) |
|
||||
| `disk`, `disk01` | `STAXI.2mg` boots, launches the game and draws on a ROM 3 and a ROM 01 IIgs |
|
||||
| `iigsaudio` | The IIgs sound interrupt is NTP's, the DOC is audible and its sound slots carry waveforms |
|
||||
| `x68kaudio` | Every X68000 noise write is a code from the port's table, and the ADPCM runs at 7812 Hz to both speakers while the game speaks |
|
||||
|
||||
`scripts/verify.sh` exits 0 when every gate passed, 1 when one failed, and 3
|
||||
when none failed but one was skipped (an emulator or a machine's build
|
||||
missing, or the game data not imported).
|
||||
|
||||
## Tools
|
||||
|
||||
- `tools/stsim` -- the simulation on the host: prints the per-tick state a
|
||||
VICE trace records, so `compare.py` can diff the port against the C64.
|
||||
- `tools/staxiharness` -- the front-end screen checks.
|
||||
- `tools/staxibake` -- writes the cel bank the build pre-compiles per machine.
|
||||
- `assets/` -- the generators behind the committed C64 tables
|
||||
(`genC64Data.py`, `genDemoStreams.py`, `genSongData.py`, `extractSpeech.py`)
|
||||
and the level extractor the build runs (`romToLevel.py`).
|
||||
- `scripts/bench-staxi-title.sh`, `scripts/diag-staxi-screen.sh` -- IIgs
|
||||
measurement and diagnosis under headless MAME.
|
||||
|
||||
MECHANICS.md and VERIFIED.md are the disassembly notes; TODO.md lists what is
|
||||
still open.
|
||||
21
TODO.md
21
TODO.md
|
|
@ -1,21 +0,0 @@
|
|||
# Space Taxi -- open items
|
||||
|
||||
Things worth doing, not commitments. Started 2026-10-05, when Space Taxi
|
||||
moved out of JoeyLib's tree into this repository; JoeyLib's own list
|
||||
(library and platform work) stays in JoeyLib.
|
||||
|
||||
- [ ] **The C64's flown-sprite warp.** The level-name intro animates its
|
||||
warp by cycling the colours of a static star field painted into
|
||||
screen RAM, on every target (stTitle.c, `starPaletteApply` in
|
||||
stRender.c). The C64 flies seven star sprites out along its rays
|
||||
instead. That version, carry-flag exact ($44F8/$4500/$450C/$4513/
|
||||
$467A, the star velocity tables back in genC64Data.py), is kept in
|
||||
`patches/spriteWarp.patch` for reference: it was made against the
|
||||
working tree of 2026-10-05 before the warp's layout and pacing were
|
||||
redone, so it no longer applies as it stands. Measured then, the
|
||||
sprite warp ran 16.6-17.0 fps on the IIgs against the palette's 29.2
|
||||
(the bar was 30); ST 29.2 against the palette's 23.3, Amiga PAL 12.3
|
||||
against 18.8/13.6, DOS ~29.9 both ways. On the IIgs the cost is
|
||||
per-call overhead in save-and-draw and restore (~1.75k and ~1.25k
|
||||
cycles a star), not bytes moved, so it becomes feasible with a leaner
|
||||
save/draw/restore path for small cels in JoeyLib.
|
||||
10
VERIFIED.md
10
VERIFIED.md
|
|
@ -1,7 +1,7 @@
|
|||
# Space Taxi disassembly: verified claims
|
||||
|
||||
Each row below references a claim in `MECHANICS.md` and the trace
|
||||
that verifies it. Trace scripts live in `stuff/trace.py`
|
||||
that verifies it. Trace scripts live in `stuff/spacetaxi/trace.py`
|
||||
plus the inline scripts run during verification sessions.
|
||||
|
||||
## VERIFIED via emulator trace
|
||||
|
|
@ -81,10 +81,10 @@ plus the inline scripts run during verification sessions.
|
|||
|
||||
## TRACE INFRASTRUCTURE
|
||||
|
||||
- `stuff/trace.py`: instrumented wrapper around `cpu6502.Cpu6502`. `Tracer.from_dump('raw.bin').call(addr)` runs a subroutine and snapshots memory + JSR chain.
|
||||
- `assets/cpu6502.py`: minimal 6502 emulator. Sufficient for all routines tested above; throws `NotImplementedError` for unsupported opcodes (none hit so far in gameplay paths).
|
||||
- `assets/dumpScenes.py` + `stuff/dumpAllLevels.py`: pre-existing scene-decompress drivers.
|
||||
- `assets/romToLevel.py`: emits the `.dat` files from raw.bin via the same emulator.
|
||||
- `stuff/spacetaxi/trace.py`: instrumented wrapper around `cpu6502.Cpu6502`. `Tracer.from_dump('raw.bin').call(addr)` runs a subroutine and snapshots memory + JSR chain.
|
||||
- `stuff/spacetaxi/cpu6502.py`: minimal 6502 emulator. Sufficient for all routines tested above; throws `NotImplementedError` for unsupported opcodes (none hit so far in gameplay paths).
|
||||
- `stuff/spacetaxi/dumpScenes.py` + `dumpAllLevels.py`: pre-existing scene-decompress drivers.
|
||||
- `stuff/spacetaxi/romToLevel.py`: emits the `.dat` files from raw.bin via the same emulator.
|
||||
|
||||
## METHODOLOGY (for future verification sessions)
|
||||
|
||||
|
|
|
|||
BIN
assets/RawTitle.png
(Stored with Git LFS)
BIN
assets/RawTitle.png
(Stored with Git LFS)
Binary file not shown.
BIN
assets/TitleShr.png
(Stored with Git LFS)
BIN
assets/TitleShr.png
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1,560 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# Minimal 6502 CPU emulator -- just enough to run the Space Taxi
|
||||
# level decompressor at $6283 against a captured 64KB RAM dump.
|
||||
#
|
||||
# Scope: implements the opcodes used by $6283 / $9656 / $964A / $96A3
|
||||
# and their helpers. Not a general-purpose 6502 emulator; it has no
|
||||
# decimal mode, no BRK handling, no NMI/IRQ -- the routine runs with
|
||||
# SEI in effect and never returns control.
|
||||
#
|
||||
# Flags tracked: N Z C. V (overflow) is computed for ADC/SBC since
|
||||
# those instructions set it, even if no branch tests it. D (decimal)
|
||||
# is ignored.
|
||||
|
||||
class Cpu6502:
|
||||
def __init__(self, ram):
|
||||
# ram is a bytearray of 65536 bytes. Caller pre-loaded the dump.
|
||||
self.ram = ram
|
||||
self.a = 0
|
||||
self.x = 0
|
||||
self.y = 0
|
||||
self.sp = 0xFF
|
||||
self.pc = 0
|
||||
self.n = self.z = self.c = self.v = False
|
||||
# Stop tracking when RTS unwinds to this depth (set by run()).
|
||||
self._depth_floor = 0
|
||||
|
||||
# ---- memory ----
|
||||
def read8(self, addr):
|
||||
return self.ram[addr & 0xFFFF]
|
||||
|
||||
def write8(self, addr, val):
|
||||
self.ram[addr & 0xFFFF] = val & 0xFF
|
||||
|
||||
def read16(self, addr):
|
||||
return self.ram[addr & 0xFFFF] | (self.ram[(addr + 1) & 0xFFFF] << 8)
|
||||
|
||||
# ---- stack ----
|
||||
def push8(self, val):
|
||||
self.ram[0x100 + self.sp] = val & 0xFF
|
||||
self.sp = (self.sp - 1) & 0xFF
|
||||
|
||||
def pop8(self):
|
||||
self.sp = (self.sp + 1) & 0xFF
|
||||
return self.ram[0x100 + self.sp]
|
||||
|
||||
def push16(self, val):
|
||||
self.push8((val >> 8) & 0xFF)
|
||||
self.push8(val & 0xFF)
|
||||
|
||||
def pop16(self):
|
||||
lo = self.pop8()
|
||||
hi = self.pop8()
|
||||
return (hi << 8) | lo
|
||||
|
||||
# ---- flag helpers ----
|
||||
def set_nz(self, val):
|
||||
val &= 0xFF
|
||||
self.z = (val == 0)
|
||||
self.n = (val & 0x80) != 0
|
||||
return val
|
||||
|
||||
def pack_p(self):
|
||||
# B and U are usually set when pushed by PHP/BRK.
|
||||
p = 0x20
|
||||
if self.n: p |= 0x80
|
||||
if self.v: p |= 0x40
|
||||
if self.z: p |= 0x02
|
||||
if self.c: p |= 0x01
|
||||
return p | 0x10 # B = 1 when pushed by PHP
|
||||
|
||||
def unpack_p(self, p):
|
||||
self.n = (p & 0x80) != 0
|
||||
self.v = (p & 0x40) != 0
|
||||
self.z = (p & 0x02) != 0
|
||||
self.c = (p & 0x01) != 0
|
||||
|
||||
# ---- addressing helpers (return effective address) ----
|
||||
def addr_imm(self):
|
||||
a = self.pc; self.pc = (self.pc + 1) & 0xFFFF; return a
|
||||
|
||||
def addr_zp(self):
|
||||
a = self.read8(self.pc); self.pc = (self.pc + 1) & 0xFFFF; return a
|
||||
|
||||
def addr_zpx(self):
|
||||
a = (self.read8(self.pc) + self.x) & 0xFF; self.pc = (self.pc + 1) & 0xFFFF; return a
|
||||
|
||||
def addr_abs(self):
|
||||
a = self.read16(self.pc); self.pc = (self.pc + 2) & 0xFFFF; return a
|
||||
|
||||
def addr_absx(self):
|
||||
a = (self.read16(self.pc) + self.x) & 0xFFFF; self.pc = (self.pc + 2) & 0xFFFF; return a
|
||||
|
||||
def addr_absy(self):
|
||||
a = (self.read16(self.pc) + self.y) & 0xFFFF; self.pc = (self.pc + 2) & 0xFFFF; return a
|
||||
|
||||
def addr_indy(self):
|
||||
zp = self.read8(self.pc); self.pc = (self.pc + 1) & 0xFFFF
|
||||
base = self.ram[zp] | (self.ram[(zp + 1) & 0xFF] << 8)
|
||||
return (base + self.y) & 0xFFFF
|
||||
|
||||
# ---- branch helper ----
|
||||
def branch(self, cond):
|
||||
off = self.read8(self.pc); self.pc = (self.pc + 1) & 0xFFFF
|
||||
if off >= 0x80: off -= 0x100
|
||||
if cond:
|
||||
self.pc = (self.pc + off) & 0xFFFF
|
||||
|
||||
# ---- step ----
|
||||
def step(self):
|
||||
op = self.read8(self.pc)
|
||||
self.pc = (self.pc + 1) & 0xFFFF
|
||||
f = _DISPATCH.get(op)
|
||||
if f is None:
|
||||
raise NotImplementedError(f"opcode ${op:02X} at PC ${(self.pc-1)&0xFFFF:04X}")
|
||||
f(self)
|
||||
|
||||
def call(self, target, max_steps=10_000_000):
|
||||
"""Call subroutine at `target` as if via JSR. Returns when the
|
||||
matching RTS pops the stack back below the original SP. Hard
|
||||
cap on steps to avoid runaway."""
|
||||
# Push a sentinel return address. When RTS pops to this, stop.
|
||||
# Use $FFFF as sentinel: subsequent step would attempt to fetch
|
||||
# from $FFFF; we detect via depth floor instead.
|
||||
original_sp = self.sp
|
||||
self.push16(0xFFFE) # RTS will pop this, then step will land at $FFFF
|
||||
self.pc = target & 0xFFFF
|
||||
for _ in range(max_steps):
|
||||
if self.pc == 0xFFFF:
|
||||
return
|
||||
self.step()
|
||||
raise RuntimeError(f"6502 call exceeded {max_steps} steps; PC=${self.pc:04X}")
|
||||
|
||||
|
||||
# ---- opcode implementations ----
|
||||
# Each takes the cpu and reads remaining operand bytes via cpu.addr_*.
|
||||
|
||||
def _NOP(cpu): pass
|
||||
def _SEI(cpu): pass # we don't model interrupts
|
||||
def _CLD(cpu): pass # no decimal mode
|
||||
def _SEC(cpu): cpu.c = True
|
||||
def _CLC(cpu): cpu.c = False
|
||||
def _PHP(cpu): cpu.push8(cpu.pack_p())
|
||||
def _PLP(cpu): cpu.unpack_p(cpu.pop8())
|
||||
def _PHA(cpu): cpu.push8(cpu.a)
|
||||
def _PLA(cpu): cpu.a = cpu.set_nz(cpu.pop8())
|
||||
def _TXA(cpu): cpu.a = cpu.set_nz(cpu.x)
|
||||
def _TAX(cpu): cpu.x = cpu.set_nz(cpu.a)
|
||||
def _TYA(cpu): cpu.a = cpu.set_nz(cpu.y)
|
||||
def _TAY(cpu): cpu.y = cpu.set_nz(cpu.a)
|
||||
def _INX(cpu): cpu.x = cpu.set_nz((cpu.x + 1) & 0xFF)
|
||||
def _INY(cpu): cpu.y = cpu.set_nz((cpu.y + 1) & 0xFF)
|
||||
def _DEX(cpu): cpu.x = cpu.set_nz((cpu.x - 1) & 0xFF)
|
||||
def _DEY(cpu): cpu.y = cpu.set_nz((cpu.y - 1) & 0xFF)
|
||||
|
||||
def _LDA_imm(cpu): cpu.a = cpu.set_nz(cpu.read8(cpu.addr_imm()))
|
||||
def _LDA_zp(cpu): cpu.a = cpu.set_nz(cpu.read8(cpu.addr_zp()))
|
||||
def _LDA_zpx(cpu): cpu.a = cpu.set_nz(cpu.read8(cpu.addr_zpx()))
|
||||
def _LDA_abs(cpu): cpu.a = cpu.set_nz(cpu.read8(cpu.addr_abs()))
|
||||
def _LDA_absx(cpu): cpu.a = cpu.set_nz(cpu.read8(cpu.addr_absx()))
|
||||
def _LDA_absy(cpu): cpu.a = cpu.set_nz(cpu.read8(cpu.addr_absy()))
|
||||
def _LDA_indy(cpu): cpu.a = cpu.set_nz(cpu.read8(cpu.addr_indy()))
|
||||
|
||||
def _LDX_imm(cpu): cpu.x = cpu.set_nz(cpu.read8(cpu.addr_imm()))
|
||||
def _LDX_abs(cpu): cpu.x = cpu.set_nz(cpu.read8(cpu.addr_abs()))
|
||||
def _LDX_absy(cpu): cpu.x = cpu.set_nz(cpu.read8(cpu.addr_absy()))
|
||||
def _LDX_zp(cpu): cpu.x = cpu.set_nz(cpu.read8(cpu.addr_zp()))
|
||||
|
||||
def _LDY_imm(cpu): cpu.y = cpu.set_nz(cpu.read8(cpu.addr_imm()))
|
||||
def _LDY_abs(cpu): cpu.y = cpu.set_nz(cpu.read8(cpu.addr_abs()))
|
||||
def _LDY_absx(cpu): cpu.y = cpu.set_nz(cpu.read8(cpu.addr_absx()))
|
||||
def _LDY_zp(cpu): cpu.y = cpu.set_nz(cpu.read8(cpu.addr_zp()))
|
||||
|
||||
def _STA_zp(cpu): cpu.write8(cpu.addr_zp(), cpu.a)
|
||||
def _STA_zpx(cpu): cpu.write8(cpu.addr_zpx(), cpu.a)
|
||||
def _STA_abs(cpu): cpu.write8(cpu.addr_abs(), cpu.a)
|
||||
def _STA_absx(cpu): cpu.write8(cpu.addr_absx(), cpu.a)
|
||||
def _STA_absy(cpu): cpu.write8(cpu.addr_absy(), cpu.a)
|
||||
def _STA_indy(cpu): cpu.write8(cpu.addr_indy(), cpu.a)
|
||||
|
||||
def _STX_zp(cpu): cpu.write8(cpu.addr_zp(), cpu.x)
|
||||
def _STX_abs(cpu): cpu.write8(cpu.addr_abs(), cpu.x)
|
||||
|
||||
def _STY_zp(cpu): cpu.write8(cpu.addr_zp(), cpu.y)
|
||||
def _STY_abs(cpu): cpu.write8(cpu.addr_abs(), cpu.y)
|
||||
|
||||
def _AND_imm(cpu): cpu.a = cpu.set_nz(cpu.a & cpu.read8(cpu.addr_imm()))
|
||||
def _AND_zp(cpu): cpu.a = cpu.set_nz(cpu.a & cpu.read8(cpu.addr_zp()))
|
||||
def _AND_abs(cpu): cpu.a = cpu.set_nz(cpu.a & cpu.read8(cpu.addr_abs()))
|
||||
def _ORA_imm(cpu): cpu.a = cpu.set_nz(cpu.a | cpu.read8(cpu.addr_imm()))
|
||||
def _ORA_zp(cpu): cpu.a = cpu.set_nz(cpu.a | cpu.read8(cpu.addr_zp()))
|
||||
def _EOR_imm(cpu): cpu.a = cpu.set_nz(cpu.a ^ cpu.read8(cpu.addr_imm()))
|
||||
def _EOR_abs(cpu): cpu.a = cpu.set_nz(cpu.a ^ cpu.read8(cpu.addr_abs()))
|
||||
|
||||
def _adc(cpu, m):
|
||||
r = cpu.a + m + (1 if cpu.c else 0)
|
||||
cpu.c = r > 0xFF
|
||||
cpu.v = ((cpu.a ^ r) & (m ^ r) & 0x80) != 0
|
||||
cpu.a = cpu.set_nz(r & 0xFF)
|
||||
|
||||
def _ADC_imm(cpu): _adc(cpu, cpu.read8(cpu.addr_imm()))
|
||||
def _ADC_zp(cpu): _adc(cpu, cpu.read8(cpu.addr_zp()))
|
||||
def _ADC_abs(cpu): _adc(cpu, cpu.read8(cpu.addr_abs()))
|
||||
def _ADC_absy(cpu): _adc(cpu, cpu.read8(cpu.addr_absy()))
|
||||
|
||||
def _sbc(cpu, m):
|
||||
_adc(cpu, m ^ 0xFF)
|
||||
|
||||
def _SBC_imm(cpu): _sbc(cpu, cpu.read8(cpu.addr_imm()))
|
||||
def _SBC_zp(cpu): _sbc(cpu, cpu.read8(cpu.addr_zp()))
|
||||
def _SBC_abs(cpu): _sbc(cpu, cpu.read8(cpu.addr_abs()))
|
||||
def _SBC_absx(cpu): _sbc(cpu, cpu.read8(cpu.addr_absx()))
|
||||
def _SBC_absy(cpu): _sbc(cpu, cpu.read8(cpu.addr_absy()))
|
||||
|
||||
def _AND_absx(cpu): cpu.a = cpu.set_nz(cpu.a & cpu.read8(cpu.addr_absx()))
|
||||
def _AND_absy(cpu): cpu.a = cpu.set_nz(cpu.a & cpu.read8(cpu.addr_absy()))
|
||||
def _AND_indy(cpu): cpu.a = cpu.set_nz(cpu.a & cpu.read8(cpu.addr_indy()))
|
||||
def _ORA_absx(cpu): cpu.a = cpu.set_nz(cpu.a | cpu.read8(cpu.addr_absx()))
|
||||
def _ORA_abs(cpu): cpu.a = cpu.set_nz(cpu.a | cpu.read8(cpu.addr_abs()))
|
||||
def _ORA_zpx(cpu): cpu.a = cpu.set_nz(cpu.a | cpu.read8(cpu.addr_zpx()))
|
||||
def _ORA_indy(cpu): cpu.a = cpu.set_nz(cpu.a | cpu.read8(cpu.addr_indy()))
|
||||
def _EOR_zp(cpu): cpu.a = cpu.set_nz(cpu.a ^ cpu.read8(cpu.addr_zp()))
|
||||
def _EOR_zpx(cpu): cpu.a = cpu.set_nz(cpu.a ^ cpu.read8(cpu.addr_zpx()))
|
||||
def _EOR_absx(cpu): cpu.a = cpu.set_nz(cpu.a ^ cpu.read8(cpu.addr_absx()))
|
||||
def _EOR_absy(cpu): cpu.a = cpu.set_nz(cpu.a ^ cpu.read8(cpu.addr_absy()))
|
||||
def _ADC_zpx(cpu): _adc(cpu, cpu.read8(cpu.addr_zpx()))
|
||||
def _ADC_absx(cpu): _adc(cpu, cpu.read8(cpu.addr_absx()))
|
||||
def _CMP_absx(cpu): _cmp_with(cpu, cpu.a, cpu.read8(cpu.addr_absx()))
|
||||
def _CMP_absy(cpu): _cmp_with(cpu, cpu.a, cpu.read8(cpu.addr_absy()))
|
||||
def _CMP_zpx(cpu): _cmp_with(cpu, cpu.a, cpu.read8(cpu.addr_zpx()))
|
||||
|
||||
def _JMP_ind(cpu):
|
||||
addr = cpu.addr_abs()
|
||||
cpu.pc = cpu.read8(addr) | (cpu.read8((addr & 0xFF00) | ((addr + 1) & 0xFF)) << 8)
|
||||
|
||||
def _BIT_zp(cpu):
|
||||
v = cpu.read8(cpu.addr_zp())
|
||||
cpu.z = (cpu.a & v) == 0
|
||||
cpu.n = (v & 0x80) != 0
|
||||
cpu.v = (v & 0x40) != 0
|
||||
|
||||
def _BIT_abs(cpu):
|
||||
v = cpu.read8(cpu.addr_abs())
|
||||
cpu.z = (cpu.a & v) == 0
|
||||
cpu.n = (v & 0x80) != 0
|
||||
cpu.v = (v & 0x40) != 0
|
||||
|
||||
def _ASL_zp(cpu):
|
||||
addr = cpu.addr_zp()
|
||||
v = cpu.read8(addr)
|
||||
cpu.c = (v & 0x80) != 0
|
||||
cpu.write8(addr, cpu.set_nz((v << 1) & 0xFF))
|
||||
|
||||
def _ASL_abs(cpu):
|
||||
addr = cpu.addr_abs()
|
||||
v = cpu.read8(addr)
|
||||
cpu.c = (v & 0x80) != 0
|
||||
cpu.write8(addr, cpu.set_nz((v << 1) & 0xFF))
|
||||
|
||||
def _LSR_abs(cpu):
|
||||
addr = cpu.addr_abs()
|
||||
v = cpu.read8(addr)
|
||||
cpu.c = (v & 0x01) != 0
|
||||
cpu.write8(addr, cpu.set_nz(v >> 1))
|
||||
|
||||
def _ROL_abs(cpu):
|
||||
addr = cpu.addr_abs()
|
||||
v = cpu.read8(addr)
|
||||
new_c = (v & 0x80) != 0
|
||||
v = ((v << 1) | (1 if cpu.c else 0)) & 0xFF
|
||||
cpu.write8(addr, cpu.set_nz(v))
|
||||
cpu.c = new_c
|
||||
|
||||
def _ROR_abs(cpu):
|
||||
addr = cpu.addr_abs()
|
||||
v = cpu.read8(addr)
|
||||
new_c = (v & 0x01) != 0
|
||||
v = ((v >> 1) | (0x80 if cpu.c else 0)) & 0xFF
|
||||
cpu.write8(addr, cpu.set_nz(v))
|
||||
cpu.c = new_c
|
||||
|
||||
def _ROR_zp(cpu):
|
||||
addr = cpu.addr_zp()
|
||||
v = cpu.read8(addr)
|
||||
new_c = (v & 0x01) != 0
|
||||
v = ((v >> 1) | (0x80 if cpu.c else 0)) & 0xFF
|
||||
cpu.write8(addr, cpu.set_nz(v))
|
||||
cpu.c = new_c
|
||||
|
||||
def _STA_zpx(cpu_): cpu_.write8(cpu_.addr_zpx(), cpu_.a)
|
||||
def _TSX(cpu): cpu.x = cpu.set_nz(cpu.sp)
|
||||
def _TXS(cpu): cpu.sp = cpu.x
|
||||
def _CLI(cpu): pass
|
||||
def _CLV(cpu): cpu.v = False
|
||||
def _SED(cpu): pass # decimal mode ignored
|
||||
def _STX_zpy(cpu):
|
||||
a = (cpu.read8(cpu.pc) + cpu.y) & 0xFF; cpu.pc = (cpu.pc + 1) & 0xFFFF
|
||||
cpu.write8(a, cpu.x)
|
||||
def _STY_zpx(cpu): cpu.write8(cpu.addr_zpx(), cpu.y)
|
||||
def _LDX_zpy(cpu):
|
||||
a = (cpu.read8(cpu.pc) + cpu.y) & 0xFF; cpu.pc = (cpu.pc + 1) & 0xFFFF
|
||||
cpu.x = cpu.set_nz(cpu.read8(a))
|
||||
def _LDY_zpx(cpu): cpu.y = cpu.set_nz(cpu.read8(cpu.addr_zpx()))
|
||||
|
||||
def _cmp_with(cpu, lhs, m):
|
||||
r = (lhs - m) & 0x1FF
|
||||
cpu.c = lhs >= m
|
||||
cpu.set_nz(r & 0xFF)
|
||||
|
||||
def _CMP_imm(cpu): _cmp_with(cpu, cpu.a, cpu.read8(cpu.addr_imm()))
|
||||
def _CMP_zp(cpu): _cmp_with(cpu, cpu.a, cpu.read8(cpu.addr_zp()))
|
||||
def _CMP_abs(cpu): _cmp_with(cpu, cpu.a, cpu.read8(cpu.addr_abs()))
|
||||
def _CPX_imm(cpu): _cmp_with(cpu, cpu.x, cpu.read8(cpu.addr_imm()))
|
||||
def _CPX_abs(cpu): _cmp_with(cpu, cpu.x, cpu.read8(cpu.addr_abs()))
|
||||
def _CPY_imm(cpu): _cmp_with(cpu, cpu.y, cpu.read8(cpu.addr_imm()))
|
||||
|
||||
def _ASL_a(cpu):
|
||||
cpu.c = (cpu.a & 0x80) != 0
|
||||
cpu.a = cpu.set_nz((cpu.a << 1) & 0xFF)
|
||||
|
||||
def _LSR_a(cpu):
|
||||
cpu.c = (cpu.a & 0x01) != 0
|
||||
cpu.a = cpu.set_nz(cpu.a >> 1)
|
||||
|
||||
def _LSR_zp(cpu):
|
||||
addr = cpu.addr_zp()
|
||||
v = cpu.read8(addr)
|
||||
cpu.c = (v & 0x01) != 0
|
||||
cpu.write8(addr, cpu.set_nz(v >> 1))
|
||||
|
||||
def _ROL_a(cpu):
|
||||
new_c = (cpu.a & 0x80) != 0
|
||||
cpu.a = cpu.set_nz(((cpu.a << 1) | (1 if cpu.c else 0)) & 0xFF)
|
||||
cpu.c = new_c
|
||||
|
||||
def _ROL_zp(cpu):
|
||||
addr = cpu.addr_zp()
|
||||
v = cpu.read8(addr)
|
||||
new_c = (v & 0x80) != 0
|
||||
v = ((v << 1) | (1 if cpu.c else 0)) & 0xFF
|
||||
cpu.write8(addr, cpu.set_nz(v))
|
||||
cpu.c = new_c
|
||||
|
||||
def _ROR_a(cpu):
|
||||
new_c = (cpu.a & 0x01) != 0
|
||||
cpu.a = cpu.set_nz(((cpu.a >> 1) | (0x80 if cpu.c else 0)) & 0xFF)
|
||||
cpu.c = new_c
|
||||
|
||||
def _INC_abs(cpu):
|
||||
addr = cpu.addr_abs()
|
||||
cpu.write8(addr, cpu.set_nz((cpu.read8(addr) + 1) & 0xFF))
|
||||
|
||||
def _INC_absx(cpu):
|
||||
addr = cpu.addr_absx()
|
||||
cpu.write8(addr, cpu.set_nz((cpu.read8(addr) + 1) & 0xFF))
|
||||
|
||||
def _INC_zp(cpu):
|
||||
addr = cpu.addr_zp()
|
||||
cpu.write8(addr, cpu.set_nz((cpu.read8(addr) + 1) & 0xFF))
|
||||
|
||||
def _INC_zpx(cpu):
|
||||
addr = cpu.addr_zpx()
|
||||
cpu.write8(addr, cpu.set_nz((cpu.read8(addr) + 1) & 0xFF))
|
||||
|
||||
def _DEC_abs(cpu):
|
||||
addr = cpu.addr_abs()
|
||||
cpu.write8(addr, cpu.set_nz((cpu.read8(addr) - 1) & 0xFF))
|
||||
|
||||
def _DEC_absx(cpu):
|
||||
addr = cpu.addr_absx()
|
||||
cpu.write8(addr, cpu.set_nz((cpu.read8(addr) - 1) & 0xFF))
|
||||
|
||||
def _DEC_zp(cpu):
|
||||
addr = cpu.addr_zp()
|
||||
cpu.write8(addr, cpu.set_nz((cpu.read8(addr) - 1) & 0xFF))
|
||||
|
||||
def _DEC_zpx(cpu):
|
||||
addr = cpu.addr_zpx()
|
||||
cpu.write8(addr, cpu.set_nz((cpu.read8(addr) - 1) & 0xFF))
|
||||
|
||||
def _ASL_zpx(cpu):
|
||||
addr = cpu.addr_zpx()
|
||||
v = cpu.read8(addr)
|
||||
cpu.c = (v & 0x80) != 0
|
||||
cpu.write8(addr, cpu.set_nz((v << 1) & 0xFF))
|
||||
|
||||
def _ASL_absx(cpu):
|
||||
addr = cpu.addr_absx()
|
||||
v = cpu.read8(addr)
|
||||
cpu.c = (v & 0x80) != 0
|
||||
cpu.write8(addr, cpu.set_nz((v << 1) & 0xFF))
|
||||
|
||||
def _LSR_zpx(cpu):
|
||||
addr = cpu.addr_zpx()
|
||||
v = cpu.read8(addr)
|
||||
cpu.c = (v & 0x01) != 0
|
||||
cpu.write8(addr, cpu.set_nz(v >> 1))
|
||||
|
||||
def _LSR_absx(cpu):
|
||||
addr = cpu.addr_absx()
|
||||
v = cpu.read8(addr)
|
||||
cpu.c = (v & 0x01) != 0
|
||||
cpu.write8(addr, cpu.set_nz(v >> 1))
|
||||
|
||||
def _ROL_zpx(cpu):
|
||||
addr = cpu.addr_zpx()
|
||||
v = cpu.read8(addr)
|
||||
new_c = (v & 0x80) != 0
|
||||
v = ((v << 1) | (1 if cpu.c else 0)) & 0xFF
|
||||
cpu.write8(addr, cpu.set_nz(v))
|
||||
cpu.c = new_c
|
||||
|
||||
def _ROL_absx(cpu):
|
||||
addr = cpu.addr_absx()
|
||||
v = cpu.read8(addr)
|
||||
new_c = (v & 0x80) != 0
|
||||
v = ((v << 1) | (1 if cpu.c else 0)) & 0xFF
|
||||
cpu.write8(addr, cpu.set_nz(v))
|
||||
cpu.c = new_c
|
||||
|
||||
def _ROR_zpx(cpu):
|
||||
addr = cpu.addr_zpx()
|
||||
v = cpu.read8(addr)
|
||||
new_c = (v & 0x01) != 0
|
||||
v = ((v >> 1) | (0x80 if cpu.c else 0)) & 0xFF
|
||||
cpu.write8(addr, cpu.set_nz(v))
|
||||
cpu.c = new_c
|
||||
|
||||
def _ROR_absx(cpu):
|
||||
addr = cpu.addr_absx()
|
||||
v = cpu.read8(addr)
|
||||
new_c = (v & 0x01) != 0
|
||||
v = ((v >> 1) | (0x80 if cpu.c else 0)) & 0xFF
|
||||
cpu.write8(addr, cpu.set_nz(v))
|
||||
cpu.c = new_c
|
||||
|
||||
def _LDA_indx(cpu):
|
||||
zp = cpu.read8(cpu.pc); cpu.pc = (cpu.pc + 1) & 0xFFFF
|
||||
addr_ptr = (zp + cpu.x) & 0xFF
|
||||
addr = cpu.ram[addr_ptr] | (cpu.ram[(addr_ptr + 1) & 0xFF] << 8)
|
||||
cpu.a = cpu.set_nz(cpu.read8(addr))
|
||||
|
||||
def _STA_indx(cpu):
|
||||
zp = cpu.read8(cpu.pc); cpu.pc = (cpu.pc + 1) & 0xFFFF
|
||||
addr_ptr = (zp + cpu.x) & 0xFF
|
||||
addr = cpu.ram[addr_ptr] | (cpu.ram[(addr_ptr + 1) & 0xFF] << 8)
|
||||
cpu.write8(addr, cpu.a)
|
||||
|
||||
def _AND_indx(cpu):
|
||||
zp = cpu.read8(cpu.pc); cpu.pc = (cpu.pc + 1) & 0xFFFF
|
||||
addr_ptr = (zp + cpu.x) & 0xFF
|
||||
addr = cpu.ram[addr_ptr] | (cpu.ram[(addr_ptr + 1) & 0xFF] << 8)
|
||||
cpu.a = cpu.set_nz(cpu.a & cpu.read8(addr))
|
||||
|
||||
def _ORA_indx(cpu):
|
||||
zp = cpu.read8(cpu.pc); cpu.pc = (cpu.pc + 1) & 0xFFFF
|
||||
addr_ptr = (zp + cpu.x) & 0xFF
|
||||
addr = cpu.ram[addr_ptr] | (cpu.ram[(addr_ptr + 1) & 0xFF] << 8)
|
||||
cpu.a = cpu.set_nz(cpu.a | cpu.read8(addr))
|
||||
|
||||
def _EOR_indx(cpu):
|
||||
zp = cpu.read8(cpu.pc); cpu.pc = (cpu.pc + 1) & 0xFFFF
|
||||
addr_ptr = (zp + cpu.x) & 0xFF
|
||||
addr = cpu.ram[addr_ptr] | (cpu.ram[(addr_ptr + 1) & 0xFF] << 8)
|
||||
cpu.a = cpu.set_nz(cpu.a ^ cpu.read8(addr))
|
||||
|
||||
def _ADC_indx(cpu):
|
||||
zp = cpu.read8(cpu.pc); cpu.pc = (cpu.pc + 1) & 0xFFFF
|
||||
addr_ptr = (zp + cpu.x) & 0xFF
|
||||
addr = cpu.ram[addr_ptr] | (cpu.ram[(addr_ptr + 1) & 0xFF] << 8)
|
||||
_adc(cpu, cpu.read8(addr))
|
||||
|
||||
def _SBC_indx(cpu):
|
||||
zp = cpu.read8(cpu.pc); cpu.pc = (cpu.pc + 1) & 0xFFFF
|
||||
addr_ptr = (zp + cpu.x) & 0xFF
|
||||
addr = cpu.ram[addr_ptr] | (cpu.ram[(addr_ptr + 1) & 0xFF] << 8)
|
||||
_sbc(cpu, cpu.read8(addr))
|
||||
|
||||
def _CMP_indx(cpu):
|
||||
zp = cpu.read8(cpu.pc); cpu.pc = (cpu.pc + 1) & 0xFFFF
|
||||
addr_ptr = (zp + cpu.x) & 0xFF
|
||||
addr = cpu.ram[addr_ptr] | (cpu.ram[(addr_ptr + 1) & 0xFF] << 8)
|
||||
_cmp_with(cpu, cpu.a, cpu.read8(addr))
|
||||
|
||||
def _CPX_zp(cpu):
|
||||
_cmp_with(cpu, cpu.x, cpu.read8(cpu.addr_zp()))
|
||||
|
||||
def _CPY_zp(cpu):
|
||||
_cmp_with(cpu, cpu.y, cpu.read8(cpu.addr_zp()))
|
||||
|
||||
def _BPL(cpu): cpu.branch(not cpu.n)
|
||||
def _BMI(cpu): cpu.branch(cpu.n)
|
||||
def _BNE(cpu): cpu.branch(not cpu.z)
|
||||
def _BEQ(cpu): cpu.branch(cpu.z)
|
||||
def _BCC(cpu): cpu.branch(not cpu.c)
|
||||
def _BCS(cpu): cpu.branch(cpu.c)
|
||||
def _BVC(cpu): cpu.branch(not cpu.v)
|
||||
def _BVS(cpu): cpu.branch(cpu.v)
|
||||
|
||||
def _JMP_abs(cpu): cpu.pc = cpu.addr_abs()
|
||||
def _JSR(cpu):
|
||||
target = cpu.addr_abs()
|
||||
cpu.push16((cpu.pc - 1) & 0xFFFF)
|
||||
cpu.pc = target
|
||||
def _RTS(cpu):
|
||||
cpu.pc = (cpu.pop16() + 1) & 0xFFFF
|
||||
|
||||
|
||||
# Build dispatch table
|
||||
_DISPATCH = {
|
||||
0x00: _NOP, # treat BRK as NOP (we never expect it)
|
||||
0x18: _CLC, 0x38: _SEC, 0x58: _NOP, 0x78: _SEI, 0xD8: _CLD,
|
||||
0x08: _PHP, 0x28: _PLP, 0x48: _PHA, 0x68: _PLA,
|
||||
0x8A: _TXA, 0xAA: _TAX, 0x98: _TYA, 0xA8: _TAY,
|
||||
0xE8: _INX, 0xC8: _INY, 0xCA: _DEX, 0x88: _DEY,
|
||||
0xA9: _LDA_imm, 0xA5: _LDA_zp, 0xB5: _LDA_zpx, 0xAD: _LDA_abs,
|
||||
0xBD: _LDA_absx, 0xB9: _LDA_absy, 0xB1: _LDA_indy,
|
||||
0xA2: _LDX_imm, 0xAE: _LDX_abs, 0xBE: _LDX_absy, 0xA6: _LDX_zp,
|
||||
0xA0: _LDY_imm, 0xAC: _LDY_abs, 0xBC: _LDY_absx, 0xA4: _LDY_zp,
|
||||
0x85: _STA_zp, 0x95: _STA_zpx, 0x8D: _STA_abs,
|
||||
0x9D: _STA_absx, 0x99: _STA_absy, 0x91: _STA_indy,
|
||||
0x86: _STX_zp, 0x8E: _STX_abs,
|
||||
0x84: _STY_zp, 0x8C: _STY_abs,
|
||||
0x29: _AND_imm, 0x25: _AND_zp, 0x2D: _AND_abs,
|
||||
0x3D: _AND_absx, 0x39: _AND_absy, 0x31: _AND_indy,
|
||||
0x09: _ORA_imm, 0x05: _ORA_zp, 0x0D: _ORA_abs, 0x15: _ORA_zpx,
|
||||
0x1D: _ORA_absx, 0x11: _ORA_indy,
|
||||
0x49: _EOR_imm, 0x4D: _EOR_abs, 0x45: _EOR_zp, 0x55: _EOR_zpx,
|
||||
0x5D: _EOR_absx, 0x59: _EOR_absy,
|
||||
0x69: _ADC_imm, 0x65: _ADC_zp, 0x6D: _ADC_abs, 0x79: _ADC_absy,
|
||||
0x75: _ADC_zpx, 0x7D: _ADC_absx,
|
||||
0xE9: _SBC_imm, 0xE5: _SBC_zp, 0xED: _SBC_abs, 0xFD: _SBC_absx, 0xF9: _SBC_absy,
|
||||
0xC9: _CMP_imm, 0xC5: _CMP_zp, 0xCD: _CMP_abs, 0xD5: _CMP_zpx,
|
||||
0xDD: _CMP_absx, 0xD9: _CMP_absy,
|
||||
0xE0: _CPX_imm, 0xEC: _CPX_abs,
|
||||
0xC0: _CPY_imm,
|
||||
0x24: _BIT_zp, 0x2C: _BIT_abs,
|
||||
0x0A: _ASL_a, 0x06: _ASL_zp, 0x0E: _ASL_abs,
|
||||
0x4A: _LSR_a, 0x46: _LSR_zp, 0x4E: _LSR_abs,
|
||||
0x2A: _ROL_a, 0x26: _ROL_zp, 0x2E: _ROL_abs,
|
||||
0x6A: _ROR_a, 0x66: _ROR_zp, 0x6E: _ROR_abs,
|
||||
0xEE: _INC_abs, 0xFE: _INC_absx, 0xE6: _INC_zp, 0xF6: _INC_zpx,
|
||||
0xCE: _DEC_abs, 0xDE: _DEC_absx, 0xC6: _DEC_zp, 0xD6: _DEC_zpx,
|
||||
0x16: _ASL_zpx, 0x1E: _ASL_absx,
|
||||
0x56: _LSR_zpx, 0x5E: _LSR_absx,
|
||||
0x36: _ROL_zpx, 0x3E: _ROL_absx,
|
||||
0x76: _ROR_zpx, 0x7E: _ROR_absx,
|
||||
0xA1: _LDA_indx, 0x81: _STA_indx, 0x21: _AND_indx,
|
||||
0x01: _ORA_indx, 0x41: _EOR_indx,
|
||||
0x61: _ADC_indx, 0xE1: _SBC_indx, 0xC1: _CMP_indx,
|
||||
0xE4: _CPX_zp, 0xC4: _CPY_zp,
|
||||
0x6C: _JMP_ind,
|
||||
0x9A: _TXS, 0xBA: _TSX,
|
||||
0x58: _CLI, 0xB8: _CLV, 0xF8: _SED,
|
||||
0x96: _STX_zpy, 0x94: _STY_zpx,
|
||||
0xB6: _LDX_zpy, 0xB4: _LDY_zpx,
|
||||
0x10: _BPL, 0x30: _BMI, 0xD0: _BNE, 0xF0: _BEQ,
|
||||
0x90: _BCC, 0xB0: _BCS, 0x50: _BVC, 0x70: _BVS,
|
||||
0x4C: _JMP_abs, 0x20: _JSR, 0x60: _RTS,
|
||||
0xEA: _NOP,
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Run Space Taxi's depacker to produce a FLAT 64K RAM image.
|
||||
|
||||
Why this exists: the disk's "SPACE TAXI+" is a crunched single file (it
|
||||
matches the running game's memory in 0.6% of bytes), so the speech
|
||||
samples cannot be read out of it statically. And a VICE memory dump is
|
||||
no good either -- it captures the banked-IN view, so $A000-$BFFF comes
|
||||
back as BASIC ROM and 13 of the 15 utterances are missing. Every
|
||||
mem0000*.bin in this tree has that problem.
|
||||
|
||||
The way out needs no emulator install: cpu6502.py (next to this script) is a
|
||||
plain 6502 core over a flat 64K bytearray with no banking and no I/O,
|
||||
which is all a depacker needs. Load the .prg, run from the BASIC SYS
|
||||
entry, and the depacked image IS the RAM -- $A000-$BFFF included.
|
||||
|
||||
Feed the result to extractSpeech.py.
|
||||
|
||||
Usage: depackSpeechRam.py [spaceTaxiPlus.prg] [out.bin]
|
||||
Defaults resolve inside stuff/ (gitignored research
|
||||
area, same as genC64Data.py's inputs).
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
STUFF = os.path.join(HERE, "..", "stuff")
|
||||
|
||||
# The BASIC stub is `0B08 D307 9E "2059" 00` -- SYS 2059.
|
||||
ENTRY = 0x080B
|
||||
MAX_STEPS = 60000000
|
||||
CHECK_MASK = 0xFFFF # how often to test for completion
|
||||
|
||||
# Completion test: the $9B00 index table is populated and the first
|
||||
# utterance's $FE lead-in is in place.
|
||||
TABLE_ADDR = 0x9B00
|
||||
FIRST_UTT = 0x9B40
|
||||
|
||||
|
||||
def main():
|
||||
prgPath = sys.argv[1] if len(sys.argv) > 1 else os.path.join(STUFF, "extract", "SPACE TAXI+.prg")
|
||||
outPath = sys.argv[2] if len(sys.argv) > 2 else os.path.join(STUFF, "staxi-ram.bin")
|
||||
|
||||
sys.path.insert(0, HERE)
|
||||
from cpu6502 import Cpu6502
|
||||
|
||||
prg = open(prgPath, "rb").read()
|
||||
load = prg[0] | (prg[1] << 8)
|
||||
ram = bytearray(65536)
|
||||
ram[load:load + len(prg) - 2] = prg[2:]
|
||||
print("loaded %s at $%04X..$%04X" % (os.path.basename(prgPath), load, load + len(prg) - 3))
|
||||
|
||||
cpu = Cpu6502(ram)
|
||||
cpu.pc = ENTRY
|
||||
steps = 0
|
||||
done = False
|
||||
try:
|
||||
while steps < MAX_STEPS:
|
||||
cpu.step()
|
||||
steps += 1
|
||||
if (steps & CHECK_MASK) == 0:
|
||||
if ram[TABLE_ADDR] != 0 and ram[FIRST_UTT] == 0xFE:
|
||||
done = True
|
||||
break
|
||||
except Exception as exc:
|
||||
# A depacker that runs off into I/O or an undocumented opcode
|
||||
# still usually gets the data down first, so report and carry on
|
||||
# to the write rather than throwing the work away.
|
||||
print("6502 stopped after %d steps at PC=$%04X: %s" % (steps, cpu.pc, exc))
|
||||
|
||||
open(outPath, "wb").write(bytes(ram))
|
||||
print("%d steps, depack %s -> %s" % (steps, "detected" if done else "NOT detected", outPath))
|
||||
if not done:
|
||||
print("WARNING: completion check never fired; the image may be partial.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -1,115 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# Drive the captured Space Taxi RAM through the 6502 emulator to
|
||||
# decompress every scene the game knows how to load.
|
||||
#
|
||||
# Approach:
|
||||
# 1. Load a VICE memory dump as flat 64KB RAM. The dump captured at
|
||||
# runtime contains the decompressor at $9656, the scene pointer
|
||||
# table at $9600, and (we hope) the compressed data the pointers
|
||||
# reach into.
|
||||
# 2. Sanity check: run scene ID 1 (the captured live level) through
|
||||
# $6283 and compare the resulting screen buffer at $7530-$7929
|
||||
# with the live screen RAM at $0400-$07E7 already in the dump.
|
||||
# If they match, the emulator + dump together can reproduce any
|
||||
# scene. If not, we know something's missing (likely hidden RAM
|
||||
# under ROMs that VICE didn't capture).
|
||||
# 3. If sanity passes: iterate scene IDs 0..23 (lettered levels)
|
||||
# plus known special IDs (title=$24, level-complete=$19) and
|
||||
# dump each scene's screen + color buffer.
|
||||
|
||||
import sys
|
||||
import os
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from cpu6502 import Cpu6502
|
||||
|
||||
|
||||
def load_dump(path):
|
||||
with open(path, 'rb') as f:
|
||||
raw = f.read()
|
||||
if len(raw) == 0x10000:
|
||||
return bytearray(raw)
|
||||
if len(raw) == 0x10002:
|
||||
# 2-byte LE load-addr header
|
||||
return bytearray(raw[2:])
|
||||
raise SystemExit(f'{path}: unexpected dump size {len(raw)}')
|
||||
|
||||
|
||||
# Memory map landmarks (from MECHANICS.md):
|
||||
ADDR_DECOMP_ENTRY = 0x6283 # bank-switch wrapper around $9656
|
||||
SCREEN_BUF_BASE = 0x7530 # decompressed screen RAM target
|
||||
SCREEN_BUF_SIZE = 0x400 # 4 pages = 1024 bytes
|
||||
COLOR_BUF_BASE = 0x7918 # decompressed color RAM target
|
||||
COLOR_BUF_SIZE = 0x400
|
||||
|
||||
|
||||
def run_scene(ram, scene_id):
|
||||
"""Run $6283(X=scene_id) on a fresh copy of `ram`. Returns a tuple
|
||||
(screen_buf, color_buf, final_ram) so the caller can compare or
|
||||
examine state."""
|
||||
work = bytearray(ram) # copy so callers can keep the original
|
||||
cpu = Cpu6502(work)
|
||||
cpu.a = scene_id
|
||||
cpu.x = scene_id
|
||||
cpu.y = 0
|
||||
cpu.call(ADDR_DECOMP_ENTRY)
|
||||
screen = bytes(work[SCREEN_BUF_BASE:SCREEN_BUF_BASE + SCREEN_BUF_SIZE])
|
||||
color = bytes(work[COLOR_BUF_BASE:COLOR_BUF_BASE + COLOR_BUF_SIZE])
|
||||
return screen, color, work
|
||||
|
||||
|
||||
def compare_screen(decomp_screen, live_screen, label):
|
||||
"""Compare 1024-byte decompressed screen vs live screen RAM.
|
||||
Only the first 1000 bytes are visible (40x25). Bytes 1000..1023
|
||||
are unused tail / overlap with color RAM, so we ignore them."""
|
||||
a = decomp_screen[:1000]
|
||||
b = live_screen[:1000]
|
||||
if a == b:
|
||||
print(f' {label}: MATCH')
|
||||
return True
|
||||
# Show how many cells differ and a sample
|
||||
diffs = [(i, a[i], b[i]) for i in range(1000) if a[i] != b[i]]
|
||||
print(f' {label}: {len(diffs)} / 1000 cells differ')
|
||||
for i, av, bv in diffs[:6]:
|
||||
row, col = divmod(i, 40)
|
||||
print(f' cell ({col:2d},{row:2d}): decomp=${av:02X} live=${bv:02X}')
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print('usage: dumpScenes.py <dump-path> [scene-id]', file=sys.stderr)
|
||||
sys.exit(2)
|
||||
ram = load_dump(sys.argv[1])
|
||||
|
||||
# Figure out what the captured state is, then pick the scene to
|
||||
# test against. $7215 = current level number (0..23 = A..X),
|
||||
# but the title screen / specials don't update it -- the
|
||||
# most-recent JSR $6283 used scene $24 (title) or $19 (other
|
||||
# special). Default to the level-number scene, but allow override.
|
||||
state_level = ram[0x7215]
|
||||
if len(sys.argv) >= 3:
|
||||
scene = int(sys.argv[2], 0)
|
||||
else:
|
||||
# Heuristic: if state sentinel $07A6 says we're on the title,
|
||||
# the live screen matches scene 36 ($24). Otherwise we're
|
||||
# mid-gameplay and the live screen matches scene = level.
|
||||
# Title state in this game: top-left tile is the inner frame
|
||||
# corner $5D / $5C. For now just try scene 36 then scene N.
|
||||
scene = 36
|
||||
print(f'=== Sanity test: scene {scene} (${scene:02X}) vs live screen RAM ===')
|
||||
print(f' captured state: $7215={state_level} $07A6=${ram[0x07A6]:02X}')
|
||||
try:
|
||||
scr, col, _ = run_scene(ram, scene)
|
||||
except Exception as e:
|
||||
print(f' emulator raised: {type(e).__name__}: {e}')
|
||||
return
|
||||
live_scr = bytes(ram[0x0400:0x0400 + 1024])
|
||||
live_col = bytes(b & 0x0F for b in ram[0xD800:0xD800 + 1024])
|
||||
compare_screen(scr, live_scr, 'screen')
|
||||
masked_col = bytes(b & 0x0F for b in col)
|
||||
compare_screen(masked_col, live_col, 'color ')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
103
assets/extractFlame.py
Normal file
103
assets/extractFlame.py
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
#!/usr/bin/env python3
|
||||
# extractFlame.py -- wire the engine-flame sprite cels from the C64 ROM
|
||||
# image (stuff/spacetaxi/raw.bin) into the sprite sheet's flame row.
|
||||
#
|
||||
# The flame is C64 sprite 2, MULTICOLOR (verified: $D01C bit2 set). Its
|
||||
# per-direction cel is chosen from the pointer table at $6DB0 (indexed by
|
||||
# the direction mask $716A). Reading raw.bin at $6DB0 (2-byte load-addr
|
||||
# header -> +2) gives, and the port's kFlameCelByDirMask mirrors exactly:
|
||||
# mask 1 UP -> block $D8 mask 8 RIGHT -> block $D7
|
||||
# mask 2 DOWN -> block $D4 mask 9 UP+RIGHT -> block $D3
|
||||
# mask 4 LEFT -> block $D5 mask 10 DOWN+RIGHT-> block $D6
|
||||
# mask 5 UP+LEFT -> block $D2
|
||||
# mask 6 DOWN+LEFT -> block $D1
|
||||
# flameCels[0..7] load from .spr cels 18..25 (sheet row 2, cols 0..7), so
|
||||
# the cel order below is the kFlameCelByDirMask index order.
|
||||
#
|
||||
# Sprite data lives at block*64 in VIC bank 0 ($3000-$37FF). Each C64
|
||||
# multicolor sprite is 12 MC-pixels x 21 rows (3 bytes/row, 2 bits/pixel);
|
||||
# each MC pixel is 2 hi-res pixels, so it maps 1 MC pixel -> 2 of the
|
||||
# port's 24-wide 4bpp cel pixels. Colors are the live-level flame palette
|
||||
# ($7D05->$D025=$02 red = MC0, $7D06->$D026=$07 yellow = MC1; sprite-2
|
||||
# colour = orange), mapped to sprites.png's indexed palette.
|
||||
#
|
||||
# Patches ONLY the flame row (row 2) of sprites.png -- taxi (row 0),
|
||||
# passenger (row 1), and font.png are left untouched. Re-run the .spr
|
||||
# bake afterwards (make/<port>.mk STAXI_SPR rule).
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
try:
|
||||
from PIL import Image
|
||||
except ImportError:
|
||||
sys.exit("extractFlame.py: needs Pillow (pip install pillow)")
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
RAW = os.path.join(HERE, "..", "..", "..", "stuff", "spacetaxi", "raw.bin")
|
||||
PNG = os.path.join(HERE, "sprites", "sprites.png")
|
||||
|
||||
CEL = 24 # cel is 24x24 px
|
||||
LOAD_HDR = 2 # raw.bin 2-byte load-address header
|
||||
|
||||
# flameCels index -> C64 sprite block (from $6DB0 / kFlameCelByDirMask).
|
||||
FLAME_BLOCKS = [0xD8, 0xD4, 0xD5, 0xD2, 0xD1, 0xD7, 0xD3, 0xD6]
|
||||
|
||||
# Multicolor 2-bit pixel -> palette index. The engine renders sprite
|
||||
# cels through kDefaultPalette (stRender.c), whose index IS the C64 VIC-II
|
||||
# colour code -- the sprite's own baked palette is ignored (loadSpriteSheet
|
||||
# passes NULL). So the C64 flame colours map DIRECTLY by code:
|
||||
# 00 transparent(0), 01 MC0=$D025=$02 red(2), 11 MC1=$D026=$07 yellow(7),
|
||||
# 10 sprite-2 colour -> orange(8) (barely used by these cels).
|
||||
# (NOT the sprites.png preview-palette indices, which differ.)
|
||||
MC_TO_INDEX = {0: 0, 1: 2, 2: 8, 3: 7}
|
||||
|
||||
FLAME_ROW_Y = 2 * CEL # sheet row 2 (flame row) top edge
|
||||
|
||||
|
||||
def loadRaw():
|
||||
with open(RAW, "rb") as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
def celFromBlock(raw, block):
|
||||
# Return a 24x24 list-of-rows of palette indices for one flame block.
|
||||
base = LOAD_HDR + block * 64
|
||||
cel = [[0] * CEL for _ in range(CEL)]
|
||||
for row in range(21): # C64 sprite is 21 rows tall
|
||||
for byteIdx in range(3): # 3 bytes/row
|
||||
b = raw[base + row * 3 + byteIdx]
|
||||
for pair in range(4): # 4 MC pixels per byte (MSB first)
|
||||
mc = (b >> ((3 - pair) * 2)) & 3
|
||||
idx = MC_TO_INDEX[mc]
|
||||
px = (byteIdx * 4 + pair) * 2 # MC pixel -> 2 cel pixels
|
||||
cel[row][px] = idx
|
||||
cel[row][px + 1] = idx
|
||||
return cel
|
||||
|
||||
|
||||
def main():
|
||||
raw = loadRaw()
|
||||
im = Image.open(PNG)
|
||||
if im.mode != "P":
|
||||
sys.exit(f"extractFlame.py: {PNG} is {im.mode}, expected indexed 'P'")
|
||||
px = im.load()
|
||||
nonEmpty = 0
|
||||
for celIdx, block in enumerate(FLAME_BLOCKS):
|
||||
cel = celFromBlock(raw, block)
|
||||
x0 = celIdx * CEL
|
||||
opaque = 0
|
||||
for y in range(CEL):
|
||||
for x in range(CEL):
|
||||
v = cel[y][x]
|
||||
px[x0 + x, FLAME_ROW_Y + y] = v
|
||||
if v != 0:
|
||||
opaque += 1
|
||||
nonEmpty += 1 if opaque else 0
|
||||
print(f" flameCels[{celIdx}] block ${block:02X}: {opaque} opaque px -> cel {18 + celIdx}")
|
||||
im.save(PNG)
|
||||
print(f"extractFlame: patched {nonEmpty}/8 flame cels into {os.path.relpath(PNG, HERE)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -1,225 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Extract Space Taxi's digitised speech from a C64 memory dump.
|
||||
|
||||
FORMAT (reverse-engineered from bankedSfxLoad at $9802, see
|
||||
stuff/live-level1.lst). It is NOT 4-bit $D418 digi and it is
|
||||
NOT the SID speech synth. It is 1-BIT RUN-LENGTH audio:
|
||||
|
||||
* An index table at $9B00 holds [code, ptrLo, ptrHi] triples,
|
||||
terminated by a zero code. `code` is the PETSCII character the game
|
||||
asks for -- 'H' 'T' '!' spell "Hey Taxi!", 'P' + digit + '?' spell
|
||||
"Pad two, please", 'U' is "up".
|
||||
* Each utterance is a byte stream. For every byte the player busy-waits
|
||||
that many passes of a fixed delay loop and then TOGGLES bit 0 of
|
||||
$D417, which flips voice 1 in and out of the filter -- a 1-bit
|
||||
speaker. So each byte is the length of one half-cycle.
|
||||
* $FE is a raster sync (the player waits on $D011), $FF ends the
|
||||
utterance.
|
||||
|
||||
The delay loop at $98A1..$98AA costs 27 cycles per pass and the
|
||||
toggle/advance/fetch tail ($98AC..$987F) another 33, hence CYCLES().
|
||||
|
||||
LIMITATION: a plain VICE memory dump captures the BANKED-IN view, so
|
||||
$A000-$BFFF comes back as BASIC ROM ("CBMBASIC" at $A004) and most of the
|
||||
speech is missing -- the player banks BASIC out ($01 = $06) precisely
|
||||
because the samples live in the RAM underneath. Dump it with the ROMs
|
||||
banked out, e.g. in the VICE monitor:
|
||||
|
||||
bank ram
|
||||
save "staxi-ram.bin" 0 0000 ffff
|
||||
|
||||
Usage: extractSpeech.py <dump.bin> [outDir]
|
||||
The dump may have a 2-byte load address or not; both are handled.
|
||||
"""
|
||||
|
||||
import os
|
||||
import struct
|
||||
import sys
|
||||
import wave
|
||||
|
||||
NTSC_HZ = 1022727.0 # the port already uses this (ST_SID_HZ_NUM)
|
||||
OUT_RATE = 22050
|
||||
TABLE_ADDR = 0x9B00
|
||||
END_MARK = 0xFF
|
||||
SYNC_MARK = 0xFE
|
||||
|
||||
|
||||
def cyclesFor(count):
|
||||
"""Half-cycle length in CPU cycles for one stream byte."""
|
||||
return 27 * count + 33
|
||||
|
||||
|
||||
def loadDump(path):
|
||||
"""Return a 64K-addressable bytes object from a dump with or without
|
||||
a load-address header."""
|
||||
raw = open(path, "rb").read()
|
||||
if len(raw) in (65538, 65536 + 2):
|
||||
return raw[2:]
|
||||
if len(raw) == 65536:
|
||||
return raw
|
||||
# Headerless dump starting at $0801 (what mem0801.prg turned out to be).
|
||||
pad = bytearray(65536)
|
||||
pad[0x0801:0x0801 + len(raw)] = raw[:65536 - 0x0801]
|
||||
return bytes(pad)
|
||||
|
||||
|
||||
def readTable(mem):
|
||||
"""[(code, pointer)] from the $9B00 index."""
|
||||
out = []
|
||||
y = 0
|
||||
while y < 0x300:
|
||||
code = mem[TABLE_ADDR + y]
|
||||
if code == 0 or code == 0xFF:
|
||||
break
|
||||
out.append((code, mem[TABLE_ADDR + y + 1] | (mem[TABLE_ADDR + y + 2] << 8)))
|
||||
y += 3
|
||||
return out
|
||||
|
||||
|
||||
def decode(mem, ptr, limit=0x4000):
|
||||
"""Return (runs, byteCount, sawEnd). runs is [(level, cycles)]."""
|
||||
runs = []
|
||||
level = 0
|
||||
addr = ptr
|
||||
while addr - ptr < limit:
|
||||
b = mem[addr]
|
||||
addr += 1
|
||||
if b == END_MARK:
|
||||
return runs, addr - ptr, True
|
||||
if b == SYNC_MARK:
|
||||
runs.append((level, int(NTSC_HZ / 60.0)))
|
||||
continue
|
||||
runs.append((level, cyclesFor(b)))
|
||||
level ^= 1
|
||||
return runs, addr - ptr, False
|
||||
|
||||
|
||||
def writeWav(runs, path):
|
||||
samples = bytearray()
|
||||
carry = 0.0
|
||||
perOut = NTSC_HZ / OUT_RATE
|
||||
for level, cyc in runs:
|
||||
carry += cyc
|
||||
n = int(carry / perOut)
|
||||
carry -= n * perOut
|
||||
samples += struct.pack("<h", 12000 if level else -12000) * n
|
||||
w = wave.open(path, "wb")
|
||||
w.setnchannels(1)
|
||||
w.setsampwidth(2)
|
||||
w.setframerate(OUT_RATE)
|
||||
w.writeframes(bytes(samples))
|
||||
w.close()
|
||||
return len(samples) // 2 / float(OUT_RATE)
|
||||
|
||||
|
||||
def emitC(mem, table, romLo, outDir):
|
||||
"""Write stSpeechData.c/.h: the raw run-length streams plus an index.
|
||||
|
||||
The streams ship as-is (about 7.4 KB for all 15) and the game expands
|
||||
them to PCM on the fly -- rendering them at bake time would be ~38 KB
|
||||
at 5 kHz, which will not fit an IIgs bank alongside everything else.
|
||||
"""
|
||||
blobs = []
|
||||
index = []
|
||||
for code, ptr in table:
|
||||
if ptr >= romLo:
|
||||
continue
|
||||
runs, nbytes, sawEnd = decode(mem, ptr)
|
||||
nxt = next((q for _, q in sorted(table, key=lambda e: e[1]) if q > ptr), None)
|
||||
if not sawEnd or (nxt is not None and nbytes > nxt - ptr):
|
||||
continue
|
||||
index.append((code, sum(len(b) for b in blobs), nbytes))
|
||||
blobs.append(bytes(mem[ptr:ptr + nbytes]))
|
||||
data = b"".join(blobs)
|
||||
|
||||
h = os.path.join(outDir, "stSpeechData.h")
|
||||
c = os.path.join(outDir, "stSpeechData.c")
|
||||
with open(h, "w") as fp:
|
||||
fp.write("// Generated by assets/extractSpeech.py. Do not hand-edit.\n"
|
||||
"//\n"
|
||||
"// Space Taxi's digitised speech as the C64 stores it: 1-bit\n"
|
||||
"// run-length streams. Each byte is one half-cycle length; the\n"
|
||||
"// player toggles its output after each. $FE is a raster sync\n"
|
||||
"// and $FF ends an utterance. stAudio.c expands these to PCM\n"
|
||||
"// through the jlAudioPlaySfxStream fill callback.\n\n"
|
||||
"#ifndef ST_SPEECH_DATA_H\n#define ST_SPEECH_DATA_H\n\n"
|
||||
"#include <stdint.h>\n\n"
|
||||
"typedef struct {\n"
|
||||
" uint8_t code; // PETSCII: 'H' 'T' '!' 'P' '1'..'9' '?' 'U'\n"
|
||||
" uint16_t offset; // into kStSpeechRle\n"
|
||||
" uint16_t length;\n"
|
||||
"} StSpeechEntryT;\n\n"
|
||||
"#define ST_SPEECH_COUNT %du\n"
|
||||
"#define ST_SPEECH_RLE_BYTES %du\n\n"
|
||||
"extern const StSpeechEntryT kStSpeechIndex[ST_SPEECH_COUNT];\n"
|
||||
"extern const uint8_t kStSpeechRle[ST_SPEECH_RLE_BYTES];\n\n"
|
||||
"#endif\n" % (len(index), len(data)))
|
||||
with open(c, "w") as fp:
|
||||
fp.write('// Generated by assets/extractSpeech.py. Do not hand-edit.\n\n'
|
||||
'#include "stSpeechData.h"\n\n'
|
||||
'const StSpeechEntryT kStSpeechIndex[ST_SPEECH_COUNT] = {\n')
|
||||
for code, off, ln in index:
|
||||
fp.write(" { 0x%02Xu, %5du, %5du }, // '%s'\n"
|
||||
% (code, off, ln, chr(code) if 33 <= code < 127 else "?"))
|
||||
fp.write("};\n\nconst uint8_t kStSpeechRle[ST_SPEECH_RLE_BYTES] = {\n")
|
||||
for i in range(0, len(data), 16):
|
||||
fp.write(" " + " ".join("0x%02X," % b for b in data[i:i + 16]) + "\n")
|
||||
fp.write("};\n")
|
||||
print("\nemitted %s (%d entries) and %s (%d bytes)" % (h, len(index), c, len(data)))
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
sys.exit(__doc__)
|
||||
emit = "--c" in sys.argv
|
||||
args = [a for a in sys.argv[1:] if a != "--c"]
|
||||
mem = loadDump(args[0])
|
||||
outDir = args[1] if len(args) > 1 else "speech"
|
||||
os.makedirs(outDir, exist_ok=True)
|
||||
|
||||
if mem[0xA004:0xA00C] == b"CBMBASIC":
|
||||
sys.stderr.write("WARNING: $A000 holds BASIC ROM, not the RAM under it.\n"
|
||||
" Utterances at $A000 and above will not decode.\n"
|
||||
" Re-dump with `bank ram` (see the module docstring).\n\n")
|
||||
|
||||
table = readTable(mem)
|
||||
romLo = 0xA000 if mem[0xA004:0xA00C] == b"CBMBASIC" else 0x10000
|
||||
ordered = sorted(p for _, p in table)
|
||||
good = 0
|
||||
|
||||
print("%-4s %-6s %7s %7s %s" % ("code", "ptr", "bytes", "dur(s)", "file"))
|
||||
for code, ptr in table:
|
||||
label = chr(code) if 33 <= code < 127 else "?"
|
||||
# Utterances are stored back to back, so the next pointer is the
|
||||
# expected end. Both checks matter: BASIC ROM is full of $FF, so
|
||||
# "found a terminator" alone happily reports success on garbage --
|
||||
# it claimed all 15 decoded from a ROM-shadowed dump.
|
||||
nxt = next((q for q in ordered if q > ptr), None)
|
||||
expected = (nxt - ptr) if nxt else None
|
||||
if ptr >= romLo:
|
||||
print("%-4s $%04X %7s %7s -- SKIPPED (shadowed by BASIC ROM in this dump)"
|
||||
% (label, ptr, "-", "-"))
|
||||
continue
|
||||
runs, nbytes, sawEnd = decode(mem, ptr)
|
||||
if not sawEnd:
|
||||
print("%-4s $%04X %7d %7s -- SKIPPED (no $FF terminator)" % (label, ptr, nbytes, "-"))
|
||||
continue
|
||||
# The terminator must land BEFORE the next utterance starts.
|
||||
# (Entries are back to back but may carry a pad byte, so this is
|
||||
# <= rather than ==.) Overrunning means the $FF we found was not
|
||||
# ours -- which is exactly what happens against a ROM shadow.
|
||||
if expected is not None and nbytes > expected:
|
||||
print("%-4s $%04X %7d %7s -- SKIPPED (ran %d bytes, next entry is %d away)"
|
||||
% (label, ptr, nbytes, "-", nbytes, expected))
|
||||
continue
|
||||
path = os.path.join(outDir, "%02X_%s.wav" % (code, label if label != "?" else "x"))
|
||||
dur = writeWav(runs, path)
|
||||
good += 1
|
||||
print("%-4s $%04X %7d %7.2f %s" % (label, ptr, nbytes, dur, os.path.basename(path)))
|
||||
print("\n%d of %d utterances decoded into %s" % (good, len(table), outDir))
|
||||
if emit:
|
||||
emitC(mem, table, romLo, outDir)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
BIN
assets/font.png
Normal file
BIN
assets/font.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
|
|
@ -4,20 +4,19 @@
|
|||
# exact sprite-to-background collision bits), the standard sprite
|
||||
# bitmaps (cab, passenger, flame, warp, death, intro star, leaving
|
||||
# cels), the demo-mode RNG table ($446A), the SID SFX programs (the
|
||||
# 9-byte blocks fed to $42E9) and the engine-flame pointer table ($6DB0).
|
||||
# (The level-intro star velocity tables, $450C/$4513, went with the moving
|
||||
# warp; claudeDocs/spacetaxiSpriteWarp.patch puts them back.)
|
||||
# 9-byte blocks fed to $42E9), the engine-flame pointer table ($6DB0)
|
||||
# and the level-intro star velocity tables ($450C/$4513).
|
||||
#
|
||||
# Everything comes straight out of stuff/raw.bin (a VICE
|
||||
# Everything comes straight out of stuff/spacetaxi/raw.bin (a VICE
|
||||
# "bank ram" dump, 2-byte load-address header) so the port has one
|
||||
# source of truth: the original bytes. Output:
|
||||
# stC64Data.c / stC64Data.h (committed, do not edit)
|
||||
# examples/spacetaxi/stC64Data.c / stC64Data.h (committed, do not edit)
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
RAW = os.path.join(HERE, "..", "stuff", "raw.bin")
|
||||
RAW = os.path.join(HERE, "..", "..", "..", "stuff", "spacetaxi", "raw.bin")
|
||||
OUT_C = os.path.join(HERE, "..", "stC64Data.c")
|
||||
OUT_H = os.path.join(HERE, "..", "stC64Data.h")
|
||||
|
||||
|
|
@ -56,27 +55,12 @@ def c_bytes(data, per_line=16, indent=" "):
|
|||
return "\n".join(lines)
|
||||
|
||||
|
||||
# The cross-segment accessors the IIgs needs (see the block this emits
|
||||
# into stC64Data.c). These used to be hand-added to the GENERATED file,
|
||||
# which meant re-running this script silently deleted them and broke the
|
||||
# IIgs build -- the header kept declaring them and nothing defined them.
|
||||
# Prototype and return expression per accessor; the header prints the
|
||||
# prototypes aligned, so keep the spacing.
|
||||
ACCESSORS = [
|
||||
("const uint8_t *stC64Charset(void);", "&kStCharset[0][0]"),
|
||||
("const uint8_t *stC64SpriteBitmap(uint8_t idx);", "kStSpriteBitmaps[idx]"),
|
||||
("uint8_t stC64RngByte(uint8_t idx);", "kStRngTable[idx]"),
|
||||
("const uint8_t *stC64SfxProgram(uint8_t idx);", "kStSfxPrograms[idx]"),
|
||||
("uint8_t stC64FlameCel(uint8_t dirMask);", "kStFlameCelByDirMask[dirMask]"),
|
||||
]
|
||||
|
||||
|
||||
def main():
|
||||
raw = load_raw()
|
||||
sprite_count = SPRITE_PTR_LAST - SPRITE_PTR_FIRST + 1
|
||||
|
||||
h = []
|
||||
h.append("// Generated by assets/genC64Data.py from stuff/raw.bin.")
|
||||
h.append("// Generated by assets/genC64Data.py from stuff/spacetaxi/raw.bin.")
|
||||
h.append("// Do not hand-edit; re-run the generator.")
|
||||
h.append("")
|
||||
h.append("#ifndef ST_C64_DATA_H")
|
||||
|
|
@ -116,16 +100,14 @@ def main():
|
|||
h.append("// 8=RIGHT); 0 = no cel for that combination.")
|
||||
h.append("extern const uint8_t kStFlameCelByDirMask[16];")
|
||||
h.append("")
|
||||
h.append("// Cross-segment-safe accessors (see stC64Data.c): reach the tables above")
|
||||
h.append("// by an inter-segment call on the IIgs, where a direct 2-byte data")
|
||||
h.append("// reference from another bank is not relocatable.")
|
||||
for decl in ACCESSORS:
|
||||
h.append(decl[0])
|
||||
h.append("// Level-intro star velocities per sprite 0..6 ($450C / $4513).")
|
||||
h.append("extern const int8_t kStIntroStarDx[7];")
|
||||
h.append("extern const int8_t kStIntroStarDy[7];")
|
||||
h.append("")
|
||||
h.append("#endif")
|
||||
|
||||
c = []
|
||||
c.append("// Generated by assets/genC64Data.py from stuff/raw.bin.")
|
||||
c.append("// Generated by assets/genC64Data.py from stuff/spacetaxi/raw.bin.")
|
||||
c.append("// Do not hand-edit; re-run the generator.")
|
||||
c.append("")
|
||||
c.append('#include "stC64Data.h"')
|
||||
|
|
@ -161,25 +143,11 @@ def main():
|
|||
c.append(c_bytes(flame))
|
||||
c.append("};")
|
||||
c.append("")
|
||||
dx = [b - 256 if b >= 128 else b for b in raw[0x450C:0x450C + 7]]
|
||||
dy = [b - 256 if b >= 128 else b for b in raw[0x4513:0x4513 + 7]]
|
||||
c.append("const int8_t kStIntroStarDx[7] = { " + ", ".join(str(v) for v in dx) + " };")
|
||||
c.append("const int8_t kStIntroStarDy[7] = { " + ", ".join(str(v) for v in dy) + " };")
|
||||
c.append("")
|
||||
c.append("// ---------------------------------------------------------------------------")
|
||||
c.append("// Accessors. On the IIgs the program is split across banks and a 2-byte")
|
||||
c.append("// data reference cannot cross a segment (only a 3-byte JSL/JML can). These")
|
||||
c.append("// tiny functions live in the same segment as the tables, so their reads")
|
||||
c.append("// are intra-segment; callers in other segments reach them by a supported")
|
||||
c.append("// inter-segment call. On the other ports they inline away to nothing.")
|
||||
c.append("// ---------------------------------------------------------------------------")
|
||||
first = True
|
||||
for decl, body in ACCESSORS:
|
||||
c.append("")
|
||||
if not first:
|
||||
c.append("")
|
||||
first = False
|
||||
# The header pads return types to align its prototypes; the
|
||||
# definitions here must not inherit that padding.
|
||||
c.append(" ".join(decl[:-1].split()) + " {")
|
||||
c.append(" return " + body + ";")
|
||||
c.append("}")
|
||||
|
||||
with open(OUT_H, "w", encoding="ascii") as fp:
|
||||
fp.write("\n".join(h) + "\n")
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
import os
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
EXTRACT = os.path.join(HERE, "..", "stuff", "extract")
|
||||
EXTRACT = os.path.join(HERE, "..", "..", "..", "stuff", "spacetaxi", "extract")
|
||||
OUT = os.path.join(HERE, "..", "stDemoStreams.h")
|
||||
|
||||
# (file, symbol, 0-based level index, screen letter) in ROTATION order.
|
||||
|
|
@ -50,7 +50,7 @@ def main():
|
|||
# The playback buffer at $0902 as the boot leaves it (demo H
|
||||
# loaded over older RAM): recordings shorter than the previous one
|
||||
# leave its tail in place, and the game reads on into it.
|
||||
raw = open(os.path.join(HERE, "..", "stuff", "raw.bin"), "rb").read()[2:]
|
||||
raw = open(os.path.join(HERE, "..", "..", "..", "stuff", "spacetaxi", "raw.bin"), "rb").read()[2:]
|
||||
buf = raw[0x0902:0x0902 + 640]
|
||||
f.write("#define ST_DEMO_BUFFER_BYTES 640u\n")
|
||||
f.write("static const uint8_t kDemoBufferInit[ST_DEMO_BUFFER_BYTES] = {\n")
|
||||
|
|
|
|||
301
assets/genPlaceholderArt.py
Normal file
301
assets/genPlaceholderArt.py
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
#!/usr/bin/env python3
|
||||
# genPlaceholderArt.py -- generate generic geometric placeholder art
|
||||
# for the Space Taxi port. Output is 320x200 indexed PNG, 16-color.
|
||||
# Replace these with hand-authored art any time; the build pipeline
|
||||
# picks up whichever PNG sits in assets/.
|
||||
|
||||
import os
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
ROOT = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
# 16-color palette matching the engine's kDefaultPalette (see stRender.c).
|
||||
# Each entry is (R, G, B) in 0..255. The .jas format will store the
|
||||
# 4-bit-per-channel quantization automatically via joeyasset.
|
||||
PALETTE_RGB = [
|
||||
(0x00, 0x00, 0x00), # 0 black
|
||||
(0x00, 0x00, 0x77), # 1 dark blue
|
||||
(0xCC, 0x00, 0x00), # 2 red
|
||||
(0x00, 0x77, 0x00), # 3 dark green
|
||||
(0x66, 0x44, 0x00), # 4 brown
|
||||
(0xEE, 0xEE, 0x00), # 5 yellow
|
||||
(0x88, 0x88, 0x88), # 6 mid gray
|
||||
(0xBB, 0xBB, 0xBB), # 7 light gray
|
||||
(0x44, 0x66, 0xFF), # 8 sky blue
|
||||
(0x77, 0x88, 0xFF), # 9 light blue
|
||||
(0xFF, 0x88, 0x88), # 10 light red
|
||||
(0x88, 0xFF, 0x44), # 11 light green
|
||||
(0xFF, 0x88, 0x00), # 12 orange
|
||||
(0xFF, 0x00, 0xFF), # 13 magenta
|
||||
(0x00, 0xFF, 0xFF), # 14 cyan
|
||||
(0xFF, 0xFF, 0xFF), # 15 white
|
||||
]
|
||||
|
||||
|
||||
def new_canvas(w=320, h=200):
|
||||
"""Returns a fresh w x h indexed PIL image with the palette installed."""
|
||||
img = Image.new('P', (w, h), 0)
|
||||
flat = []
|
||||
for r, g, b in PALETTE_RGB:
|
||||
flat.extend([r, g, b])
|
||||
flat.extend([0] * (768 - len(flat))) # pad to 256 entries
|
||||
img.putpalette(flat)
|
||||
return img
|
||||
|
||||
|
||||
def write_indexed_png(img, path):
|
||||
"""Save with mode='P' so PNG output is indexed (joeyasset wants this
|
||||
when converted through ppm; ImageMagick's `convert` preserves
|
||||
indexed mode when writing PPM)."""
|
||||
img.save(path, 'PNG', optimize=True)
|
||||
print(f" wrote {path}")
|
||||
|
||||
|
||||
def gen_tilebank():
|
||||
"""tilebank0.png: 320x200, 40x25 cells of 8x8 each (= 1000 tile
|
||||
slots). The engine convention is:
|
||||
index 0 = empty (non-solid)
|
||||
index 1..63 = solid walls
|
||||
index 64..127 = landing-pad surfaces
|
||||
index 128..255 = decorative non-solid
|
||||
We fill a few representative slots; the rest stay black so they
|
||||
show as "empty" if a level references them.
|
||||
"""
|
||||
img = new_canvas()
|
||||
px = img.load()
|
||||
|
||||
def draw_tile(idx, painter):
|
||||
bx = (idx % 40) * 8
|
||||
by = (idx // 40) * 8
|
||||
painter(bx, by)
|
||||
|
||||
def block(color):
|
||||
return lambda x, y: [px.__setitem__((x+dx, y+dy), color)
|
||||
for dx in range(8) for dy in range(8)]
|
||||
|
||||
def bricks(color_a, color_b):
|
||||
def paint(x, y):
|
||||
for dy in range(8):
|
||||
row_offset = 4 if (y // 8 + dy // 4) % 2 == 0 else 0
|
||||
for dx in range(8):
|
||||
is_grout = (dx == (row_offset % 8)) or (dy == 3 or dy == 7)
|
||||
px[x + dx, y + dy] = color_b if is_grout else color_a
|
||||
return paint
|
||||
|
||||
def stripes(color_a, color_b):
|
||||
def paint(x, y):
|
||||
for dy in range(8):
|
||||
c = color_a if (dy + (x // 8)) % 2 == 0 else color_b
|
||||
for dx in range(8):
|
||||
px[x + dx, y + dy] = c
|
||||
return paint
|
||||
|
||||
def landing_pad(color_a, color_b):
|
||||
# diagonal stripes signal a landing-pad surface
|
||||
def paint(x, y):
|
||||
for dy in range(8):
|
||||
for dx in range(8):
|
||||
px[x + dx, y + dy] = color_a if ((dx + dy) // 2) % 2 == 0 else color_b
|
||||
return paint
|
||||
|
||||
# tile 0 -> already black (empty); leave as-is
|
||||
|
||||
# walls 1..7: a variety of brick/stripe patterns
|
||||
draw_tile(1, bricks(6, 0)) # gray brick
|
||||
draw_tile(2, bricks(4, 0)) # brown brick
|
||||
draw_tile(3, block(6)) # solid gray
|
||||
draw_tile(4, block(4)) # solid brown
|
||||
draw_tile(5, stripes(6, 7)) # gray stripes (ceiling)
|
||||
draw_tile(6, stripes(1, 8)) # blue stripes (sky deco)
|
||||
draw_tile(7, block(7)) # solid light gray
|
||||
|
||||
# landing pad tiles 64..67
|
||||
draw_tile(64, landing_pad(5, 12)) # yellow/orange pad
|
||||
draw_tile(65, landing_pad(11, 3)) # green pad
|
||||
draw_tile(66, landing_pad(9, 8)) # blue pad
|
||||
draw_tile(67, landing_pad(15, 7)) # white pad
|
||||
|
||||
write_indexed_png(img, os.path.join(ROOT, 'tiles', 'tilebank0.png'))
|
||||
|
||||
|
||||
def gen_sprites():
|
||||
"""RETIRED 2026-07-20: sprites.png is now fully extracted from the
|
||||
C64 dump by stuff/spacetaxi/extractSprites.py (taxi $C1/$C0/$DC/
|
||||
$DD, passenger $C4-$CB/$D9, flame $6DB0 table, warp $E2-$E8 --
|
||||
216x96, 4 rows). Regenerating placeholder art here would clobber
|
||||
the real sheet, the same trap gen_font has with the hand-edited
|
||||
font.png. Refuses to run; use extractSprites.py instead."""
|
||||
print("gen_sprites: REFUSING -- sprites.png is real C64 art now.")
|
||||
print(" regenerate via: python3 stuff/spacetaxi/extractSprites.py "
|
||||
"stuff/spacetaxi/raw.bin examples/spacetaxi/assets/sprites/sprites.png")
|
||||
return
|
||||
|
||||
|
||||
def gen_sprites_retired():
|
||||
img = new_canvas(9 * 24, 3 * 24) # 216x72: 9 cols x 3 rows of 24x24
|
||||
d = ImageDraw.Draw(img)
|
||||
|
||||
# ---- Taxi cels: simple side-view "pod with thruster" ----
|
||||
# Cel 0 = idle (thruster off)
|
||||
# Cel 1..3 = thrust-on, alternating flame frames
|
||||
for cel in range(4):
|
||||
x = cel * 24
|
||||
# Pod body: rounded rectangle in gray
|
||||
d.rectangle([x+2, 4, x+21, 16], fill=7, outline=15)
|
||||
# Cockpit window in blue
|
||||
d.rectangle([x+13, 6, x+19, 10], fill=8, outline=15)
|
||||
# Landing skid
|
||||
d.line([(x+3, 17), (x+20, 17)], fill=6)
|
||||
d.line([(x+3, 17), (x+3, 19)], fill=6)
|
||||
d.line([(x+20,17), (x+20, 19)], fill=6)
|
||||
# Thruster flame (cels 1..3 only, alternating shape)
|
||||
if cel >= 1:
|
||||
flame_color = [2, 12, 5][(cel - 1) % 3]
|
||||
flame_top = 18
|
||||
flame_bot = 22 if cel != 2 else 23
|
||||
d.polygon([(x+8, flame_top),
|
||||
(x+15, flame_top),
|
||||
(x+12, flame_bot)], fill=flame_color)
|
||||
|
||||
# ---- Taxi shrink cels (transporter warp): the cab receding into the
|
||||
# distance, 5 progressively smaller sizes, centered in the 24x24 cell.
|
||||
# Placed in the free row-0 slots (cels 4..8). No flame -- it is warping
|
||||
# away, not thrusting. The renderer draws one per warp step ($E2..$E9
|
||||
# on the C64 = 8 shrink cels; the port approximates with 5 sizes).
|
||||
for i in range(5):
|
||||
cx = (4 + i) * 24 + 12
|
||||
scale = 1.0 - i * 0.19 # 1.0, 0.81, 0.62, 0.43, 0.24
|
||||
bw = max(3, int(round(19 * scale)))
|
||||
bh = max(2, int(round(12 * scale)))
|
||||
x0 = cx - bw // 2
|
||||
y0 = 12 - bh // 2
|
||||
# Pod body (gray, white outline)
|
||||
d.rectangle([x0, y0, x0 + bw, y0 + bh], fill=7, outline=15)
|
||||
# Cockpit window (blue) while big enough to show
|
||||
if bw >= 8:
|
||||
d.rectangle([x0 + bw - 5, y0 + 1, x0 + bw - 1, y0 + 3], fill=8)
|
||||
|
||||
# ---- Passenger cels: small stick figure, walk cycle ----
|
||||
# Centered in 16x16, walking right.
|
||||
for cel in range(4):
|
||||
x = cel * 16
|
||||
y = 24
|
||||
# Head
|
||||
d.ellipse([x+5, y+1, x+10, y+6], fill=10, outline=15)
|
||||
# Body
|
||||
d.line([(x+7, y+7), (x+7, y+12)], fill=15)
|
||||
# Arms (swing alternately based on cel)
|
||||
if cel == 0 or cel == 2:
|
||||
d.line([(x+4, y+10), (x+10, y+10)], fill=15)
|
||||
else:
|
||||
d.line([(x+3, y+9), (x+11, y+11)], fill=15)
|
||||
# Legs (alternating stride)
|
||||
if cel == 0 or cel == 2:
|
||||
d.line([(x+7, y+12), (x+4, y+15)], fill=15)
|
||||
d.line([(x+7, y+12), (x+10,y+15)], fill=15)
|
||||
else:
|
||||
d.line([(x+7, y+12), (x+5, y+15)], fill=15)
|
||||
d.line([(x+7, y+12), (x+9, y+15)], fill=15)
|
||||
|
||||
write_indexed_png(img, os.path.join(ROOT, 'sprites', 'sprites.png'))
|
||||
|
||||
|
||||
def gen_font():
|
||||
"""font.png: 320x200 (40x25 grid of 8x8 ASCII glyphs).
|
||||
Authored manually for the printable subset (0x20..0x7E).
|
||||
Each glyph cell is at (ascii%40, ascii/40) when ascii<128.
|
||||
Unfilled cells stay black (treated as TILE_NO_GLYPH at runtime).
|
||||
|
||||
WARNING: this generator is UPPERCASE-ONLY (lowercase maps to the
|
||||
uppercase glyphs). The COMMITTED font.png has been hand-extended with
|
||||
real lowercase glyphs, so running this clobbers it -- the HUD reverts
|
||||
to all-caps. Regenerate SPRITES only (gen_sprites) unless you also add
|
||||
the lowercase glyphs here first; then `git checkout` font.png.
|
||||
"""
|
||||
# A compact 5-pixel-wide bitmap font, drawn in 7 rows with 1px
|
||||
# padding on all sides (so 5x7 active inside an 8x8 cell, baseline
|
||||
# at row 6). The font supports digits, uppercase letters, space,
|
||||
# and basic punctuation -- enough for HUD text. Lowercase letters
|
||||
# map to the same glyphs as uppercase (room to add later).
|
||||
GLYPHS = {
|
||||
' ': [],
|
||||
'!': ['..#..', '..#..', '..#..', '..#..', '.....', '..#..', '.....'],
|
||||
'"': ['.#.#.', '.#.#.', '.....', '.....', '.....', '.....', '.....'],
|
||||
'0': ['.###.', '#...#', '#..##', '#.#.#', '##..#', '#...#', '.###.'],
|
||||
'1': ['..#..', '.##..', '..#..', '..#..', '..#..', '..#..', '.###.'],
|
||||
'2': ['.###.', '#...#', '....#', '...#.', '..#..', '.#...', '#####'],
|
||||
'3': ['.###.', '#...#', '....#', '..##.', '....#', '#...#', '.###.'],
|
||||
'4': ['...#.', '..##.', '.#.#.', '#..#.', '#####', '...#.', '...#.'],
|
||||
'5': ['#####', '#....', '####.', '....#', '....#', '#...#', '.###.'],
|
||||
'6': ['.###.', '#....', '#....', '####.', '#...#', '#...#', '.###.'],
|
||||
'7': ['#####', '....#', '...#.', '..#..', '..#..', '..#..', '..#..'],
|
||||
'8': ['.###.', '#...#', '#...#', '.###.', '#...#', '#...#', '.###.'],
|
||||
'9': ['.###.', '#...#', '#...#', '.####', '....#', '....#', '.###.'],
|
||||
':': ['.....', '..#..', '.....', '.....', '.....', '..#..', '.....'],
|
||||
'.': ['.....', '.....', '.....', '.....', '.....', '..#..', '.....'],
|
||||
'-': ['.....', '.....', '.....', '#####', '.....', '.....', '.....'],
|
||||
'/': ['....#', '...#.', '...#.', '..#..', '.#...', '.#...', '#....'],
|
||||
'A': ['.###.', '#...#', '#...#', '#####', '#...#', '#...#', '#...#'],
|
||||
'B': ['####.', '#...#', '#...#', '####.', '#...#', '#...#', '####.'],
|
||||
'C': ['.###.', '#...#', '#....', '#....', '#....', '#...#', '.###.'],
|
||||
'D': ['####.', '#...#', '#...#', '#...#', '#...#', '#...#', '####.'],
|
||||
'E': ['#####', '#....', '#....', '####.', '#....', '#....', '#####'],
|
||||
'F': ['#####', '#....', '#....', '####.', '#....', '#....', '#....'],
|
||||
'G': ['.###.', '#...#', '#....', '#..##', '#...#', '#...#', '.###.'],
|
||||
'H': ['#...#', '#...#', '#...#', '#####', '#...#', '#...#', '#...#'],
|
||||
'I': ['.###.', '..#..', '..#..', '..#..', '..#..', '..#..', '.###.'],
|
||||
'J': ['..###', '...#.', '...#.', '...#.', '...#.', '#..#.', '.##..'],
|
||||
'K': ['#...#', '#..#.', '#.#..', '##...', '#.#..', '#..#.', '#...#'],
|
||||
'L': ['#....', '#....', '#....', '#....', '#....', '#....', '#####'],
|
||||
'M': ['#...#', '##.##', '#.#.#', '#.#.#', '#...#', '#...#', '#...#'],
|
||||
'N': ['#...#', '##..#', '#.#.#', '#.#.#', '#..##', '#...#', '#...#'],
|
||||
'O': ['.###.', '#...#', '#...#', '#...#', '#...#', '#...#', '.###.'],
|
||||
'P': ['####.', '#...#', '#...#', '####.', '#....', '#....', '#....'],
|
||||
'Q': ['.###.', '#...#', '#...#', '#...#', '#.#.#', '#..#.', '.##.#'],
|
||||
'R': ['####.', '#...#', '#...#', '####.', '#.#..', '#..#.', '#...#'],
|
||||
'S': ['.###.', '#...#', '#....', '.###.', '....#', '#...#', '.###.'],
|
||||
'T': ['#####', '..#..', '..#..', '..#..', '..#..', '..#..', '..#..'],
|
||||
'U': ['#...#', '#...#', '#...#', '#...#', '#...#', '#...#', '.###.'],
|
||||
'V': ['#...#', '#...#', '#...#', '#...#', '.#.#.', '.#.#.', '..#..'],
|
||||
'W': ['#...#', '#...#', '#...#', '#.#.#', '#.#.#', '##.##', '#...#'],
|
||||
'X': ['#...#', '#...#', '.#.#.', '..#..', '.#.#.', '#...#', '#...#'],
|
||||
'Y': ['#...#', '#...#', '.#.#.', '..#..', '..#..', '..#..', '..#..'],
|
||||
'Z': ['#####', '....#', '...#.', '..#..', '.#...', '#....', '#####'],
|
||||
}
|
||||
|
||||
img = new_canvas()
|
||||
px = img.load()
|
||||
|
||||
def paint_glyph(ascii_code, glyph_rows):
|
||||
col = ascii_code % 40
|
||||
row = ascii_code // 40
|
||||
bx = col * 8
|
||||
by = row * 8
|
||||
# active 5x7 area: cell-x 1..5, cell-y 0..6 (top row, 1px left pad)
|
||||
for ry, line in enumerate(glyph_rows):
|
||||
for rx, ch in enumerate(line):
|
||||
if ch == '#':
|
||||
px[bx + 1 + rx, by + ry] = 15 # white
|
||||
|
||||
for ascii_code in range(32, 127):
|
||||
c = chr(ascii_code)
|
||||
if c in GLYPHS:
|
||||
paint_glyph(ascii_code, GLYPHS[c])
|
||||
elif 'a' <= c <= 'z':
|
||||
paint_glyph(ascii_code, GLYPHS[c.upper()])
|
||||
|
||||
write_indexed_png(img, os.path.join(ROOT, 'font.png'))
|
||||
|
||||
|
||||
def main():
|
||||
os.makedirs(os.path.join(ROOT, 'tiles'), exist_ok=True)
|
||||
os.makedirs(os.path.join(ROOT, 'sprites'), exist_ok=True)
|
||||
print("genPlaceholderArt: emitting 320x200 indexed PNGs...")
|
||||
gen_tilebank()
|
||||
gen_sprites()
|
||||
gen_font()
|
||||
print("done.")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# Bake assets/songs/song*.song into stSongData.h with JoeyLib's songbake
|
||||
# (JOEYLIB names the checkout, ../joeylib by default).
|
||||
#
|
||||
# python3 assets/genSongData.py
|
||||
#
|
||||
# The .song sources come from the game's own player run under a 6502
|
||||
# emulator: assets/sidCapture.py logs every SID write per PAL frame and
|
||||
# assets/sidToSong.py turns that into songbake text.
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
here = os.path.dirname(os.path.abspath(__file__))
|
||||
joeylib = os.environ.get("JOEYLIB", os.path.join(here, "..", "..", "joeylib"))
|
||||
songbake = os.path.join(joeylib, "tools", "songbake", "songbake.py")
|
||||
out = os.path.join(here, "..", "stSongData.h")
|
||||
count = 9
|
||||
|
||||
header = """// Space Taxi (C64) tunes as JoeyLib JYM1 streams. GENERATED by
|
||||
// assets/genSongData.py -- do not hand-edit. The songs were extracted by
|
||||
// running the game's own music player under a 6502 emulator
|
||||
// (assets/sidCapture.py logs every SID write per PAL frame;
|
||||
// assets/sidToSong.py turns that into songbake text in assets/songs/).
|
||||
//
|
||||
// Where the C64 plays them:
|
||||
// BLOCKING via $44CF (the game spins until the tune ends; the port
|
||||
// parks in ST_STATE_JINGLE with the sim frozen):
|
||||
// 0..3 start of every screen, rotating on $5E9B ($6906)
|
||||
// 4 level 25 start ($6935) and the bonus cab ($70B1)
|
||||
// 5 game over ($5C1F), after the two-second GAME OVER! wait
|
||||
// NON-BLOCKING via $CB02 (started and left to the IRQ player):
|
||||
// 6 the high-score name entry screen ($4E35) -- no port screen yet
|
||||
// 7 the high-score table ($4C1F, joystick UP on the title)
|
||||
// 8 the level intro while the cab flies in ($459C); endless, so
|
||||
// the source is trimmed to 30 s and looped
|
||||
#ifndef ST_SONG_DATA_H
|
||||
#define ST_SONG_DATA_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
"""
|
||||
|
||||
footer = """typedef struct {
|
||||
const uint8_t *data;
|
||||
uint16_t length;
|
||||
} StSongT;
|
||||
|
||||
#define ST_SONG_COUNT %du
|
||||
#define ST_SONG_BONUS 4u
|
||||
#define ST_SONG_GAME_OVER 5u
|
||||
#define ST_SONG_NAME_ENTRY 6u
|
||||
#define ST_SONG_SCORES 7u
|
||||
#define ST_SONG_INTRO 8u
|
||||
|
||||
static const StSongT kStSongs[ST_SONG_COUNT] = {
|
||||
%s};
|
||||
|
||||
#endif // ST_SONG_DATA_H
|
||||
"""
|
||||
|
||||
parts = []
|
||||
for n in range(count):
|
||||
src = os.path.join(here, "songs", "song%d.song" % n)
|
||||
with tempfile.NamedTemporaryFile(suffix=".h", delete=False) as tmp:
|
||||
path = tmp.name
|
||||
subprocess.run([sys.executable, songbake, "-c", "gStSong%d" % n, src, path], check=True, stderr=subprocess.DEVNULL)
|
||||
body = open(path).read()
|
||||
os.unlink(path)
|
||||
body = "\n".join(l for l in body.split("\n") if l.startswith("static") or l.startswith(" ") or l.startswith("}"))
|
||||
parts.append(body.rstrip() + "\n\n")
|
||||
|
||||
table = "".join(" { gStSong%d, (uint16_t)sizeof(gStSong%d) },\n" % (n, n) for n in range(count))
|
||||
with open(out, "w") as fp:
|
||||
fp.write(header + "".join(parts) + footer % (count, table))
|
||||
print("wrote", os.path.normpath(out))
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
"""genSpeechPcm.py - bake Space Taxi's speech to 8-bit PCM.
|
||||
Reads the C64 run-length speech streams from stSpeechData.c (the output
|
||||
of extractSpeech.py) and writes speech.bin: every utterance decoded to
|
||||
5 kHz sample bytes for each passenger pitch the game can select, in BOTH
|
||||
polarities. The decode is the exact algorithm stAudio.c used to run per
|
||||
sample at play time; on the 65816 that took 2.4 s of CPU per second of
|
||||
speech (three run bytes per sample, each a multiply) and stalled the ride
|
||||
for half a second on every "HEY TAXI". A 1-bit intermediate format came
|
||||
next, but expanding bits still needed a per-byte loop (and an asm copy of
|
||||
it on the IIgs); with sample bytes the player is a block copy.
|
||||
Polarity: an utterance is decoded from a +100 start. The C64 keeps the
|
||||
output level running from one utterance into the next, so a phrase whose
|
||||
first word ends on the low level plays its second word inverted. Both
|
||||
versions are stored and the player picks by the running parity.
|
||||
speech.bin (little-endian):
|
||||
"STSP" u8 version(3) u8 pitchBase u8 pitchCount u8 count
|
||||
u8 format (0 = signed +/-100, 1 = 0x80-biased for the IIgs raw stream)
|
||||
u8 polarities (1 or 2) u8 pad[2] u32 dataBytes
|
||||
entries[pitchCount * count * polarities]: u8 code, u8 parity,
|
||||
u16 samples, u32 offset -- polarity is the innermost index
|
||||
(0 = as decoded, 1 = inverted). parity = 1 when the utterance
|
||||
ends on the opposite level from the one it started on.
|
||||
data: sample bytes
|
||||
With --single only polarity 0 is written, halving the file; the player
|
||||
negates the samples itself when the running parity calls for it. The
|
||||
X68000 needs that: its Human68k floppy cannot spare the other 60 KB.
|
||||
usage: genSpeechPcm.py stSpeechData.c speech.bin [--biased] [--single]
|
||||
"""
|
||||
import re
|
||||
import struct
|
||||
import sys
|
||||
|
||||
SID_HZ = 1022727 # ST_SID_HZ_NUM
|
||||
RATE = 5000 # ST_SPEECH_RATE
|
||||
CYC_PER_SAMPLE = SID_HZ // RATE
|
||||
SYNC_CYCLES = SID_HZ // 60 // 8
|
||||
CYCLE_TAIL = 33
|
||||
SYNC = 0xFE
|
||||
END = 0xFF
|
||||
PITCH_BASE = 2 # stFare.c: stAudioSpeech(rng(3) + 2)
|
||||
PITCH_COUNT = 3
|
||||
|
||||
|
||||
def parseSource(path):
|
||||
text = open(path).read()
|
||||
idx = re.search(r"kStSpeechIndex\[[^\]]*\]\s*=\s*\{(.*?)\};", text, re.S).group(1)
|
||||
entries = [(int(c, 16), int(o), int(n)) for c, o, n in
|
||||
re.findall(r"\{\s*0x([0-9A-Fa-f]+)u,\s*(\d+)u,\s*(\d+)u\s*\}", idx)]
|
||||
rle = re.search(r"kStSpeechRle\[[^\]]*\]\s*=\s*\{(.*?)\};", text, re.S).group(1)
|
||||
data = bytes(int(x, 16) for x in re.findall(r"0x([0-9A-Fa-f]{2})", rle))
|
||||
return entries, data
|
||||
|
||||
|
||||
def decode(rle, perOn):
|
||||
"""speechDecode() for one utterance from a +100 start, remain 0."""
|
||||
remain = 0
|
||||
level = 100
|
||||
out = []
|
||||
i = 0
|
||||
n = len(rle)
|
||||
while True:
|
||||
while remain <= 0:
|
||||
if i >= n:
|
||||
return out, level
|
||||
b = rle[i]
|
||||
i += 1
|
||||
if b == END:
|
||||
i = n
|
||||
continue
|
||||
if b == SYNC:
|
||||
remain += SYNC_CYCLES
|
||||
continue
|
||||
remain += perOn * b + CYCLE_TAIL
|
||||
level = -100 if level > 0 else 100
|
||||
out.append(level)
|
||||
remain -= CYC_PER_SAMPLE
|
||||
|
||||
|
||||
def main():
|
||||
args = [a for a in sys.argv[1:] if not a.startswith("--")]
|
||||
biased = "--biased" in sys.argv[1:]
|
||||
single = "--single" in sys.argv[1:]
|
||||
polarities = 1 if single else 2
|
||||
src, dst = args[0], args[1]
|
||||
entries, data = parseSource(src)
|
||||
records = []
|
||||
blob = bytearray()
|
||||
for p in range(PITCH_COUNT):
|
||||
perOn = 5 * (PITCH_BASE + p) + 12
|
||||
for code, off, length in entries:
|
||||
samples, level = decode(data[off:off + length], perOn)
|
||||
parity = 1 if level < 0 else 0
|
||||
for pol in range(polarities):
|
||||
pcm = bytearray()
|
||||
for s in samples:
|
||||
v = -s if pol else s
|
||||
pcm.append((v + 0x80) & 0xFF if biased else v & 0xFF)
|
||||
records.append((code, parity, len(samples), len(blob)))
|
||||
blob += pcm
|
||||
with open(dst, "wb") as f:
|
||||
f.write(b"STSP" + struct.pack("<BBBBBBBBI", 3, PITCH_BASE, PITCH_COUNT, len(entries), 1 if biased else 0, polarities, 0, 0, len(blob)))
|
||||
for code, parity, n, off in records:
|
||||
f.write(struct.pack("<BBHI", code, parity, n, off))
|
||||
f.write(blob)
|
||||
total = sum(n for _, _, n, _ in records)
|
||||
print(f"{dst}: {len(entries)} utterances x {PITCH_COUNT} pitches x {polarities} "
|
||||
f"polarit{'y' if polarities == 1 else 'ies'}, {total} samples, {len(blob)} data bytes, "
|
||||
f"{'biased' if biased else 'signed'}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
# Space Taxi level .dat format (STL2)
|
||||
|
||||
A small custom binary format that `stLevelLoad()` reads at scene
|
||||
boot. Output files land in `generated/levels/`
|
||||
boot. Output files land in `examples/spacetaxi/generated/levels/`
|
||||
(from `romToLevel.py` for the 24 canonical C64 levels, or from
|
||||
`mkstlevel` for hand-authored levels in `assets/levels/*.txt`). The
|
||||
build copies them under `DATA/levels/levelNN.dat` in the runtime
|
||||
|
|
@ -74,15 +74,15 @@ without changes.
|
|||
1. Author a 256-tile (or smaller) tile sheet as an indexed PNG. Each
|
||||
tile is 8x8 px. Arrange in a grid; the bank loader assumes row-
|
||||
major, tile-index = `ty * tilesPerRow + tx`. Drop the PNG at
|
||||
`assets/tiles/tbank<N>.png`.
|
||||
`examples/spacetaxi/assets/tiles/tbank<N>.png`.
|
||||
2. The Makefile bakes per-target via `tools/assetbake/assetbake.py
|
||||
--type tile --target <port> tbank<N>.png tbank<N>.tbk`. Output
|
||||
lands in `generated/<port>/tiles/` and is
|
||||
lands in `examples/spacetaxi/generated/<port>/tiles/` and is
|
||||
staged into the runtime tree at `build/<port>/.../DATA/tiles/`.
|
||||
3. Author each level layout in a text editor or grid tool, save as a
|
||||
.dat per this spec. The helper `mkstlevel`
|
||||
.dat per this spec. The helper `examples/spacetaxi/mkstlevel`
|
||||
converts from a human-readable text grid to .dat. The 24 canonical
|
||||
C64 levels are emitted by `assets/romToLevel.py` directly
|
||||
C64 levels are emitted by `stuff/spacetaxi/romToLevel.py` directly
|
||||
from a raw VICE dump of the original .prg.
|
||||
|
||||
## Naming
|
||||
|
|
|
|||
|
|
@ -1,205 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# Convert ROM-extracted Space Taxi scenes directly into the runtime
|
||||
# STL1 binary format (see stLevel.c). Uses the
|
||||
# 6502 emulator + raw RAM dump to decompress each scene, then writes
|
||||
# screen + color RAM bytes straight into the .dat layout the JoeyLib
|
||||
# port loads at runtime.
|
||||
#
|
||||
# Replaces the .txt -> mkstlevel pipeline for canonical levels. The C
|
||||
# mkstlevel tool stays in tree for hand-authored .txt levels.
|
||||
#
|
||||
# Usage:
|
||||
# romToLevel.py <raw-dump> <out-dir> # all 24 levels
|
||||
# romToLevel.py <raw-dump> <out-dir> <scene_id> # one specific
|
||||
#
|
||||
# Writes:
|
||||
# <out-dir>/level01.dat ... level24.dat (scene 0 = "A" = level01)
|
||||
# <out-dir>/tbank0.png (charset extracted from $2800)
|
||||
|
||||
import os
|
||||
import struct
|
||||
import sys
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from cpu6502 import Cpu6502
|
||||
from dumpScenes import load_dump, run_scene
|
||||
|
||||
|
||||
# STL2 binary layout (see stLevel.c):
|
||||
# "STL2"
|
||||
# nameLen + name
|
||||
# tileBank, music, bgColor, borderColor, taxiSpawnX, taxiSpawnY
|
||||
# xAccel, yAccel, xGrav (int8), yGrav (int8)
|
||||
# bgColor1, bgColor2, bgColor3, spriteMc0, spriteMc1, sprite0Color, sprite1Color
|
||||
# padCount + padCount * (letter, tileX, tileY, tileW)
|
||||
# fareCount + fareCount * (spawnPad, destPad)
|
||||
# tilemap[40 * 25]
|
||||
# colormap[40 * 25]
|
||||
|
||||
ST_TILEMAP_W = 40
|
||||
ST_PLAYFIELD_ROWS = 25
|
||||
ST_TILE_PX = 8
|
||||
|
||||
# VIC-II sprite-register origins: sprite-X $18 (24) is display column 0,
|
||||
# sprite-Y $32 (50) is the first visible raster line. All in-game pad/taxi
|
||||
# positions are stored in this sprite-register space, so tile conversion
|
||||
# subtracts these origins. PAD_SURFACE_ORIGIN folds in the ~14px landing-
|
||||
# gear offset (50-14=36) so a pad's row byte maps to its contact-surface
|
||||
# tile row rather than the sprite-top row.
|
||||
VIC_X_ORIGIN = 24
|
||||
VIC_Y_ORIGIN = 50
|
||||
PAD_SURFACE_ORIGIN = 36
|
||||
LEVEL_LETTERS = [chr(ord('A') + i) for i in range(24)]
|
||||
|
||||
|
||||
def write_level(path, level_index, after, screen, colormap, sprite_blocks):
|
||||
"""Write an STL4 level .dat straight from post-decompression RAM.
|
||||
|
||||
Everything stays in the C64's own units so the runtime can mirror the
|
||||
original routines byte for byte:
|
||||
name[22] $7D78-$7D8D level name (screen codes, ASCII-compatible)
|
||||
header[9] $7D00-$7D08 VIC colours: border, bg, bg1-3, mc0, mc1,
|
||||
sprite-0, sprite-1
|
||||
padCount $7D5A pads in the table
|
||||
specialPad $7D09 fare pads (== padCount, or padCount-1 when
|
||||
the last pad is the fuel station)
|
||||
fuelRate $7D60 ticks of flight per half fuel cell
|
||||
spawn[5] $7D5B-$7D5F X frac, X lo, X hi, Y frac, Y hi (sprite coords)
|
||||
templates[8] $7D8F-$7D96 Y accel, X accel, Y gravity, X gravity
|
||||
(16-bit little-endian each)
|
||||
pads[10][8] $7D0A-$7D59 raw slots: x1 hi/lo, x2 hi/lo, row,
|
||||
passenger X frac, passenger X col, unused
|
||||
tilemap[1000] screen RAM (all 25 rows, the HUD rows included)
|
||||
colormap[1000] colour RAM low nibbles
|
||||
levelIndex 0..23 = A..X, 24 = title
|
||||
spriteCount N, then N x (ptr, 63 bytes): the per-level sprite
|
||||
bitmaps the level's extra scene loads at $2000+
|
||||
hookData[616] $7D98-$7FFF: the level's hook code and tables
|
||||
"""
|
||||
cells = ST_TILEMAP_W * ST_PLAYFIELD_ROWS
|
||||
assert len(screen) == cells, f'screen is {len(screen)}, need {cells}'
|
||||
assert len(colormap) == cells, f'colormap is {len(colormap)}, need {cells}'
|
||||
with open(path, 'wb') as fp:
|
||||
fp.write(b'STL4')
|
||||
fp.write(bytes(after[0x7D78:0x7D78 + 22]))
|
||||
fp.write(bytes(after[0x7D00:0x7D09]))
|
||||
fp.write(bytes([after[0x7D5A], after[0x7D09], after[0x7D60]]))
|
||||
fp.write(bytes(after[0x7D5B:0x7D60]))
|
||||
fp.write(bytes(after[0x7D8F:0x7D97]))
|
||||
pad_count = min(after[0x7D5A], 10)
|
||||
for pad_idx in range(10):
|
||||
if pad_idx < pad_count:
|
||||
off = 0x7D0A + pad_idx * 8
|
||||
fp.write(bytes(after[off:off + 8]))
|
||||
else:
|
||||
fp.write(bytes(8))
|
||||
fp.write(bytes(screen))
|
||||
fp.write(bytes(colormap))
|
||||
fp.write(bytes([level_index]))
|
||||
fp.write(bytes([len(sprite_blocks)]))
|
||||
for ptr, data in sprite_blocks:
|
||||
fp.write(bytes([ptr]))
|
||||
fp.write(bytes(data))
|
||||
fp.write(bytes(after[0x7D98:0x8000]))
|
||||
|
||||
|
||||
# Levels whose data blob flags an extra scene ($7D97 != 0) load a second
|
||||
# scene from this table ($6F55, indexed by level) right after the level
|
||||
# itself. Those extra scenes only write sprite bitmaps into $2000-$27FF
|
||||
# (block pointers $80-$9F) for the level's hook code to animate.
|
||||
_EXTRA_SCENE = [0, 0, 0, 0, 0, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0, 0x1F, 0, 0, 0,
|
||||
0x20, 0x21, 0, 0, 0, 0x22, 0, 0, 0, 0x23]
|
||||
|
||||
|
||||
def extract_one_level(ram, scene_id, out_dir):
|
||||
"""Decompress `scene_id` and write out_dir/level<NN>.dat (NN = scene+1)."""
|
||||
letter = LEVEL_LETTERS[scene_id]
|
||||
screen_buf, color_buf, after = run_scene(ram, scene_id)
|
||||
sprite_blocks = []
|
||||
if after[0x7D97] != 0 and _EXTRA_SCENE[scene_id] != 0:
|
||||
# Start the extra scene from a zeroed sprite window so the blocks
|
||||
# it writes are exactly the ones the level ships with.
|
||||
work = bytearray(after)
|
||||
for i in range(0x2000, 0x2800):
|
||||
work[i] = 0
|
||||
_s, _c, after2 = run_scene(bytes(work), _EXTRA_SCENE[scene_id])
|
||||
for ptr in range(0x80, 0xA0):
|
||||
block = bytes(after2[ptr * 64:ptr * 64 + 63])
|
||||
if any(block):
|
||||
sprite_blocks.append((ptr, block))
|
||||
# The level data itself (pads, hooks) is untouched by the extra
|
||||
# scene; keep reading it from the level's own image.
|
||||
cells = ST_TILEMAP_W * ST_PLAYFIELD_ROWS
|
||||
screen = list(screen_buf[:cells])
|
||||
colormap = [(b & 0x0F) for b in color_buf[:cells]]
|
||||
level_num = scene_id + 1
|
||||
out_path = os.path.join(out_dir, f'level{level_num:02d}.dat')
|
||||
write_level(out_path, scene_id, after, screen, colormap, sprite_blocks)
|
||||
return out_path, after[0x7D5A], len(sprite_blocks)
|
||||
|
||||
|
||||
def extract_screen_y(ram, out_dir):
|
||||
"""Screen Y, the 25th screen ("MYSTERY SCREEN !!!"), is scene 24.
|
||||
|
||||
$5010 dispatches on the screens-completed counter: at 24 it jumps to
|
||||
$5167, which loads #$59 ('Y') as the pending screen; the shift only
|
||||
ends when the counter reaches 25. So a full 24-hour shift plays A..X
|
||||
and then Y -- the 25 screens the intro advertises.
|
||||
"""
|
||||
screen_buf, color_buf, after = run_scene(ram, 24)
|
||||
cells = ST_TILEMAP_W * ST_PLAYFIELD_ROWS
|
||||
screen = list(screen_buf[:cells])
|
||||
colormap = [(b & 0x0F) for b in color_buf[:cells]]
|
||||
out_path = os.path.join(out_dir, 'level25.dat')
|
||||
write_level(out_path, 24, after, screen, colormap, [])
|
||||
return out_path
|
||||
|
||||
|
||||
def extract_title(ram, out_dir):
|
||||
"""The title screen is scene 25 (what $477A decompresses).
|
||||
Pads, spawn and templates are dummies."""
|
||||
screen_buf, color_buf, after = run_scene(ram, 25)
|
||||
cells = ST_TILEMAP_W * ST_PLAYFIELD_ROWS
|
||||
screen = list(screen_buf[:cells])
|
||||
colormap = [(b & 0x0F) for b in color_buf[:cells]]
|
||||
out_path = os.path.join(out_dir, 'title.dat')
|
||||
# The title scene writes no level name, so $7D78 still holds whatever
|
||||
# the previously decompressed scene left there (PUZZLER, level H).
|
||||
# The title never draws the name; ship it blank rather than stale.
|
||||
work = bytearray(after)
|
||||
work[0x7D78:0x7D78 + 22] = b' ' * 22
|
||||
write_level(out_path, 25, work, screen, colormap, [])
|
||||
return out_path
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 3:
|
||||
print('usage: romToLevel.py <raw-dump> <out-dir> [scene_id]', file=sys.stderr)
|
||||
sys.exit(2)
|
||||
ram = load_dump(sys.argv[1])
|
||||
out_dir = sys.argv[2]
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
|
||||
if len(sys.argv) >= 4:
|
||||
scene_ids = [int(sys.argv[3], 0)]
|
||||
else:
|
||||
# Levels A..X = scenes 0..23. The title comes from screen RAM
|
||||
# at $0400 (the dump was taken on the title screen), not from
|
||||
# a decompressed scene.
|
||||
scene_ids = list(range(24))
|
||||
|
||||
for sid in scene_ids:
|
||||
path, pad_count, nspr = extract_one_level(ram, sid, out_dir)
|
||||
size = os.path.getsize(path)
|
||||
print(f' scene {sid:2d} (level {LEVEL_LETTERS[sid]}): pads={pad_count} sprites={nspr} size={size} -> {path}')
|
||||
|
||||
# Screen Y and the title are not part of the A..X sweep.
|
||||
if len(sys.argv) < 4:
|
||||
y_path = extract_screen_y(ram, out_dir)
|
||||
print(f' scene 24 (screen Y) : -> {y_path} ({os.path.getsize(y_path)} bytes)')
|
||||
title_path = extract_title(ram, out_dir)
|
||||
print(f' title (screen RAM $0400) : -> {title_path} ({os.path.getsize(title_path)} bytes)')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
#!/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, os, sys
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
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"))
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
"""SID register log (sidCapture.py) -> JoeyLib .song text for songbake.
|
||||
|
||||
One row per PAL frame (tickms 20). Per voice, a note-on is a gate rising
|
||||
edge or a pitch change while gated; gate falling is 'off'. SID frequency ->
|
||||
Hz exactly (@Hz cells), so no equal-temperament rounding. Attenuation comes
|
||||
from the sustain nibble. A voice whose waveform is NOISE goes to the noise
|
||||
column instead of a tone column, with a pitch scaled from its frequency.
|
||||
Loop detection: the player is deterministic, so once the (frame-relative)
|
||||
event rows repeat with a fixed period, that period is the loop.
|
||||
"""
|
||||
import json, sys
|
||||
|
||||
PAL_CLOCK = 985248.0
|
||||
songs = json.load(open(sys.argv[1]))
|
||||
outDir = sys.argv[2]
|
||||
|
||||
|
||||
def sidHz(lo, hi):
|
||||
return ((hi << 8) | lo) * PAL_CLOCK / 16777216.0
|
||||
|
||||
|
||||
def rowsFor(writes, nframes):
|
||||
# replay register writes frame by frame, producing per-frame voice state
|
||||
regs = [0] * 0x19
|
||||
state = []
|
||||
byFrame = {}
|
||||
for f, r, v in writes:
|
||||
byFrame.setdefault(f, []).append((r, v))
|
||||
for f in range(nframes):
|
||||
for r, v in byFrame.get(f, []):
|
||||
regs[r] = v
|
||||
vs = []
|
||||
for vc in range(3):
|
||||
b = vc * 7
|
||||
vs.append(dict(hz=sidHz(regs[b], regs[b + 1]), gate=regs[b + 4] & 1,
|
||||
wave=regs[b + 4] & 0xF0, sus=regs[b + 6] >> 4))
|
||||
state.append(vs)
|
||||
return state
|
||||
|
||||
|
||||
def atten(sus):
|
||||
# SID sustain 0..15 -> AGI attenuation 14..0 (0 loudest)
|
||||
return max(0, min(14, round(14 - sus * 14 / 15)))
|
||||
|
||||
|
||||
def noisePitch(hz):
|
||||
# brighter hiss for higher SID "frequency"; 0..31, 0 = brightest
|
||||
p = int(31 - min(31, hz / 400.0))
|
||||
return max(0, min(31, p))
|
||||
|
||||
|
||||
def toRows(state):
|
||||
rows = []
|
||||
prev = [dict(hz=None, gate=0, wave=0, sus=0) for _ in range(3)]
|
||||
for vs in state:
|
||||
tone = ["---", "---", "---"]
|
||||
noise = "---"
|
||||
for vc in range(3):
|
||||
cur, was = vs[vc], prev[vc]
|
||||
isNoise = (cur["wave"] & 0x80) != 0
|
||||
on = cur["gate"] and (not was["gate"] or abs(cur["hz"] - was["hz"]) > 0.5
|
||||
or cur["wave"] != was["wave"])
|
||||
off = was["gate"] and not cur["gate"]
|
||||
if on:
|
||||
if isNoise:
|
||||
noise = f"N{noisePitch(cur['hz'])}:{atten(cur['sus'])}"
|
||||
else:
|
||||
hz = max(1, min(65535, int(round(cur["hz"]))))
|
||||
tone[vc] = f"@{hz}:{atten(cur['sus'])}"
|
||||
elif off:
|
||||
if (was["wave"] & 0x80):
|
||||
noise = "off"
|
||||
else:
|
||||
tone[vc] = "off"
|
||||
prev[vc] = dict(cur)
|
||||
rows.append((tone, noise))
|
||||
return rows
|
||||
|
||||
|
||||
def findLoop(rows):
|
||||
# smallest (offset, period) with rows[o+k] == rows[o+p+k] for the tail
|
||||
n = len(rows)
|
||||
for p in range(8, n // 2):
|
||||
for o in range(0, n - 2 * p):
|
||||
if rows[o:o + p] == rows[o + p:o + 2 * p] and rows[o:n - p] == rows[o + p:n]:
|
||||
return o, p
|
||||
return None
|
||||
|
||||
|
||||
for num, s in sorted(songs.items(), key=lambda kv: int(kv[0])):
|
||||
num = int(num)
|
||||
st = rowsFor(s["writes"], s["frames"])
|
||||
rows = toRows(st)
|
||||
loop = findLoop(rows) if s["frames"] >= 4000 else None
|
||||
if loop:
|
||||
o, p = loop
|
||||
rows = rows[:o + p]
|
||||
lines = ["; Space Taxi (C64) song %d, extracted by running the game's own" % num,
|
||||
"; player ($CB00/$CF52) under cpu6502.py and logging SID writes.",
|
||||
"; One row = one PAL frame.", "tickms 20"]
|
||||
for i, (tone, noise) in enumerate(rows):
|
||||
if loop and i == loop[0]:
|
||||
lines.append("loop")
|
||||
cells = tone + ([noise] if noise != "---" or True else [])
|
||||
lines.append(" ".join(cells))
|
||||
open(f"{outDir}/song{num}.song", "w").write("\n".join(lines) + "\n")
|
||||
events = sum(1 for t, n in rows for c in t + [n] if c != "---")
|
||||
waves = sorted({hex(v["wave"]) for vs in st for v in vs if v["gate"]})
|
||||
print(f"song {num}: {len(rows)} rows, {events} events, waveforms {waves}"
|
||||
+ (f", LOOP at row {loop[0]} period {loop[1]}" if loop else ""))
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
; Space Taxi (C64) song 0, extracted by running the game's own
|
||||
; player ($CB00/$CF52) under cpu6502.py and logging SID writes.
|
||||
; One row = one PAL frame.
|
||||
tickms 20
|
||||
--- --- --- ---
|
||||
@336:2 @424:2 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@378:2 @424:2 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@336:2 @424:2 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@378:2 @476:2 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@318:2 @252:2 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @476:2 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@283:2 @252:2 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @476:2 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@252:2 @252:2 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
; Space Taxi (C64) song 1, extracted by running the game's own
|
||||
; player ($CB00/$CF52) under cpu6502.py and logging SID writes.
|
||||
; One row = one PAL frame.
|
||||
tickms 20
|
||||
--- --- --- ---
|
||||
@252:7 @159:7 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
@283:7 @168:7 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
@252:7 @189:7 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
@283:7 @212:7 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
@318:7 @189:7 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
@336:7 @168:7 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
@318:7 @159:7 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
@336:7 @168:7 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
@378:7 @189:7 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
; Space Taxi (C64) song 2, extracted by running the game's own
|
||||
; player ($CB00/$CF52) under cpu6502.py and logging SID writes.
|
||||
; One row = one PAL frame.
|
||||
tickms 20
|
||||
--- --- --- ---
|
||||
@126:0 @159:0 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
@126:0 @168:0 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
@141:0 @189:0 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
@159:0 @159:0 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
@159:0 @168:0 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
@168:0 @189:0 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
@168:0 @159:0 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
@189:0 @168:0 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- @159:0 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @126:0 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
; Space Taxi (C64) song 3, extracted by running the game's own
|
||||
; player ($CB00/$CF52) under cpu6502.py and logging SID writes.
|
||||
; One row = one PAL frame.
|
||||
tickms 20
|
||||
--- --- --- ---
|
||||
@252:0 @336:2 @336:2 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off off ---
|
||||
--- --- --- ---
|
||||
@283:0 --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
@318:0 @336:2 @336:2 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off off ---
|
||||
--- --- --- ---
|
||||
@252:0 --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
@283:0 @336:2 @336:2 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off off ---
|
||||
--- --- --- ---
|
||||
@252:0 --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
@283:0 @336:2 @336:2 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off off ---
|
||||
--- --- --- ---
|
||||
@476:0 --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- @318:2 @378:2 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @336:2 @378:2 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @283:2 @252:2 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @252:2 @476:2 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
; Space Taxi (C64) song 4, extracted by running the game's own
|
||||
; player ($CB00/$CF52) under cpu6502.py and logging SID writes.
|
||||
; One row = one PAL frame.
|
||||
tickms 20
|
||||
--- --- --- ---
|
||||
@504:5 @449:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@504:5 @449:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
@673:5 @504:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@504:5 @424:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@673:5 @504:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
@848:5 @673:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @635:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @673:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
--- --- --- ---
|
||||
|
|
@ -1,736 +0,0 @@
|
|||
; Space Taxi (C64) song 5, extracted by running the game's own
|
||||
; player ($CB00/$CF52) under cpu6502.py and logging SID writes.
|
||||
; One row = one PAL frame.
|
||||
tickms 20
|
||||
--- --- --- ---
|
||||
@504:2 @159:2 @106:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- @212:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @159:2 @106:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- @212:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @159:2 @106:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- @212:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @159:2 @106:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
off --- --- ---
|
||||
--- --- @212:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@504:2 @159:2 @106:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- @212:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @159:2 @106:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- @212:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @159:2 @106:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- @212:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @159:2 @106:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
off --- --- ---
|
||||
--- --- @212:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@504:2 @159:2 @106:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- @212:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @159:2 @106:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- @212:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @159:2 @106:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
off --- --- ---
|
||||
--- --- @212:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
@566:2 @141:2 @100:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
off --- --- ---
|
||||
--- --- @200:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@566:2 @141:2 @100:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- @200:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @141:2 @100:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- @200:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @141:2 @100:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- @200:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
--- @141:2 @100:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
off --- --- ---
|
||||
--- --- @200:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@635:2 @504:2 @94:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
--- --- @189:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @94:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @189:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@673:2 @566:2 @84:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
--- --- @168:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @84:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @168:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@755:2 @635:2 @79:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
--- --- @159:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @79:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @159:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@800:2 @635:2 @71:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
--- --- @141:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @71:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @141:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@848:2 @635:2 @67:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @133:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @67:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @133:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @67:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @133:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @67:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @133:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@424:2 @318:2 @67:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
--- --- @133:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@848:2 @336:2 @252:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
@848:2 @336:2 @252:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
@848:2 @336:2 @252:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
@755:2 @318:2 @225:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
@755:2 @318:2 @225:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
@673:2 @283:2 @212:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
@635:2 @252:2 @189:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@566:2 @283:2 @168:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@755:2 @318:2 @126:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @189:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @126:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @189:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @126:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @189:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @126:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @189:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @126:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- @189:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@635:2 --- @126:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- @189:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@504:2 --- @126:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- @189:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@635:2 --- @126:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
--- --- @189:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
@755:2 @283:2 @119:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @189:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @119:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- @189:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@848:2 --- @119:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @189:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @119:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- @189:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@755:2 --- @119:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- @189:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@673:2 --- @119:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- @189:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@635:2 --- @119:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- @189:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@566:2 --- @119:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
--- --- @189:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
@504:2 @318:2 @126:10 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
|
|
@ -1,840 +0,0 @@
|
|||
; Space Taxi (C64) song 6, extracted by running the game's own
|
||||
; player ($CB00/$CF52) under cpu6502.py and logging SID writes.
|
||||
; One row = one PAL frame.
|
||||
tickms 20
|
||||
--- --- --- ---
|
||||
--- @378:1 @504:1 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @378:1 @504:1 ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @378:1 @504:1 ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @378:1 @504:1 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@94:0 --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@126:0 --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@159:0 --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@189:0 @336:1 @449:1 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @300:1 @400:1 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @336:1 @449:1 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @378:1 @504:1 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @378:1 @504:1 ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off @378:1 @504:1 ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @378:1 @504:1 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@212:0 --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@189:0 --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@159:0 --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @378:1 @504:1 ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @378:1 @504:1 ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@141:0 @424:1 @566:1 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @378:1 @504:1 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @424:1 @566:1 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@119:0 @476:1 @635:1 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @476:1 @635:1 ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @476:1 @635:1 ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @476:1 @635:1 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@59:0 --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @476:1 @635:1 ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @476:1 @635:1 ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @476:1 @635:1 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @424:1 @566:1 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @476:1 @635:1 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@126:0 @504:1 @673:1 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @504:1 @673:1 ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @504:1 @673:1 ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @504:1 @673:1 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@63:0 --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @504:1 @673:1 ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off @504:1 @673:1 ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@126:0 @566:1 @755:1 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @504:1 @673:1 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @566:1 @755:1 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@141:0 @635:1 @848:1 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@71:0 --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @635:1 @848:1 ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off @635:1 @848:1 ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@141:0 @566:1 @848:1 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @566:1 @848:1 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @566:1 @848:1 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- off off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@159:0 @534:1 @848:1 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- off ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
|
|
@ -1,852 +0,0 @@
|
|||
; Space Taxi (C64) song 7, extracted by running the game's own
|
||||
; player ($CB00/$CF52) under cpu6502.py and logging SID writes.
|
||||
; One row = one PAL frame.
|
||||
tickms 20
|
||||
--- --- --- ---
|
||||
@378:0 @1270:5 @126:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1346:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1270:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- @1132:5 @119:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1270:5 off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1346:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@252:0 @1270:5 @106:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1346:5 off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1510:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@283:0 @1008:5 @94:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@318:0 --- off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@336:0 --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1008:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@378:0 --- @126:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @952:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @848:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @755:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1132:5 @119:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1270:5 off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1346:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1510:5 @126:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1510:5 off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1510:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@424:0 @1132:5 @94:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
@378:0 --- off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@336:0 @1132:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@378:0 @1270:5 @126:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@336:0 @952:5 @119:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
@318:0 --- off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@283:0 @952:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@318:0 @1008:5 @106:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@283:0 @755:5 @94:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
@252:0 --- off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@238:0 @755:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
@252:0 @1008:5 @126:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @952:5 off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@318:0 @1008:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
@189:0 @1132:5 @119:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1008:5 off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1132:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1270:5 @106:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1132:5 off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1270:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@238:0 @1346:5 @94:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@252:0 @1270:5 off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@283:0 @1346:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
@252:0 @1270:5 @126:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1270:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@238:0 @1510:5 @119:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@252:0 --- off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@283:0 --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@318:0 --- @106:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@283:0 @1510:5 @94:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
@283:0 --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@378:0 @1132:5 @141:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1008:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @952:5 @119:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1008:5 off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1132:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @755:5 @106:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@378:0 @1132:5 --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@378:0 --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@378:0 @1199:5 @94:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@378:0 --- off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
@378:0 --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
@378:0 @1270:5 @126:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1510:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1270:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1510:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1270:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @94:7 ---
|
||||
--- @1510:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1270:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1510:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1270:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1510:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @126:7 ---
|
||||
--- --- --- ---
|
||||
--- @1270:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1510:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1270:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1510:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1270:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- @94:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1510:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1270:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1510:5 --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- @1270:5 --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- @126:7 ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- off --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
off --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- off ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
--- --- --- ---
|
||||
File diff suppressed because it is too large
Load diff
BIN
assets/sprites/sprites.png
(Stored with Git LFS)
Normal file
BIN
assets/sprites/sprites.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/tiles/tbank0.png
Normal file
BIN
assets/tiles/tbank0.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/tiles/tbank1.png
Normal file
BIN
assets/tiles/tbank1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/tiles/tbank2.png
Normal file
BIN
assets/tiles/tbank2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/tiles/tilebank0.png
Normal file
BIN
assets/tiles/tilebank0.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1 KiB |
496
extractFromDump.py
Normal file
496
extractFromDump.py
Normal file
|
|
@ -0,0 +1,496 @@
|
|||
#!/usr/bin/env python3
|
||||
# extractFromDump.py -- convert a VICE C64 memory dump into JoeyLib
|
||||
# Space Taxi assets.
|
||||
#
|
||||
# Reads: /tmp/spacetaxi/mem0000-<labelN>.bin (VICE `save` output;
|
||||
# 2-byte LE start-addr header + 64 KB RAM).
|
||||
# Writes: examples/spacetaxi/assets/tiles/tilebank<N>.png
|
||||
# examples/spacetaxi/assets/levels/level<NN>.txt
|
||||
# examples/spacetaxi/assets/sprites/sprites.png (level1 only)
|
||||
# examples/spacetaxi/assets/font.png (level1 only)
|
||||
#
|
||||
# Per-level:
|
||||
# - Custom charset at $2800-$2FFF (2 KB, 256 chars * 8 bytes mono)
|
||||
# becomes a tile bank PNG: 40 cols x 25 rows of 8x8 cells, with
|
||||
# tile index N at cell (N%40, N/40). The PNG uses the EGA-ish
|
||||
# 16-color palette we already install on stage.
|
||||
# - Screen RAM at $0400-$07E7 (1000 bytes = 40x25 char codes) and
|
||||
# color RAM at $D800-$DBE7 (1000 bytes = 40x25 palette nybbles)
|
||||
# become the tilemap+colormap in the level .txt source.
|
||||
#
|
||||
# Sprite data at $3000-$37FF + sprite pointer table at $07F8-$07FF
|
||||
# is decoded once (it's the same across levels for the active set):
|
||||
# - sprite 0 / 7: the taxi cab body & shadow
|
||||
# - sprite 1..6: passenger frames / animation cels
|
||||
# 64 bytes per sprite, 24x21 1-bit, MSB-leftmost. Color comes from
|
||||
# $D027-$D02E (one color per sprite).
|
||||
#
|
||||
# Usage:
|
||||
# python3 extractFromDump.py stuff/spacetaxi/mem0000-level1.bin level01 1
|
||||
# python3 extractFromDump.py stuff/spacetaxi/mem0000-level2.bin level02 2
|
||||
# args: <dump-path> <level-name-without-extension> <tilebank-id>
|
||||
#
|
||||
# Dumps live in stuff/spacetaxi/ (committed-ish, repo-local) rather
|
||||
# than /tmp so they survive reboots and stay with the project.
|
||||
|
||||
import os
|
||||
import struct
|
||||
import sys
|
||||
from collections import Counter
|
||||
|
||||
from PIL import Image
|
||||
|
||||
|
||||
# C64 standard color palette (EGA-ish 16-color JoeyLib mapping).
|
||||
# Indices 0..15 match the C64 VIC-II color register order so a value
|
||||
# read from $D8xx or $D02x maps directly to a JoeyLib palette slot.
|
||||
# Colors below approximate Pepto's standard PAL palette in $RGB form.
|
||||
C64_PALETTE_RGB = [
|
||||
(0x00, 0x00, 0x00), # 0 black
|
||||
(0xFF, 0xFF, 0xFF), # 1 white
|
||||
(0x88, 0x39, 0x32), # 2 red
|
||||
(0x67, 0xB6, 0xBD), # 3 cyan
|
||||
(0x8B, 0x3F, 0x96), # 4 purple
|
||||
(0x55, 0xA0, 0x49), # 5 green
|
||||
(0x40, 0x31, 0x8D), # 6 blue
|
||||
(0xBF, 0xCE, 0x72), # 7 yellow
|
||||
(0x8B, 0x54, 0x29), # 8 orange
|
||||
(0x57, 0x42, 0x00), # 9 brown
|
||||
(0xB8, 0x69, 0x62), # 10 light red
|
||||
(0x50, 0x50, 0x50), # 11 dark gray
|
||||
(0x78, 0x78, 0x78), # 12 mid gray
|
||||
(0x94, 0xE0, 0x89), # 13 light green
|
||||
(0x78, 0x69, 0xC4), # 14 light blue
|
||||
(0x9F, 0x9F, 0x9F), # 15 light gray
|
||||
]
|
||||
|
||||
|
||||
ROOT = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
def load_dump(path):
|
||||
with open(path, "rb") as f:
|
||||
raw = f.read()
|
||||
start = raw[0] | (raw[1] << 8)
|
||||
if start != 0:
|
||||
raise SystemExit(f"{path}: unexpected start ${start:04X}, want $0000")
|
||||
if len(raw) - 2 != 0x10000:
|
||||
raise SystemExit(f"{path}: not a 64K dump (got {len(raw) - 2} bytes)")
|
||||
return raw[2:]
|
||||
|
||||
|
||||
def new_canvas(w, h):
|
||||
img = Image.new("P", (w, h), 0)
|
||||
flat = bytearray()
|
||||
for r, g, b in C64_PALETTE_RGB:
|
||||
flat.extend((r, g, b))
|
||||
flat.extend(b"\x00" * (768 - len(flat)))
|
||||
img.putpalette(bytes(flat))
|
||||
return img
|
||||
|
||||
|
||||
def paint_charset_glyph(px, mem, src_code, bx, by):
|
||||
"""Plot the 8x8 glyph for C64 screen code `src_code` at pixel
|
||||
(bx, by) of the indexed-palette PIL image. Foreground = palette
|
||||
index 1, background untouched (already index 0 from new_canvas).
|
||||
"""
|
||||
src = 0x2800 + src_code * 8
|
||||
for r in range(8):
|
||||
byte = mem[src + r]
|
||||
for c in range(8):
|
||||
if byte & (0x80 >> c):
|
||||
px[bx + c, by + r] = 1
|
||||
|
||||
|
||||
def extract_tilebank(mem, out_path, code_to_slot=None):
|
||||
"""Render the custom charset at $2800-$2FFF as a 320x200 PNG.
|
||||
When `code_to_slot` is None, lays out all 256 chars at their raw
|
||||
C64 positions (40 cols x 25 rows). When `code_to_slot` is given,
|
||||
writes each charset glyph at the slot the level loader expects
|
||||
-- i.e. the bank tile at position N is the glyph for screen-code
|
||||
`code_for_slot[N]`. That keeps level data and tilebank aligned
|
||||
after the level extractor's frequency-sorted code remapping.
|
||||
"""
|
||||
img = new_canvas(320, 200)
|
||||
px = img.load()
|
||||
if code_to_slot is None:
|
||||
for ch in range(256):
|
||||
paint_charset_glyph(px, mem, ch, (ch % 40) * 8, (ch // 40) * 8)
|
||||
else:
|
||||
# Invert: slot -> code. Multiple codes can collide into slot 0
|
||||
# (background); only the first one we see "owns" that slot for
|
||||
# painting purposes -- the rest just fall through to the same
|
||||
# blank tile. Highest priority is the explicit space mapping.
|
||||
slot_to_code = {}
|
||||
for code, slot in code_to_slot.items():
|
||||
slot_to_code.setdefault(slot, code)
|
||||
for slot, code in slot_to_code.items():
|
||||
if slot >= 256:
|
||||
continue
|
||||
paint_charset_glyph(px, mem, code, (slot % 40) * 8, (slot // 40) * 8)
|
||||
img.save(out_path, "PNG", optimize=True)
|
||||
|
||||
|
||||
def build_code_to_slot(mem):
|
||||
"""Build the screen-code -> alphabet-slot map shared between the
|
||||
tilebank emission and the level emission. The slot index is what
|
||||
the runtime sees in level.tilemap[]; the tilebank must mirror this
|
||||
ordering so a level-data slot-N cell renders the charset glyph
|
||||
that was actually used in the original screen RAM at that code.
|
||||
"""
|
||||
screen = mem[0x0400:0x0400 + 1000]
|
||||
codes = sorted(Counter(screen).items(), key=lambda kv: (-kv[1], kv[0]))
|
||||
code_to_slot = {}
|
||||
next_slot = 0
|
||||
for code, _count in codes:
|
||||
if code == 0x20: # space -> always index 0
|
||||
code_to_slot[code] = 0
|
||||
continue
|
||||
if code in code_to_slot:
|
||||
continue
|
||||
if next_slot >= len(ALPHABET):
|
||||
code_to_slot[code] = 0
|
||||
continue
|
||||
# Reserve slot 0 for background (space). The very first
|
||||
# non-space code claims slot 1, etc.
|
||||
if next_slot == 0:
|
||||
next_slot = 1
|
||||
code_to_slot[code] = next_slot
|
||||
next_slot += 1
|
||||
return code_to_slot
|
||||
|
||||
|
||||
def screencode_to_char(code):
|
||||
"""Convert a C64 screen code into a printable tilemap character
|
||||
using the encoding the .txt format already understands:
|
||||
0 -> '.' (treated as 0)
|
||||
1..9 -> '1'..'9' (digits encode the lower 10 indices)
|
||||
but we shift to use up most of the index space below.
|
||||
For arbitrary 0..255 we need a 256-symbol alphabet -- not feasible
|
||||
in a single character per cell. So we encode as TWO chars per cell
|
||||
using base-16 hex would still only cover 0..255. Decision: keep
|
||||
.txt cells one-char and map screen codes via a custom 64-char
|
||||
alphabet plus an @char directive table emitted into the file so
|
||||
the loader knows exactly which screen code each char represents.
|
||||
"""
|
||||
# This function is unused as a one-char encoder; we encode via the
|
||||
# generic alphabet below instead.
|
||||
return code
|
||||
|
||||
|
||||
# Generic 1-char alphabet (62 codes); the level loader's index map
|
||||
# was: '.' or ' '=0, '0'..'9'=0..9, 'A'..'Z'=10..35, 'a'..'z'=36..61.
|
||||
# That's only 62 codes, not 256. The original C64 levels use ~30
|
||||
# distinct screen codes per level so most levels fit. We map the N
|
||||
# most-frequent screen codes to those alphabet slots.
|
||||
ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
|
||||
|
||||
def extract_level(mem, level_name, tilebank_id, out_path, code_to_alpha):
|
||||
"""Render screen RAM + color RAM as an STL1-format .txt source
|
||||
using the prebuilt screen-code -> alphabet-slot mapping. Caller
|
||||
must pass the same mapping that was used to reorder the tilebank
|
||||
PNG; otherwise the level tilemap and tilebank diverge.
|
||||
"""
|
||||
screen = mem[0x0400:0x0400 + 1000] # 40x25
|
||||
color = mem[0xD800:0xD800 + 1000] # 40x25, low nybble = palette slot
|
||||
codes = sorted(Counter(screen).items(), key=lambda kv: (-kv[1], kv[0]))
|
||||
|
||||
# Full 25-row C64 screen. The title screen uses all 25 rows
|
||||
# (PUBLISHED ... SOFTWARE credit wraps across rows 21, 22, 24
|
||||
# with frame chars interleaved). Gameplay HUD draws over the
|
||||
# bottom 3 rows at runtime.
|
||||
playfield_rows = 25
|
||||
|
||||
lines = []
|
||||
lines.append(f"# Extracted from VICE memory dump. Edit at your own risk;")
|
||||
lines.append(f"# regenerating will overwrite. Keep the @tile mappings in")
|
||||
lines.append(f"# sync with the tilebank PNG (extracted from $2800).")
|
||||
lines.append(f"")
|
||||
lines.append(f"@name {level_name.upper()}")
|
||||
lines.append(f"@tilebank {tilebank_id}")
|
||||
lines.append(f"@music 0")
|
||||
bg = mem[0xD021] & 0x0F
|
||||
border = mem[0xD020] & 0x0F
|
||||
lines.append(f"@bgColor {bg}")
|
||||
lines.append(f"@borderColor {border}")
|
||||
|
||||
# Best-effort taxi spawn: middle-top of playfield. The original
|
||||
# script-driven spawn position would need to be lifted out of
|
||||
# game state at dump time; for now hand-edit if you want it
|
||||
# to match the cracker's preferred spawn.
|
||||
lines.append(f"@taxiSpawn 18 2")
|
||||
lines.append(f"")
|
||||
|
||||
# Pads: scan the playfield for "landable surfaces" -- runs of
|
||||
# >= 3 identical non-background tile cells with two or more
|
||||
# empty rows directly above (so the taxi can approach from the
|
||||
# top). Per-row scanning runs top-to-bottom; for the same
|
||||
# surface tile across rows we only take the topmost contiguous
|
||||
# platform edge.
|
||||
bg_code = codes[0][0] if codes else 0x20
|
||||
def is_bg(code):
|
||||
return code == bg_code or code == 0x20
|
||||
|
||||
pads = []
|
||||
seen_used = [False] * (playfield_rows * 40)
|
||||
for row in range(2, playfield_rows):
|
||||
line = screen[row * 40:(row + 1) * 40]
|
||||
prev = screen[(row - 1) * 40:row * 40]
|
||||
prev2 = screen[(row - 2) * 40:(row - 1) * 40]
|
||||
col = 0
|
||||
while col < 40 and len(pads) < 8:
|
||||
if is_bg(line[col]):
|
||||
col += 1
|
||||
continue
|
||||
j = col
|
||||
while j < 40 and line[j] == line[col] and not seen_used[row * 40 + j]:
|
||||
j += 1
|
||||
width = j - col
|
||||
# Approachable from above? At least 2/3 of cells in the
|
||||
# 2 rows above this stretch must be background.
|
||||
above_clear = 0
|
||||
for k in range(col, j):
|
||||
if is_bg(prev[k]):
|
||||
above_clear += 1
|
||||
if is_bg(prev2[k]):
|
||||
above_clear += 1
|
||||
if width >= 3 and above_clear >= width:
|
||||
pads.append((row, col, width))
|
||||
for k in range(col, j):
|
||||
seen_used[row * 40 + k] = True
|
||||
col = j
|
||||
|
||||
lines.append("# Pads auto-detected from landable surfaces (>= 3 wide,")
|
||||
lines.append("# approachable from above). Verify and adjust as needed.")
|
||||
pad_letter = ord("A")
|
||||
for (row, col, width) in pads:
|
||||
if pad_letter > ord("H"):
|
||||
break
|
||||
lines.append(f"@pad {chr(pad_letter)} {col} {row} {width}")
|
||||
pad_letter += 1
|
||||
if pad_letter == ord("A"):
|
||||
lines.append("# (No landable surfaces auto-detected; emitting placeholders.)")
|
||||
lines.append("@pad A 4 10 4")
|
||||
lines.append("@pad B 32 10 4")
|
||||
pad_letter = ord("C")
|
||||
|
||||
lines.append("")
|
||||
# Fare list: simple round-robin between detected pads.
|
||||
pad_count = pad_letter - ord("A")
|
||||
if pad_count >= 2:
|
||||
for k in range(pad_count):
|
||||
src = chr(ord("A") + k)
|
||||
dst = chr(ord("A") + ((k + 1) % pad_count))
|
||||
lines.append(f"@fare {src} {dst} 45")
|
||||
else:
|
||||
lines.append("@fare A B 45")
|
||||
lines.append("")
|
||||
|
||||
# Emit the @tile mapping for round-trip clarity (and so the
|
||||
# author can read a level .txt and tell which screen code maps
|
||||
# to which char). Format: @tile <char> <screencode> -- the
|
||||
# mkstlevel parser ignores these today but they're useful
|
||||
# documentation, and a future loader could honor them.
|
||||
lines.append("# Screen-code -> alphabet-char mapping (one per unique code).")
|
||||
inv = {v: k for k, v in code_to_alpha.items()}
|
||||
for idx in sorted(inv):
|
||||
ch = '.' if idx == 0 else ALPHABET[idx]
|
||||
code = inv[idx]
|
||||
lines.append(f"# @tile '{ch}' = $C64_screencode_{code:02X}")
|
||||
lines.append("")
|
||||
|
||||
# Tilemap (22 rows of 40 chars each).
|
||||
lines.append("@tilemap")
|
||||
for r in range(playfield_rows):
|
||||
row = screen[r * 40:(r + 1) * 40]
|
||||
s = ""
|
||||
for code in row:
|
||||
idx = code_to_alpha.get(code, 0)
|
||||
s += '.' if idx == 0 else ALPHABET[idx]
|
||||
lines.append(s)
|
||||
|
||||
lines.append("")
|
||||
lines.append("@colormap")
|
||||
for r in range(playfield_rows):
|
||||
row = color[r * 40:(r + 1) * 40]
|
||||
s = "".join(
|
||||
('0' if (c & 0x0F) == 0
|
||||
else ALPHABET[(c & 0x0F)]
|
||||
if (c & 0x0F) < len(ALPHABET) else '0')
|
||||
for c in row
|
||||
)
|
||||
lines.append(s)
|
||||
|
||||
with open(out_path, "w") as fp:
|
||||
fp.write("\n".join(lines) + "\n")
|
||||
|
||||
|
||||
def extract_sprites(mem, out_path):
|
||||
"""Render C64 hardware sprites into the JoeyLib-expected sprite
|
||||
sheet layout (320x200, 16-color indexed PNG):
|
||||
y= 0..23 : 1 taxi cel (24x24, x = 0)
|
||||
sprite 0 ptr (in level1 dump: $DC -> $3700)
|
||||
The adjacent slot $DD is the landing-gear-down
|
||||
pose, not a thrust frame -- ignored for now.
|
||||
y= 24..47 : 4 passenger cels (16x16 each, x = 0, 16, 32, 48)
|
||||
Sprite 3 ptr ($07FB) is the passenger data; sprites 3-6 share it.
|
||||
"""
|
||||
img = new_canvas(320, 200)
|
||||
px = img.load()
|
||||
|
||||
# Shared multicolor sprite colors from VIC registers.
|
||||
mcm_color0 = mem[0xD025] & 0x0F # 2-bit value 01
|
||||
mcm_color1 = mem[0xD026] & 0x0F # 2-bit value 11
|
||||
mcm_mask = mem[0xD01C] # bit per sprite: 1 = multicolor
|
||||
|
||||
def paint_sprite_at(data_addr, sprite_color, dst_x, dst_y, dst_w, dst_h, multicolor=False):
|
||||
# Mono C64 sprite is 24x21, 1 bit per pixel, 3 bytes per row.
|
||||
# Multicolor C64 sprite is 12 "double-wide pixels" x 21 rows,
|
||||
# 2 bits per pixel, still 3 bytes per row. Color mapping:
|
||||
# 00 -> transparent
|
||||
# 01 -> mcm_color0 ($D025)
|
||||
# 10 -> sprite_color (sprite's own $D027+sp)
|
||||
# 11 -> mcm_color1 ($D026)
|
||||
if sprite_color == 0:
|
||||
sprite_color = 1
|
||||
sx_max = min(24, dst_w)
|
||||
sy_max = min(21, dst_h)
|
||||
for r in range(sy_max):
|
||||
if multicolor:
|
||||
for byte_idx in range(3):
|
||||
byte = mem[data_addr + r * 3 + byte_idx]
|
||||
# 4 multicolor pixels per byte; each spans 2
|
||||
# hardware pixels wide.
|
||||
for pp in range(4):
|
||||
bits = (byte >> ((3 - pp) * 2)) & 0x03
|
||||
if bits == 0:
|
||||
continue
|
||||
if bits == 1:
|
||||
col = mcm_color0
|
||||
elif bits == 2:
|
||||
col = sprite_color
|
||||
else:
|
||||
col = mcm_color1
|
||||
sx0 = byte_idx * 8 + pp * 2
|
||||
if sx0 + 1 >= sx_max:
|
||||
break
|
||||
px[dst_x + sx0, dst_y + r] = col
|
||||
px[dst_x + sx0 + 1, dst_y + r] = col
|
||||
else:
|
||||
for byte_idx in range(3):
|
||||
byte = mem[data_addr + r * 3 + byte_idx]
|
||||
for bit in range(8):
|
||||
sx = byte_idx * 8 + bit
|
||||
if sx >= sx_max:
|
||||
break
|
||||
if byte & (0x80 >> bit):
|
||||
px[dst_x + sx, dst_y + r] = sprite_color
|
||||
|
||||
def paint_c64_sprite(sp_index, dst_x, dst_y, dst_w, dst_h):
|
||||
ptr = mem[0x07F8 + sp_index]
|
||||
if ptr == 0:
|
||||
return False
|
||||
sprite_color = mem[0xD027 + sp_index] & 0x0F
|
||||
is_multicolor = bool(mcm_mask & (1 << sp_index))
|
||||
paint_sprite_at(ptr * 64, sprite_color, dst_x, dst_y, dst_w, dst_h,
|
||||
multicolor=is_multicolor)
|
||||
return True
|
||||
|
||||
# Single taxi cel from sprite 0's actual pointer.
|
||||
paint_c64_sprite(0, 0, 0, 24, 24)
|
||||
|
||||
# 4 passenger cels = the shared C64 sprite 3 (sprites 3..6 all
|
||||
# point to the same data in the level-1 dump).
|
||||
for cel in range(4):
|
||||
paint_c64_sprite(3, cel * 16, 24, 16, 16)
|
||||
|
||||
img.save(out_path, "PNG", optimize=True)
|
||||
|
||||
|
||||
def extract_font(mem, out_path):
|
||||
"""Render the custom charset (which IS the in-game font in C64
|
||||
text-mode games) as the 320x200 font.png. Same layout as
|
||||
tilebank, but here we want each glyph at cell (ascii%40,
|
||||
ascii/40) for jlDrawText's ASCII map.
|
||||
|
||||
For Space Taxi specifically, the charset doubles as the level
|
||||
tile bank AND any in-screen text Sierra-style. We map the C64
|
||||
screen codes used for ASCII display (codes 1..26 = A..Z,
|
||||
32..63 = punctuation/digits) directly to their ASCII positions
|
||||
in the JoeyLib font sheet.
|
||||
"""
|
||||
img = new_canvas(320, 200)
|
||||
px = img.load()
|
||||
|
||||
def render_at(ascii_code, charset_idx):
|
||||
if ascii_code < 32 or ascii_code >= 128:
|
||||
return
|
||||
src = 0x2800 + (charset_idx & 0xFF) * 8
|
||||
col = ascii_code % 40
|
||||
row = ascii_code // 40
|
||||
bx = col * 8
|
||||
by = row * 8
|
||||
for r in range(8):
|
||||
byte = mem[src + r]
|
||||
for c in range(8):
|
||||
if byte & (0x80 >> c):
|
||||
px[bx + c, by + r] = 1
|
||||
|
||||
# C64 upper-only screen codes: 1..26 = A..Z, 27=[ 28=£ 29=] 30=↑ 31=←
|
||||
# 32 = space, 33..63 = !"# ... ?, etc. We map ASCII directly.
|
||||
for c in range(32, 64):
|
||||
render_at(c, c) # symbols + digits 1:1
|
||||
for c, code in enumerate("ABCDEFGHIJKLMNOPQRSTUVWXYZ"):
|
||||
render_at(ord(code), 1 + c) # screen code 1..26 -> A..Z
|
||||
|
||||
img.save(out_path, "PNG", optimize=True)
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 4:
|
||||
print("usage: extractFromDump.py <dump-path> <level-name> <tilebank-id>", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
dump_path = sys.argv[1]
|
||||
level_name = sys.argv[2]
|
||||
tilebank_id = int(sys.argv[3])
|
||||
|
||||
mem = load_dump(dump_path)
|
||||
print(f"loaded {dump_path}: 64 KB OK")
|
||||
|
||||
tiles_dir = os.path.join(ROOT, "assets", "tiles")
|
||||
sprites_dir = os.path.join(ROOT, "assets", "sprites")
|
||||
levels_dir = os.path.join(ROOT, "assets", "levels")
|
||||
os.makedirs(tiles_dir, exist_ok=True)
|
||||
os.makedirs(sprites_dir, exist_ok=True)
|
||||
os.makedirs(levels_dir, exist_ok=True)
|
||||
|
||||
# Build the screen-code -> bank-slot mapping once and feed it to
|
||||
# both extractors so the tilemap and tilebank stay aligned.
|
||||
code_to_slot = build_code_to_slot(mem)
|
||||
|
||||
# 8.3 DOS naming -- "tilebank0" is 9 chars and fopen fails under
|
||||
# DOSBox strict 8.3 mode. Keep the short form across the pipeline.
|
||||
tilebank_path = os.path.join(tiles_dir, f"tbank{tilebank_id}.png")
|
||||
extract_tilebank(mem, tilebank_path, code_to_slot)
|
||||
print(f" wrote {tilebank_path}")
|
||||
|
||||
level_path = os.path.join(levels_dir, f"{level_name}.txt")
|
||||
extract_level(mem, level_name, tilebank_id, level_path, code_to_slot)
|
||||
print(f" wrote {level_path}")
|
||||
|
||||
# Sprites + font come from level 1 only (sprite pointer table is
|
||||
# script-driven so it differs by scene; we use the first dump's
|
||||
# set as the canonical sheet, plus its charset as the font).
|
||||
if tilebank_id == 1:
|
||||
sprite_path = os.path.join(sprites_dir, "sprites.png")
|
||||
extract_sprites(mem, sprite_path)
|
||||
print(f" wrote {sprite_path}")
|
||||
|
||||
font_path = os.path.join(ROOT, "assets", "font.png")
|
||||
extract_font(mem, font_path)
|
||||
print(f" wrote {font_path}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
BIN
generated/amiga/font.tbk
Normal file
BIN
generated/amiga/font.tbk
Normal file
Binary file not shown.
BIN
generated/amiga/levels/level01.dat
Normal file
BIN
generated/amiga/levels/level01.dat
Normal file
Binary file not shown.
BIN
generated/amiga/levels/level02.dat
Normal file
BIN
generated/amiga/levels/level02.dat
Normal file
Binary file not shown.
BIN
generated/amiga/levels/level03.dat
Normal file
BIN
generated/amiga/levels/level03.dat
Normal file
Binary file not shown.
BIN
generated/amiga/levels/level04.dat
Normal file
BIN
generated/amiga/levels/level04.dat
Normal file
Binary file not shown.
BIN
generated/amiga/levels/level05.dat
Normal file
BIN
generated/amiga/levels/level05.dat
Normal file
Binary file not shown.
BIN
generated/amiga/levels/level06.dat
Normal file
BIN
generated/amiga/levels/level06.dat
Normal file
Binary file not shown.
BIN
generated/amiga/levels/level07.dat
Normal file
BIN
generated/amiga/levels/level07.dat
Normal file
Binary file not shown.
BIN
generated/amiga/levels/level08.dat
Normal file
BIN
generated/amiga/levels/level08.dat
Normal file
Binary file not shown.
BIN
generated/amiga/levels/level09.dat
Normal file
BIN
generated/amiga/levels/level09.dat
Normal file
Binary file not shown.
BIN
generated/amiga/levels/level10.dat
Normal file
BIN
generated/amiga/levels/level10.dat
Normal file
Binary file not shown.
BIN
generated/amiga/levels/level11.dat
Normal file
BIN
generated/amiga/levels/level11.dat
Normal file
Binary file not shown.
BIN
generated/amiga/levels/level12.dat
Normal file
BIN
generated/amiga/levels/level12.dat
Normal file
Binary file not shown.
BIN
generated/amiga/levels/level13.dat
Normal file
BIN
generated/amiga/levels/level13.dat
Normal file
Binary file not shown.
BIN
generated/amiga/levels/level14.dat
Normal file
BIN
generated/amiga/levels/level14.dat
Normal file
Binary file not shown.
BIN
generated/amiga/levels/level15.dat
Normal file
BIN
generated/amiga/levels/level15.dat
Normal file
Binary file not shown.
BIN
generated/amiga/levels/level16.dat
Normal file
BIN
generated/amiga/levels/level16.dat
Normal file
Binary file not shown.
BIN
generated/amiga/levels/level17.dat
Normal file
BIN
generated/amiga/levels/level17.dat
Normal file
Binary file not shown.
BIN
generated/amiga/levels/level18.dat
Normal file
BIN
generated/amiga/levels/level18.dat
Normal file
Binary file not shown.
BIN
generated/amiga/levels/level19.dat
Normal file
BIN
generated/amiga/levels/level19.dat
Normal file
Binary file not shown.
BIN
generated/amiga/levels/level20.dat
Normal file
BIN
generated/amiga/levels/level20.dat
Normal file
Binary file not shown.
BIN
generated/amiga/levels/level21.dat
Normal file
BIN
generated/amiga/levels/level21.dat
Normal file
Binary file not shown.
BIN
generated/amiga/levels/level22.dat
Normal file
BIN
generated/amiga/levels/level22.dat
Normal file
Binary file not shown.
BIN
generated/amiga/levels/level23.dat
Normal file
BIN
generated/amiga/levels/level23.dat
Normal file
Binary file not shown.
BIN
generated/amiga/levels/level24.dat
Normal file
BIN
generated/amiga/levels/level24.dat
Normal file
Binary file not shown.
BIN
generated/amiga/levels/title.dat
Normal file
BIN
generated/amiga/levels/title.dat
Normal file
Binary file not shown.
BIN
generated/amiga/sprites/sprites.spc
(Stored with Git LFS)
Normal file
BIN
generated/amiga/sprites/sprites.spc
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
generated/amiga/sprites/sprites.spr
(Stored with Git LFS)
Normal file
BIN
generated/amiga/sprites/sprites.spr
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
generated/amiga/tiles/tbank0.tbk
Normal file
BIN
generated/amiga/tiles/tbank0.tbk
Normal file
Binary file not shown.
BIN
generated/amiga/tiles/tbank1.tbk
Normal file
BIN
generated/amiga/tiles/tbank1.tbk
Normal file
Binary file not shown.
BIN
generated/amiga/tiles/tbank2.tbk
Normal file
BIN
generated/amiga/tiles/tbank2.tbk
Normal file
Binary file not shown.
BIN
generated/amiga/tiles/tilebank0.tbk
Normal file
BIN
generated/amiga/tiles/tilebank0.tbk
Normal file
Binary file not shown.
BIN
generated/atarist/font.tbk
Normal file
BIN
generated/atarist/font.tbk
Normal file
Binary file not shown.
BIN
generated/atarist/levels/level01.dat
Normal file
BIN
generated/atarist/levels/level01.dat
Normal file
Binary file not shown.
BIN
generated/atarist/levels/level02.dat
Normal file
BIN
generated/atarist/levels/level02.dat
Normal file
Binary file not shown.
BIN
generated/atarist/levels/level03.dat
Normal file
BIN
generated/atarist/levels/level03.dat
Normal file
Binary file not shown.
BIN
generated/atarist/levels/level04.dat
Normal file
BIN
generated/atarist/levels/level04.dat
Normal file
Binary file not shown.
BIN
generated/atarist/levels/level05.dat
Normal file
BIN
generated/atarist/levels/level05.dat
Normal file
Binary file not shown.
BIN
generated/atarist/levels/level06.dat
Normal file
BIN
generated/atarist/levels/level06.dat
Normal file
Binary file not shown.
BIN
generated/atarist/levels/level07.dat
Normal file
BIN
generated/atarist/levels/level07.dat
Normal file
Binary file not shown.
BIN
generated/atarist/levels/level08.dat
Normal file
BIN
generated/atarist/levels/level08.dat
Normal file
Binary file not shown.
BIN
generated/atarist/levels/level09.dat
Normal file
BIN
generated/atarist/levels/level09.dat
Normal file
Binary file not shown.
BIN
generated/atarist/levels/level10.dat
Normal file
BIN
generated/atarist/levels/level10.dat
Normal file
Binary file not shown.
BIN
generated/atarist/levels/level11.dat
Normal file
BIN
generated/atarist/levels/level11.dat
Normal file
Binary file not shown.
BIN
generated/atarist/levels/level12.dat
Normal file
BIN
generated/atarist/levels/level12.dat
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue