Updates to match changes to JoeyLib.
This commit is contained in:
parent
57dab02e21
commit
4d73ec7e85
5 changed files with 49 additions and 46 deletions
12
README.md
12
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/<machine>/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/<machine>/`. 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
|
||||
|
|
|
|||
21
spacetaxi.c
21
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
56
stRender.c
56
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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue