#include #include #include typedef unsigned char byte; typedef signed char sbyte; typedef unsigned short word; #define POKE(addr,val) (*(unsigned char*) (addr) = (val)) #define POKEW(addr,val) (*(unsigned*) (addr) = (val)) #define PEEK(addr) (*(unsigned char*) (addr)) #define PEEKW(addr) (*(unsigned*) (addr)) #define STROBE(addr) __asm__ ("sta %w", addr) #define CLICK STROBE(0xc030) /// HIRES LOOKUP TABLE #define VHEIGHT 192 #define VBWIDTH 40 #define LUT(x) (byte*)(0x2000|x) static byte* vidmem[VHEIGHT] = { LUT(0x0000), LUT(0x0400), LUT(0x0800), LUT(0x0c00), LUT(0x1000), LUT(0x1400), LUT(0x1800), LUT(0x1c00), LUT(0x0080), LUT(0x0480), LUT(0x0880), LUT(0x0c80), LUT(0x1080), LUT(0x1480), LUT(0x1880), LUT(0x1c80), LUT(0x0100), LUT(0x0500), LUT(0x0900), LUT(0x0d00), LUT(0x1100), LUT(0x1500), LUT(0x1900), LUT(0x1d00), LUT(0x0180), LUT(0x0580), LUT(0x0980), LUT(0x0d80), LUT(0x1180), LUT(0x1580), LUT(0x1980), LUT(0x1d80), LUT(0x0200), LUT(0x0600), LUT(0x0a00), LUT(0x0e00), LUT(0x1200), LUT(0x1600), LUT(0x1a00), LUT(0x1e00), LUT(0x0280), LUT(0x0680), LUT(0x0a80), LUT(0x0e80), LUT(0x1280), LUT(0x1680), LUT(0x1a80), LUT(0x1e80), LUT(0x0300), LUT(0x0700), LUT(0x0b00), LUT(0x0f00), LUT(0x1300), LUT(0x1700), LUT(0x1b00), LUT(0x1f00), LUT(0x0380), LUT(0x0780), LUT(0x0b80), LUT(0x0f80), LUT(0x1380), LUT(0x1780), LUT(0x1b80), LUT(0x1f80), LUT(0x0028), LUT(0x0428), LUT(0x0828), LUT(0x0c28), LUT(0x1028), LUT(0x1428), LUT(0x1828), LUT(0x1c28), LUT(0x00a8), LUT(0x04a8), LUT(0x08a8), LUT(0x0ca8), LUT(0x10a8), LUT(0x14a8), LUT(0x18a8), LUT(0x1ca8), LUT(0x0128), LUT(0x0528), LUT(0x0928), LUT(0x0d28), LUT(0x1128), LUT(0x1528), LUT(0x1928), LUT(0x1d28), LUT(0x01a8), LUT(0x05a8), LUT(0x09a8), LUT(0x0da8), LUT(0x11a8), LUT(0x15a8), LUT(0x19a8), LUT(0x1da8), LUT(0x0228), LUT(0x0628), LUT(0x0a28), LUT(0x0e28), LUT(0x1228), LUT(0x1628), LUT(0x1a28), LUT(0x1e28), LUT(0x02a8), LUT(0x06a8), LUT(0x0aa8), LUT(0x0ea8), LUT(0x12a8), LUT(0x16a8), LUT(0x1aa8), LUT(0x1ea8), LUT(0x0328), LUT(0x0728), LUT(0x0b28), LUT(0x0f28), LUT(0x1328), LUT(0x1728), LUT(0x1b28), LUT(0x1f28), LUT(0x03a8), LUT(0x07a8), LUT(0x0ba8), LUT(0x0fa8), LUT(0x13a8), LUT(0x17a8), LUT(0x1ba8), LUT(0x1fa8), LUT(0x0050), LUT(0x0450), LUT(0x0850), LUT(0x0c50), LUT(0x1050), LUT(0x1450), LUT(0x1850), LUT(0x1c50), LUT(0x00d0), LUT(0x04d0), LUT(0x08d0), LUT(0x0cd0), LUT(0x10d0), LUT(0x14d0), LUT(0x18d0), LUT(0x1cd0), LUT(0x0150), LUT(0x0550), LUT(0x0950), LUT(0x0d50), LUT(0x1150), LUT(0x1550), LUT(0x1950), LUT(0x1d50), LUT(0x01d0), LUT(0x05d0), LUT(0x09d0), LUT(0x0dd0), LUT(0x11d0), LUT(0x15d0), LUT(0x19d0), LUT(0x1dd0), LUT(0x0250), LUT(0x0650), LUT(0x0a50), LUT(0x0e50), LUT(0x1250), LUT(0x1650), LUT(0x1a50), LUT(0x1e50), LUT(0x02d0), LUT(0x06d0), LUT(0x0ad0), LUT(0x0ed0), LUT(0x12d0), LUT(0x16d0), LUT(0x1ad0), LUT(0x1ed0), LUT(0x0350), LUT(0x0750), LUT(0x0b50), LUT(0x0f50), LUT(0x1350), LUT(0x1750), LUT(0x1b50), LUT(0x1f50), LUT(0x03d0), LUT(0x07d0), LUT(0x0bd0), LUT(0x0fd0), LUT(0x13d0), LUT(0x17d0), LUT(0x1bd0), LUT(0x1fd0) }; /// SOUND FUNCTIONS void tone(byte freq, byte dur, sbyte mod) { word i; while (dur--) { for (i=0; i>3]; *dest ^= 0x1 << (y&7); } void draw_vline(byte x, byte y1, byte y2) { byte yb1 = y1/8; byte yb2 = y2/8; byte* dest = &vidmem[x][yb1]; signed char nbytes = yb2 - yb1; *dest++ ^= 0xff << (y1&7); if (nbytes > 0) { while (--nbytes > 0) { *dest++ ^= 0xff; } *dest ^= 0xff >> (~y2&7); } else { *--dest ^= 0xff << ((y2+1)&7); } } #define LOCHAR 0x20 #define HICHAR 0x5e const byte font8x8[HICHAR-LOCHAR+1][8] = {/*{w:8,h:8,bpp:1,count:64,xform:"rotate(-90deg)"}*/ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, { 0x00,0x00,0x00,0x79,0x79,0x00,0x00,0x00 }, { 0x00,0x70,0x70,0x00,0x00,0x70,0x70,0x00 }, { 0x14,0x7f,0x7f,0x14,0x14,0x7f,0x7f,0x14 }, { 0x00,0x12,0x3a,0x6b,0x6b,0x2e,0x24,0x00 }, { 0x00,0x63,0x66,0x0c,0x18,0x33,0x63,0x00 }, { 0x00,0x26,0x7f,0x59,0x59,0x77,0x27,0x05 }, { 0x00,0x00,0x00,0x10,0x30,0x60,0x40,0x00 }, { 0x00,0x00,0x1c,0x3e,0x63,0x41,0x00,0x00 }, { 0x00,0x00,0x41,0x63,0x3e,0x1c,0x00,0x00 }, { 0x08,0x2a,0x3e,0x1c,0x1c,0x3e,0x2a,0x08 }, { 0x00,0x08,0x08,0x3e,0x3e,0x08,0x08,0x00 }, { 0x00,0x00,0x00,0x03,0x03,0x00,0x00,0x00 }, { 0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00 }, { 0x00,0x00,0x00,0x03,0x03,0x00,0x00,0x00 }, { 0x00,0x01,0x03,0x06,0x0c,0x18,0x30,0x20 }, { 0x00,0x3e,0x7f,0x49,0x51,0x7f,0x3e,0x00 }, { 0x00,0x01,0x11,0x7f,0x7f,0x01,0x01,0x00 }, { 0x00,0x23,0x67,0x45,0x49,0x79,0x31,0x00 }, { 0x00,0x22,0x63,0x49,0x49,0x7f,0x36,0x00 }, { 0x00,0x0c,0x0c,0x14,0x34,0x7f,0x7f,0x04 }, { 0x00,0x72,0x73,0x51,0x51,0x5f,0x4e,0x00 }, { 0x00,0x3e,0x7f,0x49,0x49,0x6f,0x26,0x00 }, { 0x00,0x60,0x60,0x4f,0x5f,0x70,0x60,0x00 }, { 0x00,0x36,0x7f,0x49,0x49,0x7f,0x36,0x00 }, { 0x00,0x32,0x7b,0x49,0x49,0x7f,0x3e,0x00 }, { 0x00,0x00,0x00,0x12,0x12,0x00,0x00,0x00 }, { 0x00,0x00,0x00,0x13,0x13,0x00,0x00,0x00 }, { 0x00,0x08,0x1c,0x36,0x63,0x41,0x41,0x00 }, { 0x00,0x14,0x14,0x14,0x14,0x14,0x14,0x00 }, { 0x00,0x41,0x41,0x63,0x36,0x1c,0x08,0x00 }, { 0x00,0x20,0x60,0x45,0x4d,0x78,0x30,0x00 }, { 0x00,0x3e,0x7f,0x41,0x59,0x79,0x3a,0x00 }, { 0x00,0x1f,0x3f,0x68,0x68,0x3f,0x1f,0x00 }, { 0x00,0x7f,0x7f,0x49,0x49,0x7f,0x36,0x00 }, { 0x00,0x3e,0x7f,0x41,0x41,0x63,0x22,0x00 }, { 0x00,0x7f,0x7f,0x41,0x63,0x3e,0x1c,0x00 }, { 0x00,0x7f,0x7f,0x49,0x49,0x41,0x41,0x00 }, { 0x00,0x7f,0x7f,0x48,0x48,0x40,0x40,0x00 }, { 0x00,0x3e,0x7f,0x41,0x49,0x6f,0x2e,0x00 }, { 0x00,0x7f,0x7f,0x08,0x08,0x7f,0x7f,0x00 }, { 0x00,0x00,0x41,0x7f,0x7f,0x41,0x00,0x00 }, { 0x00,0x02,0x03,0x41,0x7f,0x7e,0x40,0x00 }, { 0x00,0x7f,0x7f,0x1c,0x36,0x63,0x41,0x00 }, { 0x00,0x7f,0x7f,0x01,0x01,0x01,0x01,0x00 }, { 0x00,0x7f,0x7f,0x30,0x18,0x30,0x7f,0x7f }, { 0x00,0x7f,0x7f,0x38,0x1c,0x7f,0x7f,0x00 }, { 0x00,0x3e,0x7f,0x41,0x41,0x7f,0x3e,0x00 }, { 0x00,0x7f,0x7f,0x48,0x48,0x78,0x30,0x00 }, { 0x00,0x3c,0x7e,0x42,0x43,0x7f,0x3d,0x00 }, { 0x00,0x7f,0x7f,0x4c,0x4e,0x7b,0x31,0x00 }, { 0x00,0x32,0x7b,0x49,0x49,0x6f,0x26,0x00 }, { 0x00,0x40,0x40,0x7f,0x7f,0x40,0x40,0x00 }, { 0x00,0x7e,0x7f,0x01,0x01,0x7f,0x7e,0x00 }, { 0x00,0x7c,0x7e,0x03,0x03,0x7e,0x7c,0x00 }, { 0x00,0x7f,0x7f,0x06,0x0c,0x06,0x7f,0x7f }, { 0x00,0x63,0x77,0x1c,0x1c,0x77,0x63,0x00 }, { 0x00,0x70,0x78,0x0f,0x0f,0x78,0x70,0x00 }, { 0x00,0x43,0x47,0x4d,0x59,0x71,0x61,0x00 }, { 0x00,0x00,0x7f,0x7f,0x41,0x41,0x00,0x00 }, { 0x00,0x20,0x30,0x18,0x0c,0x06,0x03,0x01 }, { 0x00,0x00,0x41,0x41,0x7f,0x7f,0x00,0x00 }, { 0x00,0x08,0x18,0x3f,0x3f,0x18,0x08,0x00 } }; void draw_sprite(const byte* src, byte x, byte y) { byte i,j; byte w = *src++; byte h = *src++; for (j=0; j>= 4; } } word bcd_add(word a, word b) { word result; __asm__ ("sed"); // set decimal (BCD) mode result = a+b; __asm__ ("cld"); // clear BCD mode return result; } // // GAME GRAPHICS // const byte player_bitmap[] = {2,27,/*{w:16,h:27,bpp:1,xform:"rotate(-90deg)"}*/0x0,0x0,0x0,0x0,0x0f,0x00,0x3e,0x00,0xf4,0x07,0xec,0x00,0x76,0x00,0x2b,0x00,0x33,0x00,0x75,0x00,0xf5,0x00,0xeb,0x31,0xbf,0xef,0x3f,0xcf,0xbf,0xef,0xeb,0x31,0xf5,0x00,0x75,0x00,0x33,0x00,0x2b,0x00,0x76,0x00,0xec,0x00,0xf4,0x07,0x3e,0x00,0x0f,0x00,0x00,0x00,0x0,0x0}; const byte bomb_bitmap[] = {1,5,/*{w:8,h:5,bpp:1,xform:"rotate(-90deg)"}*/0x88,0x55,0x77,0x55,0x88}; const byte bullet_bitmap[] = {2,2,/*{w:16,h:2,bpp:1,xform:"rotate(-90deg)"}*/0x88,0x88,0x44,0x44}; const byte enemy1_bitmap[] = {2,17,/*{w:16,h:17,bpp:1,xform:"rotate(-90deg)"}*/0x00,0x00,0x00,0x0c,0x04,0x1e,0x46,0x3f,0xb8,0x7f,0xb0,0x7f,0xba,0x7f,0xfd,0x3f,0xfc,0x07,0xfc,0x07,0xfd,0x3f,0xba,0x7f,0xb0,0x7f,0xb8,0x7f,0x46,0x3f,0x04,0x1e,0x00,0x0c}; const byte enemy2_bitmap[] = {2,16,/*{w:16,h:16,bpp:1,xform:"rotate(-90deg)"}*/0x26,0x00,0x59,0x10,0x10,0x30,0x33,0x18,0xe6,0x61,0xc4,0x56,0x03,0x03,0xdc,0x03,0xdc,0x03,0x03,0x03,0xc4,0x56,0xe6,0x61,0x33,0x18,0x10,0x30,0x59,0x10,0x26,0x00}; const byte enemy3_bitmap[] = {2,16,/*{w:16,h:16,bpp:1,xform:"rotate(-90deg)"}*/0x80,0x1f,0xc0,0x03,0xf8,0x3f,0x70,0x00,0xf0,0x01,0xfc,0x07,0xe8,0x01,0xf8,0x03,0xf8,0x03,0xe8,0x01,0xf8,0x07,0xf0,0x01,0x70,0x00,0xf8,0x3f,0xc0,0x03,0x80,0x1f}; const byte enemy4_bitmap[] = {2,16,/*{w:16,h:16,bpp:1,xform:"rotate(-90deg)"}*/0x06,0x00,0x0c,0x00,0x28,0x00,0x70,0x1f,0x84,0x3f,0xde,0x37,0xbb,0x3f,0xf0,0x3f,0xf0,0x3f,0xbb,0x3f,0xde,0x37,0x84,0x3f,0x70,0x1f,0x28,0x00,0x0c,0x00,0x06,0x00}; const byte* const enemy_bitmaps[4] = { enemy1_bitmap, enemy2_bitmap, enemy3_bitmap, enemy4_bitmap }; // // GAME CODE // byte attract; byte credits; byte curplayer; word score; byte lives; #define MAXLIVES 5 byte player_x; byte bullet_x; byte bullet_y; byte bomb_x; byte bomb_y; typedef struct { byte x,y; const byte* shape; // need const here } Enemy; #define MAX_ENEMIES 28 Enemy enemies[MAX_ENEMIES]; byte enemy_index; byte num_enemies; typedef struct { int right:1; int down:1; } MarchMode; MarchMode this_mode, next_mode; void draw_lives(byte player) { byte i; byte n = lives; byte x = player ? (22-MAXLIVES) : 6; byte y = VBWIDTH-3; for (i=0; i j) { CLICK; } } } } void destroy_player() { xor_player_derez(); // xor derez pattern xor_sprite(player_bitmap, player_x, 1); // erase ship via xor xor_player_derez(); // xor 2x to erase derez pattern player_x = 0xff; lives--; } void init_enemies() { byte i,x,y,bm; x=0; y=26; bm=0; for (i=0; ix = x; e->y = y; e->shape = enemy_bitmaps[bm]; x += 28; if (x >= VHEIGHT-32) { x = 0; y -= 3; bm++; } } enemy_index = 0; num_enemies = MAX_ENEMIES; this_mode.right = 1; this_mode.down = 0; next_mode.right = 1; next_mode.down = 0; } void delete_enemy(Enemy* e) { clear_sprite(e->shape, e->x, e->y); memmove(e, e+1, sizeof(Enemy)*(enemies-e+MAX_ENEMIES-1)); num_enemies--; // update_next_enemy() will check enemy_index tone(1,10,10); } void update_next_enemy() { Enemy* e; if (enemy_index >= num_enemies) { enemy_index = 0; memcpy(&this_mode, &next_mode, sizeof(this_mode)); tone(220+num_enemies,3,1); } e = &enemies[enemy_index]; clear_sprite(e->shape, e->x, e->y); if (this_mode.down) { // if too close to ground, end game if (--e->y < 5) { destroy_player(); lives = 0; } next_mode.down = 0; } else { if (this_mode.right) { e->x += 2; if (e->x >= VHEIGHT-32) { next_mode.down = 1; next_mode.right = 0; } } else { e->x -= 2; if (e->x == 0) { next_mode.down = 1; next_mode.right = 1; } } } draw_sprite(e->shape, e->x, e->y); enemy_index++; } void draw_bunker(byte x, byte y, byte y2, byte h, byte w) { byte i; for (i=0; ishape[0]; byte ew = e->shape[1]; return (x >= e->x-w && x <= e->x+ew && y >= e->y-h && y <= e->y+eh); } Enemy* find_enemy_at(byte x, byte y) { byte i; for (i=0; i 26) { clear_sprite(bullet_bitmap, bullet_x, bullet_y); check_bullet_hit(bullet_x, bullet_y+2); bullet_y = 0; } else { bullet_y++; tone(bullet_y,3,0); xor_sprite(bullet_bitmap, bullet_x, bullet_y); // draw } } void drop_bomb() { Enemy* e = &enemies[enemy_index]; bomb_x = e->x + 7; bomb_y = e->y - 2; xor_sprite(bomb_bitmap, bomb_x, bomb_y); } void move_bomb() { byte leftover = xor_sprite(bomb_bitmap, bomb_x, bomb_y); // erase if (bomb_y < 2) { bomb_y = 0; } else if (leftover) { erase_sprite(bomb_bitmap, bomb_x, bomb_y); // erase bunker if (bomb_y < 3) { // player was hit (probably) destroy_player(); } bomb_y = 0; } else { bomb_y--; xor_sprite(bomb_bitmap, bomb_x, bomb_y); } } byte frame; signed char player_dir = 0; void move_player() { if (attract) { if (bullet_y == 0) fire_bullet(); } else { char key; // handle keyboard if (kbhit()) { key = cgetc(); switch (key) { case 'A': player_dir = player_dir < 0 ? 0 : -2; break; case 'Z': player_dir = player_dir > 0 ? 0 : 2; break; case ' ': if (bullet_y == 0) { fire_bullet(); } break; } } // move player if (player_dir < 0 && player_x > 0) player_x += player_dir; else if (player_dir > 0 && player_x < VHEIGHT-28) player_x += player_dir; } draw_sprite(player_bitmap, player_x, 1); } void play_round() { draw_playfield(); player_x = VHEIGHT/2-8; bullet_y = 0; bomb_y = 0; frame = 0; while (player_x != 0xff && num_enemies) { move_player(); if (bullet_y) { move_bullet(); } update_next_enemy(); if (frame & 1) { if (bomb_y == 0) { drop_bomb(); } else { move_bomb(); } } frame++; } } void init_game() { score = 0; lives = 5; curplayer = 0; } void game_over_msg() { byte i; for (i=0; i<50; i++) { draw_string(" *************** ", 5, 15); draw_string("*** ***", 5, 16); draw_string("** GAME OVER **", 5, 17); draw_string("*** ***", 5, 18); draw_string(" *************** ", 5, 19); } } void play_game() { attract = 0; init_game(); init_enemies(); while (lives) { play_round(); if (num_enemies == 0) { init_enemies(); } } game_over_msg(); } void attract_mode() { attract = 1; while (1) { init_enemies(); play_round(); } } void main() { // NOTE: initializers don't get run, so we init here credits = 0; while (1) { //attract_mode(); play_game(); } }