More optimizing
This commit is contained in:
parent
786ff257c3
commit
57dab02e21
27 changed files with 3605 additions and 1856 deletions
85
MECHANICS.md
85
MECHANICS.md
|
|
@ -275,6 +275,37 @@ The phase byte `$7164` is the dispatcher; `$7165/$7166` form a
|
||||||
frame-rate gate (count down each frame, reload when zero) so each
|
frame-rate gate (count down each frame, reload when zero) so each
|
||||||
phase handler runs every N frames, not every frame.
|
phase handler runs every N frames, not every frame.
|
||||||
|
|
||||||
|
**What a read sees (VERIFIED 2026-10-06 against VICE, all 25 screens).**
|
||||||
|
The latches hold until read, and the main loop reads them (`$6966`, after
|
||||||
|
the second `$404B`) two frames after its last read: one frame still
|
||||||
|
shows the sprites the previous tick flushed, the next shows this tick's
|
||||||
|
(`$4293` runs right after the first `$404B`, raster `$FC`). So a read
|
||||||
|
is the OR of both frames -- a sprite pulling away from a contact is still
|
||||||
|
touching on the tick it leaves (the pad light at `$6EF0` stays on one
|
||||||
|
tick longer than one frame would give). The older frame is drawn while
|
||||||
|
the CPU waits at `$5F94`, i.e. over the picture this tick's work has
|
||||||
|
already changed: a melting level-P flake whose windsock flipped under it
|
||||||
|
that tick does not touch it in the old frame either.
|
||||||
|
|
||||||
|
**Ticks that overrun a frame.** All of the above assumes a tick's work
|
||||||
|
between two reads fits in one frame. When it does not -- level V's row
|
||||||
|
shift (every 16th tick, `$7DA6`), the speech player (`$6C7F` blocks) --
|
||||||
|
the raster latch is already set when `$5F94` is reached, so the flush
|
||||||
|
lands mid-frame (VICE: raster 36 and 58 on V's shift ticks) and the new
|
||||||
|
positions are only drawn below that line before the read; a sprite whose
|
||||||
|
new top line has already passed keeps drawing its old start's remaining
|
||||||
|
lines at the new X. A collision at the top of the screen on such a tick
|
||||||
|
reaches the read one tick late. The port does not model this (it would
|
||||||
|
need the C64's cycle timing); it is the one difference the stsim gate
|
||||||
|
knows of, seen on level V when the cab hits the top border on a shift
|
||||||
|
tick. And `$D01E` has the bits of
|
||||||
|
EVERY pair of shown sprites: the hooks test groups of bits (`AND #$F8`
|
||||||
|
at `$69F9` and in level G's `$7E35` also catch a hook sprite touching the
|
||||||
|
exhaust flame), so the cab is not the only side that matters. The
|
||||||
|
framePresent read at `$6920` clears the latch, but the start-of-screen
|
||||||
|
jingle then shows that frame for as long as it plays, so the first
|
||||||
|
tick's read still includes it.
|
||||||
|
|
||||||
### Collision dispatch trace (VERIFIED via emulator)
|
### Collision dispatch trace (VERIFIED via emulator)
|
||||||
|
|
||||||
Verified by running `$6966` from `Tracer.from_dump('raw.bin')` with
|
Verified by running `$6966` from `Tracer.from_dump('raw.bin')` with
|
||||||
|
|
@ -667,18 +698,21 @@ actually 4 cels organized as 2 bases x 2 flicker variants.
|
||||||
|
|
||||||
### `$61BD` -- passengerEventDraw
|
### `$61BD` -- passengerEventDraw
|
||||||
|
|
||||||
If `$716F != 0` (a passenger event just happened): peek `$71CD`
|
CORRECTED 2026-10-06 (checked against VICE by the stsim gate): once the
|
||||||
(event type) and dispatch:
|
touchdown bob is over (`$716F == 0`) a landing's event `$71CD`, set by
|
||||||
|
padDetect at `$64C9..$64F5`, is voiced:
|
||||||
|
|
||||||
| `$71CD` | Event | Pointer source for $6C7F |
|
| `$71CD` | Landed... | Action |
|
||||||
| ------- | ----------- | ---------------------------------- |
|
| ------- | ------------------------------------------- | --------------------------------------- |
|
||||||
| 1 | (drop-off?) | $6A/$6B = $6C6C |
|
| 1 | while a fare waits on another pad ($64F3) | speak `$6C6C` "HT" (HEY TAXI) |
|
||||||
| 2 | (pickup?) | $6A/$6B = $6C7A |
|
| 2 | with a fare aboard, on the wrong pad ($64DF) | `$43A5` (the meter is LOST), speak `$6C7A` "P<digit>" |
|
||||||
| other | (takeoff?) | $6A/$6B = $6C7D |
|
| 3 | the same, the fare wanting UP ($64E7) | `$43A5`, speak `$6C7D` "U" |
|
||||||
|
|
||||||
`$6C7F` is "render text/scoreboard": marshals shadow, flushes,
|
`$6C7F` is the speech player (one sample per character); it zeroes
|
||||||
then draws indirect text. So this routine surfaces a HUD blurb
|
`$71CD` (`$6C99`) and sets both release timers `$7216/$7217` to 1. So any
|
||||||
based on what just happened.
|
phrase spoken before the bob ends cancels the event. `$6F18` raises event
|
||||||
|
1 for the third screen's takeoff fare too; the prelude speaks it at
|
||||||
|
`$5F30` ("P1?", `$6C72`) before the first tick.
|
||||||
|
|
||||||
### `$63DD` -- fireButtonEdge
|
### `$63DD` -- fireButtonEdge
|
||||||
|
|
||||||
|
|
@ -877,26 +911,25 @@ kills all audio, then either loops waiting for release or transitions.
|
||||||
|
|
||||||
### `$6419` -- fuelBarHud
|
### `$6419` -- fuelBarHud
|
||||||
|
|
||||||
```
|
CORRECTED 2026-10-06 (the old transcript here had the branches wrong):
|
||||||
LDA #$0B; STA $DBA2,$DBA3,$DBCA,$DBCB ; pad color cells
|
|
||||||
INC $71BE; AND #$07; STA $71BE ; mod-8 ticker
|
|
||||||
CMP #$05; BPL RTS ; only first 5 of 8 frames
|
|
||||||
|
|
||||||
LDA $714F (Y-vel hi); BMI down ; sign-test
|
```
|
||||||
BEQ check_X ; Y vel = 0
|
LDA #$0B; STA $DBA2,$DBA3,$DBCA,$DBCB ; both lamp pairs dark grey
|
||||||
A=$02, X=$28; JMP write
|
INC $71BE; AND #$07; STA $71BE ; mod-8 ticker
|
||||||
check_X:
|
CMP #$05; BPL RTS ; lit on 5 ticks of 8
|
||||||
LDA $714E
|
|
||||||
BMI down
|
LDA $714F (Y-vel hi); BMI $6449 ; climbing -> cyan
|
||||||
A=$03, X=$00; JMP write
|
BEQ $6444
|
||||||
down:
|
A=$02, X=$28; JMP write ; hi > 0 -> red, row 24
|
||||||
A=$07, X=$00
|
$6444: LDA $714E; BMI $6450 ; hi 0, lo >= $80 -> yellow
|
||||||
|
$6449: A=$03, X=$00; JMP write ; else -> cyan
|
||||||
|
$6450: A=$07, X=$00
|
||||||
write: STA $DBA2,X; STA $DBA3,X; RTS
|
write: STA $DBA2,X; STA $DBA3,X; RTS
|
||||||
```
|
```
|
||||||
|
|
||||||
So the fuel bar's color depends on cab Y-velocity sign (rising vs
|
So it is a descent-rate lamp: red on row 24 when the cab falls a whole
|
||||||
descending vs hovering). The `$DBA2-$DBCB` cells form the in-flight
|
sub-row a tick or faster, yellow on row 23 from half that, cyan on row
|
||||||
indicator strip.
|
23 otherwise (any climb, a hover, a slow fall).
|
||||||
|
|
||||||
## IRQ handler (`$CF52`) -- music only, no animation
|
## IRQ handler (`$CF52`) -- music only, no animation
|
||||||
|
|
||||||
|
|
|
||||||
2
Makefile
2
Makefile
|
|
@ -3,7 +3,7 @@
|
||||||
# and program.mk describes the game to them.
|
# and program.mk describes the game to them.
|
||||||
#
|
#
|
||||||
# make every target whose toolchain is installed
|
# make every target whose toolchain is installed
|
||||||
# make <port> one target: iigs amiga atarist dos x68000 dvx
|
# make <port> one target: iigs amiga atarist dos x68000 dvx sdl3
|
||||||
# make check build, then run the gates (scripts/verify.sh)
|
# make check build, then run the gates (scripts/verify.sh)
|
||||||
# make clean remove build/
|
# make clean remove build/
|
||||||
# make help JoeyLib's project targets
|
# make help JoeyLib's project targets
|
||||||
|
|
|
||||||
18
README.md
18
README.md
|
|
@ -9,9 +9,10 @@ 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
|
written from its disassembly (MECHANICS.md): the flight physics, the fare
|
||||||
machine, the 24 levels' hook programs, the title and level intros, the
|
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
|
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
|
SID player) and the speech. The simulation (stSim.c) is checked tick for
|
||||||
tick against VICE traces of the C64's four attract-demo rides (VERIFIED.md),
|
tick against the original game running in VICE: a ride on each of the 25
|
||||||
and the gates below hold it there.
|
screens, A..X and the MYSTERY SCREEN, traces byte for byte as on the C64
|
||||||
|
(tools/stsim; VERIFIED.md), and the gates below hold it there.
|
||||||
|
|
||||||
## Setting up
|
## Setting up
|
||||||
|
|
||||||
|
|
@ -97,7 +98,7 @@ $ scripts/verify.sh disk # or name the ones to run
|
||||||
|
|
||||||
| Gate | What it proves |
|
| 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`) |
|
| `stsim` | Every screen's ride (all 25: the four C64 attract recordings and twenty-one synthesised streams, up to 4000 ticks, crashes costing a cab as in a real game) traces exactly as the real C64 traced it -- `gateHashes.txt` holds the md5s of the C64's traces (`tools/stsim/gate.sh`) -- 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`) |
|
| `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 |
|
| `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 |
|
| `iigsaudio` | The IIgs sound interrupt is NTP's, the DOC is audible and its sound slots carry waveforms |
|
||||||
|
|
@ -109,8 +110,13 @@ missing, or the game data not imported).
|
||||||
|
|
||||||
## Tools
|
## Tools
|
||||||
|
|
||||||
- `tools/stsim` -- the simulation on the host: prints the per-tick state a
|
- `tools/stsim` -- the simulation on the host, and its C64 counterpart:
|
||||||
VICE trace records, so `compare.py` can diff the port against the C64.
|
`stsim` prints one line of state per tick, `c64trace.py` rides the same
|
||||||
|
input on the original disk in VICE and prints the same line from the
|
||||||
|
C64's memory, and `gate.sh c64` compares the two for every screen
|
||||||
|
(`compare.py` shows where they part). That needs VICE's `x64sc` and the
|
||||||
|
original `SPACETAX.D64` in `stuff/`; `gate.sh c64hashes` is how
|
||||||
|
`gateHashes.txt` is made. `recorder` searches for the synthesised rides.
|
||||||
- `tools/staxiharness` -- the front-end screen checks.
|
- `tools/staxiharness` -- the front-end screen checks.
|
||||||
- `tools/staxibake` -- writes the cel bank the build pre-compiles per machine.
|
- `tools/staxibake` -- writes the cel bank the build pre-compiles per machine.
|
||||||
- `assets/` -- the generators behind the committed C64 tables
|
- `assets/` -- the generators behind the committed C64 tables
|
||||||
|
|
|
||||||
35
TODO.md
35
TODO.md
|
|
@ -19,3 +19,38 @@ moved out of JoeyLib's tree into this repository; JoeyLib's own list
|
||||||
per-call overhead in save-and-draw and restore (~1.75k and ~1.25k
|
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
|
cycles a star), not bytes moved, so it becomes feasible with a leaner
|
||||||
save/draw/restore path for small cels in JoeyLib.
|
save/draw/restore path for small cels in JoeyLib.
|
||||||
|
The ST's palette warp was slow for a LIBRARY reason, fixed in JoeyLib
|
||||||
|
2026-10-06 with no game change: every jlPaletteSet made the ST HAL
|
||||||
|
requantize its whole per-scanline palette table (3200 entries, ~38 ms
|
||||||
|
of 8 MHz 68000 each, Hatari profiler); it now requantizes only the
|
||||||
|
palette that changed. Warp on the ST, page flips per second inside the
|
||||||
|
warp from a Hatari io_write trace (same method before and after):
|
||||||
|
13.5 -> 53.6 fps; the title's logo 17.4 -> 20.7. (The 23.3 above
|
||||||
|
predates the 2026-10-05 intro-pacing change; this method gives 13.5 on
|
||||||
|
the tree just before the fix.) JoeyLib also gained an STe build, which
|
||||||
|
Space Taxi gets with `make ste` (build/ste, STe and Mega STe only): the
|
||||||
|
warp runs the same 53.6 on an STe and 56.8 on a Mega STe. Other ports'
|
||||||
|
palette paths were not touched by this fix.
|
||||||
|
|
||||||
|
- [ ] **Does $4523 really leave sprites 0-6 enabled?** The level-name setup
|
||||||
|
($4523..$4665, stIntroEnter in stTitle.c) never clears the enable bits
|
||||||
|
of sprites 0-6 on the C64, but the port does. Settle it from the
|
||||||
|
disassembly (every $D015 write on the path into the intro), and check
|
||||||
|
it in VICE if the bytes leave doubt. Dropped out of JoeyLib's TODO in
|
||||||
|
the 2026-10-05 split; restored 2026-10-06.
|
||||||
|
|
||||||
|
- [ ] **What the C64 gate cannot reach.** `tools/stsim/gate.sh c64` holds
|
||||||
|
all 25 screens to the real game, but only along the attract demo's
|
||||||
|
machinery: its table RNG ($446A; a real game reads the SID noise
|
||||||
|
oscillator, which the port stands in for with an LCG), one cabbie, and
|
||||||
|
whatever the searched streams happen to do. Two-to-four-cabbie turns,
|
||||||
|
the bonus cab at $3.00, refuelling on a fuel pad ($6E23's pump half),
|
||||||
|
a crash with an UP fare aboard and the shift's end are exercised by no
|
||||||
|
ride (gcov over the 25 rides shows exactly what is reached; level A's
|
||||||
|
ride covers the hatch exit, $6BFB).
|
||||||
|
- [ ] **Ticks that overrun a frame.** When a tick's work does not fit in
|
||||||
|
one frame (level V's row shift, the speech player), the C64 flushes
|
||||||
|
its sprites mid-frame and a collision near the top of the screen
|
||||||
|
reaches the read a tick late (MECHANICS.md). The port assumes every
|
||||||
|
tick fits; modelling it needs the C64's cycle timing per tick. Seen
|
||||||
|
only on a level-V ride whose cab hit the top border on a shift tick.
|
||||||
|
|
|
||||||
53
VERIFIED.md
53
VERIFIED.md
|
|
@ -68,6 +68,59 @@ plus the inline scripts run during verification sessions.
|
||||||
| `$704E` hudArchive copies 7 bytes from `$07C2/$07E0` to `$71D3,X*8` / `$71F3,X*8` | With `$7214 = 2` (fare idx -> X = 16), `$07C2 = AA BB CC DD EE FF 11` and `$07E0 = 22 33 44 55 66 77 88`: after call, `$71E3 = AA BB CC DD EE FF 11 00` and `$7203 = 22 33 44 55 66 77 88 00`. Per-fare 8-byte slot stride confirmed; only 7 bytes written, 8th stays at prior value. |
|
| `$704E` hudArchive copies 7 bytes from `$07C2/$07E0` to `$71D3,X*8` / `$71F3,X*8` | With `$7214 = 2` (fare idx -> X = 16), `$07C2 = AA BB CC DD EE FF 11` and `$07E0 = 22 33 44 55 66 77 88`: after call, `$71E3 = AA BB CC DD EE FF 11 00` and `$7203 = 22 33 44 55 66 77 88 00`. Per-fare 8-byte slot stride confirmed; only 7 bytes written, 8th stays at prior value. |
|
||||||
| `$43A5` hudInit copies template `$43B1-$43B7 -> $07E0-$07E6` | After clobbering `$07E0` with `$FF * 7`, calling `$43A5`: `$07E0 = $66 $66 $66 $74 $77 $74 $74` matching template byte-for-byte. |
|
| `$43A5` hudInit copies template `$43B1-$43B7 -> $07E0-$07E6` | After clobbering `$07E0` with `$FF * 7`, calling `$43A5`: `$07E0 = $66 $66 $66 $74 $77 $74 $74` matching template byte-for-byte. |
|
||||||
|
|
||||||
|
## VERIFIED against the C64 in VICE: every screen (2026-10-06)
|
||||||
|
|
||||||
|
`tools/stsim/gate.sh c64` rides every screen -- A..X and the MYSTERY
|
||||||
|
SCREEN, Y -- on the original disk in VICE (`c64trace.py`) and on the port's
|
||||||
|
simulation (`stsim`), and the two per-tick traces are byte-identical: the
|
||||||
|
cab's position and velocity, pad, fare stage, crash phase, input, sprite 0
|
||||||
|
and 1 shadows, the shown sprites' positions and enable mask, the demo
|
||||||
|
playback offset and timer, the RNG, fuel, fare slots, destination, the
|
||||||
|
score, fare and fuel rows, border and background, and FNV hashes of all of
|
||||||
|
screen RAM, colour RAM and the charset. `gateHashes.txt` is the md5 of each
|
||||||
|
C64 trace, so `make check` holds the port to the real machine without an
|
||||||
|
emulator.
|
||||||
|
|
||||||
|
A ride is the attract demo's machinery driven to every screen: the demo
|
||||||
|
slots ($50C1) name the screen, the playback buffer ($0902) holds the same
|
||||||
|
640 bytes the sim plays, the screens-played count ($7215) makes it the
|
||||||
|
screen's place in the 24-hour shift (so every screen after B starts with
|
||||||
|
the takeoff fare, and Y with the finished-shift HUD), and a finished wreck
|
||||||
|
takes the real game's path ($6B52 -> $6B64: a cab lost, the cab respawned
|
||||||
|
at $5F27) instead of the demo's return to the title. H, T, W and X ride
|
||||||
|
the C64's own attract recordings; the other 21 ride streams searched for by
|
||||||
|
`tools/stsim/recorder`.
|
||||||
|
|
||||||
|
What the comparison found wrong in the port, each fixed from the asm:
|
||||||
|
|
||||||
|
| Fix | Evidence |
|
||||||
|
| --- | -------- |
|
||||||
|
| The descent lamp (`fuelBarHud`) lit by the wrong rule | `$6419`: hi byte of `$714F` negative -> cyan, zero with `$714E` bit 7 -> yellow, positive -> red on row 24 (`X=$28`) |
|
||||||
|
| Collisions saw one frame; the latch holds two | `$6966` reads two frames after the last read (`$404B` x2), so both the old and new sprite positions are in the read |
|
||||||
|
| Only cab pairs were latched | Hooks test `AND #$F8` (G `$7E35`, dispatch `$69F9`): level G re-hops when the flame touches an orb |
|
||||||
|
| Hazards' background bits were wiped when the cab hit too | `bg = 1` replaced the hazards' `$D01F` bits; now OR'd |
|
||||||
|
| The older frame's `$D01F` used the previous read's picture | VICE: at tick 2743 of level P, `$D01F` was still `$00` at `$5F97` -- the windsock at `$0677` flipped before the wait, so the melting flake no longer touched it |
|
||||||
|
| Level O had no gaps in its barriers, rolled the wrong glyph, ran at the wrong hook | prelude0 `$7D9C` blanks 4 x 8 cells; `$7E6E` rolls `$2BE0` = glyph `$7C`; per-tick is `$7D6F` |
|
||||||
|
| Level R's maze flip missed two cells | `$7D9C` writes `$0709..$070D` (five cells, not three) |
|
||||||
|
| Level K's and U's contact verdicts | `$7DC4` `LDA #$FF` (kill, no wreck); `$7DDA` `LDA #$00` (safe) |
|
||||||
|
| Level V scrolled every tick, columns 0..38, silently | `$7DA6`: every 16th tick (`AND #$0F`), columns 1..38 (`$7E0E`/`$7E35`), SFX `$7D9D` |
|
||||||
|
| Level E grew its beanstalk only when the fare wanted UP -- backwards | `$7DD5`: `CMP #$0B; BNE` grow, `RTS` when UP |
|
||||||
|
| Level I drew its bullet's climb and direction in the wrong order; hires bullets | `$7E21` rng(2) before `$7E2F` rng(4); prelude1 `$7DB1` sets `$D01C = $FF` |
|
||||||
|
| Level Q cycled cels every tick, hires, and never buzzed | `$7DFF` every other tick; prelude1 `$7D9B` `$D01C = $FF`; `$7E55` buzz SFX with rng(100) while `$7217` is 0 |
|
||||||
|
| Voice release timers missing | `$42E9` arms `$7216+voice`, `$4320` counts down, `$6C85` sets 1, `$6906` clears; read by Q and Y |
|
||||||
|
| A landing's event never voiced; wrong-pad landings kept the fare | `$61BD`: event 2/3 runs `$43A5` (meter lost) and speaks `$6C7A`/`$6C7D`; `$6C7F` clears `$71CD` |
|
||||||
|
| Respawn re-ran the level's first prelude and the takeoff fare | `$6BCD JMP $5F27` skips `$6946`, `$7D66`, `$6F18`: a waiting passenger stays put across a crash |
|
||||||
|
| The message blank was 11 characters | `$6C38` is 12 spaces; the takeoff text was 13 (`$6C1E` is 12) |
|
||||||
|
| Level F (TAXI PONG) and Y (MYSTERY SCREEN) had no hooks at all | F `$7D9C`/`$7DDB`; Y `$7D9B`/`$7E02`/`$7E21`, its program running on to `$80CA` (STL5 ships `$7D98..$80CA`); Y's sprites come from extra scene `$23` |
|
||||||
|
| The gate's own start differed from the C64's | `$61FB` wraps the turn and counts the screen before `$62F0` (the port's game did; stsim did not) |
|
||||||
|
|
||||||
|
Known and not modelled: a tick whose work overruns a frame (level V's row
|
||||||
|
shift, speech) flushes the sprites mid-frame, so a collision at the top
|
||||||
|
of the screen can reach the C64's read a tick later than the port's
|
||||||
|
(MECHANICS.md, "Ticks that overrun a frame"). Found on a level-V ride
|
||||||
|
whose cab hit the top border on a shift tick (VICE: flush at raster 58);
|
||||||
|
the gate's V ride does not do that.
|
||||||
|
|
||||||
## UNVERIFIED (acknowledged, not yet proven)
|
## UNVERIFIED (acknowledged, not yet proven)
|
||||||
|
|
||||||
| Claim | Why unverified |
|
| Claim | Why unverified |
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,13 @@
|
||||||
# mkstlevel tool stays in tree for hand-authored .txt levels.
|
# mkstlevel tool stays in tree for hand-authored .txt levels.
|
||||||
#
|
#
|
||||||
# Usage:
|
# Usage:
|
||||||
# romToLevel.py <raw-dump> <out-dir> # all 24 levels
|
# romToLevel.py <raw-dump> <out-dir> # all 25 screens + title
|
||||||
# romToLevel.py <raw-dump> <out-dir> <scene_id> # one specific
|
# romToLevel.py <raw-dump> <out-dir> <scene_id> # one specific
|
||||||
#
|
#
|
||||||
# Writes:
|
# Writes:
|
||||||
# <out-dir>/level01.dat ... level24.dat (scene 0 = "A" = level01)
|
# <out-dir>/level01.dat ... level25.dat (scene 0 = "A" = level01,
|
||||||
# <out-dir>/tbank0.png (charset extracted from $2800)
|
# scene 24 = "Y" = level25)
|
||||||
|
# <out-dir>/title.dat
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import struct
|
import struct
|
||||||
|
|
@ -49,11 +50,18 @@ ST_TILE_PX = 8
|
||||||
VIC_X_ORIGIN = 24
|
VIC_X_ORIGIN = 24
|
||||||
VIC_Y_ORIGIN = 50
|
VIC_Y_ORIGIN = 50
|
||||||
PAD_SURFACE_ORIGIN = 36
|
PAD_SURFACE_ORIGIN = 36
|
||||||
LEVEL_LETTERS = [chr(ord('A') + i) for i in range(24)]
|
# Screens A..X, then Y: the 25th, " MYSTERY SCREEN !!! ", which $5167
|
||||||
|
# loads once the 24-hour shifts have cleared A..X.
|
||||||
|
LEVEL_LETTERS = [chr(ord('A') + i) for i in range(25)]
|
||||||
|
# The hook blob each level ships: its six trampolines' targets and
|
||||||
|
# tables from $7D98 up to the last byte any hook reads. Level Y's
|
||||||
|
# program runs on past $8000; its last SFX program is $80C2..$80CA.
|
||||||
|
HOOK_FIRST = 0x7D98
|
||||||
|
HOOK_END = 0x80CB
|
||||||
|
|
||||||
|
|
||||||
def write_level(path, level_index, after, screen, colormap, sprite_blocks):
|
def write_level(path, level_index, after, screen, colormap, sprite_blocks):
|
||||||
"""Write an STL4 level .dat straight from post-decompression RAM.
|
"""Write an STL5 level .dat straight from post-decompression RAM.
|
||||||
|
|
||||||
Everything stays in the C64's own units so the runtime can mirror the
|
Everything stays in the C64's own units so the runtime can mirror the
|
||||||
original routines byte for byte:
|
original routines byte for byte:
|
||||||
|
|
@ -71,16 +79,16 @@ def write_level(path, level_index, after, screen, colormap, sprite_blocks):
|
||||||
passenger X frac, passenger X col, unused
|
passenger X frac, passenger X col, unused
|
||||||
tilemap[1000] screen RAM (all 25 rows, the HUD rows included)
|
tilemap[1000] screen RAM (all 25 rows, the HUD rows included)
|
||||||
colormap[1000] colour RAM low nibbles
|
colormap[1000] colour RAM low nibbles
|
||||||
levelIndex 0..23 = A..X, 24 = title
|
levelIndex 0..23 = A..X, 24 = Y, 25 = title
|
||||||
spriteCount N, then N x (ptr, 63 bytes): the per-level sprite
|
spriteCount N, then N x (ptr, 63 bytes): the per-level sprite
|
||||||
bitmaps the level's extra scene loads at $2000+
|
bitmaps the level's extra scene loads at $2000+
|
||||||
hookData[616] $7D98-$7FFF: the level's hook code and tables
|
hookData[819] $7D98-$80CA: the level's hook code and tables
|
||||||
"""
|
"""
|
||||||
cells = ST_TILEMAP_W * ST_PLAYFIELD_ROWS
|
cells = ST_TILEMAP_W * ST_PLAYFIELD_ROWS
|
||||||
assert len(screen) == cells, f'screen is {len(screen)}, need {cells}'
|
assert len(screen) == cells, f'screen is {len(screen)}, need {cells}'
|
||||||
assert len(colormap) == cells, f'colormap is {len(colormap)}, need {cells}'
|
assert len(colormap) == cells, f'colormap is {len(colormap)}, need {cells}'
|
||||||
with open(path, 'wb') as fp:
|
with open(path, 'wb') as fp:
|
||||||
fp.write(b'STL4')
|
fp.write(b'STL5')
|
||||||
fp.write(bytes(after[0x7D78:0x7D78 + 22]))
|
fp.write(bytes(after[0x7D78:0x7D78 + 22]))
|
||||||
fp.write(bytes(after[0x7D00:0x7D09]))
|
fp.write(bytes(after[0x7D00:0x7D09]))
|
||||||
fp.write(bytes([after[0x7D5A], after[0x7D09], after[0x7D60]]))
|
fp.write(bytes([after[0x7D5A], after[0x7D09], after[0x7D60]]))
|
||||||
|
|
@ -100,7 +108,7 @@ def write_level(path, level_index, after, screen, colormap, sprite_blocks):
|
||||||
for ptr, data in sprite_blocks:
|
for ptr, data in sprite_blocks:
|
||||||
fp.write(bytes([ptr]))
|
fp.write(bytes([ptr]))
|
||||||
fp.write(bytes(data))
|
fp.write(bytes(data))
|
||||||
fp.write(bytes(after[0x7D98:0x8000]))
|
fp.write(bytes(after[HOOK_FIRST:HOOK_END]))
|
||||||
|
|
||||||
|
|
||||||
# Levels whose data blob flags an extra scene ($7D97 != 0) load a second
|
# Levels whose data blob flags an extra scene ($7D97 != 0) load a second
|
||||||
|
|
@ -138,23 +146,6 @@ def extract_one_level(ram, scene_id, out_dir):
|
||||||
return out_path, after[0x7D5A], len(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):
|
def extract_title(ram, out_dir):
|
||||||
"""The title screen is scene 25 (what $477A decompresses).
|
"""The title screen is scene 25 (what $477A decompresses).
|
||||||
Pads, spawn and templates are dummies."""
|
Pads, spawn and templates are dummies."""
|
||||||
|
|
@ -183,22 +174,17 @@ def main():
|
||||||
if len(sys.argv) >= 4:
|
if len(sys.argv) >= 4:
|
||||||
scene_ids = [int(sys.argv[3], 0)]
|
scene_ids = [int(sys.argv[3], 0)]
|
||||||
else:
|
else:
|
||||||
# Levels A..X = scenes 0..23. The title comes from screen RAM
|
# Screens A..Y = scenes 0..24, then the title (scene 25).
|
||||||
# at $0400 (the dump was taken on the title screen), not from
|
scene_ids = list(range(len(LEVEL_LETTERS)))
|
||||||
# a decompressed scene.
|
|
||||||
scene_ids = list(range(24))
|
|
||||||
|
|
||||||
for sid in scene_ids:
|
for sid in scene_ids:
|
||||||
path, pad_count, nspr = extract_one_level(ram, sid, out_dir)
|
path, pad_count, nspr = extract_one_level(ram, sid, out_dir)
|
||||||
size = os.path.getsize(path)
|
size = os.path.getsize(path)
|
||||||
print(f' scene {sid:2d} (level {LEVEL_LETTERS[sid]}): pads={pad_count} sprites={nspr} size={size} -> {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:
|
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)
|
title_path = extract_title(ram, out_dir)
|
||||||
print(f' title (screen RAM $0400) : -> {title_path} ({os.path.getsize(title_path)} bytes)')
|
print(f' title (scene 25) : -> {title_path} ({os.path.getsize(title_path)} bytes)')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
||||||
24
program.mk
24
program.mk
|
|
@ -41,13 +41,14 @@ STAXI_GEN := $(PROJECT_BUILD)/generated/$(ASSET_PLATFORM)
|
||||||
STAXI_RUN := $(call programDataDir,STAXI)
|
STAXI_RUN := $(call programDataDir,STAXI)
|
||||||
STAXI_OPT := $(call programOptionalDir,STAXI)
|
STAXI_OPT := $(call programOptionalDir,STAXI)
|
||||||
|
|
||||||
# 24 lettered levels (A..X) from scenes 0..23 plus the title from scene 24,
|
# The 25 screens -- A..X from scenes 0..23 and the MYSTERY SCREEN, Y, from
|
||||||
# extracted by assets/romToLevel.py, which runs the captured decompressor
|
# scene 24 -- plus the title from scene 25, extracted by assets/romToLevel.py,
|
||||||
# through a Python 6502 emulator over the raw RAM dump and writes STL2 .dat
|
# which runs the captured decompressor through a Python 6502 emulator over the
|
||||||
# files directly. (mkstlevel + title.txt is no longer used: the C64 title
|
# raw RAM dump and writes STL5 .dat files directly. (mkstlevel + title.txt is
|
||||||
# encodes its screen with the game's custom charset, so a hand-authored tile
|
# no longer used: the C64 title encodes its screen with the game's custom
|
||||||
# layout with a different glyph mapping renders as garbage.)
|
# charset, so a hand-authored tile layout with a different glyph mapping
|
||||||
STAXI_LEVEL_NUMS := 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
# renders as garbage.)
|
||||||
|
STAXI_LEVEL_NUMS := 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
||||||
STAXI_LEVELS := $(foreach n,$(STAXI_LEVEL_NUMS),levels/level$(n).dat) levels/title.dat
|
STAXI_LEVELS := $(foreach n,$(STAXI_LEVEL_NUMS),levels/level$(n).dat) levels/title.dat
|
||||||
ROMTOLEVEL := $(STAXI_HOME)/assets/romToLevel.py
|
ROMTOLEVEL := $(STAXI_HOME)/assets/romToLevel.py
|
||||||
|
|
||||||
|
|
@ -58,16 +59,17 @@ ROMTOLEVEL := $(STAXI_HOME)/assets/romToLevel.py
|
||||||
# cels at entry -- so they are optional data, which the IIgs data disk packs
|
# cels at entry -- so they are optional data, which the IIgs data disk packs
|
||||||
# last, while there is room.
|
# last, while there is room.
|
||||||
# ...except on the X68000, where they do not fit. The Human68k floppy is
|
# ...except on the X68000, where they do not fit. The Human68k floppy is
|
||||||
# 1232 KB and these eight banks are 787 KB of it (level09 alone is 236 KB,
|
# 1232 KB and, measured when there were eight, the banks were 787 KB of it
|
||||||
# level10 175, level17 165), which with the 296 KB binary, the levels and
|
# (level09 alone 236 KB, level10 175, level17 165), which with the 296 KB
|
||||||
# the speech put the disk about 300 KB over however small speech gets.
|
# binary, the levels and the speech put the disk about 300 KB over however
|
||||||
|
# small speech gets.
|
||||||
# stRenderLevelCels already handles their absence the right way: it builds
|
# stRenderLevelCels already handles their absence the right way: it builds
|
||||||
# and compiles the level's cels at ENTRY, behind the LOADING bar, so the
|
# and compiles the level's cels at ENTRY, behind the LOADING bar, so the
|
||||||
# ride itself still never compiles. That is also what spacetaxi.c's
|
# ride itself still never compiles. That is also what spacetaxi.c's
|
||||||
# codegen-arena comment says the 68k ports are meant to do -- their cels
|
# codegen-arena comment says the 68k ports are meant to do -- their cels
|
||||||
# are big and their JIT is fast. Ship them there again if the payload ever
|
# are big and their JIT is fast. Ship them there again if the payload ever
|
||||||
# moves to a hard disk image.
|
# moves to a hard disk image.
|
||||||
STAXI_CEL_LEVELS := $(if $(filter x68000,$(PLATFORM)),,07 08 09 10 12 16 17 21)
|
STAXI_CEL_LEVELS := $(if $(filter x68000,$(PLATFORM)),,06 07 08 09 10 12 16 17 21 25)
|
||||||
|
|
||||||
# The C64-exact cel bank, generated by staxibake (from the shared cel table
|
# The C64-exact cel bank, generated by staxibake (from the shared cel table
|
||||||
# stCels.c + C64 bitmaps) and compiled for this target by the host
|
# stCels.c + C64 bitmaps) and compiled for this target by the host
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# verify.sh - Space Taxi's gates, run one after another, with a summary.
|
# verify.sh - Space Taxi's gates, run one after another, with a summary.
|
||||||
#
|
#
|
||||||
# stsim every hook level's 4000-tick simulation trace is identical to
|
# stsim every screen's ride (all 25) traces byte for byte as the real
|
||||||
# its golden (tools/stsim/gate.sh), and every cel a ride shows
|
# C64 traced it in VICE -- gateHashes.txt holds the C64 traces'
|
||||||
# is baked (tools/stsim/celGate.sh)
|
# md5s (tools/stsim/gate.sh) -- and every cel a ride shows is
|
||||||
|
# baked (tools/stsim/celGate.sh)
|
||||||
# harness the front-end screens above the simulation -- the high-score
|
# harness the front-end screens above the simulation -- the high-score
|
||||||
# table, name entry and its save -- on JoeyLib's host BLANK
|
# table, name entry and its save -- on JoeyLib's host BLANK
|
||||||
# port (tools/staxiharness)
|
# port (tools/staxiharness)
|
||||||
|
|
|
||||||
98
stFare.c
98
stFare.c
|
|
@ -8,6 +8,8 @@
|
||||||
// pad has been used the destination becomes "UP PLEASE" and the top
|
// pad has been used the destination becomes "UP PLEASE" and the top
|
||||||
// hatch opens.
|
// hatch opens.
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "stSim.h"
|
#include "stSim.h"
|
||||||
|
|
||||||
// Screen-code strings ($6C1E..$6C7D). All ASCII-compatible glyphs.
|
// Screen-code strings ($6C1E..$6C7D). All ASCII-compatible glyphs.
|
||||||
|
|
@ -21,6 +23,20 @@ static const uint8_t kSpeechUp[] = "U?"; // $6C6F
|
||||||
static const uint8_t kSpeechPad[] = "PX?"; // $6C72 (X = digit)
|
static const uint8_t kSpeechPad[] = "PX?"; // $6C72 (X = digit)
|
||||||
static const uint8_t kSpeechThanks[] = "!"; // $6C76
|
static const uint8_t kSpeechThanks[] = "!"; // $6C76
|
||||||
static const uint8_t kSpeechHey[] = "H"; // $6C78
|
static const uint8_t kSpeechHey[] = "H"; // $6C78
|
||||||
|
static const uint8_t kSpeechPadAgain[] = "PX"; // $6C7A (X = digit)
|
||||||
|
static const uint8_t kSpeechUpAgain[] = "U"; // $6C7D
|
||||||
|
// $6727/$6F3A/$6F3D patch the destination digit into "PAD PLEASE"
|
||||||
|
// ($6C22, the message's fifth cell) and the two pad phrases ($6C73,
|
||||||
|
// $6C7B: the second character).
|
||||||
|
#define ST_TEXT_PAD_DIGIT_AT 4u
|
||||||
|
#define ST_SPEECH_DIGIT_AT 1u
|
||||||
|
#define ST_DIGIT_GLYPH_BASE 0x30u
|
||||||
|
// $6F20: from the third screen on, the cab starts with a fare aboard
|
||||||
|
// who wants pad 1.
|
||||||
|
#define ST_TAKEOFF_LEVEL 2u
|
||||||
|
#define ST_TAKEOFF_PAD 1u
|
||||||
|
// $6C85: speaking leaves voices 1 and 2 one tick from their gate-off.
|
||||||
|
#define ST_SPEECH_RELEASE 1u
|
||||||
// Waiting wave cycle ($66D6) and the base-fare blob ($43B9 = +5.00).
|
// Waiting wave cycle ($66D6) and the base-fare blob ($43B9 = +5.00).
|
||||||
static const uint8_t kWaveCels[4] = { 0xC6, 0xC7, 0xD9, 0xC7 };
|
static const uint8_t kWaveCels[4] = { 0xC6, 0xC7, 0xD9, 0xC7 };
|
||||||
static const uint8_t kFareBlob[7] = { 0x66, 0x66, 0x66, 0x6F, 0x77, 0x74, 0x74 };
|
static const uint8_t kFareBlob[7] = { 0x66, 0x66, 0x66, 0x6F, 0x77, 0x74, 0x74 };
|
||||||
|
|
@ -28,9 +44,11 @@ static const uint8_t kFareBlob[7] = { 0x66, 0x66, 0x66, 0x6F, 0x77, 0x74, 0
|
||||||
|
|
||||||
static void beamIn(StSimT *sim);
|
static void beamIn(StSimT *sim);
|
||||||
static bool decayStep(StSimT *sim);
|
static bool decayStep(StSimT *sim);
|
||||||
|
static void drawPadPlease(StSimT *sim);
|
||||||
static void idleTicker(StSimT *sim);
|
static void idleTicker(StSimT *sim);
|
||||||
static void lightOnAndLatch(StSimT *sim);
|
static void lightOnAndLatch(StSimT *sim);
|
||||||
static void newFare(StSimT *sim);
|
static void newFare(StSimT *sim);
|
||||||
|
static void sayPadPhrase(StSimT *sim, const uint8_t *phrase);
|
||||||
static void stageBoard(StSimT *sim);
|
static void stageBoard(StSimT *sim);
|
||||||
static void stageCleanup(StSimT *sim);
|
static void stageCleanup(StSimT *sim);
|
||||||
static void stageDropIn(StSimT *sim);
|
static void stageDropIn(StSimT *sim);
|
||||||
|
|
@ -76,6 +94,13 @@ static bool decayStep(StSimT *sim) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// "PAD n PLEASE" on the message row, n = the fare's destination.
|
||||||
|
static void drawPadPlease(StSimT *sim) {
|
||||||
|
stSimDrawText(sim, ST_MESSAGE_COL, ST_MESSAGE_ROW, kTextPadPlease, 1u);
|
||||||
|
stSimPutChar(sim, (uint8_t)(ST_MESSAGE_COL + ST_TEXT_PAD_DIGIT_AT), ST_MESSAGE_ROW, (uint8_t)(sim->activeSpriteIdx + ST_DIGIT_GLYPH_BASE));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// $660B -- a new fare turns up on a 3% roll per tick or when the
|
// $660B -- a new fare turns up on a 3% roll per tick or when the
|
||||||
// 100-tick timer runs out, never mid-crash.
|
// 100-tick timer runs out, never mid-crash.
|
||||||
static void idleTicker(StSimT *sim) {
|
static void idleTicker(StSimT *sim) {
|
||||||
|
|
@ -123,6 +148,17 @@ static void newFare(StSimT *sim) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// $6C7F on a pad phrase ($6C72 "PX?" or $6C7A "PX"), the destination's
|
||||||
|
// digit in place of the X.
|
||||||
|
static void sayPadPhrase(StSimT *sim, const uint8_t *phrase) {
|
||||||
|
uint8_t speech[sizeof(kSpeechPad)];
|
||||||
|
|
||||||
|
memcpy(speech, phrase, strlen((const char *)phrase) + 1u);
|
||||||
|
speech[ST_SPEECH_DIGIT_AT] = (uint8_t)(sim->activeSpriteIdx + ST_DIGIT_GLYPH_BASE);
|
||||||
|
stFareBoardingSound(sim, speech);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// $66DD -- the passenger shrinks into the cab, then picks where to go.
|
// $66DD -- the passenger shrinks into the cab, then picks where to go.
|
||||||
static void stageBoard(StSimT *sim) {
|
static void stageBoard(StSimT *sim) {
|
||||||
if (!decayStep(sim)) {
|
if (!decayStep(sim)) {
|
||||||
|
|
@ -139,16 +175,8 @@ static void stageBoard(StSimT *sim) {
|
||||||
if (sim->activeSpriteIdx == ST_FARE_DEST_UP) {
|
if (sim->activeSpriteIdx == ST_FARE_DEST_UP) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stSimDrawText(sim, 14u, 24u, kTextPadPlease, 1u);
|
drawPadPlease(sim);
|
||||||
stSimPutChar(sim, 18u, 24u, (uint8_t)(sim->activeSpriteIdx + 0x30u));
|
sayPadPhrase(sim, kSpeechPad);
|
||||||
{
|
|
||||||
uint8_t speech[4];
|
|
||||||
speech[0] = kSpeechPad[0];
|
|
||||||
speech[1] = (uint8_t)(sim->activeSpriteIdx + 0x30u);
|
|
||||||
speech[2] = kSpeechPad[2];
|
|
||||||
speech[3] = 0u;
|
|
||||||
stFareBoardingSound(sim, speech);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -251,7 +279,8 @@ static void walkToTarget(StSimT *sim) {
|
||||||
|
|
||||||
// $6C7F -- the passenger speaks: one sample per character.
|
// $6C7F -- the passenger speaks: one sample per character.
|
||||||
void stFareBoardingSound(StSimT *sim, const uint8_t *speech) {
|
void stFareBoardingSound(StSimT *sim, const uint8_t *speech) {
|
||||||
(void)sim;
|
memset(sim->sidRelTimer, ST_SPEECH_RELEASE, sizeof(sim->sidRelTimer)); // $6C85
|
||||||
|
sim->eventDispatchType = ST_EVENT_NONE; // $6C99
|
||||||
stAudioSilence();
|
stAudioSilence();
|
||||||
stAudioNoise(false);
|
stAudioNoise(false);
|
||||||
stAudioSpeechPhrase(speech);
|
stAudioSpeechPhrase(speech);
|
||||||
|
|
@ -283,6 +312,28 @@ uint8_t stFareChooseDestination(StSimT *sim) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// $61BD -- once the touchdown bob has settled, a landing that left an
|
||||||
|
// event ($645C) is voiced: a fare waiting on another pad calls "HEY
|
||||||
|
// TAXI" ($6C6C); a fare aboard set down on the wrong pad loses what is
|
||||||
|
// on its meter ($43A5) and names its destination again, "PAD n" ($6C7A)
|
||||||
|
// or "UP" ($6C7D).
|
||||||
|
void stFareEventVoice(StSimT *sim) {
|
||||||
|
if (sim->bobTimer != 0u || sim->eventDispatchType == ST_EVENT_NONE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (sim->eventDispatchType == ST_EVENT_HAIL) {
|
||||||
|
stFareBoardingSound(sim, kSpeechHeyTaxi);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
stSimHudInit(sim);
|
||||||
|
if (sim->eventDispatchType == ST_EVENT_WRONG_PAD) {
|
||||||
|
sayPadPhrase(sim, kSpeechPadAgain);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
stFareBoardingSound(sim, kSpeechUpAgain);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// $6EF0 -- while the passenger walks (to or from the cab) the pad stays
|
// $6EF0 -- while the passenger walks (to or from the cab) the pad stays
|
||||||
// lit only while the two sprites touch.
|
// lit only while the two sprites touch.
|
||||||
void stFarePadLightingGate(StSimT *sim) {
|
void stFarePadLightingGate(StSimT *sim) {
|
||||||
|
|
@ -410,6 +461,31 @@ void stFareStageDispatch(StSimT *sim) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// $6F18 -- from the third screen on, the cab sets out with a fare aboard
|
||||||
|
// who wants pad 1, and the prelude will have them say so ($5F30).
|
||||||
|
void stFareTakeoffSetup(StSimT *sim) {
|
||||||
|
sim->eventDispatchType = ST_EVENT_NONE;
|
||||||
|
sim->stage = ST_STAGE_IDLE;
|
||||||
|
if (sim->levelState < ST_TAKEOFF_LEVEL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sim->stage = ST_STAGE_RIDING;
|
||||||
|
sim->activeSpriteIdx = ST_TAKEOFF_PAD;
|
||||||
|
sim->activeDyingSlot = ST_TAKEOFF_PAD;
|
||||||
|
drawPadPlease(sim);
|
||||||
|
sim->eventDispatchType = ST_EVENT_HAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// $5F30 -- the end of the prelude: the takeoff fare names its pad.
|
||||||
|
void stFareTakeoffVoice(StSimT *sim) {
|
||||||
|
if (sim->eventDispatchType == ST_EVENT_NONE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sayPadPhrase(sim, kSpeechPad);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// $6CB8 -- no fare pad left: the passenger wants to leave the screen,
|
// $6CB8 -- no fare pad left: the passenger wants to leave the screen,
|
||||||
// so the top hatch opens.
|
// so the top hatch opens.
|
||||||
void stFareUpPlease(StSimT *sim) {
|
void stFareUpPlease(StSimT *sim) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// Space Taxi -- where each level's hook program keeps its tables.
|
// Space Taxi -- where each level's hook program keeps its tables.
|
||||||
//
|
//
|
||||||
// Every address is a byte in the level's shipped hook blob ($7D98..$7FFF,
|
// Every address is a byte in the level's shipped hook blob ($7D98..$80CA,
|
||||||
// StLevelT.hookData), so the same numbers serve the hook programs
|
// StLevelT.hookData), so the same numbers serve the hook programs
|
||||||
// (stHooks.c, reading through the live simulation) and the cel manifest
|
// (stHooks.c, reading through the live simulation) and the cel manifest
|
||||||
// (stLevelCels.c, reading the level file alone -- the offline cel baker
|
// (stLevelCels.c, reading the level file alone -- the offline cel baker
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
// Level indices (0 = A).
|
// Level indices (0 = A).
|
||||||
#define ST_LEVEL_E 4u
|
#define ST_LEVEL_E 4u
|
||||||
|
#define ST_LEVEL_F 5u
|
||||||
#define ST_LEVEL_G 6u
|
#define ST_LEVEL_G 6u
|
||||||
#define ST_LEVEL_H 7u
|
#define ST_LEVEL_H 7u
|
||||||
#define ST_LEVEL_I 8u
|
#define ST_LEVEL_I 8u
|
||||||
|
|
@ -26,10 +27,20 @@
|
||||||
#define ST_LEVEL_V 21u
|
#define ST_LEVEL_V 21u
|
||||||
#define ST_LEVEL_W 22u
|
#define ST_LEVEL_W 22u
|
||||||
#define ST_LEVEL_X 23u
|
#define ST_LEVEL_X 23u
|
||||||
|
#define ST_LEVEL_Y 24u
|
||||||
|
|
||||||
|
// The level header's shared sprite multicolours ($7D05/$7D06 -> $D025/$D026).
|
||||||
|
#define ST_HDR_SPRITE_MC0 5u
|
||||||
|
#define ST_HDR_SPRITE_MC1 6u
|
||||||
|
|
||||||
// Hardware sprites 3..7 are the hook sprites on every level that has any.
|
// Hardware sprites 3..7 are the hook sprites on every level that has any.
|
||||||
#define ST_HOOK_FIRST_SPRITE 3u
|
#define ST_HOOK_FIRST_SPRITE 3u
|
||||||
|
|
||||||
|
// ---- level F "TAXI PONG" ----
|
||||||
|
#define ST_HK_F_SPRITE 3u // the ball
|
||||||
|
#define ST_HK_F_CEL 0x80u
|
||||||
|
#define ST_HK_F_COLOR 1u
|
||||||
|
|
||||||
// ---- level G "TELEPORTS" ----
|
// ---- level G "TELEPORTS" ----
|
||||||
#define ST_HK_G_CEL 0x7DD4u // 6 cels the orbs cycle through
|
#define ST_HK_G_CEL 0x7DD4u // 6 cels the orbs cycle through
|
||||||
#define ST_HK_G_CEL_PHASES 6u
|
#define ST_HK_G_CEL_PHASES 6u
|
||||||
|
|
@ -82,4 +93,19 @@
|
||||||
#define ST_HK_U_CEL_PHASES 4u
|
#define ST_HK_U_CEL_PHASES 4u
|
||||||
#define ST_HK_U_COLOR 7u
|
#define ST_HK_U_COLOR 7u
|
||||||
|
|
||||||
|
// ---- level Y "MYSTERY SCREEN" ----
|
||||||
|
// Sprite 3 is a multicolour saucer, sprite 4 a hires walker, sprite 5
|
||||||
|
// the walker's shot ($83 rising, then $84/$85 bursting).
|
||||||
|
#define ST_HK_Y_SAUCER 3u
|
||||||
|
#define ST_HK_Y_WALKER 4u
|
||||||
|
#define ST_HK_Y_SHOT 5u
|
||||||
|
#define ST_HK_Y_CEL_SAUCER 0x80u
|
||||||
|
#define ST_HK_Y_CEL_WALK 0x81u
|
||||||
|
#define ST_HK_Y_CEL_WALK_ALT 0x82u
|
||||||
|
#define ST_HK_Y_CEL_SHOT 0x83u
|
||||||
|
#define ST_HK_Y_CEL_SHOT_END 0x86u // one past the last burst cel
|
||||||
|
#define ST_HK_Y_COLOR_SAUCER 1u
|
||||||
|
#define ST_HK_Y_COLOR_WALKER 0x0Fu
|
||||||
|
#define ST_HK_Y_MULTICOLOR 0x0Fu // $7E02: $D01C, sprites 0..3
|
||||||
|
|
||||||
#endif // ST_HOOK_TABLES_H
|
#endif // ST_HOOK_TABLES_H
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Space Taxi -- level loader (JoeyLib data path + the shared STL4 parser).
|
// Space Taxi -- level loader (JoeyLib data path + the shared STL5 parser).
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,9 @@ uint8_t stLevelCelList(const StLevelT *level, StCelDefT *out, uint8_t max) {
|
||||||
uint8_t c;
|
uint8_t c;
|
||||||
|
|
||||||
switch (level->levelIndex) {
|
switch (level->levelIndex) {
|
||||||
|
case ST_LEVEL_F:
|
||||||
|
celAdd(out, &n, max, ST_HK_F_CEL, ST_HK_F_COLOR, 0u, 0u, 0u);
|
||||||
|
break;
|
||||||
case ST_LEVEL_G:
|
case ST_LEVEL_G:
|
||||||
for (k = 0u; k < ST_HK_G_CEL_PHASES; k++) {
|
for (k = 0u; k < ST_HK_G_CEL_PHASES; k++) {
|
||||||
celAdd(out, &n, max, HKL(level, ST_HK_G_CEL + k), ST_HK_G_ORB_COLOR, 0u, 0u, 0u);
|
celAdd(out, &n, max, HKL(level, ST_HK_G_CEL + k), ST_HK_G_ORB_COLOR, 0u, 0u, 0u);
|
||||||
|
|
@ -58,14 +61,15 @@ uint8_t stLevelCelList(const StLevelT *level, StCelDefT *out, uint8_t max) {
|
||||||
break;
|
break;
|
||||||
case ST_LEVEL_I:
|
case ST_LEVEL_I:
|
||||||
// Every cel in the idle colour and in each of the three the
|
// Every cel in the idle colour and in each of the three the
|
||||||
// launcher picks at random.
|
// launcher picks at random; multicolour, like every sprite
|
||||||
|
// on this level ($7DB1).
|
||||||
for (c = 0u; c <= ST_HK_I_COLORS; c++) {
|
for (c = 0u; c <= ST_HK_I_COLORS; c++) {
|
||||||
uint8_t color = (c == 0u) ? ST_HK_I_COLOR_IDLE : HKL(level, ST_HK_I_COLOR + c - 1u);
|
uint8_t color = (c == 0u) ? ST_HK_I_COLOR_IDLE : HKL(level, ST_HK_I_COLOR + c - 1u);
|
||||||
for (k = 0u; k < ST_HK_I_RISE_PHASES; k++) {
|
for (k = 0u; k < ST_HK_I_RISE_PHASES; k++) {
|
||||||
celAdd(out, &n, max, HKL(level, ST_HK_I_CEL_RISE + k), color, 0u, 0u, 0u);
|
celAdd(out, &n, max, HKL(level, ST_HK_I_CEL_RISE + k), color, 1u, level->header[ST_HDR_SPRITE_MC0], level->header[ST_HDR_SPRITE_MC1]);
|
||||||
}
|
}
|
||||||
for (k = 0u; k < ST_HK_I_BURST_PHASES; k++) {
|
for (k = 0u; k < ST_HK_I_BURST_PHASES; k++) {
|
||||||
celAdd(out, &n, max, HKL(level, ST_HK_I_CEL_BURST + k), color, 0u, 0u, 0u);
|
celAdd(out, &n, max, HKL(level, ST_HK_I_CEL_BURST + k), color, 1u, level->header[ST_HDR_SPRITE_MC0], level->header[ST_HDR_SPRITE_MC1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -91,15 +95,10 @@ uint8_t stLevelCelList(const StLevelT *level, StCelDefT *out, uint8_t max) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ST_LEVEL_Q:
|
case ST_LEVEL_Q:
|
||||||
// The hook's cels show HIRES in colour 6 for the whole recorded
|
// Multicolour: the level's second prelude hook ($7D9B) sets
|
||||||
// ride (tools/stsim STSIM_CELS census, 2026-09-22); the
|
// $D01C to $FF after the cab spawn set it to $07.
|
||||||
// multicolour variant (the level's own $D025/$D026) stays
|
|
||||||
// listed for the phases the census did not reach.
|
|
||||||
for (k = 0u; k < ST_HK_Q_CEL_PHASES; k++) {
|
for (k = 0u; k < ST_HK_Q_CEL_PHASES; k++) {
|
||||||
celAdd(out, &n, max, HKL(level, ST_HK_Q_CEL + k), ST_HK_Q_COLOR, 0u, 0u, 0u);
|
celAdd(out, &n, max, HKL(level, ST_HK_Q_CEL + k), ST_HK_Q_COLOR, 1u, level->header[ST_HDR_SPRITE_MC0], level->header[ST_HDR_SPRITE_MC1]);
|
||||||
}
|
|
||||||
for (k = 0u; k < ST_HK_Q_CEL_PHASES; k++) {
|
|
||||||
celAdd(out, &n, max, HKL(level, ST_HK_Q_CEL + k), ST_HK_Q_COLOR, 1u, level->header[5], level->header[6]);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ST_LEVEL_U:
|
case ST_LEVEL_U:
|
||||||
|
|
@ -107,6 +106,14 @@ uint8_t stLevelCelList(const StLevelT *level, StCelDefT *out, uint8_t max) {
|
||||||
celAdd(out, &n, max, HKL(level, ST_HK_U_CEL + k), ST_HK_U_COLOR, 0u, 0u, 0u);
|
celAdd(out, &n, max, HKL(level, ST_HK_U_CEL + k), ST_HK_U_COLOR, 0u, 0u, 0u);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case ST_LEVEL_Y:
|
||||||
|
// The saucer is multicolour ($7E02 sets $D01C to $0F); the
|
||||||
|
// walker and its shot are hires.
|
||||||
|
celAdd(out, &n, max, ST_HK_Y_CEL_SAUCER, ST_HK_Y_COLOR_SAUCER, 1u, level->header[ST_HDR_SPRITE_MC0], level->header[ST_HDR_SPRITE_MC1]);
|
||||||
|
for (k = ST_HK_Y_CEL_WALK; k < ST_HK_Y_CEL_SHOT_END; k++) {
|
||||||
|
celAdd(out, &n, max, k, ST_HK_Y_COLOR_WALKER, 0u, 0u, 0u);
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
// Space Taxi -- STL4 level file parser (plain stdio, no JoeyLib), shared
|
// Space Taxi -- STL5 level file parser (plain stdio, no JoeyLib), shared
|
||||||
// by the game and the host-side simulation harness.
|
// by the game and the host-side simulation harness.
|
||||||
//
|
//
|
||||||
// Layout (all bytes, written by assets/romToLevel.py):
|
// Layout (all bytes, written by assets/romToLevel.py):
|
||||||
// "STL4"
|
// "STL5"
|
||||||
// name[22] $7D78 level name in screen codes
|
// name[22] $7D78 level name in screen codes
|
||||||
// header[9] $7D00 VIC colour block
|
// header[9] $7D00 VIC colour block
|
||||||
// padCount, specialPad, fuelRate
|
// padCount, specialPad, fuelRate
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
// screen[1000], color[1000]
|
// screen[1000], color[1000]
|
||||||
// levelIndex
|
// levelIndex
|
||||||
// spriteCount, then spriteCount x (ptr, 63 bytes)
|
// spriteCount, then spriteCount x (ptr, 63 bytes)
|
||||||
// hookData[616] $7D98..$7FFF
|
// hookData[819] $7D98..$80CA
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
@ -36,7 +36,7 @@ bool stLevelParse(StLevelT *out, FILE *fp) {
|
||||||
uint8_t k;
|
uint8_t k;
|
||||||
|
|
||||||
memset(out, 0, sizeof(*out));
|
memset(out, 0, sizeof(*out));
|
||||||
if (!readBlock(fp, hdr, 4u) || hdr[0] != 'S' || hdr[1] != 'T' || hdr[2] != 'L' || hdr[3] != '4') {
|
if (!readBlock(fp, hdr, 4u) || hdr[0] != 'S' || hdr[1] != 'T' || hdr[2] != 'L' || hdr[3] != '5') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!readBlock(fp, out->name, ST_LEVEL_NAME_CHARS)) {
|
if (!readBlock(fp, out->name, ST_LEVEL_NAME_CHARS)) {
|
||||||
|
|
|
||||||
|
|
@ -1222,7 +1222,13 @@ static bool prewarmReport(uint16_t expect) {
|
||||||
ST_LOG("spacetaxi: ! only %u of %u cels built", used, expect);
|
ST_LOG("spacetaxi: ! only %u of %u cels built", used, expect);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#if defined(JOEYLIB_SPRITE_INTERPRETED)
|
||||||
|
// A port with no sprite emitter (joey/platform.h) interprets every cel
|
||||||
|
// by design: none compiled is how it always runs.
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
return hot != 0u;
|
return hot != 0u;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
461
stSim.c
461
stSim.c
|
|
@ -15,8 +15,22 @@
|
||||||
#define ST_JINGLE_SONG_BONUS 4u // level-25 start and the bonus cab
|
#define ST_JINGLE_SONG_BONUS 4u // level-25 start and the bonus cab
|
||||||
#define ST_JINGLE_LEVEL_25 0x19u // $7215 compared against #$19
|
#define ST_JINGLE_LEVEL_25 0x19u // $7215 compared against #$19
|
||||||
|
|
||||||
|
// The descent lamp ($6419): two cell pairs beside the fuel gauge, the
|
||||||
|
// colours they light in, and the Y velocities that pick one.
|
||||||
|
#define ST_LAMP_CELL_SLOW ST_CELL(23, 10) // $DBA2
|
||||||
|
#define ST_LAMP_CELL_FAST ST_CELL(24, 10) // $DBCA
|
||||||
|
#define ST_LAMP_OFF 0x0Bu // dark grey
|
||||||
|
#define ST_LAMP_SLOW 0x03u // cyan
|
||||||
|
#define ST_LAMP_FALL 0x07u // yellow
|
||||||
|
#define ST_LAMP_FAST 0x02u // red
|
||||||
|
#define ST_LAMP_FALL_VEL 0x0080 // high byte 0, low bit 7
|
||||||
|
#define ST_LAMP_FAST_VEL 0x0100 // high byte positive
|
||||||
|
#define ST_LAMP_TICK_MASK 7u // $71BE counts mod 8...
|
||||||
|
#define ST_LAMP_LIT_TICKS 5u // ...and lights on 0..4
|
||||||
|
|
||||||
// Screen-code strings the engine writes (all ASCII-compatible glyphs).
|
// Screen-code strings the engine writes (all ASCII-compatible glyphs).
|
||||||
// $6C38 -- the 11-character blank that clears the message row.
|
// $6C38 -- the 12-character blank that clears the message row (every
|
||||||
|
// message is 12 characters: "PAD PLEASE", " UP PLEASE! " and the rest).
|
||||||
static const uint8_t kTextBlank[] = " ";
|
static const uint8_t kTextBlank[] = " ";
|
||||||
// $43B1 -- the fare meter's blank template: three leading blanks, then
|
// $43B1 -- the fare meter's blank template: three leading blanks, then
|
||||||
// "0.00" in the inverse-video digit glyphs.
|
// "0.00" in the inverse-video digit glyphs.
|
||||||
|
|
@ -44,6 +58,8 @@ static void drawCabIcons(StSimT *sim);
|
||||||
static void drawScreensCount(StSimT *sim);
|
static void drawScreensCount(StSimT *sim);
|
||||||
static void edgeReflect(StSimT *sim);
|
static void edgeReflect(StSimT *sim);
|
||||||
static void fireButtonEdge(StSimT *sim);
|
static void fireButtonEdge(StSimT *sim);
|
||||||
|
static uint8_t frameBgCollisions(StSimT *sim);
|
||||||
|
static uint8_t frameSpriteCollisions(StSimT *sim);
|
||||||
static void fuelBarHud(StSimT *sim);
|
static void fuelBarHud(StSimT *sim);
|
||||||
static void fuelTick(StSimT *sim);
|
static void fuelTick(StSimT *sim);
|
||||||
static void hudDraw(StSimT *sim);
|
static void hudDraw(StSimT *sim);
|
||||||
|
|
@ -58,13 +74,16 @@ static uint8_t readInput(StSimT *sim);
|
||||||
static void rowWindow(const uint8_t *row, uint8_t s, uint8_t *w);
|
static void rowWindow(const uint8_t *row, uint8_t s, uint8_t *w);
|
||||||
static void setCell(StSimT *sim, uint16_t cell, uint8_t ch);
|
static void setCell(StSimT *sim, uint16_t cell, uint8_t ch);
|
||||||
static void setColorCell(StSimT *sim, uint16_t cell, uint8_t color);
|
static void setColorCell(StSimT *sim, uint16_t cell, uint8_t color);
|
||||||
|
static void sfxEnvelopeTick(StSimT *sim);
|
||||||
static void spriteMasks(StSimT *sim, uint8_t idx);
|
static void spriteMasks(StSimT *sim, uint8_t idx);
|
||||||
static bool spriteRowsTouchBackground(const StSimT *sim, uint8_t idx, int16_t firstCol, int16_t py, uint8_t phase);
|
static bool spriteRowsTouchBackground(const StSimT *sim, uint8_t idx, int16_t firstCol, int16_t py, uint8_t phase);
|
||||||
static bool spriteTouchesBackground(StSimT *sim, uint8_t idx);
|
static bool spriteTouchesBackground(StSimT *sim, uint8_t idx);
|
||||||
static void spritesOffReset(StSimT *sim);
|
static void spritesOffReset(StSimT *sim);
|
||||||
|
static bool spritesTouch(StSimT *sim, uint8_t a, uint8_t b, int16_t dx, int16_t dy);
|
||||||
static void taxiSpawnInit(StSimT *sim);
|
static void taxiSpawnInit(StSimT *sim);
|
||||||
static void taxiSpriteCelSelect(StSimT *sim);
|
static void taxiSpriteCelSelect(StSimT *sim);
|
||||||
static uint8_t validateDigit(uint8_t ch);
|
static uint8_t validateDigit(uint8_t ch);
|
||||||
|
static void voicesOff(StSimT *sim);
|
||||||
|
|
||||||
|
|
||||||
// $6112 -- add the X velocity to the 17-bit X position (msb:col:frac)
|
// $6112 -- add the X velocity to the 17-bit X position (msb:col:frac)
|
||||||
|
|
@ -250,7 +269,7 @@ static void collisionPhase1(StSimT *sim) {
|
||||||
sim->collisionPhase++;
|
sim->collisionPhase++;
|
||||||
sim->phaseReload = 2u;
|
sim->phaseReload = 2u;
|
||||||
sim->phaseTimer = 2u;
|
sim->phaseTimer = 2u;
|
||||||
stAudioSfx(stC64SfxProgram(ST_SFX_IMPACT));
|
stSimSfx(sim, stC64SfxProgram(ST_SFX_IMPACT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -274,8 +293,6 @@ static void collisionPhase2(StSimT *sim) {
|
||||||
|
|
||||||
// $6B4C -- 70-tick pause, then the life bookkeeping.
|
// $6B4C -- 70-tick pause, then the life bookkeeping.
|
||||||
static StTickResultE collisionPhase3(StSimT *sim) {
|
static StTickResultE collisionPhase3(StSimT *sim) {
|
||||||
uint8_t p;
|
|
||||||
|
|
||||||
sim->phaseTimer--;
|
sim->phaseTimer--;
|
||||||
if (sim->phaseTimer != 0u) {
|
if (sim->phaseTimer != 0u) {
|
||||||
return ST_TICK_CONTINUE;
|
return ST_TICK_CONTINUE;
|
||||||
|
|
@ -283,142 +300,18 @@ static StTickResultE collisionPhase3(StSimT *sim) {
|
||||||
if (sim->demoMode != 0u) {
|
if (sim->demoMode != 0u) {
|
||||||
return ST_TICK_CRASH_DONE;
|
return ST_TICK_CRASH_DONE;
|
||||||
}
|
}
|
||||||
if (sim->stage == ST_STAGE_RIDING) {
|
return stSimLoseCab(sim);
|
||||||
stSimHudInit(sim);
|
|
||||||
if (sim->fareSlotCount != 0u) {
|
|
||||||
sim->spriteSlots[sim->activeDyingSlot] = 0u;
|
|
||||||
sim->fareSlotCount--;
|
|
||||||
if (sim->activeSpriteIdx == ST_FARE_DEST_UP) {
|
|
||||||
uint8_t k;
|
|
||||||
for (k = 0u; k < 4u; k++) {
|
|
||||||
setCell(sim, (uint16_t)(ST_CELL_TRANSPORTER + k), ST_CHAR_TRANSPORTER);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sim->spriteSlots[sim->activeSpriteIdx] = 0u;
|
|
||||||
sim->fareSlotCount--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sim->activeSpriteIdx = 0u;
|
|
||||||
sim->stage = ST_STAGE_IDLE;
|
|
||||||
stSimClearMessage(sim);
|
|
||||||
}
|
|
||||||
// $6BBA
|
|
||||||
p = sim->player;
|
|
||||||
sim->cabs[p]--;
|
|
||||||
if (sim->cabs[p] == 0u) {
|
|
||||||
return ST_TICK_PLAYER_OUT;
|
|
||||||
}
|
|
||||||
setCell(sim, (uint16_t)(ST_CELL_CAB_ICONS + sim->cabs[p]), ST_CHAR_BLANK);
|
|
||||||
return ST_TICK_LIFE_LOST;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// $6966 -- what its read of $D01E/$D01F sees: this tick's frame OR the
|
||||||
// The C64 reads two VIC collision registers each frame: $D01F (a sprite
|
// frame drawn before the flush (see prevFrameSpriteColl in StSimT).
|
||||||
// touched the background) and $D01E (two sprites touched). Only the cab
|
|
||||||
// (sprite 0) can drive a gameplay outcome from them: the crash dispatch
|
|
||||||
// returns unless the cab or the background is touched, and the hooks
|
|
||||||
// read a bit set by cab contact -- except levels J and P, which watch
|
|
||||||
// their own hazards hit the scenery and opt those sprites in through
|
|
||||||
// bgCollSprites. So the full all-pairs register is not reproduced --
|
|
||||||
// only sprite 0 versus the background and versus the other sprites,
|
|
||||||
// plus the opted-in sprites versus the background, and the exhaust
|
|
||||||
// flame (sprite 2, the cab's own, always overlapping it) is skipped.
|
|
||||||
// Non-cab pairs (hazard vs hazard, passenger vs hazard with the cab
|
|
||||||
// clear) never change the dispatch outcome, so their bits are left
|
|
||||||
// unset. Proven against all four demo traces: the observable state
|
|
||||||
// (positions, crash phase, score) is byte-identical.
|
|
||||||
static void computeCollisions(StSimT *sim) {
|
static void computeCollisions(StSimT *sim) {
|
||||||
uint8_t ss = 0u;
|
uint8_t ss = frameSpriteCollisions(sim);
|
||||||
uint8_t bg = 0u;
|
|
||||||
uint8_t b;
|
|
||||||
uint8_t r;
|
|
||||||
|
|
||||||
for (b = 1u; b < ST_HW_SPRITES; b++) {
|
sim->spriteSpriteColl = (uint8_t)(ss | sim->prevFrameSpriteColl);
|
||||||
if ((sim->bgCollSprites & kStBit[b]) == 0u || (sim->frame.enableMask & kStBit[b]) == 0u) {
|
sim->spriteBgColl = (uint8_t)(frameBgCollisions(sim) | sim->prevFrameBgColl);
|
||||||
continue;
|
sim->prevFrameSpriteColl = ss;
|
||||||
}
|
|
||||||
spriteMasks(sim, b);
|
|
||||||
if (spriteTouchesBackground(sim, b)) {
|
|
||||||
bg |= kStBit[b];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((sim->frame.enableMask & 1u) == 0u) {
|
|
||||||
sim->spriteSpriteColl = 0u;
|
|
||||||
sim->spriteBgColl = bg;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
spriteMasks(sim, 0u);
|
|
||||||
if (spriteTouchesBackground(sim, 0u)) {
|
|
||||||
bg = 1u;
|
|
||||||
}
|
|
||||||
for (b = 1u; b < ST_HW_SPRITES; b++) {
|
|
||||||
int16_t dx;
|
|
||||||
int16_t dy;
|
|
||||||
uint8_t s;
|
|
||||||
int16_t q;
|
|
||||||
uint8_t kLo;
|
|
||||||
uint8_t kHi;
|
|
||||||
uint8_t rLo;
|
|
||||||
uint8_t rHi;
|
|
||||||
int16_t lo;
|
|
||||||
int16_t hi;
|
|
||||||
const uint8_t *win;
|
|
||||||
const uint8_t *bRow;
|
|
||||||
// Skip the exhaust flame (sprite 2) and any disabled sprite.
|
|
||||||
if (b == 2u || (sim->frame.enableMask & kStBit[b]) == 0u) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
dx = (int16_t)((int16_t)sim->frame.x[b] - (int16_t)sim->frame.x[0]);
|
|
||||||
dy = (int16_t)((int16_t)sim->frame.y[b] - (int16_t)sim->frame.y[0]);
|
|
||||||
if (dx <= -ST_SPRITE_W || dx >= ST_SPRITE_W || dy <= -ST_SPRITE_H || dy >= ST_SPRITE_H) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
spriteMasks(sim, b);
|
|
||||||
// Sprite b sits dx pixels right of the cab; with dx = 8q - s
|
|
||||||
// (s in 0..7) its row byte k lines up with byte k + q of the
|
|
||||||
// cab's phase-s windows, and only 0 <= k + q <= 3 can overlap.
|
|
||||||
s = (uint8_t)((0 - dx) & (ST_ROW_PHASES - 1));
|
|
||||||
q = (int16_t)(((dx + (int16_t)s + 32) >> 3) - 4);
|
|
||||||
kLo = (q < 0) ? (uint8_t)(0 - q) : 0u;
|
|
||||||
kHi = (q > 1) ? (uint8_t)(3 - q) : 2u;
|
|
||||||
// Rows r of the cab meet rows r - dy of sprite b; walk only the
|
|
||||||
// rows where both have pixels, both pointers advancing in step.
|
|
||||||
if (sim->collRowFirst[b] == ST_SPRITE_H || sim->collRowFirst[0] == ST_SPRITE_H) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
lo = (int16_t)sim->collRowFirst[0];
|
|
||||||
hi = (int16_t)(sim->collRowLast[0] + 1);
|
|
||||||
if ((int16_t)sim->collRowFirst[b] + dy > lo) {
|
|
||||||
lo = (int16_t)((int16_t)sim->collRowFirst[b] + dy);
|
|
||||||
}
|
|
||||||
if ((int16_t)sim->collRowLast[b] + 1 + dy < hi) {
|
|
||||||
hi = (int16_t)((int16_t)sim->collRowLast[b] + 1 + dy);
|
|
||||||
}
|
|
||||||
if (lo >= hi) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
rLo = (uint8_t)lo;
|
|
||||||
rHi = (uint8_t)hi;
|
|
||||||
win = cabWindows(sim, s) + (uint16_t)rLo * 4u;
|
|
||||||
bRow = sim->collMask[b][(uint8_t)((int16_t)rLo - dy)];
|
|
||||||
for (r = rLo; r < rHi; r++, win += 4, bRow += 3) {
|
|
||||||
bool hit = false;
|
|
||||||
uint8_t k;
|
|
||||||
for (k = kLo; k <= kHi; k++) {
|
|
||||||
if ((bRow[k] & win[(uint8_t)((int16_t)k + q)]) != 0u) {
|
|
||||||
hit = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hit) {
|
|
||||||
ss |= (uint8_t)(1u | kStBit[b]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sim->spriteSpriteColl = ss;
|
|
||||||
sim->spriteBgColl = bg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -446,12 +339,12 @@ static void crashStart(StSimT *sim) {
|
||||||
sim->phaseTimer = 2u;
|
sim->phaseTimer = 2u;
|
||||||
sim->phaseReload = 2u;
|
sim->phaseReload = 2u;
|
||||||
stAudioNoise(false);
|
stAudioNoise(false);
|
||||||
sim->eventDispatchType = 0u;
|
sim->eventDispatchType = ST_EVENT_NONE;
|
||||||
sim->activePad = 0u;
|
sim->activePad = 0u;
|
||||||
sim->spr[2].enable = 0u;
|
sim->spr[2].enable = 0u;
|
||||||
sim->spr[2].row = 0u;
|
sim->spr[2].row = 0u;
|
||||||
sim->velY = 0x0303;
|
sim->velY = 0x0303;
|
||||||
stAudioSfx(stC64SfxProgram(ST_SFX_CRASH));
|
stSimSfx(sim, stC64SfxProgram(ST_SFX_CRASH));
|
||||||
sim->thrustSweep = 0xA0u;
|
sim->thrustSweep = 0xA0u;
|
||||||
if (sim->stage == ST_STAGE_WALK_TO_CAB) {
|
if (sim->stage == ST_STAGE_WALK_TO_CAB) {
|
||||||
sim->stage--;
|
sim->stage--;
|
||||||
|
|
@ -529,7 +422,7 @@ static void fireButtonEdge(StSimT *sim) {
|
||||||
}
|
}
|
||||||
sim->fireWasHeld = 0x10u;
|
sim->fireWasHeld = 0x10u;
|
||||||
sim->spr[0].ptr ^= 1u;
|
sim->spr[0].ptr ^= 1u;
|
||||||
stAudioSfx(stC64SfxProgram(ST_SFX_GEAR));
|
stSimSfx(sim, stC64SfxProgram(ST_SFX_GEAR));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -558,29 +451,91 @@ void stSimFlameUpdate(StSimT *sim) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// $6419 -- the climb/descend indicator: colour RAM cells beside the
|
// The displayed frame's $D01F bits over the current picture -- only the
|
||||||
// fuel gauge flash red (rising), cyan (level) or yellow (falling).
|
// bits anyone reads: the cab's, and those of the hazards levels J and P
|
||||||
static void fuelBarHud(StSimT *sim) {
|
// opt in through bgCollSprites.
|
||||||
uint16_t cell;
|
static uint8_t frameBgCollisions(StSimT *sim) {
|
||||||
uint8_t color;
|
uint8_t bg = 0u;
|
||||||
|
uint8_t b;
|
||||||
|
|
||||||
setColorCell(sim, ST_CELL(23, 10), 0x0Bu);
|
for (b = 0u; b < ST_HW_SPRITES; b++) {
|
||||||
setColorCell(sim, ST_CELL(23, 11), 0x0Bu);
|
if ((sim->frame.enableMask & kStBit[b]) == 0u) {
|
||||||
setColorCell(sim, ST_CELL(24, 10), 0x0Bu);
|
continue;
|
||||||
setColorCell(sim, ST_CELL(24, 11), 0x0Bu);
|
}
|
||||||
sim->fuelBarTick = (uint8_t)((sim->fuelBarTick + 1u) & 7u);
|
if (b != 0u && (sim->bgCollSprites & kStBit[b]) == 0u) {
|
||||||
if (sim->fuelBarTick >= 5u) {
|
continue;
|
||||||
|
}
|
||||||
|
spriteMasks(sim, b);
|
||||||
|
if (spriteTouchesBackground(sim, b)) {
|
||||||
|
bg |= kStBit[b];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The displayed frame's $D01E: both bits of every pair of shown sprites
|
||||||
|
// whose pixels meet -- any pair, because the hooks test whole groups of
|
||||||
|
// bits (G's and the dispatch's AND #$F8 also catch a hook sprite touching
|
||||||
|
// the exhaust flame or another hook sprite, H's and U's walk every bit,
|
||||||
|
// and the pad light's AND #$02 catches the passenger touching anything).
|
||||||
|
static uint8_t frameSpriteCollisions(StSimT *sim) {
|
||||||
|
uint8_t shown = sim->frame.enableMask;
|
||||||
|
uint8_t ss = 0u;
|
||||||
|
uint8_t a;
|
||||||
|
uint8_t b;
|
||||||
|
|
||||||
|
for (a = 0u; a < (uint8_t)(ST_HW_SPRITES - 1u); a++) {
|
||||||
|
if ((shown & kStBit[a]) == 0u) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (b = (uint8_t)(a + 1u); b < ST_HW_SPRITES; b++) {
|
||||||
|
uint8_t pair = (uint8_t)(kStBit[a] | kStBit[b]);
|
||||||
|
int16_t dx;
|
||||||
|
int16_t dy;
|
||||||
|
// A pair already latched on both sides cannot add a bit, and
|
||||||
|
// most pairs are too far apart to test at all.
|
||||||
|
if ((shown & kStBit[b]) == 0u || (ss & pair) == pair) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
dx = (int16_t)((int16_t)sim->frame.x[b] - (int16_t)sim->frame.x[a]);
|
||||||
|
dy = (int16_t)((int16_t)sim->frame.y[b] - (int16_t)sim->frame.y[a]);
|
||||||
|
if (dx <= -ST_SPRITE_W || dx >= ST_SPRITE_W || dy <= -ST_SPRITE_H || dy >= ST_SPRITE_H) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (spritesTouch(sim, a, b, dx, dy)) {
|
||||||
|
ss |= pair;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ss;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// $6419 -- the descent lamp beside the fuel gauge. Every tick its two
|
||||||
|
// cell pairs ($DBA2 on row 23, $DBCA on row 24) go dark grey; then, on
|
||||||
|
// five ticks of every eight, one pair lights by the Y velocity
|
||||||
|
// ($714F/$714E, positive = falling): red on row 24 when the high byte is
|
||||||
|
// positive ($643D), yellow on row 23 when it is zero and the low byte has
|
||||||
|
// bit 7 set ($6450), and cyan on row 23 otherwise ($6449) -- any climb,
|
||||||
|
// a hover and a slow fall alike.
|
||||||
|
static void fuelBarHud(StSimT *sim) {
|
||||||
|
uint16_t cell = ST_LAMP_CELL_SLOW;
|
||||||
|
uint8_t color = ST_LAMP_SLOW;
|
||||||
|
|
||||||
|
setColorCell(sim, ST_LAMP_CELL_SLOW, ST_LAMP_OFF);
|
||||||
|
setColorCell(sim, (uint16_t)(ST_LAMP_CELL_SLOW + 1u), ST_LAMP_OFF);
|
||||||
|
setColorCell(sim, ST_LAMP_CELL_FAST, ST_LAMP_OFF);
|
||||||
|
setColorCell(sim, (uint16_t)(ST_LAMP_CELL_FAST + 1u), ST_LAMP_OFF);
|
||||||
|
sim->fuelBarTick = (uint8_t)((sim->fuelBarTick + 1u) & ST_LAMP_TICK_MASK);
|
||||||
|
if (sim->fuelBarTick >= ST_LAMP_LIT_TICKS) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (sim->velY < 0) {
|
if (sim->velY >= ST_LAMP_FAST_VEL) {
|
||||||
color = 0x02u;
|
color = ST_LAMP_FAST;
|
||||||
cell = ST_CELL(24, 10);
|
cell = ST_LAMP_CELL_FAST;
|
||||||
} else if (sim->velY == 0) {
|
} else if (sim->velY >= ST_LAMP_FALL_VEL) {
|
||||||
color = 0x03u;
|
color = ST_LAMP_FALL;
|
||||||
cell = ST_CELL(23, 10);
|
|
||||||
} else {
|
|
||||||
color = 0x07u;
|
|
||||||
cell = ST_CELL(23, 10);
|
|
||||||
}
|
}
|
||||||
setColorCell(sim, cell, color);
|
setColorCell(sim, cell, color);
|
||||||
setColorCell(sim, (uint16_t)(cell + 1u), color);
|
setColorCell(sim, (uint16_t)(cell + 1u), color);
|
||||||
|
|
@ -603,7 +558,7 @@ static void fuelTick(StSimT *sim) {
|
||||||
}
|
}
|
||||||
sim->padAnimTick = (uint8_t)((sim->padAnimTick + 1u) & 7u);
|
sim->padAnimTick = (uint8_t)((sim->padAnimTick + 1u) & 7u);
|
||||||
if (sim->padAnimTick == 0u) {
|
if (sim->padAnimTick == 0u) {
|
||||||
stAudioSfx(stC64SfxProgram(ST_SFX_CASH));
|
stSimSfx(sim, stC64SfxProgram(ST_SFX_CASH));
|
||||||
} else if (sim->padAnimTick < 5u) {
|
} else if (sim->padAnimTick < 5u) {
|
||||||
stAudioVoice1Freq(kPumpFreq[sim->padAnimTick]);
|
stAudioVoice1Freq(kPumpFreq[sim->padAnimTick]);
|
||||||
}
|
}
|
||||||
|
|
@ -619,7 +574,7 @@ static void fuelTick(StSimT *sim) {
|
||||||
}
|
}
|
||||||
setCell(sim, (uint16_t)(ST_CELL_FUEL + x), ST_CHAR_BLANK);
|
setCell(sim, (uint16_t)(ST_CELL_FUEL + x), ST_CHAR_BLANK);
|
||||||
if (sim->screen[ST_CELL_FUEL_LAST] == ST_CHAR_BLANK) {
|
if (sim->screen[ST_CELL_FUEL_LAST] == ST_CHAR_BLANK) {
|
||||||
stAudioSfx(stC64SfxProgram(ST_SFX_FUEL_FULL));
|
stSimSfx(sim, stC64SfxProgram(ST_SFX_FUEL_FULL));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -632,7 +587,7 @@ static void fuelTick(StSimT *sim) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (sim->fuelCells < 3u) {
|
if (sim->fuelCells < 3u) {
|
||||||
stAudioSfx(stC64SfxProgram(ST_SFX_FUEL_LOW));
|
stSimSfx(sim, stC64SfxProgram(ST_SFX_FUEL_LOW));
|
||||||
}
|
}
|
||||||
sim->fuelCountdown = L->fuelRate;
|
sim->fuelCountdown = L->fuelRate;
|
||||||
x = sim->fuelCells;
|
x = sim->fuelCells;
|
||||||
|
|
@ -716,12 +671,12 @@ static void padDetect(StSimT *sim) {
|
||||||
}
|
}
|
||||||
sim->activePad = (uint8_t)(i + 1);
|
sim->activePad = (uint8_t)(i + 1);
|
||||||
sim->activePadMirror = sim->activePad;
|
sim->activePadMirror = sim->activePad;
|
||||||
sim->eventDispatchType = 0u;
|
sim->eventDispatchType = ST_EVENT_NONE;
|
||||||
if (sim->activePad != sim->activeSpriteIdx) {
|
if (sim->activePad != sim->activeSpriteIdx) {
|
||||||
if (sim->stage == ST_STAGE_RIDING) {
|
if (sim->stage == ST_STAGE_RIDING) {
|
||||||
sim->eventDispatchType = (sim->activeSpriteIdx == ST_FARE_DEST_UP) ? 3u : 2u;
|
sim->eventDispatchType = (sim->activeSpriteIdx == ST_FARE_DEST_UP) ? ST_EVENT_WRONG_PAD_UP : ST_EVENT_WRONG_PAD;
|
||||||
} else if (sim->stage == ST_STAGE_WAIT) {
|
} else if (sim->stage == ST_STAGE_WAIT) {
|
||||||
sim->eventDispatchType = 1u;
|
sim->eventDispatchType = ST_EVENT_HAIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sim->dirMask = 0u;
|
sim->dirMask = 0u;
|
||||||
|
|
@ -729,11 +684,11 @@ static void padDetect(StSimT *sim) {
|
||||||
stSimPadLight(sim, true);
|
stSimPadLight(sim, true);
|
||||||
sim->bobTimer = 2u;
|
sim->bobTimer = 2u;
|
||||||
if (((uint8_t)sim->velY & 0x80u) == 0u) {
|
if (((uint8_t)sim->velY & 0x80u) == 0u) {
|
||||||
stAudioSfx(stC64SfxProgram(ST_SFX_LAND_SOFT));
|
stSimSfx(sim, stC64SfxProgram(ST_SFX_LAND_SOFT));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sim->bobTimer = 8u;
|
sim->bobTimer = 8u;
|
||||||
stAudioSfx(stC64SfxProgram(ST_SFX_LAND_HARD));
|
stSimSfx(sim, stC64SfxProgram(ST_SFX_LAND_HARD));
|
||||||
if (sim->stage == ST_STAGE_RIDING) {
|
if (sim->stage == ST_STAGE_RIDING) {
|
||||||
stSimHudInit(sim);
|
stSimHudInit(sim);
|
||||||
}
|
}
|
||||||
|
|
@ -959,6 +914,20 @@ static void setColorCell(StSimT *sim, uint16_t cell, uint8_t color) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// $4320 -- the release countdown of voices 1 and 2. Gating a voice off
|
||||||
|
// when its count runs out is the audio layer's business; the counts are
|
||||||
|
// what the game reads.
|
||||||
|
static void sfxEnvelopeTick(StSimT *sim) {
|
||||||
|
uint8_t v;
|
||||||
|
|
||||||
|
for (v = 0u; v < ST_SID_TIMED_VOICES; v++) {
|
||||||
|
if (sim->sidRelTimer[v] != 0u) {
|
||||||
|
sim->sidRelTimer[v]--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 21 rows of 3-byte foreground masks for a displayed sprite, kept in
|
// 21 rows of 3-byte foreground masks for a displayed sprite, kept in
|
||||||
// collMask[idx] and rebuilt only when its bitmap or multicolour mode
|
// collMask[idx] and rebuilt only when its bitmap or multicolour mode
|
||||||
// changes. A multicolour sprite's 2-bit pairs cover two pixels each: a
|
// changes. A multicolour sprite's 2-bit pairs cover two pixels each: a
|
||||||
|
|
@ -1068,13 +1037,9 @@ static bool spriteTouchesBackground(StSimT *sim, uint8_t idx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// $6946 -- all sprites off, latches cleared, nobody on a pad.
|
// $6946 -- the VIC's sprites off ($D015; the shadow enables are $4248's
|
||||||
|
// job), the collision latches cleared, nobody on a pad.
|
||||||
static void spritesOffReset(StSimT *sim) {
|
static void spritesOffReset(StSimT *sim) {
|
||||||
uint8_t i;
|
|
||||||
|
|
||||||
for (i = 0u; i < ST_HW_SPRITES; i++) {
|
|
||||||
sim->spr[i].enable = 0u;
|
|
||||||
}
|
|
||||||
sim->spriteSpriteColl = 0u;
|
sim->spriteSpriteColl = 0u;
|
||||||
sim->spriteBgColl = 0u;
|
sim->spriteBgColl = 0u;
|
||||||
sim->activePad = 0u;
|
sim->activePad = 0u;
|
||||||
|
|
@ -1082,6 +1047,71 @@ static void spritesOffReset(StSimT *sim) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Whether shown sprites a < b, b sitting dx right of a and dy below it
|
||||||
|
// (both inside a sprite's size), have a pixel in common. With dx = 8q - s
|
||||||
|
// (s in 0..7), b's row byte k lines up with byte k + q of a's row shifted
|
||||||
|
// to phase s, and only 0 <= k + q <= 3 can overlap; rows r of a meet rows
|
||||||
|
// r - dy of b, and only the rows where both have pixels are walked. The
|
||||||
|
// cab's shifted rows are cached (cabWindows) -- it is a side of most
|
||||||
|
// tests -- any other sprite's are shifted as they are needed.
|
||||||
|
static bool spritesTouch(StSimT *sim, uint8_t a, uint8_t b, int16_t dx, int16_t dy) {
|
||||||
|
uint8_t s;
|
||||||
|
int16_t q;
|
||||||
|
uint8_t kLo;
|
||||||
|
uint8_t kHi;
|
||||||
|
uint8_t r;
|
||||||
|
uint8_t rHi;
|
||||||
|
int16_t lo;
|
||||||
|
int16_t hi;
|
||||||
|
const uint8_t *win = 0;
|
||||||
|
const uint8_t *bRow;
|
||||||
|
uint8_t own[4];
|
||||||
|
|
||||||
|
spriteMasks(sim, a);
|
||||||
|
spriteMasks(sim, b);
|
||||||
|
if (sim->collRowFirst[a] == ST_SPRITE_H || sim->collRowFirst[b] == ST_SPRITE_H) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
s = (uint8_t)((0 - dx) & (ST_ROW_PHASES - 1));
|
||||||
|
q = (int16_t)(((dx + (int16_t)s + 32) >> 3) - 4);
|
||||||
|
kLo = (q < 0) ? (uint8_t)(0 - q) : 0u;
|
||||||
|
kHi = (q > 1) ? (uint8_t)(3 - q) : 2u;
|
||||||
|
lo = (int16_t)sim->collRowFirst[a];
|
||||||
|
hi = (int16_t)(sim->collRowLast[a] + 1);
|
||||||
|
if ((int16_t)sim->collRowFirst[b] + dy > lo) {
|
||||||
|
lo = (int16_t)((int16_t)sim->collRowFirst[b] + dy);
|
||||||
|
}
|
||||||
|
if ((int16_t)sim->collRowLast[b] + 1 + dy < hi) {
|
||||||
|
hi = (int16_t)((int16_t)sim->collRowLast[b] + 1 + dy);
|
||||||
|
}
|
||||||
|
if (lo >= hi) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
r = (uint8_t)lo;
|
||||||
|
rHi = (uint8_t)hi;
|
||||||
|
bRow = sim->collMask[b][(uint8_t)((int16_t)r - dy)];
|
||||||
|
if (a == 0u) {
|
||||||
|
win = cabWindows(sim, s) + (uint16_t)r * 4u;
|
||||||
|
}
|
||||||
|
for (; r < rHi; r++, bRow += 3) {
|
||||||
|
const uint8_t *w = win;
|
||||||
|
uint8_t k;
|
||||||
|
if (a == 0u) {
|
||||||
|
win += 4;
|
||||||
|
} else {
|
||||||
|
rowWindow(sim->collMask[a][r], s, own);
|
||||||
|
w = own;
|
||||||
|
}
|
||||||
|
for (k = kLo; k <= kHi; k++) {
|
||||||
|
if ((bRow[k] & w[(uint8_t)((int16_t)k + q)]) != 0u) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// $6888 -- place the cab at the level's spawn point with a full tank.
|
// $6888 -- place the cab at the level's spawn point with a full tank.
|
||||||
static void taxiSpawnInit(StSimT *sim) {
|
static void taxiSpawnInit(StSimT *sim) {
|
||||||
const StLevelT *L = sim->level;
|
const StLevelT *L = sim->level;
|
||||||
|
|
@ -1149,6 +1179,13 @@ static uint8_t validateDigit(uint8_t ch) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// $6909 / $6C02 -- voices 1 and 2 gated off, their release timers cleared.
|
||||||
|
static void voicesOff(StSimT *sim) {
|
||||||
|
stAudioSilence();
|
||||||
|
memset(sim->sidRelTimer, 0, sizeof(sim->sidRelTimer));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Public API
|
// Public API
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
@ -1382,26 +1419,15 @@ void stSimEnterLevel(StSimT *sim, const StLevelT *level) {
|
||||||
}
|
}
|
||||||
sim->hookLevel = level->levelIndex;
|
sim->hookLevel = level->levelIndex;
|
||||||
stHookSceneLoad(sim);
|
stHookSceneLoad(sim);
|
||||||
|
spritesOffReset(sim);
|
||||||
|
stHookPrelude0(sim);
|
||||||
|
stFareTakeoffSetup(sim);
|
||||||
stSimRespawn(sim);
|
stSimRespawn(sim);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// $5F27 -- the respawn half of the prelude.
|
// $5F27 -- the respawn half of the prelude.
|
||||||
void stSimRespawn(StSimT *sim) {
|
void stSimRespawn(StSimT *sim) {
|
||||||
spritesOffReset(sim);
|
|
||||||
stHookPrelude0(sim);
|
|
||||||
// $6F18 takeoffSetup: from the third screen on the cab starts with
|
|
||||||
// a passenger aboard who wants pad 1.
|
|
||||||
sim->eventDispatchType = 0u;
|
|
||||||
sim->stage = ST_STAGE_IDLE;
|
|
||||||
if (sim->levelState >= 2u) {
|
|
||||||
static const uint8_t kPadPlease[] = "PAD 1 PLEASE";
|
|
||||||
sim->stage = ST_STAGE_RIDING;
|
|
||||||
sim->activeSpriteIdx = 1u;
|
|
||||||
sim->activeDyingSlot = 1u;
|
|
||||||
stSimDrawText(sim, ST_MESSAGE_COL, ST_MESSAGE_ROW, kPadPlease, 1u);
|
|
||||||
sim->eventDispatchType = 1u;
|
|
||||||
}
|
|
||||||
taxiSpawnInit(sim);
|
taxiSpawnInit(sim);
|
||||||
stHookPrelude1(sim);
|
stHookPrelude1(sim);
|
||||||
// $6906 framePresent: the start-of-screen jingle, then the SID (and
|
// $6906 framePresent: the start-of-screen jingle, then the SID (and
|
||||||
|
|
@ -1409,7 +1435,7 @@ void stSimRespawn(StSimT *sim) {
|
||||||
// screen rotates songs 0..3 on $5E9B. The host plays it with the sim
|
// screen rotates songs 0..3 on $5E9B. The host plays it with the sim
|
||||||
// frozen, which is what the C64's blocking play-and-wait amounts to.
|
// frozen, which is what the C64's blocking play-and-wait amounts to.
|
||||||
stSimMarshal(sim);
|
stSimMarshal(sim);
|
||||||
stAudioSilence();
|
voicesOff(sim);
|
||||||
sim->jingleRotate++;
|
sim->jingleRotate++;
|
||||||
if (sim->levelState == ST_JINGLE_LEVEL_25) {
|
if (sim->levelState == ST_JINGLE_LEVEL_25) {
|
||||||
sim->jingleRequest = (uint8_t)(ST_JINGLE_SONG_BONUS + 1u);
|
sim->jingleRequest = (uint8_t)(ST_JINGLE_SONG_BONUS + 1u);
|
||||||
|
|
@ -1418,8 +1444,12 @@ void stSimRespawn(StSimT *sim) {
|
||||||
}
|
}
|
||||||
sim->spriteSpriteColl = 0u;
|
sim->spriteSpriteColl = 0u;
|
||||||
sim->spriteBgColl = 0u;
|
sim->spriteBgColl = 0u;
|
||||||
|
// $6920 cleared the latch, but the VIC goes on showing this frame
|
||||||
|
// through the jingle, so the first tick's read still sees it.
|
||||||
|
sim->prevFrameSpriteColl = frameSpriteCollisions(sim);
|
||||||
sim->rngT1 = 0x06u;
|
sim->rngT1 = 0x06u;
|
||||||
sim->rngT2 = 0x17u;
|
sim->rngT2 = 0x17u;
|
||||||
|
stFareTakeoffVoice(sim);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1462,6 +1492,43 @@ void stSimNewGame(StSimT *sim, uint8_t playerCount, bool demo) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// $6B64..$6BCD -- the game's side of a finished wreck (the attract
|
||||||
|
// demo returns to the title instead): a fare aboard is lost -- its meter,
|
||||||
|
// its pads, an open hatch, its message -- then the player's cab count
|
||||||
|
// drops. With cabs left the host respawns the cab (stSimRespawn, $5F27).
|
||||||
|
StTickResultE stSimLoseCab(StSimT *sim) {
|
||||||
|
uint8_t p;
|
||||||
|
|
||||||
|
if (sim->stage == ST_STAGE_RIDING) {
|
||||||
|
stSimHudInit(sim);
|
||||||
|
if (sim->fareSlotCount != 0u) {
|
||||||
|
sim->spriteSlots[sim->activeDyingSlot] = 0u;
|
||||||
|
sim->fareSlotCount--;
|
||||||
|
if (sim->activeSpriteIdx == ST_FARE_DEST_UP) {
|
||||||
|
uint8_t k;
|
||||||
|
for (k = 0u; k < ST_TRANSPORTER_CELLS; k++) {
|
||||||
|
setCell(sim, (uint16_t)(ST_CELL_TRANSPORTER + k), ST_CHAR_TRANSPORTER);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sim->spriteSlots[sim->activeSpriteIdx] = 0u;
|
||||||
|
sim->fareSlotCount--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sim->activeSpriteIdx = 0u;
|
||||||
|
sim->stage = ST_STAGE_IDLE;
|
||||||
|
stSimClearMessage(sim);
|
||||||
|
}
|
||||||
|
// $6BBA
|
||||||
|
p = sim->player;
|
||||||
|
sim->cabs[p]--;
|
||||||
|
if (sim->cabs[p] == 0u) {
|
||||||
|
return ST_TICK_PLAYER_OUT;
|
||||||
|
}
|
||||||
|
setCell(sim, (uint16_t)(ST_CELL_CAB_ICONS + sim->cabs[p]), ST_CHAR_BLANK);
|
||||||
|
return ST_TICK_LIFE_LOST;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Everything must be repainted.
|
// Everything must be repainted.
|
||||||
// A glyph's bits changed: the renderer has to repaint every cell showing
|
// A glyph's bits changed: the renderer has to repaint every cell showing
|
||||||
// that character. The list is short (one or two per frame), so a linear
|
// that character. The list is short (one or two per frame), so a linear
|
||||||
|
|
@ -1625,6 +1692,17 @@ void stSimSetDemoStream(StSimT *sim, const uint8_t *stream, uint16_t len) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// $42E9 -- an SFX program starts; voices 0 and 1 arm their release timer.
|
||||||
|
void stSimSfx(StSimT *sim, const uint8_t *program9) {
|
||||||
|
uint8_t voice = program9[ST_SFX_VOICE_AT];
|
||||||
|
|
||||||
|
if (voice < ST_SID_TIMED_VOICES) {
|
||||||
|
sim->sidRelTimer[voice] = program9[ST_SFX_RELEASE_AT];
|
||||||
|
}
|
||||||
|
stAudioSfx(program9);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// $411B -- add a signed byte to a sprite's 17-bit X, carrying into the
|
// $411B -- add a signed byte to a sprite's 17-bit X, carrying into the
|
||||||
// msb shadow.
|
// msb shadow.
|
||||||
void stSimSpriteAddX(StSimT *sim, uint8_t idx, uint8_t delta) {
|
void stSimSpriteAddX(StSimT *sim, uint8_t idx, uint8_t delta) {
|
||||||
|
|
@ -1671,18 +1749,20 @@ StTickResultE stSimTick(StSimT *sim) {
|
||||||
taxiSpriteCelSelect(sim);
|
taxiSpriteCelSelect(sim);
|
||||||
if (levelEndCheck(sim)) {
|
if (levelEndCheck(sim)) {
|
||||||
stAudioNoise(false);
|
stAudioNoise(false);
|
||||||
stAudioSilence();
|
voicesOff(sim);
|
||||||
stSimArchiveHud(sim);
|
stSimArchiveHud(sim);
|
||||||
return ST_TICK_LEVEL_EXIT;
|
return ST_TICK_LEVEL_EXIT;
|
||||||
}
|
}
|
||||||
padLandingBob(sim);
|
padLandingBob(sim);
|
||||||
fuelTick(sim);
|
fuelTick(sim);
|
||||||
// $61BD passengerEventDraw only re-voices the pickup/drop-off
|
stFareEventVoice(sim);
|
||||||
// messages already drawn by the fare machine.
|
|
||||||
}
|
}
|
||||||
stFarePostTickGate(sim);
|
stFarePostTickGate(sim);
|
||||||
stFareStageDispatch(sim);
|
stFareStageDispatch(sim);
|
||||||
stHookPerTick2(sim);
|
stHookPerTick2(sim);
|
||||||
|
// $5F94: the frame drawn while the CPU waits shows the last flush's
|
||||||
|
// sprites over the picture as this tick has left it.
|
||||||
|
sim->prevFrameBgColl = frameBgCollisions(sim);
|
||||||
stSimMarshal(sim);
|
stSimMarshal(sim);
|
||||||
stSimRotateGlyph(sim, ST_CHAR_TRANSPORTER);
|
stSimRotateGlyph(sim, ST_CHAR_TRANSPORTER);
|
||||||
stHookPerTick3(sim);
|
stHookPerTick3(sim);
|
||||||
|
|
@ -1703,6 +1783,7 @@ StTickResultE stSimTick(StSimT *sim) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hudDraw(sim);
|
hudDraw(sim);
|
||||||
|
sfxEnvelopeTick(sim);
|
||||||
// $4FCB runStopWatcher: any joystick input ends the demo.
|
// $4FCB runStopWatcher: any joystick input ends the demo.
|
||||||
if (sim->demoMode != 0u && (sim->rawInput & 0x1Fu) != 0u) {
|
if (sim->demoMode != 0u && (sim->rawInput & 0x1Fu) != 0u) {
|
||||||
return ST_TICK_DEMO_INPUT;
|
return ST_TICK_DEMO_INPUT;
|
||||||
|
|
|
||||||
106
stSim.h
106
stSim.h
|
|
@ -52,13 +52,14 @@
|
||||||
#define ST_HW_SPRITES 8u
|
#define ST_HW_SPRITES 8u
|
||||||
|
|
||||||
// Fixed screen cells the game writes directly (C64 screen addresses).
|
// Fixed screen cells the game writes directly (C64 screen addresses).
|
||||||
#define ST_CELL_TRANSPORTER ST_CELL(0, 18) // $0412..$0415, 4 cells
|
#define ST_CELL_TRANSPORTER ST_CELL(0, 18) // $0412..$0415
|
||||||
|
#define ST_TRANSPORTER_CELLS 4u
|
||||||
#define ST_CELL_CAB_ICONS ST_CELL(23, 1) // $0799..$079F
|
#define ST_CELL_CAB_ICONS ST_CELL(23, 1) // $0799..$079F
|
||||||
#define ST_CELL_FUEL ST_CELL(23, 14) // $07A6..$07B1, 12 cells
|
#define ST_CELL_FUEL ST_CELL(23, 14) // $07A6..$07B1, 12 cells
|
||||||
#define ST_CELL_FUEL_LAST ST_CELL(23, 25) // $07B1
|
#define ST_CELL_FUEL_LAST ST_CELL(23, 25) // $07B1
|
||||||
#define ST_CELL_SCREENS ST_CELL(23, 32) // $07B8..$07BC
|
#define ST_CELL_SCREENS ST_CELL(23, 32) // $07B8..$07BC
|
||||||
#define ST_CELL_SCORE ST_CELL(24, 2) // $07C2..$07C8, 7 chars
|
#define ST_CELL_SCORE ST_CELL(24, 2) // $07C2..$07C8, 7 chars
|
||||||
#define ST_CELL_MESSAGE ST_CELL(24, 14) // $07CE, 11 chars
|
#define ST_CELL_MESSAGE ST_CELL(24, 14) // $07CE, 12 chars
|
||||||
#define ST_MESSAGE_COL 14u
|
#define ST_MESSAGE_COL 14u
|
||||||
#define ST_MESSAGE_ROW 24u
|
#define ST_MESSAGE_ROW 24u
|
||||||
#define ST_CELL_PAD_DIGIT ST_CELL(24, 18) // $07D2
|
#define ST_CELL_PAD_DIGIT ST_CELL(24, 18) // $07D2
|
||||||
|
|
@ -79,7 +80,7 @@
|
||||||
#define ST_CHAR_FUEL_EMPTY 0x7Au
|
#define ST_CHAR_FUEL_EMPTY 0x7Au
|
||||||
#define ST_CHAR_TRANSPORTER 0x67u // animated top-wall hatch
|
#define ST_CHAR_TRANSPORTER 0x67u // animated top-wall hatch
|
||||||
#define ST_CHAR_CAB_ICON 0xC8u
|
#define ST_CHAR_CAB_ICON 0xC8u
|
||||||
#define ST_CHAR_ELECTROID 0x6Fu // level O's scrolling barrier glyph
|
#define ST_CHAR_ELECTROID 0x7Cu // level O's barrier glyph ($7E6E rolls $2BE0)
|
||||||
#define ST_FARE_DEST_UP 0x0Bu // $7D8E: the fare aboard wants UP
|
#define ST_FARE_DEST_UP 0x0Bu // $7D8E: the fare aboard wants UP
|
||||||
|
|
||||||
// ---- level-intro starfield --------------------------------------------
|
// ---- level-intro starfield --------------------------------------------
|
||||||
|
|
@ -149,9 +150,16 @@ typedef char stStarRingCountIsPow2[((ST_STAR_RING_COUNT & (ST_STAR_RING_COUNT -
|
||||||
// Distinct characters whose glyph can change in one frame before the
|
// Distinct characters whose glyph can change in one frame before the
|
||||||
// renderer gives up and repaints everything.
|
// renderer gives up and repaints everything.
|
||||||
#define ST_CHAR_DIRTY_MAX 8u
|
#define ST_CHAR_DIRTY_MAX 8u
|
||||||
|
// A 9-byte SFX program ($42E9): SID registers 0..6, then the release
|
||||||
|
// timer and the voice. Only voices 0 and 1 have a timer ($7216/$7217;
|
||||||
|
// voice 2's lands on $7218, the voice 1 gate mask, which only the audio
|
||||||
|
// layer uses).
|
||||||
|
#define ST_SFX_RELEASE_AT 7u
|
||||||
|
#define ST_SFX_VOICE_AT 8u
|
||||||
|
#define ST_SID_TIMED_VOICES 2u
|
||||||
// Characters that may hold an edited glyph at once. The game edits six
|
// Characters that may hold an edited glyph at once. The game edits six
|
||||||
// across the whole game (the transporter hatch $67, level O's barrier
|
// across the whole game (the transporter hatch $67, level O's barrier
|
||||||
// $6F, the title logo $84 and level W's three laser glyphs $91..$93), so
|
// $7C, the title logo $84 and level W's three laser glyphs $91..$93), so
|
||||||
// this is generous; an overflow parks the edit in the spill entry, where
|
// this is generous; an overflow parks the edit in the spill entry, where
|
||||||
// it is simply never displayed rather than corrupting another character.
|
// it is simply never displayed rather than corrupting another character.
|
||||||
#define ST_GLYPH_OVERRIDES 16u
|
#define ST_GLYPH_OVERRIDES 16u
|
||||||
|
|
@ -160,14 +168,16 @@ typedef struct {
|
||||||
uint8_t rows[8];
|
uint8_t rows[8];
|
||||||
} StGlyphT;
|
} StGlyphT;
|
||||||
|
|
||||||
// Level image as loaded from a .dat (STL4). Kept in the C64's units.
|
// Level image as loaded from a .dat (STL5). Kept in the C64's units.
|
||||||
#define ST_MAX_PADS 10u
|
#define ST_MAX_PADS 10u
|
||||||
#define ST_MAX_LEVEL_SPRITES 16u
|
#define ST_MAX_LEVEL_SPRITES 16u
|
||||||
// Per-level hook code + tables live at $7D98..$7FFF in the level's data
|
// Per-level hook code + tables live from $7D98 in the level's data blob;
|
||||||
// blob; the port keeps the bytes (for the tables) and re-expresses the
|
// the port keeps the bytes (for the tables) and re-expresses the code in
|
||||||
// code in stHooks.c.
|
// stHooks.c. Most levels end by $7FFF, but level Y's program runs on past
|
||||||
|
// $8000, its last table being the SFX program at $80C2..$80CA.
|
||||||
#define ST_HOOK_BASE 0x7D98u
|
#define ST_HOOK_BASE 0x7D98u
|
||||||
#define ST_HOOK_BYTES (0x8000u - ST_HOOK_BASE)
|
#define ST_HOOK_END 0x80CBu
|
||||||
|
#define ST_HOOK_BYTES (ST_HOOK_END - ST_HOOK_BASE)
|
||||||
|
|
||||||
// Per-level hook scratch. The original keeps a hook's running state
|
// Per-level hook scratch. The original keeps a hook's running state
|
||||||
// inside the level's own data block, at fixed C64 addresses interleaved
|
// inside the level's own data block, at fixed C64 addresses interleaved
|
||||||
|
|
@ -190,6 +200,13 @@ typedef struct {
|
||||||
uint8_t tone; // $7E56 rising chime
|
uint8_t tone; // $7E56 rising chime
|
||||||
} StHookHT;
|
} StHookHT;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t vx; // $7DD1/$7DD2 the ball's X step
|
||||||
|
uint16_t x; // $7DD5/$7DD6 its 9-bit sprite X
|
||||||
|
uint16_t y; // $7DD7/$7DD8 its row, 8.8
|
||||||
|
uint16_t vy; // $7DD9/$7DDA 8.8
|
||||||
|
} StHookFT;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t celPhase; // $7DD2 orb cel cycle 0..5
|
uint8_t celPhase; // $7DD2 orb cel cycle 0..5
|
||||||
uint8_t tick; // $7E00
|
uint8_t tick; // $7E00
|
||||||
|
|
@ -215,12 +232,34 @@ typedef struct {
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t phase[ST_HW_SPRITES]; // $7E83 interference cel phase
|
uint8_t phase[ST_HW_SPRITES]; // $7E83 interference cel phase
|
||||||
|
uint8_t parity; // $7DFE the cels step every other tick
|
||||||
} StHookQT;
|
} StHookQT;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t gateShut; // $7D9C
|
uint8_t gateShut; // $7D9C
|
||||||
} StHookTT;
|
} StHookTT;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t tick; // $7DE2 shift every sixteenth
|
||||||
|
} StHookVT;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t humStep; // $7ECC the saucer's hum pitch index
|
||||||
|
uint8_t walkerDir; // $7EC2 0 left, 1 right
|
||||||
|
uint8_t walkerCount; // $7EC9
|
||||||
|
uint8_t walkerParity; // $7ECB
|
||||||
|
uint8_t lightTick; // $7F10
|
||||||
|
uint8_t lightCell; // $7F13 0..119 over rows 19..21
|
||||||
|
uint8_t lightSaveChar; // $7F11
|
||||||
|
uint8_t lightSaveColor; // $7F12
|
||||||
|
uint8_t blockTimer; // $7F66
|
||||||
|
uint8_t blockDir; // $7168 0 left, 1 right
|
||||||
|
uint8_t blockCol; // $7F69
|
||||||
|
uint8_t blockFrame; // $7F6A
|
||||||
|
uint8_t shotDelay; // $803B
|
||||||
|
uint8_t shotLive; // $808C
|
||||||
|
} StHookYT;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t colorPhase; // $7DAB
|
uint8_t colorPhase; // $7DAB
|
||||||
uint8_t state[ST_HOOK_W_LASERS]; // $7DAC 0 idle, 1 extending, 2 fading
|
uint8_t state[ST_HOOK_W_LASERS]; // $7DAC 0 idle, 1 extending, 2 fading
|
||||||
|
|
@ -262,6 +301,7 @@ typedef struct {
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
StHookET e;
|
StHookET e;
|
||||||
|
StHookFT f;
|
||||||
StHookGT g;
|
StHookGT g;
|
||||||
StHookHT h;
|
StHookHT h;
|
||||||
StHookIT i;
|
StHookIT i;
|
||||||
|
|
@ -272,8 +312,10 @@ typedef union {
|
||||||
StHookQT q;
|
StHookQT q;
|
||||||
StHookTT t;
|
StHookTT t;
|
||||||
StHookUT u;
|
StHookUT u;
|
||||||
|
StHookVT v;
|
||||||
StHookWT w;
|
StHookWT w;
|
||||||
StHookXT x;
|
StHookXT x;
|
||||||
|
StHookYT y;
|
||||||
} StHookScratchT;
|
} StHookScratchT;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -309,7 +351,7 @@ typedef struct {
|
||||||
uint8_t levelIndex; // 0..23 = A..X, 24 = Y, 25 = title
|
uint8_t levelIndex; // 0..23 = A..X, 24 = Y, 25 = title
|
||||||
uint8_t spriteCount;
|
uint8_t spriteCount;
|
||||||
StLevelSpriteT sprites[ST_MAX_LEVEL_SPRITES];
|
StLevelSpriteT sprites[ST_MAX_LEVEL_SPRITES];
|
||||||
uint8_t hookData[ST_HOOK_BYTES]; // $7D98..$7FFF as shipped
|
uint8_t hookData[ST_HOOK_BYTES]; // $7D98..$80CA as shipped
|
||||||
} StLevelT;
|
} StLevelT;
|
||||||
|
|
||||||
// One VIC sprite as the game sees it through its shadow tables. Padded
|
// One VIC sprite as the game sees it through its shadow tables. Padded
|
||||||
|
|
@ -358,6 +400,16 @@ typedef enum {
|
||||||
ST_STAGE_CLEANUP // $67C6
|
ST_STAGE_CLEANUP // $67C6
|
||||||
} StStageE;
|
} StStageE;
|
||||||
|
|
||||||
|
// What a landing has left to say ($71CD), voiced once the touchdown bob
|
||||||
|
// settles ($61BD) and cleared by every phrase the game speaks ($6C7F).
|
||||||
|
typedef enum {
|
||||||
|
ST_EVENT_NONE = 0,
|
||||||
|
ST_EVENT_HAIL, // $64F3: a fare waits on another pad; also
|
||||||
|
// $6F51: the takeoff fare's call ($5F30)
|
||||||
|
ST_EVENT_WRONG_PAD, // $64DF: a fare aboard, set down on the wrong pad
|
||||||
|
ST_EVENT_WRONG_PAD_UP // $64E7: ...and the fare wants UP
|
||||||
|
} StEventE;
|
||||||
|
|
||||||
|
|
||||||
// What a tick asked the host to do next (the original abandons the
|
// What a tick asked the host to do next (the original abandons the
|
||||||
// main loop with PLA/PLA on these).
|
// main loop with PLA/PLA on these).
|
||||||
|
|
@ -460,6 +512,10 @@ typedef struct {
|
||||||
uint8_t bobTimer; // $716F
|
uint8_t bobTimer; // $716F
|
||||||
uint8_t flameParity; // $716C
|
uint8_t flameParity; // $716C
|
||||||
uint8_t thrustSweep; // $721B
|
uint8_t thrustSweep; // $721B
|
||||||
|
// Voice 1 and 2 release timers ($7216/$7217): $42E9 loads one from an
|
||||||
|
// SFX program and $4320 counts it down every tick. Game state, not
|
||||||
|
// just sound: levels Q and Y only buzz while voice 2's has run out.
|
||||||
|
uint8_t sidRelTimer[ST_SID_TIMED_VOICES];
|
||||||
|
|
||||||
// ---- collision / crash ----
|
// ---- collision / crash ----
|
||||||
uint8_t collisionPhase; // $7164
|
uint8_t collisionPhase; // $7164
|
||||||
|
|
@ -468,6 +524,18 @@ typedef struct {
|
||||||
uint8_t hitDispatchResult; // $721D
|
uint8_t hitDispatchResult; // $721D
|
||||||
uint8_t spriteSpriteColl; // $71CA latched $D01E
|
uint8_t spriteSpriteColl; // $71CA latched $D01E
|
||||||
uint8_t spriteBgColl; // $71CB latched $D01F
|
uint8_t spriteBgColl; // $71CB latched $D01F
|
||||||
|
// The older of the two frames a read sees (derived state, not C64
|
||||||
|
// memory). The VIC latches $D01E/$D01F until they are read, and the
|
||||||
|
// main loop reads them two frames after the last read: one frame still
|
||||||
|
// shows the previous tick's sprites -- drawn while the CPU waits at
|
||||||
|
// $5F94, so over the picture this tick's work has already changed --
|
||||||
|
// and the next shows this tick's. A read is the OR of both, so a
|
||||||
|
// sprite pulling away from a contact is still seen touching on the
|
||||||
|
// tick it leaves. Sprite-sprite contact does not depend on the
|
||||||
|
// picture and carries over from the last read; sprite-background
|
||||||
|
// contact is worked out again before the flush (stSimTick).
|
||||||
|
uint8_t prevFrameSpriteColl;
|
||||||
|
uint8_t prevFrameBgColl;
|
||||||
// Sprites besides the cab whose $D01F bit a hook reads (levels J
|
// Sprites besides the cab whose $D01F bit a hook reads (levels J
|
||||||
// and P watch their hazards hit the scenery); the rest are never
|
// and P watch their hazards hit the scenery); the rest are never
|
||||||
// tested (derived state, not C64 memory).
|
// tested (derived state, not C64 memory).
|
||||||
|
|
@ -571,8 +639,14 @@ typedef struct {
|
||||||
// fresh screen: copy the image, restore the player's HUD rows, draw
|
// fresh screen: copy the image, restore the player's HUD rows, draw
|
||||||
// the cab icons, spawn the cab, refill the fuel.
|
// the cab icons, spawn the cab, refill the fuel.
|
||||||
void stSimEnterLevel(StSimT *sim, const StLevelT *level);
|
void stSimEnterLevel(StSimT *sim, const StLevelT *level);
|
||||||
// Re-entry after a crash with cabs left ($5F27): spawn + refill only.
|
// Re-entry after a crash with cabs left ($5F27, where $6BCD jumps): spawn,
|
||||||
|
// refill, the second prelude hook and the jingle -- the fare machine and
|
||||||
|
// the hooks' first prelude carry on from where the crash left them.
|
||||||
void stSimRespawn(StSimT *sim);
|
void stSimRespawn(StSimT *sim);
|
||||||
|
// $6B64 -- a finished wreck costs a fare aboard and a cab: LIFE_LOST
|
||||||
|
// (respawn with stSimRespawn) or PLAYER_OUT. What a tick returns in a
|
||||||
|
// real game; the attract demo returns CRASH_DONE instead.
|
||||||
|
StTickResultE stSimLoseCab(StSimT *sim);
|
||||||
// One game tick = one $5F40 iteration (two video frames on the C64).
|
// One game tick = one $5F40 iteration (two video frames on the C64).
|
||||||
StTickResultE stSimTick(StSimT *sim);
|
StTickResultE stSimTick(StSimT *sim);
|
||||||
// Seed the whole struct for a new game ($5EC4 + $4207 + $48AD).
|
// Seed the whole struct for a new game ($5EC4 + $4207 + $48AD).
|
||||||
|
|
@ -609,7 +683,7 @@ void stSimGlyphReset(StSimT *sim);
|
||||||
// The 17-bit VIC sprite X (msb:col), read and signed-byte add ($411B).
|
// The 17-bit VIC sprite X (msb:col), read and signed-byte add ($411B).
|
||||||
uint16_t stSimSpriteX(const StSimT *sim, uint8_t idx);
|
uint16_t stSimSpriteX(const StSimT *sim, uint8_t idx);
|
||||||
void stSimSpriteAddX(StSimT *sim, uint8_t idx, uint8_t delta);
|
void stSimSpriteAddX(StSimT *sim, uint8_t idx, uint8_t delta);
|
||||||
// $6C38 -- blank the 11-character message row.
|
// $6C38 -- blank the 12-character message row.
|
||||||
void stSimClearMessage(StSimT *sim);
|
void stSimClearMessage(StSimT *sim);
|
||||||
// A glyph's bits changed: every cell showing it has to be repainted.
|
// A glyph's bits changed: every cell showing it has to be repainted.
|
||||||
void stSimMarkChar(StSimT *sim, uint8_t ch);
|
void stSimMarkChar(StSimT *sim, uint8_t ch);
|
||||||
|
|
@ -637,8 +711,10 @@ void stFareStageDispatch(StSimT *sim); // $65C1
|
||||||
void stFarePostTickGate(StSimT *sim); // $67E3
|
void stFarePostTickGate(StSimT *sim); // $67E3
|
||||||
void stFarePadLightingGate(StSimT *sim); // $6EF0
|
void stFarePadLightingGate(StSimT *sim); // $6EF0
|
||||||
void stFareUpPlease(StSimT *sim); // $6CB8
|
void stFareUpPlease(StSimT *sim); // $6CB8
|
||||||
|
void stFareEventVoice(StSimT *sim); // $61BD
|
||||||
|
void stFareTakeoffSetup(StSimT *sim); // $6F18
|
||||||
|
void stFareTakeoffVoice(StSimT *sim); // $5F30
|
||||||
uint8_t stFareChooseDestination(StSimT *sim); // $6537: reserves a pad, returns its number
|
uint8_t stFareChooseDestination(StSimT *sim); // $6537: reserves a pad, returns its number
|
||||||
void stFareDrawMessage(StSimT *sim, const uint8_t *text);
|
|
||||||
void stFareSquashed(StSimT *sim); // $69B4..$69F8
|
void stFareSquashed(StSimT *sim); // $69B4..$69F8
|
||||||
void stFareBoardingSound(StSimT *sim, const uint8_t *speech); // $6C7F
|
void stFareBoardingSound(StSimT *sim, const uint8_t *speech); // $6C7F
|
||||||
void stFareWaveStep(StSimT *sim); // $66B7
|
void stFareWaveStep(StSimT *sim); // $66B7
|
||||||
|
|
@ -661,6 +737,10 @@ uint8_t stHookHitVerdict(StSimT *sim); // $7D75: 0 = passenger contact is
|
||||||
// Seed a converted level's scratch from the shipped blob (scene load).
|
// Seed a converted level's scratch from the shipped blob (scene load).
|
||||||
void stHookSceneLoad(StSimT *sim);
|
void stHookSceneLoad(StSimT *sim);
|
||||||
|
|
||||||
|
// $42E9 -- start a 9-byte SFX program (stAudioSfx) and arm its voice's
|
||||||
|
// release timer.
|
||||||
|
void stSimSfx(StSimT *sim, const uint8_t *program9);
|
||||||
|
|
||||||
// ---- audio sink (host provides; stAudio.c / stsim stub) ----
|
// ---- audio sink (host provides; stAudio.c / stsim stub) ----
|
||||||
void stAudioSfx(const uint8_t *program9); // $42E9 load
|
void stAudioSfx(const uint8_t *program9); // $42E9 load
|
||||||
void stAudioNoise(bool on); // $D412 = $81 / $80
|
void stAudioNoise(bool on); // $D412 = $81 / $80
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ $(OUT)/harness: harness.c $(REPO)/spacetaxi.c $(SRCS)
|
||||||
$(CC) $(CFLAGS) harness.c $(SRCS) $(WRAPS) -o $@
|
$(CC) $(CFLAGS) harness.c $(SRCS) $(WRAPS) -o $@
|
||||||
|
|
||||||
# The title screen's level file is read through DATA/levels (the DOS bake
|
# The title screen's level file is read through DATA/levels (the DOS bake
|
||||||
# is the cross-platform STL4 form), so run from a directory that has one.
|
# is the cross-platform STL5 form), so run from a directory that has one.
|
||||||
run: $(OUT)/harness
|
run: $(OUT)/harness
|
||||||
@test -f $(GEN)/levels/title.dat || { echo "staxiharness: no $(GEN)/levels/title.dat (build the game for DOS first)" >&2; exit 2; }
|
@test -f $(GEN)/levels/title.dat || { echo "staxiharness: no $(GEN)/levels/title.dat (build the game for DOS first)" >&2; exit 2; }
|
||||||
@mkdir -p $(OUT)/DATA
|
@mkdir -p $(OUT)/DATA
|
||||||
|
|
|
||||||
442
tools/stsim/c64trace.py
Executable file
442
tools/stsim/c64trace.py
Executable file
|
|
@ -0,0 +1,442 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# c64trace.py -- ride a gate stream on the real game in VICE and print
|
||||||
|
# the trace stsim prints, line for line, so the two can be compared byte
|
||||||
|
# for byte (gate.sh c64).
|
||||||
|
#
|
||||||
|
# c64trace.py <level letter> <ticks> <demobuf.json> [rawOut]
|
||||||
|
# c64trace.py --prepare make the cached snapshot (below) and exit
|
||||||
|
#
|
||||||
|
# demobuf.json is stsim's STSIM_DEMOBUF line: the whole 640-byte playback
|
||||||
|
# buffer ($0902) as the sim starts the ride with it. rawOut, if given,
|
||||||
|
# receives each tick's screen RAM, colour RAM and charset in stsim's
|
||||||
|
# STSIM_RAW layout.
|
||||||
|
#
|
||||||
|
# How the ride is set up on the C64, all from the game's own attract demo:
|
||||||
|
# * The original disk boots once (the crack's intro is skipped at its
|
||||||
|
# key test, the trainer menu answered with START GAME, every cheat
|
||||||
|
# off) to the point the first attract demo picks its screen ($50C6);
|
||||||
|
# VICE's snapshot of that moment is cached in build/c64trace/.
|
||||||
|
# * Each ride restores the snapshot, makes every demo slot ($50C1..
|
||||||
|
# $50C4) the wanted screen, and when the screen and its recording are
|
||||||
|
# loaded ($625F, the disk load is skipped) writes the sim's playback
|
||||||
|
# buffer over $0902, zeroes the flame parity, wave index and walk
|
||||||
|
# parity ($716C/$716E/$7161) that the title leaves behind -- the sim
|
||||||
|
# starts its rides from a fresh game -- and sets the screens-played
|
||||||
|
# count so that $6211's INC makes it the screen's place in the 24-hour
|
||||||
|
# shift (A = 1 .. Y = 25), as stsim's rides have it.
|
||||||
|
# * A finished wreck takes the real game's path ($6B52 -> $6B64: the cab
|
||||||
|
# count drops and the cab respawns at $5F27) instead of the demo's
|
||||||
|
# return to the title, as stsim's rides do. A ride ends when the cab
|
||||||
|
# leaves the screen ($6BFB, end 1), the cabs run out ($6BD0, end 4),
|
||||||
|
# the playback reaches the buffer's last pair (end "stream"), or after
|
||||||
|
# <ticks> ticks.
|
||||||
|
# * Every $5F40 (the main loop's top) the state is read and printed.
|
||||||
|
#
|
||||||
|
# Needs VICE's x64sc (VICE=path, else on PATH; VICE_DATA=its ROM folder
|
||||||
|
# when x64sc cannot find its own), Xvfb, and the original disk image
|
||||||
|
# (STAXI_D64, default stuff/SPACETAX.D64). Exits 2 when one is missing.
|
||||||
|
# The disk and everything derived from it stay out of the repository.
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import signal
|
||||||
|
import socket
|
||||||
|
import struct
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
REPO = os.path.normpath(os.path.join(HERE, '..', '..'))
|
||||||
|
CACHE = os.path.join(REPO, 'build', 'c64trace')
|
||||||
|
D64 = os.environ.get('STAXI_D64', os.path.join(REPO, 'stuff', 'SPACETAX.D64'))
|
||||||
|
LIFE_S = 900 # an emulator outlives no ride by more than this
|
||||||
|
|
||||||
|
# VICE binary monitor protocol (src/monitor/monitor_binary.c).
|
||||||
|
STX = 0x02
|
||||||
|
API_VERSION = 0x02
|
||||||
|
CMD_MEM_GET = 0x01
|
||||||
|
CMD_MEM_SET = 0x02
|
||||||
|
CMD_CP_SET = 0x12
|
||||||
|
CMD_CP_DELETE = 0x13
|
||||||
|
CMD_REGS_SET = 0x32
|
||||||
|
CMD_REGS_GET = 0x31
|
||||||
|
CMD_DUMP = 0x41
|
||||||
|
CMD_UNDUMP = 0x42
|
||||||
|
CMD_KEYS = 0x72
|
||||||
|
CMD_PING = 0x81
|
||||||
|
CMD_BANKS = 0x82
|
||||||
|
CMD_EXIT = 0xAA
|
||||||
|
CMD_QUIT = 0xBB
|
||||||
|
EV_CHECKPOINT = 0x11
|
||||||
|
EV_JAM = 0x61
|
||||||
|
EV_STOPPED = 0x62
|
||||||
|
REG_PC = 0x03
|
||||||
|
OP_EXEC = 0x04
|
||||||
|
|
||||||
|
# The game's addresses.
|
||||||
|
INTRO_KEY_TEST = 0x0994 # the crack's "space pressed?" test
|
||||||
|
INTRO_GO = 0x099E # ...and where it goes when it is
|
||||||
|
DEMO_PICK = 0x50C6 # $5010's demo branch: a demo picks its screen
|
||||||
|
DEMO_SLOTS = 0x50C1 # "HWTX", the demo rotation
|
||||||
|
DEMO_LOAD = 0x625F # JSR $98C4: load the screen's recording
|
||||||
|
DEMO_LOADED = 0x6262 # ...returned
|
||||||
|
DEMO_BUFFER = 0x0902
|
||||||
|
CARRIED = (0x716C, 0x716E, 0x7161)
|
||||||
|
SCREENS_PLAYED = 0x7215
|
||||||
|
TICK_TOP = 0x5F40
|
||||||
|
CAB_LEFT = 0x6BFB
|
||||||
|
WRECK_DONE = 0x6B52
|
||||||
|
LOSE_CAB = 0x6B64
|
||||||
|
PLAYER_OUT = 0x6BD0
|
||||||
|
LAST_PAIR = 638 # the playback buffer's last (mask, count) pair
|
||||||
|
END_EXIT = 1 # StTickResultE: ST_TICK_LEVEL_EXIT
|
||||||
|
END_OUT = 4 # ST_TICK_PLAYER_OUT
|
||||||
|
TRAINER_ROWS = 12 # cursor-downs from the first option to START GAME
|
||||||
|
KEY_DOWN = '\x11'
|
||||||
|
KEY_RETURN = '\x0d'
|
||||||
|
KEY_WAIT_S = 0.3
|
||||||
|
INTRO_WAIT_S = 6
|
||||||
|
BOOT_WAIT_S = 300
|
||||||
|
STOP_WAIT_S = 120
|
||||||
|
|
||||||
|
|
||||||
|
class Monitor:
|
||||||
|
"""A VICE binary-monitor connection."""
|
||||||
|
|
||||||
|
def __init__(self, port):
|
||||||
|
self.sock = None
|
||||||
|
for _ in range(200):
|
||||||
|
try:
|
||||||
|
self.sock = socket.create_connection(('127.0.0.1', port), timeout=2)
|
||||||
|
break
|
||||||
|
except OSError:
|
||||||
|
time.sleep(0.1)
|
||||||
|
if self.sock is None:
|
||||||
|
raise RuntimeError('no VICE monitor on port %d' % port)
|
||||||
|
self.sock.settimeout(None)
|
||||||
|
self.reqId = 1
|
||||||
|
self.events = []
|
||||||
|
self.buf = b''
|
||||||
|
|
||||||
|
def recvExact(self, n):
|
||||||
|
while len(self.buf) < n:
|
||||||
|
chunk = self.sock.recv(1 << 20)
|
||||||
|
if not chunk:
|
||||||
|
raise EOFError('VICE closed the monitor')
|
||||||
|
self.buf += chunk
|
||||||
|
out, self.buf = self.buf[:n], self.buf[n:]
|
||||||
|
return out
|
||||||
|
|
||||||
|
def packet(self):
|
||||||
|
hdr = self.recvExact(12)
|
||||||
|
length = struct.unpack_from('<I', hdr, 2)[0]
|
||||||
|
return hdr[6], hdr[7], struct.unpack_from('<I', hdr, 8)[0], self.recvExact(length)
|
||||||
|
|
||||||
|
def cmd(self, ctype, body=b''):
|
||||||
|
rid = self.reqId
|
||||||
|
self.reqId += 1
|
||||||
|
self.sock.sendall(struct.pack('<BBIIB', STX, API_VERSION, len(body), rid, ctype) + body)
|
||||||
|
while True:
|
||||||
|
rtype, err, r, b = self.packet()
|
||||||
|
if r == rid:
|
||||||
|
if err != 0:
|
||||||
|
raise RuntimeError('monitor command %02X failed (%02X)' % (ctype, err))
|
||||||
|
return b
|
||||||
|
self.events.append((rtype, b))
|
||||||
|
|
||||||
|
def mem(self, start, end, bank):
|
||||||
|
b = self.cmd(CMD_MEM_GET, struct.pack('<BHHBH', 0, start, end, 0, bank))
|
||||||
|
return b[2:2 + struct.unpack_from('<H', b, 0)[0]]
|
||||||
|
|
||||||
|
def poke(self, start, data, bank):
|
||||||
|
self.cmd(CMD_MEM_SET, struct.pack('<BHHBH', 0, start, start + len(data) - 1, 0, bank) + bytes(data))
|
||||||
|
|
||||||
|
def stopAt(self, addr):
|
||||||
|
b = self.cmd(CMD_CP_SET, struct.pack('<HHBBBB', addr, addr, 1, 1, OP_EXEC, 0))
|
||||||
|
return struct.unpack_from('<I', b, 0)[0]
|
||||||
|
|
||||||
|
def unstop(self, num):
|
||||||
|
self.cmd(CMD_CP_DELETE, struct.pack('<I', num))
|
||||||
|
|
||||||
|
def pc(self):
|
||||||
|
b = self.cmd(CMD_REGS_GET, b'\x00')
|
||||||
|
off = 2
|
||||||
|
for _ in range(struct.unpack_from('<H', b, 0)[0]):
|
||||||
|
if b[off + 1] == REG_PC:
|
||||||
|
return struct.unpack_from('<H', b, off + 2)[0]
|
||||||
|
off += b[off] + 1
|
||||||
|
raise RuntimeError('no PC in the register dump')
|
||||||
|
|
||||||
|
def setPc(self, value):
|
||||||
|
self.cmd(CMD_REGS_SET, struct.pack('<BH', 0, 1) + struct.pack('<BBH', 3, REG_PC, value))
|
||||||
|
|
||||||
|
def banks(self):
|
||||||
|
b = self.cmd(CMD_BANKS)
|
||||||
|
out = {}
|
||||||
|
off = 2
|
||||||
|
for _ in range(struct.unpack_from('<H', b, 0)[0]):
|
||||||
|
nameLen = b[off + 3]
|
||||||
|
out[b[off + 4:off + 4 + nameLen].decode()] = struct.unpack_from('<H', b, off + 1)[0]
|
||||||
|
off += b[off] + 1
|
||||||
|
return out
|
||||||
|
|
||||||
|
def go(self):
|
||||||
|
self.events = []
|
||||||
|
self.cmd(CMD_EXIT)
|
||||||
|
|
||||||
|
def halt(self):
|
||||||
|
self.cmd(CMD_PING)
|
||||||
|
|
||||||
|
def waitStop(self, timeout):
|
||||||
|
self.sock.settimeout(timeout)
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
if self.events:
|
||||||
|
rtype, b = self.events.pop(0)
|
||||||
|
else:
|
||||||
|
rtype, _err, _rid, b = self.packet()
|
||||||
|
if rtype == EV_STOPPED:
|
||||||
|
return struct.unpack_from('<H', b, 0)[0]
|
||||||
|
if rtype == EV_JAM:
|
||||||
|
raise RuntimeError('the C64 jammed at $%04X' % struct.unpack_from('<H', b, 0)[0])
|
||||||
|
finally:
|
||||||
|
self.sock.settimeout(None)
|
||||||
|
|
||||||
|
def keys(self, text):
|
||||||
|
t = text.encode('latin-1')
|
||||||
|
self.cmd(CMD_KEYS, bytes([len(t)]) + t)
|
||||||
|
|
||||||
|
def snapshotSave(self, path):
|
||||||
|
p = path.encode()
|
||||||
|
self.cmd(CMD_DUMP, bytes([0, 0, len(p)]) + p)
|
||||||
|
|
||||||
|
def snapshotLoad(self, path):
|
||||||
|
p = path.encode()
|
||||||
|
self.cmd(CMD_UNDUMP, bytes([len(p)]) + p)
|
||||||
|
|
||||||
|
|
||||||
|
class Vice:
|
||||||
|
"""x64sc on a private Xvfb display, driven through its monitor."""
|
||||||
|
|
||||||
|
def __init__(self, autostart=None):
|
||||||
|
exe = os.environ.get('VICE') or shutil.which('x64sc')
|
||||||
|
if not exe or not shutil.which('Xvfb'):
|
||||||
|
raise FileNotFoundError('VICE (x64sc) or Xvfb not found')
|
||||||
|
self.procs = []
|
||||||
|
display = self.freeDisplay()
|
||||||
|
# Each in a session of its own, so close() can take down the
|
||||||
|
# timeout guard and what it runs together.
|
||||||
|
self.procs.append(subprocess.Popen(['timeout', '-s', 'KILL', str(LIFE_S), 'Xvfb', display, '-screen', '0', '640x480x24'],
|
||||||
|
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, start_new_session=True))
|
||||||
|
os.makedirs(os.path.join(CACHE, 'home'), exist_ok=True)
|
||||||
|
env = dict(os.environ, DISPLAY=display, XDG_CONFIG_HOME=os.path.join(CACHE, 'home'), XDG_CACHE_HOME=os.path.join(CACHE, 'home'))
|
||||||
|
port = self.freePort()
|
||||||
|
args = ['timeout', '-s', 'KILL', str(LIFE_S), exe, '-binarymonitor', '-binarymonitoraddress', 'ip4://127.0.0.1:%d' % port,
|
||||||
|
'-sounddev', 'dummy', '-warp', '+confirmonexit']
|
||||||
|
if os.environ.get('VICE_DATA'):
|
||||||
|
args += ['-directory', os.environ['VICE_DATA']]
|
||||||
|
if autostart:
|
||||||
|
args += ['-autostart-warp', '-autostart', autostart]
|
||||||
|
self.procs.append(subprocess.Popen(args, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, env=env, start_new_session=True))
|
||||||
|
try:
|
||||||
|
self.mon = Monitor(port)
|
||||||
|
except BaseException:
|
||||||
|
self.close()
|
||||||
|
raise
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def freePort():
|
||||||
|
s = socket.socket()
|
||||||
|
s.bind(('127.0.0.1', 0))
|
||||||
|
port = s.getsockname()[1]
|
||||||
|
s.close()
|
||||||
|
return port
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def freeDisplay():
|
||||||
|
n = 100 + os.getpid() % 800
|
||||||
|
while os.path.exists('/tmp/.X%d-lock' % n):
|
||||||
|
n += 1
|
||||||
|
return ':%d' % n
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
try:
|
||||||
|
self.mon.cmd(CMD_QUIT)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
for p in reversed(self.procs):
|
||||||
|
try:
|
||||||
|
os.killpg(p.pid, signal.SIGKILL)
|
||||||
|
except ProcessLookupError:
|
||||||
|
pass
|
||||||
|
p.wait()
|
||||||
|
|
||||||
|
|
||||||
|
def fnv(data):
|
||||||
|
h = 2166136261
|
||||||
|
for b in data:
|
||||||
|
h = ((h ^ b) * 16777619) & 0xFFFFFFFF
|
||||||
|
return h
|
||||||
|
|
||||||
|
|
||||||
|
def demoOffset(mon, ram):
|
||||||
|
zp = mon.mem(0x003F, 0x0040, ram)
|
||||||
|
return ((zp[1] << 8) | zp[0]) - DEMO_BUFFER
|
||||||
|
|
||||||
|
|
||||||
|
def stateLine(mon, tick, ram, io, raw):
|
||||||
|
"""stsim's printState, from the C64's memory."""
|
||||||
|
scr = mon.mem(0x0400, 0x07E7, ram)
|
||||||
|
chs = mon.mem(0x2800, 0x2FFF, ram)
|
||||||
|
vic = mon.mem(0xD020, 0xD021, io)
|
||||||
|
col = bytes(b & 0x0F for b in mon.mem(0xD800, 0xDBE7, io))
|
||||||
|
g = mon.mem(0x7140, 0x71FF, ram)
|
||||||
|
p = mon.mem(0x7D5A, 0x7D9F, ram)
|
||||||
|
G = lambda a: g[a - 0x7140]
|
||||||
|
P = lambda a: p[a - 0x7D5A]
|
||||||
|
en = G(0x7196)
|
||||||
|
msb = G(0x718D)
|
||||||
|
if raw is not None:
|
||||||
|
raw.write(scr + col + chs)
|
||||||
|
# A hidden sprite's registers print as dashes, as stsim's do.
|
||||||
|
sprx = ''
|
||||||
|
for k in range(8):
|
||||||
|
x = G(0x71A7 + 2 * k) | (0x100 if (msb >> k) & 1 else 0)
|
||||||
|
sprx += '%03X%02X' % (x, G(0x71A8 + 2 * k)) if (en >> k) & 1 else '-----'
|
||||||
|
if en & 2:
|
||||||
|
spr1 = '"ptr1":"%02X","s1":"%02X%02X.%02X",' % (G(0x7198), G(0x7186), G(0x7176), G(0x717E))
|
||||||
|
else:
|
||||||
|
spr1 = '"ptr1":"--","s1":"--",'
|
||||||
|
s = ('{"tick":%u,"x":"%02X%02X.%02X","y":"%02X.%02X","vx":"%04X","vy":"%04X",'
|
||||||
|
'"pad":%u,"st":%u,"ph":%u,"in":"%02X","dir":"%02X","ptr0":"%02X",' % (
|
||||||
|
tick, P(0x7D63), P(0x7D62), P(0x7D61), P(0x7D65), P(0x7D64),
|
||||||
|
(G(0x714D) << 8) | G(0x714C), (G(0x714F) << 8) | G(0x714E),
|
||||||
|
G(0x7150), G(0x7163), G(0x7164), G(0x7169), G(0x716A), G(0x7197)))
|
||||||
|
s += spr1
|
||||||
|
s += ('"en":"%02X","t4740":"%02X","off":%u,"rng":"%02X%02X","fuel":%u,"slots":%u,"asi":%u,"score":"' % (
|
||||||
|
en, mon.mem(0x4740, 0x4740, ram)[0], demoOffset(mon, ram), G(0x7171), G(0x7172), G(0x71C9), G(0x715C), P(0x7D8E)))
|
||||||
|
s += ''.join('%02X' % scr[0x3C2 + k] for k in range(7))
|
||||||
|
s += '","fare":"' + ''.join('%02X' % scr[0x3E0 + k] for k in range(7))
|
||||||
|
s += '","fuelc":"' + ''.join('%02X' % scr[0x3A6 + k] for k in range(12))
|
||||||
|
s += '","sprx":"' + sprx
|
||||||
|
s += '","scr":"%08X","col":"%08X","chr":"%08X","bd":"%02X","bg":"%02X"}' % (
|
||||||
|
fnv(scr), fnv(col), fnv(chs), vic[0] & 0x0F, vic[1] & 0x0F)
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
def snapshot():
|
||||||
|
"""The cached VICE snapshot of the first attract demo picking its
|
||||||
|
screen, made from the original disk on first use."""
|
||||||
|
path = os.path.join(CACHE, 'attract.vsf')
|
||||||
|
if os.path.exists(path):
|
||||||
|
return path
|
||||||
|
if not os.path.exists(D64):
|
||||||
|
raise FileNotFoundError('no disk image at %s (STAXI_D64)' % D64)
|
||||||
|
os.makedirs(CACHE, exist_ok=True)
|
||||||
|
vice = Vice(autostart=D64)
|
||||||
|
try:
|
||||||
|
mon = vice.mon
|
||||||
|
cp = mon.stopAt(INTRO_KEY_TEST)
|
||||||
|
mon.go()
|
||||||
|
mon.waitStop(BOOT_WAIT_S)
|
||||||
|
mon.unstop(cp)
|
||||||
|
mon.setPc(INTRO_GO)
|
||||||
|
mon.go()
|
||||||
|
time.sleep(INTRO_WAIT_S)
|
||||||
|
mon.halt()
|
||||||
|
for _ in range(TRAINER_ROWS):
|
||||||
|
mon.keys(KEY_DOWN)
|
||||||
|
mon.go()
|
||||||
|
time.sleep(KEY_WAIT_S)
|
||||||
|
mon.halt()
|
||||||
|
mon.keys(KEY_RETURN)
|
||||||
|
cp = mon.stopAt(DEMO_PICK)
|
||||||
|
mon.go()
|
||||||
|
mon.waitStop(BOOT_WAIT_S)
|
||||||
|
mon.unstop(cp)
|
||||||
|
mon.snapshotSave(path + '.part')
|
||||||
|
finally:
|
||||||
|
vice.close()
|
||||||
|
os.replace(path + '.part', path)
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def ride(letter, ticks, demobuf, out, raw):
|
||||||
|
vice = Vice()
|
||||||
|
try:
|
||||||
|
mon = vice.mon
|
||||||
|
mon.snapshotLoad(snapshot())
|
||||||
|
banks = mon.banks()
|
||||||
|
ram = banks['ram']
|
||||||
|
io = banks['io']
|
||||||
|
if mon.pc() != DEMO_PICK:
|
||||||
|
raise RuntimeError('the snapshot is not at $%04X' % DEMO_PICK)
|
||||||
|
mon.poke(DEMO_SLOTS, bytes([ord(letter)] * 4), ram)
|
||||||
|
cp = mon.stopAt(DEMO_LOAD)
|
||||||
|
mon.go()
|
||||||
|
mon.waitStop(STOP_WAIT_S)
|
||||||
|
mon.unstop(cp)
|
||||||
|
mon.poke(DEMO_BUFFER, demobuf, ram)
|
||||||
|
for a in CARRIED:
|
||||||
|
mon.poke(a, b'\x00', ram)
|
||||||
|
mon.poke(SCREENS_PLAYED, bytes([ord(letter) - ord('A')]), ram)
|
||||||
|
mon.setPc(DEMO_LOADED)
|
||||||
|
for a in (TICK_TOP, CAB_LEFT, WRECK_DONE, PLAYER_OUT):
|
||||||
|
mon.stopAt(a)
|
||||||
|
tick = 0
|
||||||
|
exited = False
|
||||||
|
while tick < ticks:
|
||||||
|
mon.go()
|
||||||
|
pc = mon.waitStop(STOP_WAIT_S)
|
||||||
|
if pc == TICK_TOP:
|
||||||
|
if demoOffset(mon, ram) >= LAST_PAIR:
|
||||||
|
out.write('{"tick":%u,"end":"stream"}\n' % (tick - 1))
|
||||||
|
break
|
||||||
|
out.write(stateLine(mon, tick, ram, io, raw) + '\n')
|
||||||
|
tick += 1
|
||||||
|
elif pc == CAB_LEFT:
|
||||||
|
exited = True
|
||||||
|
elif pc == WRECK_DONE:
|
||||||
|
if exited:
|
||||||
|
out.write('{"tick":%u,"end":%d}\n' % (tick - 1, END_EXIT))
|
||||||
|
break
|
||||||
|
mon.setPc(LOSE_CAB)
|
||||||
|
elif pc == PLAYER_OUT:
|
||||||
|
out.write('{"tick":%u,"end":%d}\n' % (tick - 1, END_OUT))
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise RuntimeError('unexpected stop at $%04X' % pc)
|
||||||
|
finally:
|
||||||
|
vice.close()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) == 2 and sys.argv[1] == '--prepare':
|
||||||
|
try:
|
||||||
|
snapshot()
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
print('c64trace: %s' % e, file=sys.stderr)
|
||||||
|
return 2
|
||||||
|
return 0
|
||||||
|
if len(sys.argv) < 4:
|
||||||
|
print('usage: c64trace.py <level letter> <ticks> <demobuf.json> [rawOut] | --prepare', file=sys.stderr)
|
||||||
|
return 2
|
||||||
|
letter = sys.argv[1]
|
||||||
|
ticks = int(sys.argv[2], 0)
|
||||||
|
demobuf = bytes.fromhex(json.load(open(sys.argv[3]))['demobuf'])
|
||||||
|
raw = open(sys.argv[4], 'wb') if len(sys.argv) > 4 else None
|
||||||
|
try:
|
||||||
|
ride(letter, ticks, demobuf, sys.stdout, raw)
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
print('c64trace: %s' % e, file=sys.stderr)
|
||||||
|
return 2
|
||||||
|
finally:
|
||||||
|
if raw is not None:
|
||||||
|
raw.close()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
# sprites on the fly" hitch. Runs the same rides gate.sh does with the
|
# sprites on the fly" hitch. Runs the same rides gate.sh does with the
|
||||||
# stsim census (STSIM_CELS=1) and fails on any MISSING line.
|
# stsim census (STSIM_CELS=1) and fails on any MISSING line.
|
||||||
#
|
#
|
||||||
# celGate.sh check every hook level
|
# celGate.sh check every screen's ride
|
||||||
set -u
|
set -u
|
||||||
|
|
||||||
here=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
here=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
|
|
||||||
|
|
@ -1,79 +1,72 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# Compare stsim output against a VICE trace segment.
|
# Compare two ride traces in stsim's line format -- stsim's against the
|
||||||
# compare.py <trace.jsonl> <startTick> <stsim-output.jsonl> [maxReports]
|
# real C64's (c64trace.py), or a golden against the current tree -- field
|
||||||
import json, sys
|
# by field.
|
||||||
|
#
|
||||||
|
# compare.py <a.jsonl> <b.jsonl> [maxReports]
|
||||||
|
# the first maxReports ticks that differ (default 12), with the
|
||||||
|
# fields that differ, then how often and from when each field does
|
||||||
|
# compare.py --picture <a.raw> <b.raw> <tick>
|
||||||
|
# the screen cells, colour cells and glyphs that differ on one tick,
|
||||||
|
# from the two machines' picture dumps (STSIM_RAW / c64trace's rawOut)
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
trace = [json.loads(l) for l in open(sys.argv[1])]
|
SCREEN_CELLS = 1000
|
||||||
start = int(sys.argv[2])
|
SCREEN_COLS = 40
|
||||||
sim = [json.loads(l) for l in open(sys.argv[3])]
|
CHARSET = 2048
|
||||||
maxRep = int(sys.argv[4]) if len(sys.argv) > 4 else 12
|
GLYPH_BYTES = 8
|
||||||
|
RAW_BYTES = SCREEN_CELLS * 2 + CHARSET
|
||||||
|
|
||||||
def fromTrace(r):
|
|
||||||
g = bytes.fromhex(r['g7140'])
|
|
||||||
pos = bytes.fromhex(r['pos'])
|
|
||||||
hud = bytes.fromhex(r['hud']) # 0x0799 .. 0x07E6
|
|
||||||
def G(a): return g[a - 0x7140]
|
|
||||||
def H(a): return hud[a - 0x0799]
|
|
||||||
d = {}
|
|
||||||
d['x'] = '%02X%02X.%02X' % (pos[9], pos[8], pos[7]) # 7D63 7D62 7D61
|
|
||||||
d['y'] = '%02X.%02X' % (pos[11], pos[10])
|
|
||||||
d['vx'] = '%02X%02X' % (G(0x714D), G(0x714C))
|
|
||||||
d['vy'] = '%02X%02X' % (G(0x714F), G(0x714E))
|
|
||||||
d['pad'] = G(0x7150)
|
|
||||||
d['st'] = G(0x7163)
|
|
||||||
d['ph'] = G(0x7164)
|
|
||||||
d['in'] = '%02X' % G(0x7169)
|
|
||||||
d['dir'] = '%02X' % G(0x716A)
|
|
||||||
d['ptr0'] = '%02X' % G(0x7197)
|
|
||||||
d['ptr1'] = '%02X' % G(0x7198)
|
|
||||||
d['s1'] = '%02X%02X.%02X' % (G(0x7186), G(0x7176), G(0x717E))
|
|
||||||
d['en'] = '%02X' % G(0x7196)
|
|
||||||
d['t4740']= r['t4740'].upper()
|
|
||||||
d['rng'] = '%02X%02X' % (G(0x7171), G(0x7172))
|
|
||||||
d['fuel'] = G(0x71C9)
|
|
||||||
d['slots']= G(0x715C)
|
|
||||||
d['asi'] = pos[0x7D8E - 0x7D5A] if len(pos) > 0x7D8E - 0x7D5A else -1
|
|
||||||
d['score']= ''.join('%02X' % H(0x07C2 + k) for k in range(7))
|
|
||||||
d['fare'] = ''.join('%02X' % H(0x07E0 + k) for k in range(7))
|
|
||||||
d['fuelc']= ''.join('%02X' % H(0x07A6 + k) for k in range(12))
|
|
||||||
# sprite shadow positions $71A7.. interleaved X,Y for 8 sprites + msb $718D
|
|
||||||
msb = G(0x718D)
|
|
||||||
sx = ''
|
|
||||||
for k in range(8):
|
|
||||||
x = G(0x71A7 + 2*k) | (0x100 if (msb >> k) & 1 else 0)
|
|
||||||
sx += '%03X%02X' % (x, G(0x71A8 + 2*k))
|
|
||||||
d['sprx'] = sx
|
|
||||||
return d
|
|
||||||
|
|
||||||
fields = ['x','y','vx','vy','pad','st','ph','in','dir','ptr0','ptr1','s1','en','t4740','rng','fuel','slots','score','fare','fuelc','sprx']
|
def traces(pathA, pathB, maxReports):
|
||||||
|
a = [json.loads(line) for line in open(pathA)]
|
||||||
|
b = [json.loads(line) for line in open(pathB)]
|
||||||
|
counts = {}
|
||||||
|
first = {}
|
||||||
reports = 0
|
reports = 0
|
||||||
firstBad = None
|
for i in range(min(len(a), len(b))):
|
||||||
for s in sim:
|
x, y = a[i], b[i]
|
||||||
if 'end' in s:
|
fields = [k for k in x if x.get(k) != y.get(k)] + [k for k in y if k not in x]
|
||||||
print('sim ended at tick', s['tick'], 'result', s['end'])
|
for k in fields:
|
||||||
break
|
counts[k] = counts.get(k, 0) + 1
|
||||||
t = s['tick']
|
first.setdefault(k, i)
|
||||||
if start + t >= len(trace):
|
if fields and reports < maxReports:
|
||||||
print('trace exhausted at sim tick', t); break
|
print('line %d: ' % i + ' '.join('%s: %s vs %s' % (k, x.get(k), y.get(k)) for k in fields))
|
||||||
r = fromTrace(trace[start + t])
|
|
||||||
# Disabled sprites keep stale shadow values on the C64; only compare
|
|
||||||
# positions/pointers of enabled ones.
|
|
||||||
en = int(r['en'], 16)
|
|
||||||
def spr(sx):
|
|
||||||
return ''.join(sx[k*5:k*5+5] if (en >> k) & 1 else '-----' for k in range(8))
|
|
||||||
s2 = dict(s); r2 = dict(r)
|
|
||||||
s2['sprx'] = spr(s['sprx']); r2['sprx'] = spr(r['sprx'])
|
|
||||||
if not (en & 2):
|
|
||||||
s2['ptr1'] = r2['ptr1'] = '--'; s2['s1'] = r2['s1'] = '--'
|
|
||||||
bad = [f for f in fields if f in s2 and str(s2[f]) != str(r2[f])]
|
|
||||||
s = s2; r = r2
|
|
||||||
if bad:
|
|
||||||
if firstBad is None:
|
|
||||||
firstBad = t
|
|
||||||
if reports < maxRep:
|
|
||||||
print(f'tick {t}: ' + ' '.join(f'{f}: sim={s[f]} c64={r[f]}' for f in bad))
|
|
||||||
reports += 1
|
reports += 1
|
||||||
if firstBad is None:
|
if len(a) != len(b):
|
||||||
print('ALL', len(sim), 'ticks match')
|
print('lengths differ: %d vs %d lines' % (len(a), len(b)))
|
||||||
else:
|
if not counts:
|
||||||
print('first mismatch at tick', firstBad)
|
print('every common line matches' if len(a) != len(b) else 'identical')
|
||||||
|
return
|
||||||
|
print('fields that differ (lines, first line): ' + ', '.join('%s %d @%d' % (k, counts[k], first[k]) for k in sorted(counts, key=first.get)))
|
||||||
|
|
||||||
|
|
||||||
|
def picture(pathA, pathB, tick):
|
||||||
|
a = open(pathA, 'rb').read()[tick * RAW_BYTES:(tick + 1) * RAW_BYTES]
|
||||||
|
b = open(pathB, 'rb').read()[tick * RAW_BYTES:(tick + 1) * RAW_BYTES]
|
||||||
|
for i in range(SCREEN_CELLS):
|
||||||
|
if a[i] != b[i]:
|
||||||
|
print('screen row %d col %d: %02X vs %02X' % (i // SCREEN_COLS, i % SCREEN_COLS, a[i], b[i]))
|
||||||
|
for i in range(SCREEN_CELLS):
|
||||||
|
if a[SCREEN_CELLS + i] != b[SCREEN_CELLS + i]:
|
||||||
|
print('colour row %d col %d: %X vs %X' % (i // SCREEN_COLS, i % SCREEN_COLS, a[SCREEN_CELLS + i], b[SCREEN_CELLS + i]))
|
||||||
|
for ch in range(CHARSET // GLYPH_BYTES):
|
||||||
|
o = SCREEN_CELLS * 2 + ch * GLYPH_BYTES
|
||||||
|
if a[o:o + GLYPH_BYTES] != b[o:o + GLYPH_BYTES]:
|
||||||
|
print('glyph %02X: %s vs %s' % (ch, a[o:o + GLYPH_BYTES].hex(), b[o:o + GLYPH_BYTES].hex()))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) == 5 and sys.argv[1] == '--picture':
|
||||||
|
picture(sys.argv[2], sys.argv[3], int(sys.argv[4]))
|
||||||
|
return 0
|
||||||
|
if len(sys.argv) not in (3, 4):
|
||||||
|
print('usage: compare.py <a.jsonl> <b.jsonl> [maxReports] | --picture <a.raw> <b.raw> <tick>', file=sys.stderr)
|
||||||
|
return 2
|
||||||
|
traces(sys.argv[1], sys.argv[2], int(sys.argv[3]) if len(sys.argv) == 4 else 12)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
|
|
|
||||||
|
|
@ -1,80 +1,155 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Behaviour gate for the Space Taxi simulation.
|
# Behaviour gate for the Space Taxi simulation: every screen's ride --
|
||||||
|
# A..X and the 25th, Y -- checked against the real C64.
|
||||||
#
|
#
|
||||||
# Runs every hook level through stsim and compares the per-tick trace
|
# stsim rides a screen for up to 4000 ticks and prints one line of state
|
||||||
# against a golden capture. A refactor that changes nothing observable
|
# a tick: the cab, the fare machine, the demo playback, the RNG, the HUD,
|
||||||
# leaves all seventeen traces byte-identical; anything else is a regression.
|
# the sprites shown and hashes of screen RAM, colour RAM and the charset.
|
||||||
|
# c64trace.py rides the same input on the original game in VICE and
|
||||||
|
# prints the same line from the C64's memory. gateHashes.txt holds the
|
||||||
|
# md5 of each C64 trace, so `check` -- which needs no emulator -- is a
|
||||||
|
# byte-for-byte comparison with the real machine.
|
||||||
#
|
#
|
||||||
# H, W, T and X ride the C64's own attract recordings (the ones checked
|
# H, W, T and X ride the C64's own attract recordings; the other screens
|
||||||
# against VICE traces). The other thirteen ride synthesised fixtures from
|
# ride synthesised streams from gateStreams.h (tools/stsim/recorder).
|
||||||
# gateStreams.h -- a regression gate only, not evidence of C64 fidelity.
|
|
||||||
#
|
#
|
||||||
# gate.sh capture [dir] record goldens from the CURRENT tree
|
# gate.sh check [dir] every ride's md5 against gateHashes.txt; a ride
|
||||||
# gate.sh check [dir] compare the current tree against them, or, with
|
# that differs is diffed against its golden in dir,
|
||||||
# none captured, against gateHashes.txt (the md5
|
# if one was captured, to show where
|
||||||
# of each golden trace, the committed reference)
|
# gate.sh c64 [L...] ride the C64 too (VICE, c64trace.py) and compare
|
||||||
# gate.sh hashes print one md5 per level (compact reference)
|
# the two traces byte for byte, showing the first
|
||||||
|
# fields that differ; every screen by default
|
||||||
|
# gate.sh c64hashes the C64 traces' md5s in gateHashes.txt's form --
|
||||||
|
# the only way gateHashes.txt is made
|
||||||
|
# gate.sh capture [dir] record goldens from the current tree
|
||||||
|
# gate.sh hashes print the current tree's md5 per screen
|
||||||
#
|
#
|
||||||
# Capture from a tree you trust, refactor, then check. The rides run on the
|
# The rides run on the game's level bake, so build the game first
|
||||||
# game's level bake, so build the game first (gateLib.sh finds it).
|
# (gateLib.sh finds it). The c64 modes keep their traces in
|
||||||
|
# build/c64trace/ and run C64_JOBS rides at once (default 4); they exit 2
|
||||||
|
# when VICE or the original disk is missing (see c64trace.py).
|
||||||
set -u
|
set -u
|
||||||
|
|
||||||
here=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
here=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
mode=${1:-check}
|
mode=${1:-check}
|
||||||
dir=${2:-$here/golden}
|
|
||||||
source "$here/gateLib.sh"
|
source "$here/gateLib.sh"
|
||||||
|
traces=$here/../../build/c64trace/traces
|
||||||
|
|
||||||
|
# simTrace L NN FILE: the current tree's ride.
|
||||||
|
simTrace() {
|
||||||
|
"$here/stsim" "$levels/level$2.dat" "$1" "$ticks" > "$3" 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# c64Ride L NN: ride screen L on the C64 into $traces/L.jsonl and compare
|
||||||
|
# it with stsim's; prints one verdict line, returns 1 on a difference. A
|
||||||
|
# ride that could not be traced leaves $traces/L.failed behind.
|
||||||
|
c64Ride() {
|
||||||
|
local l=$1 n=$2
|
||||||
|
rm -f "$traces/$l.failed"
|
||||||
|
STSIM_DEMOBUF=1 "$here/stsim" "$levels/level$n.dat" "$l" 0 > "$traces/$l.buf.json"
|
||||||
|
simTrace "$l" "$n" "$traces/$l.sim.jsonl"
|
||||||
|
if ! timeout -s KILL 900 python3 "$here/c64trace.py" "$l" "$ticks" "$traces/$l.buf.json" > "$traces/$l.jsonl"; then
|
||||||
|
echo " $l: *** the C64 ride failed ***"
|
||||||
|
touch "$traces/$l.failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if cmp -s "$traces/$l.sim.jsonl" "$traces/$l.jsonl"; then
|
||||||
|
printf ' %s: IDENTICAL to the C64 (%s lines)\n' "$l" "$(wc -l < "$traces/$l.jsonl")"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
echo " $l: *** DIFFERS from the C64 ***"
|
||||||
|
python3 "$here/compare.py" "$traces/$l.sim.jsonl" "$traces/$l.jsonl" 3 | sed 's/^/ /'
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# c64All [L...]: every named screen (all of them by default), C64_JOBS at
|
||||||
|
# a time.
|
||||||
|
c64All() {
|
||||||
|
local want=" ${*:-} " fail=0 p l n
|
||||||
|
python3 "$here/c64trace.py" --prepare || exit $?
|
||||||
|
mkdir -p "$traces"
|
||||||
|
for p in $pairs; do
|
||||||
|
l=${p%%:*}; n=${p##*:}
|
||||||
|
if [ -n "${*:-}" ] && [[ "$want" != *" $l "* ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
while [ "$(jobs -rp | wc -l)" -ge "${C64_JOBS:-4}" ]; do
|
||||||
|
wait -n || fail=1
|
||||||
|
done
|
||||||
|
c64Ride "$l" "$n" &
|
||||||
|
done
|
||||||
|
while [ "$(jobs -rp | wc -l)" -gt 0 ]; do
|
||||||
|
wait -n || fail=1
|
||||||
|
done
|
||||||
|
return $fail
|
||||||
|
}
|
||||||
|
|
||||||
case "$mode" in
|
case "$mode" in
|
||||||
capture)
|
capture)
|
||||||
|
dir=${2:-$here/golden}
|
||||||
mkdir -p "$dir"
|
mkdir -p "$dir"
|
||||||
for p in $pairs; do
|
for p in $pairs; do
|
||||||
l=${p%%:*}; n=${p##*:}
|
l=${p%%:*}; n=${p##*:}
|
||||||
"$here/stsim" "$levels/level$n.dat" "$l" "$ticks" > "$dir/demo$l.jsonl" 2>/dev/null
|
simTrace "$l" "$n" "$dir/demo$l.jsonl"
|
||||||
printf ' %s: %s ticks\n' "$l" "$(wc -l < "$dir/demo$l.jsonl")"
|
printf ' %s: %s lines\n' "$l" "$(wc -l < "$dir/demo$l.jsonl")"
|
||||||
done
|
done
|
||||||
echo "gate: captured to $dir"
|
echo "gate: captured to $dir"
|
||||||
;;
|
;;
|
||||||
check)
|
check)
|
||||||
|
dir=${2:-$here/golden}
|
||||||
fail=0
|
fail=0
|
||||||
for p in $pairs; do
|
for p in $pairs; do
|
||||||
l=${p%%:*}; n=${p##*:}
|
l=${p%%:*}; n=${p##*:}
|
||||||
got=$(mktemp)
|
got=$(mktemp)
|
||||||
"$here/stsim" "$levels/level$n.dat" "$l" "$ticks" > "$got" 2>/dev/null
|
simTrace "$l" "$n" "$got"
|
||||||
if [ -f "$dir/demo$l.jsonl" ]; then
|
|
||||||
if cmp -s "$dir/demo$l.jsonl" "$got"; then
|
|
||||||
printf ' %s: IDENTICAL (%s ticks)\n' "$l" "$(wc -l < "$got")"
|
|
||||||
else
|
|
||||||
echo " $l: *** DIFFERS ***"
|
|
||||||
diff "$dir/demo$l.jsonl" "$got" | head -3
|
|
||||||
fail=1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
want=$(awk -v l="$l" '$1 == l { print $2 }' "$here/gateHashes.txt")
|
want=$(awk -v l="$l" '$1 == l { print $2 }' "$here/gateHashes.txt")
|
||||||
have=$(md5sum < "$got" | cut -d' ' -f1)
|
have=$(md5sum < "$got" | cut -d' ' -f1)
|
||||||
if [ "$have" = "$want" ]; then
|
if [ "$have" = "$want" ]; then
|
||||||
printf ' %s: IDENTICAL (%s ticks, md5 as gateHashes.txt)\n' "$l" "$(wc -l < "$got")"
|
printf ' %s: IDENTICAL to the C64 (%s lines, md5 as gateHashes.txt)\n' "$l" "$(wc -l < "$got")"
|
||||||
else
|
else
|
||||||
echo " $l: *** DIFFERS *** (md5 $have, gateHashes.txt ${want:-has none}; capture goldens from a trusted tree to see where)"
|
echo " $l: *** DIFFERS from the C64 *** (md5 $have, gateHashes.txt ${want:-has none}; gate.sh c64 $l shows where)"
|
||||||
fail=1
|
if [ -f "$dir/demo$l.jsonl" ]; then
|
||||||
|
python3 "$here/compare.py" "$dir/demo$l.jsonl" "$got" 3 | sed 's/^/ golden vs now: /'
|
||||||
fi
|
fi
|
||||||
|
fail=1
|
||||||
fi
|
fi
|
||||||
rm -f "$got"
|
rm -f "$got"
|
||||||
done
|
done
|
||||||
[ $fail -eq 0 ] && echo "gate: PASS" || echo "gate: FAIL"
|
[ $fail -eq 0 ] && echo "gate: PASS" || echo "gate: FAIL"
|
||||||
# The cel census rides the same recordings: a cel a ride displays that
|
# The cel census rides the same streams: a cel a ride displays that its
|
||||||
# its manifest does not bake is a mid-ride interpreted sprite.
|
# manifest does not bake is a mid-ride interpreted sprite.
|
||||||
"$here/celGate.sh" || fail=1
|
"$here/celGate.sh" || fail=1
|
||||||
exit $fail
|
exit $fail
|
||||||
;;
|
;;
|
||||||
|
c64)
|
||||||
|
shift
|
||||||
|
c64All "$@"
|
||||||
|
fail=$?
|
||||||
|
[ $fail -eq 0 ] && echo "gate c64: PASS" || echo "gate c64: FAIL"
|
||||||
|
exit $fail
|
||||||
|
;;
|
||||||
|
c64hashes)
|
||||||
|
c64All >&2
|
||||||
|
if compgen -G "$traces/*.failed" > /dev/null; then
|
||||||
|
echo "gate c64hashes: a C64 ride failed; no hashes" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
for p in $pairs; do
|
||||||
|
l=${p%%:*}
|
||||||
|
printf '%s %s\n' "$l" "$(md5sum < "$traces/$l.jsonl" | cut -d' ' -f1)"
|
||||||
|
done
|
||||||
|
;;
|
||||||
hashes)
|
hashes)
|
||||||
for p in $pairs; do
|
for p in $pairs; do
|
||||||
l=${p%%:*}; n=${p##*:}
|
l=${p%%:*}; n=${p##*:}
|
||||||
h=$("$here/stsim" "$levels/level$n.dat" "$l" "$ticks" 2>/dev/null | md5sum | cut -d' ' -f1)
|
got=$(mktemp)
|
||||||
printf '%s %s\n' "$l" "$h"
|
simTrace "$l" "$n" "$got"
|
||||||
|
printf '%s %s\n' "$l" "$(md5sum < "$got" | cut -d' ' -f1)"
|
||||||
|
rm -f "$got"
|
||||||
done
|
done
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
sed -n '2,20p' "$0"
|
sed -n '2,33p' "$0"
|
||||||
exit 2
|
exit 2
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,31 @@
|
||||||
# Space Taxi behaviour gate -- md5 of each level's 4000-tick trace.
|
# Space Taxi behaviour gate -- the md5 of each screen's ride as the REAL
|
||||||
# Regenerate with: tools/stsim/gate.sh hashes
|
# C64 traced it: the original game in VICE, ridden by c64trace.py, one
|
||||||
# H/W/T/X ride the C64's attract recordings; the rest ride
|
# line of state per tick (stsim prints the same line from the port).
|
||||||
# synthesised fixtures from gateStreams.h (regression gate only).
|
# `gate.sh check` compares the port's rides against these, so a pass means
|
||||||
H 760501b0e48f3922bfb0f23fd2831935
|
# byte-identical to the C64 on all 25 screens.
|
||||||
T c0b3f9e9a74e0d526703cf9b1875557b
|
# Regenerate (needs VICE and the original disk): tools/stsim/gate.sh c64hashes
|
||||||
W 92226455989e8326577a6ea20df8c8ea
|
A e5cc75d60b41a3fb6ad70a8475a21a7a
|
||||||
X 87251d616a1be6e0c9c9317987b0fe20
|
B 4348bfdde94d3fc6d28d1ee7ca4e73bb
|
||||||
G 73530651372c60042f1f46b5c0cbada0
|
C 6669b71a3cdeebcbab62e1a29eda5faa
|
||||||
I 7fe4415429d2b72a50884424269cb685
|
D 2df4ea48591d8fc7bafe20638a1eaf00
|
||||||
K 567be2ed37219356a055866459971345
|
E 7595460b2296bd863b740060547bdc3d
|
||||||
O 02455280933e915e57803a4059aaefd9
|
F 96d1f6589336430399e4fd04941b079d
|
||||||
Q a7732866d54ed06d52e5dc7406b5b229
|
G 9f07d6ed41c634ba8acf7d0162fca172
|
||||||
R 066a9be80107446893b4571d247ee5cc
|
H 5682e029bbf147d429bba1f774deca13
|
||||||
U 02dd2f26245442ab016ef02e10fde611
|
I ecb6dbe5b9765bbf3c0bf4638dc9f72f
|
||||||
V cc2cb13c5e393d07fbece9c0c465e387
|
J 171cfb357d771abd7db4b6d3ac3b4402
|
||||||
E 21ff29d69d8318bcb2e870b9602e910e
|
K d7bc29828c42cda46f66485d2252bb4c
|
||||||
J 4c22a7515949b1701ed4fedabb7823dd
|
L 05a2098c950e76c78799227fa748b1d3
|
||||||
L a0383cbde2800bb09353df15214934ef
|
M 77fe51edf453736f7000f91ad9c52d19
|
||||||
P f345d0adc9e011662d582c4277ee663e
|
N 1e7fd970699f408b7e6ea246882fa778
|
||||||
S daa3cd12320e32d3e9b6b97b19b0a6d0
|
O 0af4d661248ae1e0bf57eaa24973051a
|
||||||
|
P 740e45a7fe08cfde34d9ee6b6d35c116
|
||||||
|
Q d4da1893385a16b09f8ea383e7d2c4ce
|
||||||
|
R 5089cfb129fc29b3c6ebffa2dd2f6923
|
||||||
|
S 561f7a0c757e82118331dbc8dd582214
|
||||||
|
T 7223a56296bd4f0023fcc3073afaf955
|
||||||
|
U b4178bc4c5aef13d4e87a006ed0fab75
|
||||||
|
V 69976c2e32a03448d8f3adf7a2fe2df8
|
||||||
|
W 5c0b20833ede9e848660bb98a67be208
|
||||||
|
X f3182a3975c023b961c1eee85bcab687
|
||||||
|
Y 67f5ace2d1688717de126b313c436149
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,19 @@
|
||||||
# Sourced by gate.sh and celGate.sh, which set $here first: the rides both
|
# Sourced by gate.sh and celGate.sh, which set $here first: the rides both
|
||||||
# gates replay, and the levels they replay them on.
|
# gates replay, and the levels they replay them on.
|
||||||
|
|
||||||
# Every hook level as letter:level file number, and how long each ride runs.
|
# Every screen as letter:level file number -- A..X and the 25th, Y -- and
|
||||||
|
# how long a ride may run.
|
||||||
ticks=4000
|
ticks=4000
|
||||||
pairs="H:08 T:20 W:23 X:24 G:07 I:09 K:11 O:15 Q:17 R:18 U:21 V:22 E:05 J:10 L:12 P:16 S:19"
|
pairs="A:01 B:02 C:03 D:04 E:05 F:06 G:07 H:08 I:09 J:10 K:11 L:12 M:13 N:14 O:15 P:16 Q:17 R:18 S:19 T:20 U:21 V:22 W:23 X:24 Y:25"
|
||||||
|
|
||||||
# The game's own level bake (program.mk). It is the same STL4 bytes for every
|
# The game's own level bake (program.mk). It is the same STL5 bytes for every
|
||||||
# target, so whichever target was built first will do; STSIM_LEVELS names
|
# target, so whichever target was built first will do; STSIM_LEVELS names
|
||||||
# another folder.
|
# another folder.
|
||||||
levels=${STSIM_LEVELS:-}
|
levels=${STSIM_LEVELS:-}
|
||||||
if [ -z "$levels" ]; then
|
if [ -z "$levels" ]; then
|
||||||
for port in iigs dos amiga atarist x68000; do
|
for bake in "$here"/../../build/generated/*/levels; do
|
||||||
if [ -f "$here/../../build/generated/$port/levels/level01.dat" ]; then
|
if [ -f "$bake/level25.dat" ]; then
|
||||||
levels=$(cd "$here/../../build/generated/$port/levels" && pwd)
|
levels=$(cd "$bake" && pwd)
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
|
||||||
|
|
@ -1,89 +1,313 @@
|
||||||
// Synthesised demo input streams -- a TEST FIXTURE, not game content.
|
// Synthesised demo input streams -- a TEST FIXTURE, not game content.
|
||||||
//
|
//
|
||||||
// The C64 ships attract recordings for four screens only (H, W, T and X);
|
// The C64 ships attract recordings for four screens only (H, W, T and X;
|
||||||
// those live in stDemoStreams.h and are the streams
|
// stDemoStreams.h). The streams below cover the other twenty-one: each was
|
||||||
// checked against VICE traces. The thirteen streams below were searched for
|
// searched for by tools/stsim's recorder against this port's simulation,
|
||||||
// by tools/stsim's recorder against this port's own simulation, scored on
|
// scored on how long the cab survives and how much of the level's hook
|
||||||
// how long the cab survives and how much of the level's hook state, fare
|
// state, fare machine and screen animation the ride reaches.
|
||||||
// machine and screen animation the ride reaches.
|
|
||||||
//
|
//
|
||||||
// So they prove one thing and not another: they are a REGRESSION gate --
|
// Every ride, these and the four recordings alike, is then ridden on the
|
||||||
// refactor the hooks and the tick-by-tick trace must not move -- but they
|
// original game in VICE (gate.sh c64, c64trace.py) and must trace byte
|
||||||
// are NOT evidence that a hook matches the original, because no C64 trace
|
// for byte as the simulation does; gateHashes.txt holds the C64's traces.
|
||||||
// exists for these screens. Treat a change to these bytes as invalidating
|
// A change to these bytes therefore needs `gate.sh c64hashes` on a
|
||||||
// the goldens, and never put them in the attract rotation.
|
// machine with VICE. Never put them in the attract rotation.
|
||||||
|
|
||||||
#ifndef ST_GATE_STREAMS_H
|
#ifndef ST_GATE_STREAMS_H
|
||||||
#define ST_GATE_STREAMS_H
|
#define ST_GATE_STREAMS_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
// level G -- 1400 ticks, 5 hook bytes, 10 fare stages, 256 pictures, 13 landings
|
// level A -- 1397 ticks, 0 hook bytes, 8 fare stages, 256 pictures, 8 landings, left through the hatch
|
||||||
// regenerate: tools/stsim/recorder levels/level??.dat G 20260921
|
// regenerate: tools/stsim/recorder levels/level01.dat A 20261006 60 2000
|
||||||
|
static const uint8_t kGateStreamA[320] = {
|
||||||
|
0x88, 0x06, 0x82, 0x0B, 0x84, 0x0E, 0x90, 0x06, 0x84, 0x0B, 0x82, 0x08,
|
||||||
|
0x88, 0x0C, 0x84, 0x0B, 0x81, 0x0E, 0x90, 0x0E, 0x90, 0x0E, 0x84, 0x0D,
|
||||||
|
0x81, 0x0C, 0x84, 0x01, 0x88, 0x01, 0x82, 0x03, 0x81, 0x01, 0x88, 0x0C,
|
||||||
|
0x81, 0x01, 0x88, 0x0C, 0x90, 0x0D, 0x81, 0x01, 0x81, 0x0E, 0x81, 0x0D,
|
||||||
|
0x81, 0x0C, 0x82, 0x09, 0x84, 0x09, 0x90, 0x0E, 0x81, 0x01, 0x81, 0x04,
|
||||||
|
0x82, 0x0B, 0x81, 0x0D, 0x81, 0x03, 0x88, 0x01, 0x84, 0x06, 0x82, 0x0D,
|
||||||
|
0x81, 0x0A, 0x81, 0x08, 0x90, 0x0C, 0x90, 0x0D, 0x84, 0x0E, 0x84, 0x07,
|
||||||
|
0x90, 0x0B, 0x88, 0x0C, 0x82, 0x08, 0x81, 0x01, 0x81, 0x06, 0x90, 0x0D,
|
||||||
|
0x84, 0x02, 0x84, 0x0D, 0x88, 0x05, 0x82, 0x09, 0x81, 0x01, 0x88, 0x04,
|
||||||
|
0x84, 0x09, 0x88, 0x01, 0x81, 0x05, 0x88, 0x06, 0x81, 0x03, 0x81, 0x0A,
|
||||||
|
0x84, 0x06, 0x81, 0x05, 0x82, 0x01, 0x84, 0x0E, 0x84, 0x09, 0x90, 0x02,
|
||||||
|
0x81, 0x03, 0x82, 0x0A, 0x84, 0x06, 0x84, 0x0E, 0x82, 0x07, 0x88, 0x04,
|
||||||
|
0x90, 0x0D, 0x88, 0x09, 0x81, 0x04, 0x88, 0x0E, 0x81, 0x08, 0x88, 0x0C,
|
||||||
|
0x84, 0x0D, 0x81, 0x08, 0x84, 0x09, 0x82, 0x06, 0x88, 0x0E, 0x84, 0x06,
|
||||||
|
0x81, 0x04, 0x88, 0x0C, 0x84, 0x03, 0x88, 0x0E, 0x84, 0x09, 0x90, 0x0E,
|
||||||
|
0x82, 0x0E, 0x88, 0x0C, 0x82, 0x06, 0x81, 0x0C, 0x90, 0x0C, 0x81, 0x07,
|
||||||
|
0x82, 0x0E, 0x81, 0x04, 0x81, 0x07, 0x84, 0x0D, 0x84, 0x07, 0x81, 0x0C,
|
||||||
|
0x84, 0x03, 0x81, 0x05, 0x82, 0x0E, 0x90, 0x04, 0x90, 0x0A, 0x90, 0x0E,
|
||||||
|
0x81, 0x0D, 0x82, 0x08, 0x82, 0x0D, 0x88, 0x0D, 0x81, 0x09, 0x81, 0x0B,
|
||||||
|
0x81, 0x0C, 0x84, 0x0E, 0x81, 0x0D, 0x82, 0x0C, 0x82, 0x08, 0x90, 0x08,
|
||||||
|
0x81, 0x0B, 0x82, 0x09, 0x88, 0x0A, 0x88, 0x02, 0x82, 0x03, 0x90, 0x08,
|
||||||
|
0x81, 0x08, 0x82, 0x08, 0x81, 0x0A, 0x81, 0x06, 0x88, 0x06, 0x81, 0x04,
|
||||||
|
0x84, 0x01, 0x90, 0x0E, 0x81, 0x07, 0x84, 0x0D, 0x82, 0x08, 0x90, 0x06,
|
||||||
|
0x82, 0x0E, 0x90, 0x0D, 0x81, 0x03, 0x90, 0x0B, 0x82, 0x0A, 0x88, 0x0A,
|
||||||
|
0x84, 0x0C, 0x81, 0x03, 0x81, 0x01, 0x81, 0x0B, 0x88, 0x0E, 0x81, 0x07,
|
||||||
|
0x82, 0x04, 0x85, 0x02, 0x81, 0x0D, 0x81, 0x08, 0x84, 0x0D, 0x82, 0x04,
|
||||||
|
0x82, 0x0A, 0x81, 0x0C, 0x81, 0x07, 0x88, 0x0A,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level B -- 1400 ticks, 0 hook bytes, 10 fare stages, 256 pictures, 6 landings
|
||||||
|
// regenerate: tools/stsim/recorder levels/level02.dat B 20261006 60 2000
|
||||||
|
static const uint8_t kGateStreamB[320] = {
|
||||||
|
0x81, 0x04, 0x88, 0x02, 0x89, 0x01, 0x89, 0x02, 0x81, 0x01, 0x88, 0x05,
|
||||||
|
0x81, 0x04, 0x84, 0x0D, 0x84, 0x06, 0x82, 0x0C, 0x84, 0x08, 0x88, 0x07,
|
||||||
|
0x88, 0x06, 0x81, 0x0B, 0x88, 0x0E, 0x82, 0x07, 0x88, 0x0D, 0x81, 0x07,
|
||||||
|
0x84, 0x06, 0x81, 0x03, 0x82, 0x0C, 0x88, 0x02, 0x84, 0x04, 0x84, 0x0E,
|
||||||
|
0x81, 0x02, 0x82, 0x09, 0x80, 0x0B, 0x84, 0x09, 0x88, 0x07, 0x90, 0x0B,
|
||||||
|
0x81, 0x0C, 0x81, 0x0A, 0x90, 0x0B, 0x81, 0x01, 0x81, 0x07, 0x92, 0x09,
|
||||||
|
0x81, 0x04, 0x82, 0x09, 0x81, 0x0E, 0x88, 0x09, 0x88, 0x0D, 0x81, 0x02,
|
||||||
|
0x82, 0x03, 0x84, 0x0A, 0x82, 0x05, 0x90, 0x0C, 0x81, 0x07, 0x81, 0x0D,
|
||||||
|
0x84, 0x05, 0x90, 0x03, 0x88, 0x0D, 0x82, 0x0C, 0x88, 0x07, 0x90, 0x0E,
|
||||||
|
0x81, 0x05, 0x90, 0x02, 0x88, 0x06, 0x82, 0x0B, 0x81, 0x05, 0x88, 0x09,
|
||||||
|
0x82, 0x06, 0x84, 0x07, 0x84, 0x01, 0x88, 0x07, 0x81, 0x0B, 0x90, 0x0C,
|
||||||
|
0x82, 0x0D, 0x88, 0x07, 0x81, 0x05, 0x88, 0x0C, 0x84, 0x0E, 0x90, 0x0A,
|
||||||
|
0x84, 0x08, 0x90, 0x09, 0x81, 0x07, 0x84, 0x03, 0x82, 0x02, 0x81, 0x03,
|
||||||
|
0x88, 0x06, 0x90, 0x08, 0x90, 0x01, 0x81, 0x03, 0x82, 0x0C, 0x81, 0x05,
|
||||||
|
0x88, 0x05, 0x84, 0x07, 0x84, 0x08, 0x90, 0x09, 0x88, 0x0D, 0x81, 0x0D,
|
||||||
|
0x90, 0x0C, 0x88, 0x02, 0x88, 0x0E, 0x81, 0x04, 0x81, 0x0E, 0x82, 0x0C,
|
||||||
|
0x82, 0x0A, 0x90, 0x07, 0x81, 0x06, 0x82, 0x0E, 0x82, 0x01, 0x81, 0x07,
|
||||||
|
0x90, 0x0D, 0x90, 0x04, 0x81, 0x09, 0x84, 0x0D, 0x84, 0x08, 0x81, 0x07,
|
||||||
|
0x88, 0x03, 0x84, 0x01, 0x81, 0x01, 0x88, 0x07, 0x88, 0x0C, 0x82, 0x09,
|
||||||
|
0x81, 0x03, 0x81, 0x03, 0x88, 0x0D, 0x91, 0x01, 0x84, 0x0C, 0x82, 0x08,
|
||||||
|
0x81, 0x03, 0x90, 0x0C, 0x88, 0x0D, 0x82, 0x06, 0x82, 0x01, 0x81, 0x07,
|
||||||
|
0x90, 0x06, 0x84, 0x04, 0x90, 0x0A, 0x81, 0x06, 0x88, 0x0E, 0x90, 0x03,
|
||||||
|
0x81, 0x03, 0x90, 0x0D, 0x84, 0x0C, 0x88, 0x0E, 0x81, 0x04, 0x90, 0x0B,
|
||||||
|
0x88, 0x0B, 0x82, 0x08, 0x81, 0x0C, 0x81, 0x0B, 0x81, 0x02, 0x84, 0x08,
|
||||||
|
0x84, 0x04, 0x82, 0x0E, 0x84, 0x04, 0x81, 0x05, 0x90, 0x0C, 0x90, 0x08,
|
||||||
|
0x8A, 0x0D, 0x88, 0x06, 0x81, 0x01, 0x81, 0x04, 0x81, 0x01, 0x90, 0x0A,
|
||||||
|
0x88, 0x0E, 0x82, 0x07, 0x81, 0x03, 0x82, 0x04,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level C -- 1400 ticks, 0 hook bytes, 10 fare stages, 256 pictures, 24 landings
|
||||||
|
// regenerate: tools/stsim/recorder levels/level03.dat C 20261006 60 2000
|
||||||
|
static const uint8_t kGateStreamC[320] = {
|
||||||
|
0x81, 0x04, 0x84, 0x01, 0x84, 0x09, 0x89, 0x03, 0x90, 0x05, 0x84, 0x0D,
|
||||||
|
0x81, 0x04, 0x88, 0x0E, 0x88, 0x0A, 0x82, 0x0C, 0x84, 0x02, 0x84, 0x07,
|
||||||
|
0x81, 0x06, 0x81, 0x0B, 0x88, 0x08, 0x82, 0x07, 0x84, 0x0A, 0x81, 0x0D,
|
||||||
|
0x84, 0x0B, 0x81, 0x02, 0x82, 0x0C, 0x88, 0x0E, 0x84, 0x0B, 0x88, 0x0E,
|
||||||
|
0x81, 0x0C, 0x81, 0x08, 0x81, 0x02, 0x90, 0x0E, 0x81, 0x08, 0x90, 0x0E,
|
||||||
|
0x84, 0x02, 0x81, 0x03, 0x84, 0x0E, 0x88, 0x04, 0x84, 0x06, 0x90, 0x06,
|
||||||
|
0x81, 0x01, 0x90, 0x09, 0x81, 0x05, 0x88, 0x09, 0x84, 0x0E, 0x90, 0x05,
|
||||||
|
0x90, 0x04, 0x82, 0x06, 0x84, 0x0D, 0x81, 0x09, 0x82, 0x07, 0x90, 0x0E,
|
||||||
|
0x88, 0x0C, 0x84, 0x0E, 0x81, 0x0E, 0x81, 0x0B, 0x84, 0x0B, 0x81, 0x01,
|
||||||
|
0x88, 0x0B, 0x81, 0x06, 0x88, 0x0B, 0x82, 0x08, 0x88, 0x04, 0x81, 0x09,
|
||||||
|
0x81, 0x02, 0x81, 0x08, 0x84, 0x06, 0x81, 0x0A, 0x90, 0x07, 0x84, 0x01,
|
||||||
|
0x82, 0x0D, 0x88, 0x0D, 0x82, 0x06, 0x88, 0x09, 0x88, 0x0A, 0x84, 0x05,
|
||||||
|
0x84, 0x0E, 0x81, 0x01, 0x88, 0x03, 0x84, 0x09, 0x88, 0x0C, 0x81, 0x0A,
|
||||||
|
0x82, 0x0A, 0x90, 0x0A, 0x90, 0x0D, 0x81, 0x03, 0x90, 0x0B, 0x82, 0x03,
|
||||||
|
0x84, 0x03, 0x84, 0x01, 0x81, 0x02, 0x90, 0x01, 0x84, 0x0C, 0x81, 0x01,
|
||||||
|
0x90, 0x09, 0x81, 0x0B, 0x82, 0x05, 0x82, 0x0D, 0x90, 0x0D, 0x82, 0x0A,
|
||||||
|
0x90, 0x07, 0x81, 0x03, 0x90, 0x0B, 0x88, 0x02, 0x84, 0x07, 0x84, 0x0E,
|
||||||
|
0x88, 0x07, 0x88, 0x0D, 0x81, 0x02, 0x88, 0x0B, 0x90, 0x07, 0x81, 0x01,
|
||||||
|
0x84, 0x0A, 0x90, 0x08, 0x84, 0x08, 0x90, 0x08, 0x81, 0x0A, 0x90, 0x04,
|
||||||
|
0x88, 0x0B, 0x82, 0x09, 0x84, 0x0E, 0x81, 0x06, 0x84, 0x04, 0x88, 0x0A,
|
||||||
|
0x81, 0x0B, 0x81, 0x02, 0x88, 0x0C, 0x82, 0x0E, 0x84, 0x07, 0x82, 0x05,
|
||||||
|
0x88, 0x0A, 0x82, 0x09, 0x81, 0x04, 0x90, 0x03, 0x84, 0x0A, 0x82, 0x0B,
|
||||||
|
0x81, 0x08, 0x90, 0x06, 0x82, 0x0C, 0x82, 0x03, 0x84, 0x08, 0x81, 0x07,
|
||||||
|
0x90, 0x0E, 0x82, 0x0C, 0x81, 0x0E, 0x88, 0x09, 0x81, 0x01, 0x90, 0x0E,
|
||||||
|
0x90, 0x0A, 0x88, 0x09, 0x81, 0x01, 0x90, 0x06, 0x90, 0x09, 0x81, 0x02,
|
||||||
|
0x90, 0x0E, 0x81, 0x02, 0x90, 0x0C, 0x84, 0x06, 0x84, 0x09, 0x81, 0x03,
|
||||||
|
0x90, 0x0D, 0x82, 0x0C, 0x81, 0x06, 0x81, 0x0D,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level D -- 1400 ticks, 0 hook bytes, 10 fare stages, 256 pictures, 9 landings
|
||||||
|
// regenerate: tools/stsim/recorder levels/level04.dat D 20261006 60 2000
|
||||||
|
static const uint8_t kGateStreamD[320] = {
|
||||||
|
0x81, 0x04, 0x82, 0x02, 0x81, 0x01, 0x90, 0x02, 0x84, 0x01, 0x88, 0x05,
|
||||||
|
0x81, 0x04, 0x84, 0x0B, 0x81, 0x0E, 0x84, 0x07, 0x81, 0x0E, 0x82, 0x0D,
|
||||||
|
0x81, 0x05, 0x81, 0x01, 0x82, 0x01, 0x88, 0x03, 0x84, 0x0C, 0x88, 0x07,
|
||||||
|
0x88, 0x07, 0x84, 0x06, 0x81, 0x04, 0x81, 0x03, 0x88, 0x06, 0x84, 0x03,
|
||||||
|
0x81, 0x06, 0x84, 0x0A, 0x81, 0x0D, 0x84, 0x0E, 0x81, 0x0B, 0x81, 0x07,
|
||||||
|
0x82, 0x06, 0x81, 0x04, 0x84, 0x09, 0x90, 0x06, 0x88, 0x07, 0x80, 0x01,
|
||||||
|
0x84, 0x0D, 0x84, 0x09, 0x88, 0x0A, 0x90, 0x08, 0x88, 0x05, 0x81, 0x0E,
|
||||||
|
0x84, 0x0E, 0x84, 0x0E, 0x81, 0x02, 0x90, 0x0C, 0x90, 0x0D, 0x84, 0x06,
|
||||||
|
0x84, 0x01, 0x88, 0x03, 0x90, 0x09, 0x82, 0x08, 0x85, 0x05, 0x81, 0x04,
|
||||||
|
0x81, 0x06, 0x81, 0x01, 0x81, 0x05, 0x88, 0x06, 0x84, 0x0A, 0x81, 0x0A,
|
||||||
|
0x88, 0x01, 0x81, 0x02, 0x82, 0x0B, 0x84, 0x03, 0x90, 0x0B, 0x81, 0x03,
|
||||||
|
0x84, 0x03, 0x82, 0x03, 0x84, 0x06, 0x88, 0x0E, 0x84, 0x0B, 0x81, 0x01,
|
||||||
|
0x81, 0x02, 0x81, 0x09, 0x81, 0x04, 0x88, 0x06, 0x81, 0x04, 0x88, 0x0D,
|
||||||
|
0x81, 0x0D, 0x81, 0x08, 0x88, 0x06, 0x82, 0x0E, 0x81, 0x0E, 0x84, 0x02,
|
||||||
|
0x81, 0x01, 0x84, 0x0B, 0x81, 0x0E, 0x81, 0x0A, 0x83, 0x07, 0x90, 0x0B,
|
||||||
|
0x84, 0x0A, 0x88, 0x0D, 0x90, 0x03, 0x88, 0x0B, 0x82, 0x0B, 0x88, 0x01,
|
||||||
|
0x80, 0x0B, 0x81, 0x07, 0x81, 0x07, 0x81, 0x0C, 0x81, 0x03, 0x90, 0x0C,
|
||||||
|
0x88, 0x0B, 0x81, 0x0E, 0x82, 0x01, 0x81, 0x0C, 0x81, 0x0E, 0x81, 0x07,
|
||||||
|
0x90, 0x0C, 0x90, 0x01, 0x88, 0x03, 0x88, 0x03, 0x88, 0x07, 0x81, 0x02,
|
||||||
|
0x88, 0x06, 0x90, 0x0E, 0x88, 0x08, 0x81, 0x04, 0x81, 0x0D, 0x84, 0x07,
|
||||||
|
0x90, 0x01, 0x82, 0x07, 0x81, 0x05, 0x81, 0x01, 0x84, 0x0B, 0x84, 0x01,
|
||||||
|
0x81, 0x02, 0x90, 0x0E, 0x82, 0x02, 0x81, 0x01, 0x84, 0x02, 0x81, 0x01,
|
||||||
|
0x81, 0x09, 0x81, 0x0D, 0x84, 0x05, 0x8A, 0x0E, 0x81, 0x08, 0x85, 0x0B,
|
||||||
|
0x81, 0x02, 0x81, 0x0D, 0x90, 0x02, 0x82, 0x0D, 0x84, 0x02, 0x82, 0x06,
|
||||||
|
0x81, 0x0D, 0x82, 0x03, 0x84, 0x04, 0x81, 0x05, 0x90, 0x0E, 0x81, 0x08,
|
||||||
|
0x8A, 0x0D, 0x88, 0x0B, 0x90, 0x04, 0x81, 0x06, 0x84, 0x09, 0x81, 0x08,
|
||||||
|
0x88, 0x0E, 0x80, 0x07, 0x82, 0x05, 0x90, 0x0C,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level E -- 1400 ticks, 4 hook bytes, 10 fare stages, 256 pictures, 3 landings
|
||||||
|
// regenerate: tools/stsim/recorder levels/level05.dat E 20261006 60 2000
|
||||||
|
static const uint8_t kGateStreamE[320] = {
|
||||||
|
0x88, 0x04, 0x81, 0x02, 0x84, 0x01, 0x90, 0x02, 0x88, 0x01, 0x84, 0x08,
|
||||||
|
0x81, 0x04, 0x88, 0x0B, 0x81, 0x0E, 0x88, 0x07, 0x81, 0x0E, 0x82, 0x0D,
|
||||||
|
0x81, 0x05, 0x81, 0x02, 0x84, 0x01, 0x84, 0x03, 0x84, 0x0E, 0x88, 0x08,
|
||||||
|
0x88, 0x07, 0x84, 0x0D, 0x81, 0x07, 0x81, 0x03, 0x84, 0x0B, 0x84, 0x02,
|
||||||
|
0x81, 0x06, 0x88, 0x09, 0x81, 0x0C, 0x84, 0x0B, 0x81, 0x0B, 0x81, 0x07,
|
||||||
|
0x82, 0x06, 0x81, 0x04, 0x84, 0x09, 0x84, 0x04, 0x88, 0x05, 0x90, 0x04,
|
||||||
|
0x84, 0x0D, 0x84, 0x04, 0x88, 0x0C, 0x90, 0x07, 0x81, 0x09, 0x90, 0x0B,
|
||||||
|
0x81, 0x0A, 0x88, 0x0C, 0x88, 0x02, 0x81, 0x01, 0x81, 0x06, 0x84, 0x0D,
|
||||||
|
0x90, 0x0E, 0x84, 0x05, 0x84, 0x03, 0x81, 0x09, 0x81, 0x02, 0x81, 0x04,
|
||||||
|
0x81, 0x0C, 0x81, 0x01, 0x90, 0x05, 0x91, 0x0B, 0x84, 0x0A, 0x81, 0x0A,
|
||||||
|
0x81, 0x01, 0x81, 0x02, 0x82, 0x01, 0x84, 0x0C, 0x90, 0x0E, 0x84, 0x04,
|
||||||
|
0x88, 0x0E, 0x82, 0x03, 0x88, 0x02, 0x88, 0x0E, 0x82, 0x0B, 0x82, 0x01,
|
||||||
|
0x90, 0x02, 0x88, 0x09, 0x81, 0x0B, 0x88, 0x07, 0x81, 0x04, 0x88, 0x05,
|
||||||
|
0x81, 0x0D, 0x81, 0x0C, 0x88, 0x04, 0x82, 0x0D, 0x81, 0x0E, 0x88, 0x06,
|
||||||
|
0x81, 0x01, 0x84, 0x0C, 0x81, 0x0E, 0x81, 0x0A, 0x82, 0x07, 0x82, 0x0A,
|
||||||
|
0x84, 0x02, 0x90, 0x0B, 0x81, 0x05, 0x84, 0x0B, 0x82, 0x03, 0x81, 0x06,
|
||||||
|
0x84, 0x0D, 0x81, 0x0C, 0x81, 0x05, 0x81, 0x06, 0x88, 0x0E, 0x84, 0x0A,
|
||||||
|
0x90, 0x0A, 0x81, 0x0A, 0x90, 0x05, 0x82, 0x05, 0x84, 0x06, 0x84, 0x06,
|
||||||
|
0x81, 0x0B, 0x81, 0x05, 0x88, 0x0E, 0x84, 0x0E, 0x81, 0x0E, 0x81, 0x0C,
|
||||||
|
0x81, 0x05, 0x90, 0x0E, 0x81, 0x0A, 0x88, 0x0C, 0x8A, 0x05, 0x84, 0x03,
|
||||||
|
0x81, 0x04, 0x88, 0x02, 0x80, 0x06, 0x84, 0x05, 0x88, 0x06, 0x84, 0x07,
|
||||||
|
0x82, 0x0B, 0x82, 0x07, 0x82, 0x07, 0x89, 0x05, 0x88, 0x02, 0x81, 0x04,
|
||||||
|
0x81, 0x01, 0x88, 0x09, 0x81, 0x0B, 0x84, 0x01, 0x81, 0x0D, 0x84, 0x01,
|
||||||
|
0x81, 0x0C, 0x84, 0x0E, 0x88, 0x09, 0x81, 0x08, 0x90, 0x09, 0x82, 0x09,
|
||||||
|
0x81, 0x05, 0x84, 0x0C, 0x81, 0x0B, 0x81, 0x0C, 0x90, 0x09, 0x82, 0x0E,
|
||||||
|
0x81, 0x0E, 0x82, 0x09, 0x81, 0x04, 0x81, 0x02, 0x88, 0x08, 0x84, 0x06,
|
||||||
|
0x90, 0x06, 0x81, 0x09, 0x82, 0x06, 0x81, 0x01,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level F -- 1400 ticks, 8 hook bytes, 10 fare stages, 256 pictures, 5 landings
|
||||||
|
// regenerate: tools/stsim/recorder levels/level06.dat F 20261006 60 2000
|
||||||
|
static const uint8_t kGateStreamF[320] = {
|
||||||
|
0x81, 0x02, 0x88, 0x02, 0x84, 0x09, 0x89, 0x03, 0x81, 0x01, 0x82, 0x05,
|
||||||
|
0x81, 0x04, 0x80, 0x0D, 0x84, 0x08, 0x82, 0x05, 0x84, 0x09, 0x88, 0x07,
|
||||||
|
0x88, 0x06, 0x81, 0x0A, 0x90, 0x0E, 0x82, 0x07, 0x81, 0x07, 0x88, 0x0E,
|
||||||
|
0x88, 0x0B, 0x88, 0x08, 0x82, 0x0C, 0x84, 0x0B, 0x81, 0x08, 0x81, 0x0B,
|
||||||
|
0x90, 0x02, 0x88, 0x0E, 0x88, 0x0E, 0x90, 0x04, 0x81, 0x05, 0x84, 0x06,
|
||||||
|
0x88, 0x01, 0x88, 0x03, 0x81, 0x01, 0x88, 0x04, 0x81, 0x0E, 0x88, 0x04,
|
||||||
|
0x88, 0x08, 0x82, 0x08, 0x84, 0x0E, 0x88, 0x04, 0x90, 0x0E, 0x81, 0x0C,
|
||||||
|
0x90, 0x06, 0x84, 0x0B, 0x82, 0x06, 0x88, 0x03, 0x81, 0x01, 0x81, 0x05,
|
||||||
|
0x81, 0x03, 0x81, 0x06, 0x88, 0x0A, 0x88, 0x01, 0x90, 0x0C, 0x88, 0x02,
|
||||||
|
0x81, 0x06, 0x81, 0x03, 0x81, 0x03, 0x88, 0x02, 0x82, 0x0B, 0x90, 0x09,
|
||||||
|
0x88, 0x08, 0x84, 0x0E, 0x90, 0x01, 0x84, 0x0D, 0x81, 0x08, 0x88, 0x05,
|
||||||
|
0x90, 0x07, 0x81, 0x08, 0x84, 0x05, 0x82, 0x07, 0x90, 0x02, 0x90, 0x06,
|
||||||
|
0x81, 0x01, 0x82, 0x03, 0x88, 0x09, 0x88, 0x07, 0x82, 0x0D, 0x84, 0x08,
|
||||||
|
0x81, 0x0B, 0x81, 0x01, 0x81, 0x0E, 0x81, 0x0D, 0x81, 0x0B, 0x82, 0x0C,
|
||||||
|
0x84, 0x0A, 0x82, 0x0E, 0x88, 0x01, 0x81, 0x05, 0x90, 0x0E, 0x81, 0x06,
|
||||||
|
0x82, 0x09, 0x90, 0x06, 0x81, 0x09, 0x81, 0x03, 0x82, 0x04, 0x82, 0x0D,
|
||||||
|
0x84, 0x0E, 0x88, 0x08, 0x88, 0x07, 0x81, 0x0B, 0x81, 0x0A, 0x81, 0x05,
|
||||||
|
0x84, 0x0A, 0x88, 0x0A, 0x84, 0x0D, 0x82, 0x07, 0x81, 0x08, 0x82, 0x0B,
|
||||||
|
0x81, 0x08, 0x88, 0x0D, 0x88, 0x0B, 0x88, 0x06, 0x84, 0x0D, 0x82, 0x0C,
|
||||||
|
0x88, 0x06, 0x90, 0x02, 0x81, 0x03, 0x81, 0x0E, 0x81, 0x0A, 0x88, 0x0A,
|
||||||
|
0x90, 0x08, 0x81, 0x09, 0x81, 0x05, 0x82, 0x08, 0x84, 0x09, 0x88, 0x01,
|
||||||
|
0x81, 0x02, 0x84, 0x0D, 0x8A, 0x09, 0x81, 0x01, 0x84, 0x02, 0x81, 0x02,
|
||||||
|
0x90, 0x0E, 0x81, 0x0D, 0x82, 0x05, 0x8A, 0x0E, 0x81, 0x0A, 0x81, 0x0A,
|
||||||
|
0x84, 0x02, 0x81, 0x0A, 0x84, 0x04, 0x82, 0x0D, 0x81, 0x0A, 0x82, 0x06,
|
||||||
|
0x84, 0x06, 0x81, 0x02, 0x84, 0x0C, 0x81, 0x0C, 0x81, 0x03, 0x81, 0x05,
|
||||||
|
0x84, 0x03, 0x82, 0x08, 0x88, 0x06, 0x82, 0x0C, 0x84, 0x01, 0x88, 0x08,
|
||||||
|
0x88, 0x0A, 0x81, 0x03, 0x90, 0x0E, 0x84, 0x03,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level G -- 1400 ticks, 5 hook bytes, 10 fare stages, 256 pictures, 11 landings
|
||||||
|
// regenerate: tools/stsim/recorder levels/level07.dat G 20261006 60 2000
|
||||||
static const uint8_t kGateStreamG[320] = {
|
static const uint8_t kGateStreamG[320] = {
|
||||||
0x88, 0x05, 0x81, 0x09, 0x81, 0x05, 0x81, 0x04, 0x81, 0x06, 0x84, 0x0C,
|
0x81, 0x04, 0x88, 0x04, 0x81, 0x01, 0x81, 0x03, 0x88, 0x01, 0x88, 0x06,
|
||||||
0x81, 0x01, 0x89, 0x02, 0x81, 0x09, 0x82, 0x0D, 0x88, 0x05, 0x81, 0x03,
|
0x81, 0x04, 0x88, 0x0D, 0x84, 0x0D, 0x84, 0x03, 0x84, 0x08, 0x81, 0x07,
|
||||||
0x80, 0x09, 0x81, 0x01, 0x90, 0x02, 0x81, 0x04, 0x90, 0x08, 0x81, 0x03,
|
0x88, 0x06, 0x81, 0x0B, 0x81, 0x0E, 0x88, 0x01, 0x84, 0x0E, 0x81, 0x0D,
|
||||||
0x90, 0x0A, 0x88, 0x0E, 0x88, 0x0C, 0x90, 0x0C, 0x81, 0x04, 0x81, 0x0D,
|
0x84, 0x06, 0x81, 0x03, 0x82, 0x08, 0x81, 0x06, 0x84, 0x0B, 0x81, 0x0A,
|
||||||
0x90, 0x0A, 0x81, 0x0D, 0x84, 0x0E, 0x81, 0x09, 0x81, 0x08, 0x81, 0x0C,
|
0x84, 0x02, 0x88, 0x06, 0x81, 0x01, 0x81, 0x09, 0x88, 0x0E, 0x81, 0x0A,
|
||||||
0x82, 0x04, 0x81, 0x02, 0x81, 0x0E, 0x81, 0x02, 0x84, 0x01, 0x88, 0x0A,
|
0x81, 0x03, 0x84, 0x05, 0x88, 0x0A, 0x81, 0x01, 0x84, 0x07, 0x88, 0x08,
|
||||||
0x88, 0x05, 0x90, 0x09, 0x81, 0x02, 0x84, 0x09, 0x84, 0x05, 0x81, 0x0D,
|
0x81, 0x0A, 0x81, 0x03, 0x81, 0x09, 0x81, 0x02, 0x81, 0x01, 0x81, 0x09,
|
||||||
0x84, 0x09, 0x88, 0x03, 0x84, 0x06, 0x84, 0x0D, 0x81, 0x05, 0x84, 0x0C,
|
0x81, 0x01, 0x81, 0x0C, 0x82, 0x0A, 0x90, 0x0D, 0x88, 0x0E, 0x81, 0x0D,
|
||||||
0x88, 0x0C, 0x88, 0x0C, 0x88, 0x03, 0x88, 0x09, 0x81, 0x0A, 0x81, 0x09,
|
0x84, 0x03, 0x88, 0x09, 0x84, 0x0D, 0x88, 0x0D, 0x81, 0x05, 0x88, 0x03,
|
||||||
0x81, 0x06, 0x81, 0x07, 0x81, 0x05, 0x88, 0x09, 0x88, 0x02, 0x81, 0x0B,
|
0x81, 0x08, 0x90, 0x0E, 0x81, 0x02, 0x88, 0x0D, 0x82, 0x06, 0x88, 0x08,
|
||||||
0x84, 0x0A, 0x84, 0x05, 0x88, 0x03, 0x81, 0x05, 0x81, 0x0B, 0x81, 0x05,
|
0x81, 0x06, 0x82, 0x03, 0x81, 0x01, 0x88, 0x0D, 0x81, 0x05, 0x81, 0x07,
|
||||||
0x90, 0x05, 0x84, 0x09, 0x81, 0x06, 0x81, 0x07, 0x90, 0x0D, 0x81, 0x04,
|
0x81, 0x0B, 0x90, 0x0E, 0x84, 0x01, 0x81, 0x0B, 0x82, 0x0A, 0x90, 0x08,
|
||||||
0x81, 0x0E, 0x88, 0x0D, 0x88, 0x0D, 0x88, 0x06, 0x82, 0x02, 0x88, 0x0D,
|
0x84, 0x02, 0x82, 0x0C, 0x88, 0x05, 0x84, 0x08, 0x81, 0x0D, 0x81, 0x0B,
|
||||||
0x90, 0x0E, 0x81, 0x04, 0x81, 0x0B, 0x84, 0x0B, 0x81, 0x05, 0x84, 0x07,
|
0x84, 0x0D, 0x88, 0x0B, 0x84, 0x0C, 0x81, 0x07, 0x88, 0x07, 0x88, 0x08,
|
||||||
0x88, 0x0A, 0x80, 0x04, 0x84, 0x07, 0x84, 0x0B, 0x88, 0x0D, 0x90, 0x0E,
|
0x81, 0x05, 0x90, 0x0A, 0x90, 0x0C, 0x82, 0x09, 0x81, 0x07, 0x81, 0x0A,
|
||||||
0x90, 0x06, 0x88, 0x08, 0x82, 0x0C, 0x81, 0x02, 0x81, 0x0E, 0x88, 0x0A,
|
0x82, 0x0B, 0x88, 0x0D, 0x81, 0x01, 0x90, 0x04, 0x90, 0x01, 0x81, 0x0B,
|
||||||
0x90, 0x0B, 0x81, 0x04, 0x88, 0x08, 0x90, 0x07, 0x84, 0x03, 0x82, 0x08,
|
0x82, 0x05, 0x84, 0x07, 0x81, 0x08, 0x80, 0x04, 0x88, 0x04, 0x81, 0x0A,
|
||||||
0x81, 0x05, 0x81, 0x03, 0x81, 0x03, 0x81, 0x06, 0x90, 0x0B, 0x84, 0x0E,
|
0x89, 0x02, 0x88, 0x03, 0x82, 0x05, 0x90, 0x0D, 0x90, 0x0E, 0x81, 0x0B,
|
||||||
0x82, 0x0E, 0x88, 0x08, 0x88, 0x04, 0x81, 0x0D, 0x88, 0x04, 0x81, 0x08,
|
0x84, 0x01, 0x82, 0x0A, 0x83, 0x04, 0x81, 0x09, 0x84, 0x0C, 0x81, 0x0E,
|
||||||
0x88, 0x0C, 0x81, 0x06, 0x81, 0x04, 0x90, 0x0B, 0x81, 0x08, 0x84, 0x04,
|
0x81, 0x01, 0x82, 0x0D, 0x81, 0x0C, 0x81, 0x03, 0x82, 0x01, 0x81, 0x0C,
|
||||||
0x90, 0x08, 0x84, 0x09, 0x88, 0x09, 0x81, 0x0B, 0x81, 0x07, 0x84, 0x0D,
|
0x88, 0x05, 0x81, 0x08, 0x88, 0x0C, 0x82, 0x0E, 0x84, 0x0B, 0x81, 0x0B,
|
||||||
0x88, 0x0A, 0x88, 0x09, 0x90, 0x0B, 0x90, 0x02, 0x84, 0x0D, 0x81, 0x0E,
|
0x88, 0x0C, 0x81, 0x0C, 0x81, 0x05, 0x84, 0x08, 0x82, 0x02, 0x82, 0x0A,
|
||||||
0x90, 0x0D, 0x84, 0x07, 0x82, 0x0C, 0x81, 0x0E, 0x81, 0x06, 0x88, 0x09,
|
0x81, 0x0C, 0x84, 0x0B, 0x81, 0x02, 0x90, 0x08, 0x81, 0x06, 0x90, 0x0E,
|
||||||
0x90, 0x02, 0x90, 0x04, 0x81, 0x09, 0x84, 0x0A, 0x90, 0x06, 0x81, 0x04,
|
0x88, 0x03, 0x82, 0x0B, 0x84, 0x0E, 0x84, 0x0B, 0x84, 0x0D, 0x88, 0x04,
|
||||||
0x90, 0x03, 0x90, 0x08, 0x84, 0x0B, 0x81, 0x02, 0x90, 0x04, 0x84, 0x03,
|
0x84, 0x05, 0x81, 0x06, 0x84, 0x06, 0x90, 0x0D, 0x90, 0x0C, 0x88, 0x03,
|
||||||
0x81, 0x03, 0x84, 0x05, 0x90, 0x0C, 0x81, 0x01, 0x90, 0x0D, 0x81, 0x02,
|
0x81, 0x0E, 0x82, 0x02, 0x84, 0x05, 0x90, 0x0B, 0x82, 0x08, 0x84, 0x09,
|
||||||
0x90, 0x05, 0x90, 0x0E, 0x81, 0x03, 0x90, 0x0A,
|
0x81, 0x07, 0x84, 0x0D, 0x81, 0x02, 0x90, 0x04,
|
||||||
};
|
};
|
||||||
|
|
||||||
// level I -- 1400 ticks, 20 hook bytes, 10 fare stages, 256 pictures, 22 landings
|
// level I -- 1400 ticks, 20 hook bytes, 10 fare stages, 256 pictures, 4 landings
|
||||||
// regenerate: tools/stsim/recorder levels/level??.dat I 20260918
|
// regenerate: tools/stsim/recorder levels/level09.dat I 20261006 60 2000
|
||||||
static const uint8_t kGateStreamI[320] = {
|
static const uint8_t kGateStreamI[320] = {
|
||||||
0x84, 0x0E, 0x81, 0x06, 0x84, 0x0E, 0x88, 0x0B, 0x88, 0x05, 0x81, 0x03,
|
0x81, 0x04, 0x88, 0x02, 0x89, 0x01, 0x89, 0x02, 0x81, 0x01, 0x88, 0x05,
|
||||||
0x82, 0x07, 0x90, 0x0A, 0x81, 0x08, 0x88, 0x0E, 0x88, 0x0E, 0x90, 0x0B,
|
0x81, 0x04, 0x80, 0x0E, 0x84, 0x06, 0x82, 0x05, 0x84, 0x08, 0x88, 0x07,
|
||||||
0x82, 0x0D, 0x81, 0x01, 0x81, 0x0A, 0x90, 0x0A, 0x82, 0x03, 0x81, 0x07,
|
0x88, 0x06, 0x81, 0x0B, 0x82, 0x05, 0x82, 0x07, 0x81, 0x0E, 0x81, 0x02,
|
||||||
0x82, 0x05, 0x81, 0x0B, 0x81, 0x05, 0x82, 0x05, 0x82, 0x0C, 0x81, 0x08,
|
0x81, 0x03, 0x82, 0x03, 0x82, 0x0C, 0x88, 0x06, 0x84, 0x0B, 0x84, 0x0E,
|
||||||
0x88, 0x0C, 0x90, 0x04, 0x81, 0x02, 0x88, 0x08, 0x81, 0x03, 0x81, 0x05,
|
0x81, 0x02, 0x81, 0x06, 0x81, 0x02, 0x84, 0x09, 0x90, 0x0E, 0x81, 0x02,
|
||||||
0x81, 0x0A, 0x88, 0x0E, 0x81, 0x06, 0x84, 0x0E, 0x82, 0x07, 0x82, 0x01,
|
0x81, 0x07, 0x81, 0x02, 0x90, 0x08, 0x81, 0x02, 0x84, 0x04, 0x88, 0x09,
|
||||||
0x81, 0x04, 0x82, 0x08, 0x90, 0x0C, 0x90, 0x05, 0x81, 0x08, 0x82, 0x0D,
|
0x88, 0x05, 0x81, 0x0C, 0x88, 0x0E, 0x84, 0x0E, 0x88, 0x08, 0x90, 0x09,
|
||||||
0x90, 0x06, 0x81, 0x0A, 0x84, 0x0B, 0x84, 0x0C, 0x84, 0x0C, 0x88, 0x09,
|
0x90, 0x05, 0x84, 0x09, 0x82, 0x0A, 0x90, 0x0C, 0x88, 0x07, 0x88, 0x09,
|
||||||
0x81, 0x06, 0x81, 0x0C, 0x81, 0x04, 0x82, 0x09, 0x88, 0x05, 0x82, 0x0E,
|
0x84, 0x05, 0x81, 0x04, 0x81, 0x02, 0x81, 0x03, 0x81, 0x08, 0x88, 0x0B,
|
||||||
0x81, 0x0C, 0x81, 0x07, 0x88, 0x01, 0x90, 0x0E, 0x81, 0x02, 0x90, 0x0E,
|
0x90, 0x05, 0x81, 0x0A, 0x81, 0x08, 0x82, 0x0E, 0x82, 0x09, 0x90, 0x09,
|
||||||
0x81, 0x02, 0x90, 0x07, 0x84, 0x05, 0x84, 0x06, 0x88, 0x06, 0x88, 0x0C,
|
0x81, 0x07, 0x81, 0x07, 0x81, 0x08, 0x88, 0x0D, 0x82, 0x0C, 0x81, 0x08,
|
||||||
0x81, 0x0C, 0x84, 0x0B, 0x82, 0x09, 0x81, 0x05, 0x81, 0x0B, 0x82, 0x09,
|
0x81, 0x02, 0x81, 0x03, 0x82, 0x04, 0x84, 0x0B, 0x81, 0x05, 0x88, 0x02,
|
||||||
0x90, 0x0E, 0x90, 0x06, 0x88, 0x0D, 0x84, 0x03, 0x82, 0x0B, 0x84, 0x0D,
|
0x88, 0x07, 0x90, 0x0D, 0x88, 0x0B, 0x84, 0x02, 0x81, 0x02, 0x84, 0x09,
|
||||||
0x90, 0x0B, 0x90, 0x04, 0x81, 0x0B, 0x81, 0x08, 0x88, 0x08, 0x90, 0x0A,
|
0x88, 0x0C, 0x81, 0x06, 0x88, 0x09, 0x81, 0x02, 0x84, 0x0A, 0x88, 0x06,
|
||||||
0x81, 0x03, 0x81, 0x01, 0x81, 0x0E, 0x81, 0x04, 0x88, 0x02, 0x90, 0x05,
|
0x88, 0x0C, 0x81, 0x05, 0x82, 0x03, 0x88, 0x02, 0x81, 0x06, 0x81, 0x08,
|
||||||
0x81, 0x07, 0x81, 0x01, 0x82, 0x09, 0x90, 0x06, 0x81, 0x09, 0x90, 0x0E,
|
0x82, 0x03, 0x82, 0x0E, 0x81, 0x03, 0x81, 0x02, 0x81, 0x0C, 0x82, 0x0B,
|
||||||
0x88, 0x05, 0x82, 0x0D, 0x88, 0x03, 0x81, 0x06, 0x84, 0x0C, 0x82, 0x01,
|
0x84, 0x05, 0x88, 0x0C, 0x84, 0x0C, 0x81, 0x07, 0x81, 0x06, 0x88, 0x07,
|
||||||
0x81, 0x09, 0x82, 0x0E, 0x90, 0x01, 0x81, 0x01, 0x84, 0x05, 0x82, 0x06,
|
0x88, 0x0C, 0x90, 0x0E, 0x90, 0x03, 0x81, 0x0C, 0x84, 0x0C, 0x81, 0x03,
|
||||||
0x88, 0x0A, 0x81, 0x05, 0x82, 0x03, 0x90, 0x07, 0x90, 0x08, 0x81, 0x02,
|
0x89, 0x01, 0x88, 0x0D, 0x81, 0x05, 0x81, 0x06, 0x90, 0x04, 0x83, 0x0D,
|
||||||
0x90, 0x0D, 0x88, 0x0D, 0x81, 0x08, 0x90, 0x0E, 0x82, 0x0D, 0x88, 0x04,
|
0x81, 0x01, 0x88, 0x05, 0x84, 0x05, 0x81, 0x01, 0x84, 0x09, 0x81, 0x02,
|
||||||
0x81, 0x0C, 0x82, 0x09, 0x90, 0x04, 0x81, 0x0C, 0x82, 0x06, 0x90, 0x0E,
|
0x90, 0x05, 0x81, 0x0D, 0x80, 0x09, 0x81, 0x0C, 0x88, 0x02, 0x84, 0x0C,
|
||||||
0x90, 0x0D, 0x90, 0x07, 0x90, 0x03, 0x82, 0x03, 0x88, 0x06, 0x81, 0x07,
|
0x82, 0x0E, 0x82, 0x0C, 0x90, 0x02, 0x81, 0x07, 0x88, 0x04, 0x88, 0x0D,
|
||||||
0x90, 0x08, 0x81, 0x02, 0x90, 0x09, 0x81, 0x04, 0x90, 0x06, 0x82, 0x0A,
|
0x88, 0x09, 0x81, 0x0D, 0x88, 0x0C, 0x88, 0x0E, 0x88, 0x0E, 0x82, 0x0B,
|
||||||
0x90, 0x05, 0x88, 0x05, 0x88, 0x06, 0x90, 0x0B, 0x81, 0x03, 0x82, 0x02,
|
0x82, 0x0B, 0x81, 0x0C, 0x82, 0x0C, 0x90, 0x0B, 0x81, 0x0A, 0x84, 0x08,
|
||||||
0x90, 0x0B, 0x81, 0x02, 0x90, 0x0C, 0x90, 0x07, 0x81, 0x01, 0x84, 0x08,
|
0x81, 0x08, 0x82, 0x0B, 0x81, 0x09, 0x84, 0x09, 0x81, 0x0B, 0x81, 0x03,
|
||||||
0x90, 0x0C, 0x81, 0x0E, 0x88, 0x08, 0x88, 0x0C, 0x81, 0x0C, 0x90, 0x04,
|
0x8A, 0x0D, 0x88, 0x0B, 0x90, 0x04, 0x81, 0x0E, 0x88, 0x07, 0x81, 0x08,
|
||||||
0x84, 0x0D, 0x81, 0x01, 0x88, 0x08, 0x88, 0x09,
|
0x88, 0x03, 0x80, 0x0E, 0x84, 0x05, 0x84, 0x07,
|
||||||
};
|
};
|
||||||
|
|
||||||
// level K -- 1400 ticks, 1 hook bytes, 10 fare stages, 256 pictures, 16 landings
|
// level J -- 1400 ticks, 10 hook bytes, 10 fare stages, 256 pictures, 6 landings
|
||||||
// regenerate: tools/stsim/recorder levels/level??.dat K 11
|
// regenerate: tools/stsim/recorder levels/level10.dat J 20261006 60 2000
|
||||||
|
static const uint8_t kGateStreamJ[320] = {
|
||||||
|
0x81, 0x04, 0x88, 0x02, 0x84, 0x01, 0x81, 0x02, 0x90, 0x01, 0x82, 0x05,
|
||||||
|
0x81, 0x04, 0x88, 0x0D, 0x88, 0x0C, 0x82, 0x0C, 0x81, 0x03, 0x84, 0x0A,
|
||||||
|
0x81, 0x06, 0x84, 0x04, 0x88, 0x05, 0x88, 0x08, 0x81, 0x0E, 0x88, 0x06,
|
||||||
|
0x81, 0x0A, 0x81, 0x05, 0x81, 0x0C, 0x84, 0x0D, 0x81, 0x09, 0x90, 0x0D,
|
||||||
|
0x81, 0x0A, 0x81, 0x09, 0x84, 0x0E, 0x88, 0x0C, 0x88, 0x03, 0x88, 0x07,
|
||||||
|
0x88, 0x0D, 0x82, 0x04, 0x82, 0x0D, 0x88, 0x0B, 0x81, 0x03, 0x81, 0x0A,
|
||||||
|
0x81, 0x0A, 0x81, 0x0C, 0x81, 0x05, 0x82, 0x04, 0x88, 0x0E, 0x82, 0x0C,
|
||||||
|
0x82, 0x09, 0x88, 0x07, 0x88, 0x0B, 0x81, 0x09, 0x82, 0x03, 0x81, 0x0A,
|
||||||
|
0x81, 0x03, 0x88, 0x0C, 0x88, 0x05, 0x81, 0x04, 0x81, 0x01, 0x81, 0x0E,
|
||||||
|
0x90, 0x07, 0x84, 0x01, 0x84, 0x0C, 0x88, 0x05, 0x88, 0x0C, 0x90, 0x0D,
|
||||||
|
0x90, 0x0C, 0x82, 0x09, 0x90, 0x03, 0x81, 0x07, 0x90, 0x0C, 0x81, 0x04,
|
||||||
|
0x81, 0x04, 0x81, 0x0B, 0x88, 0x0C, 0x90, 0x0B, 0x88, 0x0C, 0x81, 0x0C,
|
||||||
|
0x82, 0x0A, 0x82, 0x07, 0x81, 0x0A, 0x81, 0x01, 0x84, 0x07, 0x82, 0x09,
|
||||||
|
0x84, 0x0E, 0x90, 0x01, 0x84, 0x01, 0x81, 0x0B, 0x81, 0x05, 0x88, 0x01,
|
||||||
|
0x82, 0x02, 0x81, 0x05, 0x81, 0x05, 0x80, 0x0E, 0x84, 0x09, 0x90, 0x0D,
|
||||||
|
0x90, 0x0B, 0x81, 0x05, 0x81, 0x08, 0x84, 0x03, 0x81, 0x08, 0x88, 0x0D,
|
||||||
|
0x88, 0x0A, 0x81, 0x0A, 0x82, 0x06, 0x88, 0x06, 0x84, 0x0B, 0x81, 0x06,
|
||||||
|
0x90, 0x0B, 0x81, 0x07, 0x82, 0x0E, 0x84, 0x0A, 0x81, 0x03, 0x82, 0x01,
|
||||||
|
0x88, 0x09, 0x82, 0x01, 0x82, 0x08, 0x81, 0x06, 0x81, 0x0C, 0x84, 0x07,
|
||||||
|
0x90, 0x08, 0x81, 0x02, 0x84, 0x0B, 0x84, 0x04, 0x84, 0x0D, 0x81, 0x06,
|
||||||
|
0x81, 0x07, 0x88, 0x0E, 0x88, 0x04, 0x82, 0x02, 0x90, 0x09, 0x88, 0x03,
|
||||||
|
0x81, 0x04, 0x90, 0x0C, 0x81, 0x0B, 0x82, 0x0C, 0x81, 0x0B, 0x81, 0x0E,
|
||||||
|
0x82, 0x0E, 0x81, 0x0D, 0x84, 0x0B, 0x81, 0x0E, 0x88, 0x0E, 0x84, 0x0C,
|
||||||
|
0x84, 0x03, 0x90, 0x0D, 0x82, 0x0A, 0x82, 0x07, 0x90, 0x0C, 0x82, 0x0A,
|
||||||
|
0x81, 0x06, 0x81, 0x06, 0x84, 0x08, 0x88, 0x07, 0x90, 0x05, 0x81, 0x01,
|
||||||
|
0x81, 0x03, 0x82, 0x03, 0x82, 0x0D, 0x84, 0x0B, 0x81, 0x06, 0x90, 0x0A,
|
||||||
|
0x88, 0x03, 0x90, 0x04, 0x81, 0x04, 0x90, 0x06,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level K -- searched 2026-09 by the recorder of then (seed 11), from a
|
||||||
|
// fresh game's first screen; kept because, ridden as the shift reaches
|
||||||
|
// the screen, it covers more than the 2026-10-06 search did
|
||||||
static const uint8_t kGateStreamK[320] = {
|
static const uint8_t kGateStreamK[320] = {
|
||||||
0x84, 0x06, 0x82, 0x0B, 0x90, 0x0C, 0x82, 0x0E, 0x90, 0x05, 0x88, 0x03,
|
0x84, 0x06, 0x82, 0x0B, 0x90, 0x0C, 0x82, 0x0E, 0x90, 0x05, 0x88, 0x03,
|
||||||
0x88, 0x02, 0x82, 0x0E, 0x88, 0x07, 0x84, 0x02, 0x84, 0x0B, 0x84, 0x08,
|
0x88, 0x02, 0x82, 0x0E, 0x88, 0x07, 0x84, 0x02, 0x84, 0x0B, 0x84, 0x08,
|
||||||
|
|
@ -114,8 +338,105 @@ static const uint8_t kGateStreamK[320] = {
|
||||||
0x82, 0x09, 0x90, 0x01, 0x88, 0x07, 0x88, 0x08,
|
0x82, 0x09, 0x90, 0x01, 0x88, 0x07, 0x88, 0x08,
|
||||||
};
|
};
|
||||||
|
|
||||||
// level O -- 1400 ticks, 1 hook bytes, 3 fare stages, 256 pictures, 0 landings
|
// level L -- 1400 ticks, 0 hook bytes, 10 fare stages, 256 pictures, 4 landings
|
||||||
// regenerate: tools/stsim/recorder levels/level??.dat O 20260918
|
// regenerate: tools/stsim/recorder levels/level12.dat L 20261006 60 2000
|
||||||
|
static const uint8_t kGateStreamL[320] = {
|
||||||
|
0x81, 0x04, 0x84, 0x02, 0x81, 0x01, 0x81, 0x02, 0x90, 0x01, 0x88, 0x05,
|
||||||
|
0x81, 0x04, 0x88, 0x0E, 0x84, 0x06, 0x88, 0x01, 0x88, 0x08, 0x88, 0x07,
|
||||||
|
0x88, 0x06, 0x81, 0x0B, 0x84, 0x0E, 0x82, 0x07, 0x81, 0x0C, 0x88, 0x06,
|
||||||
|
0x81, 0x0A, 0x81, 0x07, 0x81, 0x0C, 0x81, 0x0D, 0x81, 0x09, 0x84, 0x0B,
|
||||||
|
0x86, 0x0E, 0x81, 0x0E, 0x84, 0x0E, 0x81, 0x0A, 0x84, 0x05, 0x88, 0x06,
|
||||||
|
0x84, 0x09, 0x88, 0x01, 0x82, 0x0D, 0x84, 0x0D, 0x81, 0x06, 0x88, 0x09,
|
||||||
|
0x84, 0x0E, 0x82, 0x08, 0x82, 0x04, 0x82, 0x04, 0x81, 0x09, 0x88, 0x0D,
|
||||||
|
0x90, 0x05, 0x88, 0x0D, 0x90, 0x0B, 0x84, 0x0C, 0x88, 0x0D, 0x88, 0x09,
|
||||||
|
0x84, 0x0A, 0x82, 0x0B, 0x84, 0x06, 0x88, 0x0D, 0x88, 0x07, 0x88, 0x09,
|
||||||
|
0x88, 0x04, 0x81, 0x0B, 0x81, 0x0B, 0x88, 0x0C, 0x82, 0x05, 0x88, 0x0C,
|
||||||
|
0x81, 0x05, 0x84, 0x09, 0x84, 0x03, 0x88, 0x04, 0x81, 0x05, 0x90, 0x03,
|
||||||
|
0x90, 0x0A, 0x82, 0x0E, 0x84, 0x06, 0x90, 0x07, 0x90, 0x0C, 0x81, 0x06,
|
||||||
|
0x82, 0x08, 0x81, 0x09, 0x81, 0x08, 0x81, 0x01, 0x81, 0x07, 0x81, 0x07,
|
||||||
|
0x90, 0x0E, 0x90, 0x01, 0x81, 0x07, 0x81, 0x0B, 0x81, 0x0C, 0x84, 0x0C,
|
||||||
|
0x82, 0x0B, 0x82, 0x09, 0x88, 0x05, 0x80, 0x08, 0x90, 0x06, 0x81, 0x04,
|
||||||
|
0x81, 0x05, 0x81, 0x0C, 0x81, 0x0A, 0x88, 0x03, 0x81, 0x08, 0x90, 0x0D,
|
||||||
|
0x84, 0x09, 0x81, 0x0B, 0x82, 0x06, 0x81, 0x03, 0x81, 0x0B, 0x81, 0x0E,
|
||||||
|
0x90, 0x0A, 0x81, 0x0E, 0x84, 0x0E, 0x81, 0x0A, 0x81, 0x0C, 0x82, 0x01,
|
||||||
|
0x88, 0x02, 0x90, 0x01, 0x82, 0x06, 0x81, 0x07, 0x81, 0x0C, 0x90, 0x02,
|
||||||
|
0x82, 0x0D, 0x81, 0x0E, 0x82, 0x08, 0x81, 0x09, 0x81, 0x08, 0x81, 0x08,
|
||||||
|
0x81, 0x0E, 0x81, 0x07, 0x80, 0x05, 0x84, 0x0B, 0x84, 0x0B, 0x81, 0x01,
|
||||||
|
0x84, 0x0D, 0x8C, 0x06, 0x84, 0x08, 0x81, 0x07, 0x81, 0x0B, 0x88, 0x06,
|
||||||
|
0x81, 0x06, 0x81, 0x0C, 0x81, 0x03, 0x84, 0x08, 0x81, 0x0C, 0x81, 0x05,
|
||||||
|
0x88, 0x03, 0x82, 0x07, 0x88, 0x0C, 0x82, 0x05, 0x88, 0x0A, 0x81, 0x05,
|
||||||
|
0x89, 0x0B, 0x81, 0x06, 0x86, 0x0D, 0x81, 0x0D, 0x90, 0x0A, 0x81, 0x0A,
|
||||||
|
0x88, 0x0A, 0x81, 0x02, 0x90, 0x08, 0x81, 0x02, 0x90, 0x05, 0x84, 0x09,
|
||||||
|
0x81, 0x06, 0x81, 0x06, 0x88, 0x02, 0x82, 0x02,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level M -- 1400 ticks, 0 hook bytes, 8 fare stages, 256 pictures, 9 landings
|
||||||
|
// regenerate: tools/stsim/recorder levels/level13.dat M 20261006 60 2000
|
||||||
|
static const uint8_t kGateStreamM[320] = {
|
||||||
|
0x88, 0x06, 0x88, 0x06, 0x84, 0x04, 0x89, 0x03, 0x90, 0x09, 0x82, 0x05,
|
||||||
|
0x81, 0x04, 0x88, 0x0E, 0x81, 0x07, 0x84, 0x0A, 0x84, 0x09, 0x90, 0x08,
|
||||||
|
0x84, 0x01, 0x90, 0x03, 0x81, 0x08, 0x82, 0x0A, 0x81, 0x03, 0x84, 0x06,
|
||||||
|
0x88, 0x02, 0x81, 0x04, 0x81, 0x02, 0x90, 0x0E, 0x84, 0x0E, 0x84, 0x0B,
|
||||||
|
0x90, 0x0E, 0x84, 0x0E, 0x90, 0x06, 0x82, 0x03, 0x88, 0x07, 0x88, 0x03,
|
||||||
|
0x88, 0x04, 0x81, 0x05, 0x88, 0x03, 0x81, 0x05, 0x84, 0x0A, 0x81, 0x04,
|
||||||
|
0x88, 0x07, 0x90, 0x09, 0x88, 0x0E, 0x84, 0x03, 0x84, 0x0A, 0x81, 0x05,
|
||||||
|
0x88, 0x0B, 0x84, 0x0A, 0x84, 0x0E, 0x90, 0x0C, 0x82, 0x08, 0x84, 0x07,
|
||||||
|
0x81, 0x05, 0x81, 0x06, 0x84, 0x05, 0x81, 0x01, 0x82, 0x06, 0x84, 0x01,
|
||||||
|
0x90, 0x08, 0x84, 0x04, 0x81, 0x0B, 0x81, 0x06, 0x88, 0x0C, 0x81, 0x07,
|
||||||
|
0x90, 0x0B, 0x81, 0x0B, 0x90, 0x04, 0x90, 0x0C, 0x90, 0x09, 0x90, 0x06,
|
||||||
|
0x84, 0x05, 0x90, 0x0E, 0x81, 0x05, 0x88, 0x01, 0x82, 0x01, 0x90, 0x0C,
|
||||||
|
0x90, 0x0E, 0x88, 0x01, 0x81, 0x07, 0x84, 0x0E, 0x82, 0x06, 0x88, 0x0C,
|
||||||
|
0x88, 0x08, 0x90, 0x0B, 0x90, 0x0B, 0x81, 0x0D, 0x84, 0x06, 0x90, 0x0C,
|
||||||
|
0x90, 0x0C, 0x84, 0x0D, 0x84, 0x0D, 0x88, 0x0C, 0x90, 0x0D, 0x88, 0x0C,
|
||||||
|
0x88, 0x0C, 0x88, 0x0D, 0x81, 0x02, 0x82, 0x02, 0x90, 0x09, 0x84, 0x0D,
|
||||||
|
0x88, 0x04, 0x88, 0x01, 0x88, 0x0E, 0x90, 0x0A, 0x81, 0x09, 0x82, 0x09,
|
||||||
|
0x90, 0x0E, 0x81, 0x07, 0x84, 0x0D, 0x88, 0x05, 0x88, 0x08, 0x82, 0x06,
|
||||||
|
0x84, 0x0D, 0x84, 0x03, 0x82, 0x0C, 0x84, 0x02, 0x90, 0x03, 0x81, 0x07,
|
||||||
|
0x82, 0x0B, 0x81, 0x09, 0x84, 0x0C, 0x88, 0x0E, 0x81, 0x05, 0x82, 0x09,
|
||||||
|
0x81, 0x08, 0x84, 0x0E, 0x90, 0x0E, 0x88, 0x04, 0x84, 0x04, 0x88, 0x01,
|
||||||
|
0x81, 0x05, 0x82, 0x08, 0x81, 0x0E, 0x82, 0x09, 0x88, 0x06, 0x81, 0x0D,
|
||||||
|
0x90, 0x0C, 0x84, 0x09, 0x81, 0x05, 0x81, 0x02, 0x82, 0x08, 0x81, 0x01,
|
||||||
|
0x90, 0x03, 0x90, 0x02, 0x84, 0x0D, 0x84, 0x03, 0x88, 0x0D, 0x88, 0x0D,
|
||||||
|
0x84, 0x0A, 0x90, 0x06, 0x88, 0x06, 0x82, 0x03, 0x82, 0x0C, 0x88, 0x0E,
|
||||||
|
0x84, 0x0A, 0x81, 0x06, 0x82, 0x05, 0x90, 0x0E, 0x81, 0x0C, 0x84, 0x0C,
|
||||||
|
0x88, 0x07, 0x84, 0x0C, 0x88, 0x0E, 0x90, 0x04,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level N -- 1400 ticks, 0 hook bytes, 1 fare stages, 45 pictures, 26 landings
|
||||||
|
// regenerate: tools/stsim/recorder levels/level14.dat N 20261006 60 2000
|
||||||
|
static const uint8_t kGateStreamN[320] = {
|
||||||
|
0x81, 0x04, 0x88, 0x03, 0x82, 0x01, 0x89, 0x02, 0x90, 0x01, 0x84, 0x0B,
|
||||||
|
0x81, 0x04, 0x88, 0x0D, 0x84, 0x0C, 0x82, 0x03, 0x81, 0x09, 0x81, 0x04,
|
||||||
|
0x88, 0x06, 0x81, 0x0D, 0x82, 0x0D, 0x90, 0x0A, 0x84, 0x03, 0x81, 0x08,
|
||||||
|
0x88, 0x06, 0x81, 0x03, 0x88, 0x0C, 0x88, 0x03, 0x84, 0x04, 0x84, 0x0E,
|
||||||
|
0x82, 0x02, 0x88, 0x09, 0x80, 0x03, 0x84, 0x09, 0x81, 0x07, 0x81, 0x0B,
|
||||||
|
0x81, 0x05, 0x81, 0x03, 0x84, 0x0C, 0x81, 0x02, 0x81, 0x0D, 0x82, 0x04,
|
||||||
|
0x90, 0x08, 0x82, 0x09, 0x82, 0x04, 0x84, 0x0A, 0x88, 0x01, 0x81, 0x0B,
|
||||||
|
0x90, 0x0B, 0x81, 0x0A, 0x82, 0x0B, 0x90, 0x07, 0x81, 0x08, 0x81, 0x04,
|
||||||
|
0x82, 0x0E, 0x90, 0x09, 0x81, 0x0A, 0x82, 0x08, 0x81, 0x03, 0x90, 0x07,
|
||||||
|
0x88, 0x0B, 0x81, 0x08, 0x84, 0x04, 0x90, 0x0B, 0x84, 0x06, 0x88, 0x0A,
|
||||||
|
0x84, 0x0A, 0x81, 0x03, 0x90, 0x0E, 0x81, 0x07, 0x90, 0x0E, 0x84, 0x04,
|
||||||
|
0x84, 0x06, 0x82, 0x0A, 0x81, 0x0A, 0x90, 0x08, 0x84, 0x0D, 0x82, 0x0B,
|
||||||
|
0x81, 0x0D, 0x84, 0x08, 0x90, 0x0A, 0x81, 0x09, 0x90, 0x08, 0x88, 0x0A,
|
||||||
|
0x82, 0x07, 0x84, 0x07, 0x90, 0x0A, 0x81, 0x04, 0x90, 0x0B, 0x84, 0x0A,
|
||||||
|
0x81, 0x0B, 0x81, 0x01, 0x90, 0x02, 0x82, 0x0D, 0x84, 0x0C, 0x81, 0x08,
|
||||||
|
0x90, 0x0D, 0x82, 0x07, 0x88, 0x0C, 0x81, 0x03, 0x90, 0x0D, 0x81, 0x07,
|
||||||
|
0x90, 0x0C, 0x82, 0x0C, 0x81, 0x02, 0x90, 0x08, 0x88, 0x0B, 0x81, 0x0E,
|
||||||
|
0x82, 0x07, 0x90, 0x0E, 0x82, 0x07, 0x81, 0x06, 0x84, 0x05, 0x81, 0x04,
|
||||||
|
0x90, 0x0E, 0x84, 0x03, 0x81, 0x0A, 0x82, 0x08, 0x90, 0x03, 0x90, 0x05,
|
||||||
|
0x84, 0x09, 0x81, 0x03, 0x90, 0x0C, 0x81, 0x03, 0x82, 0x06, 0x81, 0x0A,
|
||||||
|
0x81, 0x03, 0x88, 0x06, 0x81, 0x02, 0x88, 0x02, 0x90, 0x02, 0x88, 0x0B,
|
||||||
|
0x84, 0x0A, 0x82, 0x08, 0x88, 0x08, 0x81, 0x03, 0x81, 0x02, 0x82, 0x04,
|
||||||
|
0x90, 0x06, 0x81, 0x0D, 0x90, 0x0C, 0x82, 0x0E, 0x81, 0x08, 0x81, 0x07,
|
||||||
|
0x81, 0x0E, 0x81, 0x02, 0x81, 0x07, 0x84, 0x03, 0x88, 0x0C, 0x81, 0x0B,
|
||||||
|
0x90, 0x0A, 0x84, 0x09, 0x88, 0x04, 0x83, 0x06, 0x84, 0x09, 0x88, 0x09,
|
||||||
|
0x81, 0x09, 0x81, 0x01, 0x90, 0x0A, 0x84, 0x0B, 0x82, 0x08, 0x81, 0x09,
|
||||||
|
0x88, 0x08, 0x82, 0x0C, 0x81, 0x06, 0x88, 0x06,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level O -- searched 2026-09 by the recorder of then (seed 20260918), from a
|
||||||
|
// fresh game's first screen; kept because, ridden as the shift reaches
|
||||||
|
// the screen, it covers more than the 2026-10-06 search did
|
||||||
static const uint8_t kGateStreamO[320] = {
|
static const uint8_t kGateStreamO[320] = {
|
||||||
0x84, 0x0E, 0x90, 0x08, 0x82, 0x07, 0x81, 0x0B, 0x84, 0x02, 0x81, 0x09,
|
0x84, 0x0E, 0x90, 0x08, 0x82, 0x07, 0x81, 0x0B, 0x84, 0x02, 0x81, 0x09,
|
||||||
0x82, 0x06, 0x90, 0x0C, 0x81, 0x08, 0x88, 0x01, 0x88, 0x02, 0x90, 0x04,
|
0x82, 0x06, 0x90, 0x0C, 0x81, 0x08, 0x88, 0x01, 0x88, 0x02, 0x90, 0x04,
|
||||||
|
|
@ -146,40 +467,73 @@ static const uint8_t kGateStreamO[320] = {
|
||||||
0x81, 0x0E, 0x81, 0x08, 0x90, 0x0D, 0x84, 0x0D,
|
0x81, 0x0E, 0x81, 0x08, 0x90, 0x0D, 0x84, 0x0D,
|
||||||
};
|
};
|
||||||
|
|
||||||
// level Q -- 1400 ticks, 5 hook bytes, 10 fare stages, 256 pictures, 10 landings
|
// level P -- 1400 ticks, 11 hook bytes, 10 fare stages, 256 pictures, 5 landings
|
||||||
// regenerate: tools/stsim/recorder levels/level??.dat Q 20260918
|
// regenerate: tools/stsim/recorder levels/level16.dat P 20261006 60 2000
|
||||||
static const uint8_t kGateStreamQ[320] = {
|
static const uint8_t kGateStreamP[320] = {
|
||||||
0x84, 0x0E, 0x81, 0x04, 0x81, 0x03, 0x84, 0x09, 0x88, 0x0A, 0x81, 0x09,
|
0x81, 0x04, 0x88, 0x02, 0x84, 0x05, 0x89, 0x02, 0x81, 0x01, 0x88, 0x05,
|
||||||
0x82, 0x06, 0x88, 0x0C, 0x81, 0x08, 0x82, 0x06, 0x88, 0x0E, 0x84, 0x0B,
|
0x81, 0x04, 0x80, 0x0D, 0x84, 0x0C, 0x84, 0x03, 0x84, 0x08, 0x88, 0x07,
|
||||||
0x88, 0x08, 0x81, 0x09, 0x81, 0x0A, 0x84, 0x05, 0x84, 0x01, 0x88, 0x01,
|
0x88, 0x01, 0x81, 0x05, 0x88, 0x0E, 0x82, 0x01, 0x81, 0x0C, 0x81, 0x0C,
|
||||||
0x81, 0x08, 0x84, 0x0B, 0x81, 0x05, 0x82, 0x05, 0x80, 0x0D, 0x81, 0x08,
|
0x88, 0x03, 0x80, 0x0C, 0x88, 0x0E, 0x88, 0x03, 0x84, 0x04, 0x84, 0x0E,
|
||||||
0x88, 0x0A, 0x88, 0x04, 0x84, 0x08, 0x81, 0x06, 0x81, 0x0C, 0x81, 0x01,
|
0x81, 0x07, 0x88, 0x06, 0x80, 0x08, 0x84, 0x09, 0x81, 0x0E, 0x90, 0x0A,
|
||||||
0x90, 0x0A, 0x84, 0x08, 0x81, 0x0A, 0x81, 0x0A, 0x82, 0x08, 0x81, 0x06,
|
0x81, 0x03, 0x81, 0x05, 0x84, 0x09, 0x81, 0x01, 0x88, 0x07, 0x88, 0x08,
|
||||||
0x81, 0x09, 0x90, 0x06, 0x81, 0x09, 0x90, 0x0D, 0x88, 0x04, 0x82, 0x05,
|
0x81, 0x05, 0x81, 0x0C, 0x82, 0x0D, 0x88, 0x0E, 0x88, 0x07, 0x81, 0x02,
|
||||||
0x81, 0x0B, 0x84, 0x08, 0x81, 0x08, 0x81, 0x03, 0x82, 0x0D, 0x82, 0x0A,
|
0x81, 0x03, 0x84, 0x0C, 0x88, 0x06, 0x88, 0x05, 0x81, 0x07, 0x81, 0x0A,
|
||||||
0x84, 0x0E, 0x81, 0x05, 0x81, 0x0E, 0x81, 0x08, 0x84, 0x0A, 0x82, 0x0C,
|
0x84, 0x09, 0x90, 0x06, 0x84, 0x09, 0x81, 0x0B, 0x88, 0x01, 0x81, 0x03,
|
||||||
0x84, 0x09, 0x84, 0x01, 0x82, 0x06, 0x88, 0x0E, 0x81, 0x09, 0x90, 0x07,
|
0x81, 0x06, 0x90, 0x0C, 0x88, 0x06, 0x81, 0x0B, 0x81, 0x06, 0x90, 0x06,
|
||||||
0x81, 0x09, 0x81, 0x07, 0x84, 0x03, 0x81, 0x0B, 0x84, 0x01, 0x90, 0x08,
|
0x82, 0x06, 0x82, 0x04, 0x82, 0x01, 0x81, 0x02, 0x81, 0x07, 0x90, 0x0E,
|
||||||
0x90, 0x06, 0x82, 0x03, 0x88, 0x06, 0x88, 0x0C, 0x84, 0x0A, 0x81, 0x03,
|
0x90, 0x0B, 0x84, 0x07, 0x81, 0x03, 0x84, 0x0E, 0x84, 0x08, 0x84, 0x0C,
|
||||||
0x82, 0x0E, 0x81, 0x07, 0x81, 0x04, 0x81, 0x0D, 0x84, 0x02, 0x81, 0x09,
|
0x81, 0x0D, 0x81, 0x0A, 0x81, 0x0E, 0x88, 0x0C, 0x88, 0x0B, 0x81, 0x05,
|
||||||
0x82, 0x08, 0x88, 0x04, 0x81, 0x09, 0x84, 0x0C, 0x81, 0x0B, 0x84, 0x0D,
|
0x90, 0x04, 0x88, 0x0A, 0x82, 0x0B, 0x84, 0x0D, 0x81, 0x0C, 0x84, 0x0C,
|
||||||
0x81, 0x02, 0x82, 0x03, 0x81, 0x05, 0x88, 0x0E, 0x84, 0x0D, 0x81, 0x0B,
|
0x90, 0x05, 0x84, 0x0D, 0x81, 0x02, 0x81, 0x08, 0x81, 0x03, 0x88, 0x06,
|
||||||
0x88, 0x08, 0x84, 0x0C, 0x90, 0x07, 0x82, 0x0B, 0x81, 0x03, 0x90, 0x05,
|
0x90, 0x0E, 0x81, 0x05, 0x84, 0x0B, 0x88, 0x08, 0x81, 0x09, 0x84, 0x0E,
|
||||||
0x84, 0x05, 0x84, 0x09, 0x81, 0x04, 0x81, 0x0B, 0x81, 0x08, 0x82, 0x0E,
|
0x81, 0x0A, 0x81, 0x05, 0x88, 0x07, 0x81, 0x02, 0x81, 0x0C, 0x81, 0x03,
|
||||||
0x81, 0x0A, 0x81, 0x03, 0x81, 0x06, 0x88, 0x09, 0x84, 0x0A, 0x88, 0x0B,
|
0x8A, 0x0C, 0x84, 0x0B, 0x80, 0x03, 0x85, 0x04, 0x81, 0x04, 0x82, 0x0A,
|
||||||
0x81, 0x05, 0x81, 0x0D, 0x88, 0x03, 0x88, 0x0D, 0x81, 0x01, 0x90, 0x06,
|
0x85, 0x07, 0x84, 0x0D, 0x88, 0x08, 0x80, 0x07, 0x81, 0x01, 0x80, 0x0D,
|
||||||
0x88, 0x07, 0x82, 0x02, 0x81, 0x04, 0x84, 0x09, 0x81, 0x07, 0x90, 0x06,
|
0x80, 0x04, 0x81, 0x05, 0x82, 0x01, 0x82, 0x0B, 0x84, 0x05, 0x88, 0x0A,
|
||||||
0x82, 0x03, 0x84, 0x07, 0x81, 0x05, 0x90, 0x0B, 0x81, 0x0B, 0x88, 0x05,
|
0x90, 0x01, 0x90, 0x08, 0x81, 0x08, 0x81, 0x08, 0x81, 0x05, 0x81, 0x07,
|
||||||
0x81, 0x0D, 0x81, 0x0B, 0x92, 0x0E, 0x90, 0x05, 0x82, 0x02, 0x88, 0x0C,
|
0x81, 0x01, 0x81, 0x0D, 0x88, 0x02, 0x81, 0x01, 0x88, 0x03, 0x90, 0x02,
|
||||||
0x84, 0x08, 0x81, 0x0A, 0x81, 0x0D, 0x81, 0x05, 0x81, 0x07, 0x81, 0x01,
|
0x88, 0x0A, 0x81, 0x0D, 0x82, 0x05, 0x88, 0x01, 0x81, 0x02, 0x85, 0x0A,
|
||||||
0x81, 0x04, 0x88, 0x01, 0x81, 0x0E, 0x84, 0x04, 0x90, 0x03, 0x81, 0x0D,
|
0x84, 0x0C, 0x88, 0x0C, 0x88, 0x05, 0x88, 0x03, 0x90, 0x08, 0x81, 0x08,
|
||||||
0x90, 0x09, 0x81, 0x02, 0x81, 0x08, 0x90, 0x07, 0x82, 0x0B, 0x90, 0x08,
|
0x84, 0x0E, 0x82, 0x0C, 0x84, 0x0E, 0x90, 0x0C, 0x82, 0x0E, 0x90, 0x08,
|
||||||
0x81, 0x03, 0x90, 0x0C, 0x81, 0x0B, 0x88, 0x08, 0x90, 0x04, 0x81, 0x02,
|
0x84, 0x0D, 0x88, 0x0B, 0x84, 0x0C, 0x82, 0x0C, 0x84, 0x08, 0x90, 0x0A,
|
||||||
0x90, 0x0C, 0x88, 0x05, 0x81, 0x01, 0x90, 0x03,
|
0x88, 0x0E, 0x81, 0x07, 0x90, 0x03, 0x88, 0x0C,
|
||||||
};
|
};
|
||||||
|
|
||||||
// level R -- 1400 ticks, 0 hook bytes, 3 fare stages, 256 pictures, 0 landings
|
// level Q -- 1400 ticks, 6 hook bytes, 10 fare stages, 256 pictures, 6 landings
|
||||||
// regenerate: tools/stsim/recorder levels/level??.dat R 20260918
|
// regenerate: tools/stsim/recorder levels/level17.dat Q 20261006 60 2000
|
||||||
|
static const uint8_t kGateStreamQ[320] = {
|
||||||
|
0x81, 0x04, 0x88, 0x02, 0x84, 0x0E, 0x89, 0x06, 0x81, 0x01, 0x82, 0x05,
|
||||||
|
0x81, 0x04, 0x80, 0x05, 0x84, 0x0C, 0x82, 0x0C, 0x81, 0x06, 0x88, 0x07,
|
||||||
|
0x81, 0x06, 0x81, 0x0B, 0x88, 0x0E, 0x84, 0x07, 0x88, 0x0E, 0x84, 0x06,
|
||||||
|
0x81, 0x0A, 0x81, 0x05, 0x81, 0x0C, 0x81, 0x0D, 0x81, 0x09, 0x88, 0x0D,
|
||||||
|
0x86, 0x0A, 0x81, 0x09, 0x84, 0x09, 0x88, 0x0E, 0x81, 0x05, 0x88, 0x06,
|
||||||
|
0x80, 0x09, 0x84, 0x04, 0x82, 0x0A, 0x84, 0x0C, 0x81, 0x0E, 0x90, 0x09,
|
||||||
|
0x81, 0x0D, 0x84, 0x07, 0x90, 0x03, 0x82, 0x04, 0x82, 0x06, 0x81, 0x0E,
|
||||||
|
0x84, 0x09, 0x88, 0x06, 0x88, 0x0C, 0x88, 0x07, 0x81, 0x02, 0x84, 0x0D,
|
||||||
|
0x88, 0x04, 0x81, 0x05, 0x88, 0x05, 0x81, 0x05, 0x88, 0x02, 0x90, 0x0E,
|
||||||
|
0x81, 0x06, 0x84, 0x02, 0x81, 0x02, 0x84, 0x05, 0x81, 0x0C, 0x90, 0x05,
|
||||||
|
0x81, 0x0C, 0x81, 0x09, 0x81, 0x07, 0x82, 0x04, 0x81, 0x05, 0x81, 0x04,
|
||||||
|
0x81, 0x0A, 0x82, 0x0E, 0x90, 0x09, 0x81, 0x07, 0x90, 0x0C, 0x82, 0x06,
|
||||||
|
0x82, 0x08, 0x81, 0x01, 0x82, 0x0B, 0x81, 0x01, 0x81, 0x07, 0x81, 0x07,
|
||||||
|
0x90, 0x03, 0x81, 0x01, 0x88, 0x01, 0x81, 0x0B, 0x81, 0x07, 0x84, 0x06,
|
||||||
|
0x88, 0x01, 0x81, 0x05, 0x81, 0x08, 0x88, 0x0E, 0x81, 0x08, 0x90, 0x05,
|
||||||
|
0x82, 0x07, 0x84, 0x0A, 0x81, 0x01, 0x81, 0x0D, 0x81, 0x0E, 0x84, 0x06,
|
||||||
|
0x90, 0x0A, 0x81, 0x06, 0x82, 0x06, 0x84, 0x01, 0x90, 0x03, 0x90, 0x04,
|
||||||
|
0x81, 0x03, 0x81, 0x08, 0x90, 0x05, 0x82, 0x05, 0x88, 0x06, 0x88, 0x06,
|
||||||
|
0x81, 0x0B, 0x84, 0x05, 0x84, 0x0E, 0x82, 0x0E, 0x81, 0x0E, 0x81, 0x0C,
|
||||||
|
0x81, 0x05, 0x90, 0x0C, 0x81, 0x0C, 0x88, 0x0B, 0x81, 0x05, 0x88, 0x03,
|
||||||
|
0x81, 0x04, 0x88, 0x06, 0x88, 0x07, 0x88, 0x05, 0x84, 0x06, 0x90, 0x0A,
|
||||||
|
0x82, 0x0B, 0x82, 0x07, 0x81, 0x01, 0x89, 0x0D, 0x88, 0x02, 0x84, 0x04,
|
||||||
|
0x81, 0x01, 0x88, 0x09, 0x84, 0x0B, 0x84, 0x07, 0x90, 0x0E, 0x82, 0x01,
|
||||||
|
0x81, 0x0C, 0x84, 0x0E, 0x81, 0x02, 0x84, 0x0B, 0x81, 0x08, 0x82, 0x09,
|
||||||
|
0x81, 0x0E, 0x81, 0x0D, 0x90, 0x06, 0x81, 0x0D, 0x90, 0x0E, 0x81, 0x0A,
|
||||||
|
0x81, 0x0E, 0x81, 0x02, 0x81, 0x05, 0x88, 0x05, 0x88, 0x08, 0x84, 0x09,
|
||||||
|
0x84, 0x0D, 0x80, 0x0A, 0x84, 0x02, 0x81, 0x0C,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level R -- searched 2026-09 by the recorder of then (seed 20260918), from a
|
||||||
|
// fresh game's first screen; kept because, ridden as the shift reaches
|
||||||
|
// the screen, it covers more than the 2026-10-06 search did
|
||||||
static const uint8_t kGateStreamR[320] = {
|
static const uint8_t kGateStreamR[320] = {
|
||||||
0x90, 0x0E, 0x81, 0x02, 0x82, 0x07, 0x81, 0x05, 0x88, 0x0B, 0x88, 0x09,
|
0x90, 0x0E, 0x81, 0x02, 0x82, 0x07, 0x81, 0x05, 0x88, 0x0B, 0x88, 0x09,
|
||||||
0x90, 0x0D, 0x90, 0x06, 0x81, 0x08, 0x81, 0x01, 0x90, 0x02, 0x88, 0x04,
|
0x90, 0x0D, 0x90, 0x06, 0x81, 0x08, 0x81, 0x01, 0x90, 0x02, 0x88, 0x04,
|
||||||
|
|
@ -210,228 +564,132 @@ static const uint8_t kGateStreamR[320] = {
|
||||||
0x90, 0x0A, 0x90, 0x01, 0x84, 0x0A, 0x81, 0x0B,
|
0x90, 0x0A, 0x90, 0x01, 0x84, 0x0A, 0x81, 0x0B,
|
||||||
};
|
};
|
||||||
|
|
||||||
// level U -- 896 ticks, 21 hook bytes, 3 fare stages, 256 pictures, 0 landings
|
// level S -- 1400 ticks, 0 hook bytes, 10 fare stages, 256 pictures, 25 landings
|
||||||
// regenerate: tools/stsim/recorder levels/level??.dat U 44
|
// regenerate: tools/stsim/recorder levels/level19.dat S 20261006 60 2000
|
||||||
static const uint8_t kGateStreamU[320] = {
|
|
||||||
0x90, 0x01, 0x90, 0x0B, 0x88, 0x08, 0x90, 0x07, 0x88, 0x0D, 0x82, 0x0A,
|
|
||||||
0x82, 0x06, 0x88, 0x0C, 0x90, 0x03, 0x81, 0x04, 0x84, 0x0B, 0x81, 0x0C,
|
|
||||||
0x88, 0x02, 0x84, 0x08, 0x84, 0x01, 0x81, 0x04, 0x90, 0x05, 0x84, 0x06,
|
|
||||||
0x84, 0x08, 0x84, 0x05, 0x81, 0x03, 0x81, 0x09, 0x81, 0x08, 0x88, 0x07,
|
|
||||||
0x81, 0x08, 0x84, 0x06, 0x81, 0x0B, 0x88, 0x0D, 0x90, 0x08, 0x81, 0x01,
|
|
||||||
0x82, 0x01, 0x88, 0x03, 0x81, 0x0E, 0x82, 0x01, 0x81, 0x0D, 0x81, 0x07,
|
|
||||||
0x88, 0x05, 0x88, 0x07, 0x88, 0x0E, 0x84, 0x07, 0x81, 0x03, 0x81, 0x01,
|
|
||||||
0x84, 0x03, 0x81, 0x04, 0x81, 0x08, 0x90, 0x06, 0x81, 0x0C, 0x82, 0x05,
|
|
||||||
0x81, 0x08, 0x82, 0x09, 0x84, 0x06, 0x81, 0x06, 0x90, 0x0C, 0x84, 0x0E,
|
|
||||||
0x82, 0x0C, 0x81, 0x03, 0x81, 0x06, 0x84, 0x0D, 0x88, 0x03, 0x84, 0x0C,
|
|
||||||
0x81, 0x02, 0x81, 0x09, 0x81, 0x0E, 0x88, 0x01, 0x90, 0x06, 0x88, 0x08,
|
|
||||||
0x81, 0x08, 0x81, 0x0C, 0x81, 0x0C, 0x82, 0x06, 0x82, 0x08, 0x88, 0x01,
|
|
||||||
0x81, 0x07, 0x88, 0x0D, 0x84, 0x03, 0x81, 0x02, 0x82, 0x06, 0x81, 0x05,
|
|
||||||
0x81, 0x0E, 0x81, 0x04, 0x90, 0x01, 0x81, 0x02, 0x90, 0x03, 0x81, 0x0A,
|
|
||||||
0x81, 0x04, 0x84, 0x02, 0x81, 0x06, 0x81, 0x0E, 0x81, 0x06, 0x84, 0x07,
|
|
||||||
0x84, 0x04, 0x81, 0x07, 0x81, 0x0E, 0x81, 0x0D, 0x90, 0x0D, 0x90, 0x05,
|
|
||||||
0x88, 0x0C, 0x88, 0x02, 0x81, 0x0B, 0x90, 0x07, 0x88, 0x06, 0x82, 0x0D,
|
|
||||||
0x84, 0x08, 0x84, 0x08, 0x81, 0x01, 0x81, 0x06, 0x82, 0x05, 0x84, 0x06,
|
|
||||||
0x81, 0x06, 0x84, 0x0D, 0x81, 0x04, 0x90, 0x07, 0x88, 0x0D, 0x81, 0x09,
|
|
||||||
0x88, 0x0D, 0x81, 0x0E, 0x81, 0x03, 0x84, 0x0E, 0x81, 0x03, 0x81, 0x0C,
|
|
||||||
0x84, 0x0A, 0x81, 0x06, 0x90, 0x02, 0x81, 0x01, 0x88, 0x02, 0x90, 0x02,
|
|
||||||
0x81, 0x08, 0x84, 0x01, 0x88, 0x0B, 0x88, 0x0B, 0x81, 0x04, 0x81, 0x0C,
|
|
||||||
0x81, 0x07, 0x82, 0x06, 0x88, 0x0C, 0x81, 0x03, 0x90, 0x02, 0x88, 0x0A,
|
|
||||||
0x84, 0x08, 0x81, 0x0E, 0x81, 0x05, 0x88, 0x0C, 0x81, 0x0D, 0x81, 0x05,
|
|
||||||
0x81, 0x04, 0x90, 0x0D, 0x88, 0x03, 0x88, 0x06, 0x81, 0x0B, 0x81, 0x0D,
|
|
||||||
0x84, 0x0D, 0x81, 0x01, 0x81, 0x0D, 0x81, 0x01, 0x82, 0x07, 0x88, 0x02,
|
|
||||||
0x81, 0x09, 0x90, 0x0B, 0x88, 0x0D, 0x82, 0x02,
|
|
||||||
};
|
|
||||||
|
|
||||||
// level V -- 1400 ticks, 0 hook bytes, 6 fare stages, 256 pictures, 23 landings
|
|
||||||
// regenerate: tools/stsim/recorder levels/level??.dat V 20260918
|
|
||||||
static const uint8_t kGateStreamV[320] = {
|
|
||||||
0x84, 0x0E, 0x81, 0x04, 0x81, 0x05, 0x88, 0x0B, 0x84, 0x01, 0x81, 0x03,
|
|
||||||
0x82, 0x06, 0x84, 0x0C, 0x81, 0x08, 0x84, 0x07, 0x82, 0x03, 0x88, 0x0A,
|
|
||||||
0x90, 0x03, 0x81, 0x0D, 0x81, 0x0D, 0x82, 0x05, 0x81, 0x02, 0x81, 0x0E,
|
|
||||||
0x90, 0x06, 0x81, 0x05, 0x90, 0x05, 0x81, 0x0E, 0x82, 0x0D, 0x90, 0x0E,
|
|
||||||
0x88, 0x0E, 0x81, 0x03, 0x90, 0x07, 0x84, 0x0A, 0x81, 0x02, 0x90, 0x07,
|
|
||||||
0x82, 0x07, 0x81, 0x02, 0x81, 0x06, 0x82, 0x08, 0x81, 0x04, 0x88, 0x0C,
|
|
||||||
0x81, 0x06, 0x88, 0x0D, 0x90, 0x0D, 0x81, 0x05, 0x90, 0x03, 0x84, 0x0D,
|
|
||||||
0x84, 0x0A, 0x88, 0x0A, 0x88, 0x0D, 0x81, 0x08, 0x90, 0x08, 0x90, 0x01,
|
|
||||||
0x84, 0x06, 0x82, 0x0B, 0x81, 0x0D, 0x82, 0x07, 0x82, 0x0C, 0x81, 0x01,
|
|
||||||
0x90, 0x08, 0x81, 0x02, 0x81, 0x04, 0x90, 0x0C, 0x84, 0x04, 0x88, 0x04,
|
|
||||||
0x82, 0x03, 0x88, 0x07, 0x81, 0x03, 0x90, 0x05, 0x81, 0x02, 0x81, 0x03,
|
|
||||||
0x88, 0x01, 0x90, 0x05, 0x81, 0x08, 0x81, 0x06, 0x81, 0x07, 0x81, 0x08,
|
|
||||||
0x81, 0x06, 0x81, 0x02, 0x84, 0x0B, 0x88, 0x0D, 0x81, 0x06, 0x90, 0x02,
|
|
||||||
0x81, 0x07, 0x82, 0x0B, 0x88, 0x04, 0x84, 0x04, 0x84, 0x05, 0x81, 0x05,
|
|
||||||
0x81, 0x02, 0x82, 0x09, 0x81, 0x03, 0x90, 0x0C, 0x88, 0x0B, 0x82, 0x01,
|
|
||||||
0x88, 0x0D, 0x88, 0x05, 0x81, 0x02, 0x90, 0x0B, 0x81, 0x02, 0x90, 0x0A,
|
|
||||||
0x88, 0x0C, 0x81, 0x01, 0x90, 0x0A, 0x88, 0x0E, 0x90, 0x0A, 0x84, 0x0B,
|
|
||||||
0x81, 0x03, 0x90, 0x08, 0x88, 0x03, 0x82, 0x0E, 0x84, 0x06, 0x90, 0x0E,
|
|
||||||
0x81, 0x03, 0x88, 0x0A, 0x90, 0x0A, 0x82, 0x09, 0x81, 0x04, 0x88, 0x0C,
|
|
||||||
0x84, 0x04, 0x81, 0x07, 0x90, 0x03, 0x81, 0x01, 0x81, 0x01, 0x81, 0x01,
|
|
||||||
0x82, 0x0E, 0x81, 0x05, 0x81, 0x06, 0x81, 0x08, 0x84, 0x08, 0x88, 0x0C,
|
|
||||||
0x82, 0x09, 0x88, 0x05, 0x81, 0x02, 0x88, 0x04, 0x81, 0x04, 0x88, 0x0C,
|
|
||||||
0x90, 0x07, 0x88, 0x0E, 0x81, 0x03, 0x81, 0x0A, 0x82, 0x0B, 0x81, 0x02,
|
|
||||||
0x88, 0x02, 0x90, 0x0E, 0x88, 0x0D, 0x81, 0x03, 0x88, 0x09, 0x90, 0x04,
|
|
||||||
0x84, 0x01, 0x88, 0x04, 0x90, 0x04, 0x84, 0x04, 0x81, 0x03, 0x90, 0x0C,
|
|
||||||
0x88, 0x07, 0x84, 0x05, 0x81, 0x02, 0x90, 0x0B, 0x90, 0x04, 0x90, 0x09,
|
|
||||||
0x81, 0x02, 0x81, 0x01, 0x90, 0x0B, 0x88, 0x04,
|
|
||||||
};
|
|
||||||
|
|
||||||
// level E -- 1400 ticks, 4 hook bytes, 8 fare stages, 256 pictures, 11 landings
|
|
||||||
// regenerate: tools/stsim/recorder levels/level??.dat E 20260921
|
|
||||||
static const uint8_t kGateStreamE[320] = {
|
|
||||||
0x84, 0x05, 0x82, 0x03, 0x82, 0x05, 0x81, 0x0B, 0x81, 0x07, 0x81, 0x02,
|
|
||||||
0x81, 0x01, 0x89, 0x02, 0x90, 0x03, 0x81, 0x0A, 0x81, 0x05, 0x84, 0x0C,
|
|
||||||
0x81, 0x02, 0x88, 0x07, 0x81, 0x0B, 0x90, 0x08, 0x90, 0x0A, 0x88, 0x02,
|
|
||||||
0x81, 0x05, 0x84, 0x08, 0x81, 0x05, 0x90, 0x03, 0x84, 0x01, 0x84, 0x08,
|
|
||||||
0x88, 0x0A, 0x84, 0x07, 0x81, 0x04, 0x84, 0x07, 0x90, 0x0C, 0x80, 0x08,
|
|
||||||
0x90, 0x07, 0x88, 0x03, 0x90, 0x0B, 0x88, 0x01, 0x90, 0x07, 0x88, 0x04,
|
|
||||||
0x81, 0x02, 0x88, 0x08, 0x84, 0x05, 0x81, 0x05, 0x81, 0x02, 0x81, 0x09,
|
|
||||||
0x88, 0x05, 0x90, 0x0E, 0x81, 0x05, 0x81, 0x01, 0x90, 0x04, 0x90, 0x08,
|
|
||||||
0x81, 0x06, 0x84, 0x0E, 0x81, 0x09, 0x81, 0x01, 0x84, 0x05, 0x81, 0x01,
|
|
||||||
0x81, 0x02, 0x88, 0x0B, 0x90, 0x08, 0x88, 0x0C, 0x88, 0x05, 0x88, 0x08,
|
|
||||||
0x84, 0x09, 0x88, 0x05, 0x90, 0x05, 0x82, 0x0A, 0x81, 0x07, 0x90, 0x0A,
|
|
||||||
0x81, 0x07, 0x81, 0x03, 0x84, 0x0B, 0x84, 0x0E, 0x88, 0x09, 0x90, 0x02,
|
|
||||||
0x81, 0x0E, 0x81, 0x06, 0x90, 0x0E, 0x81, 0x02, 0x90, 0x08, 0x84, 0x08,
|
|
||||||
0x81, 0x0B, 0x88, 0x0D, 0x84, 0x0C, 0x81, 0x09, 0x90, 0x08, 0x82, 0x0B,
|
|
||||||
0x88, 0x08, 0x81, 0x07, 0x82, 0x02, 0x84, 0x0B, 0x88, 0x0B, 0x81, 0x0D,
|
|
||||||
0x84, 0x0E, 0x82, 0x01, 0x81, 0x03, 0x90, 0x0C, 0x81, 0x05, 0x90, 0x04,
|
|
||||||
0x82, 0x0C, 0x81, 0x06, 0x90, 0x0E, 0x88, 0x0B, 0x88, 0x09, 0x81, 0x0E,
|
|
||||||
0x82, 0x0E, 0x90, 0x0C, 0x81, 0x06, 0x81, 0x05, 0x84, 0x0E, 0x81, 0x0C,
|
|
||||||
0x88, 0x0B, 0x90, 0x06, 0x82, 0x05, 0x81, 0x06, 0x88, 0x0D, 0x88, 0x07,
|
|
||||||
0x81, 0x07, 0x84, 0x07, 0x88, 0x08, 0x81, 0x07, 0x90, 0x0C, 0x84, 0x04,
|
|
||||||
0x84, 0x0A, 0x88, 0x0B, 0x80, 0x0A, 0x81, 0x0C, 0x81, 0x07, 0x88, 0x06,
|
|
||||||
0x88, 0x09, 0x88, 0x09, 0x81, 0x0C, 0x80, 0x06, 0x81, 0x06, 0x81, 0x07,
|
|
||||||
0x84, 0x08, 0x82, 0x09, 0x84, 0x0C, 0x90, 0x09, 0x90, 0x03, 0x90, 0x0D,
|
|
||||||
0x81, 0x08, 0x84, 0x05, 0x84, 0x09, 0x88, 0x0B, 0x81, 0x01, 0x81, 0x0A,
|
|
||||||
0x90, 0x0D, 0x88, 0x0E, 0x88, 0x0D, 0x88, 0x05, 0x84, 0x06, 0x81, 0x0E,
|
|
||||||
0x81, 0x0A, 0x81, 0x08, 0x84, 0x0C, 0x88, 0x05, 0x81, 0x02, 0x90, 0x0E,
|
|
||||||
0x84, 0x0D, 0x84, 0x0A, 0x81, 0x05, 0x90, 0x09,
|
|
||||||
};
|
|
||||||
|
|
||||||
// level J -- 1400 ticks, 10 hook bytes, 10 fare stages, 256 pictures, 8 landings
|
|
||||||
// regenerate: tools/stsim/recorder levels/level??.dat J 20260921
|
|
||||||
static const uint8_t kGateStreamJ[320] = {
|
|
||||||
0x88, 0x05, 0x82, 0x09, 0x81, 0x01, 0x81, 0x04, 0x90, 0x06, 0x81, 0x05,
|
|
||||||
0x81, 0x01, 0x90, 0x09, 0x88, 0x05, 0x82, 0x08, 0x81, 0x0A, 0x81, 0x03,
|
|
||||||
0x84, 0x07, 0x88, 0x0D, 0x81, 0x06, 0x81, 0x0A, 0x84, 0x05, 0x90, 0x08,
|
|
||||||
0x81, 0x0B, 0x90, 0x0E, 0x84, 0x0E, 0x81, 0x03, 0x90, 0x09, 0x81, 0x02,
|
|
||||||
0x88, 0x04, 0x88, 0x08, 0x84, 0x0E, 0x81, 0x04, 0x88, 0x0D, 0x90, 0x0E,
|
|
||||||
0x84, 0x09, 0x90, 0x05, 0x81, 0x01, 0x81, 0x03, 0x88, 0x03, 0x88, 0x07,
|
|
||||||
0x81, 0x0B, 0x90, 0x0B, 0x88, 0x08, 0x82, 0x03, 0x90, 0x09, 0x88, 0x03,
|
|
||||||
0x84, 0x06, 0x88, 0x0A, 0x88, 0x0B, 0x84, 0x04, 0x81, 0x05, 0x88, 0x0E,
|
|
||||||
0x81, 0x0A, 0x90, 0x0C, 0x85, 0x04, 0x81, 0x0E, 0x90, 0x02, 0x82, 0x0D,
|
|
||||||
0x84, 0x01, 0x81, 0x02, 0x88, 0x09, 0x81, 0x0B, 0x81, 0x02, 0x82, 0x0C,
|
|
||||||
0x84, 0x04, 0x81, 0x05, 0x81, 0x04, 0x81, 0x04, 0x81, 0x05, 0x90, 0x05,
|
|
||||||
0x88, 0x09, 0x81, 0x02, 0x88, 0x01, 0x81, 0x05, 0x90, 0x04, 0x81, 0x0B,
|
|
||||||
0x81, 0x05, 0x82, 0x0D, 0x88, 0x06, 0x88, 0x07, 0x88, 0x02, 0x84, 0x02,
|
|
||||||
0x90, 0x07, 0x88, 0x09, 0x84, 0x0C, 0x81, 0x01, 0x88, 0x0B, 0x81, 0x0C,
|
|
||||||
0x82, 0x0A, 0x81, 0x08, 0x82, 0x01, 0x88, 0x09, 0x84, 0x05, 0x81, 0x08,
|
|
||||||
0x84, 0x0A, 0x84, 0x09, 0x88, 0x0E, 0x81, 0x01, 0x84, 0x03, 0x82, 0x0C,
|
|
||||||
0x81, 0x0E, 0x84, 0x0A, 0x88, 0x02, 0x84, 0x0D, 0x81, 0x0A, 0x90, 0x0E,
|
|
||||||
0x88, 0x09, 0x90, 0x09, 0x81, 0x05, 0x90, 0x03, 0x88, 0x09, 0x84, 0x06,
|
|
||||||
0x81, 0x01, 0x88, 0x06, 0x81, 0x05, 0x81, 0x09, 0x81, 0x06, 0x90, 0x04,
|
|
||||||
0x82, 0x0A, 0x90, 0x07, 0x81, 0x07, 0x81, 0x0A, 0x84, 0x0E, 0x84, 0x07,
|
|
||||||
0x81, 0x0A, 0x90, 0x0C, 0x81, 0x01, 0x90, 0x01, 0x88, 0x0D, 0x81, 0x03,
|
|
||||||
0x88, 0x05, 0x81, 0x03, 0x88, 0x01, 0x81, 0x01, 0x82, 0x02, 0x88, 0x07,
|
|
||||||
0x83, 0x02, 0x90, 0x07, 0x81, 0x05, 0x81, 0x04, 0x81, 0x0E, 0x88, 0x09,
|
|
||||||
0x81, 0x07, 0x90, 0x03, 0x82, 0x0B, 0x82, 0x0B, 0x90, 0x04, 0x81, 0x05,
|
|
||||||
0x81, 0x03, 0x90, 0x0C, 0x82, 0x07, 0x88, 0x0E, 0x90, 0x04, 0x81, 0x0A,
|
|
||||||
0x88, 0x02, 0x88, 0x0D, 0x82, 0x07, 0x90, 0x01, 0x81, 0x02, 0x81, 0x01,
|
|
||||||
0x82, 0x07, 0x81, 0x08, 0x84, 0x0E, 0x81, 0x03,
|
|
||||||
};
|
|
||||||
|
|
||||||
// level L -- 1400 ticks, 0 hook bytes, 10 fare stages, 256 pictures, 8 landings
|
|
||||||
// regenerate: tools/stsim/recorder levels/level??.dat L 20260921
|
|
||||||
static const uint8_t kGateStreamL[320] = {
|
|
||||||
0x84, 0x05, 0x84, 0x09, 0x81, 0x06, 0x81, 0x04, 0x81, 0x08, 0x81, 0x05,
|
|
||||||
0x81, 0x01, 0x84, 0x09, 0x90, 0x09, 0x82, 0x0D, 0x88, 0x07, 0x81, 0x06,
|
|
||||||
0x81, 0x09, 0x82, 0x01, 0x81, 0x08, 0x88, 0x0B, 0x84, 0x0E, 0x81, 0x03,
|
|
||||||
0x82, 0x02, 0x82, 0x06, 0x82, 0x03, 0x88, 0x0E, 0x84, 0x0C, 0x81, 0x06,
|
|
||||||
0x88, 0x02, 0x81, 0x04, 0x88, 0x0E, 0x81, 0x04, 0x90, 0x0B, 0x81, 0x08,
|
|
||||||
0x84, 0x09, 0x82, 0x09, 0x81, 0x01, 0x90, 0x03, 0x81, 0x05, 0x84, 0x07,
|
|
||||||
0x81, 0x07, 0x90, 0x0B, 0x81, 0x07, 0x88, 0x09, 0x81, 0x0E, 0x88, 0x03,
|
|
||||||
0x81, 0x01, 0x88, 0x09, 0x81, 0x07, 0x81, 0x0C, 0x88, 0x0C, 0x90, 0x0E,
|
|
||||||
0x81, 0x0E, 0x90, 0x05, 0x84, 0x0E, 0x82, 0x0E, 0x82, 0x01, 0x84, 0x0A,
|
|
||||||
0x88, 0x03, 0x90, 0x09, 0x82, 0x05, 0x81, 0x0E, 0x81, 0x05, 0x81, 0x07,
|
|
||||||
0x81, 0x0D, 0x81, 0x0E, 0x90, 0x04, 0x90, 0x04, 0x81, 0x06, 0x81, 0x01,
|
|
||||||
0x88, 0x0B, 0x81, 0x01, 0x90, 0x0B, 0x81, 0x05, 0x84, 0x0D, 0x81, 0x01,
|
|
||||||
0x81, 0x0A, 0x84, 0x0D, 0x82, 0x09, 0x88, 0x06, 0x88, 0x04, 0x84, 0x0C,
|
|
||||||
0x88, 0x0C, 0x84, 0x02, 0x81, 0x07, 0x82, 0x07, 0x84, 0x08, 0x88, 0x09,
|
|
||||||
0x81, 0x01, 0x88, 0x01, 0x81, 0x0C, 0x81, 0x04, 0x84, 0x06, 0x82, 0x06,
|
|
||||||
0x90, 0x0D, 0x81, 0x06, 0x81, 0x0D, 0x82, 0x0D, 0x82, 0x08, 0x81, 0x0C,
|
|
||||||
0x84, 0x05, 0x82, 0x09, 0x81, 0x05, 0x84, 0x07, 0x82, 0x02, 0x84, 0x01,
|
|
||||||
0x81, 0x02, 0x90, 0x02, 0x82, 0x06, 0x88, 0x03, 0x84, 0x0B, 0x81, 0x0E,
|
|
||||||
0x88, 0x0E, 0x90, 0x07, 0x82, 0x08, 0x82, 0x02, 0x90, 0x02, 0x82, 0x03,
|
|
||||||
0x84, 0x07, 0x82, 0x05, 0x84, 0x07, 0x81, 0x0B, 0x82, 0x0E, 0x90, 0x05,
|
|
||||||
0x88, 0x04, 0x90, 0x05, 0x84, 0x0A, 0x82, 0x0A, 0x88, 0x0C, 0x81, 0x08,
|
|
||||||
0x82, 0x0C, 0x81, 0x06, 0x84, 0x09, 0x81, 0x04, 0x84, 0x0B, 0x88, 0x0B,
|
|
||||||
0x81, 0x0C, 0x81, 0x0C, 0x82, 0x0E, 0x81, 0x03, 0x82, 0x09, 0x88, 0x04,
|
|
||||||
0x81, 0x05, 0x88, 0x04, 0x80, 0x0B, 0x84, 0x06, 0x81, 0x0E, 0x84, 0x0B,
|
|
||||||
0x90, 0x0B, 0x90, 0x07, 0x82, 0x0D, 0x84, 0x0D, 0x81, 0x08, 0x81, 0x09,
|
|
||||||
0x84, 0x0E, 0x81, 0x0B, 0x90, 0x04, 0x84, 0x0B, 0x84, 0x03, 0x81, 0x03,
|
|
||||||
0x88, 0x0A, 0x81, 0x07, 0x84, 0x0B, 0x90, 0x0E,
|
|
||||||
};
|
|
||||||
|
|
||||||
// level P -- 1400 ticks, 11 hook bytes, 10 fare stages, 256 pictures, 4 landings
|
|
||||||
// regenerate: tools/stsim/recorder levels/level??.dat P 20260921
|
|
||||||
static const uint8_t kGateStreamP[320] = {
|
|
||||||
0x88, 0x05, 0x82, 0x09, 0x81, 0x0B, 0x81, 0x04, 0x81, 0x04, 0x84, 0x05,
|
|
||||||
0x82, 0x01, 0x90, 0x0C, 0x81, 0x03, 0x81, 0x0D, 0x81, 0x01, 0x84, 0x0C,
|
|
||||||
0x84, 0x0E, 0x88, 0x07, 0x81, 0x0B, 0x88, 0x0D, 0x90, 0x0A, 0x88, 0x02,
|
|
||||||
0x81, 0x05, 0x84, 0x08, 0x81, 0x05, 0x90, 0x0C, 0x84, 0x01, 0x88, 0x08,
|
|
||||||
0x88, 0x0A, 0x84, 0x0C, 0x81, 0x04, 0x84, 0x07, 0x90, 0x0C, 0x80, 0x09,
|
|
||||||
0x90, 0x06, 0x81, 0x03, 0x81, 0x0C, 0x88, 0x01, 0x82, 0x03, 0x81, 0x04,
|
|
||||||
0x84, 0x0D, 0x88, 0x09, 0x84, 0x05, 0x81, 0x05, 0x81, 0x02, 0x81, 0x09,
|
|
||||||
0x81, 0x02, 0x90, 0x0E, 0x81, 0x05, 0x81, 0x01, 0x81, 0x03, 0x88, 0x0A,
|
|
||||||
0x81, 0x06, 0x88, 0x05, 0x90, 0x0C, 0x90, 0x01, 0x84, 0x0E, 0x88, 0x01,
|
|
||||||
0x81, 0x02, 0x82, 0x05, 0x90, 0x08, 0x88, 0x0A, 0x84, 0x0C, 0x90, 0x08,
|
|
||||||
0x84, 0x0C, 0x81, 0x06, 0x81, 0x03, 0x88, 0x0A, 0x84, 0x08, 0x90, 0x0E,
|
|
||||||
0x81, 0x02, 0x88, 0x0C, 0x81, 0x06, 0x84, 0x0C, 0x81, 0x09, 0x80, 0x07,
|
|
||||||
0x88, 0x04, 0x81, 0x07, 0x81, 0x03, 0x81, 0x09, 0x81, 0x05, 0x86, 0x05,
|
|
||||||
0x90, 0x09, 0x84, 0x06, 0x81, 0x03, 0x84, 0x09, 0x81, 0x0E, 0x88, 0x0B,
|
|
||||||
0x90, 0x08, 0x88, 0x0A, 0x81, 0x04, 0x90, 0x0D, 0x88, 0x0E, 0x88, 0x09,
|
|
||||||
0x81, 0x0B, 0x90, 0x0C, 0x84, 0x05, 0x81, 0x08, 0x84, 0x01, 0x82, 0x02,
|
|
||||||
0x81, 0x03, 0x82, 0x0E, 0x88, 0x02, 0x88, 0x05, 0x81, 0x01, 0x81, 0x08,
|
|
||||||
0x82, 0x03, 0x84, 0x0E, 0x81, 0x01, 0x81, 0x08, 0x84, 0x03, 0x82, 0x03,
|
|
||||||
0x82, 0x05, 0x81, 0x0A, 0x81, 0x02, 0x90, 0x01, 0x81, 0x0E, 0x81, 0x0C,
|
|
||||||
0x90, 0x0C, 0x88, 0x0A, 0x84, 0x03, 0x81, 0x03, 0x84, 0x0A, 0x84, 0x05,
|
|
||||||
0x82, 0x09, 0x88, 0x0A, 0x81, 0x06, 0x84, 0x07, 0x84, 0x0C, 0x81, 0x0C,
|
|
||||||
0x82, 0x07, 0x81, 0x06, 0x81, 0x03, 0x88, 0x09, 0x90, 0x01, 0x84, 0x0E,
|
|
||||||
0x81, 0x01, 0x88, 0x06, 0x81, 0x0E, 0x81, 0x0A, 0x88, 0x0A, 0x81, 0x08,
|
|
||||||
0x84, 0x08, 0x88, 0x0E, 0x81, 0x02, 0x84, 0x0E, 0x82, 0x05, 0x81, 0x06,
|
|
||||||
0x88, 0x0C, 0x81, 0x0B, 0x81, 0x0D, 0x84, 0x02, 0x90, 0x0E, 0x84, 0x0E,
|
|
||||||
0x81, 0x02, 0x90, 0x07, 0x88, 0x0C, 0x90, 0x0A, 0x81, 0x0E, 0x81, 0x0A,
|
|
||||||
0x81, 0x0D, 0x81, 0x0B, 0x81, 0x08, 0x81, 0x03,
|
|
||||||
};
|
|
||||||
|
|
||||||
// level S -- 1400 ticks, 0 hook bytes, 10 fare stages, 256 pictures, 23 landings
|
|
||||||
// regenerate: tools/stsim/recorder levels/level??.dat S 20260921
|
|
||||||
static const uint8_t kGateStreamS[320] = {
|
static const uint8_t kGateStreamS[320] = {
|
||||||
0x90, 0x04, 0x90, 0x0E, 0x88, 0x08, 0x82, 0x0D, 0x88, 0x0C, 0x81, 0x03,
|
0x82, 0x04, 0x88, 0x0E, 0x88, 0x02, 0x88, 0x0D, 0x81, 0x04, 0x82, 0x06,
|
||||||
0x81, 0x0A, 0x81, 0x0D, 0x88, 0x09, 0x88, 0x0C, 0x82, 0x0E, 0x81, 0x07,
|
0x81, 0x0D, 0x81, 0x09, 0x81, 0x07, 0x88, 0x09, 0x84, 0x07, 0x81, 0x01,
|
||||||
0x81, 0x0D, 0x81, 0x0A, 0x90, 0x0D, 0x84, 0x01, 0x90, 0x05, 0x88, 0x0E,
|
0x90, 0x0C, 0x81, 0x09, 0x82, 0x0A, 0x88, 0x0D, 0x81, 0x0A, 0x84, 0x03,
|
||||||
0x81, 0x0A, 0x81, 0x0A, 0x90, 0x06, 0x88, 0x03, 0x82, 0x0B, 0x82, 0x0B,
|
0x88, 0x0E, 0x82, 0x05, 0x81, 0x08, 0x90, 0x0C, 0x81, 0x0C, 0x88, 0x0A,
|
||||||
0x88, 0x03, 0x88, 0x0A, 0x90, 0x0C, 0x81, 0x0E, 0x81, 0x0E, 0x90, 0x06,
|
0x82, 0x02, 0x81, 0x01, 0x90, 0x05, 0x84, 0x0A, 0x88, 0x0D, 0x82, 0x02,
|
||||||
0x88, 0x02, 0x82, 0x08, 0x81, 0x0A, 0x90, 0x0D, 0x90, 0x05, 0x81, 0x05,
|
0x90, 0x02, 0x81, 0x05, 0x88, 0x08, 0x81, 0x08, 0x88, 0x01, 0x82, 0x01,
|
||||||
0x88, 0x0B, 0x81, 0x05, 0x90, 0x0B, 0x88, 0x0E, 0x82, 0x09, 0x90, 0x07,
|
0x90, 0x08, 0x81, 0x0B, 0x81, 0x0A, 0x88, 0x0A, 0x90, 0x04, 0x81, 0x02,
|
||||||
0x81, 0x03, 0x81, 0x0D, 0x88, 0x09, 0x88, 0x0E, 0x84, 0x0A, 0x88, 0x0A,
|
0x82, 0x01, 0x90, 0x07, 0x82, 0x05, 0x90, 0x07, 0x81, 0x0D, 0x81, 0x0E,
|
||||||
0x82, 0x01, 0x90, 0x0C, 0x82, 0x01, 0x90, 0x0A, 0x82, 0x0A, 0x84, 0x03,
|
0x81, 0x07, 0x88, 0x09, 0x84, 0x04, 0x82, 0x01, 0x90, 0x0C, 0x81, 0x09,
|
||||||
0x81, 0x02, 0x90, 0x04, 0x84, 0x0E, 0x90, 0x09, 0x81, 0x0D, 0x90, 0x06,
|
0x81, 0x0A, 0x81, 0x05, 0x81, 0x03, 0x88, 0x03, 0x88, 0x02, 0x82, 0x01,
|
||||||
0x81, 0x02, 0x90, 0x0E, 0x90, 0x07, 0x82, 0x05, 0x81, 0x06, 0x90, 0x07,
|
0x81, 0x05, 0x90, 0x0B, 0x82, 0x08, 0x88, 0x05, 0x84, 0x0B, 0x90, 0x0A,
|
||||||
0x82, 0x03, 0x90, 0x07, 0x90, 0x0B, 0x82, 0x06, 0x88, 0x0E, 0x90, 0x08,
|
0x81, 0x0E, 0x81, 0x05, 0x88, 0x0B, 0x82, 0x0B, 0x81, 0x07, 0x90, 0x08,
|
||||||
0x84, 0x0A, 0x82, 0x07, 0x81, 0x04, 0x81, 0x03, 0x84, 0x05, 0x90, 0x08,
|
0x84, 0x02, 0x81, 0x02, 0x81, 0x0C, 0x82, 0x01, 0x81, 0x05, 0x90, 0x06,
|
||||||
0x81, 0x0C, 0x82, 0x01, 0x90, 0x0B, 0x81, 0x0A, 0x84, 0x05, 0x81, 0x02,
|
0x82, 0x04, 0x84, 0x04, 0x90, 0x04, 0x84, 0x0C, 0x81, 0x05, 0x81, 0x03,
|
||||||
0x82, 0x09, 0x90, 0x0E, 0x81, 0x02, 0x84, 0x03, 0x81, 0x0C, 0x81, 0x0C,
|
0x81, 0x01, 0x88, 0x01, 0x90, 0x01, 0x81, 0x0E, 0x88, 0x07, 0x82, 0x02,
|
||||||
0x81, 0x0D, 0x82, 0x08, 0x81, 0x03, 0x81, 0x0D, 0x81, 0x0A, 0x81, 0x0A,
|
0x81, 0x06, 0x90, 0x03, 0x81, 0x0C, 0x82, 0x04, 0x88, 0x0B, 0x90, 0x09,
|
||||||
0x81, 0x0C, 0x82, 0x06, 0x90, 0x0D, 0x88, 0x0C, 0x84, 0x04, 0x81, 0x01,
|
0x81, 0x0C, 0x88, 0x0D, 0x81, 0x02, 0x82, 0x03, 0x81, 0x06, 0x90, 0x02,
|
||||||
0x81, 0x0A, 0x81, 0x03, 0x90, 0x06, 0x88, 0x07, 0x88, 0x0E, 0x84, 0x0E,
|
0x90, 0x03, 0x84, 0x0E, 0x82, 0x03, 0x81, 0x05, 0x88, 0x02, 0x90, 0x01,
|
||||||
0x81, 0x0E, 0x82, 0x03, 0x90, 0x0E, 0x82, 0x05, 0x90, 0x02, 0x88, 0x03,
|
0x84, 0x0D, 0x84, 0x09, 0x82, 0x08, 0x82, 0x02, 0x84, 0x03, 0x84, 0x07,
|
||||||
0x81, 0x07, 0x81, 0x0A, 0x81, 0x0B, 0x81, 0x05, 0x84, 0x0D, 0x81, 0x01,
|
0x90, 0x0D, 0x82, 0x02, 0x88, 0x07, 0x84, 0x01, 0x90, 0x05, 0x81, 0x09,
|
||||||
0x82, 0x02, 0x90, 0x0C, 0x82, 0x03, 0x90, 0x08, 0x81, 0x05, 0x84, 0x08,
|
0x82, 0x02, 0x90, 0x06, 0x90, 0x04, 0x81, 0x05, 0x82, 0x06, 0x88, 0x0B,
|
||||||
0x84, 0x06, 0x88, 0x08, 0x81, 0x0E, 0x82, 0x01, 0x81, 0x01, 0x90, 0x08,
|
0x90, 0x03, 0x82, 0x04, 0x81, 0x06, 0x84, 0x08, 0x81, 0x0D, 0x81, 0x03,
|
||||||
0x82, 0x03, 0x90, 0x0C, 0x84, 0x02, 0x81, 0x09, 0x82, 0x04, 0x88, 0x0D,
|
0x81, 0x02, 0x82, 0x0B, 0x88, 0x0B, 0x81, 0x0E, 0x90, 0x06, 0x90, 0x08,
|
||||||
0x90, 0x07, 0x88, 0x0E, 0x82, 0x05, 0x81, 0x0C, 0x90, 0x0A, 0x81, 0x04,
|
0x88, 0x06, 0x81, 0x02, 0x81, 0x0D, 0x88, 0x03, 0x84, 0x07, 0x81, 0x07,
|
||||||
0x84, 0x06, 0x82, 0x02, 0x81, 0x09, 0x90, 0x0D, 0x82, 0x06, 0x82, 0x01,
|
0x81, 0x05, 0x81, 0x06, 0x81, 0x06, 0x81, 0x03, 0x82, 0x06, 0x81, 0x03,
|
||||||
0x90, 0x09, 0x81, 0x0B, 0x84, 0x04, 0x88, 0x07, 0x81, 0x0B, 0x81, 0x05,
|
0x88, 0x0A, 0x84, 0x08, 0x90, 0x0B, 0x90, 0x03, 0x82, 0x06, 0x84, 0x02,
|
||||||
0x82, 0x03, 0x81, 0x07, 0x90, 0x06, 0x82, 0x07,
|
0x84, 0x07, 0x90, 0x05, 0x82, 0x0E, 0x90, 0x04,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level U -- 802 ticks, 21 hook bytes, 1 fare stages, 4 pictures, 0 landings
|
||||||
|
// regenerate: tools/stsim/recorder levels/level21.dat U 20261006 60 2000
|
||||||
|
static const uint8_t kGateStreamU[320] = {
|
||||||
|
0x81, 0x04, 0x88, 0x02, 0x84, 0x08, 0x89, 0x02, 0x90, 0x01, 0x84, 0x05,
|
||||||
|
0x84, 0x08, 0x88, 0x0D, 0x84, 0x0A, 0x82, 0x03, 0x82, 0x08, 0x90, 0x0B,
|
||||||
|
0x88, 0x07, 0x90, 0x02, 0x90, 0x07, 0x81, 0x07, 0x90, 0x04, 0x81, 0x06,
|
||||||
|
0x88, 0x05, 0x84, 0x04, 0x81, 0x04, 0x88, 0x02, 0x88, 0x07, 0x84, 0x04,
|
||||||
|
0x81, 0x0A, 0x81, 0x03, 0x81, 0x0E, 0x82, 0x08, 0x81, 0x0B, 0x84, 0x0A,
|
||||||
|
0x84, 0x02, 0x81, 0x08, 0x84, 0x03, 0x90, 0x08, 0x88, 0x02, 0x81, 0x09,
|
||||||
|
0x81, 0x0A, 0x88, 0x03, 0x84, 0x09, 0x90, 0x06, 0x81, 0x06, 0x81, 0x07,
|
||||||
|
0x88, 0x03, 0x81, 0x07, 0x88, 0x05, 0x81, 0x0D, 0x81, 0x08, 0x88, 0x07,
|
||||||
|
0x84, 0x07, 0x81, 0x06, 0x81, 0x01, 0x81, 0x0A, 0x88, 0x0E, 0x81, 0x03,
|
||||||
|
0x81, 0x0C, 0x84, 0x02, 0x88, 0x0D, 0x81, 0x07, 0x84, 0x07, 0x82, 0x0A,
|
||||||
|
0x90, 0x09, 0x81, 0x0C, 0x84, 0x06, 0x88, 0x04, 0x81, 0x06, 0x81, 0x02,
|
||||||
|
0x81, 0x0E, 0x82, 0x02, 0x88, 0x0E, 0x81, 0x0B, 0x81, 0x06, 0x81, 0x02,
|
||||||
|
0x81, 0x09, 0x81, 0x0D, 0x81, 0x05, 0x82, 0x0E, 0x88, 0x04, 0x90, 0x08,
|
||||||
|
0x81, 0x0A, 0x81, 0x09, 0x84, 0x07, 0x81, 0x03, 0x81, 0x0A, 0x82, 0x09,
|
||||||
|
0x88, 0x07, 0x90, 0x05, 0x81, 0x09, 0x88, 0x02, 0x82, 0x09, 0x82, 0x0D,
|
||||||
|
0x81, 0x0C, 0x81, 0x01, 0x81, 0x07, 0x81, 0x0A, 0x84, 0x06, 0x90, 0x01,
|
||||||
|
0x81, 0x08, 0x81, 0x08, 0x81, 0x0D, 0x81, 0x09, 0x90, 0x01, 0x90, 0x08,
|
||||||
|
0x88, 0x04, 0x81, 0x02, 0x84, 0x04, 0x81, 0x0D, 0x84, 0x01, 0x88, 0x0A,
|
||||||
|
0x82, 0x06, 0x81, 0x03, 0x88, 0x0E, 0x88, 0x03, 0x81, 0x0B, 0x81, 0x0D,
|
||||||
|
0x82, 0x09, 0x88, 0x0C, 0x81, 0x07, 0x88, 0x0B, 0x81, 0x0B, 0x81, 0x09,
|
||||||
|
0x82, 0x0B, 0x81, 0x03, 0x88, 0x09, 0x81, 0x08, 0x88, 0x0A, 0x81, 0x01,
|
||||||
|
0x88, 0x05, 0x90, 0x08, 0x82, 0x02, 0x84, 0x05, 0x81, 0x0E, 0x90, 0x0A,
|
||||||
|
0x81, 0x0B, 0x81, 0x0D, 0x82, 0x05, 0x81, 0x0C, 0x84, 0x07, 0x88, 0x0A,
|
||||||
|
0x81, 0x09, 0x84, 0x05, 0x81, 0x02, 0x90, 0x0B, 0x82, 0x01, 0x81, 0x0D,
|
||||||
|
0x81, 0x09, 0x81, 0x05, 0x81, 0x0C, 0x81, 0x0D, 0x84, 0x02, 0x90, 0x08,
|
||||||
|
0x84, 0x0D, 0x90, 0x0C, 0x84, 0x01, 0x81, 0x0A, 0x82, 0x0E, 0x81, 0x0C,
|
||||||
|
0x81, 0x0A, 0x81, 0x09, 0x81, 0x04, 0x81, 0x08,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level V -- 1400 ticks, 1 hook bytes, 10 fare stages, 256 pictures, 25 landings
|
||||||
|
// regenerate: tools/stsim/recorder levels/level22.dat V 20261011 60 2000
|
||||||
|
static const uint8_t kGateStreamV[320] = {
|
||||||
|
0x88, 0x0D, 0x81, 0x02, 0x81, 0x09, 0x82, 0x0C, 0x90, 0x04, 0x81, 0x0B,
|
||||||
|
0x81, 0x01, 0x84, 0x0A, 0x84, 0x06, 0x90, 0x0E, 0x88, 0x0B, 0x81, 0x01,
|
||||||
|
0x90, 0x0C, 0x81, 0x03, 0x90, 0x02, 0x88, 0x0E, 0x90, 0x0E, 0x82, 0x0D,
|
||||||
|
0x84, 0x0A, 0x81, 0x06, 0x90, 0x04, 0x82, 0x05, 0x82, 0x02, 0x81, 0x06,
|
||||||
|
0x82, 0x0C, 0x84, 0x0B, 0x81, 0x05, 0x88, 0x09, 0x81, 0x02, 0x90, 0x0C,
|
||||||
|
0x81, 0x01, 0x84, 0x08, 0x88, 0x0E, 0x88, 0x08, 0x81, 0x01, 0x81, 0x07,
|
||||||
|
0x88, 0x07, 0x90, 0x04, 0x84, 0x0C, 0x90, 0x0B, 0x88, 0x0C, 0x88, 0x06,
|
||||||
|
0x82, 0x03, 0x81, 0x07, 0x84, 0x0C, 0x90, 0x0C, 0x84, 0x0C, 0x88, 0x0B,
|
||||||
|
0x84, 0x08, 0x81, 0x04, 0x90, 0x06, 0x88, 0x03, 0x82, 0x0D, 0x84, 0x04,
|
||||||
|
0x88, 0x07, 0x84, 0x04, 0x81, 0x01, 0x90, 0x0E, 0x84, 0x02, 0x88, 0x07,
|
||||||
|
0x81, 0x0A, 0x82, 0x02, 0x82, 0x09, 0x90, 0x0B, 0x84, 0x0B, 0x81, 0x06,
|
||||||
|
0x90, 0x0D, 0x90, 0x03, 0x84, 0x02, 0x82, 0x08, 0x81, 0x07, 0x88, 0x03,
|
||||||
|
0x88, 0x02, 0x81, 0x03, 0x90, 0x0C, 0x90, 0x06, 0x88, 0x08, 0x88, 0x0C,
|
||||||
|
0x88, 0x08, 0x81, 0x09, 0x82, 0x06, 0x90, 0x0B, 0x88, 0x0D, 0x90, 0x02,
|
||||||
|
0x81, 0x0C, 0x82, 0x0A, 0x84, 0x0B, 0x90, 0x0D, 0x81, 0x04, 0x90, 0x0C,
|
||||||
|
0x81, 0x04, 0x88, 0x02, 0x82, 0x06, 0x81, 0x02, 0x88, 0x06, 0x81, 0x07,
|
||||||
|
0x81, 0x0B, 0x81, 0x0E, 0x90, 0x07, 0x90, 0x06, 0x88, 0x0C, 0x81, 0x01,
|
||||||
|
0x90, 0x07, 0x90, 0x03, 0x81, 0x04, 0x81, 0x02, 0x84, 0x0A, 0x84, 0x0D,
|
||||||
|
0x84, 0x01, 0x84, 0x04, 0x88, 0x0B, 0x90, 0x09, 0x88, 0x0C, 0x90, 0x02,
|
||||||
|
0x84, 0x03, 0x81, 0x01, 0x90, 0x07, 0x90, 0x0A, 0x81, 0x06, 0x90, 0x05,
|
||||||
|
0x82, 0x08, 0x88, 0x0B, 0x81, 0x08, 0x88, 0x09, 0x84, 0x0C, 0x82, 0x05,
|
||||||
|
0x90, 0x02, 0x88, 0x0D, 0x81, 0x0E, 0x81, 0x0D, 0x84, 0x04, 0x81, 0x06,
|
||||||
|
0x82, 0x04, 0x90, 0x08, 0x84, 0x0E, 0x81, 0x01, 0x81, 0x05, 0x82, 0x07,
|
||||||
|
0x90, 0x09, 0x81, 0x01, 0x90, 0x07, 0x88, 0x07, 0x81, 0x03, 0x90, 0x0E,
|
||||||
|
0x88, 0x0C, 0x81, 0x01, 0x90, 0x05, 0x81, 0x06, 0x88, 0x01, 0x84, 0x04,
|
||||||
|
0x84, 0x01, 0x84, 0x0E, 0x90, 0x06, 0x81, 0x02, 0x81, 0x03, 0x88, 0x03,
|
||||||
|
0x82, 0x0A, 0x84, 0x02, 0x81, 0x04, 0x81, 0x0A,
|
||||||
|
};
|
||||||
|
|
||||||
|
// level Y -- 1400 ticks, 14 hook bytes, 10 fare stages, 256 pictures, 15 landings
|
||||||
|
// regenerate: tools/stsim/recorder levels/level25.dat Y 20261006 60 2000
|
||||||
|
static const uint8_t kGateStreamY[320] = {
|
||||||
|
0x88, 0x04, 0x88, 0x06, 0x84, 0x01, 0x90, 0x01, 0x88, 0x01, 0x84, 0x05,
|
||||||
|
0x81, 0x03, 0x84, 0x0B, 0x81, 0x0E, 0x88, 0x07, 0x81, 0x0E, 0x82, 0x0D,
|
||||||
|
0x81, 0x05, 0x81, 0x01, 0x82, 0x01, 0x88, 0x07, 0x84, 0x0A, 0x88, 0x0B,
|
||||||
|
0x84, 0x07, 0x88, 0x0D, 0x81, 0x07, 0x81, 0x03, 0x84, 0x04, 0x84, 0x02,
|
||||||
|
0x81, 0x06, 0x88, 0x08, 0x81, 0x0C, 0x82, 0x0E, 0x81, 0x0B, 0x81, 0x08,
|
||||||
|
0x81, 0x01, 0x82, 0x0D, 0x81, 0x04, 0x90, 0x04, 0x84, 0x06, 0x84, 0x01,
|
||||||
|
0x84, 0x0C, 0x81, 0x04, 0x81, 0x02, 0x90, 0x08, 0x88, 0x06, 0x81, 0x0B,
|
||||||
|
0x88, 0x05, 0x88, 0x08, 0x82, 0x0D, 0x81, 0x08, 0x88, 0x02, 0x81, 0x0E,
|
||||||
|
0x84, 0x07, 0x81, 0x04, 0x90, 0x0B, 0x88, 0x0E, 0x81, 0x05, 0x90, 0x0D,
|
||||||
|
0x90, 0x0B, 0x81, 0x01, 0x81, 0x05, 0x81, 0x06, 0x90, 0x0A, 0x90, 0x0E,
|
||||||
|
0x88, 0x01, 0x81, 0x0A, 0x82, 0x01, 0x88, 0x06, 0x90, 0x08, 0x81, 0x03,
|
||||||
|
0x84, 0x0E, 0x82, 0x03, 0x88, 0x06, 0x84, 0x0E, 0x81, 0x07, 0x88, 0x01,
|
||||||
|
0x90, 0x02, 0x90, 0x09, 0x81, 0x09, 0x88, 0x06, 0x81, 0x04, 0x88, 0x0B,
|
||||||
|
0x84, 0x0D, 0x81, 0x08, 0x84, 0x04, 0x82, 0x0B, 0x81, 0x0E, 0x81, 0x0A,
|
||||||
|
0x84, 0x01, 0x81, 0x0C, 0x81, 0x07, 0x81, 0x0A, 0x81, 0x07, 0x82, 0x0B,
|
||||||
|
0x84, 0x08, 0x90, 0x0B, 0x88, 0x08, 0x84, 0x0C, 0x90, 0x0C, 0x81, 0x06,
|
||||||
|
0x80, 0x0D, 0x84, 0x0E, 0x81, 0x05, 0x90, 0x0C, 0x81, 0x02, 0x90, 0x09,
|
||||||
|
0x82, 0x0D, 0x90, 0x0E, 0x88, 0x03, 0x81, 0x09, 0x81, 0x0D, 0x88, 0x03,
|
||||||
|
0x88, 0x0C, 0x90, 0x03, 0x81, 0x0E, 0x81, 0x03, 0x81, 0x04, 0x88, 0x05,
|
||||||
|
0x88, 0x04, 0x88, 0x02, 0x88, 0x01, 0x81, 0x0C, 0x88, 0x09, 0x82, 0x0A,
|
||||||
|
0x88, 0x09, 0x84, 0x0D, 0x90, 0x0C, 0x84, 0x03, 0x88, 0x07, 0x84, 0x0D,
|
||||||
|
0x90, 0x02, 0x84, 0x09, 0x82, 0x04, 0x84, 0x0C, 0x81, 0x0E, 0x81, 0x0D,
|
||||||
|
0x82, 0x0C, 0x81, 0x03, 0x90, 0x0D, 0x81, 0x08, 0x88, 0x0A, 0x81, 0x08,
|
||||||
|
0x84, 0x0A, 0x90, 0x0D, 0x88, 0x0B, 0x81, 0x0D, 0x82, 0x08, 0x90, 0x0D,
|
||||||
|
0x90, 0x0A, 0x90, 0x04, 0x81, 0x03, 0x84, 0x02, 0x88, 0x08, 0x90, 0x05,
|
||||||
|
0x81, 0x0D, 0x81, 0x05, 0x88, 0x06, 0x84, 0x06, 0x90, 0x04, 0x82, 0x01,
|
||||||
|
0x84, 0x0C, 0x82, 0x0D, 0x88, 0x08, 0x81, 0x0C,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -441,19 +699,27 @@ typedef struct {
|
||||||
} StGateStreamT;
|
} StGateStreamT;
|
||||||
|
|
||||||
static const StGateStreamT kGateStreams[] = {
|
static const StGateStreamT kGateStreams[] = {
|
||||||
|
{ 'A', kGateStreamA, (uint16_t)sizeof(kGateStreamA) },
|
||||||
|
{ 'B', kGateStreamB, (uint16_t)sizeof(kGateStreamB) },
|
||||||
|
{ 'C', kGateStreamC, (uint16_t)sizeof(kGateStreamC) },
|
||||||
|
{ 'D', kGateStreamD, (uint16_t)sizeof(kGateStreamD) },
|
||||||
|
{ 'E', kGateStreamE, (uint16_t)sizeof(kGateStreamE) },
|
||||||
|
{ 'F', kGateStreamF, (uint16_t)sizeof(kGateStreamF) },
|
||||||
{ 'G', kGateStreamG, (uint16_t)sizeof(kGateStreamG) },
|
{ 'G', kGateStreamG, (uint16_t)sizeof(kGateStreamG) },
|
||||||
{ 'I', kGateStreamI, (uint16_t)sizeof(kGateStreamI) },
|
{ 'I', kGateStreamI, (uint16_t)sizeof(kGateStreamI) },
|
||||||
|
{ 'J', kGateStreamJ, (uint16_t)sizeof(kGateStreamJ) },
|
||||||
{ 'K', kGateStreamK, (uint16_t)sizeof(kGateStreamK) },
|
{ 'K', kGateStreamK, (uint16_t)sizeof(kGateStreamK) },
|
||||||
|
{ 'L', kGateStreamL, (uint16_t)sizeof(kGateStreamL) },
|
||||||
|
{ 'M', kGateStreamM, (uint16_t)sizeof(kGateStreamM) },
|
||||||
|
{ 'N', kGateStreamN, (uint16_t)sizeof(kGateStreamN) },
|
||||||
{ 'O', kGateStreamO, (uint16_t)sizeof(kGateStreamO) },
|
{ 'O', kGateStreamO, (uint16_t)sizeof(kGateStreamO) },
|
||||||
|
{ 'P', kGateStreamP, (uint16_t)sizeof(kGateStreamP) },
|
||||||
{ 'Q', kGateStreamQ, (uint16_t)sizeof(kGateStreamQ) },
|
{ 'Q', kGateStreamQ, (uint16_t)sizeof(kGateStreamQ) },
|
||||||
{ 'R', kGateStreamR, (uint16_t)sizeof(kGateStreamR) },
|
{ 'R', kGateStreamR, (uint16_t)sizeof(kGateStreamR) },
|
||||||
|
{ 'S', kGateStreamS, (uint16_t)sizeof(kGateStreamS) },
|
||||||
{ 'U', kGateStreamU, (uint16_t)sizeof(kGateStreamU) },
|
{ 'U', kGateStreamU, (uint16_t)sizeof(kGateStreamU) },
|
||||||
{ 'V', kGateStreamV, (uint16_t)sizeof(kGateStreamV) },
|
{ 'V', kGateStreamV, (uint16_t)sizeof(kGateStreamV) },
|
||||||
{ 'E', kGateStreamE, (uint16_t)sizeof(kGateStreamE) },
|
{ 'Y', kGateStreamY, (uint16_t)sizeof(kGateStreamY) },
|
||||||
{ 'J', kGateStreamJ, (uint16_t)sizeof(kGateStreamJ) },
|
|
||||||
{ 'L', kGateStreamL, (uint16_t)sizeof(kGateStreamL) },
|
|
||||||
{ 'P', kGateStreamP, (uint16_t)sizeof(kGateStreamP) },
|
|
||||||
{ 'S', kGateStreamS, (uint16_t)sizeof(kGateStreamS) },
|
|
||||||
};
|
};
|
||||||
#define ST_GATE_STREAMS (sizeof(kGateStreams) / sizeof(kGateStreams[0]))
|
#define ST_GATE_STREAMS (sizeof(kGateStreams) / sizeof(kGateStreams[0]))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,17 @@
|
||||||
// recorder -- search for a demo input stream that exercises a level.
|
// recorder -- search for a demo input stream that exercises a level.
|
||||||
//
|
//
|
||||||
// The C64 ships attract recordings for four screens (H, W, T, X). The
|
// The C64 ships attract recordings for four screens (H, W, T, X). The
|
||||||
// other levels have none, so a refactor of their hooks had nothing to be
|
// other levels have none, so this searches for a synthetic ride instead:
|
||||||
// checked against. This searches for a synthetic ride instead: random
|
// random restarts plus hill climbing over (joystick mask, duration)
|
||||||
// restarts plus hill climbing over (joystick mask, duration) pairs,
|
// pairs, scored on how long the cab survives its first life and how much
|
||||||
// scored on how long the cab survives and how much of the level it
|
// of the level it reaches -- hook state bytes written, fare-machine
|
||||||
// reaches -- hook state bytes written, fare-machine stages entered,
|
// stages entered, distinct pictures drawn, pads landed on.
|
||||||
// distinct pictures drawn, pads landed on.
|
|
||||||
//
|
//
|
||||||
// The result is a REGRESSION fixture, not a recording: it pins current
|
// The search runs on this port's simulation; what proves the ride is
|
||||||
// behaviour so a refactor that changes nothing observable leaves the
|
// the original game: gate.sh c64 rides the stream on the C64 in VICE and
|
||||||
// trace identical. It says nothing about matching the real C64.
|
// the two traces must match byte for byte.
|
||||||
//
|
//
|
||||||
// Usage: recorder <level.dat> <letter> <seed> > stream.h
|
// Usage: recorder <level.dat> <letter> <seed> [restarts] [iterations] > stream.h
|
||||||
// Then assemble the per-level outputs into gateStreams.h.
|
// Then assemble the per-level outputs into gateStreams.h.
|
||||||
//
|
//
|
||||||
// Determinism: a candidate is only useful if it replays identically from
|
// Determinism: a candidate is only useful if it replays identically from
|
||||||
|
|
@ -41,8 +40,10 @@ void stAudioVoice1Freq(uint8_t v){(void)v;}
|
||||||
#define PAIRS 160
|
#define PAIRS 160
|
||||||
#define STREAMLEN (PAIRS * 2)
|
#define STREAMLEN (PAIRS * 2)
|
||||||
#define MAXTICKS 1400
|
#define MAXTICKS 1400
|
||||||
|
#define RESTARTS 120
|
||||||
|
#define ITERATIONS 3000
|
||||||
|
|
||||||
typedef struct { uint32_t score; uint32_t ticks; uint32_t cover; uint32_t lands; uint32_t stages; uint32_t scr; uint32_t near; } ResT;
|
typedef struct { uint32_t score; uint32_t ticks; uint32_t cover; uint32_t lands; uint32_t stages; uint32_t scr; uint32_t near; uint32_t exited; } ResT;
|
||||||
|
|
||||||
static uint32_t rs = 1;
|
static uint32_t rs = 1;
|
||||||
static uint32_t rnd(void){ rs = rs*1664525u + 1013904223u; return rs>>8; }
|
static uint32_t rnd(void){ rs = rs*1664525u + 1013904223u; return rs>>8; }
|
||||||
|
|
@ -59,6 +60,7 @@ static ResT evaluate(const uint8_t *stream, uint8_t *touchOut)
|
||||||
ResT r;
|
ResT r;
|
||||||
|
|
||||||
memset(stageSeen, 0, sizeof(stageSeen));
|
memset(stageSeen, 0, sizeof(stageSeen));
|
||||||
|
memset(&r, 0, sizeof(r));
|
||||||
memset(touched, 0, sizeof(touched));
|
memset(touched, 0, sizeof(touched));
|
||||||
// stSimNewGame deliberately PRESERVES flameParity, waveIdx and
|
// stSimNewGame deliberately PRESERVES flameParity, waveIdx and
|
||||||
// walkParity (the C64 carries them from screen to screen), so a
|
// walkParity (the C64 carries them from screen to screen), so a
|
||||||
|
|
@ -67,14 +69,22 @@ static ResT evaluate(const uint8_t *stream, uint8_t *touchOut)
|
||||||
// would then not reproduce from a cold start. Clear the whole struct
|
// would then not reproduce from a cold start. Clear the whole struct
|
||||||
// so every evaluation begins exactly where the gate will.
|
// so every evaluation begins exactly where the gate will.
|
||||||
memset(&gSim, 0, sizeof(gSim));
|
memset(&gSim, 0, sizeof(gSim));
|
||||||
|
// Exactly as stsim starts a gate ride.
|
||||||
stSimNewGame(&gSim, 1u, true);
|
stSimNewGame(&gSim, 1u, true);
|
||||||
stSimSeedDemoBuffer(&gSim, kDemoBufferInit, ST_DEMO_BUFFER_BYTES);
|
stSimSeedDemoBuffer(&gSim, kDemoBufferInit, ST_DEMO_BUFFER_BYTES);
|
||||||
|
(void)stSimAdvancePlayer(&gSim);
|
||||||
|
gSim.levelState = (uint8_t)(gLevel.levelIndex + 1u);
|
||||||
stSimEnterLevel(&gSim, &gLevel);
|
stSimEnterLevel(&gSim, &gLevel);
|
||||||
stSimSetDemoStream(&gSim, stream, STREAMLEN);
|
stSimSetDemoStream(&gSim, stream, STREAMLEN);
|
||||||
memcpy(base, &gSim.hookScratch, sizeof(base));
|
memcpy(base, &gSim.hookScratch, sizeof(base));
|
||||||
for (t = 0; t < MAXTICKS; t++) {
|
for (t = 0; t < MAXTICKS; t++) {
|
||||||
uint16_t k;
|
uint16_t k;
|
||||||
if (stSimTick(&gSim) != ST_TICK_CONTINUE) { break; }
|
StTickResultE res = stSimTick(&gSim);
|
||||||
|
if (res != ST_TICK_CONTINUE) {
|
||||||
|
/* Leaving through the hatch is a path of its own: reward it. */
|
||||||
|
r.exited = (res == ST_TICK_LEVEL_EXIT) ? 1u : 0u;
|
||||||
|
break;
|
||||||
|
}
|
||||||
{ const uint8_t *cur = (const uint8_t *)&gSim.hookScratch;
|
{ const uint8_t *cur = (const uint8_t *)&gSim.hookScratch;
|
||||||
for (k = 0; k < (uint16_t)sizeof(base); k++) {
|
for (k = 0; k < (uint16_t)sizeof(base); k++) {
|
||||||
if (cur[k] != base[k]) { touched[k] = 1; } } }
|
if (cur[k] != base[k]) { touched[k] = 1; } } }
|
||||||
|
|
@ -116,7 +126,7 @@ static ResT evaluate(const uint8_t *stream, uint8_t *touchOut)
|
||||||
r.ticks = t; r.cover = cover; r.lands = lands; r.stages = stages; r.scr = scrSeen;
|
r.ticks = t; r.cover = cover; r.lands = lands; r.stages = stages; r.scr = scrSeen;
|
||||||
r.near = nearest;
|
r.near = nearest;
|
||||||
r.score = cover * 30000u + stages * 30000u + lands * 4000u + t * 100u + scrSeen * 200u
|
r.score = cover * 30000u + stages * 30000u + lands * 4000u + t * 100u + scrSeen * 200u
|
||||||
+ (255u - nearest) * 60u;
|
+ (255u - nearest) * 60u + r.exited * 300000u;
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,20 +153,28 @@ int main(int argc, char **argv)
|
||||||
ResT bestR, curR;
|
ResT bestR, curR;
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
int restart, iter;
|
int restart, iter;
|
||||||
|
int restarts = RESTARTS;
|
||||||
|
int iterations = ITERATIONS;
|
||||||
|
|
||||||
if (argc < 4) { fprintf(stderr, "usage: recorder <level.dat> <name> <seed>\n"); return 2; }
|
if (argc < 4) { fprintf(stderr, "usage: recorder <level.dat> <name> <seed> [restarts] [iterations]\n"); return 2; }
|
||||||
fp = fopen(argv[1], "rb");
|
fp = fopen(argv[1], "rb");
|
||||||
if (!fp || !stLevelParse(&gLevel, fp)) { fprintf(stderr, "cannot load %s\n", argv[1]); return 1; }
|
if (!fp || !stLevelParse(&gLevel, fp)) { fprintf(stderr, "cannot load %s\n", argv[1]); return 1; }
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
rs = (uint32_t)strtoul(argv[3], NULL, 0);
|
rs = (uint32_t)strtoul(argv[3], NULL, 0);
|
||||||
|
if (argc > 4) {
|
||||||
|
restarts = (int)strtol(argv[4], NULL, 0);
|
||||||
|
}
|
||||||
|
if (argc > 5) {
|
||||||
|
iterations = (int)strtol(argv[5], NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
randomise(best, 0);
|
randomise(best, 0);
|
||||||
bestR = evaluate(best, NULL);
|
bestR = evaluate(best, NULL);
|
||||||
for (restart = 0; restart < 120; restart++) {
|
for (restart = 0; restart < restarts; restart++) {
|
||||||
memcpy(cur, best, STREAMLEN);
|
memcpy(cur, best, STREAMLEN);
|
||||||
if (restart > 0) { randomise(cur, (int)(rnd() % PAIRS)); }
|
if (restart > 0) { randomise(cur, (int)(rnd() % PAIRS)); }
|
||||||
curR = evaluate(cur, NULL);
|
curR = evaluate(cur, NULL);
|
||||||
for (iter = 0; iter < 3000; iter++) {
|
for (iter = 0; iter < iterations; iter++) {
|
||||||
uint8_t trial[STREAMLEN];
|
uint8_t trial[STREAMLEN];
|
||||||
ResT tr;
|
ResT tr;
|
||||||
int j, nmut = 1 + (int)(rnd() % 3u);
|
int j, nmut = 1 + (int)(rnd() % 3u);
|
||||||
|
|
@ -182,10 +200,10 @@ int main(int argc, char **argv)
|
||||||
if (curR.score > bestR.score) { memcpy(best, cur, STREAMLEN); bestR = curR; }
|
if (curR.score > bestR.score) { memcpy(best, cur, STREAMLEN); bestR = curR; }
|
||||||
}
|
}
|
||||||
bestR = evaluate(best, touched);
|
bestR = evaluate(best, touched);
|
||||||
fprintf(stderr, "%s: ticks=%u hookBytes=%u stages=%u pictures=%u lands=%u nearPad=%u\n",
|
fprintf(stderr, "%s: ticks=%u hookBytes=%u stages=%u pictures=%u lands=%u nearPad=%u exited=%u\n",
|
||||||
argv[2], bestR.ticks, bestR.cover, bestR.stages, bestR.scr, bestR.lands, bestR.near);
|
argv[2], bestR.ticks, bestR.cover, bestR.stages, bestR.scr, bestR.lands, bestR.near, bestR.exited);
|
||||||
printf("// %s: %u ticks, %u hook bytes, %u fare stages, %u pictures, %u landings\n",
|
printf("// %s: %u ticks, %u hook bytes, %u fare stages, %u pictures, %u landings%s\n",
|
||||||
argv[2], bestR.ticks, bestR.cover, bestR.stages, bestR.scr, bestR.lands);
|
argv[2], bestR.ticks, bestR.cover, bestR.stages, bestR.scr, bestR.lands, bestR.exited ? ", left through the hatch" : "");
|
||||||
printf("static const uint8_t kGateStream%s[%d] = {", argv[2], STREAMLEN);
|
printf("static const uint8_t kGateStream%s[%d] = {", argv[2], STREAMLEN);
|
||||||
{ int i; for (i = 0; i < STREAMLEN; i++) {
|
{ int i; for (i = 0; i < STREAMLEN; i++) {
|
||||||
if (i % 12 == 0) { printf("\n "); }
|
if (i % 12 == 0) { printf("\n "); }
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,33 @@
|
||||||
// stsim -- host-side driver for the Space Taxi simulation core.
|
// stsim -- host-side driver for the Space Taxi simulation core.
|
||||||
//
|
//
|
||||||
// Loads an STL4 level, attaches a recorded demo input stream and runs
|
// Loads an STL5 level, attaches a recorded demo input stream and runs
|
||||||
// the game tick, printing one line of state per tick in the same shape
|
// the game tick, printing one line of state per tick -- exactly the line
|
||||||
// the VICE tracer records (see stuff/ and the compare script),
|
// c64trace.py prints from the real game in VICE, so the two traces can
|
||||||
// so the port's engine can be diffed against the real machine.
|
// be compared byte for byte.
|
||||||
//
|
//
|
||||||
// stsim <level.dat> <level letter> <ticks>
|
// stsim <level.dat> <level letter> <ticks>
|
||||||
//
|
//
|
||||||
// H, W, T and X use the C64's own attract recordings; every other
|
// H, W, T and X use the C64's own attract recordings; every other
|
||||||
// letter uses a synthesised gate fixture from gateStreams.h.
|
// letter uses a synthesised gate fixture from gateStreams.h.
|
||||||
|
//
|
||||||
|
// A ride is the attract demo's input and random numbers on the screen as
|
||||||
|
// the 24-hour shift reaches it ($7215 = the screen's place, A = 1 .. Y =
|
||||||
|
// 25), on the game's crash path: a finished wreck costs a cab and the cab respawns
|
||||||
|
// (stSimLoseCab, stSimRespawn -- c64trace.py sends the real machine down
|
||||||
|
// $6B64 too), until the cabs run out, the cab leaves the screen, the
|
||||||
|
// ticks are done, or the playback reaches the buffer's last pair -- past
|
||||||
|
// that the C64 would read on into whatever follows $0B81.
|
||||||
|
//
|
||||||
|
// STSIM_DEMOBUF=1 prints the ride's whole playback buffer ($0902, the
|
||||||
|
// boot-time image with the stream laid over it) as one hex line instead
|
||||||
|
// of running it: c64trace.py loads exactly those bytes into the real
|
||||||
|
// game, so both machines ride the same input.
|
||||||
|
//
|
||||||
|
// STSIM_RAW=<file> also appends each tick's picture to <file> -- screen
|
||||||
|
// RAM, colour RAM and the effective charset, 1000 + 1000 + 2048 bytes a
|
||||||
|
// tick -- so a hash mismatch can be traced to the cells and glyphs that
|
||||||
|
// differ (c64trace.py writes the same layout from the real machine, and
|
||||||
|
// compare.py --picture lists the differences).
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
@ -20,9 +39,33 @@
|
||||||
#include "gateStreams.h"
|
#include "gateStreams.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define ST_CHARSET_BYTES (ST_CHARSET_CHARS * 8u)
|
||||||
|
// The playback buffer's last (mask, count) pair: reading on from it, the
|
||||||
|
// sim stops at the buffer's end while the C64 reads past $0B81.
|
||||||
|
#define ST_DEMO_LAST_PAIR (ST_DEMO_BUFFER_BYTES - 2u)
|
||||||
|
|
||||||
|
|
||||||
|
static void celCensusReport(const StLevelT *level);
|
||||||
|
static void celCensusTick(const StSimT *sim);
|
||||||
|
static uint32_t charsetHash(const StSimT *sim);
|
||||||
|
static void charsetImage(const StSimT *sim, uint8_t *image);
|
||||||
|
static uint32_t hashBytes(const uint8_t *p, size_t n);
|
||||||
|
static void printDemoBuffer(const StSimT *sim);
|
||||||
|
static void printState(const StSimT *sim, uint32_t tick);
|
||||||
|
static void writeRaw(FILE *raw, const StSimT *sim);
|
||||||
|
|
||||||
|
|
||||||
static StLevelT gLevel;
|
static StLevelT gLevel;
|
||||||
static StSimT gSim;
|
static StSimT gSim;
|
||||||
|
|
||||||
|
// STSIM_CELS=1: every level cel (ptr below the baked set) a ride
|
||||||
|
// displays, as (ptr, colour, multi, mc0, mc1), against what
|
||||||
|
// stLevelCelList bakes for the level -- a cel shown but not listed is
|
||||||
|
// built and drawn interpreted mid-ride on the real ports.
|
||||||
|
#define CENSUS_MAX 64u
|
||||||
|
static StCelDefT gCensus[CENSUS_MAX];
|
||||||
|
static uint8_t gCensusCount;
|
||||||
|
|
||||||
|
|
||||||
void stAudioSfx(const uint8_t *program9) { (void)program9; }
|
void stAudioSfx(const uint8_t *program9) { (void)program9; }
|
||||||
void stAudioNoise(bool on) { (void)on; }
|
void stAudioNoise(bool on) { (void)on; }
|
||||||
|
|
@ -34,45 +77,24 @@ void stAudioVoice2(uint8_t freqLo, uint8_t freqHi, uint8_t ctrl) { (void)freqLo;
|
||||||
void stAudioVoice1Freq(uint8_t value) { (void)value; }
|
void stAudioVoice1Freq(uint8_t value) { (void)value; }
|
||||||
|
|
||||||
|
|
||||||
// FNV-1a over a byte span: the picture the player actually sees. The
|
static void celCensusReport(const StLevelT *level) {
|
||||||
// hooks that animate the screen (O, R, V, W, X) change screen, colour or
|
StCelDefT listed[ST_LEVEL_CELS_MAX];
|
||||||
// charset bytes and nothing else this line used to capture.
|
uint8_t n = stLevelCelList(level, listed, ST_LEVEL_CELS_MAX);
|
||||||
static uint32_t hashBytes(const uint8_t *p, size_t n) {
|
uint8_t k;
|
||||||
uint32_t h = 2166136261u;
|
uint8_t j;
|
||||||
size_t i;
|
|
||||||
|
|
||||||
for (i = 0u; i < n; i++) {
|
for (k = 0u; k < gCensusCount; k++) {
|
||||||
h = (h ^ p[i]) * 16777619u;
|
for (j = 0u; j < n; j++) {
|
||||||
|
if (memcmp(&listed[j], &gCensus[k], sizeof(StCelDefT)) == 0) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return h;
|
|
||||||
}
|
}
|
||||||
|
fprintf(stderr, "CEL %s ptr=%02X color=%u multi=%u mc=%u/%u\n", (j < n) ? "listed " : "MISSING", gCensus[k].ptr, gCensus[k].color, gCensus[k].multi, gCensus[k].mc0, gCensus[k].mc1);
|
||||||
|
|
||||||
// The charset the game would be showing. The sim no longer holds a
|
|
||||||
// contiguous 2 KB copy -- edited glyphs live in an override table -- so
|
|
||||||
// rebuild the effective image here. Hashing this keeps the trace value
|
|
||||||
// identical to the pre-override captures, which is what lets the goldens
|
|
||||||
// prove the change is behaviour-preserving.
|
|
||||||
static uint32_t charsetHash(const StSimT *sim) {
|
|
||||||
uint8_t image[ST_CHARSET_CHARS * 8u];
|
|
||||||
uint16_t ch;
|
|
||||||
|
|
||||||
for (ch = 0u; ch < ST_CHARSET_CHARS; ch++) {
|
|
||||||
memcpy(&image[ch * 8u], stSimGlyph(sim, (uint8_t)ch), 8u);
|
|
||||||
}
|
}
|
||||||
return hashBytes(image, sizeof(image));
|
fprintf(stderr, "CENSUS shown=%u listed=%u\n", gCensusCount, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// STSIM_CELS=1: every level cel (ptr below the baked set) a ride
|
|
||||||
// displays, as (ptr, colour, multi, mc0, mc1), against what
|
|
||||||
// stLevelCelList bakes for the level -- a cel shown but not listed is
|
|
||||||
// built and drawn interpreted mid-ride on the real ports.
|
|
||||||
#define CENSUS_MAX 64u
|
|
||||||
static StCelDefT gCensus[CENSUS_MAX];
|
|
||||||
static uint8_t gCensusCount;
|
|
||||||
|
|
||||||
|
|
||||||
static void celCensusTick(const StSimT *sim) {
|
static void celCensusTick(const StSimT *sim) {
|
||||||
uint8_t i;
|
uint8_t i;
|
||||||
|
|
||||||
|
|
@ -105,40 +127,75 @@ static void celCensusTick(const StSimT *sim) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void celCensusReport(const StLevelT *level) {
|
// The charset the game would be showing. The sim no longer holds a
|
||||||
StCelDefT listed[ST_LEVEL_CELS_MAX];
|
// contiguous 2 KB copy -- edited glyphs live in an override table -- so
|
||||||
uint8_t n = stLevelCelList(level, listed, ST_LEVEL_CELS_MAX);
|
// rebuild the effective image here. Hashing this keeps the trace value
|
||||||
uint8_t k;
|
// identical to the pre-override captures, which is what lets the goldens
|
||||||
uint8_t j;
|
// prove the change is behaviour-preserving.
|
||||||
|
static uint32_t charsetHash(const StSimT *sim) {
|
||||||
|
uint8_t image[ST_CHARSET_BYTES];
|
||||||
|
|
||||||
for (k = 0u; k < gCensusCount; k++) {
|
charsetImage(sim, image);
|
||||||
for (j = 0u; j < n; j++) {
|
return hashBytes(image, sizeof(image));
|
||||||
if (memcmp(&listed[j], &gCensus[k], sizeof(StCelDefT)) == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fprintf(stderr, "CEL %s ptr=%02X color=%u multi=%u mc=%u/%u\n", (j < n) ? "listed " : "MISSING", gCensus[k].ptr, gCensus[k].color, gCensus[k].multi, gCensus[k].mc0, gCensus[k].mc1);
|
|
||||||
}
|
|
||||||
fprintf(stderr, "CENSUS shown=%u listed=%u\n", gCensusCount, n);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void celCensusReport(const StLevelT *level);
|
static void charsetImage(const StSimT *sim, uint8_t *image) {
|
||||||
static void celCensusTick(const StSimT *sim);
|
uint16_t ch;
|
||||||
|
|
||||||
|
for (ch = 0u; ch < ST_CHARSET_CHARS; ch++) {
|
||||||
|
memcpy(&image[ch * 8u], stSimGlyph(sim, (uint8_t)ch), 8u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// FNV-1a over a byte span: the picture the player actually sees. The
|
||||||
|
// hooks that animate the screen (O, R, V, W, X) change screen, colour or
|
||||||
|
// charset bytes and nothing else this line used to capture.
|
||||||
|
static uint32_t hashBytes(const uint8_t *p, size_t n) {
|
||||||
|
uint32_t h = 2166136261u;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0u; i < n; i++) {
|
||||||
|
h = (h ^ p[i]) * 16777619u;
|
||||||
|
}
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void printDemoBuffer(const StSimT *sim) {
|
||||||
|
size_t k;
|
||||||
|
|
||||||
|
printf("{\"demobuf\":\"");
|
||||||
|
for (k = 0u; k < sizeof(sim->demoBuf); k++) {
|
||||||
|
printf("%02X", sim->demoBuf[k]);
|
||||||
|
}
|
||||||
|
printf("\"}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void printState(const StSimT *sim, uint32_t tick) {
|
static void printState(const StSimT *sim, uint32_t tick) {
|
||||||
|
bool shown1 = (sim->frame.enableMask & kStBit[1]) != 0u;
|
||||||
uint8_t k;
|
uint8_t k;
|
||||||
|
|
||||||
printf("{\"tick\":%u,\"x\":\"%02X%02X.%02X\",\"y\":\"%02X.%02X\",\"vx\":\"%04X\",\"vy\":\"%04X\","
|
printf("{\"tick\":%u,\"x\":\"%02X%02X.%02X\",\"y\":\"%02X.%02X\",\"vx\":\"%04X\",\"vy\":\"%04X\","
|
||||||
"\"pad\":%u,\"st\":%u,\"ph\":%u,\"in\":\"%02X\",\"dir\":\"%02X\",\"ptr0\":\"%02X\",\"ptr1\":\"%02X\","
|
"\"pad\":%u,\"st\":%u,\"ph\":%u,\"in\":\"%02X\",\"dir\":\"%02X\",\"ptr0\":\"%02X\",",
|
||||||
"\"s1\":\"%02X%02X.%02X\",\"en\":\"%02X\",\"t4740\":\"%02X\",\"off\":%u,\"rng\":\"%02X%02X\","
|
|
||||||
"\"fuel\":%u,\"slots\":%u,\"asi\":%u,\"score\":\"",
|
|
||||||
tick, sim->posXmsb, sim->posXcol, sim->posXlo, sim->posYrow, sim->posYlo,
|
tick, sim->posXmsb, sim->posXcol, sim->posXlo, sim->posYrow, sim->posYlo,
|
||||||
(uint16_t)sim->velX, (uint16_t)sim->velY, sim->activePad, sim->stage, sim->collisionPhase,
|
(uint16_t)sim->velX, (uint16_t)sim->velY, sim->activePad, sim->stage, sim->collisionPhase,
|
||||||
sim->inputMask, sim->dirMask, sim->spr[0].ptr, sim->spr[1].ptr,
|
sim->inputMask, sim->dirMask, sim->spr[0].ptr);
|
||||||
sim->spr[1].msb, sim->spr[1].col, sim->spr[1].row, sim->frame.enableMask,
|
// A hidden sprite's registers are not part of the picture, and the
|
||||||
sim->demoTimer, sim->demoOff, sim->rngT1, sim->rngT2, sim->fuelCells, sim->fareSlotCount, sim->activeSpriteIdx);
|
// C64 leaves whatever the screen before set in them: sprite 1's
|
||||||
|
// shadow and every hidden sprite's displayed position print as
|
||||||
|
// dashes, on the real machine's trace too.
|
||||||
|
if (shown1) {
|
||||||
|
printf("\"ptr1\":\"%02X\",\"s1\":\"%02X%02X.%02X\",", sim->spr[1].ptr, sim->spr[1].msb, sim->spr[1].col, sim->spr[1].row);
|
||||||
|
} else {
|
||||||
|
printf("\"ptr1\":\"--\",\"s1\":\"--\",");
|
||||||
|
}
|
||||||
|
printf("\"en\":\"%02X\",\"t4740\":\"%02X\",\"off\":%u,\"rng\":\"%02X%02X\","
|
||||||
|
"\"fuel\":%u,\"slots\":%u,\"asi\":%u,\"score\":\"",
|
||||||
|
sim->frame.enableMask, sim->demoTimer, sim->demoOff, sim->rngT1, sim->rngT2,
|
||||||
|
sim->fuelCells, sim->fareSlotCount, sim->activeSpriteIdx);
|
||||||
for (k = 0u; k < ST_NUMBER_CHARS; k++) {
|
for (k = 0u; k < ST_NUMBER_CHARS; k++) {
|
||||||
printf("%02X", sim->screen[ST_CELL_SCORE + k]);
|
printf("%02X", sim->screen[ST_CELL_SCORE + k]);
|
||||||
}
|
}
|
||||||
|
|
@ -152,7 +209,11 @@ static void printState(const StSimT *sim, uint32_t tick) {
|
||||||
}
|
}
|
||||||
printf("\",\"sprx\":\"");
|
printf("\",\"sprx\":\"");
|
||||||
for (k = 0u; k < ST_HW_SPRITES; k++) {
|
for (k = 0u; k < ST_HW_SPRITES; k++) {
|
||||||
|
if ((sim->frame.enableMask & kStBit[k]) != 0u) {
|
||||||
printf("%03X%02X", sim->frame.x[k], sim->frame.y[k]);
|
printf("%03X%02X", sim->frame.x[k], sim->frame.y[k]);
|
||||||
|
} else {
|
||||||
|
printf("-----");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
printf("\",\"scr\":\"%08X\",\"col\":\"%08X\",\"chr\":\"%08X\",\"bd\":\"%02X\",\"bg\":\"%02X\"}\n",
|
printf("\",\"scr\":\"%08X\",\"col\":\"%08X\",\"chr\":\"%08X\",\"bd\":\"%02X\",\"bg\":\"%02X\"}\n",
|
||||||
hashBytes(sim->screen, ST_SCREEN_CELLS),
|
hashBytes(sim->screen, ST_SCREEN_CELLS),
|
||||||
|
|
@ -162,8 +223,21 @@ static void printState(const StSimT *sim, uint32_t tick) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// STSIM_RAW: one tick's picture (see the top of the file).
|
||||||
|
static void writeRaw(FILE *raw, const StSimT *sim) {
|
||||||
|
uint8_t image[ST_CHARSET_BYTES];
|
||||||
|
|
||||||
|
charsetImage(sim, image);
|
||||||
|
fwrite(sim->screen, 1u, ST_SCREEN_CELLS, raw);
|
||||||
|
fwrite(sim->color, 1u, ST_SCREEN_CELLS, raw);
|
||||||
|
fwrite(image, 1u, sizeof(image), raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
|
FILE *raw = NULL;
|
||||||
|
const char *rawPath;
|
||||||
const uint8_t *stream = kDemoStreamH;
|
const uint8_t *stream = kDemoStreamH;
|
||||||
uint16_t len = (uint16_t)sizeof(kDemoStreamH);
|
uint16_t len = (uint16_t)sizeof(kDemoStreamH);
|
||||||
uint8_t want;
|
uint8_t want;
|
||||||
|
|
@ -205,34 +279,63 @@ int main(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ticks = (uint32_t)strtoul(argv[3], NULL, 0);
|
ticks = (uint32_t)strtoul(argv[3], NULL, 0);
|
||||||
|
// A fresh one-player demo, the flame parity, wave index and walk
|
||||||
|
// parity at zero (c64trace.py zeroes them on the real machine too).
|
||||||
|
// The recorder starts its rides the same way.
|
||||||
stSimNewGame(&gSim, 1u, true);
|
stSimNewGame(&gSim, 1u, true);
|
||||||
stSimSeedDemoBuffer(&gSim, kDemoBufferInit, ST_DEMO_BUFFER_BYTES);
|
stSimSeedDemoBuffer(&gSim, kDemoBufferInit, ST_DEMO_BUFFER_BYTES);
|
||||||
if (argc > 4) {
|
// $61FB, as startDemoScreen runs it: the turn wraps to player 0 and
|
||||||
// carried-over state: flame parity, wave index, walk parity
|
// the screens-played count ($7215, this one included) is set before
|
||||||
gSim.flameParity = (uint8_t)strtoul(argv[4], NULL, 16);
|
// the screen is loaded -- to the screen's place in the 24-hour shift
|
||||||
}
|
// (A is the first, Y the 25th), so the rides cover the takeoff fare of
|
||||||
if (argc > 5) {
|
// every screen after the second and the finished shift's HUD.
|
||||||
gSim.waveIdx = (uint8_t)strtoul(argv[5], NULL, 16);
|
(void)stSimAdvancePlayer(&gSim);
|
||||||
}
|
gSim.levelState = (uint8_t)(gLevel.levelIndex + 1u);
|
||||||
if (argc > 6) {
|
|
||||||
gSim.walkParity = (uint8_t)strtoul(argv[6], NULL, 16);
|
|
||||||
}
|
|
||||||
stSimEnterLevel(&gSim, &gLevel);
|
stSimEnterLevel(&gSim, &gLevel);
|
||||||
stSimSetDemoStream(&gSim, stream, len);
|
stSimSetDemoStream(&gSim, stream, len);
|
||||||
|
if (getenv("STSIM_DEMOBUF") != NULL) {
|
||||||
|
printDemoBuffer(&gSim);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
rawPath = getenv("STSIM_RAW");
|
||||||
|
if (rawPath != NULL) {
|
||||||
|
raw = fopen(rawPath, "wb");
|
||||||
|
if (raw == NULL) {
|
||||||
|
fprintf(stderr, "stsim: cannot write %s\n", rawPath);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
for (t = 0u; t < ticks; t++) {
|
for (t = 0u; t < ticks; t++) {
|
||||||
StTickResultE r;
|
StTickResultE r;
|
||||||
printState(&gSim, t);
|
printState(&gSim, t);
|
||||||
|
if (raw != NULL) {
|
||||||
|
writeRaw(raw, &gSim);
|
||||||
|
}
|
||||||
r = stSimTick(&gSim);
|
r = stSimTick(&gSim);
|
||||||
if (getenv("STSIM_CELS") != NULL) {
|
if (getenv("STSIM_CELS") != NULL) {
|
||||||
celCensusTick(&gSim);
|
celCensusTick(&gSim);
|
||||||
}
|
}
|
||||||
|
if (r == ST_TICK_CRASH_DONE) {
|
||||||
|
r = stSimLoseCab(&gSim);
|
||||||
|
if (r == ST_TICK_LIFE_LOST) {
|
||||||
|
stSimRespawn(&gSim);
|
||||||
|
r = ST_TICK_CONTINUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (r != ST_TICK_CONTINUE) {
|
if (r != ST_TICK_CONTINUE) {
|
||||||
printf("{\"tick\":%u,\"end\":%d}\n", t, (int)r);
|
printf("{\"tick\":%u,\"end\":%d}\n", t, (int)r);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (gSim.demoOff >= ST_DEMO_LAST_PAIR) {
|
||||||
|
printf("{\"tick\":%u,\"end\":\"stream\"}\n", t);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (getenv("STSIM_CELS") != NULL) {
|
if (getenv("STSIM_CELS") != NULL) {
|
||||||
celCensusReport(&gLevel);
|
celCensusReport(&gLevel);
|
||||||
}
|
}
|
||||||
|
if (raw != NULL) {
|
||||||
|
fclose(raw);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue