From ec62b978682e42004090b6146e781479cd0fe057 Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Sun, 24 May 2020 12:18:27 -0500 Subject: [PATCH] c64: patched bios to fix gotoxy() problem ($EA24 KIL -> RTS) --- doc/notes.txt | 4 + presets/c64/siegegame.c | 239 ++++++++++++++++++++++++++++++++++++++++ src/platform/c64.ts | 1 + wasm/c64.bios | Bin 20480 -> 20480 bytes 4 files changed, 244 insertions(+) create mode 100644 presets/c64/siegegame.c diff --git a/doc/notes.txt b/doc/notes.txt index f6ddf9f5..40b55f56 100644 --- a/doc/notes.txt +++ b/doc/notes.txt @@ -193,6 +193,10 @@ TODO: - probably XMLHttpRequest.timeout (https://github.com/getsentry/sentry-javascript/issues/2210) - C64 - debugging at reset + - gotoxy goes to $EA24 (KIL) from PLOT (kplot.s) missing UPDCRAMPTR + - emu doesn't reset properly (after gotoxy() BIOS call) + - disk image support + - https://github.com/cc65/cc65/issues/946 WEB WORKER FORMAT diff --git a/presets/c64/siegegame.c b/presets/c64/siegegame.c new file mode 100644 index 00000000..bb2b8b81 --- /dev/null +++ b/presets/c64/siegegame.c @@ -0,0 +1,239 @@ + +#include +#include +#include +#include +#include +#include +#include + +#define COLS 40 +#define ROWS 24 + +typedef unsigned char byte; +typedef signed char sbyte; +typedef unsigned short word; + +// BASL = text address of cursor position +static byte** BASL = (byte**) 0xD1; + +byte getchar(byte x, byte y) { + // JSR VTABZ + // LDA (BASL) ($28) + gotoxy(x,y); // set cursor position + return (*BASL)[x]; // lookup value @ cursor address +} + +void delay(byte count) { + while (count--) { + word i; + for (i=0; i<200; i++) ; + } +} + +////////// GAME DATA + +typedef struct { + byte x; + byte y; + byte dir; + word score; + char head_attr; + char tail_attr; + int collided:1; + int human:1; +} Player; + +Player players[2]; + +byte credits = 0; +byte frames_per_move; +byte gameover; + +#define START_SPEED 12 +#define MAX_SPEED 5 +#define MAX_SCORE 7 + +/////////// + +const char BOX_CHARS[8] = { '+', '+', '+', '+', + '-', '-', '!', '!'}; + +void draw_box(byte x, byte y, byte x2, byte y2, const char* chars) { + byte x1 = x; + cputcxy(x, y, chars[2]); + cputcxy(x2, y, chars[3]); + cputcxy(x, y2, chars[0]); + cputcxy(x2, y2, chars[1]); + while (++x < x2) { + cputcxy(x, y, chars[5]); + cputcxy(x, y2, chars[4]); + } + while (++y < y2) { + cputcxy(x1, y, chars[6]); + cputcxy(x2, y, chars[7]); + } +} + +void draw_playfield() { + draw_box(0,1,COLS-1,ROWS-1,BOX_CHARS); + cputsxy( 0, 0, "Plyr1:"); + cputsxy(20, 0, "Plyr2:"); + cputcxy( 7, 0, players[0].score+'0'); + cputcxy(27, 0, players[1].score+'0'); +} + +typedef enum { D_RIGHT, D_DOWN, D_LEFT, D_UP } dir_t; +const sbyte DIR_X[4] = { 1, 0, -1, 0 }; +const sbyte DIR_Y[4] = { 0, 1, 0, -1 }; + +void init_game() { + memset(players, 0, sizeof(players)); + players[0].head_attr = '1'; + players[1].head_attr = '2'; + players[0].tail_attr = '#'; + players[1].tail_attr = '*'; + frames_per_move = START_SPEED; +} + +void reset_players() { + players[0].x = players[0].y = 5; + players[0].dir = D_RIGHT; + players[1].x = COLS-6; + players[1].y = ROWS-6; + players[1].dir = D_LEFT; + players[0].collided = players[1].collided = 0; +} + +void draw_player(Player* p) { + cputcxy(p->x, p->y, p->head_attr); +} + +void move_player(Player* p) { + cputcxy(p->x, p->y, p->tail_attr); + p->x += DIR_X[p->dir]; + p->y += DIR_Y[p->dir]; + if ((getchar(p->x, p->y) & 0x7f) != ' ') + p->collided = 1; + draw_player(p); +} + +void human_control(Player* p) { + byte dir = 0xff; + char joy; + if (!p->human) return; + if (!kbhit()) return; + joy = joy_read(0); + if (JOY_UP(joy)) dir = D_UP; + if (JOY_LEFT(joy)) dir = D_LEFT; + if (JOY_RIGHT(joy)) dir = D_RIGHT; + if (JOY_DOWN(joy)) dir = D_DOWN; + // don't let the player reverse direction + if (dir < 0x80 && dir != (p->dir ^ 2)) { + p->dir = dir; + } +} + +byte ai_try_dir(Player* p, dir_t dir, byte shift) { + byte x,y; + dir &= 3; + x = p->x + (DIR_X[dir] << shift); + y = p->y + (DIR_Y[dir] << shift); + if (x < COLS && y < ROWS && (getchar(x, y) & 0x7f) == ' ') { + p->dir = dir; + return 1; + } else { + return 0; + } +} + +void ai_control(Player* p) { + dir_t dir; + if (p->human) return; + dir = p->dir; + if (!ai_try_dir(p, dir, 0)) { + ai_try_dir(p, dir+1, 0); + ai_try_dir(p, dir-1, 0); + } else { + ai_try_dir(p, dir-1, 0) && ai_try_dir(p, dir-1, 1+(rand() & 3)); + ai_try_dir(p, dir+1, 0) && ai_try_dir(p, dir+1, 1+(rand() & 3)); + ai_try_dir(p, dir, rand() & 3); + } +} + +void flash_colliders() { + byte i; + // flash players that collided + for (i=0; i<56; i++) { + delay(2); + revers(players[0].collided && (i&1)); + draw_player(&players[0]); + revers(players[1].collided && (i&1)); + draw_player(&players[1]); + } + revers(0); +} + +void make_move() { + byte i; + for (i=0; i MAX_SPEED) frames_per_move--; + // game over? + if (players[0].score != players[1].score) { + if (players[0].score >= MAX_SCORE) + declare_winner(0); + else if (players[1].score >= MAX_SCORE) + declare_winner(1); + } +} + +void play_game() { + gameover = 0; + init_game(); + players[0].human = 1; + while (!gameover) { + play_round(); + } +} + +void main() { + joy_install (joy_static_stddrv); + play_game(); +} diff --git a/src/platform/c64.ts b/src/platform/c64.ts index b3b95545..310abf3c 100644 --- a/src/platform/c64.ts +++ b/src/platform/c64.ts @@ -10,6 +10,7 @@ const C64_PRESETS = [ {id:'tgidemo.c', name:'TGI Graphics Demo (C)'}, {id:'upandaway.c', name:'Up, Up and Away (C)'}, {id:'joymove.c', name:'Joystick Movement (C)'}, + {id:'siegegame.c', name:'Siege Game (C)'}, {id:'scroll1.c', name:'Scrolling 1 (C)'}, {id:'scroll2.c', name:'Scrolling 2 (C)'}, {id:'scroll3.c', name:'Scrolling 3 (C)'}, diff --git a/wasm/c64.bios b/wasm/c64.bios index d648a68a5c05da194dd6b8abfc4d5916d2f460fa..9a1951fc7f3cc663a03ffa77942a32728bd3a732 100644 GIT binary patch delta 16 XcmZozz}T>Waf6B#W5Q-ttJ|UgGEfDy delta 16 XcmZozz}T>Waf6B#BhzM8tJ|UgFdPL|