Updates to match JoeyLib.
This commit is contained in:
parent
e45707bd27
commit
df36b0ae84
32 changed files with 394 additions and 393 deletions
|
|
@ -914,7 +914,7 @@ kills all audio, then either loops waiting for release or transitions.
|
|||
CORRECTED 2026-10-06 (the old transcript here had the branches wrong):
|
||||
|
||||
```
|
||||
LDA #$0B; STA $DBA2,$DBA3,$DBCA,$DBCB ; both lamp pairs dark grey
|
||||
LDA #$0B; STA $DBA2,$DBA3,$DBCA,$DBCB ; both lamp pairs dark gray
|
||||
INC $71BE; AND #$07; STA $71BE ; mod-8 ticker
|
||||
CMP #$05; BPL RTS ; lit on 5 ticks of 8
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ $ scripts/importData.sh PATH...
|
|||
```
|
||||
|
||||
PATH is the image itself, a folder holding it, or a `.zip` or `.tar.gz`
|
||||
with it inside. It is recognised by its checksum and installed as
|
||||
with it inside. It is recognized by its checksum and installed as
|
||||
`stuff/raw.bin`, which git ignores. Without it the game still builds; only
|
||||
the levels (and the per-level sprite banks baked from them) are left out,
|
||||
and make says so.
|
||||
|
|
|
|||
4
TODO.md
4
TODO.md
|
|
@ -5,7 +5,7 @@ moved out of JoeyLib's tree into this repository; JoeyLib's own list
|
|||
(library and platform work) stays in JoeyLib.
|
||||
|
||||
- [ ] **The C64's flown-sprite warp.** The level-name intro animates its
|
||||
warp by cycling the colours of a static star field painted into
|
||||
warp by cycling the colors of a static star field painted into
|
||||
screen RAM, on every target (stTitle.c, `starPaletteApply` in
|
||||
stRender.c). The C64 flies seven star sprites out along its rays
|
||||
instead. That version, carry-flag exact ($44F8/$4500/$450C/$4513/
|
||||
|
|
@ -52,5 +52,5 @@ moved out of JoeyLib's tree into this repository; JoeyLib's own list
|
|||
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
|
||||
tick fits; modeling 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.
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ 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
|
||||
screen RAM, color 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.
|
||||
|
||||
|
|
@ -114,7 +114,7 @@ What the comparison found wrong in the port, each fixed from the asm:
|
|||
| 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
|
||||
Known and not modeled: 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
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#!/usr/bin/env python3
|
||||
# Bake assets/songs/song*.song into stSongData.h with JoeyLib's songbake
|
||||
# (JOEYLIB names the checkout, ../joeylib by default).
|
||||
# (JOEYLIB names the checkout, ../joeylib by default; its make/tools.mk
|
||||
# builds the tool, into JOEY_BUILD's tools/).
|
||||
#
|
||||
# python3 assets/genSongData.py
|
||||
#
|
||||
|
|
@ -9,12 +10,11 @@
|
|||
# assets/sidToSong.py turns that into songbake text.
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
here = os.path.dirname(os.path.abspath(__file__))
|
||||
joeylib = os.environ.get("JOEYLIB", os.path.join(here, "..", "..", "joeylib"))
|
||||
songbake = os.path.join(joeylib, "tools", "songbake", "songbake.py")
|
||||
songbake = os.path.join(os.environ.get("JOEY_BUILD", os.path.join(joeylib, "build")), "tools", "songbake")
|
||||
out = os.path.join(here, "..", "stSongData.h")
|
||||
count = 9
|
||||
|
||||
|
|
@ -60,12 +60,13 @@ static const StSongT kStSongs[ST_SONG_COUNT] = {
|
|||
#endif // ST_SONG_DATA_H
|
||||
"""
|
||||
|
||||
subprocess.run(["make", "-s", "-f", os.path.join(joeylib, "make", "tools.mk"), "songbake"], check=True, stdout=subprocess.DEVNULL)
|
||||
parts = []
|
||||
for n in range(count):
|
||||
src = os.path.join(here, "songs", "song%d.song" % n)
|
||||
with tempfile.NamedTemporaryFile(suffix=".h", delete=False) as tmp:
|
||||
path = tmp.name
|
||||
subprocess.run([sys.executable, songbake, "-c", "gStSong%d" % n, src, path], check=True, stderr=subprocess.DEVNULL)
|
||||
subprocess.run([songbake, "-c", "gStSong%d" % n, src, path], check=True, stderr=subprocess.DEVNULL)
|
||||
body = open(path).read()
|
||||
os.unlink(path)
|
||||
body = "\n".join(l for l in body.split("\n") if l.startswith("static") or l.startswith(" ") or l.startswith("}"))
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ def write_level(path, level_index, after, screen, colormap, sprite_blocks):
|
|||
Everything stays in the C64's own units so the runtime can mirror the
|
||||
original routines byte for byte:
|
||||
name[22] $7D78-$7D8D level name (screen codes, ASCII-compatible)
|
||||
header[9] $7D00-$7D08 VIC colours: border, bg, bg1-3, mc0, mc1,
|
||||
header[9] $7D00-$7D08 VIC colors: border, bg, bg1-3, mc0, mc1,
|
||||
sprite-0, sprite-1
|
||||
padCount $7D5A pads in the table
|
||||
specialPad $7D09 fare pads (== padCount, or padCount-1 when
|
||||
|
|
@ -78,7 +78,7 @@ def write_level(path, level_index, after, screen, colormap, sprite_blocks):
|
|||
pads[10][8] $7D0A-$7D59 raw slots: x1 hi/lo, x2 hi/lo, row,
|
||||
passenger X frac, passenger X col, unused
|
||||
tilemap[1000] screen RAM (all 25 rows, the HUD rows included)
|
||||
colormap[1000] colour RAM low nibbles
|
||||
colormap[1000] color RAM low nibbles
|
||||
levelIndex 0..23 = A..X, 24 = Y, 25 = title
|
||||
spriteCount N, then N x (ptr, 63 bytes): the per-level sprite
|
||||
bitmaps the level's extra scene loads at $2000+
|
||||
|
|
|
|||
|
|
@ -114,11 +114,11 @@ diff -ruN a/stCels.c b/stCels.c
|
|||
@@ -2,9 +2,9 @@
|
||||
// See stCels.h. Ordered exactly as stRenderPrewarm built the cels: the
|
||||
// cab set (flying, wreck and the intro decorations) and passenger set in
|
||||
// multicolour at the constant level colours (cab/passenger 6, shared
|
||||
-// $D025/$D026 = 2/7), then the flame set in multicolour colour 7. No
|
||||
// multicolor at the constant level colors (cab/passenger 6, shared
|
||||
-// $D025/$D026 = 2/7), then the flame set in multicolor color 7. No
|
||||
-// intro star: the level intro paints its starfield into the character
|
||||
-// screen (stTitle.c), so the star sprite is never drawn.
|
||||
+// $D025/$D026 = 2/7), then the flame set in multicolour colour 7. The
|
||||
+// $D025/$D026 = 2/7), then the flame set in multicolor color 7. The
|
||||
+// intro star is not here: a bank's cels share one size, and the star is
|
||||
+// built cropped to its one tile at boot (prewarmStar in stRender.c).
|
||||
|
||||
|
|
@ -151,8 +151,8 @@ diff -ruN a/stRender.c b/stRender.c
|
|||
static const uint8_t kLogoPalSlot[ST_LOGO_PAL_SLOTS] = { 4u, 8u, 9u, 10u, 13u, 14u, 15u };
|
||||
|
||||
-// Level-intro warp: how a star ring looks at each distance BEHIND the
|
||||
-// bright band, as C64 colour indices (white, light grey, grey, dark
|
||||
-// grey, then black). A ring this far behind the band or further is
|
||||
-// bright band, as C64 color indices (white, light gray, gray, dark
|
||||
-// gray, then black). A ring this far behind the band or further is
|
||||
-// unlit, so a lit head with a short fading tail sweeps outward and the
|
||||
-// rest of the field stays dark.
|
||||
-static const uint8_t kStarRamp[] = { 1u, 15u, 12u, 11u };
|
||||
|
|
@ -177,7 +177,7 @@ diff -ruN a/stRender.c b/stRender.c
|
|||
}
|
||||
|
||||
|
||||
-// Light the starfield's ring colours for the current phase: the ring
|
||||
-// Light the starfield's ring colors for the current phase: the ring
|
||||
-// the band is on takes kStarRamp[0], the ones just inside it the rest
|
||||
-// of the ramp, and everything else the background. One palette write
|
||||
-// per step and not a single pixel moves -- that is the whole point of
|
||||
|
|
@ -191,7 +191,7 @@ diff -ruN a/stRender.c b/stRender.c
|
|||
- jlPaletteGet(gRender.stage, 0u, pal);
|
||||
- for (ring = 0u; ring < ST_STAR_RING_COUNT; ring++) {
|
||||
- // How far this ring sits behind the band, wrapping so the band
|
||||
- // re-enters at the centre as it leaves the edge.
|
||||
- // re-enters at the center as it leaves the edge.
|
||||
- behind = (uint8_t)((gRender.starPhase - ring) & (ST_STAR_RING_COUNT - 1u));
|
||||
- color = (behind < ST_STAR_RAMP_LEN) ? kC64Palette[kStarRamp[behind]] : kC64Palette[0];
|
||||
- pal[ST_STAR_RING_FIRST + ring] = color;
|
||||
|
|
@ -276,7 +276,7 @@ diff -ruN a/stRender.c b/stRender.c
|
|||
|
||||
- if (gRender.logoMode || gRender.starMode) {
|
||||
+ if (gRender.logoMode) {
|
||||
// The borrowed slots go back to being C64 colours.
|
||||
// The borrowed slots go back to being C64 colors.
|
||||
gRender.logoMode = false;
|
||||
gRender.logoTileOk = false;
|
||||
- gRender.starMode = false;
|
||||
|
|
@ -312,24 +312,24 @@ diff -ruN a/stSim.h b/stSim.h
|
|||
-// ---- level-intro starfield --------------------------------------------
|
||||
+// ---- level-intro star warp --------------------------------------------
|
||||
//
|
||||
-// The C64's level intro streams seven star SPRITES out of the centre of
|
||||
-// The C64's level intro streams seven star SPRITES out of the center of
|
||||
-// the screen. That is the most expensive thing this port asks a present
|
||||
-// for: eight sprites are save-under drawn and restored every frame, and
|
||||
-// because a row's dirty band is one min..max interval, two stars far
|
||||
-// apart on the same row widen that row to nearly full width. Radially
|
||||
-// spreading stars maximise exactly that, so the warp ends up presenting
|
||||
-// spreading stars maximize exactly that, so the warp ends up presenting
|
||||
-// close to the whole framebuffer every frame -- which is what makes it
|
||||
-// the slowest thing the IIgs does, and wasted work everywhere else.
|
||||
-//
|
||||
-// So the warp is not animated in pixels at all. The starfield is painted
|
||||
-// ONCE into screen RAM and animated by cycling the colours its cells are
|
||||
-// ONCE into screen RAM and animated by cycling the colors its cells are
|
||||
-// drawn in -- the same trick the title logo already uses
|
||||
-// (stRenderTitleLogo). A frame costs one palette upload and NO dirty
|
||||
-// pixel rows.
|
||||
-//
|
||||
-// The contract between the two halves is just this colour range:
|
||||
-// stTitle.c colours a star's cell ST_STAR_RING_FIRST + ring (ring 0 is
|
||||
-// the centre), and the renderer cycles that range. These are C64 colour
|
||||
-// The contract between the two halves is just this color range:
|
||||
-// stTitle.c colors a star's cell ST_STAR_RING_FIRST + ring (ring 0 is
|
||||
-// the center), and the renderer cycles that range. These are C64 color
|
||||
-// indices the intro screen never uses -- it draws in 0, 3 and, in demo
|
||||
-// mode, 4 and 5; the cab sprite adds 2, 6 and 7.
|
||||
-#define ST_STAR_RING_FIRST 8u
|
||||
|
|
@ -340,17 +340,17 @@ diff -ruN a/stSim.h b/stSim.h
|
|||
-// something else must fail to compile here instead of quietly making
|
||||
-// the warp skip rings.
|
||||
-typedef char stStarRingCountIsPow2[((ST_STAR_RING_COUNT & (ST_STAR_RING_COUNT - 1u)) == 0u) ? 1 : -1];
|
||||
-// Ring 0 is the centre, which in a warp is the VANISHING POINT: those
|
||||
-// Ring 0 is the center, which in a warp is the VANISHING POINT: those
|
||||
-// stars are the far ones and get the small glyph. The big glyph belongs
|
||||
-// to the outer rings, the stars about to rush past the viewer.
|
||||
-#define ST_STAR_GLYPH_NEAR 0x2Au // '*', the outer rings
|
||||
-#define ST_STAR_GLYPH_FAR 0x2Eu // '.', the inner rings
|
||||
-#define ST_STAR_FAR_RINGS 5u // rings 0..4 are still distant
|
||||
+// The C64's level intro ($4666) streams seven hires star sprites --
|
||||
+// hardware sprites 0..6, all showing block $DA -- out of the centre of
|
||||
+// hardware sprites 0..6, all showing block $DA -- out of the center of
|
||||
+// the screen: each waits out a random spawn delay ($44F8), then flies on
|
||||
+// its own fixed velocity ($450C / $4513) for $20 frames ($4500) and
|
||||
+// respawns near the centre.
|
||||
+// respawns near the center.
|
||||
+#define ST_INTRO_STARS 7u
|
||||
+#define ST_INTRO_STAR_PTR 0xDAu // a hires 5x3-pixel star
|
||||
+#define ST_INTRO_STAR_COLOR 0x07u
|
||||
|
|
@ -358,8 +358,8 @@ diff -ruN a/stSim.h b/stSim.h
|
|||
// VIC sprite block pointers the game selects by name. The cab's bit 0 is
|
||||
// the landing gear, so each facing is a pair.
|
||||
@@ -423,6 +397,10 @@
|
||||
// 407 cells of colour RAM); part of the picture, so it survives the
|
||||
// same way colour RAM does.
|
||||
// 407 cells of color RAM); part of the picture, so it survives the
|
||||
// same way color RAM does.
|
||||
uint8_t logoColor;
|
||||
+ // $4500: frames until each intro star respawns. Neither $5EC4 nor the
|
||||
+ // intro ($4523 seeds only the spawn delays) clears it, so a star whose
|
||||
|
|
@ -386,10 +386,10 @@ diff -ruN a/stTitle.c b/stTitle.c
|
|||
@@ -11,11 +11,6 @@
|
||||
// Level intro ($4523..$4727): a black screen with the level name, the
|
||||
// cab rising from the bottom and seven star sprites streaming out of
|
||||
// the centre, until the cab reaches mid-screen.
|
||||
// the center, until the cab reaches mid-screen.
|
||||
-//
|
||||
-// The seven star sprites are replaced by a static starfield whose
|
||||
-// colours are cycled -- see ST_STAR_RING_FIRST in stSim.h for why and
|
||||
-// colors are cycled -- see ST_STAR_RING_FIRST in stSim.h for why and
|
||||
-// how. The cab still flies, and it is still what ends the intro, so the
|
||||
-// sequence keeps its length and its cue; only the warp changes.
|
||||
|
||||
|
|
@ -476,8 +476,8 @@ diff -ruN a/stTitle.c b/stTitle.c
|
|||
|
||||
|
||||
-// Paint the level-intro starfield into screen RAM, once. A star's cell
|
||||
-// is coloured ST_STAR_RING_FIRST + its ring and never touched again --
|
||||
-// the renderer animates the warp by cycling those colours, which costs
|
||||
-// is colored ST_STAR_RING_FIRST + its ring and never touched again --
|
||||
-// the renderer animates the warp by cycling those colors, which costs
|
||||
-// no dirty rows at all. See ST_STAR_RING_FIRST in stSim.h.
|
||||
-static void paintStarfield(StSimT *sim) {
|
||||
- const StStarT *star;
|
||||
|
|
@ -489,7 +489,7 @@ diff -ruN a/stTitle.c b/stTitle.c
|
|||
- star = &kIntroStar[k];
|
||||
- one[0] = (star->ring < ST_STAR_FAR_RINGS) ? ST_STAR_GLYPH_FAR : ST_STAR_GLYPH_NEAR;
|
||||
- stSimDrawText(sim, star->col, star->row, one, (uint8_t)(ST_STAR_RING_FIRST + star->ring));
|
||||
+// $467A -- a star (re)appears near the centre, placed from star 0's
|
||||
+// $467A -- a star (re)appears near the center, placed from star 0's
|
||||
+// row. The column's ADC adds the carry the loop arrived with; the row's
|
||||
+// adds the bit the last LSR shifted out, which the mask keeps clear, and
|
||||
+// its sum stays below $100, so the loop goes on with the carry clear.
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ STAXI_TITLE := Space Taxi
|
|||
STAXI_HELP := Space Taxi: fly the taxi, pick up the fares, take them where they ask to go.
|
||||
# IIgs: large overflow segments. 26 bank-aligned segments starved the Memory
|
||||
# Manager, so NTP's 34K NewHandle failed and the IIgs build ran SILENT
|
||||
# (gNTPReady stayed 0). Freeing that memory lets audio initialise; the
|
||||
# (gNTPReady stayed 0). Freeing that memory lets audio initialize; the
|
||||
# latent crash that then surfaced is fixed in src/iigs/audio_full.c
|
||||
# (sfxTrigger now requires a running replayer).
|
||||
STAXI_IIGS_OVERFLOW_CAP := 0xF000
|
||||
|
|
@ -94,7 +94,7 @@ STAXI_SPEECH_FLAGS_atarist := --single
|
|||
STAXI_SPEECH_FLAGS_x68000 := --single
|
||||
STAXI_SPEECH_FLAGS_iigs := --biased
|
||||
|
||||
STAXI_DATA := $(STAXI_RUN)/sprites/staxicels.spc $(STAXI_RUN)/speech.bin
|
||||
STAXI_DATA := $(STAXI_RUN)/sprites/staxicel.spc $(STAXI_RUN)/speech.bin
|
||||
ifneq ($(wildcard $(STAXI_RAW_DUMP)),)
|
||||
STAXI_DATA += $(addprefix $(STAXI_RUN)/,$(STAXI_LEVELS)) $(foreach n,$(STAXI_CEL_LEVELS),$(STAXI_OPT)/sprites/level$(n).spc)
|
||||
else
|
||||
|
|
@ -106,7 +106,7 @@ staxi-no-levels:
|
|||
@echo "note: Space Taxi levels skipped: $(STAXI_RAW_DUMP) is not present"
|
||||
|
||||
# The bakes are kept: they are what the DATA copies come from.
|
||||
.SECONDARY: $(addprefix $(STAXI_GEN)/,$(STAXI_LEVELS) sprites/staxicels.spr sprites/staxicels.spc speech.bin) \
|
||||
.SECONDARY: $(addprefix $(STAXI_GEN)/,$(STAXI_LEVELS) sprites/staxicel.spr sprites/staxicel.spc speech.bin) \
|
||||
$(foreach n,$(STAXI_CEL_LEVELS),$(STAXI_GEN)/sprites/level$(n).spr $(STAXI_GEN)/sprites/level$(n).spc)
|
||||
|
||||
# Every level and the title in one run of the extractor: a grouped target
|
||||
|
|
@ -115,7 +115,7 @@ $(addprefix $(STAXI_GEN)/,$(STAXI_LEVELS)) &: $(STAXI_RAW_DUMP) $(ROMTOLEVEL)
|
|||
@mkdir -p $(STAXI_GEN)/levels
|
||||
python3 $(ROMTOLEVEL) $(STAXI_RAW_DUMP) $(STAXI_GEN)/levels
|
||||
|
||||
$(STAXI_GEN)/sprites/staxicels.spr: $(STAXIBAKE) $(STAXI_HOME)/stCels.c $(STAXI_HOME)/stC64Data.c
|
||||
$(STAXI_GEN)/sprites/staxicel.spr: $(STAXIBAKE) $(STAXI_HOME)/stCels.c $(STAXI_HOME)/stC64Data.c
|
||||
@mkdir -p $(dir $@)
|
||||
$(STAXIBAKE) $@
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# importData.sh - install the one file Space Taxi needs that is not in the
|
||||
# repository: a memory image of the original Commodore 64 game, which the
|
||||
# build extracts the 24 levels and the title screen from (program.mk). It is
|
||||
# never downloaded from anywhere: you supply your copy, and it is recognised
|
||||
# never downloaded from anywhere: you supply your copy, and it is recognized
|
||||
# by its checksum, so its file name does not matter.
|
||||
#
|
||||
# Usage: scripts/importData.sh PATH...
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ static const uint8_t *kAboutLines[7] = {
|
|||
(const uint8_t *)"ALSO TRY RESCUE SQUAD BY JOHN KUTCHER",
|
||||
};
|
||||
static const uint8_t kTextReturn[] = "PRESS FIRE TO RETURN";
|
||||
// Fatal load messages. Two short centred lines in the game's own charset,
|
||||
// Fatal load messages. Two short centered lines in the game's own charset,
|
||||
// upper case because the C64 font has no lower case in this bank.
|
||||
static const char kTextFatalMemory[] = "OUT OF MEMORY";
|
||||
static const char kTextFatalSpeech[] = "SPEECH WILL NOT LOAD";
|
||||
|
|
@ -370,7 +370,7 @@ static void highScoreSave(void) {
|
|||
|
||||
|
||||
// $4D52 -- after a player's GAME OVER: their score as "dddd.dd" text, and
|
||||
// if it ranks, the $4E35 screen: red border, grey field, the cabbie's
|
||||
// if it ranks, the $4E35 screen: red border, gray field, the cabbie's
|
||||
// number in the greeting, the score at the top, song 6, and a blinking
|
||||
// cursor where the name goes. Returns false when the score did not rank
|
||||
// and the game moves straight on.
|
||||
|
|
@ -880,7 +880,7 @@ static void runTitle(uint8_t ticks) {
|
|||
static const uint8_t kAboutCols[7] = { 1u, 12u, 15u, 4u, 5u, 6u, 1u };
|
||||
static const uint8_t kAboutRows[7] = { 3u, 5u, 7u, 10u, 12u, 14u, 18u };
|
||||
uint8_t i;
|
||||
blankScreen(&gSim, 12u, 15u); // $558E: grey border, light-grey field
|
||||
blankScreen(&gSim, 12u, 15u); // $558E: gray border, light-gray field
|
||||
for (i = 0u; i < 7u; i++) {
|
||||
stSimDrawText(&gSim, kAboutCols[i], kAboutRows[i], kAboutLines[i], 1u);
|
||||
}
|
||||
|
|
@ -891,7 +891,7 @@ static void runTitle(uint8_t ticks) {
|
|||
}
|
||||
|
||||
|
||||
// $554F -- colour one shift line (row 10 + 2*sel, 35 cells from col 2).
|
||||
// $554F -- color one shift line (row 10 + 2*sel, 35 cells from col 2).
|
||||
static void shiftMenuColor(uint8_t sel, uint8_t color) {
|
||||
uint8_t col;
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ void stRenderFrame(jlSurfaceT *stage, StSimT *sim);
|
|||
// leaves the title's logo mode (below).
|
||||
void stRenderSceneChanged(StSimT *sim);
|
||||
// Title screen only: draw the logo's four flip-book frames from one
|
||||
// tile held in reserved palette slots, so the flip and the colour
|
||||
// tile held in reserved palette slots, so the flip and the color
|
||||
// cycle are palette writes instead of repainting 103 (and 407) cells.
|
||||
// Call after stRenderSceneChanged, which turns it back off.
|
||||
void stRenderTitleLogo(StSimT *sim);
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@
|
|||
// (5k + 12) cycles per pass plus a 33-cycle tail, where k is the inner
|
||||
// count at $98A2 -- normally 3, giving 27. A code below $09 does not
|
||||
// speak: it REWRITES k, which is how the original gives each fare a
|
||||
// slightly different voice pitch. stAudioSpeech honours that.
|
||||
// slightly different voice pitch. stAudioSpeech honors that.
|
||||
#define ST_SPEECH_SLOT 0u // slots 1..3 are the SID voices
|
||||
#define ST_SPEECH_RATE 5000u // enough for a 1-bit source
|
||||
#define ST_SPEECH_PITCH_BASE 3u // $98A2's power-on value
|
||||
|
|
@ -368,7 +368,7 @@ static void speechPumpSlice(uint16_t samples) {
|
|||
if (samples > room) {
|
||||
samples = room;
|
||||
}
|
||||
// Fill towards the ring's end, then from its start.
|
||||
// Fill toward the ring's end, then from its start.
|
||||
while (samples != 0u) {
|
||||
uint16_t run = (uint16_t)(ST_SPEECH_STAGE_BYTES - gSpeechStageWrite);
|
||||
uint16_t got;
|
||||
|
|
|
|||
24
stCels.c
24
stCels.c
|
|
@ -1,19 +1,19 @@
|
|||
// Space Taxi -- the cel bank definition + the shared cel expansion.
|
||||
// See stCels.h. Ordered exactly as stRenderPrewarm built the cels: the
|
||||
// cab set (flying, wreck and the intro decorations) and passenger set in
|
||||
// multicolour at the constant level colours (cab/passenger 6, shared
|
||||
// $D025/$D026 = 2/7), then the flame set in multicolour colour 7. No
|
||||
// multicolor at the constant level colors (cab/passenger 6, shared
|
||||
// $D025/$D026 = 2/7), then the flame set in multicolor color 7. No
|
||||
// intro star: the level intro paints its starfield into the character
|
||||
// screen (stTitle.c), so the star sprite is never drawn.
|
||||
|
||||
#include "stCels.h"
|
||||
|
||||
// mc0/mc1 are the level-wide multicolour registers, constant across all
|
||||
// mc0/mc1 are the level-wide multicolor registers, constant across all
|
||||
// 24 screens and the title (verified from the level headers): 2 and 7.
|
||||
#define MC0 0x02u
|
||||
#define MC1 0x07u
|
||||
#define CAB 0x06u // cab and passenger per-sprite colour
|
||||
#define FLM 0x07u // flame colour
|
||||
#define CAB 0x06u // cab and passenger per-sprite color
|
||||
#define FLM 0x07u // flame color
|
||||
|
||||
const StCelDefT kStCels[] = {
|
||||
// Cab: flying (gear up/down x left/right), wreck cels, intro cabs.
|
||||
|
|
@ -33,7 +33,7 @@ const StCelDefT kStCels[] = {
|
|||
{ 0xC8, CAB, 1u, MC0, MC1 }, { 0xC9, CAB, 1u, MC0, MC1 },
|
||||
{ 0xCA, CAB, 1u, MC0, MC1 }, { 0xCB, CAB, 1u, MC0, MC1 },
|
||||
{ 0xD9, CAB, 1u, MC0, MC1 },
|
||||
// Flame (exhaust) cels, colour 7.
|
||||
// Flame (exhaust) cels, color 7.
|
||||
{ 0xD8, FLM, 1u, MC0, MC1 }, { 0xD4, FLM, 1u, MC0, MC1 },
|
||||
{ 0xD5, FLM, 1u, MC0, MC1 }, { 0xD2, FLM, 1u, MC0, MC1 },
|
||||
{ 0xD1, FLM, 1u, MC0, MC1 }, { 0xD7, FLM, 1u, MC0, MC1 },
|
||||
|
|
@ -45,10 +45,10 @@ const uint16_t kStCelCount = (uint16_t)(sizeof(kStCels) / sizeof(kStCels[0]));
|
|||
|
||||
// Cels the title screen draws that are deliberately NOT pre-compiled.
|
||||
//
|
||||
// Its five decoration sprites are recoloured by logoColorCycle ($4861),
|
||||
// which rewrites sprites 3..7 with the next logo colour every cycle
|
||||
// Its five decoration sprites are recolored by logoColorCycle ($4861),
|
||||
// which rewrites sprites 3..7 with the next logo color every cycle
|
||||
// step, so each is seen in all eight of kLogoColor's values -- forty
|
||||
// combinations. The parked cab's jet is a forty-first, colour 6 rather
|
||||
// combinations. The parked cab's jet is a forty-first, color 6 rather
|
||||
// than the flame list's 7. None of them are worth a bank slot: on the
|
||||
// 65816 a compile costs about half a second, so baking the forty would
|
||||
// add some twenty seconds to startup, and raising kStCelCount at all is
|
||||
|
|
@ -56,9 +56,9 @@ const uint16_t kStCelCount = (uint16_t)(sizeof(kStCels) / sizeof(kStCels[0]));
|
|||
// the note in the project memory). They draw interpreted, which
|
||||
// a static title screen can well afford. stRender consults this list so
|
||||
// the mid-ride warning stays about real misses.
|
||||
// A zero colour means "in any colour": the decorations cycle through all
|
||||
// eight. The jet is listed for the cab colour alone, because the flame
|
||||
// list above already carries it in colour 7 for the ride itself.
|
||||
// A zero color means "in any color": the decorations cycle through all
|
||||
// eight. The jet is listed for the cab color alone, because the flame
|
||||
// list above already carries it in color 7 for the ride itself.
|
||||
const StCelDefT kStInterpretedCels[] = {
|
||||
{ 0xDB, 0u, 1u, MC0, MC1 }, { 0xDE, 0u, 1u, MC0, MC1 },
|
||||
{ 0xDF, 0u, 1u, MC0, MC1 }, { 0xE0, 0u, 1u, MC0, MC1 },
|
||||
|
|
|
|||
16
stCels.h
16
stCels.h
|
|
@ -3,7 +3,7 @@
|
|||
// spritebake compiles it to a per-platform .spc, and stRenderPrewarm
|
||||
// loads that with jlSpriteBankLoadPrecompiled -- so the moving cels
|
||||
// arrive already compiled and the ~0.4 s-per-cel 65816 JIT never runs at
|
||||
// boot. The game maps loaded cel i back to its (ptr, colour, mode) key
|
||||
// boot. The game maps loaded cel i back to its (ptr, color, mode) key
|
||||
// from kStCels[i]. stCelBlob is the single expansion both the baker and
|
||||
// the interpreter fallback use, so the compiled and built cels agree.
|
||||
|
||||
|
|
@ -22,16 +22,16 @@
|
|||
|
||||
typedef struct {
|
||||
uint8_t ptr; // VIC sprite block pointer
|
||||
uint8_t color; // per-sprite colour (the multicolour "10" pair)
|
||||
uint8_t multi; // 1 = multicolour, 0 = hires
|
||||
uint8_t mc0; // shared multicolour 0 ($D025); 0 when hires
|
||||
uint8_t mc1; // shared multicolour 1 ($D026); 0 when hires
|
||||
uint8_t color; // per-sprite color (the multicolor "10" pair)
|
||||
uint8_t multi; // 1 = multicolor, 0 = hires
|
||||
uint8_t mc0; // shared multicolor 0 ($D025); 0 when hires
|
||||
uint8_t mc1; // shared multicolor 1 ($D026); 0 when hires
|
||||
} StCelDefT;
|
||||
|
||||
extern const StCelDefT kStCels[];
|
||||
extern const uint16_t kStCelCount;
|
||||
// Cels the title screen draws interpreted by design; a zero colour
|
||||
// entry matches any colour. See the note beside the table in stCels.c.
|
||||
// Cels the title screen draws interpreted by design; a zero color
|
||||
// entry matches any color. See the note beside the table in stCels.c.
|
||||
extern const StCelDefT kStInterpretedCels[];
|
||||
extern const uint16_t kStInterpretedCount;
|
||||
|
||||
|
|
@ -41,7 +41,7 @@ extern const uint16_t kStInterpretedCount;
|
|||
uint8_t stLevelCelList(const StLevelT *level, StCelDefT *out, uint8_t max);
|
||||
|
||||
// Expand a VIC bitmap (63 bytes, 21 rows x 3, or NULL) into the 288-byte
|
||||
// tile-major 4bpp-packed cel blob. Colours as in the C64 multicolour
|
||||
// tile-major 4bpp-packed cel blob. Colors as in the C64 multicolor
|
||||
// rules; hires uses `color` for a set bit.
|
||||
void stCelBlob(const uint8_t *bm, uint8_t multi, uint8_t color, uint8_t mc0, uint8_t mc1, uint8_t *out);
|
||||
|
||||
|
|
|
|||
2
stFare.c
2
stFare.c
|
|
@ -189,7 +189,7 @@ static void stageCleanup(StSimT *sim) {
|
|||
}
|
||||
|
||||
|
||||
// $6742 -- the passenger materialises beside the cab; the fare pays
|
||||
// $6742 -- the passenger materializes beside the cab; the fare pays
|
||||
// out: $5.00 plus whatever is left on the meter (twice the base when
|
||||
// no pads are reserved).
|
||||
static void stageDropIn(StSimT *sim) {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
#define ST_LEVEL_X 23u
|
||||
#define ST_LEVEL_Y 24u
|
||||
|
||||
// The level header's shared sprite multicolours ($7D05/$7D06 -> $D025/$D026).
|
||||
// The level header's shared sprite multicolors ($7D05/$7D06 -> $D025/$D026).
|
||||
#define ST_HDR_SPRITE_MC0 5u
|
||||
#define ST_HDR_SPRITE_MC1 6u
|
||||
|
||||
|
|
@ -64,7 +64,7 @@
|
|||
#define ST_HK_I_RISE_PHASES 5u
|
||||
#define ST_HK_I_CEL_BURST 0x7DE5u // 8 cels
|
||||
#define ST_HK_I_BURST_PHASES 8u
|
||||
#define ST_HK_I_COLOR 0x7E7Fu // 3 colours
|
||||
#define ST_HK_I_COLOR 0x7E7Fu // 3 colors
|
||||
#define ST_HK_I_COLORS 3u
|
||||
#define ST_HK_I_COLOR_IDLE 0x02u
|
||||
|
||||
|
|
@ -84,7 +84,7 @@
|
|||
#define ST_HK_L_COLOR 0u
|
||||
|
||||
// ---- level Q "INTERFERENCE" ----
|
||||
#define ST_HK_Q_CEL 0x7DF2u // 12 cels, multicolour
|
||||
#define ST_HK_Q_CEL 0x7DF2u // 12 cels, multicolor
|
||||
#define ST_HK_Q_CEL_PHASES 12u
|
||||
#define ST_HK_Q_COLOR 6u
|
||||
|
||||
|
|
@ -94,7 +94,7 @@
|
|||
#define ST_HK_U_COLOR 7u
|
||||
|
||||
// ---- level Y "MYSTERY SCREEN" ----
|
||||
// Sprite 3 is a multicolour saucer, sprite 4 a hires walker, sprite 5
|
||||
// Sprite 3 is a multicolor 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
|
||||
|
|
|
|||
24
stHooks.c
24
stHooks.c
|
|
@ -169,7 +169,7 @@ static const StHookFallT kFallP = { 0x7EF9u, 0x7F01u, ST_HK_P_CEL_BURST, ST_HK_P
|
|||
#define ST_HOOK_BUZZ_VOICE 1u
|
||||
#define ST_HOOK_Q_BUZZ_SPAN 0x64u
|
||||
|
||||
// $D01C with every sprite multicolour (levels I and Q).
|
||||
// $D01C with every sprite multicolor (levels I and Q).
|
||||
#define ST_HOOK_ALL_MULTICOLOR 0xFFu
|
||||
|
||||
// A hit verdict ($7D75) with bit 7 set skips the falling wreck ($6A72
|
||||
|
|
@ -401,7 +401,7 @@ static void fillDownChar(StSimT *sim, uint8_t col, uint8_t row, uint8_t ch, uint
|
|||
}
|
||||
|
||||
|
||||
// $41AD after $401E -- the same colour into the `rows` cells below.
|
||||
// $41AD after $401E -- the same color into the `rows` cells below.
|
||||
static void fillDownColor(StSimT *sim, uint8_t col, uint8_t row, uint8_t color, uint8_t rows) {
|
||||
uint8_t k;
|
||||
|
||||
|
|
@ -1165,7 +1165,7 @@ ST_NOINLINE static void hookPerTickLow(StSimT *sim) {
|
|||
// Level Q "INTERFERENCE" ($7DA1 prelude0, $7D9B prelude1, $7DFF
|
||||
// per-tick2, $7E2E input): five interference sprites (hw 3..7) sit at
|
||||
// fixed spots flickering through cels $80..$8B every other tick, every
|
||||
// sprite multicolour; while the cab is airborne mid-screen (rows $5A..
|
||||
// sprite multicolor; while the cab is airborne mid-screen (rows $5A..
|
||||
// $B3) the joystick is replaced by one random direction on 3 ticks in
|
||||
// 10 (rng(10) >= 8), with a buzz.
|
||||
static uint8_t hookQInput(StSimT *sim, uint8_t input) {
|
||||
|
|
@ -1258,7 +1258,7 @@ static void hookRPerTick(StSimT *sim) {
|
|||
}
|
||||
|
||||
|
||||
// A row's cells 1..38 rotate one cell, screen and colour together, the
|
||||
// A row's cells 1..38 rotate one cell, screen and color together, the
|
||||
// end cell wrapping round; the border columns 0 and 39 stay. Level O's
|
||||
// barriers ($7DD1..$7E62) and V's left bands ($7E0E) go left, V's other
|
||||
// bands ($7E35) right.
|
||||
|
|
@ -1300,7 +1300,7 @@ static uint8_t hookSInput(StSimT *sim, uint8_t input) {
|
|||
|
||||
|
||||
// Level T "FAST BREAK" $7D9D -- the barrier on row 3: `ch` into the
|
||||
// centre opening (cols 18..21), its complement into the side gaps.
|
||||
// center opening (cols 18..21), its complement into the side gaps.
|
||||
static void hookTGate(StSimT *sim, uint8_t ch) {
|
||||
uint8_t k;
|
||||
|
||||
|
|
@ -1316,7 +1316,7 @@ static void hookTGate(StSimT *sim, uint8_t ch) {
|
|||
|
||||
|
||||
// Level T per-tick ($7DDA): a slow cab up on the right bounces off
|
||||
// the ceiling; a fast climb into the top rows slams the centre gate
|
||||
// the ceiling; a fast climb into the top rows slams the center gate
|
||||
// shut and opens the sides until the cab drops back down.
|
||||
static void hookTPerTick(StSimT *sim) {
|
||||
if (sim->hookScratch.t.gateShut != 0u) {
|
||||
|
|
@ -1350,7 +1350,7 @@ static void hookTPerTick(StSimT *sim) {
|
|||
}
|
||||
|
||||
|
||||
// Level T prelude 1 ($7DC4): centre open, sides barred.
|
||||
// Level T prelude 1 ($7DC4): center open, sides barred.
|
||||
static void hookTPrelude1(StSimT *sim) {
|
||||
hookTGate(sim, ST_CHAR_SPACE);
|
||||
sim->hookScratch.t.gateShut = 0u;
|
||||
|
|
@ -1475,7 +1475,7 @@ static void hookUPerTick(StSimT *sim) {
|
|||
}
|
||||
|
||||
|
||||
// $7D9B -- clear the orb states and colour the five hazard sprites.
|
||||
// $7D9B -- clear the orb states and color the five hazard sprites.
|
||||
static void hookUPrelude0(StSimT *sim) {
|
||||
uint8_t x;
|
||||
|
||||
|
|
@ -1510,7 +1510,7 @@ static void hookVPerTick(StSimT *sim) {
|
|||
// Level W "LASERS" $7E46 -- clear the beam glyphs, then step each of
|
||||
// the eight lasers: idle ones fire on a 2-in-12 roll, extending ones
|
||||
// grow a cell per pass until their end row, retracting ones fade
|
||||
// through two colours and vanish.
|
||||
// through two colors and vanish.
|
||||
static void hookWLaserUpdate(StSimT *sim) {
|
||||
uint8_t i;
|
||||
uint8_t k;
|
||||
|
|
@ -1741,7 +1741,7 @@ static void hookXPrelude0(StSimT *sim) {
|
|||
}
|
||||
|
||||
|
||||
// Copy cells of one screen row (char + colour) one column along, from
|
||||
// Copy cells of one screen row (char + color) one column along, from
|
||||
// column `from` to column `to` inclusive, in the direction that keeps
|
||||
// the copy from overwriting its own source.
|
||||
static void hookXShiftRow(StSimT *sim, uint16_t rowCell, uint8_t from, uint8_t to, int8_t dir) {
|
||||
|
|
@ -1922,7 +1922,7 @@ static void hookYPrelude0(StSimT *sim) {
|
|||
}
|
||||
|
||||
|
||||
// $7E02 -- every sprite up to the saucer is multicolour, and a saucer
|
||||
// $7E02 -- every sprite up to the saucer is multicolor, and a saucer
|
||||
// already in flight hums again.
|
||||
static void hookYPrelude1(StSimT *sim) {
|
||||
sim->multiColorMask = ST_HK_Y_MULTICOLOR;
|
||||
|
|
@ -1978,7 +1978,7 @@ static void hookYShot(StSimT *sim) {
|
|||
}
|
||||
|
||||
|
||||
// Sprites 3..7 share one colour in the puzzle level.
|
||||
// Sprites 3..7 share one color in the puzzle level.
|
||||
static void setSpriteColors(StSimT *sim, uint8_t color) {
|
||||
uint8_t k;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Space Taxi -- the cels a level's hook program can put on screen.
|
||||
//
|
||||
// Hook sprites are built from the level's own VIC bitmaps in whatever
|
||||
// colour the hook gives them, and compiling one on the 65816 costs about
|
||||
// color the hook gives them, and compiling one on the 65816 costs about
|
||||
// half a second -- far too long to do the first time a cel appears mid-
|
||||
// ride. So every level lists its cels up front, from the same hook
|
||||
// tables the hook program reads (stHookTables.h): the offline baker turns
|
||||
|
|
@ -60,8 +60,8 @@ uint8_t stLevelCelList(const StLevelT *level, StCelDefT *out, uint8_t max) {
|
|||
celAdd(out, &n, max, ST_HK_H_CEL, ST_HK_H_COLOR_ACTIVE, 0u, 0u, 0u);
|
||||
break;
|
||||
case ST_LEVEL_I:
|
||||
// Every cel in the idle colour and in each of the three the
|
||||
// launcher picks at random; multicolour, like every sprite
|
||||
// Every cel in the idle color and in each of the three the
|
||||
// launcher picks at random; multicolor, like every sprite
|
||||
// on this level ($7DB1).
|
||||
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);
|
||||
|
|
@ -95,7 +95,7 @@ uint8_t stLevelCelList(const StLevelT *level, StCelDefT *out, uint8_t max) {
|
|||
}
|
||||
break;
|
||||
case ST_LEVEL_Q:
|
||||
// Multicolour: the level's second prelude hook ($7D9B) sets
|
||||
// Multicolor: the level's second prelude hook ($7D9B) sets
|
||||
// $D01C to $FF after the cab spawn set it to $07.
|
||||
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[ST_HDR_SPRITE_MC0], level->header[ST_HDR_SPRITE_MC1]);
|
||||
|
|
@ -107,7 +107,7 @@ uint8_t stLevelCelList(const StLevelT *level, StCelDefT *out, uint8_t max) {
|
|||
}
|
||||
break;
|
||||
case ST_LEVEL_Y:
|
||||
// The saucer is multicolour ($7E02 sets $D01C to $0F); the
|
||||
// The saucer is multicolor ($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++) {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
// Layout (all bytes, written by assets/romToLevel.py):
|
||||
// "STL5"
|
||||
// name[22] $7D78 level name in screen codes
|
||||
// header[9] $7D00 VIC colour block
|
||||
// header[9] $7D00 VIC color block
|
||||
// padCount, specialPad, fuelRate
|
||||
// spawn[5] $7D5B
|
||||
// templates[8] $7D8F Y accel, X accel, Y gravity, X gravity (LE words)
|
||||
|
|
|
|||
80
stRender.c
80
stRender.c
|
|
@ -1,17 +1,17 @@
|
|||
// Space Taxi -- renderer: the simulation's screen RAM, colour RAM,
|
||||
// Space Taxi -- renderer: the simulation's screen RAM, color RAM,
|
||||
// charset and sprite frame onto the JoeyLib stage.
|
||||
//
|
||||
// The screen is 40x25 character cells painted from the live charset
|
||||
// (the game edits glyphs at runtime: the transporter hatch, the title
|
||||
// logo flip-book, the laser beams). A cell goes to the stage in one
|
||||
// call: jlTilePasteGlyph takes the character's eight 1bpp charset rows
|
||||
// as they stand and colours them from the cell's colour RAM, so an
|
||||
// edited glyph needs no rebuild and a colour change costs nothing.
|
||||
// as they stand and colors them from the cell's color RAM, so an
|
||||
// edited glyph needs no rebuild and a color change costs nothing.
|
||||
// Only the cells the simulation listed as dirty are
|
||||
// repainted; a changed glyph dirties the cells showing it, found
|
||||
// inside the cell range each character was last painted in. The eight
|
||||
// VIC sprites are drawn from their bitmaps through a small cache of
|
||||
// JoeyLib sprites keyed on (pointer, colour, multicolour mode), with
|
||||
// JoeyLib sprites keyed on (pointer, color, multicolor mode), with
|
||||
// save-under and LIFO restore. Sprite 0 has VIC priority, so the draw
|
||||
// order is sprite 7 first, sprite 0 last, and a sprite that has not
|
||||
// moved (and has nothing repainted under it) simply stays on the
|
||||
|
|
@ -20,12 +20,12 @@
|
|||
//
|
||||
// The title screen gets a special path (stRenderTitleLogo): its logo is
|
||||
// 103 cells of one character that the flip-book animates by copying one
|
||||
// of four frame glyphs over it, and a colour cycle that used to rewrite
|
||||
// the colour of 407 cells. Both were pure repaint cost. Instead the four
|
||||
// of four frame glyphs over it, and a color cycle that used to rewrite
|
||||
// the color of 407 cells. Both were pure repaint cost. Instead the four
|
||||
// frames collapse into ONE tile whose pixels are drawn in reserved
|
||||
// palette slots -- one slot per set of frames a pixel belongs to -- so a
|
||||
// flip and a recolour are both a single palette write and no cell is
|
||||
// ever repainted. The title's decoration sprites, which cycle colour
|
||||
// flip and a recolor are both a single palette write and no cell is
|
||||
// ever repainted. The title's decoration sprites, which cycle color
|
||||
// with the logo, share one more reserved slot for the same reason.
|
||||
|
||||
#include <string.h>
|
||||
|
|
@ -43,18 +43,18 @@
|
|||
// Room for the baked cab/passenger/exhaust set (36) plus the largest
|
||||
// level's hook cels (I: 30) at once.
|
||||
#define ST_SPRITE_CACHE 80u
|
||||
#define ST_SPRITE_BACKUP_BYTES jlSpriteBackupLength(uint8_t, ST_SPRITE_TILES, ST_SPRITE_TILES)
|
||||
#define ST_SPRITE_BACKUP_BYTES ((uint16_t)(jlSpriteBackupLength(ST_SPRITE_TILES, ST_SPRITE_TILES) * sizeof(uint16_t)))
|
||||
#define ST_SPRITE_PX (8 * ST_SPRITE_TILES)
|
||||
// Palette slots the title logo animation owns. The title screen draws
|
||||
// in C64 colours 0, 1, 2, 6, 7, 11 and 12 only (plus colour 5 for the
|
||||
// in C64 colors 0, 1, 2, 6, 7, 11 and 12 only (plus color 5 for the
|
||||
// "GET READY" banner that appears over it), so these are free while it
|
||||
// is up and the whole trick needs no per-scanline SCB band.
|
||||
#define ST_PAL_LOGO_SPR 3u // the decoration sprites' cycling colour
|
||||
#define ST_PAL_LOGO_SPR 3u // the decoration sprites' cycling color
|
||||
#define ST_LOGO_PAL_SLOTS 7u
|
||||
// The first hardware sprite that cycles colour with the logo.
|
||||
// The first hardware sprite that cycles color with the logo.
|
||||
#define ST_LOGO_SPR_FIRST 3u
|
||||
|
||||
// Where the startup message sits, and its C64 colour (1 = white).
|
||||
// Where the startup message sits, and its C64 color (1 = white).
|
||||
#define ST_LOADING_ROW 12u
|
||||
#define ST_LOADING_COLOR 1u
|
||||
|
||||
|
|
@ -102,8 +102,8 @@ typedef struct {
|
|||
uint8_t logoSlotCount;
|
||||
uint8_t logoSlotMask[ST_LOGO_PAL_SLOTS]; // frames each slot is lit in
|
||||
uint8_t logoFrame; // frame the palette currently shows
|
||||
uint8_t logoColor; // logo colour the palette currently shows
|
||||
uint8_t logoSprColor; // decoration-sprite colour it shows
|
||||
uint8_t logoColor; // logo color the palette currently shows
|
||||
uint8_t logoSprColor; // decoration-sprite color it shows
|
||||
jlTileT logoTile;
|
||||
// Cell range (inclusive) each character has been painted in since
|
||||
// the last full repaint: where a changed glyph's cells can be.
|
||||
|
|
@ -141,8 +141,8 @@ static const uint16_t kC64Palette[16] = {
|
|||
static const uint8_t kLogoPalSlot[ST_LOGO_PAL_SLOTS] = { 4u, 8u, 9u, 10u, 13u, 14u, 15u };
|
||||
|
||||
// Level-intro warp: how a star step looks at each distance BEHIND the
|
||||
// lit step, as C64 colour indices; further behind is unlit. One yellow
|
||||
// star per ray, as the C64's star sprites (colour 7, $4523) fly with no
|
||||
// lit step, as C64 color indices; further behind is unlit. One yellow
|
||||
// star per ray, as the C64's star sprites (color 7, $4523) fly with no
|
||||
// trail.
|
||||
static const uint8_t kStarRamp[] = { 7u };
|
||||
#define ST_STAR_RAMP_LEN (sizeof(kStarRamp) / sizeof(kStarRamp[0]))
|
||||
|
|
@ -192,7 +192,7 @@ static void cacheCompile(StSpriteCacheT *e) {
|
|||
|
||||
|
||||
// The cached cel for hardware sprite `idx` of the current frame, built
|
||||
// on first use from its VIC bitmap and colours; NULL when there is
|
||||
// on first use from its VIC bitmap and colors; NULL when there is
|
||||
// nothing to draw.
|
||||
static StSpriteCacheT *cachedSprite(StSimT *sim, uint8_t idx) {
|
||||
uint8_t ptr = sim->frame.ptr[idx];
|
||||
|
|
@ -261,7 +261,7 @@ static StSpriteCacheT *cachedSprite(StSimT *sim, uint8_t idx) {
|
|||
cacheCompile(slot);
|
||||
}
|
||||
if (!gPrewarming && !celDrawsInterpreted(ptr, color)) {
|
||||
ST_LOG("spacetaxi: ! cel $%02X colour %u (level %u) built mid-ride -- add it to stLevelCelList", ptr, color, level);
|
||||
ST_LOG("spacetaxi: ! cel $%02X color %u (level %u) built mid-ride -- add it to stLevelCelList", ptr, color, level);
|
||||
}
|
||||
slot->lastUse = gRender.cacheStamp;
|
||||
slot->ptr = ptr;
|
||||
|
|
@ -349,8 +349,8 @@ static bool celCreate(StSpriteCacheT *e, const uint8_t *bm, uint8_t multi, uint8
|
|||
|
||||
// True for a cel that is MEANT to be built here rather than pre-listed,
|
||||
// so the warning below stays about real misses. Only the title screen's
|
||||
// decorations qualify: logoColorCycle recolours them every cycle step,
|
||||
// which is forty ptr/colour pairs that would each cost a compile (see
|
||||
// decorations qualify: logoColorCycle recolors them every cycle step,
|
||||
// which is forty ptr/color pairs that would each cost a compile (see
|
||||
// the note beside kStTitleDecorCels in stCels.c).
|
||||
static bool celDrawsInterpreted(uint8_t ptr, uint8_t color) {
|
||||
uint16_t k;
|
||||
|
|
@ -512,8 +512,8 @@ static bool dirtyUnderSprite(int16_t px, int16_t py) {
|
|||
|
||||
|
||||
// The palette index a hardware sprite is drawn in. On the title screen
|
||||
// sprites 3..7 all follow the logo's colour cycle, so they share one
|
||||
// reserved slot: the cycle then recolours them with a palette write
|
||||
// sprites 3..7 all follow the logo's color cycle, so they share one
|
||||
// reserved slot: the cycle then recolors them with a palette write
|
||||
// instead of rebuilding five cels, and -- because their drawn state
|
||||
// stops changing -- without redrawing the sprites stacked above them.
|
||||
static uint8_t drawColor(const StSimT *sim, uint8_t idx) {
|
||||
|
|
@ -539,7 +539,7 @@ static void dropCache(void) {
|
|||
|
||||
// Collapse the four flip-book frame glyphs into one tile: every pixel
|
||||
// is drawn in the reserved palette slot that stands for the set of
|
||||
// frames it is set in (no frame = colour 0, the background). Animating
|
||||
// frames it is set in (no frame = color 0, the background). Animating
|
||||
// the logo is then a palette write per flip instead of a repaint of
|
||||
// every cell showing it. Returns false if the frames need more distinct
|
||||
// sets than there are reserved slots, leaving the ordinary per-cell
|
||||
|
|
@ -600,10 +600,10 @@ static uint8_t logoFrameIndex(const StSimT *sim) {
|
|||
}
|
||||
|
||||
|
||||
// Show flip-book frame `frame` in the logo's current colour: every
|
||||
// reserved slot lights up in the colour if that frame is in its set and
|
||||
// goes to the background colour if it is not. The decoration sprites'
|
||||
// slot follows their own colour, which the cycle sets one step behind
|
||||
// Show flip-book frame `frame` in the logo's current color: every
|
||||
// reserved slot lights up in the color if that frame is in its set and
|
||||
// goes to the background color if it is not. The decoration sprites'
|
||||
// slot follows their own color, which the cycle sets one step behind
|
||||
// the logo's (the title's sprite table overwrites it on entry).
|
||||
static void logoPaletteApply(const StSimT *sim, uint8_t frame) {
|
||||
uint16_t pal[JL_SURFACE_COLORS_PER_PALETTE];
|
||||
|
|
@ -655,7 +655,7 @@ static void paintCells(jlSurfaceT *stage, StSimT *sim) {
|
|||
}
|
||||
|
||||
|
||||
// Paint one cell: the character's charset rows coloured from colour RAM
|
||||
// Paint one cell: the character's charset rows colored from color RAM
|
||||
// over the background. Also notes the cell in the character's range,
|
||||
// which is where a changed glyph's cells are looked for.
|
||||
static void pasteCell(jlSurfaceT *stage, const StSimT *sim, uint16_t cell, uint8_t bx, uint8_t by) {
|
||||
|
|
@ -672,7 +672,7 @@ static void pasteCell(jlSurfaceT *stage, const StSimT *sim, uint16_t cell, uint8
|
|||
}
|
||||
|
||||
|
||||
// Light the starfield's ring colours for the current phase: the ring
|
||||
// Light the starfield's ring colors for the current phase: the ring
|
||||
// the band is on takes kStarRamp[0], the ones just inside it the rest
|
||||
// of the ramp, and everything else the background. One palette write
|
||||
// per step and not a single pixel moves -- that is the whole point of
|
||||
|
|
@ -686,7 +686,7 @@ static void starPaletteApply(void) {
|
|||
jlPaletteGet(gRender.stage, 0u, pal);
|
||||
for (ring = 0u; ring < ST_STAR_RING_COUNT; ring++) {
|
||||
// How far this ring sits behind the band, wrapping so the band
|
||||
// re-enters at the centre as it leaves the edge.
|
||||
// re-enters at the center as it leaves the edge.
|
||||
behind = (uint8_t)((gRender.starPhase - ring) & (ST_STAR_RING_COUNT - 1u));
|
||||
color = (behind < ST_STAR_RAMP_LEN) ? kC64Palette[kStarRamp[behind]] : kC64Palette[0];
|
||||
pal[ST_STAR_RING_FIRST + ring] = color;
|
||||
|
|
@ -748,7 +748,7 @@ void stRenderFrame(jlSurfaceT *stage, StSimT *sim) {
|
|||
// undrawn (last first) and redrawn below, the rest stay put.
|
||||
if (gRender.logoMode) {
|
||||
// The flip book only ever swaps the logo glyph for one of the
|
||||
// four frames the logo tile already carries: recolour the
|
||||
// four frames the logo tile already carries: recolor the
|
||||
// reserved slots and drop the glyph's repaint on the floor.
|
||||
uint8_t frame = logoFrameIndex(sim);
|
||||
gRender.logoTileOk = (frame < ST_LOGO_FRAMES);
|
||||
|
|
@ -1033,7 +1033,7 @@ bool stRenderLevelCels(jlSurfaceT *stage, const StLevelT *level) {
|
|||
// Load the offline-baked, already-compiled cel bank into the pinned
|
||||
// cache so no cel is JIT-compiled at boot (each 65816 compile costs
|
||||
// ~0.4 s). Every kStCels entry becomes a pinned cache slot keyed exactly
|
||||
// as the play lookups: system ptr (level 0xFF), the level-wide colours.
|
||||
// as the play lookups: system ptr (level 0xFF), the level-wide colors.
|
||||
// Returns false if the .spc is missing or built for another target/shift
|
||||
// count (a stale bake), so the caller falls back to the runtime JIT
|
||||
// prewarm below. Every port stages its own bake (make/<port>.mk).
|
||||
|
|
@ -1045,7 +1045,7 @@ static bool loadPrecompiledCels(void) {
|
|||
if (kStCelCount > (uint16_t)(sizeof(cels) / sizeof(cels[0])) || kStCelCount > ST_SPRITE_CACHE) {
|
||||
return false;
|
||||
}
|
||||
n = jlSpriteBankLoadPrecompiled("sprites/staxicels.spc", cels, kStCelCount, 0);
|
||||
n = jlSpriteBankLoadPrecompiled("sprites/staxicel.spc", cels, kStCelCount, 0);
|
||||
if (n < kStCelCount) {
|
||||
for (i = 0u; i < n; i++) {
|
||||
jlSpriteDestroy(cels[i]);
|
||||
|
|
@ -1079,7 +1079,7 @@ static bool loadPrecompiledCels(void) {
|
|||
}
|
||||
|
||||
|
||||
// A fatal message, centred, in the game's own charset. Used when an
|
||||
// A fatal message, centered, in the game's own charset. Used when an
|
||||
// asset that IS on the disk will not load: the game then stops instead
|
||||
// of playing on in a degraded state, because a mute game or one running
|
||||
// every sprite through the interpreter looks like a slow machine rather
|
||||
|
|
@ -1117,7 +1117,7 @@ void stRenderFatal(jlSurfaceT *stage, const char *line1, const char *line2) {
|
|||
}
|
||||
|
||||
|
||||
// "LOADING..." centred on an otherwise black stage. The charset is the
|
||||
// "LOADING..." centered on an otherwise black stage. The charset is the
|
||||
// game's own, indexed by ASCII (the C64 font's letters sit at their
|
||||
// ASCII codes), so this needs no font surface and no simulation state.
|
||||
void stRenderLoading(jlSurfaceT *stage) {
|
||||
|
|
@ -1152,10 +1152,10 @@ bool stRenderPrewarm(jlSurfaceT *stage, StSimT *sim) {
|
|||
// Precompiled bank: instant, no per-cel JIT. Falls through to the
|
||||
// runtime build below on any port without a matching .spc.
|
||||
if (loadPrecompiledCels()) {
|
||||
ST_LOG("spacetaxi: cels from sprites/staxicels.spc");
|
||||
ST_LOG("spacetaxi: cels from sprites/staxicel.spc");
|
||||
return prewarmReport(kStCelCount);
|
||||
}
|
||||
ST_LOG("spacetaxi: ! no usable sprites/staxicels.spc -- JIT prewarm");
|
||||
ST_LOG("spacetaxi: ! no usable sprites/staxicel.spc -- JIT prewarm");
|
||||
saved = sim->frame;
|
||||
gPrewarming = true;
|
||||
sim->frame.multiMask = 0x07u;
|
||||
|
|
@ -1237,7 +1237,7 @@ void stRenderSceneChanged(StSimT *sim) {
|
|||
uint8_t k;
|
||||
|
||||
if (gRender.logoMode || gRender.starMode) {
|
||||
// The borrowed slots go back to being C64 colours.
|
||||
// The borrowed slots go back to being C64 colors.
|
||||
gRender.logoMode = false;
|
||||
gRender.logoTileOk = false;
|
||||
gRender.starMode = false;
|
||||
|
|
@ -1254,7 +1254,7 @@ void stRenderSceneChanged(StSimT *sim) {
|
|||
|
||||
// Turn on the title's palette-animated logo. Call it after
|
||||
// stRenderSceneChanged (which turns it back off) on entry to the title
|
||||
// screen; from then on a flip-book step and a colour cycle both cost
|
||||
// screen; from then on a flip-book step and a color cycle both cost
|
||||
// one palette write and no cell repaint at all.
|
||||
void stRenderTitleLogo(StSimT *sim) {
|
||||
uint8_t frame;
|
||||
|
|
|
|||
18
stSim.c
18
stSim.c
|
|
@ -16,10 +16,10 @@
|
|||
#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.
|
||||
// colors 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_OFF 0x0Bu // dark gray
|
||||
#define ST_LAMP_SLOW 0x03u // cyan
|
||||
#define ST_LAMP_FALL 0x07u // yellow
|
||||
#define ST_LAMP_FAST 0x02u // red
|
||||
|
|
@ -513,7 +513,7 @@ static uint8_t frameSpriteCollisions(StSimT *sim) {
|
|||
|
||||
|
||||
// $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
|
||||
// cell pairs ($DBA2 on row 23, $DBCA on row 24) go dark gray; 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
|
||||
|
|
@ -929,8 +929,8 @@ static void sfxEnvelopeTick(StSimT *sim) {
|
|||
|
||||
|
||||
// 21 rows of 3-byte foreground masks for a displayed sprite, kept in
|
||||
// collMask[idx] and rebuilt only when its bitmap or multicolour mode
|
||||
// changes. A multicolour sprite's 2-bit pairs cover two pixels each: a
|
||||
// collMask[idx] and rebuilt only when its bitmap or multicolor mode
|
||||
// changes. A multicolor sprite's 2-bit pairs cover two pixels each: a
|
||||
// non-zero pair is two set mask bits (kPairMask maps a nibble's two
|
||||
// pairs at once). The cab's cached windows die with its mask.
|
||||
static void spriteMasks(StSimT *sim, uint8_t idx) {
|
||||
|
|
@ -1243,7 +1243,7 @@ void stSimBcdAdd(StSimT *sim, uint16_t dstCell, const uint8_t *blob) {
|
|||
}
|
||||
|
||||
|
||||
// $6C38 -- blank the message row (colour 1, as the original writes it).
|
||||
// $6C38 -- blank the message row (color 1, as the original writes it).
|
||||
void stSimClearMessage(StSimT *sim) {
|
||||
stSimDrawText(sim, ST_MESSAGE_COL, ST_MESSAGE_ROW, kTextBlank, 1u);
|
||||
}
|
||||
|
|
@ -1300,7 +1300,7 @@ bool stSimDecrementNumber(StSimT *sim, uint16_t cell, uint8_t position) {
|
|||
|
||||
|
||||
// $41C2 -- write a screen-code string (terminated by any byte < 6)
|
||||
// with one colour.
|
||||
// with one color.
|
||||
void stSimDrawText(StSimT *sim, uint8_t col, uint8_t row, const uint8_t *text, uint8_t color) {
|
||||
uint16_t cell = ST_CELL(row, col);
|
||||
|
||||
|
|
@ -1463,7 +1463,7 @@ void stSimNewGame(StSimT *sim, uint8_t playerCount, bool demo) {
|
|||
|
||||
// $5EC4 clears the game state but the playback buffer, the flame
|
||||
// parity, the wave index and the walk parity all survive. So does
|
||||
// the displayed picture -- screen RAM, colour RAM and the charset
|
||||
// the displayed picture -- screen RAM, color RAM and the charset
|
||||
// are not game state, and the attract demo's "GET READY" banner is
|
||||
// drawn over the still-live title screen -- so the clear starts at
|
||||
// ST_SIM_CLEAR_FROM. Every level entry rebinds the image anyway
|
||||
|
|
@ -1616,7 +1616,7 @@ void stSimPutChar(StSimT *sim, uint8_t col, uint8_t row, uint8_t ch) {
|
|||
}
|
||||
|
||||
|
||||
// $401E -- one colour into colour RAM.
|
||||
// $401E -- one color into color RAM.
|
||||
void stSimPutColor(StSimT *sim, uint8_t col, uint8_t row, uint8_t color) {
|
||||
setColorCell(sim, ST_CELL(row, col), color);
|
||||
}
|
||||
|
|
|
|||
38
stSim.h
38
stSim.h
|
|
@ -85,25 +85,25 @@
|
|||
|
||||
// ---- level-intro starfield --------------------------------------------
|
||||
//
|
||||
// The C64's level intro streams seven star SPRITES out of the centre of
|
||||
// The C64's level intro streams seven star SPRITES out of the center of
|
||||
// the screen. That is the most expensive thing this port asks a present
|
||||
// for: eight sprites are save-under drawn and restored every frame, and
|
||||
// because a row's dirty band is one min..max interval, two stars far
|
||||
// apart on the same row widen that row to nearly full width. Radially
|
||||
// spreading stars maximise exactly that, so the warp ends up presenting
|
||||
// spreading stars maximize exactly that, so the warp ends up presenting
|
||||
// close to the whole framebuffer every frame -- which is what makes it
|
||||
// the slowest thing the IIgs does, and wasted work everywhere else.
|
||||
//
|
||||
// So the warp is not animated in pixels at all. The starfield is painted
|
||||
// ONCE into screen RAM and animated by cycling the colours its cells are
|
||||
// ONCE into screen RAM and animated by cycling the colors its cells are
|
||||
// drawn in -- the same trick the title logo already uses
|
||||
// (stRenderTitleLogo). A frame costs one palette upload and NO dirty
|
||||
// pixel rows.
|
||||
//
|
||||
// The contract between the two halves is just this colour range:
|
||||
// stTitle.c colours a star's cell ST_STAR_RING_FIRST + the step of its
|
||||
// The contract between the two halves is just this color range:
|
||||
// stTitle.c colors a star's cell ST_STAR_RING_FIRST + the step of its
|
||||
// ray's flight (offset by the ray's launch delay), and the renderer
|
||||
// cycles that range. These are C64 colour
|
||||
// cycles that range. These are C64 color
|
||||
// indices the intro screen never uses -- it draws in 0, 3 and, in demo
|
||||
// mode, 4 and 5; the cab sprite adds 2, 6 and 7.
|
||||
#define ST_STAR_RING_FIRST 8u
|
||||
|
|
@ -115,7 +115,7 @@
|
|||
// the warp skip rings.
|
||||
typedef char stStarRingCountIsPow2[((ST_STAR_RING_COUNT & (ST_STAR_RING_COUNT - 1u)) == 0u) ? 1 : -1];
|
||||
// A C64 star flies $20 frames ($4500) and the painted field marks every
|
||||
// ST_STAR_FRAMES_PER_STEP of them, so the renderer steps the lit colour on
|
||||
// ST_STAR_FRAMES_PER_STEP of them, so the renderer steps the lit color on
|
||||
// that many intro frames (introStepCount): the warp keeps the C64's speed at
|
||||
// any frame rate.
|
||||
#define ST_INTRO_STAR_FLIGHT 0x20u
|
||||
|
|
@ -140,7 +140,7 @@ typedef char stStarRingCountIsPow2[((ST_STAR_RING_COUNT & (ST_STAR_RING_COUNT -
|
|||
// glyphs over it. The glyph stays the simulation's own record of which
|
||||
// frame is showing; the renderer draws all four frames as one tile in
|
||||
// reserved palette slots and animates them by rewriting those entries,
|
||||
// so a flip and a colour cycle both cost no repaint at all.
|
||||
// so a flip and a color cycle both cost no repaint at all.
|
||||
#define ST_LOGO_CHAR 0x84u
|
||||
#define ST_LOGO_FRAME_FIRST 0x85u // $85..$88 = frames 0..3
|
||||
#define ST_LOGO_FRAMES 4u
|
||||
|
|
@ -336,7 +336,7 @@ typedef struct {
|
|||
|
||||
typedef struct {
|
||||
uint8_t name[ST_LEVEL_NAME_CHARS + 1u]; // $7D78, screen codes
|
||||
uint8_t header[9]; // $7D00..$7D08 VIC colours
|
||||
uint8_t header[9]; // $7D00..$7D08 VIC colors
|
||||
uint8_t padCount; // $7D5A
|
||||
uint8_t specialPad; // $7D09
|
||||
uint8_t fuelRate; // $7D60
|
||||
|
|
@ -347,7 +347,7 @@ typedef struct {
|
|||
uint16_t gravX; // $7D95/96
|
||||
StPadT pads[ST_MAX_PADS]; // $7D0A..
|
||||
uint8_t screen[ST_SCREEN_CELLS]; // decompressed screen RAM
|
||||
uint8_t color[ST_SCREEN_CELLS]; // decompressed colour RAM
|
||||
uint8_t color[ST_SCREEN_CELLS]; // decompressed color RAM
|
||||
uint8_t levelIndex; // 0..23 = A..X, 24 = Y, 25 = title
|
||||
uint8_t spriteCount;
|
||||
StLevelSpriteT sprites[ST_MAX_LEVEL_SPRITES];
|
||||
|
|
@ -389,12 +389,12 @@ typedef struct {
|
|||
// Passenger/fare state machine stages ($7163).
|
||||
typedef enum {
|
||||
ST_STAGE_IDLE = 0, // $660B RNG-timed next fare
|
||||
ST_STAGE_BEAM_IN, // $665F passenger materialises
|
||||
ST_STAGE_BEAM_IN, // $665F passenger materializes
|
||||
ST_STAGE_WAIT, // $66B7 waves on the pad
|
||||
ST_STAGE_WALK_TO_CAB, // $6CE8 walks to the landed cab
|
||||
ST_STAGE_BOARD, // $66DD shrinks into the cab
|
||||
ST_STAGE_RIDING, // $6739 aboard
|
||||
ST_STAGE_DROP_IN, // $6742 materialises beside the cab
|
||||
ST_STAGE_DROP_IN, // $6742 materializes beside the cab
|
||||
ST_STAGE_WALK_TO_PAD, // $6CE8 walks to the stand
|
||||
ST_STAGE_LEAVE, // $67A6 shrinks away
|
||||
ST_STAGE_CLEANUP // $67C6
|
||||
|
|
@ -423,7 +423,7 @@ typedef enum {
|
|||
} StTickResultE;
|
||||
|
||||
typedef struct {
|
||||
// ---- the displayed picture: screen RAM, colour RAM, the charset
|
||||
// ---- the displayed picture: screen RAM, color RAM, the charset
|
||||
// and the renderer's dirty bookkeeping for them. stSimNewGame
|
||||
// ($5EC4) does NOT clear this block -- the C64 resets its game
|
||||
// variables with the previous picture still on screen, which is how
|
||||
|
|
@ -431,7 +431,7 @@ typedef struct {
|
|||
// title. Everything from `level` down IS cleared; keep new fields on
|
||||
// the right side of that line ----
|
||||
uint8_t screen[ST_SCREEN_CELLS]; // $0400 screen RAM
|
||||
uint8_t color[ST_SCREEN_CELLS]; // $D800 colour RAM
|
||||
uint8_t color[ST_SCREEN_CELLS]; // $D800 color RAM
|
||||
uint8_t cellDirty[ST_SCREEN_CELLS]; // renderer clears
|
||||
uint16_t dirtyList[512]; // cells marked since the last frame
|
||||
uint16_t dirtyCount;
|
||||
|
|
@ -474,9 +474,9 @@ typedef struct {
|
|||
bool charDirtyAll;
|
||||
uint8_t borderColor; // $D020
|
||||
uint8_t bgColor; // $D021
|
||||
// The colour the logo band's cells would carry ($4861 writes it into
|
||||
// 407 cells of colour RAM); part of the picture, so it survives the
|
||||
// same way colour RAM does.
|
||||
// The color the logo band's cells would carry ($4861 writes it into
|
||||
// 407 cells of color RAM); part of the picture, so it survives the
|
||||
// same way color RAM does.
|
||||
uint8_t logoColor;
|
||||
|
||||
// ---- cleared by stSimNewGame from here down ----
|
||||
|
|
@ -542,7 +542,7 @@ typedef struct {
|
|||
uint8_t bgCollSprites;
|
||||
// Per-sprite row masks of the displayed frame (3 bytes per row, the
|
||||
// leftmost pixel is bit 7 of byte 0), rebuilt only when the sprite's
|
||||
// bitmap or multicolour mode changes (derived state, not C64 memory).
|
||||
// bitmap or multicolor mode changes (derived state, not C64 memory).
|
||||
uint8_t collMask[ST_HW_SPRITES][ST_SPRITE_H][3];
|
||||
const uint8_t *collMaskBm[ST_HW_SPRITES];
|
||||
uint8_t collMaskMulti[ST_HW_SPRITES];
|
||||
|
|
@ -698,7 +698,7 @@ uint8_t stSimRng(StSimT *sim, uint8_t n);
|
|||
const uint8_t *stSimSpriteBitmap(const StSimT *sim, uint8_t ptr);
|
||||
// The score the current player has on screen, as pennies.
|
||||
uint32_t stSimScorePennies(const StSimT *sim);
|
||||
// $6866 / $6877 -- the pad indicator colour cells.
|
||||
// $6866 / $6877 -- the pad indicator color cells.
|
||||
void stSimPadLight(StSimT *sim, bool on);
|
||||
// $6D6A -- the exhaust sprite (shared with the title lift-off).
|
||||
void stSimFlameUpdate(StSimT *sim);
|
||||
|
|
|
|||
26
stTitle.c
26
stTitle.c
|
|
@ -3,17 +3,17 @@
|
|||
// that carries over (flame parity, wave index, walk parity, the
|
||||
// sprite shadows) behaves as on the C64.
|
||||
//
|
||||
// Title ($477A + $47B0..$4A6C): the logo flip-book and colour cycle,
|
||||
// Title ($477A + $47B0..$4A6C): the logo flip-book and color cycle,
|
||||
// a passenger who beams in, walks to the parked cab and boards, then
|
||||
// the cab lifts off under thrust. When it has left, the attract demo
|
||||
// starts.
|
||||
//
|
||||
// Level intro ($4523..$4727): a black screen with the level name, the
|
||||
// cab rising from the bottom and seven star sprites streaming out of
|
||||
// the centre, until the cab reaches mid-screen.
|
||||
// the center, until the cab reaches mid-screen.
|
||||
//
|
||||
// The seven star sprites are replaced by a static starfield whose
|
||||
// colours are cycled -- see ST_STAR_RING_FIRST in stSim.h for why and
|
||||
// colors are cycled -- see ST_STAR_RING_FIRST in stSim.h for why and
|
||||
// how. The stars sit on the seven rays the C64's stars fly, where those
|
||||
// stars are every few frames of a flight, so the field plays the
|
||||
// original's paths. The cab still flies, and it is still what ends the
|
||||
|
|
@ -29,7 +29,7 @@ static const uint8_t kTitleMsb[8] = { 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
|
|||
static const uint8_t kTitleColor[8] = { 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07 };
|
||||
static const uint8_t kTitleRow[8] = { 0x84, 0x84, 0x00, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2 };
|
||||
static const uint8_t kTitlePtr[8] = { 0xC1, 0xC7, 0x00, 0xDB, 0xDE, 0xDF, 0xE0, 0xE1 };
|
||||
// Logo flip-book frame order ($48A7) and colour cycle ($489C).
|
||||
// Logo flip-book frame order ($48A7) and color cycle ($489C).
|
||||
static const uint8_t kLogoFlip[6] = { 1u, 2u, 3u, 4u, 3u, 2u };
|
||||
static const uint8_t kLogoColor[8] = { 0x02, 0x08, 0x07, 0x05, 0x06, 0x0E, 0x03, 0x04 };
|
||||
// Intro cab drift per loop count ($470D).
|
||||
|
|
@ -54,9 +54,9 @@ static const uint8_t kTextScreens[] = "THIS IS 1 OF 25 DIFFERENT SCREENS!";
|
|||
// field puts a star where each flight is every ST_STAR_FRAMES_PER_STEP
|
||||
// frames; the renderer lights one step of every ray at a time, so a star
|
||||
// hops out along its ray. A star is drawn where the sprite's image ($DA:
|
||||
// a 5x3 speck centred on pixel 11, 9 of the sprite) would be.
|
||||
// a 5x3 speck centered on pixel 11, 9 of the sprite) would be.
|
||||
#define ST_INTRO_STARS 7u
|
||||
#define ST_INTRO_STAR_IMAGE_X 11 // the speck's centre in its sprite
|
||||
#define ST_INTRO_STAR_IMAGE_X 11 // the speck's center in its sprite
|
||||
#define ST_INTRO_STAR_IMAGE_Y 9
|
||||
#define ST_SCREEN_WIDTH_PX ((int16_t)(ST_SCREEN_COLS * 8u))
|
||||
#define ST_SCREEN_HEIGHT_PX ((int16_t)(ST_SCREEN_ROWS * 8u))
|
||||
|
|
@ -79,12 +79,12 @@ static void addToRow(StSimT *sim, uint8_t idx, uint8_t delta) {
|
|||
}
|
||||
|
||||
|
||||
// $4861 -- the next logo colour, onto sprites 3..7 and onto the logo
|
||||
// itself. The C64 writes the colour into all 407 cells of rows 1..11,
|
||||
// $4861 -- the next logo color, onto sprites 3..7 and onto the logo
|
||||
// itself. The C64 writes the color into all 407 cells of rows 1..11,
|
||||
// columns 1..37; only the cells showing the logo character take any
|
||||
// colour from it (the rest are blank), so the colour is published in
|
||||
// color from it (the rest are blank), so the color is published in
|
||||
// logoColor instead and the renderer applies it to the logo's palette
|
||||
// slot. Nothing reads the band's colour RAM.
|
||||
// slot. Nothing reads the band's color RAM.
|
||||
static void logoColorCycle(StSimT *sim) {
|
||||
uint8_t color;
|
||||
uint8_t k;
|
||||
|
|
@ -99,7 +99,7 @@ static void logoColorCycle(StSimT *sim) {
|
|||
|
||||
|
||||
// $4827 -- every fourth tick copy the next flip frame ($85..$88) over
|
||||
// the logo glyph; the colour advances when the ping-pong hits 4.
|
||||
// the logo glyph; the color advances when the ping-pong hits 4.
|
||||
static void logoFlip(StSimT *sim) {
|
||||
sim->logoDiv = (uint8_t)((sim->logoDiv + 1u) & 3u);
|
||||
if (sim->logoDiv != 0u) {
|
||||
|
|
@ -120,10 +120,10 @@ static void logoFlip(StSimT *sim) {
|
|||
|
||||
|
||||
// Paint the level-intro starfield into screen RAM, once. A star's cell is
|
||||
// coloured ST_STAR_RING_FIRST + the step of its flight it marks, offset by
|
||||
// colored ST_STAR_RING_FIRST + the step of its flight it marks, offset by
|
||||
// its ray's launch delay so the rays fire out of step as on the C64, and is
|
||||
// never touched again -- the renderer animates the warp by cycling those
|
||||
// colours, which costs no dirty rows at all (ST_STAR_RING_FIRST, stSim.h).
|
||||
// colors, which costs no dirty rows at all (ST_STAR_RING_FIRST, stSim.h).
|
||||
// Step 0 is the launch point, which every ray shares, and steps off the
|
||||
// screen are left out, as are rows that carry the intro's text: on the C64
|
||||
// the stars fly over the text, but a cell star there reads as part of it --
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
// staxibake --level LEVEL.dat OUTPUT.spr one level's hook-sprite cels
|
||||
//
|
||||
// The per-level bank holds the cels stLevelCelList names for that level
|
||||
// (its own VIC bitmaps in the colours its hook program uses), in list
|
||||
// (its own VIC bitmaps in the colors its hook program uses), in list
|
||||
// order -- the game rebuilds the same list at level entry to key them.
|
||||
|
||||
#include <stdio.h>
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ int main(void) {
|
|||
gKeys = "zz"; // typed before the screen: flushed like $0277
|
||||
check(nameEntryStart(), "1234.56 ranks");
|
||||
check(gGame.state == ST_STATE_NAME_ENTRY && gGame.hsSlot == 3u, "slot 3 (under the two 3877.56 and 1809.51)");
|
||||
check(gSim.borderColor == 2u && gSim.bgColor == 15u, "red border, grey field");
|
||||
check(gSim.borderColor == 2u && gSim.bgColor == 15u, "red border, gray field");
|
||||
rowText(3u, 7u, 25u, buf);
|
||||
check(strcmp(buf, "CONGRATULATIONS CABBIE 2,") == 0, buf);
|
||||
rowText(0u, 15u, 8u, buf);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
#
|
||||
# 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
|
||||
# receives each tick's screen RAM, color 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:
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
# 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,
|
||||
# the screen cells, color cells and glyphs that differ on one tick,
|
||||
# from the two machines' picture dumps (STSIM_RAW / c64trace's rawOut)
|
||||
import json
|
||||
import sys
|
||||
|
|
@ -50,7 +50,7 @@ def picture(pathA, pathB, tick):
|
|||
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]))
|
||||
print('color 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]:
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
#!/usr/bin/env bash
|
||||
# Behaviour gate for the Space Taxi simulation: every screen's ride --
|
||||
# Behavior gate for the Space Taxi simulation: every screen's ride --
|
||||
# A..X and the 25th, Y -- checked against the real C64.
|
||||
#
|
||||
# stsim rides a screen for up to 4000 ticks and prints one line of state
|
||||
# a tick: the cab, the fare machine, the demo playback, the RNG, the HUD,
|
||||
# the sprites shown and hashes of screen RAM, colour RAM and the charset.
|
||||
# the sprites shown and hashes of screen RAM, color 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
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Space Taxi behaviour gate -- the md5 of each screen's ride as the REAL
|
||||
# Space Taxi behavior gate -- the md5 of each screen's ride as the REAL
|
||||
# C64 traced it: the original game in VICE, ridden by c64trace.py, one
|
||||
# line of state per tick (stsim prints the same line from the port).
|
||||
# `gate.sh check` compares the port's rides against these, so a pass means
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ static ResT evaluate(const uint8_t *stream, uint8_t *touchOut)
|
|||
if (d < nearest) { nearest = d; }
|
||||
}
|
||||
}
|
||||
/* distinct pictures the hook has produced (screen + colour) */
|
||||
/* distinct pictures the hook has produced (screen + color) */
|
||||
{ uint32_t h = 2166136261u; uint16_t c; uint32_t i2; int dup = 0;
|
||||
for (c = 0; c < ST_SCREEN_CELLS; c++) { h = (h ^ gSim.screen[c]) * 16777619u; }
|
||||
for (c = 0; c < ST_SCREEN_CELLS; c++) { h = (h ^ gSim.color[c]) * 16777619u; }
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
// 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
|
||||
// RAM, color 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).
|
||||
|
|
@ -59,7 +59,7 @@ static StLevelT gLevel;
|
|||
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
|
||||
// displays, as (ptr, color, 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
|
||||
|
|
@ -131,7 +131,7 @@ static void celCensusTick(const StSimT *sim) {
|
|||
// 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.
|
||||
// prove the change is behavior-preserving.
|
||||
static uint32_t charsetHash(const StSimT *sim) {
|
||||
uint8_t image[ST_CHARSET_BYTES];
|
||||
|
||||
|
|
@ -150,7 +150,7 @@ static void charsetImage(const StSimT *sim, uint8_t *image) {
|
|||
|
||||
|
||||
// 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
|
||||
// hooks that animate the screen (O, R, V, W, X) change screen, color 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;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue