diff --git a/README.md b/README.md index da7d395..1f3536f 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,14 @@ A port of Space Taxi (John Kutcher, Muse Software, 1984), the Commodore 64 game, to every machine [JoeyLib](../joeylib) targets: the Apple IIgs, -Commodore Amiga, Atari ST, MS-DOS, Sharp X68000 and DVX. One C codebase +Commodore Amiga, Atari ST, MS-DOS, Sharp X68000, and DVX. One C codebase builds for all six. The game is a routine-for-routine re-expression of the C64 original in C, written from its disassembly (MECHANICS.md): the flight physics, the fare machine, the 24 levels' hook programs, the title and level intros, the high-score table and name entry, the music (captured from the game's own -SID player) and the speech. The simulation (stSim.c) is checked tick for +SID player), and the speech. The simulation (stSim.c) is checked tick for tick against the original game running in VICE: a ride on each of the 25 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. @@ -17,7 +17,7 @@ screens, A..X and the MYSTERY SCREEN, traces byte for byte as on the C64 ## Setting up Space Taxi builds with JoeyLib, which owns the cross compilers, the -library, the asset tools and every machine's build rules. Check JoeyLib out +library, the asset tools, and every machine's build rules. Check JoeyLib out beside this repository and install its toolchains (its README covers the installer): @@ -69,7 +69,7 @@ Everything lands in `build//bin/`: | Atari ST | `STAXI/STAXI.PRG` with its `DATA/` | | MS-DOS | `STAXI/STAXI.EXE` with its `DATA/` | | Sharp X68000 | `STAXI/STAXI.X` with its `DATA/` | -| DVX | `apps/joeylib/staxi/`: `staxi.app`, its help file and `DATA/` | +| DVX | `apps/joeylib/staxi/`: `staxi.app`, its help file, and `DATA/` | The bakes (levels, the pre-compiled sprite cels, the speech) are made per machine in `build/generated//`. JoeyLib's library is built on @@ -100,8 +100,8 @@ $ scripts/verify.sh disk # or name the ones to run |------|----------------| | `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`) | -| `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 | +| `disk`, `disk01` | `STAXI.2mg` boots, launches the game, and draws on a ROM 3 and a ROM 01 IIgs | +| `iigsaudio` | The IIgs sound interrupt is NTP's, the DOC is audible, and its sound slots carry waveforms | | `x68kaudio` | Every X68000 noise write is a code from the port's table, and the ADPCM runs at 7812 Hz to both speakers while the game speaks | `scripts/verify.sh` exits 0 when every gate passed, 1 when one failed, and 3 diff --git a/spacetaxi.c b/spacetaxi.c index d67d548..f1a987a 100644 --- a/spacetaxi.c +++ b/spacetaxi.c @@ -524,26 +524,26 @@ static void gameOverEnter(void) { static bool joyFire(void) { - return jlKeyDown(KEY_SPACE) || jlJoyDown(JOYSTICK_0, JOY_BUTTON_0); + return jlKeyDown(JL_KEY_SPACE) || jlJoyDown(JL_JOYSTICK_0, JL_JOY_BUTTON_0); } // The C64's EOR'd $DC00: bit0 UP, 1 DOWN, 2 LEFT, 3 RIGHT, 4 FIRE, bit 7 set. static uint8_t joyMask(void) { - int8_t jx = jlJoystickX(JOYSTICK_0); - int8_t jy = jlJoystickY(JOYSTICK_0); + int8_t jx = jlJoystickX(JL_JOYSTICK_0); + int8_t jy = jlJoystickY(JL_JOYSTICK_0); uint8_t mask = 0x80u; - if (jlKeyDown(KEY_UP) || jy < -32) { + if (jlKeyDown(JL_KEY_UP) || jy < -32) { mask |= 0x01u; } - if (jlKeyDown(KEY_DOWN) || jy > 32) { + if (jlKeyDown(JL_KEY_DOWN) || jy > 32) { mask |= 0x02u; } - if (jlKeyDown(KEY_LEFT) || jx < -32) { + if (jlKeyDown(JL_KEY_LEFT) || jx < -32) { mask |= 0x04u; } - if (jlKeyDown(KEY_RIGHT) || jx > 32) { + if (jlKeyDown(JL_KEY_RIGHT) || jx > 32) { mask |= 0x08u; } if (joyFire()) { @@ -1030,11 +1030,12 @@ int main(void) { // gets one; whatever does not fit draws interpreted). The 68k ports' // cels are far larger (9-13 KB each) and their JIT is fast, so they // keep a modest arena and compile at level entry. -#if defined(JOEYLIB_PLATFORM_IIGS) || defined(JOEYLIB_CPU_X86) +#if defined(JOEYLIB_PLATFORM_IIGS) || defined(JL_CPU_X86) config.codegenBytes = 256UL * 1024; #else config.codegenBytes = 160UL * 1024; #endif + config.noAudio = false; if (!jlInit(&config)) { return 1; } @@ -1083,7 +1084,7 @@ int main(void) { // here -- abandons the game for the title, and on the title it // leaves the program (the C64 had nowhere else to go). if (gGame.state != ST_STATE_JINGLE && gGame.state != ST_STATE_GAME_OVER && gGame.state != ST_STATE_NAME_ENTRY) { - if (jlKeyDown(KEY_LSHIFT) || jlKeyDown(KEY_RSHIFT)) { + if (jlKeyDown(JL_KEY_LSHIFT) || jlKeyDown(JL_KEY_RSHIFT)) { if (!gGame.paused) { gGame.paused = true; stAudioSilence(); @@ -1103,7 +1104,7 @@ int main(void) { stAudioMusic(ST_SONG_INTRO, true); // $46B5 } } - if (jlKeyPressed(KEY_ESCAPE)) { + if (jlKeyPressed(JL_KEY_ESCAPE)) { if (gGame.state == ST_STATE_TITLE) { break; } diff --git a/stAudio.c b/stAudio.c index 1967593..65523f9 100644 --- a/stAudio.c +++ b/stAudio.c @@ -431,7 +431,6 @@ void stAudioTick(void) { bool stAudioInit(void) { bool ok; - (void)jlAudioInit(); gSpeechStage = (uint8_t *)jlAlloc(ST_SPEECH_STAGE_BYTES); ok = (gSpeechStage != 0) && speechLoad(); gSfx.voice = ST_VOICE_SFX; @@ -454,7 +453,9 @@ void stAudioShutdown(void) { voiceOff(&gSfx); voiceOff(&gTone); jlAudioNoise(0u, ST_ATTEN_OFF); - jlAudioShutdown(); + // The engine runs until jlShutdown: the speech stream stops before the + // buffers it is filled from go. + jlAudioStopSfx(ST_SPEECH_SLOT); jlFree(gSpeechStage); gSpeechStage = 0; jlFree(gSpeechBlob); diff --git a/stRender.c b/stRender.c index d4c2196..7cad9d1 100644 --- a/stRender.c +++ b/stRender.c @@ -43,7 +43,7 @@ // 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 JOEY_SPRITE_BACKUP_BYTES(ST_SPRITE_TILES, ST_SPRITE_TILES) +#define ST_SPRITE_BACKUP_BYTES jlSpriteBackupLength(uint8_t, ST_SPRITE_TILES, ST_SPRITE_TILES) #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 @@ -321,7 +321,7 @@ static bool celCreate(StSpriteCacheT *e, const uint8_t *bm, uint8_t multi, uint8 const uint8_t *chunk; tx--; - chunk = &blob[(ty * ST_SPRITE_TILES + tx) * TILE_BYTES]; + chunk = &blob[(ty * ST_SPRITE_TILES + tx) * JL_TILE_BYTES]; tileFromChunky(&tile, chunk); jlTilePaste(gRender.scratch, tx, ty, &tile); if (tileHasPixels(chunk)) { @@ -338,11 +338,11 @@ static bool celCreate(StSpriteCacheT *e, const uint8_t *bm, uint8_t multi, uint8 txMax = ST_SPRITE_TILES - 1u; tyMax = ST_SPRITE_TILES - 1u; } - e->offX = (uint8_t)(txMin * TILE_PIXELS_PER_SIDE); - e->offY = (uint8_t)(tyMin * TILE_PIXELS_PER_SIDE); - e->w = (uint8_t)((txMax - txMin + 1u) * TILE_PIXELS_PER_SIDE); - e->h = (uint8_t)((tyMax - tyMin + 1u) * TILE_PIXELS_PER_SIDE); - e->sprite = jlSpriteCreateFromSurface(gRender.scratch, (int16_t)e->offX, (int16_t)e->offY, (uint8_t)(e->w / TILE_PIXELS_PER_SIDE), (uint8_t)(e->h / TILE_PIXELS_PER_SIDE)); + e->offX = (uint8_t)(txMin * JL_TILE_PIXELS_PER_SIDE); + e->offY = (uint8_t)(tyMin * JL_TILE_PIXELS_PER_SIDE); + e->w = (uint8_t)((txMax - txMin + 1u) * JL_TILE_PIXELS_PER_SIDE); + e->h = (uint8_t)((tyMax - tyMin + 1u) * JL_TILE_PIXELS_PER_SIDE); + e->sprite = jlSpriteCreateFromSurface(gRender.scratch, (int16_t)e->offX, (int16_t)e->offY, (uint8_t)(e->w / JL_TILE_PIXELS_PER_SIDE), (uint8_t)(e->h / JL_TILE_PIXELS_PER_SIDE)); return e->sprite != 0; } @@ -490,11 +490,11 @@ static bool dirtyUnderSprite(int16_t px, int16_t py) { uint8_t colFirst; uint8_t colLast; - if (x1 > SURFACE_WIDTH) { - x1 = SURFACE_WIDTH; + if (x1 > JL_SURFACE_WIDTH) { + x1 = JL_SURFACE_WIDTH; } - if (y1 > SURFACE_HEIGHT) { - y1 = SURFACE_HEIGHT; + if (y1 > JL_SURFACE_HEIGHT) { + y1 = JL_SURFACE_HEIGHT; } // spriteOnScreen has already rejected anything fully off the stage, // so only the top/left overhang needs clamping to row/column 0. @@ -546,15 +546,15 @@ static void dropCache(void) { // repaint to draw the flip book. static bool logoBuildTile(const StSimT *sim) { uint8_t slotOfMask[1u << ST_LOGO_FRAMES]; - uint8_t chunky[TILE_BYTES]; + uint8_t chunky[JL_TILE_BYTES]; uint8_t row; uint8_t col; memset(slotOfMask, 0, sizeof(slotOfMask)); gRender.logoSlotCount = 0u; - for (row = 0u; row < TILE_PIXELS_PER_SIDE; row++) { - uint8_t *out = &chunky[row * TILE_BYTES_PER_ROW]; - for (col = 0u; col < TILE_PIXELS_PER_SIDE; col++) { + for (row = 0u; row < JL_TILE_PIXELS_PER_SIDE; row++) { + uint8_t *out = &chunky[row * JL_TILE_BYTES_PER_ROW]; + for (col = 0u; col < JL_TILE_PIXELS_PER_SIDE; col++) { uint8_t mask = 0u; uint8_t f; uint8_t slot; @@ -606,7 +606,7 @@ static uint8_t logoFrameIndex(const StSimT *sim) { // slot follows their own colour, 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[SURFACE_COLORS_PER_PALETTE]; + uint16_t pal[JL_SURFACE_COLORS_PER_PALETTE]; uint16_t on = kC64Palette[sim->logoColor & 15u]; uint16_t off = kC64Palette[sim->bgColor & 15u]; uint8_t k; @@ -678,7 +678,7 @@ static void pasteCell(jlSurfaceT *stage, const StSimT *sim, uint16_t cell, uint8 // per step and not a single pixel moves -- that is the whole point of // ST_STAR_RING_FIRST (stSim.h). static void starPaletteApply(void) { - uint16_t pal[SURFACE_COLORS_PER_PALETTE]; + uint16_t pal[JL_SURFACE_COLORS_PER_PALETTE]; uint16_t color; uint8_t behind; uint8_t ring; @@ -696,7 +696,7 @@ static void starPaletteApply(void) { static bool spriteOnScreen(int16_t px, int16_t py) { - return px < SURFACE_WIDTH && py < SURFACE_HEIGHT && px > -ST_SPRITE_W && py > -(int16_t)ST_SPRITE_PX; + return px < JL_SURFACE_WIDTH && py < JL_SURFACE_HEIGHT && px > -ST_SPRITE_W && py > -(int16_t)ST_SPRITE_PX; } @@ -704,15 +704,15 @@ static bool spriteOnScreen(int16_t px, int16_t py) { // this port's jlTileT: a straight copy on the chunky ports, drawn and // snapped through the scratch surface on the planar ones. static void tileFromChunky(jlTileT *out, const uint8_t *chunky) { -#if defined(JOEYLIB_NATIVE_CHUNKY) - memcpy(out->pixels, chunky, TILE_BYTES); +#if defined(JL_NATIVE_CHUNKY) + memcpy(out->pixels, chunky, JL_TILE_BYTES); #else uint8_t row; uint8_t k; - for (row = 0u; row < TILE_PIXELS_PER_SIDE; row++) { - for (k = 0u; k < TILE_BYTES_PER_ROW; k++) { - uint8_t b = chunky[row * TILE_BYTES_PER_ROW + k]; + for (row = 0u; row < JL_TILE_PIXELS_PER_SIDE; row++) { + for (k = 0u; k < JL_TILE_BYTES_PER_ROW; k++) { + uint8_t b = chunky[row * JL_TILE_BYTES_PER_ROW + k]; jlDrawPixel(gRender.scratch, (int16_t)(k * 2u), (int16_t)row, (uint8_t)(b >> 4)); jlDrawPixel(gRender.scratch, (int16_t)(k * 2u + 1u), (int16_t)row, (uint8_t)(b & 0x0Fu)); } @@ -726,7 +726,7 @@ static void tileFromChunky(jlTileT *out, const uint8_t *chunky) { static bool tileHasPixels(const uint8_t *chunky) { uint8_t k; - for (k = 0u; k < TILE_BYTES; k++) { + for (k = 0u; k < JL_TILE_BYTES; k++) { if (chunky[k] != 0u) { return true; } @@ -831,7 +831,7 @@ void stRenderFrame(jlSurfaceT *stage, StSimT *sim) { // A compiled cel wholly on the stage takes the trusted calls, // which skip the per-call geometry checks; one that is clipped // or interpreted needs the checked ones, and so does its restore. - d->trusted = e->compiled && px >= 0 && py >= 0 && px <= (int16_t)(SURFACE_WIDTH - e->w) && py <= (int16_t)(SURFACE_HEIGHT - e->h); + d->trusted = e->compiled && px >= 0 && py >= 0 && px <= (int16_t)(JL_SURFACE_WIDTH - e->w) && py <= (int16_t)(JL_SURFACE_HEIGHT - e->h); if (d->trusted) { jlSpriteSaveAndDrawTrusted(stage, e->sprite, px, py, &gRender.backup[k]); } else { @@ -874,12 +874,12 @@ void stRenderInit(jlSurfaceT *stage) { gRender.backup[k].bytes = (backupMem == 0) ? 0 : backupMem + (uint16_t)k * ST_SPRITE_BACKUP_BYTES; } jlPaletteSet(stage, 0u, kC64Palette); - jlScbSetRange(stage, 0u, (uint16_t)(SURFACE_HEIGHT - 1u), 0u); + jlScbSetRange(stage, 0u, (uint16_t)(JL_SURFACE_HEIGHT - 1u), 0u); jlSurfaceClear(stage, 0u); gRender.scratch = jlSurfaceCreate(); if (gRender.scratch != 0) { jlPaletteSet(gRender.scratch, 0u, kC64Palette); - jlScbSetRange(gRender.scratch, 0u, (uint16_t)(SURFACE_HEIGHT - 1u), 0u); + jlScbSetRange(gRender.scratch, 0u, (uint16_t)(JL_SURFACE_HEIGHT - 1u), 0u); jlSurfaceClear(gRender.scratch, 0u); } } @@ -1222,7 +1222,7 @@ static bool prewarmReport(uint16_t expect) { ST_LOG("spacetaxi: ! only %u of %u cels built", used, expect); return false; } -#if defined(JOEYLIB_SPRITE_INTERPRETED) +#if defined(JL_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; diff --git a/tools/staxiharness/harness.c b/tools/staxiharness/harness.c index c368693..23fe965 100644 --- a/tools/staxiharness/harness.c +++ b/tools/staxiharness/harness.c @@ -95,6 +95,7 @@ int main(void) { uint8_t k; config.codegenBytes = 160UL * 1024; + config.noAudio = true; if (!jlInit(&config)) { printf("jlInit failed\n"); return 1;