1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2024-11-20 01:31:51 +00:00
8bitworkshop/presets/vicdual/snake2.c

430 lines
20 KiB
C
Raw Normal View History

2017-02-20 23:50:29 +00:00
#include <string.h>
typedef unsigned char byte;
typedef unsigned short word;
// PLATFORM DEFINITION
__sfr __at (0x0) input0;
__sfr __at (0x1) input1;
__sfr __at (0x2) input2;
__sfr __at (0x3) input3;
__sfr __at (0x1) ay8910_reg;
__sfr __at (0x2) ay8910_data;
__sfr __at (0x40) palette;
2017-03-15 14:11:28 +00:00
byte __at (0xe000) cellram[28][32];
2017-02-20 23:50:29 +00:00
byte __at (0xe800) tileram[256][8];
#define LEFT1 !(input1 & 0x10)
#define RIGHT1 !(input1 & 0x20)
#define UP1 !(input1 & 0x40)
#define DOWN1 !(input1 & 0x80)
#define FIRE1 !(input2 & 0x20)
#define COIN1 (input3 & 0x8)
#define START1 !(input2 & 0x10)
#define START2 !(input3 & 0x20)
2017-03-15 14:11:28 +00:00
#define VSYNC (input1 & 0x8)
2017-02-20 23:50:29 +00:00
// GAME DATA
typedef struct {
byte x;
byte y;
byte dir;
word score;
char head_attr;
char tail_attr;
char collided:1;
char human:1;
} Player;
Player players[2];
byte attract;
byte credits = 0;
byte frames_per_move;
#define START_SPEED 12
#define MAX_SPEED 5
#define MAX_SCORE 7
// GAME CODE
void main();
void gsinit();
// start routine @ 0x0
void start() {
__asm
LD SP,#0xE800 ; set up stack pointer
DI ; disable interrupts
__endasm;
gsinit();
main();
}
#define INIT_MAGIC 0xdeadbeef
static long is_initialized = INIT_MAGIC;
// set initialized portion of global memory
// by copying INITIALIZER area -> INITIALIZED area
void gsinit() {
// already initialized? skip it
if (is_initialized == INIT_MAGIC)
return;
__asm
; copy initialized data to RAM
LD BC, #l__INITIALIZER
LD A, B
LD DE, #s__INITIALIZED
LD HL, #s__INITIALIZER
LDIR
__endasm;
}
// SOUND CODE
inline void set8910(byte reg, byte data) {
if (attract) return; // no sound in attract mode
ay8910_reg = reg;
ay8910_data = data;
}
////////
// https://en.wikipedia.org/wiki/Linear-feedback_shift_register#Galois_LFSRs
static word lfsr = 1;
word rand() {
2017-03-30 21:26:06 +00:00
byte lsb = lfsr & 1; /* Get LSB (i.e., the output bit). */
2017-02-20 23:50:29 +00:00
lfsr >>= 1; /* Shift register */
if (lsb) { /* If the output bit is 1, apply toggle mask. */
lfsr ^= 0xB400u;
}
return lfsr;
}
void wait_for_vsync() {
2017-03-15 14:11:28 +00:00
while (VSYNC != 0) lfsr++; // wait for VSYNC end
while (VSYNC == 0) lfsr++; // wait for VSYNC start
2017-02-20 23:50:29 +00:00
}
#define LOCHAR 0x0
#define HICHAR 0xff
#define CHAR(ch) (ch-LOCHAR)
void clrscr() {
memset(cellram, CHAR(' '), sizeof(cellram));
}
byte getchar(byte x, byte y) {
return cellram[x][y];
}
void putchar(byte x, byte y, byte attr) {
cellram[x][y] = attr;
}
void putstring(byte x, byte y, const char* string) {
while (*string) {
2017-03-15 14:11:28 +00:00
putchar(x++, y, CHAR(*string++));
2017-02-20 23:50:29 +00:00
}
}
// PC font (code page 437)
const byte font8x8[0x100][8] = {
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, { 0x7e,0x81,0x95,0xb1,0xb1,0x95,0x81,0x7e }, { 0x7e,0xff,0xeb,0xcf,0xcf,0xeb,0xff,0x7e }, { 0x0e,0x1f,0x3f,0x7e,0x3f,0x1f,0x0e,0x00 }, { 0x08,0x1c,0x3e,0x7f,0x3e,0x1c,0x08,0x00 }, { 0x38,0x3a,0x9f,0xff,0x9f,0x3a,0x38,0x00 }, { 0x10,0x38,0xbc,0xff,0xbc,0x38,0x10,0x00 }, { 0x00,0x00,0x18,0x3c,0x3c,0x18,0x00,0x00 }, { 0xff,0xff,0xe7,0xc3,0xc3,0xe7,0xff,0xff }, { 0x00,0x3c,0x66,0x42,0x42,0x66,0x3c,0x00 }, { 0xff,0xc3,0x99,0xbd,0xbd,0x99,0xc3,0xff }, { 0x70,0xf8,0x88,0x88,0xfd,0x7f,0x07,0x0f }, { 0x00,0x4e,0x5f,0xf1,0xf1,0x5f,0x4e,0x00 }, { 0xc0,0xe0,0xff,0x7f,0x05,0x05,0x07,0x07 }, { 0xc0,0xff,0x7f,0x05,0x05,0x65,0x7f,0x3f }, { 0x99,0x5a,0x3c,0xe7,0xe7,0x3c,0x5a,0x99 }, { 0x7f,0x3e,0x3e,0x1c,0x1c,0x08,0x08,0x00 }, { 0x08,0x08,0x1c,0x1c,0x3e,0x3e,0x7f,0x00 }, { 0x00,0x24,0x66,0xff,0xff,0x66,0x24,0x00 }, { 0x00,0x5f,0x5f,0x00,0x00,0x5f,0x5f,0x00 }, { 0x06,0x0f,0x09,0x7f,0x7f,0x01,0x7f,0x7f }, { 0xda,0xbf,0xa5,0xa5,0xfd,0x59,0x03,0x02 }, { 0x00,0x70,0x70,0x70,0x70,0x70,0x70,0x00 }, { 0x80,0x94,0xb6,0xff,0xff,0xb6,0x94,0x80 }, { 0x00,0x04,0x06,0x7f,0x7f,0x06,0x04,0x00 }, { 0x00,0x10,0x30,0x7f,0x7f,0x30,0x10,0x00 }, { 0x08,0x08,0x08,0x2a,0x3e,0x1c,0x08,0x00 }, { 0x08,0x1c,0x3e,0x2a,0x08,0x08,0x08,0x00 }, { 0x3c,0x3c,0x20,0x20,0x20,0x20,0x20,0x00 }, { 0x08,0x1c,0x3e,0x08,0x08,0x3e,0x1c,0x08 }, { 0x30,0x38,0x3c,0x3e,0x3e,0x3c,0x38,0x30 }, { 0x06,0x0e,0x1e,0x3e,0x3e,0x1e,0x0e,0x06 }, { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, { 0x00,0x06,0x5f,0x5f,0x06,0x00,0x00,0x00 }, { 0x00,0x07,0x07,0x00,0x07,0x07,0x00,0x00 }, { 0x14,0x7f,0x7f,0x14,0x7f,0x7f,0x14,0x00 }, { 0x24,0x2e,0x6b,0x6b,0x3a,0x12,0x00,0x00 }, { 0x46,0x66,0x30,0x18,0x0c,0x66,0x62,0x00 }, { 0x30,0x7a,0x4f,0x5d,0x37,0x7a,0x48,0x00 }, { 0x04,0x07,0x03,0x00,0x00,0x00,0x00,0x00 }, { 0x00,0x1c,0x3e,0x63,0x41,0x00,0x00,0x00 }, { 0x00,0x41,0x63,0x3e,0x1c,0x00,0x00,0x00 }, { 0x08,0x2a,0x3e,0x1c,0x1c,0x3e,0x2a,0x08 }, { 0x08,0x08,0x3e,0x3e,0x08,0x08,0x00,0x00 }, { 0x00,0xa0,0xe0,0x60,0x00,0x00,0x00,0x00 }, { 0x08,0x08,0x08,0x08,0x08,0x08,0x00,0x00 }, { 0x00,0x00,0x60,0x60,0x00,0x00,0x00,0x00 }, { 0x60,0x30,0x18,0x0c,0x06,0x03,0x01,0x00 }, { 0x3e,0x7f,0x59,0x4d,0x7f,0x3e,0x00,0x00 }, { 0x42,0x42,0x7f,0x7f,0x40,0x40,0x00,0x00 }, { 0x62,0x73,0x59,0x49,0x6f,0x66,0x00,0x00 }, { 0x22,0x63,0x49,0x49,0x7f,0x36,0x00,0x00 }, { 0x18,0x1c,0x16,0x13,0x7f,0x7f,0x10,0x00 }, { 0x27,0x67,0x45,0x45,0x7d,0x39,0x00,0x00 }, { 0x3c,0x7e,0x4b,0x49,0x79,0x30,0x00,0x00 }, { 0x03,0x63,0x71,0x19,0x0f,0x07,0x00,0x00 }, { 0x36,0x7f,0x49,0x49,0x7f,0x36,0x00,0x00 }, { 0x06,0x4f,0x49,0x69,0x3f,0x1e,0x00,0x00 }, { 0x00,0x00,0x6c,0x6c,0x00,0x00,0x00,0x00 }, { 0x00,0xa0,0xec,0x6c,0x00,0x00,0x00,0x00 }, { 0x08,0x1c,0x36,0x63,0x41,0x00,0x00,0x00 }, { 0x14,0x14,0x14,0x14,0x14,0x14,0x00,0x00 }, { 0x00,0x41,0x63,0x36,0x1c,0x08,0x00,0x00 }, { 0x02,0x03,0x51,0x59,0x0f,0x06,0x00,0x00 }, { 0x3e,0x7f,0x41,0x5d,0x5d,0x1f,0x1e,0x00 }, { 0x7c,0x7e,0x13,0x13,0x7e,0x7c,0x00,0x00 }, { 0x41,0x7f,0x7f,0x49,0x49,0x7f,0x36,0x00 }, { 0x1c,0x3e,0x63,0x41,0x41,0x63,0x22,0x00 }, { 0x41,0x7f,0x7f,0x41,0x63,0x7f,0x1c,0x00 }, { 0x41,0x7f,0x7f,0x49,0x5d,0x41,0x63,0x00 }, { 0x41,0x7f,0x7f,0x49,0x1d,0x01,0x03,0x00 }, { 0x1c,0x3e,0x63,0x41,0x51,0x73,0x72,0x00 }, { 0x7f,0x7f,0x08,0x08,0x7f,0x7f,0x00,0x00 }, { 0x00,0x41,0x7f,0x7f,0x41,0x00,0x00,0x00 }, { 0x30,0x70,0x40,0x41,0x7f,0x3f,0x01,0x00 }, { 0x41,0x7f,0x7f,0x08,0x1c,0x77,0x63,0x00 }, { 0x41,0x7f,0x7f,0x41,0x40,0x60,0x70,0x00 }, { 0x7f,0x7f,0x06,0x0c,0x06,0x7f,0x7f,0x00 }, { 0x7f,0x7f,0x06,0x0c,0x18,0x7f,0x7f,0x00 }, { 0x1c,0x3e,0x63,0x41,0x63,0x3e,0x1c,0x00 }, { 0x41,0x7f,0x7f,0x49,0x09,0x0f,0x06,0x00 }, { 0x1e,0x3f,0x21,0x71,0x7f,0x5e,0x00,0x00 }, { 0x41,0x7f,0x7f,0x19,0x39,0x6f,0x46,0x00 }, { 0x26,0x67,0x4d,0x59,0x7b,0x32,0x00,0x00 }, { 0x03,0x41,0x7f,0x7f,0x41,0x03,0x00,0x00 }, { 0x7f,0x7f,0x40,0x40,0x7f,0x7f,0x00,0x00 }, { 0x1f,0x3f,0x60,0x60,0x3f,0x1f,0x00,0x00 }, { 0x7f,0x7f,0x30,0x18,0x30,0x7f,0x7f,0x00 }, { 0x63,0x77,0x1c,0x08,0x1c,0x77,0x63,0x00 }, { 0x07,0x4f,0x78,0x78,0x4f,0x07,0x00,0x00 }, { 0x67,0x73,0x59,0x4d,0x47,0x63,0x71,0x00 },
};
const char BOX_CHARS[8] = { 218, 191, 192, 217, 196, 196, 179, 179 };
void draw_box(byte x, byte y, byte x2, byte y2, const char* chars) {
byte x1 = x;
putchar(x, y, chars[2]);
putchar(x2, y, chars[3]);
putchar(x, y2, chars[0]);
putchar(x2, y2, chars[1]);
while (++x < x2) {
putchar(x, y, chars[5]);
putchar(x, y2, chars[4]);
}
while (++y < y2) {
putchar(x1, y, chars[6]);
putchar(x2, y, chars[7]);
}
}
void draw_bcd_word(byte x, byte y, word bcd) {
byte j;
x += 3;
for (j=0; j<4; j++) {
putchar(x, y, CHAR('0'+(bcd&0xf)));
x--;
bcd >>= 4;
}
}
2017-03-15 14:11:28 +00:00
/*
void draw_bcd_byte(byte x, byte y, byte bcd) {
putchar(CHAR('0'+(bcd&0xf)), x+1, y);
putchar(CHAR('0'+((bcd>>4)&0xf)), x, y);
}
void draw_bcd_word2(byte x, byte y, word bcd) {
draw_bcd_byte(x+2, y, bcd);
draw_bcd_byte(x, y, bcd>>8);
}
*/
2017-02-20 23:50:29 +00:00
void draw_playfield() {
draw_box(0,0,27,29,BOX_CHARS);
putstring(0,31,"PLAYER 1");
putstring(20,31,"PLAYER 2");
draw_bcd_word(0,30,players[0].score);
draw_bcd_word(24,30,players[1].score);
if (attract) {
if (credits) {
putstring(8,29,"PRESS START");
putstring(9,0,"CREDITS ");
putchar(9+8, 0, (credits>9?9:credits)+CHAR('0'));
} else {
putstring(9,29,"GAME OVER");
putstring(8,0,"INSERT COIN");
}
}
}
typedef enum { D_RIGHT, D_DOWN, D_LEFT, D_UP } dir_t;
const char DIR_X[4] = { 1, 0, -1, 0 };
const char DIR_Y[4] = { 0, -1, 0, 1 };
void init_game() {
memset(players, 0, sizeof(players));
players[0].head_attr = CHAR('1');
players[1].head_attr = CHAR('2');
players[0].tail_attr = 254;
players[1].tail_attr = 254;
frames_per_move = START_SPEED;
}
void reset_players() {
players[0].x = players[0].y = 6;
players[0].dir = D_RIGHT;
players[1].x = players[1].y = 21;
players[1].dir = D_LEFT;
players[0].collided = players[1].collided = 0;
}
void draw_player(Player* p) {
putchar(p->x, p->y, p->head_attr);
}
void move_player(Player* p) {
putchar(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) != CHAR(' '))
p->collided = 1;
draw_player(p);
}
void human_control(Player* p) {
byte dir = 0xff;
if (!p->human) return;
if (LEFT1) dir = D_LEFT;
if (RIGHT1) dir = D_RIGHT;
if (UP1) dir = D_UP;
if (DOWN1) dir = D_DOWN;
// don't let the player reverse
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 < 29 && y < 27 && getchar(x, y) == CHAR(' ')) {
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);
}
}
// add two 16-bit BCD values
word bcd_add(word a, word b) {
a; b; // to avoid warning
__asm
ld hl,#4
add hl,sp
ld iy,#2
add iy,sp
ld a,0 (iy)
add a, (hl)
daa
ld c,a
ld a,1 (iy)
inc hl
adc a, (hl)
daa
ld b,a
ld l, c
ld h, b
__endasm;
}
void slide_right() {
byte j;
for (j=0; j<32; j++) {
memmove(&cellram[1], &cellram[0], sizeof(cellram)-sizeof(cellram[0]));
memset(&cellram[0], 0, sizeof(cellram[0]));
}
}
void flash_colliders() {
byte i;
// flash players that collided
for (i=0; i<60; i++) {
if (players[0].collided) players[0].head_attr ^= 0x80;
if (players[1].collided) players[1].head_attr ^= 0x80;
wait_for_vsync();
wait_for_vsync();
draw_player(&players[0]);
draw_player(&players[1]);
palette = i;
set8910(7, 0xff ^ 0x8);
set8910(6, i>>1);
set8910(8, 10);
}
set8910(8, 0);
palette = 0;
}
void make_move() {
byte i;
for (i=0; i<frames_per_move; i++) {
human_control(&players[0]);
wait_for_vsync();
}
ai_control(&players[0]);
ai_control(&players[1]);
// if players collide, 2nd player gets the point
move_player(&players[1]);
move_player(&players[0]);
}
void play_game();
char start_pressed() {
if (attract) {
if (credits > 0 && START1) {
credits--;
return 1;
}
}
return 0;
}
void declare_winner(byte winner) {
byte i;
for (i=0; i<10; i++) {
draw_box(i,i,27-i,29-i,BOX_CHARS);
wait_for_vsync();
}
putstring(10,16,"WINNER:");
putstring(10,13,"PLAYER ");
putchar(10+7, 13, CHAR('1')+winner);
for (i=0; i<250; i++)
wait_for_vsync();
slide_right();
attract = 1;
}
void play_round() {
reset_players();
clrscr();
draw_playfield();
while (1) {
make_move();
if (players[0].collided || players[1].collided) break;
if (start_pressed()) {
play_game();
return;
}
}
flash_colliders();
// don't keep score in attract mode
if (attract) return;
// add scores to players that didn't collide
if (players[0].collided)
players[1].score = bcd_add(players[1].score, 1);
if (players[1].collided)
players[0].score = bcd_add(players[0].score, 1);
// increase speed
if (frames_per_move > 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() {
attract = 0;
init_game();
players[0].human = 1;
while (!attract) {
play_round();
}
}
void attract_mode() {
attract = 1;
init_game();
frames_per_move = 9;
players[0].human = 0;
while (1) {
play_round();
}
}
void test_ram() {
word i;
for (i=0; i<0x800; i++) {
cellram[0][i & 0x3ff] = rand();
}
}
void main() {
if (COIN1) {
credits++;
} else {
test_ram();
}
palette = 0;
memcpy(tileram, font8x8, sizeof(font8x8));
draw_playfield();
attract_mode();
}