#pragma regalloc- //I allocate the registers myself #pragma optsize- //optimize for speed // Macros used to delay things using nops rather than delay, when a fine delay is needed #asm .macro assemblymaster nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop .endm .macro buttondelay nop nop nop nop nop nop nop nop nop nop nop nop .endm #endasm #include #include #include #include #include //cycles = 63.625 * 16 Note NTSC is 63.55 //but this line duration makes each frame exactly 1/60 sec //which is nice for keeping a realtime clock #define lineTime 1018 // Dimensions of the raster we are drawing #define ScreenTop 30 #define ScreenBot 222 #define T0reload 256-60 // Defines which bits on PORT A are used for what NES Controller function #define NIN_DATA_P1 1 #define NIN_DATA_P2 2 #define NIN_DATA_P3 4 #define NIN_DATA_P4 8 #define NIN_PULSE 16 #define NIN_LATCH 32 // Defines which bit in the input vector corresponds to which button on the NES controller #define BTN_A 128 #define BTN_B 64 #define BTN_SELECT 32 #define BTN_START 16 #define BTN_UP 8 #define BTN_DOWN 4 #define BTN_LEFT 2 #define BTN_RIGHT 1 // Defines the states the game can be in #define STATE_STARTUP 0 #define STATE_SCOREBOARD 1 #define STATE_PLAY 2 // The maximum velocity the ship can go (in 1/256ths of pixels) #define MAX_VELOCITY 450 void readNintendo(void); void overlayImage(unsigned char scr_x, unsigned char scr_y, unsigned char *NW, unsigned char *NE, unsigned char *SW, unsigned char *SE, flash unsigned char *image); void eraseImage(unsigned char scr_x, unsigned char scr_y); void findStoragePtrs(char scr_x, char scr_y); void initialize(void); void alterShips(void); void moveShips(void); int allocateStorage(void); // Hard allocation to registers, these variables are used in video drawing as well // as for temporary variables in various points in the program register unsigned char v1 @4; register unsigned char v2 @5; register unsigned char k @6; register unsigned char j @7; register unsigned char v3 @8; register int i @12; #pragma regalloc+ // The first graphic we ever made :) flash unsigned char penguin[32] = {0x00, 0x0a, 0xa0, 0x00, 0x00, 0xac, 0xca, 0x00, 0x00, 0xaf, 0xfa, 0x00, 0x00, 0xaf, 0xfa, 0x00, 0x03, 0xcf, 0xfc, 0x30, 0xaf, 0xf6, 0x6f, 0xfa, 0x0f, 0xf8, 0x8f, 0xf0, 0x00, 0xf0, 0x0f, 0x00}; // The bulk of our graphics come from very large arrays // Each nibble corresponds to a pixel, and the images are 8x8 flash unsigned char shipgraphics[512] = { // Ship: 0 degrees 0x00, 0x0A, 0x00, 0x00, 0x00, 0x2C, 0x20, 0x00, 0x00, 0x55, 0x50, 0x00, 0x00, 0x95, 0x80, 0x00, 0x29, 0xB8, 0xB8, 0x10, 0x47, 0xAB, 0xA6, 0x40, 0x05, 0x88, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, // Ship: 22.5 degrees 0x00, 0x00, 0x54, 0x00, 0x00, 0x02, 0xB2, 0x00, 0x00, 0x38, 0x71, 0x00, 0x39, 0xB7, 0xA0, 0x00, 0x06, 0xAB, 0xB5, 0x00, 0x03, 0x79, 0x95, 0x10, 0x00, 0x02, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, // Ship: 45 degrees 0x00, 0x00, 0x01, 0x20, 0x10, 0x00, 0x6C, 0x00, 0x19, 0x99, 0x65, 0x00, 0x29, 0xB7, 0x90, 0x00, 0x18, 0xBB, 0x80, 0x00, 0x02, 0x89, 0x80, 0x00, 0x00, 0x12, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, // Ship: 67.5 degrees 0x01, 0x00, 0x00, 0x00, 0x06, 0x61, 0x13, 0x40, 0x49, 0xBA, 0x7B, 0x40, 0x3A, 0xB8, 0x81, 0x00, 0x08, 0xAB, 0x10, 0x00, 0x04, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Ship: 90 degrees 0x05, 0x10, 0x00, 0x00, 0x58, 0x80, 0x00, 0x00, 0x9A, 0xB8, 0x51, 0x00, 0x9B, 0x74, 0x6C, 0x80, 0x9A, 0xB8, 0x41, 0x00, 0x58, 0x60, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Ship: 112.5 degrees 0x00, 0x03, 0x00, 0x00, 0x04, 0x59, 0x00, 0x00, 0x19, 0xAB, 0x10, 0x00, 0x4A, 0xB7, 0x81, 0x00, 0x49, 0xBA, 0x7B, 0x30, 0x06, 0x40, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Ship: 135 degrees 0x00, 0x11, 0x20, 0x00, 0x03, 0x99, 0x80, 0x00, 0x29, 0xBB, 0x70, 0x00, 0x3A, 0xB6, 0x90, 0x00, 0x28, 0x89, 0x64, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, // Ship: 157.5 degrees 0x00, 0x14, 0x30, 0x00, 0x05, 0x9A, 0x86, 0x00, 0x07, 0xAB, 0xB4, 0x00, 0x39, 0xB7, 0xA0, 0x00, 0x00, 0x28, 0x80, 0x00, 0x00, 0x03, 0xC1, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, // Ship: 180 degrees 0x06, 0x99, 0x93, 0x00, 0x58, 0xAB, 0xA8, 0x50, 0x18, 0xB8, 0xA6, 0x00, 0x00, 0x95, 0x70, 0x00, 0x00, 0x67, 0x30, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Ship: 202.5 degrees 0x00, 0x53, 0x00, 0x00, 0x16, 0xAA, 0x83, 0x00, 0x06, 0xBB, 0xA4, 0x00, 0x02, 0x88, 0xB8, 0x20, 0x03, 0x68, 0x10, 0x00, 0x05, 0xA0, 0x00, 0x00, 0x06, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Ship: 225 degrees 0x01, 0x13, 0x10, 0x00, 0x00, 0x9A, 0x82, 0x00, 0x00, 0xAB, 0xB8, 0x10, 0x01, 0x97, 0xB8, 0x10, 0x07, 0x68, 0x88, 0x10, 0x3B, 0x30, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Ship: 247.5 degrees 0x00, 0x03, 0x00, 0x00, 0x00, 0x09, 0x83, 0x00, 0x00, 0x4B, 0xA7, 0x00, 0x04, 0x87, 0xB9, 0x20, 0x8B, 0x8A, 0xB7, 0x20, 0x20, 0x00, 0x55, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // Ship: 270 degrees 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x97, 0x60, 0x04, 0x69, 0xBA, 0x80, 0x9B, 0x65, 0x8B, 0x80, 0x00, 0x38, 0xBA, 0x80, 0x00, 0x00, 0x86, 0x30, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, // Ship: 292.5 degrees 0x00, 0x00, 0x01, 0x00, 0x64, 0x32, 0x75, 0x00, 0x3A, 0x69, 0xBA, 0x40, 0x01, 0x88, 0xB9, 0x20, 0x00, 0x2B, 0xA7, 0x00, 0x00, 0x08, 0x42, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Ship: 315 degrees 0x32, 0x00, 0x00, 0x00, 0x0C, 0x61, 0x00, 0x00, 0x05, 0x59, 0xA8, 0x10, 0x00, 0xA8, 0xBA, 0x20, 0x00, 0xAB, 0xB8, 0x00, 0x00, 0x89, 0x81, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Ship: 337.5 degrees 0x04, 0x50, 0x00, 0x00, 0x02, 0xA3, 0x00, 0x00, 0x01, 0x78, 0x30, 0x00, 0x01, 0xA8, 0xB8, 0x20, 0x07, 0xBB, 0xA6, 0x00, 0x15, 0x99, 0x72, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; flash unsigned char explosiongraphics[512] = { // Explosion: frame 1 0x00, 0x10, 0x41, 0x00, 0x03, 0xBC, 0x57, 0x40, 0x15, 0xBF, 0xB9, 0xC0, 0x10, 0x6C, 0x90, 0x30, 0x04, 0xFF, 0x13, 0x40, 0x0B, 0xEE, 0x40, 0x00, 0x00, 0x28, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, // Explosion: frame 2 0x00, 0x11, 0x77, 0x00, 0x28, 0x42, 0x55, 0x10, 0x1B, 0x84, 0x78, 0x20, 0x4C, 0xB8, 0xA8, 0x30, 0x65, 0x38, 0x95, 0x10, 0x01, 0x31, 0x77, 0x00, 0x03, 0x30, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, // Explosion: frame 3 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x02, 0xEF, 0x50, 0x90, 0x00, 0xAC, 0x40, 0x00, 0x00, 0x3F, 0xF5, 0x00, 0x00, 0x5F, 0xFF, 0x10, 0x00, 0x03, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, // Explosion: frame 4 0x00, 0x00, 0xC0, 0x00, 0x00, 0x01, 0x10, 0x00, 0x04, 0x87, 0x24, 0x80, 0x48, 0xA8, 0xBB, 0x70, 0x27, 0x75, 0xAA, 0x00, 0x01, 0x42, 0x43, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Explosion: frame 5 0x00, 0x00, 0x31, 0x00, 0x02, 0x89, 0x45, 0x30, 0x13, 0x8B, 0x87, 0x90, 0x10, 0x49, 0x70, 0x20, 0x03, 0xBB, 0x12, 0x30, 0x08, 0xAB, 0x30, 0x00, 0x00, 0x16, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, // Explosion: frame 6 0x00, 0x00, 0x67, 0x00, 0x07, 0x10, 0x33, 0x00, 0x0C, 0x82, 0x78, 0x00, 0x2D, 0xD8, 0xB8, 0x00, 0x43, 0x18, 0xA4, 0x00, 0x00, 0x00, 0x66, 0x00, 0x01, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, // Explosion: frame 7 0x00, 0x01, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x02, 0x77, 0x30, 0x00, 0x01, 0x5F, 0x30, 0x00, 0x00, 0x28, 0x73, 0x00, 0x00, 0x38, 0x97, 0x20, 0x00, 0x02, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, // Explosion: frame 8 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x03, 0x65, 0x23, 0x60, 0x36, 0x86, 0x88, 0x50, 0x15, 0x53, 0x78, 0x00, 0x01, 0x31, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Explosion: frame 9 0x00, 0x14, 0x10, 0x00, 0x00, 0x28, 0x76, 0x00, 0x21, 0x08, 0x82, 0x00, 0x20, 0x57, 0x30, 0x10, 0x65, 0x68, 0x62, 0x10, 0x24, 0x37, 0x61, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Explosion: frame 10 0x00, 0x40, 0x00, 0x00, 0x05, 0x50, 0x00, 0x00, 0x03, 0x76, 0x12, 0x30, 0x06, 0x86, 0x9A, 0x20, 0x06, 0x52, 0x69, 0x00, 0x02, 0x20, 0x15, 0x00, 0x05, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Explosion: frame 11 0x02, 0x32, 0x00, 0x00, 0x15, 0x66, 0x20, 0x00, 0x02, 0x56, 0x20, 0x00, 0x00, 0x20, 0x41, 0x00, 0x00, 0x25, 0x51, 0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Explosion: frame 12 0x04, 0x40, 0x00, 0x00, 0x02, 0x66, 0x10, 0x00, 0x01, 0x65, 0x20, 0x00, 0x03, 0x42, 0x10, 0x00, 0x04, 0x64, 0x20, 0x00, 0x02, 0x44, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Explosion: frame 13 0x00, 0x10, 0x00, 0x00, 0x00, 0x24, 0x31, 0x00, 0x01, 0x25, 0x42, 0x00, 0x02, 0x23, 0x10, 0x00, 0x03, 0x44, 0x41, 0x00, 0x00, 0x46, 0x51, 0x00, 0x00, 0x15, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, // Explosion: frame 14 0x00, 0x10, 0x00, 0x00, 0x00, 0x43, 0x21, 0x00, 0x03, 0x42, 0x22, 0x00, 0x04, 0x42, 0x54, 0x10, 0x04, 0x42, 0x43, 0x00, 0x00, 0x10, 0x31, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Explosion: frame 15 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x01, 0x33, 0x21, 0x00, 0x00, 0x11, 0x12, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x23, 0x12, 0x00, 0x01, 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // Explosion: frame 16 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x11, 0x21, 0x00, 0x00, 0x11, 0x10, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; flash unsigned char smallnumbergraphics[320] = { // [0,0] 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xDB, 0x20, 0x00, 0x6F, 0x5D, 0xC0, 0x00, 0x9E, 0x09, 0xF0, 0x00, 0x9E, 0x09, 0xF0, 0x00, 0x7F, 0x1B, 0xD0, 0x00, 0x1D, 0xFF, 0x50, 0x00, 0x00, 0x10, 0x00, // [1,0] 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x1A, 0xFF, 0x00, 0x00, 0x5A, 0xBF, 0x00, 0x00, 0x00, 0x9F, 0x00, 0x00, 0x00, 0x9F, 0x00, 0x00, 0x00, 0x9F, 0x00, 0x00, 0x00, 0x00, 0x00, // [2,0] 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0xDB, 0x40, 0x00, 0x8F, 0x5C, 0xE0, 0x00, 0x01, 0x1D, 0xC0, 0x00, 0x03, 0xED, 0x20, 0x00, 0x2F, 0xD4, 0x30, 0x00, 0xAF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, // [3,0] 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0xDB, 0x20, 0x00, 0x5C, 0x3E, 0xA0, 0x00, 0x00, 0x8F, 0x50, 0x00, 0x00, 0x6D, 0xD0, 0x00, 0x5A, 0x0A, 0xF0, 0x00, 0x3E, 0xEF, 0x60, 0x00, 0x00, 0x10, 0x00, // [4,0] 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x60, 0x00, 0x01, 0xEF, 0x70, 0x00, 0x0D, 0x8F, 0x70, 0x00, 0xAE, 0x6F, 0xA0, 0x00, 0xCD, 0xDF, 0xE1, 0x00, 0x00, 0x0F, 0x70, 0x00, 0x00, 0x00, 0x00, // [5,0] 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0xBB, 0x80, 0x00, 0x3F, 0x87, 0x50, 0x00, 0x6F, 0xCC, 0x40, 0x00, 0x27, 0x3C, 0xE0, 0x00, 0x59, 0x0A, 0xE0, 0x00, 0x3E, 0xEF, 0x60, 0x00, 0x00, 0x10, 0x00, // [6,0] 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xCC, 0x30, 0x00, 0x5F, 0x59, 0x80, 0x00, 0x9F, 0x9C, 0x40, 0x00, 0x9F, 0x4A, 0xE0, 0x00, 0x7F, 0x18, 0xF0, 0x00, 0x0C, 0xEF, 0x70, 0x00, 0x00, 0x10, 0x00, // [7,0] 0x00, 0x00, 0x00, 0x00, 0x00, 0x7B, 0xBB, 0xB0, 0x00, 0x69, 0xAF, 0xC0, 0x00, 0x00, 0x7E, 0x10, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x05, 0xF2, 0x00, 0x00, 0x09, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, // [8,0] 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0xDB, 0x30, 0x00, 0x7F, 0x3D, 0xC0, 0x00, 0x3F, 0x9E, 0x70, 0x00, 0x6F, 0x7D, 0xB0, 0x00, 0x9E, 0x0A, 0xF0, 0x00, 0x3E, 0xEF, 0x70, 0x00, 0x00, 0x10, 0x00, // [9,0] 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0xDA, 0x10, 0x00, 0x9E, 0x3D, 0xB0, 0x00, 0xAD, 0x0B, 0xF0, 0x00, 0x3E, 0xEE, 0xF0, 0x00, 0x27, 0x1C, 0xC0, 0x00, 0x3E, 0xEE, 0x30, 0x00, 0x00, 0x10, 0x00 }; flash unsigned char numbergraphics[320] = { // 0 0x00, 0x1C, 0xFC, 0x10, 0x00, 0x9F, 0x5F, 0x90, 0x00, 0xDF, 0x0F, 0xD0, 0x00, 0xFF, 0x0F, 0xF0, 0x00, 0xFF, 0x0F, 0xF0, 0x00, 0xEF, 0x0F, 0xD0, 0x00, 0x9F, 0x5F, 0x90, 0x00, 0x1C, 0xFC, 0x10, // 1 0x00, 0x00, 0xBF, 0x00, 0x00, 0x2B, 0xFF, 0x00, 0x00, 0xFD, 0xFF, 0x00, 0x00, 0xB1, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, // 2 0x00, 0x4D, 0xFD, 0x30, 0x00, 0xCF, 0x2F, 0xD0, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x7F, 0xB0, 0x00, 0x05, 0xFE, 0x10, 0x00, 0x3F, 0xC1, 0x00, 0x00, 0xBA, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, // 3 0x00, 0x3D, 0xFD, 0x30, 0x00, 0xBF, 0x2F, 0xE0, 0x00, 0x00, 0x2F, 0xC0, 0x00, 0x00, 0xEE, 0x20, 0x00, 0x00, 0x2F, 0xC0, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0xCF, 0x3F, 0xB0, 0x00, 0x3D, 0xFC, 0x20, // 4 0x00, 0x00, 0x6F, 0xF0, 0x00, 0x00, 0xDF, 0xF0, 0x00, 0x08, 0x6F, 0xF0, 0x00, 0x2D, 0x0F, 0xF0, 0x00, 0xB5, 0x0F, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x0F, 0xF0, // 5 0x00, 0x1F, 0xFF, 0xF0, 0x00, 0x4F, 0xA0, 0x00, 0x00, 0x8F, 0x60, 0x00, 0x00, 0xBF, 0xFE, 0x40, 0x00, 0xC6, 0x2F, 0xD0, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0xCF, 0x4F, 0xB0, 0x00, 0x3D, 0xFC, 0x20, // 6 0x00, 0x0A, 0xFE, 0x40, 0x00, 0x8F, 0x3F, 0xC0, 0x00, 0xDE, 0x00, 0x00, 0x00, 0xFE, 0xDE, 0x30, 0x00, 0xFF, 0x3F, 0xD0, 0x00, 0xEF, 0x0F, 0xF0, 0x00, 0x9F, 0x4F, 0xC0, 0x00, 0x0B, 0xFD, 0x20, // 7 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x6F, 0xB0, 0x00, 0x00, 0xEF, 0x20, 0x00, 0x03, 0xFC, 0x00, 0x00, 0x08, 0xF7, 0x00, 0x00, 0x0B, 0xF4, 0x00, 0x00, 0x0E, 0xF1, 0x00, 0x00, 0x0F, 0xF0, 0x00, // 8 0x00, 0x4D, 0xFD, 0x40, 0x00, 0xEF, 0x2F, 0xE0, 0x00, 0xCF, 0x2F, 0xC0, 0x00, 0x1E, 0xFE, 0x10, 0x00, 0xAF, 0x4F, 0xB0, 0x00, 0xFF, 0x0F, 0xF0, 0x00, 0xDF, 0x4F, 0xC0, 0x00, 0x2C, 0xFD, 0x20, // 9 0x00, 0x2D, 0xFB, 0x00, 0x00, 0xCF, 0x4F, 0x90, 0x00, 0xFF, 0x0F, 0xD0, 0x00, 0xDF, 0x3F, 0xF0, 0x00, 0x3E, 0xDE, 0xF0, 0x00, 0x00, 0x0E, 0xD0, 0x00, 0xCF, 0x3F, 0x80, 0x00, 0x5E, 0xFA, 0x00 }; flash unsigned char dashgraphics[32] = { // Dash graphic 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; flash unsigned char welcomegraphics[1024] = { // [0,0] 0x34, 0x44, 0x45, 0x56, 0x37, 0xA7, 0x65, 0x66, 0x33, 0xBF, 0xFD, 0xB9, 0x33, 0x4B, 0xFF, 0xFF, 0x33, 0x55, 0xCF, 0xFF, 0x44, 0x54, 0x6D, 0xFF, 0x45, 0x54, 0x46, 0xDF, 0x55, 0x66, 0x66, 0x7D, // [1,0] 0x66, 0x55, 0x55, 0x55, 0x77, 0x77, 0x77, 0x66, 0x76, 0x55, 0x55, 0x55, 0xFE, 0xCA, 0x87, 0x76, 0xFE, 0xEE, 0xED, 0xB9, 0xA5, 0x56, 0xAD, 0xED, 0xB4, 0x22, 0x36, 0xAD, 0xF8, 0x32, 0x22, 0x48, // [2,0] 0x55, 0x55, 0x54, 0x44, 0x66, 0x66, 0x65, 0x55, 0x45, 0x44, 0x45, 0x77, 0x65, 0x56, 0x78, 0xAC, 0x77, 0x79, 0xBC, 0xDB, 0xDD, 0xCC, 0xCD, 0xC8, 0xDD, 0xCC, 0xCC, 0xB9, 0xCD, 0xCC, 0xCC, 0xBB, // [3,0] 0x45, 0x65, 0x55, 0x55, 0x67, 0x64, 0x44, 0x44, 0x87, 0x66, 0x66, 0x77, 0x86, 0x55, 0x66, 0x55, 0x77, 0x77, 0x77, 0x88, 0x77, 0x65, 0x66, 0x55, 0x76, 0x55, 0x55, 0x55, 0xBA, 0xA9, 0x87, 0x77, // [4,0] 0x55, 0x66, 0x78, 0x88, 0x44, 0x57, 0x77, 0x78, 0x77, 0x77, 0x77, 0x88, 0x67, 0x79, 0x9A, 0x9A, 0x87, 0x87, 0x77, 0x77, 0x55, 0x55, 0x69, 0x98, 0x77, 0x88, 0x85, 0x47, 0x43, 0x43, 0x67, 0x98, // [5,0] 0x99, 0x88, 0x88, 0x88, 0x88, 0x89, 0x99, 0xAA, 0x89, 0xAA, 0xBB, 0xBB, 0xAA, 0x99, 0x87, 0x8A, 0x78, 0xA9, 0x99, 0x88, 0x97, 0x57, 0x99, 0xA9, 0x89, 0x98, 0x79, 0xAA, 0x88, 0x99, 0x88, 0x78, // [6,0] 0x89, 0x99, 0x99, 0xAA, 0xAA, 0xBC, 0xDD, 0xCD, 0xBB, 0xBB, 0xAA, 0xBC, 0x9A, 0xAA, 0xAC, 0xCC, 0xAA, 0xAB, 0xBB, 0xCD, 0xAA, 0xA9, 0xBB, 0xAA, 0x79, 0xBB, 0x9A, 0xA9, 0xBB, 0x97, 0x98, 0xA9, // [7,0] 0xBC, 0xDE, 0xFF, 0xFF, 0xEE, 0xFF, 0xFF, 0xFF, 0xCE, 0xFF, 0xFF, 0xFF, 0xDE, 0xEE, 0xBE, 0xED, 0xCB, 0xCA, 0xBD, 0xDC, 0xBA, 0x89, 0xBC, 0xBB, 0xA8, 0x89, 0xAB, 0x99, 0x78, 0x69, 0x99, 0x88, // [0,1] 0x55, 0x56, 0x55, 0x45, 0x55, 0x55, 0x44, 0x43, 0x55, 0x54, 0x44, 0x33, 0x55, 0x55, 0x44, 0x43, 0x55, 0x55, 0x65, 0x44, 0x56, 0x65, 0x54, 0x33, 0x55, 0x55, 0x44, 0x44, 0x55, 0x44, 0x54, 0x43, // [1,1] 0xDE, 0x84, 0x22, 0x23, 0x5D, 0xEA, 0x63, 0x22, 0x35, 0xDE, 0xDA, 0x65, 0x34, 0x6D, 0xDD, 0xCB, 0x33, 0x36, 0xDD, 0xDD, 0x34, 0x34, 0x9D, 0xCC, 0x34, 0x35, 0x8C, 0xCC, 0x33, 0x44, 0x7C, 0xCC, // [2,1] 0x7C, 0xDC, 0xCC, 0xCC, 0x38, 0xDA, 0x8B, 0xDD, 0x46, 0xCD, 0xB8, 0x8B, 0xBC, 0xDD, 0xDC, 0xA7, 0xCB, 0xDD, 0xDD, 0xDB, 0xC9, 0x9B, 0xDD, 0xCA, 0xCD, 0xB8, 0x9B, 0xBA, 0xCC, 0xDC, 0x97, 0x89, // [3,1] 0xBA, 0xAA, 0x53, 0x44, 0xDB, 0xA8, 0x66, 0x78, 0xCA, 0xA8, 0x66, 0x67, 0x89, 0x75, 0x77, 0x86, 0x76, 0x52, 0x56, 0x54, 0xA6, 0x33, 0x33, 0x66, 0x94, 0x23, 0x46, 0x66, 0x55, 0x65, 0x56, 0x76, // [4,1] 0x67, 0x88, 0x68, 0x89, 0x76, 0x77, 0x98, 0x87, 0x78, 0x77, 0x56, 0x78, 0x76, 0x55, 0x66, 0x88, 0x46, 0x66, 0x78, 0x99, 0x66, 0x77, 0x88, 0x88, 0x76, 0x88, 0x78, 0x76, 0x88, 0x86, 0x66, 0x67, // [5,1] 0xA9, 0x75, 0x77, 0xBA, 0x77, 0x78, 0x9A, 0x88, 0x98, 0x99, 0x77, 0x77, 0x99, 0x78, 0x66, 0x88, 0x88, 0x66, 0x77, 0x67, 0x75, 0x67, 0x66, 0x88, 0x77, 0x74, 0x47, 0x76, 0x65, 0x45, 0x76, 0x67, // [6,1] 0x97, 0x68, 0x68, 0x76, 0x57, 0x76, 0x86, 0x55, 0x86, 0x75, 0x76, 0x48, 0x57, 0x67, 0x54, 0x75, 0x87, 0x75, 0x46, 0x65, 0x57, 0x54, 0x76, 0x58, 0x75, 0x46, 0x85, 0x66, 0x65, 0x58, 0x65, 0x86, // [7,1] 0x77, 0x78, 0x87, 0x88, 0x87, 0x78, 0x87, 0x87, 0x57, 0x78, 0x85, 0x86, 0x76, 0x88, 0x75, 0x77, 0x87, 0x79, 0x66, 0x77, 0x68, 0x88, 0x66, 0x66, 0x78, 0x88, 0x66, 0x66, 0x76, 0x87, 0x66, 0x66, // [0,2] 0x55, 0x54, 0x44, 0x34, 0x55, 0x55, 0x55, 0x43, 0x55, 0x55, 0x44, 0x44, 0x55, 0x54, 0x45, 0x43, 0x55, 0x54, 0x44, 0x44, 0x55, 0x54, 0x44, 0x44, 0x55, 0x55, 0x44, 0x44, 0x54, 0x55, 0x44, 0x44, // [1,2] 0x44, 0x33, 0x7B, 0xDC, 0x33, 0x45, 0x7B, 0xDC, 0x44, 0x34, 0x7A, 0xDA, 0x34, 0x45, 0x69, 0xC8, 0x54, 0x33, 0x59, 0x97, 0x44, 0x34, 0x68, 0x75, 0x34, 0x54, 0x47, 0x65, 0x45, 0x44, 0x57, 0x56, // [2,2] 0xCB, 0xCD, 0xDA, 0x76, 0xAB, 0xBC, 0xCA, 0x95, 0x77, 0xBA, 0xAA, 0x84, 0x66, 0x8A, 0xA9, 0x76, 0x55, 0x58, 0xA7, 0x67, 0x65, 0x64, 0x87, 0x66, 0x66, 0x76, 0x55, 0x45, 0x77, 0x55, 0x43, 0x54, // [3,2] 0x43, 0x56, 0x65, 0x77, 0x25, 0x65, 0x77, 0x66, 0x46, 0x77, 0x77, 0x66, 0x67, 0x66, 0x56, 0x55, 0x66, 0x46, 0x66, 0x56, 0x55, 0x65, 0x56, 0x77, 0x54, 0x55, 0x67, 0x86, 0x55, 0x56, 0x66, 0x66, // [4,2] 0x76, 0x55, 0x45, 0x67, 0x56, 0x45, 0x56, 0x64, 0x65, 0x56, 0x64, 0x44, 0x56, 0x64, 0x33, 0x35, 0x87, 0x43, 0x24, 0x45, 0x75, 0x43, 0x43, 0x54, 0x65, 0x44, 0xF4, 0x44, 0x66, 0x55, 0xF4, 0x44, // [5,2] 0x44, 0x56, 0x65, 0x65, 0x35, 0x66, 0x67, 0x56, 0x56, 0x57, 0x65, 0x65, 0x55, 0x66, 0x56, 0x57, 0x55, 0x55, 0x65, 0x66, 0x45, 0x45, 0x55, 0x76, 0x44, 0x54, 0x57, 0x55, 0x24, 0x33, 0x64, 0x54, // [6,2] 0x65, 0x86, 0x57, 0x65, 0x57, 0x75, 0x57, 0x66, 0x77, 0x65, 0x75, 0x57, 0x66, 0x56, 0x55, 0x56, 0x65, 0x57, 0x54, 0x64, 0x55, 0x74, 0x55, 0x64, 0x56, 0x65, 0x47, 0x44, 0x46, 0x45, 0x56, 0x34, // [7,2] 0x86, 0x75, 0x66, 0x66, 0x77, 0x76, 0x57, 0x56, 0x57, 0x65, 0x57, 0x56, 0x46, 0x45, 0x47, 0x56, 0x45, 0x44, 0x56, 0x56, 0x44, 0x34, 0x55, 0x56, 0x53, 0x43, 0x55, 0x56, 0x43, 0x43, 0x54, 0x45, // [0,3] 0x55, 0x55, 0x54, 0x44, 0x55, 0x54, 0x44, 0x44, 0x44, 0x43, 0x43, 0x44, 0x33, 0x33, 0x33, 0x44, 0x33, 0x33, 0x33, 0x45, 0x33, 0x33, 0x43, 0x34, 0x33, 0x44, 0x43, 0x33, 0x34, 0x34, 0x33, 0x32, // [1,3] 0x55, 0x55, 0x55, 0x77, 0x55, 0x55, 0x77, 0x55, 0x55, 0x67, 0x65, 0x44, 0x66, 0x55, 0x44, 0x55, 0x45, 0x44, 0x45, 0x45, 0x33, 0x44, 0x44, 0x55, 0x33, 0x34, 0x65, 0x44, 0x33, 0x45, 0x55, 0x45, // [2,3] 0x56, 0x53, 0x45, 0x45, 0x64, 0x55, 0x34, 0x35, 0x56, 0x44, 0x44, 0x55, 0x56, 0x54, 0x44, 0x45, 0x65, 0x55, 0x44, 0x44, 0x46, 0x65, 0x44, 0x44, 0x55, 0x55, 0x54, 0x44, 0x44, 0x55, 0x55, 0x44, // [3,3] 0x45, 0x55, 0x55, 0xFD, 0x55, 0x54, 0x45, 0xFA, 0x55, 0x44, 0x44, 0xF5, 0x44, 0x44, 0x45, 0xFA, 0x44, 0x44, 0x56, 0xFC, 0x44, 0x45, 0x55, 0xF5, 0x44, 0x55, 0x56, 0xF5, 0x44, 0x55, 0x55, 0x55, // [4,3] 0xFE, 0x94, 0xF4, 0x3B, 0x5A, 0xE5, 0xF3, 0x34, 0x66, 0xF5, 0xF4, 0x47, 0x59, 0xE4, 0xF5, 0x4F, 0xFE, 0x64, 0xF4, 0x49, 0x44, 0x45, 0x44, 0x55, 0x44, 0x54, 0x44, 0x45, 0x44, 0x44, 0x45, 0x55, // [5,3] 0xFF, 0x85, 0xD6, 0x36, 0x34, 0xF3, 0x9B, 0x3B, 0xBE, 0xF3, 0x5F, 0x5F, 0x78, 0xF3, 0x3C, 0xEC, 0xFC, 0xF3, 0x38, 0xF8, 0x43, 0x33, 0x39, 0xF5, 0x44, 0x33, 0xFF, 0x94, 0x54, 0x43, 0x34, 0x34, // [6,3] 0xE7, 0xFF, 0x85, 0xFD, 0xAE, 0x66, 0xE3, 0xF6, 0x5F, 0xFF, 0xF3, 0xF4, 0x3E, 0x64, 0x33, 0xF4, 0x47, 0xFF, 0xA3, 0xF3, 0x34, 0x43, 0x33, 0x33, 0x34, 0x43, 0x33, 0x44, 0x33, 0x33, 0x33, 0x45, // [7,3] 0xD3, 0x9F, 0xFB, 0x45, 0x43, 0xF8, 0x64, 0x45, 0x33, 0x9E, 0xF9, 0x45, 0x34, 0x45, 0x7F, 0x45, 0x44, 0xBF, 0xE9, 0x44, 0x44, 0x55, 0x44, 0x34, 0x54, 0x45, 0x44, 0x34, 0x55, 0x45, 0x44, 0x34 }; flash unsigned char scoreboardgraphics[1024] = { // [0,0] 0x22, 0x33, 0x34, 0x55, 0x23, 0x33, 0x44, 0x66, 0x23, 0x34, 0x56, 0x67, 0x23, 0x34, 0x46, 0x78, 0x23, 0x34, 0x56, 0x78, 0x33, 0x45, 0x66, 0x79, 0x33, 0x45, 0x67, 0x89, 0x33, 0x45, 0x67, 0x89, // [1,0] 0x67, 0x78, 0x89, 0x99, 0x77, 0x89, 0x9A, 0xAA, 0x89, 0x99, 0xAB, 0xBC, 0x89, 0xAA, 0xBC, 0xCD, 0x9A, 0xBC, 0xCD, 0xDE, 0xAB, 0xCC, 0xDE, 0xEF, 0xAB, 0xCD, 0xEF, 0xFF, 0xAB, 0xCD, 0xFF, 0xFF, // [2,0] 0x99, 0x99, 0x88, 0x76, 0xBB, 0xAA, 0x99, 0x87, 0xCC, 0xBB, 0xA9, 0x99, 0xDD, 0xCC, 0xBA, 0xA9, 0xEE, 0xDD, 0xCC, 0xB9, 0xFF, 0xEE, 0xDC, 0xBB, 0xFF, 0xFE, 0xEE, 0xDC, 0xFF, 0xFF, 0xFF, 0xFF, // [3,0] 0x65, 0x54, 0x43, 0x33, 0x76, 0x54, 0x43, 0x43, 0x86, 0x55, 0x55, 0x44, 0x87, 0x76, 0x66, 0x54, 0x99, 0x98, 0x66, 0x55, 0xBA, 0x98, 0x88, 0x88, 0xCC, 0xCC, 0xBA, 0xA9, 0xED, 0xDC, 0xBB, 0xAA, // [4,0] 0x32, 0x22, 0x22, 0x22, 0x33, 0x33, 0x32, 0x22, 0x43, 0x33, 0x33, 0x33, 0x43, 0x34, 0x33, 0x44, 0x55, 0x55, 0x66, 0x66, 0x88, 0x87, 0x76, 0x66, 0x87, 0x65, 0x55, 0x54, 0x99, 0x99, 0x98, 0x88, // [5,0] 0x22, 0x22, 0x22, 0x22, 0x32, 0x22, 0x22, 0x22, 0x23, 0x33, 0x33, 0x44, 0x45, 0x55, 0x55, 0x55, 0x65, 0x55, 0x44, 0x44, 0x55, 0x44, 0x33, 0x22, 0x55, 0x55, 0x55, 0x55, 0x77, 0x77, 0x65, 0x55, // [6,0] 0x22, 0x21, 0x11, 0x11, 0x22, 0x22, 0x22, 0x33, 0x44, 0x44, 0x34, 0x33, 0x44, 0x44, 0x32, 0x22, 0x43, 0x32, 0x22, 0x11, 0x22, 0x33, 0x33, 0x33, 0x55, 0x55, 0x44, 0x32, 0x54, 0x44, 0x43, 0x22, // [7,0] 0x12, 0x22, 0x22, 0x21, 0x32, 0x22, 0x33, 0x22, 0x33, 0x32, 0x22, 0x11, 0x22, 0x21, 0x11, 0x11, 0x22, 0x22, 0x22, 0x52, 0x33, 0x22, 0x28, 0xB1, 0x22, 0x22, 0x8E, 0x91, 0x22, 0x28, 0xEE, 0x61, // [0,1] 0x33, 0x45, 0x67, 0x89, 0x33, 0x45, 0x56, 0x79, 0x23, 0x45, 0x56, 0x89, 0x23, 0x45, 0x66, 0x78, 0x23, 0x44, 0x56, 0x78, 0x23, 0x44, 0x56, 0x67, 0x23, 0x44, 0x55, 0x67, 0x23, 0x34, 0x55, 0x66, // [1,1] 0xAB, 0xCD, 0xFF, 0xFF, 0xAB, 0xCD, 0xEF, 0xFF, 0xAB, 0xCD, 0xEF, 0xFF, 0x9A, 0xCC, 0xDE, 0xFF, 0x9A, 0xBC, 0xDE, 0xFF, 0x89, 0xBC, 0xDD, 0xEE, 0x89, 0xAB, 0xCC, 0xDD, 0x89, 0x9A, 0xBC, 0xCB, // [2,1] 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xEE, 0xEE, 0xEE, 0xED, 0xDC, 0xCD, 0xDD, 0xDC, 0xDC, 0xBB, 0xCC, 0xCB, 0xBB, 0xA9, // [3,1] 0xEE, 0xDC, 0xCB, 0xA9, 0xFE, 0xDC, 0xBA, 0x99, 0xFE, 0xDC, 0xBB, 0xAA, 0xED, 0xDC, 0xCB, 0xB9, 0xEE, 0xCB, 0xBA, 0xAA, 0xCC, 0xDC, 0xAA, 0x99, 0xBB, 0xAA, 0xBB, 0x99, 0x9A, 0x99, 0x99, 0xAA, // [4,1] 0x99, 0x87, 0x66, 0x65, 0x87, 0x66, 0x66, 0x65, 0x99, 0x88, 0x76, 0x67, 0x87, 0x77, 0x87, 0x76, 0xAA, 0x97, 0x76, 0x55, 0x98, 0x88, 0x78, 0x87, 0x88, 0x77, 0x77, 0x66, 0x88, 0x76, 0x66, 0x66, // [5,1] 0x44, 0x44, 0x44, 0x43, 0x65, 0x55, 0x55, 0x54, 0x66, 0x55, 0x44, 0x43, 0x66, 0x54, 0x55, 0x55, 0x55, 0x66, 0x66, 0x55, 0x65, 0x44, 0x44, 0x45, 0x67, 0x76, 0x65, 0x43, 0x66, 0x66, 0x77, 0x77, // [6,1] 0x33, 0x33, 0x32, 0x22, 0x44, 0x44, 0x43, 0x33, 0x34, 0x44, 0x33, 0x23, 0x44, 0x43, 0x33, 0x4A, 0x44, 0x33, 0x34, 0xAC, 0x44, 0x55, 0x6B, 0xC9, 0x44, 0x46, 0xBC, 0xA5, 0x78, 0x9B, 0xCB, 0x74, // [7,1] 0x23, 0x9E, 0xED, 0x31, 0x39, 0xEE, 0xEB, 0x22, 0xAD, 0xDE, 0xE9, 0x21, 0xDB, 0x9C, 0xD6, 0x22, 0x95, 0x5B, 0xC4, 0x21, 0x43, 0x5B, 0xB2, 0x21, 0x33, 0x6C, 0x92, 0x21, 0x34, 0x8C, 0x62, 0x11, // [0,2] 0x27, 0xEE, 0x54, 0x66, 0x2D, 0xFF, 0x55, 0x55, 0x3F, 0xFF, 0x44, 0x55, 0x6F, 0xFD, 0x34, 0x45, 0x9F, 0xFA, 0x34, 0x36, 0xCF, 0xF8, 0x33, 0x46, 0xFF, 0xF5, 0x34, 0x48, 0x6B, 0xFF, 0xFF, 0xFF, // [1,2] 0x8E, 0xFB, 0xAB, 0xBE, 0xAF, 0xFC, 0x99, 0xAF, 0xCF, 0xFA, 0x89, 0x88, 0xEF, 0xF8, 0x77, 0x77, 0xFF, 0xF7, 0x66, 0x66, 0xFF, 0xD6, 0x65, 0x65, 0xFF, 0xB5, 0x55, 0x55, 0xEA, 0x55, 0x44, 0x54, // [2,2] 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFE, 0x99, 0x98, 0x9F, 0xFD, 0x88, 0x87, 0xAF, 0xFB, 0x67, 0x67, 0xBF, 0xF9, 0x66, 0x66, 0xCF, 0xF7, 0x55, 0x55, 0xFF, 0xF4, 0x44, 0x44, 0xCE, 0x94, // [3,2] 0x88, 0x88, 0x8B, 0xFF, 0x77, 0x77, 0xCE, 0xFF, 0x76, 0x59, 0xFF, 0xB6, 0x76, 0x6A, 0xFF, 0x94, 0x46, 0x5C, 0xFF, 0x65, 0x54, 0x5E, 0xFF, 0x53, 0x53, 0x5F, 0xFE, 0x34, 0x44, 0x38, 0xCF, 0xFF, // [4,2] 0xFF, 0xFF, 0xE6, 0x66, 0xFF, 0xFF, 0xC7, 0x65, 0x65, 0x66, 0x67, 0x66, 0x56, 0x65, 0x66, 0x56, 0x44, 0x46, 0x64, 0x65, 0x43, 0x43, 0x45, 0x44, 0x34, 0x33, 0x44, 0x54, 0xFF, 0xF7, 0x33, 0x34, // [5,2] 0x77, 0x88, 0x99, 0xAB, 0x56, 0x79, 0xBB, 0xBB, 0x65, 0x56, 0x8A, 0xBB, 0x65, 0x65, 0x57, 0x9B, 0x56, 0x54, 0x58, 0xAA, 0x55, 0x56, 0x7A, 0xAB, 0x44, 0x58, 0xAA, 0xBB, 0x44, 0x69, 0x9A, 0xBB, // [6,2] 0xBB, 0xBC, 0xCA, 0x53, 0xBB, 0xBB, 0xB7, 0x43, 0xBB, 0xBB, 0xB6, 0x34, 0xBB, 0xBB, 0xA5, 0x46, 0xBB, 0x9A, 0xB7, 0x8B, 0xBA, 0x9B, 0xBB, 0xBB, 0xB8, 0xAB, 0xBB, 0xAA, 0x99, 0xBB, 0xBB, 0x9B, // [7,2] 0x36, 0xBC, 0x42, 0x22, 0x49, 0xCB, 0x32, 0x22, 0x7B, 0xC9, 0x22, 0x21, 0xBC, 0xC7, 0x22, 0x21, 0xBB, 0xB7, 0x21, 0x22, 0xBB, 0xB8, 0x42, 0x21, 0xBB, 0xB9, 0x62, 0x22, 0xBB, 0xBB, 0x73, 0x11, // [0,3] 0x26, 0xFF, 0xFF, 0xFF, 0x22, 0x23, 0x32, 0x3D, 0x22, 0x33, 0x23, 0x3F, 0x12, 0x32, 0x33, 0x5F, 0x12, 0x22, 0x22, 0x7F, 0x22, 0x22, 0x22, 0xAF, 0x11, 0x22, 0x22, 0xCF, 0x12, 0x22, 0x22, 0xAE, // [1,3] 0xC8, 0x44, 0x44, 0x43, 0xFF, 0x43, 0x33, 0x33, 0xFE, 0x33, 0x33, 0x33, 0xFC, 0x32, 0x33, 0x22, 0xFA, 0x22, 0x22, 0x22, 0xF7, 0x23, 0x22, 0x22, 0xF4, 0x23, 0x22, 0x22, 0xB2, 0x22, 0x22, 0x22, // [2,3] 0x44, 0x44, 0xCC, 0x53, 0x43, 0x38, 0xFF, 0x93, 0x33, 0x3B, 0xFF, 0x73, 0x33, 0x2D, 0xFF, 0x43, 0x33, 0x2F, 0xFF, 0x22, 0x22, 0x4F, 0xFD, 0x22, 0x22, 0x6F, 0xFA, 0x22, 0x22, 0x5E, 0xE4, 0x22, // [3,3] 0x34, 0x38, 0xDF, 0xFF, 0x33, 0xAF, 0xF7, 0x33, 0x32, 0xBF, 0xF5, 0x33, 0x22, 0xCF, 0xF2, 0x33, 0x22, 0xEF, 0xD2, 0x22, 0x22, 0xFF, 0xA2, 0x22, 0x22, 0x9D, 0xFF, 0xFF, 0x22, 0x4F, 0xFF, 0xFF, // [4,3] 0xFF, 0xFB, 0x63, 0x32, 0x35, 0xFF, 0xF3, 0x33, 0x28, 0xFF, 0xD2, 0x33, 0x2A, 0xFF, 0xA3, 0x23, 0x2C, 0xFF, 0x72, 0x32, 0x2E, 0xFF, 0x52, 0x22, 0xFF, 0xC7, 0x22, 0x22, 0xFE, 0x52, 0x22, 0x22, // [5,3] 0x33, 0x46, 0x99, 0xA9, 0x32, 0x33, 0x46, 0x97, 0x23, 0x22, 0x33, 0x67, 0x22, 0x32, 0x23, 0x44, 0x32, 0x22, 0x22, 0x33, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x11, // [6,3] 0x8A, 0xBB, 0xB8, 0xAB, 0x8A, 0xBB, 0x98, 0xBB, 0x99, 0xAA, 0x8A, 0xBB, 0x58, 0x98, 0x8B, 0xBA, 0x23, 0x67, 0x9A, 0xAA, 0x23, 0x45, 0x69, 0x99, 0x22, 0x33, 0x23, 0x69, 0x21, 0x22, 0x21, 0x23, // [7,3] 0xBB, 0xBB, 0x95, 0x11, 0xAA, 0xBB, 0xA7, 0x21, 0xA9, 0x89, 0xB8, 0x41, 0xA6, 0x36, 0x88, 0x62, 0xA5, 0x33, 0x47, 0x73, 0x93, 0x22, 0x22, 0x45, 0x72, 0x22, 0x22, 0x22, 0x41, 0x22, 0x11, 0x22 }; flash unsigned char winnergraphics[256] = { // [0,0] 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFA, 0x0F, 0x00, 0x0F, 0x1F, 0x0F, 0x00, 0x0F, 0xFA, 0x0F, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x00, 0x00, // [1,0] 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xF4, 0x0A, 0x50, 0x0A, 0x9A, 0x00, 0xD3, 0x1F, 0xFF, 0x10, 0x4F, 0x68, 0x08, 0x60, 0x0F, 0xC2, 0x02, 0xC0, 0x0F, 0x00, 0x00, 0x00, 0x00, // [2,0] 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0x0F, 0xFF, 0xF0, 0xD0, 0x0F, 0x00, 0x00, 0x40, 0x0F, 0xFF, 0xF0, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, // [3,0] 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0xF0, 0xF0, 0x00, 0x00, 0xFF, 0xB0, 0x00, 0x00, 0xF0, 0xC1, 0x00, 0x00, 0xF0, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // [4,0] 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // [5,0] 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x6F, 0x60, 0x00, 0xD2, 0xB7, 0xB2, 0x00, 0xB4, 0xE0, 0xE4, 0x00, 0x9A, 0xA0, 0xAA, 0x00, 0x7F, 0x60, 0x6F, 0x00, 0x00, 0x00, 0x00, // [6,0] 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xF0, 0xF7, 0xF0, 0xD0, 0xF0, 0xFF, 0xF0, 0xB0, 0xF0, 0xFF, 0xF0, 0x90, 0xF0, 0xFF, 0xF0, 0x70, 0xF0, 0xF7, 0xF0, 0x00, 0x00, 0x00, 0x00, // [7,0] 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xFD, 0x40, 0xF0, 0xE2, 0x04, 0xE0, 0xF0, 0x27, 0x78, 0x40, 0xF0, 0xE4, 0x03, 0xF0, 0x00, 0x3D, 0xFE, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00 }; // A precalculated table giving us the X acceleration based on orientation, starting at 0 degrees flash char accelerationX[16] = {0,24,45,59,64,59,45,24,0,-24,-45,-59,-64,-59,-45,-24}; // A precalculated table giving us the Y acceleration based on orientation, starting at 0 degrees flash char accelerationY[16] = {-64,-59,-45,-24,0,24,45,59,64,59,45,24,0,-24,-45,-59}; // Buffer that is written to on the "off" line time that will be read in the "on" line time unsigned char linebuffer[64]; //16 horizontally * 12 vertically bins = 192 bytes (seperates the screen into 8x8 "bins") char binptrs[192]; // Each grid area on the screen points to a storagebin where the actual graphic is held. // More than one grid area can point to the same graphic... that's the trick that allows // us to do gray-scale graphics when really there shouldn't be enough memory to do it. // 32 bytes per bin, game can actually use a maximum of 44 bins unsigned char binstorage[1408]; // The index into the storage bin, acts as a stack that is filled up every frame with images unsigned char storageStack; // The input vectors from each nintendo gamepad (active-low) unsigned char button_input[4]; // Information about the ships unsigned char shipX[4]; // X coord unsigned char shipY[4]; // y coord unsigned char shipROT[4]; // orientation char shipXfrac[4]; // determines how close the ship is to moving to the next pixel char shipYfrac[4]; // determines how close the ship is to moving to the next pixel int shipXvel[4]; // velocity X-component int shipYvel[4]; // velocity Y-component char shipDead[4]; // indicates whether the ship has died or not char shipExploding[4]; // indicates whether the ship is exploding or not (it gives the exploding frame if it is) char shipScore[4]; // the score of the ship char shipReady[4]; // Indicates that this player is ready to play unsigned char shipBulletX[4]; // X Coord: Each ship gets 3 bullets unsigned char shipBulletY[4]; // Y Coord char shipBulletXfrac[4]; // determines how close the bullet is to moving to the next pixel char shipBulletYfrac[4]; // determines how close the bullet is to moving to the next pixel int shipBulletXvel[4]; // X velocity int shipBulletYvel[4]; // Y velocity char shipBulletLifeline[4]; // Number of frames the bullet has left to live char shipBulletCount[4]; // Number of active bullets from this ship char shipBulletDelay[4]; // Time before next bullet can be fired // These pointers are used to point to storage locations that will need to be overlayed into // ----------- The box in the middle is the image we are overlaying onto the 4 "bins" (marked with $) // | NW | NE | // | ----- | The four quadrants around the image are labeled, and it was each of the ptrNE, ptrNW, etc // | |%%%| | are meant to point to. // |--+%%%+--| // | |%%%| | // | ----- | // | SW | SE | // ----------- unsigned char *ptrNE; unsigned char *ptrNW; unsigned char *ptrSE; unsigned char *ptrSW; char gameState; char numPlayers; // Used for TV signal production char syncON, syncOFF; int LineCount; // Various software timers char t, tRotate, tSelect, tExplode; //================================== //This is the sync generator and raster generator. It MUST be entered from //sleep mode to get accurate timing of the sync pulses #pragma warn- interrupt [TIM1_COMPA] void t1_cmpA(void) { //start the Horizontal sync pulse PORTD = syncON; //count timer 0 at 1/usec TCNT0=0; //update the curent scanline number LineCount ++ ; //begin inverted (Vertical) synch after line 247 if (LineCount==248) { syncON = 0b00000001; syncOFF = 0; } //back to regular sync after line 250 if (LineCount==251) { syncON = 0; syncOFF = 0b00000001; } //start new frame after line 262 if (LineCount==263) { LineCount = 1; } delay_us(2); //adjust to make 5 us pulses //end sync pulse PORTD = syncOFF; if (LineCount=ScreenTop) { // Only draw every other line, the other line is used for filling up the linebuffer // This time is needed because of the complexity of this operation if (LineCount & 1) { //compute byte index for beginning of the next line //left-shift 4 would be individual lines // <<3 means // line-double the pixels //The 0xfff8 truncates the odd line bit //i=(LineCount-ScreenTop)<<3 & 0xfff8; // // note: i'm not sure if this is actually needed anymore #asm push r16 lds r12, _LineCount lds r13, _Linecount+1 ldi r16, 30 sub r12, r16 ldi r16,0 sbc r13, r16 lsl r12 rol r13 lsl r12 rol r13 lsl r12 rol r13 mov r16,r12 andi r16,0xf0 mov r12,r16 pop r16 #endasm // Grab the pointer to the linebuffer, which holds the image data we need to spit out in this line #asm push r26 push r27 ldi r26,low(_linebuffer) ;base address of linebuffer ldi r27,high(_linebuffer) #endasm delay_us(8); //adjust to center image on screen #asm ; This macro has been changed from the original to accomodate our needs ; Basically, it reads pixels from the linebuffer and pumps them out to the screen ; Remember that each nibble corresponds to a pixel.. Also it is assumed that x ; points to the linebuffer .macro videobits LD R30,x SBR R30,0x01 nop OUT 0x12,R30 LD R30,x+ SWAP R30 SBR R30,0x01 OUT 0x12,R30 LD R30,x SBR R30,0x01 nop OUT 0x12,R30 LD R30,x+ SWAP R30 SBR R30,0x01 OUT 0x12,R30 LD R30,x SBR R30,0x01 nop OUT 0x12,R30 LD R30,x+ SWAP R30 SBR R30,0x01 OUT 0x12,R30 LD R30,x SBR R30,0x01 nop OUT 0x12,R30 LD R30,x+ SWAP R30 SBR R30,0x01 OUT 0x12,R30 .endm ; Draw hardcoded border around screen LDI R30,0x31 OUT 0x12,R30 ; Start pumping out the pixels videobits ;video line -- byte 1 videobits ;byte 2 videobits ;byte 3 videobits ;byte 4 videobits ;byte 5 videobits ;byte 6 videobits ;byte 7 videobits ;byte 8 videobits ;byte 9 videobits ;byte 10 videobits ;byte 11 videobits ;byte 12 videobits ;byte 13 videobits ;byte 14 videobits ;byte 15 videobits ;byte 16 nop nop nop ; Draw hardcoded border around the screen LDI R30,0x31 OUT 0x12,R30 ; Make sure all output is at black level after line is drawn CLR R30 SBR R30,0x01 nop nop OUT 0x12,R30 pop r27 pop r26 #endasm } // if linecount&1 else { // Here we decompress our image information from our bin system into a linebuffer // that the next drawn line will read from #asm ; This macro decompresses one line from a bin to the linebuffer... ; Note that there are 16 bins that make up the width of the screen .macro bufferline LD r4,z+ ; get the ptr for this bin, r4 is now an index into binstorage ; this index needs to be multiplied by 2^5 to be useful CLR r5 LSL r4 ROL r5 LSL r4 ROL r5 LSL r4 ROL r5 LSL r4 ROL r5 LSL r4 ROL r5 ; y (28/29) will become the pointer to this storage data MOV r28,r18 MOV r29,r19 ; add the index offset ADD r28,r4 ADC r29,r5 ; add the line offset ADD r28,r16 ADC r29,r17 ; Take the storage data and put it into the linebuffer LD r4,y LDD r5,y+1 LDD r6,y+2 LDD r7,y+3 ST x+,r4 ST x+,r5 ST x+,r6 ST x+,r7 .endm ; Save registers that we are going to be using push r17 push r16 push r18 push r19 push r20 push r21 push r26 push r27 push r28 push r29 ; precomputations lds r12, _LineCount lds r13, _Linecount+1 ldi r16, 30 sub r12,r16 ldi r16,0 sbc r13,r16 ; set i = Linecount - 30 mov r20,r12 andi r20,0xf0 clr r21 lsl r12 rol r13 ; i = line * 4 now mov r16,r12 ; save line * 4 in register 16 andi r16,0x1f ; we only want line * 4 mod 32 ldi r17,0 ; z will point to binptr offset where we read which bin to grab (binptr + line * 16) ldi r30,low(_binptrs) ;base address of linebuffer ldi r31,high(_binptrs) add r30,r20 adc r31,r21 ; (18/19) will point to the binstorage base address ldi r18,low(_binstorage) ;base address of graphics ldi r19,high(_binstorage) ; x will point to the linebuffer, which we are writing to ldi r26,low(_linebuffer) ;base address of linebuffer ldi r27,high(_linebuffer) ;fill up 64 bytes of the linebuffer bufferline bufferline bufferline bufferline bufferline bufferline bufferline bufferline bufferline bufferline bufferline bufferline bufferline bufferline bufferline bufferline ; Clean up after ourselves pop r29 pop r28 pop r27 pop r26 pop r21 pop r20 pop r19 pop r18 pop r16 pop r17 #endasm } // else linecount&1 } // if in screen bounds else if ((LineCount == (ScreenTop - 1)) || (LineCount == ScreenBot)) { // This code is just making a hardcoded border around the raster delay_us(9); //adjust to center image on screen #asm nop nop nop nop nop nop ; 0xf1 would produce a very bright line.. we use 0x71 to output a dimmer line LDI R30,0x31 OUT 0x12,R30 #endasm delay_us(40); #asm nop nop nop nop nop nop nop CLR R30 SBR R30,0x01 OUT 0x12,R30 #endasm } } // interrupt #pragma warn+ /** * This function reads input from the nintendo controllers attached to PORTA * The protocol is too complicated to explain here, but documentation on it can * be found on the internet (which was how we figured out how to do it). It supports * 4 controllers, and uses some defines that are defined at the top of this file **/ void readNintendo(void) { // Set latch PORTA = PORTA | NIN_LATCH; // 12 us (Read button val A) #asm ("buttondelay"); #asm ("buttondelay"); button_input[0] = (PINA & NIN_DATA_P1); button_input[1] = ((PINA & NIN_DATA_P2) >> 1); button_input[2] = ((PINA & NIN_DATA_P3) >> 2); button_input[3] = ((PINA & NIN_DATA_P4) >> 3); // Drop latch PORTA = PORTA & ~(NIN_LATCH); // 6 us #asm ("buttondelay"); // Set Pulse PORTA = PORTA | NIN_PULSE; // 6 us (Read button val B) #asm ("buttondelay"); button_input[0] = (button_input[0] << 1) | (PINA & NIN_DATA_P1); button_input[1] = (button_input[1] << 1) | ((PINA & NIN_DATA_P2) == NIN_DATA_P2); button_input[2] = (button_input[2] << 1) | ((PINA & NIN_DATA_P3) == NIN_DATA_P3); button_input[3] = (button_input[3] << 1) | ((PINA & NIN_DATA_P4) == NIN_DATA_P4); // Drop Pulse PORTA = PORTA & ~(NIN_PULSE); // 6 us #asm ("buttondelay"); // Set Pulse PORTA = PORTA | NIN_PULSE; // 6 us (Read button val select) #asm ("buttondelay"); button_input[0] = (button_input[0] << 1) | (PINA & NIN_DATA_P1); button_input[1] = (button_input[1] << 1) | ((PINA & NIN_DATA_P2) == NIN_DATA_P2); button_input[2] = (button_input[2] << 1) | ((PINA & NIN_DATA_P3) == NIN_DATA_P3); button_input[3] = (button_input[3] << 1) | ((PINA & NIN_DATA_P4) == NIN_DATA_P4); // Drop Pulse PORTA = PORTA & ~(NIN_PULSE); // 6 us #asm ("buttondelay"); // Set Pulse PORTA = PORTA | NIN_PULSE; // 6 us (Read button val start) #asm ("buttondelay"); button_input[0] = (button_input[0] << 1) | (PINA & NIN_DATA_P1); button_input[1] = (button_input[1] << 1) | ((PINA & NIN_DATA_P2) == NIN_DATA_P2); button_input[2] = (button_input[2] << 1) | ((PINA & NIN_DATA_P3) == NIN_DATA_P3); button_input[3] = (button_input[3] << 1) | ((PINA & NIN_DATA_P4) == NIN_DATA_P4); // Drop Pulse PORTA = PORTA & ~(NIN_PULSE); // 6 us #asm ("buttondelay"); // Set Pulse PORTA = PORTA | NIN_PULSE; // 6 us (Read button val up) #asm ("buttondelay"); button_input[0] = (button_input[0] << 1) | (PINA & NIN_DATA_P1); button_input[1] = (button_input[1] << 1) | ((PINA & NIN_DATA_P2) == NIN_DATA_P2); button_input[2] = (button_input[2] << 1) | ((PINA & NIN_DATA_P3) == NIN_DATA_P3); button_input[3] = (button_input[3] << 1) | ((PINA & NIN_DATA_P4) == NIN_DATA_P4); // Drop Pulse PORTA = PORTA & ~(NIN_PULSE); // 6 us #asm ("buttondelay"); // Set Pulse PORTA = PORTA | NIN_PULSE; // 6 us (Read button val down) #asm ("buttondelay"); button_input[0] = (button_input[0] << 1) | (PINA & NIN_DATA_P1); button_input[1] = (button_input[1] << 1) | ((PINA & NIN_DATA_P2) == NIN_DATA_P2); button_input[2] = (button_input[2] << 1) | ((PINA & NIN_DATA_P3) == NIN_DATA_P3); button_input[3] = (button_input[3] << 1) | ((PINA & NIN_DATA_P4) == NIN_DATA_P4); // Drop Pulse PORTA = PORTA & ~(NIN_PULSE); // 6 us #asm ("buttondelay"); // Set Pulse PORTA = PORTA | NIN_PULSE; // 6 us (Read button val left) #asm ("buttondelay"); button_input[0] = (button_input[0] << 1) | (PINA & NIN_DATA_P1); button_input[1] = (button_input[1] << 1) | ((PINA & NIN_DATA_P2) == NIN_DATA_P2); button_input[2] = (button_input[2] << 1) | ((PINA & NIN_DATA_P3) == NIN_DATA_P3); button_input[3] = (button_input[3] << 1) | ((PINA & NIN_DATA_P4) == NIN_DATA_P4); // Drop Pulse PORTA = PORTA & ~(NIN_PULSE); // 6 us #asm ("buttondelay"); // Set Pulse PORTA = PORTA | NIN_PULSE; // 6 us (Read button val right) #asm ("buttondelay"); button_input[0] = (button_input[0] << 1) | (PINA & NIN_DATA_P1); button_input[1] = (button_input[1] << 1) | ((PINA & NIN_DATA_P2) == NIN_DATA_P2); button_input[2] = (button_input[2] << 1) | ((PINA & NIN_DATA_P3) == NIN_DATA_P3); button_input[3] = (button_input[3] << 1) | ((PINA & NIN_DATA_P4) == NIN_DATA_P4); // Drop Pulse PORTA = PORTA & ~(NIN_PULSE); // 6 us #asm ("buttondelay"); // Set Pulse PORTA = PORTA | NIN_PULSE; // 6 us #asm ("buttondelay"); // Drop Pulse PORTA = PORTA & ~(NIN_PULSE); } /** * This function allocates storage for another bin onto our storage stack, * and then returns the byte offset into the stack where it starts **/ int allocateStorage() { unsigned int storeIndex; unsigned char *newdest; unsigned char *background; // Compute the location for this new binstorage item storeIndex = ((int)++storageStack) << 5; newdest = &binstorage[storeIndex]; background = binstorage; // Clean it up to look like the background image for (i = 0; i < 32; i++) { *(newdest++) = *(background++); } return storeIndex; } /** * Given an x,y coordinate, this function sets the ptrNE, ptrNW, etc. to point to the write * storage bins, and makes sure our binptr map is updated accordingly. For more information on * this, check out the nice little ascii picture of it at the top of this file near * where ptrNW, ptrNE, ptrSE, and ptrSW are declared. **/ void findStoragePtrs(char scr_x, char scr_y) { unsigned char block_off_x, block_off_y; unsigned char NW_ind, NE_ind, SW_ind, SE_ind; unsigned int index; // Compute the offsets into the binptr map where we can find the screen coordinate block_off_x = scr_x >> 3; block_off_y = (scr_y & 0xf8) << 1; // The coordinate always points to the NW bin (or NW corner of the image) NW_ind = block_off_x + block_off_y; // The NE bin will be directly to the rigth of the NW bin, the 0x0f is to account for wrap-around NE_ind = ((block_off_x + 1) & 0x0f) + block_off_y; // The SW bin will be directly under the NW bin SW_ind = NW_ind + 16; if (SW_ind >= 192) SW_ind -= 192; // Account for wrap-around // The SE bin will be directly under the NE bin SE_ind = NE_ind + 16; if (SE_ind >= 192) SE_ind -= 192; // Account for wrap-around // Set northwest pointer if (binptrs[NW_ind] == 0) { // If this bin points to the background image, we need to allocate another storage space index = allocateStorage(); binptrs[NW_ind] = storageStack; } else { // Point to the already allocated space index = ((int)binptrs[NW_ind]) << 5; } ptrNW = binstorage + index; // Set northeast pointer if ((binptrs[NE_ind] == 0) && (scr_x & 0x07)) { // If this bin points to the background image, we need to allocate another storage space // Also, if this x,y combination doesn't utilize the NE bin, then there's no need to allocate space for it index = allocateStorage(); binptrs[NE_ind] = storageStack; } else { // Point to the already allocated space index = ((int)binptrs[NE_ind]) << 5; } ptrNE = binstorage + index; // Set southwest pointer if ((binptrs[SW_ind] == 0) && (scr_y & 0x07)) { // If this bin points to the background image, we need to allocate another storage space // Also, if this x,y combination doesn't utilize the SW bin, then there's no need to allocate space for it index = allocateStorage(); binptrs[SW_ind] = storageStack; } else { // Point to the already allocated space index = ((int)binptrs[SW_ind]) << 5; } ptrSW = binstorage + index; // Set southeast pointer if ((binptrs[SE_ind] == 0) && (scr_x & 0x07) && (scr_y & 0x07)) { // If this bin points to the background image, we need to allocate another storage space // Also, if this x,y combination doesn't utilize the SE bin, then there's no need to allocate space for it index = allocateStorage(); binptrs[SE_ind] = storageStack; } else { // Point to the already allocated space index = ((int)binptrs[SE_ind]) << 5; } ptrSE = binstorage + index; } /** * Copy an image from flash straight to a bin on the screen. This assumes that the image * should be printed in an 8x8 bin and that it is aligned with the bins (which is why * the bin coordinates are passed in rather than screen coordinates) **/ void copyImage(unsigned char bin_x, unsigned char bin_y, flash unsigned char *image) { unsigned char bin_ind; unsigned char *dest; flash unsigned char *src; dest = &binstorage[allocateStorage()]; src = image; for (k = 0; k < 32; k++) { *(dest++) = *(src++); } bin_ind = bin_x + bin_y * 16; binptrs[bin_ind] = storageStack; } /** * The heart of our graphics engine. It overlays an image into 4 quadrants depending * on the scr_x and scr_y passed in * Parameters: * scr_x: the x coordinate of the upper-left corner of the image * scr_y: the y coordinate of the upper-left corner of the image * *NW: a pointer to the storage bin for the NW quadrant (see top of file for more info) * *NE: a pointer to the storage bin for the NE quadrant * *SW: a pointer to the storage bin for the SW quadrant * *SE: a pointer to the storage bin for the SE quadrant * *image: a pointer to the image that we want to apply, images have to be from flash memory **/ void overlayImage(unsigned char scr_x, unsigned char scr_y, unsigned char *NW, unsigned char *NE, unsigned char *SW, unsigned char *SE, flash unsigned char *image) { unsigned char src_ind, src_x, src_y; unsigned char tar_ind, tar_x, tar_y; unsigned char pixel; flash unsigned char *image_ptr; unsigned char offset_x, offset_y; unsigned char currentdata; char isLeft, isUp; char *target; // Determine the offset into the other quadrants that this image will have offset_x = scr_x & 0x07; offset_y = scr_y & 0x07; // All src variables refer to the source image src_x = 0; src_y = 0; src_ind = 0; image_ptr = image; // Read every byte in the source image (images are 32 bytes) while (src_ind < 32) { // Get the current byte (actually it holds 2 pixels!) pixel = *(image_ptr++); // Copy even nibble from src // Determine which bin will be the target isLeft = (src_x + offset_x) < 8; isUp = (src_y + offset_y) < 8; if (isLeft && isUp) target = NW; else if (isLeft && (!isUp)) target = SW; else if ((!isLeft) && isUp) target = NE; else target = SE; // Determine where to copy the pixel tar_x = (src_x + offset_x) & 0x07; tar_y = (src_y + offset_y) & 0x07; tar_ind = (tar_x >> 1) + (tar_y << 2); // If the pixel is 0, this indicates transparency to us, so no drawing is needed if (pixel & 0xf0) { // Copy the pixel, keeping the old opposite nibble intact currentdata = target[tar_ind]; if (tar_x & 0x01) target[tar_ind] = (currentdata & 0xf0) | (pixel >> 4); else target[tar_ind] = (pixel & 0xf0) | (currentdata & 0x0f); } // Increment pixel x pos that corresponds to the source image src_x++; // Copy odd nibble from src // Determine which bin will be the target isLeft = (src_x + offset_x) < 8; if (isLeft && isUp) target = NW; else if (isLeft && !isUp) target = SW; else if (!isLeft && isUp) target = NE; else target = SE; // Determine where to copy the pixel tar_x = (src_x + offset_x) & 0x07; tar_ind = (tar_x >> 1) + (tar_y << 2); target = target + tar_ind; // If the pixel is 0, this indicates transparency to us, so no drawing is needed if (pixel & 0x0f) { // Copy the pixel, keeping the old opposite nibble intact currentdata = *target; if (tar_x & 0x01) *target = (currentdata & 0xf0) | (pixel & 0x0f); else *target = (pixel << 4) | (currentdata & 0x0f); } // Increment counts pertaining to the source image src_ind++; src_x = (src_x + 1) & 0x07; src_y += ((src_ind & 0x03) == 0); } } /** * Erases all pointers to images in the space that an image at scr_x,scr_y would take up **/ void eraseImage(unsigned char scr_x, unsigned char scr_y) { unsigned char block_off_x, block_off_y; unsigned char NW_ind, NE_ind, SW_ind, SE_ind; // Compute the offsets into the binptr map where we can find the screen coordinate block_off_x = scr_x >> 3; block_off_y = (scr_y & 0xf8) << 1; // The coordinate always points to the NW bin (or NW corner of the image) NW_ind = block_off_x + block_off_y; // The NE bin will be directly to the rigth of the NW bin, the 0x0f is to account for wrap-around NE_ind = ((block_off_x + 1) & 0x0f) + block_off_y; // The SW bin will be directly under the NW bin SW_ind = NW_ind + 16; if (SW_ind >= 192) SW_ind -= 192; // The SE bin will be directly under the NE bin SE_ind = NE_ind + 16; if (SE_ind >= 192) SE_ind -= 192; // Set all quadrants to point to the background tile binptrs[NW_ind] = 0; binptrs[NE_ind] = 0; binptrs[SW_ind] = 0; binptrs[SE_ind] = 0; } /** * Erase pointers to bullets from the screen **/ void eraseBullet(unsigned char scr_x, unsigned char scr_y) { unsigned char block_off_x, block_off_y; unsigned char ind; // Compute the offsets into the binptr map where we can find the screen coordinate block_off_x = scr_x >> 3; block_off_y = (scr_y & 0xf8) << 1; // The coordinate always points to the NW bin (or NW corner of the image) ind = block_off_x + block_off_y; // Set all quadrants to point to the background tile binptrs[ind] = 0; } /** * Place ships in their initial positions and set their initial attributes **/ void resetShips(void) { // Initialize ship attributes shipX[0] = 16; shipY[0] = 16; shipROT[0] = 6; shipX[1] = 104; shipY[1] = 16; shipROT[1] = 10; shipX[2] = 16; shipY[2] = 72; shipROT[2] = 2; shipX[3] = 104; shipY[3] = 72; shipROT[3] = 14; for (i = 0; i < 4; i++) { // Initialize more ship attributes shipXfrac[i] = 0; shipYfrac[i] = 0; shipXvel[i] = 0; shipYvel[i] = 0; shipExploding[i] = 0; shipDead[i] = 0; shipBulletCount[i] = 0; shipBulletDelay[i] = 0; // Initialize bullets shipBulletX[i] = 0; shipBulletY[i] = 0; shipBulletXvel[i] = 0; shipBulletYvel[i] = 0; shipBulletLifeline[i] = 0; } } /** * Change the ship's rotation and velocity based on input from the NES controller **/ void alterShips(void) { // Check for rotation every 3 frames if (++tRotate>3) { tRotate = 0; if (!shipDead[j]) { for (j = 0; j < numPlayers; j++) { // Rotate the ship clockwise if RIGHT is held down if (!(button_input[j] & BTN_RIGHT)) shipROT[j] = (shipROT[j] + 1) & 0x0f; // Rotate the ship counter-clockwise if LEFT is held down else if (!(button_input[j] & BTN_LEFT)) shipROT[j] = (shipROT[j] - 1) & 0x0f; // If the ship is exploding, increment its frame, and if it's done, kill it if (shipExploding[j] > 0) shipExploding[j]++; if (shipExploding[j] == 17) { shipExploding[j] = 0; shipDead[j] = 1; } } // end for } // end if } // end if // Change acceleration if thrusters are on forwards or reverse for (j = 0; j < numPlayers; j++) { if (!shipDead[j]) { if (!(button_input[j] & BTN_UP)) { // Modify the ship's velocity using the sin/cos table to determine how much goes to each component shipXvel[j] += (accelerationX[shipROT[j]] >> 2); shipYvel[j] += (accelerationY[shipROT[j]] >> 2); } else if (!(button_input[j] & BTN_DOWN)) { // Modify the ship's velocity using the sin/cos table to determine how much goes to each component shipXvel[j] -= (accelerationX[shipROT[j]] >> 2); shipYvel[j] -= (accelerationY[shipROT[j]] >> 2); } // Make sure the velocity limit isn't exceeded if (shipXvel[j] > MAX_VELOCITY) shipXvel[j] = MAX_VELOCITY; else if (shipXvel[j] < -MAX_VELOCITY) shipXvel[j] = -MAX_VELOCITY; if (shipYvel[j] > MAX_VELOCITY) shipYvel[j] = MAX_VELOCITY; else if (shipYvel[j] < -MAX_VELOCITY) shipYvel[j] = -MAX_VELOCITY; } // end if } // end for } /** * Move the ships based on their current velocity **/ void moveShips(void) { // Move ships based on current velocity for (j = 0; j < numPlayers; j++) { if (!shipDead[j]) { // X direction if (shipXvel[j] < 0) { // Negate velocity to get magnitude i = -shipXvel[j]; // Save magnitude % 256 v1 = (unsigned char)(i & 0xff); shipX[j] = shipX[j] - (unsigned char)(i/256); v2 = shipXfrac[j] - v1; // If we wrapped around... move another pixel over if (v2 > shipXfrac[j]) shipX[j]--; } else { // Magnitude is just velocity i = shipXvel[j]; // Save magnitude % 256 v1 = (unsigned char)(i & 0xff); shipX[j] = shipX[j] + (unsigned char)(i/256); v2 = shipXfrac[j] + v1; // If we wrapped around... move another pixel over if (v2 < shipXfrac[j]) shipX[j]++; } shipXfrac[j] = v2; // Make sure X position is within screen bounds shipX[j] = shipX[j] & 0x7f; // Y direction if (shipYvel[j] < 0) { // Negate velocity to get magnitude i = -shipYvel[j]; // Save magnitude % 256 v1 = (unsigned char)(i & 0xff); shipY[j] = shipY[j] - (unsigned char)(i >> 8); v2 = shipYfrac[j] - v1; // If we wrapped around... move another pixel over if (v2 > shipYfrac[j]) shipY[j]--; // Make sure Y position is within screen bounds if (shipY[j] > 96) shipY[j] += 96; } else { // Magnitude is just velocity i = shipYvel[j]; // Save magnitude % 256 v1 = (unsigned char)(i & 0xff); shipY[j] = shipY[j] + (unsigned char)(i/256); v2 = shipYfrac[j] + v1; // If we wrapped around... move another pixel over if (v2 < shipYfrac[j]) shipY[j]++; // Make sure Y position is within screen bounds shipY[j] = shipY[j] % 96; } shipYfrac[j] = v2; } } } /** * Draws the ships into the screen structure **/ void drawShips(void) { // Draw the ships on the screen for (j = 0; j < numPlayers; j++) { // Don't draw dead ships if (!shipDead[j]) { // Allocate storage or use existing storage for the ships findStoragePtrs(shipX[j], shipY[j]); // If the ship is exploding, use the explosion graphic if (shipExploding[j]) overlayImage(shipX[j], shipY[j], ptrNW, ptrNE, ptrSW, ptrSE, explosiongraphics + ((int)(shipExploding[j]-1) * 32)); // Otherwise, draw the ship (in the right orientation) else overlayImage(shipX[j], shipY[j], ptrNW, ptrNE, ptrSW, ptrSE, shipgraphics + (int)shipROT[j] * 32); } } } /** * Draws a bullet into the screen structure **/ void drawBullet(unsigned char scr_x, unsigned char scr_y) { unsigned char block_off_x, block_off_y, ind; int index; // Compute the offsets into the binptr map where we can find the screen coordinate block_off_x = scr_x >> 3; block_off_y = (scr_y & 0xf8) << 1; // Determine what bin it is ind = block_off_x + block_off_y; if (binptrs[ind] == 0) { // If this bin points to the background image, we need to allocate another storage space index = allocateStorage(); binptrs[ind] = storageStack; } else { // Point to the already allocated space index = ((int)binptrs[ind]) << 5; } ptrNW = binstorage + index; block_off_x = (scr_x & 0x07) >> 1; block_off_y = (scr_y & 0x07) << 2; // Turn on the right pixel in the image if (scr_x & 0x01) { ptrNW[block_off_x + block_off_y] = ptrNW[block_off_x + block_off_y] | 0x0f; } else { ptrNW[block_off_x + block_off_y] = ptrNW[block_off_x + block_off_y] | 0xf0; } } /** * Sets all registers/ports to correct settings and initializes the program **/ void initialize(void) { //init timer 1 to generate sync OCR1A = lineTime; // One NTSC line TCCR1B = 9; // full speed; clear-on-match TCCR1A = 0x00; // turn off pwm and oc lines TIMSK = 0x10; // enable interrupt T1 cmp //init ports DDRD = 0xff; // video/sync out DDRC = 0x20; // DDRA = 0xf0; // need 1 pins for input and 2 for output to comm with nintendo gamepad PORTA = 0x00; // set output pins all to 0 DDRB = 0xff; // LEDs are used for debugging purposes PORTB = 0xff; //init timer 0 to 1/uSec TCCR0 = 2; //initialize synch constants LineCount = 1; syncON = 0b00000000; syncOFF = 0b00000001; // Initialize the bin pointers to all point to the first element (0 field) i = 0; while (i < 192) { binptrs[i] = 0; i++; } // Initialize bin storage to all have 0 fields i = 0; while (i < 512) { binstorage[i] = 0x00; i++; } // Initialize linebuffer for (i = 0; i < 64; i++) { linebuffer[i] = 0; } // Initialize background image to all black for (i = 0; i < 32; i++) { binstorage[i] = 0; } // Initialize last bin to upward ship for (i = 0; i < 32; i++) { binstorage[1376 + i] = penguin[i]; } //init software timer t=0; tRotate=0; tExplode=0; tSelect=0; // Set the initial attributes of the spaceships resetShips(); // The first state of the game is the STARTUP state gameState = STATE_STARTUP; numPlayers = 2; for (i = 0; i < 4; i++) { // Initialize input vectors from NES controller button_input[i] = 0xff; } // Initialize stack pointer in our storage stack storageStack = 0; //enable sleep mode MCUCR = 0b10000000; #asm ("sei"); } //================================== // set up the ports and timers void main(void) { initialize(); //The following loop executes once/video line during lines //1-226, then does all of the frame-end processing while(1) { // stall here until next line starts // sleep enable; mode=idle // use sleep to make entry into sync ISR uniform time #asm ("sleep"); // The following code executes during the vertical blanking, and contains the game logic if (LineCount==224) { switch (gameState) { case STATE_PLAY: // STATE_PLAY: ship combat state of the game // Poll the NES controller for input readNintendo(); // For debugging, show what buttons are being presses // PORTB = button_input[0] & button_input[1] & button_input[2] & button_input[3]; // Initialize the storageStack to 0 storageStack = 0; // Erase pointers to old ship and bullet images from screen for (j = 0; j < numPlayers; j++) { eraseImage(shipX[j], shipY[j]); eraseBullet(shipBulletX[j], shipBulletY[j]); } // Alter the ships in accordance with the input read from the NES controller // This includes changing the ship's velocity and orientation alterShips(); // This performs the actualy moving of the ship, based on its current velocity moveShips(); // Produce bullets (If the A button is pressed, the ships can shoot 1 bullet to kill other ships) for (j = 0; j < numPlayers; j++) { // Ships that are dead or exploding should not be able to shoot if (!(shipExploding[j] || shipDead[j])) { // After shooting, there is a delay before another bullet can be shot if (shipBulletDelay[j] > 0) shipBulletDelay[j]--; // If the ship doesn't already have a bullet flying around, and the user pressed A, shoot another one if ((shipBulletDelay[j] == 0) && (shipBulletCount[j] < 1) && (!(button_input[j] & BTN_A))) { // Make sure they can't shoot another bullet really fast shipBulletDelay[j] = 8; shipBulletCount[j]++; // Lifeline dictates how long a bullet lasts before fizzling out shipBulletLifeline[j] = 50; // Bullets emerge from the center of the ship, for simplicity shipBulletX[j] = (shipX[j] + 3) & 0x7f; shipBulletY[j] = (shipY[j] + 3) % 96; shipBulletXfrac[j] = shipXfrac[j]; shipBulletYfrac[j] = shipYfrac[j]; // Bullets trajectory inherit the ship's trajectory in conjunction // with an added thrust component depending on the orientation of the ship shipBulletXvel[j] = shipXvel[j] + ((int)accelerationX[shipROT[j]] << 2); shipBulletYvel[j] = shipYvel[j] + ((int)accelerationY[shipROT[j]] << 2); } } } // Move bullets according to their velocities for (j = 0; j < numPlayers; j++) { // Is this bullet active? if (shipBulletLifeline[j] > 0) { // Decrement its lifeline so it will eventually fizzle out shipBulletLifeline[j]--; // If this bullet is dead, remove any trace of it if (shipBulletLifeline[j] == 0) { shipBulletCount[j]--; } // Otherwise, do the actualy movement else { // Move X direction if (shipBulletXvel[j] < 0) { // Negate velocity to get magnitude i = -shipBulletXvel[j]; // Save magnitude % 256 v1 = (unsigned char)(i & 0xff); shipBulletX[j] = shipBulletX[j] - (unsigned char)(i/256); v2 = shipBulletXfrac[j] - v1; // If we wrapped around... move another pixel over if (v2 > shipBulletXfrac[j]) shipBulletX[j]--; } else { // Magnitude is just velocity i = shipBulletXvel[j]; // Save magnitude % 256 v1 = (unsigned char)(i & 0xff); shipBulletX[j] = shipBulletX[j] + (unsigned char)(i/256); v2 = shipBulletXfrac[j] + v1; // If we wrapped around... move another pixel over if (v2 < shipBulletXfrac[j]) shipBulletX[j]++; } shipBulletXfrac[j] = v2; // Make sure X position is within screen bounds shipBulletX[j] = shipBulletX[j] & 0x7f; // Move Y direction if (shipBulletYvel[j] < 0) { // Negate velocity to get magnitude i = -shipBulletYvel[j]; // Save magnitude % 256 v1 = (unsigned char)(i & 0xff); shipBulletY[j] = shipBulletY[j] - (unsigned char)(i>>8); v2 = shipBulletYfrac[j] - v1; // If we wrapped around... move another pixel over if (v2 > shipBulletYfrac[j]) shipBulletY[j]--; } else { // Magnitude is just velocity i = shipBulletYvel[j]; // Save magnitude % 256 v1 = (unsigned char)(i & 0xff); shipBulletY[j] = shipBulletY[j] + (unsigned char)(i>>8); v2 = shipBulletYfrac[j] + v1; // If we wrapped around... move another pixel over if (v2 < shipBulletYfrac[j]) shipBulletY[j]++; } shipBulletYfrac[j] = v2; // Make sure Y position is within screen bounds if (shipBulletY[j] > 200) shipBulletY[j] += 96; shipBulletY[j] = shipBulletY[j] % 96; } // end if bulletlifeline } // end if else bulletlifeline > 0 } // end for loop // Collision Detection, to find out which ships are being hit // For each ship for (j = 0; j < numPlayers; j++) { // Loop through all of this ship's opponents for (v1 = 0; v1 < numPlayers; v1++) { // (A ship cannot be hit by its own bullet) if (v1 != j) { // Is the bullet active? if (shipBulletLifeline[v1] > 0) { // Enemy ship is v1, friendly is j // Special case: Ship overlapping right edge, bullet near left if ((shipX[j] > 120) && (shipBulletX[v1] < 8)) { v2 = shipBulletX[v1] + 128; } else { v2 = shipBulletX[v1]; } // Special casE: Ship overlapping bottom edge if ((shipY[j] > 88) && (shipBulletY[v1] < 8)) { v3 = shipBulletY[v1] + 96; } else { v3 = shipBulletY[v1]; } // Do they collide? if ((v2 >= shipX[j]) && (v2 < (shipX[j] + 8)) && (v3 >= shipY[j]) && (v3 < (shipY[j] + 8))) { // Start the ship exploding animation shipExploding[j] = 1; // Make sure the bullet that hit this ship is dead now shipBulletLifeline[v1] = 0; shipBulletCount[v1]--; } } } } } // Draw the ships onto the screen drawShips(); // Draw bullets for (j = 0; j < numPlayers; j++) { if (shipBulletLifeline[j] > 0) { drawBullet(shipBulletX[j], shipBulletY[j]); } } // End round detection: // When there is only one ship alive, the game is done v1 = 0; v2 = 42; // For lack of a better number // Find the players that are still alive, and remember the last one found for (j = 0; j < numPlayers; j++) { if (!shipDead[j]) { v1++; v2 = j; } } // If there are less than 2 players left alive, the round is over if (v1 < 2) { // Someone won the game (game could end in a draw, that's why this 42 business is here) if (v2 != 42) { shipScore[v2]++; } for (j = 0; j < numPlayers; j++) { shipReady[j] = 0; } tSelect = 50; // Ensure blank screen for next state for (i = 0; i < 192; i++) binptrs[i] = 0; // Go to scoreboard gameState = STATE_SCOREBOARD; t = 0; } break; // STATE_STARTUP: triggered upon reset... shows a nice graphic and allows selection of # of players case STATE_STARTUP: // This state has 2 substates, which alternate running every frame, // so this code is essentially running once every 2 frames if (t == 0) { // Poll the NES controller for input readNintendo(); // Initialize the stack to 0 storageStack = 0; // There is a delay after they press select, just so it doesn't go superfast if (tSelect > 0) tSelect--; // Trasnfer the first line of our nice little game logo // (to show off the grey scale capabilities of our setup) :) copyImage(4, 4, welcomegraphics); copyImage(5, 4, welcomegraphics + 32); copyImage(6, 4, welcomegraphics + 64); copyImage(7, 4, welcomegraphics + 96); copyImage(8, 4, welcomegraphics + 128); copyImage(9, 4, welcomegraphics + 160); copyImage(10, 4, welcomegraphics + 192); copyImage(11, 4, welcomegraphics + 224); // Second line of graphic copyImage(4, 5, welcomegraphics + 256); copyImage(5, 5, welcomegraphics + 288); copyImage(6, 5, welcomegraphics + 320); copyImage(7, 5, welcomegraphics + 352); copyImage(8, 5, welcomegraphics + 384); copyImage(9, 5, welcomegraphics + 416); copyImage(10, 5, welcomegraphics + 448); copyImage(11, 5, welcomegraphics + 480); t = !t; } else { // Third line of graphic copyImage(4, 6, welcomegraphics + 512); copyImage(5, 6, welcomegraphics + 544); copyImage(6, 6, welcomegraphics + 576); copyImage(7, 6, welcomegraphics + 608); copyImage(8, 6, welcomegraphics + 640); copyImage(9, 6, welcomegraphics + 672); copyImage(10, 6, welcomegraphics + 704); copyImage(11, 6, welcomegraphics + 736); // Fourth line of graphic copyImage(4, 7, welcomegraphics + 768); copyImage(5, 7, welcomegraphics + 800); copyImage(6, 7, welcomegraphics + 832); copyImage(7, 7, welcomegraphics + 864); copyImage(8, 7, welcomegraphics + 896); copyImage(9, 7, welcomegraphics + 928); copyImage(10, 7, welcomegraphics + 960); copyImage(11, 7, welcomegraphics + 992); // If they press select (1st player controller), change number of players in game if ((tSelect == 0) && (!(button_input[0] & BTN_SELECT))) { numPlayers++; if (numPlayers == 5) numPlayers = 2; tSelect = 5; } // Show the current # of players that are selected copyImage(7, 9, numbergraphics + 32*((int)(numPlayers))); // If first player presses start, advance to next state in game (scoreboard) if ((tSelect == 0) && !(button_input[0] & BTN_START)) { tSelect = 50; for (j = 0; j < 4; j++) { shipScore[j] = 0; shipReady[j] = 0; } // Ensure blank screen for (i = 0; i < 192; i++) binptrs[i] = 0; gameState = STATE_SCOREBOARD; t = 1; } t = !t; } break; // STATE_SCOREBOARD: Displayed after/before each round is played, shows the current score case STATE_SCOREBOARD: // This state has 2 substates, which alternate running every frame, // so this code is essentially running once every 2 frames if (t == 0) { // Poll the NES controller for input readNintendo(); // Initialize storage stack to 0 storageStack = 0; // Make sure the scoreboard shows for a good amount of time if (tSelect > 0) tSelect--; // Print the current score ("score0 - score1 - score2 - score3") for (j = 0; j < numPlayers; j++) { copyImage(4+j*2, 9, smallnumbergraphics + ((int)shipScore[j]) * 32); if (j != numPlayers - 1) { copyImage(5+j*2, 9, dashgraphics); } } // Show another cool looking graphic... first line copyImage(4, 4, scoreboardgraphics); copyImage(5, 4, scoreboardgraphics + 32); copyImage(6, 4, scoreboardgraphics + 64); copyImage(7, 4, scoreboardgraphics + 96); copyImage(8, 4, scoreboardgraphics + 128); copyImage(9, 4, scoreboardgraphics + 160); copyImage(10, 4, scoreboardgraphics + 192); copyImage(11, 4, scoreboardgraphics + 224); // Second line copyImage(4, 5, scoreboardgraphics + 256); copyImage(5, 5, scoreboardgraphics + 288); copyImage(6, 5, scoreboardgraphics + 320); copyImage(7, 5, scoreboardgraphics + 352); copyImage(8, 5, scoreboardgraphics + 384); copyImage(9, 5, scoreboardgraphics + 416); copyImage(10, 5, scoreboardgraphics + 448); copyImage(11, 5, scoreboardgraphics + 480); // First half of third line copyImage(4, 6, scoreboardgraphics + 512); copyImage(5, 6, scoreboardgraphics + 544); copyImage(6, 6, scoreboardgraphics + 576); copyImage(7, 6, scoreboardgraphics + 608); copyImage(8, 6, scoreboardgraphics + 640); t = !t; } else { // Second half of third line copyImage(9, 6, scoreboardgraphics + 672); copyImage(10, 6, scoreboardgraphics + 704); copyImage(11, 6, scoreboardgraphics + 736); // Fourth line copyImage(4, 7, scoreboardgraphics + 768); copyImage(5, 7, scoreboardgraphics + 800); copyImage(6, 7, scoreboardgraphics + 832); copyImage(7, 7, scoreboardgraphics + 864); copyImage(8, 7, scoreboardgraphics + 896); copyImage(9, 7, scoreboardgraphics + 928); copyImage(10, 7, scoreboardgraphics + 960); copyImage(11, 7, scoreboardgraphics + 992); // Poll all players in the game for the ready signal (which is START) for (j = 0; j < numPlayers; j++) { // Check all active players for start button if (!(button_input[j] & BTN_START) && (tSelect < 40)) { shipReady[j] = 1; } if (shipReady[j]) { // Draws a graphic under the player's score to indicate that they // have signaled ready binptrs[164+j*2] = 43; } } // If we showed the screen long enough, let's check for ready signals if (tSelect == 0) { // Is everyone ready to advance? v2 = 1; for (j = 0; j < numPlayers; j++) { v2 = v2 && shipReady[j]; } // If they are, it's time for the next state if (v2) { // Ensure blank screen for (i = 0; i < 192; i++) binptrs[i] = 0; // If the game isn't over yet, go to the PLAY state again if (shipScore[0] < 5 && shipScore[1] < 5 && shipScore[2] < 5 && shipScore[3] < 5) { resetShips(); for (j = 0; j < 4; j++) { shipReady[j] = 0; } tSelect = 10; gameState = STATE_PLAY; } // The game has been won, go back to the beginning else { tSelect = 120; t = 0; gameState = STATE_STARTUP; } } //end if } // end if tSelect t = !t; } // end else t } } //line 231 } //while } //main