/* AVR RELATED */ #include "Video-defs.h" #include "iomacros.h" #include "interrupt.h" /* GLIB and ASTEROIDS related */ #include "feature.h" #include "types.h" #include "glib.h" #include "dispatcher.h" // #define ROWS 254 // #define COLUMNS 69 #define PROFILING #define AGD_DEBUG 1 #define FULL_SPEED (0) // 1 disables interrupts for immediate rendering #define TEST_DIAG (0) // test diagonal lines #define TEST_XMAJOR (1) // test x major lines #define TEST_CIRCLE (1) // duh! extern GLib_t GLib; unsigned int killtime; char MusicState; FrameData_t *FrameBuffer; FrameData_t *CurrentFrameBufferPtr; /* Pointer to the Frame-Buffer, used in the assembly-code to traverse the FrameBuffer */ uint8 Sync; /* this is just pre-loaded with a constant Value */ uint8 Blank; /* this is just pre-loaded with a constant Value */ volatile uint8 Field; /* This tells us which field of the Frame we are in */ volatile uint8 CurrentState; /* This is the State-Machine Variable */ volatile uint8 Count; /* Used in Vertical and Horizontal Sync Sequence */ // volatile uint8 Hstate; /* Used in Horizontal Sync Sequence */ uint8 *tempBuff; void Init_NTSC(void) { /* Initialize NTSC code and configure Timer/Counter and I/O ports for NTSC */ /* outputs */ outp(0xff,DDRE); /* E -> Video o/p */ outp(0xff,DDRD); /* D -> Sync and Barret's code */ outp(0x03,PORTD); /* Initialising the global NTSC variables */ Field = 0x00; Count = 0x01; CurrentState = VSYNC; // Hstate = Fp; Sync = SyncLevel; Blank = BlankLevel; FrameBuffer = (FrameData_t *) GLib.FrameBuffer; CurrentFrameBufferPtr = FrameBuffer; tempBuff = FrameBuffer; /* Setting up T/C-1 to interrupt on overflow */ outp(0x00,TCCR1A); outp(0x00,TCCR1C); outp(0x04,TIMSK); outp(0xff,TCNT1H); outp(0x00,TCNT1L); outp(0x01,TCCR1B); } void Init_MCU(void){ /* Initialize the microcontroller and its ports */ /* Setting Port-Directions */ outp(0xe1,DDRB); outp(0x00,PORTB); /* PORTB Controllers */ outp(0xff,DDRF); outp(0xff,PORTF); outp(0xff,DDRG); outp(0x00,PORTG); outp(0x04, TCCR0); } extern unsigned char * logo; void ClearScreen(void) { Color_t old_color = GLib.PenColor; GLib.PenColor = COLOR_BLACK; GLib_FillRectangle(&GLib, 0, 0, GLIB_RES_X-1, GLIB_RES_Y-1); GLib.PenColor = old_color; } void setnote(char note, char octave, char flat, char sharp) /* Configures Timer2 to generate a Musical Note */ /* note - character value of which note is to be played */ /* octave - which musical octave to play the note */ /* flat - sharp - Musical Modifiers of the Note */ /* A Mathematical error is locate between two octaves which is not yet corrected */ { unsigned char givenote; if(flat && sharp) return; if(flat) { note = note - 1; if(note < 'A') { note = 'G'; octave--; } sharp = 1; } if(1 && (note < 'C')) octave++; /* Compatibility with strange music */ if(octave < 1) return; switch(note) { /* Base Octave Values */ case 'A': if(sharp) givenote = 168; else givenote = 178; break; case 'B': if(sharp) givenote = 149; else givenote = 158; break; case 'C': if(sharp) givenote = 141; else givenote = 149; break; case 'D': if(sharp) givenote = 126; else givenote = 133; break; case 'E': if(sharp) givenote = 112; else givenote = 118; break; case 'F': if(sharp) givenote = 106; else givenote = 112; break; case 'G': if(sharp) givenote = 94; else givenote = 100; break; default: return 255; break; } /* end-case */ if(octave < 3) { // Timer Prescale TCCR2 = 0x1D; // Octave Multiplier } else { if(octave < 5) { TCCR2 = 0x1C; octave = octave - 2; } else { TCCR2 = 0x1B; octave = octave - 4; } } while(octave > 1) { givenote = givenote >> 1; octave--; } OCR2 = givenote - 1; //return givenote - 1; } unsigned int getwait(char n1, char n2, char n3) /* returns the number of cycles to wait per whole note */ { unsigned int wait = 24000; /* 36621 */ unsigned int fin = 0; while(n1 >= '0' && n1 <= '9') { fin = (fin * 10) + n1 - '0'; n1 = n2; n2 = n3; n3 = 'A'; } fin = fin; wait = wait / fin; wait = wait; return wait; } unsigned int music(unsigned char selection) /* Music Player Function - interprets QBasic PLAY notes to generate intricate Music */ /* selection - contains the number of quick music piece is to be played */ /* TCCR2 = 0x18 : Off, 0x1D : 1024, 0x1C : 256, 0x1B : 64, 0x1A : 8 */ { // #1: Shave and a Hair Cut // #2: Wall Impact Sound // #3: Ball Score // #4: Elise // #5: Jingle Bells // #6: William Tell Overture // #8: In the Hall of the Mountain King // #9: FanFare PGM_P text = PSTR("[1]MNT225O3L4EL8CCL4DCP4EFX[2]T200MNL16O3CX[3]T150L64MSO4BEDCCEABEG+B>C<E>ED+ED+EDCCEABE>CDCCEABEG+B>C<E>ED+ED+EDCCEABE>CEEL4EF+G+L16EL16G+G+L8F+D+EF+G+L16EG+L4BL16BAG+F+L8EG+EL16>BBL8BL16BBL8BL16BBL8>EF+G+L16EL16G+G+L8F+D+EF+G+L16EG+L4BL16BAG+F+L8EG+EL16C+C+C+C+C+C+C+C+C+C+C+C+F+F+L8F+L16F+F+L8F+G+AL4F+L8AG+L4EL8G+F+L16EF+G+L16EL16G+G+L8F+D+EF+G+L16EG+L4BL16BAG+F+L8EG+EL16>BBL8BL16BBL8BL16BBL8>EF+G+L16EL16G+G+L8F+D+EF+G+L16EG+L4BL16BAG+F+L8EG+EL64CDL8EL16EEL8EEL4G+.L8F+ED+EC+L16C+C+C+D+EC+D+C+B>C+D+L8ED+EC+L16C+C+C+D+EC+D+C+C+D+C+C+EG+>C+C+C+C+C+C+D+C+C+EG+>C+ED+C+D+G+EC+D+G+EC+D+EF+G+ED+EG+EAF+B>G+F+ED+F+EC+C+C+D+EF+G+C+D+EF+C+D+EC+D+EEF+G+ED+EG+EAF+B>G+F+ED+F+EC+C+C+D+EF+G+C+D+EF+C+D+EC+C+D+C+EF+G+AB>C+D+L8EL16EEL8EEL4G+.L8FED+EC+L16C+C+C+D+EC+D+C+B>C+D+L8EL16EEL8EEL4G+.L8F+ED+EC+L16C+C+C+D+EC+D+C+EL16G+G+L8G+L16G+G+L8G+L16G+G+L8G+>C+C+C+C+C+C+C+C+C+C+C+C+F+F+L8F+L16F+F+L8F+G+AL4F+L8AG+L4EL8G+F+L16BBL8BL16EF+G+L16EL16G+G+L8F+D+EF+G+L16EG+L4BL16BAG+F+L8EEL16>BBL8BL16BBL8BL16BBL8>EF+G+L16EL16G+G+L8F+D+EF+G+L16C+C+D+ED+EF+G+AAAAAAAAC+C+D+ED+EF+G+AAAAAAAAG+D+EAG+D+EAGGGGGECEL16G+G+G+G+G+EEL16G+G+G+G+G+FC+FL16A+A+A+A+A+GEGBP16L16A+P16AP16G+P16F+P16EP16D+P16C+P16C+C+D+ED+EF+G+AAAAAAAAC+C+D+ED+EF+G+AAAAAAAAG+D+EAG+D+EAGGGGGECEL16G+G+G+G+G+EEL16G+G+G+G+G+FC+FL16A+A+A+A+A+GEGBP16L16A+P16AP16G+P16F+P16EP16D+P16C+P16EF+G+L16G+ABP8EF+G+P8C+DD+EFF+GG+AA+BC>C+D+ED+F+D+ED+F+D+ED+F+D+ED+F+D+ED+F+D+ED+F+D+ED+F+D+ED+F+D+L8EL16EEEEL16BBBL16G+G+G+GL16EEEEL16EEL8EEEL16EG+EBG+>EEG+EBG+L4>EP8L16EEL8EEEEL4EP8L16EL4EP8L16O2EL2EX[7]T120L16MLO3BF#X[8]MNT120O1L8MSAB>CDECL4EL8D+L4D+L8DL4DCDECEAGECEL4GP4L8AB>CDECL4EL8D+L4D+L8DL4DL8CDECEAG+EG+BL4AX[9]T255MNO2L4DL8DDL4DL8DDL2GX"); static unsigned int position; static unsigned int Quarternote; static unsigned int Currentnote; static char killstate; static char octave; unsigned int time = 0; unsigned char temp; unsigned char song[4]; MusicState = 1; switch(selection) { case 1: position = 3; break; case 2: position = 54-25; break; case 3: position = 70-25; break; case 4: position = 98-25; break; case 5: position = 266-25; break; case 6: position = 371-25; break; case 7: position = 3870-25; break; case 8: position = 3887-25; break; case 9: position = 4010-25; break; case 10: position = 25; break; default: break; } /* end-case */ while(time == 0) { song[0] = PRG_RDB(text+position); song[1] = PRG_RDB(text+position+1); song[2] = PRG_RDB(text+position+2); switch(song[0]) { case 'T': Quarternote = getwait(song[1], song[2], PRG_RDB(text+position+3)); position++; break; case 'L': temp = song[1] - '0'; if(song[2] >= '0' && song[2] <= '9') temp = (temp * 10) + song[2] - '0'; Currentnote = Quarternote << 2; Currentnote = Currentnote / temp; position++; break; case 'P': TCCR2 = 0x18; temp = song[1] - '0'; if(song[2] >= '0' && song[2] <= '9') temp = (temp * 10) + song[2] - '0'; temp = temp >> 2; time = Quarternote * temp; position++; break; case 'M': if(song[1] == 'S') killstate = 1; else if(song[1] == 'N') killstate = 2; else killstate = 0; position++; break; case 'O': octave = song[1] - '0'; break; case '<': octave--; break; case '>': octave++; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': setnote(song[0], octave, song[1] == '-', song[1] == '+'); time = Currentnote; PORTD = ~OCR2; TCNT2 = 0x00; break; case 'X': MusicState = 0; TCCR2 = 0x18; break; default: break; } /* end-case */ position++; } killtime = 0; if(killstate > 0) killtime = Currentnote >> 2; if(killstate > 1) killtime = killtime >> 1; return time; } void Player(const char sense) // #1: Shave and a Hair Cut // #2: Wall Impact Sound // #3: Ball Score // #4: Elise // #5: Jingle Bells // #6: William Tell Overture { unsigned int MusicTime = 0; char done = 0; char select = 1; char Selprevious = 0; char Startprevious = 0; char n; char temp = 0; char Players = 0x00; outp(0x02, TCCR0); ClearScreen(); GLib.PenColor = COLOR_DARK_GREY; GLib_FillRectangle(&GLib, 20, 20, 220, 2); GLib_FillRectangle(&GLib, 20, 240, 220, 2); GLib_FillRectangle(&GLib, 240, 20, 2, 222); GLib_FillRectangle(&GLib, 20, 20, 2, 220); GLib.PenColor = COLOR_WHITE; GLib_Sprite_TextOut(&GLib, 70, 40, "**** MUSIC ****", 0); GLib_Sprite_TextOut(&GLib, 70, 90, "FUr Elise", 0); GLib_Sprite_TextOut(&GLib, 70, 100, "Jingle Bells", 0); GLib_Sprite_TextOut(&GLib, 70, 110, "Shave and a Hair Cut", 0); GLib_Sprite_TextOut(&GLib, 70, 120, "William Tell Overture", 0); GLib_Sprite_TextOut(&GLib, 70, 130, "Mountain King", 0); GLib_Sprite_TextOut(&GLib, 70, 140, "FanFare", 0); GLib_Sprite_TextOut(&GLib, 70, 150, "Frequency Tester", 0); GLib_Sprite_TextOut(&GLib, 70, 160, "Exit", 0); GLib_Sprite_TextOut(&GLib, 90, 200, "Press Start", 0); while(!done) { if(select) { GLib.PenColor = COLOR_BLACK; GLib_FillRectangle(&GLib, 50, 80+(Players*10), 12, 10); Players++; if(Players > 8) Players = 1; GLib.PenColor = COLOR_WHITE; GLib_Sprite_TextOut(&GLib, 50, 80 + (Players * 10), "*", 1); select = 0; } for(n = 0; n < 9; n++) { if(n < 2) outp(0x40, PORTB); else outp((n & 0x01) << 5, PORTB); if(n == 6) { if(((~(PINB >> 1)) & sense) & ~Selprevious) { select = 1; } Selprevious = (~(PINB >> 1)) & sense; PORTF = ~((~(PINB >> 1)) & sense); } if(n == 8) { if(((~(PINB >> 1)) & sense) & ~Startprevious) { switch(Players) { case 1: temp = 4; break; case 2: temp = 5; break; case 3: temp = 1; break; case 4: temp = 6; break; case 5: temp = 8; break; case 6: temp = 9; break; case 7: Tester(sense); done = 1; break; case 8: temp = 3; done = 1; break; } /* end-case */ MusicTime = music(temp); } Startprevious = (~(PINB >> 1)) & sense; } while(!(TIFR&0x02)); TIFR = 0x02; if(MusicState) { if(MusicTime <= killtime) TCCR2 = 0x18; if(MusicTime < 1) MusicTime = music(0); else MusicTime--; } else TCCR2 = 0x18; } } MusicState = 0; TCCR2 = 0x18; return; } void Tester(const char sense) /* Note to Frequency Generator */ // #1: Shave and a Hair Cut // #2: Wall Impact Sound // #3: Ball Score // #4: Elise // #5: Jingle Bells // #6: William Tell Overture { char previous = 0; char n; char done = 0; char Note = 'A'; char Octave = 3; char Sharp = 0; char Flat = 0; char stuff[2] = " "; outp(0x02, TCCR0); ClearScreen(); GLib.PenColor = COLOR_DARK_GREY; GLib_FillRectangle(&GLib, 20, 20, 220, 2); GLib_FillRectangle(&GLib, 20, 240, 220, 2); GLib_FillRectangle(&GLib, 240, 20, 2, 222); GLib_FillRectangle(&GLib, 20, 20, 2, 220); GLib.PenColor = COLOR_WHITE; GLib_Sprite_TextOut(&GLib, 50, 40, "Frequency Tester", 0); GLib_Sprite_TextOut(&GLib, 40, 100, "Up & Down Change Note", 0); GLib_Sprite_TextOut(&GLib, 30, 110, "Left & Right Change Octave", 0); GLib_Sprite_TextOut(&GLib, 40, 120, "A is Sharp, B is Flat", 0); GLib_Sprite_TextOut(&GLib, 40, 140, "Note:", 0); GLib_Sprite_TextOut(&GLib, 130, 140, "Octave:", 0); GLib_Sprite_TextOut(&GLib, 60, 180, "Press Start to Exit", 0); while(!done) { for(n = 0; n < 17; n++) { /*** Controller Code ***/ if(n < 2) outp(0x40, PORTB); else outp((n & 0x01) << 5, PORTB); switch(n) { case 0: if(Note > 'G') { Note = 'A'; Octave++; } if(Note < 'A') { Note = 'G'; Octave--; } if(Octave > 6) Octave = 1; if(Octave < 1) Octave = 6; setnote(Note,Octave,Flat,Sharp); break; case 1: stuff[0] = Note; GLib_Sprite_TextOut(&GLib, 100, 140, stuff, 1); break; case 3: stuff[0] = Octave+'0'; GLib_Sprite_TextOut(&GLib, 200, 140, stuff, 1); break; case 2: Sharp = (~(PINB >> 1)) & sense; break; case 4: Flat = (~(PINB >> 1)) & sense; break; case 6: break; case 8: done = (~(PINB >> 1)) & sense; break; case 10: if((~(PINB >> 1)) & sense) { if(!(previous & 0x01)) { Note++; previous = previous | 0x01; } } else previous = previous & 0xfe; break; case 12: if((~(PINB >> 1)) & sense) { if(!(previous & 0x02)) { Note--; previous = previous | 0x02; } } else previous = previous & 0xfd; break; case 14: if((~(PINB >> 1)) & sense) { if(!(previous & 0x04)) { Octave--; previous = previous | 0x04; } } else previous = previous & 0xfb; break; case 16: if((~(PINB >> 1)) & sense) { if(!(previous & 0x08)) { Octave++; previous = previous | 0x08; } } else previous = previous & 0xf7; break; } /* end-case */ /*** Timing Code ***/ while(!(TIFR&0x02)); TIFR = 0x02; } } MusicState = 0; TCCR2 = 0x18; return; } void ShowWelcomeScreen(void) { unsigned int time = 0; GLib.PenColor = COLOR_DARK_GREY; GLib_FillRectangle(&GLib, 20, 20, 220, 2); GLib_FillRectangle(&GLib, 20, 240, 220, 2); GLib_FillRectangle(&GLib, 240, 20, 2, 222); GLib_FillRectangle(&GLib, 20, 20, 2, 220); GLib.PenColor = COLOR_WHITE; GLib_Sprite_TextOut(&GLib, 25, 40, "STIGLitz:G-PONG", 0); // GLib_Sprite_TextOut(&GLib, 15, 40, "+++++++++++++++++++++++++++++", 0); GLib_Sprite_TextOut(&GLib, 50, 50, "STI Graphics Library", 0); GLib_Sprite_TextOut(&GLib, 25, 70, "North Carolina State Univ.", 0); GLib_Sprite_TextOut(&GLib, 25, 80, "Department of Electrical", 0); GLib_Sprite_TextOut(&GLib, 25, 90, "and Computer Engineering", 0); GLib_Sprite_TextOut(&GLib, 25, 110, "ECE561 Project", 0); GLib_Sprite_TextOut(&GLib, 25, 120,"Gregory Parsons", 0); time = music(1); outp(0x02, TCCR0); while(MusicState) { if(time <= killtime) TCCR2 = 0x18; if(time < 1) time = music(0); else time--; while (!(TIFR&0x02)); // Waiting for timer0 overflow flag to set TIFR = 0x02; } TCCR2 = 0x18; } void GregDelay(int time) { int n; outp(0x05, TCCR0); for(n = 0; n < time; n++) { while(!(TIFR&0x02)); TIFR = 0x02; } } char ControllerCheck(void) /* returns a mask which can filter out any Controllers that were not Detected */ { char step; char check; ClearScreen(); GLib_Sprite_TextOut(&GLib, 25, 40, "Checking Controllers:", 0); GLib_Sprite_TextOut(&GLib, 25, 60, "Controller 1", 0); GLib_Sprite_TextOut(&GLib, 25, 80, "Controller 2", 0); GLib_Sprite_TextOut(&GLib, 25, 100, "Controller 3", 0); GLib_Sprite_TextOut(&GLib, 25, 120, "Controller 4", 0); check = 0x00; outp(0xe0, DDRB); outp(0x00, PORTB); outp(0x40, PORTB); //outp(0x00, PORTB); for(step = 0; step < 0x10; step++) { if(step & 0x01) { check = check | ((inp(PINB) >> 1) & 0x01); check = check | ((inp(PINB) >> 1) & 0x02); check = check | ((inp(PINB) >> 1) & 0x04); check = check | ((inp(PINB) >> 1) & 0x08); } outp(((step & 0x01) << 5),PORTB); //while(!(TIFR&0x02)); //TIFR = 0x02; } GLib.PenColor = COLOR_WHITE; if(check & 0x01) GLib_Sprite_TextOut(&GLib, 130, 60, "Detected", 0); if(check & 0x02) GLib_Sprite_TextOut(&GLib, 130, 80, "Detected", 0); if(check & 0x04) GLib_Sprite_TextOut(&GLib, 130, 100, "Detected", 0); if(check & 0x08) GLib_Sprite_TextOut(&GLib, 130, 120, "Detected", 0); PORTF = ~check; return check; } /* Note: 256X254...X * Y #define COLOR_WHITE 0x03 #define COLOR_LIGHT_GREY 0x02 #define COLOR_DARK_GREY 0x01 #define COLOR_BLACK 0x00 4 1 2 3 20 20 240 240 */ char GetMisc(const char sense, const char Max) /* sense - const char containing a mask which filters any Controllers that were not Detected */ /* Max - const char containing the number of elements in the selection process */ /* General function which using the select and start buttons on all Detected Controllers */ /* To determine which option the user chooses. This numeric value is returned as a char */ { unsigned int MusicTime = 0; char start = 0; char select = 1; char previous = 0; char n; char Misc = 0x00; GLib.PenColor = COLOR_DARK_GREY; GLib_FillRectangle(&GLib, 20, 20, 220, 2); GLib_FillRectangle(&GLib, 20, 240, 220, 2); GLib_FillRectangle(&GLib, 240, 20, 2, 222); GLib_FillRectangle(&GLib, 20, 20, 2, 220); GLib.PenColor = COLOR_WHITE; outp(0x02, TCCR0); while((!start) || (MusicState)) { if(select) { GLib.PenColor = COLOR_BLACK; GLib_FillRectangle(&GLib, 80, 90+(Misc*10), 12, 10); Misc++; if(Misc > Max) Misc = 1; GLib.PenColor = COLOR_WHITE; GLib_Sprite_TextOut(&GLib, 80, 90 + (Misc * 10), "*", 1); select = 0; } for(n = 0; n < 9; n++) { if(n < 2) outp(0x40, PORTB); else outp((n & 0x01) << 5, PORTB); if(n == 6) { if(((~(PINB >> 1)) & sense) & ~previous) { select = 1; MusicTime = music(2); } previous = (~(PINB >> 1)) & sense; PORTF = ~((~(PINB >> 1)) & sense); } if((!start) && (n == 8) && ((~(PINB >> 1)) & sense)) { start = 1; MusicTime = music(3); } while(!(TIFR&0x02)); TIFR = 0x02; if(MusicState) { if(MusicTime <= killtime) TCCR2 = 0x18; if(MusicTime < 1) MusicTime = music(0); else MusicTime--; } else TCCR2 = 0x18; } } TCCR2 = 0x18; return Misc; } char GetPlayers(const char sense) /* Cookie Cutter Function */ /* Returns the Number of Players entered into GetMisc above */ { ClearScreen(); GLib.PenColor = COLOR_WHITE; GLib_Sprite_TextOut(&GLib, 70, 40, "**** G-PONG ****", 0); GLib_Sprite_TextOut(&GLib, 100, 100, "1 Player", 0); GLib_Sprite_TextOut(&GLib, 100, 110, "2 Players", 0); GLib_Sprite_TextOut(&GLib, 100, 120, "3 Players", 0); GLib_Sprite_TextOut(&GLib, 100, 130, "4 Players", 0); GLib_Sprite_TextOut(&GLib, 100, 140, "Play Music", 0); GLib_Sprite_TextOut(&GLib, 90, 180, "Press Start", 0); return GetMisc(sense, 5); } char GetBalls(const char sense) /* Cookie Cutter Function of GetPlayers */ /* Returns a true/false value, 0x00 = 1 Ball, 0x01 = 2 Balls */ { ClearScreen(); GLib.PenColor = COLOR_WHITE; GLib_Sprite_TextOut(&GLib, 70, 40, "**** G-PONG ****", 0); GLib_Sprite_TextOut(&GLib, 100, 100, "1 Ball", 0); GLib_Sprite_TextOut(&GLib, 100, 110, "2 Balls", 0); GLib_Sprite_TextOut(&GLib, 100, 120, "1-Unschedulable", 0); GLib_Sprite_TextOut(&GLib, 100, 130, "2-Unschedulable", 0); GLib_Sprite_TextOut(&GLib, 90, 180, "Press Start", 0); return GetMisc(sense, 4) - 1; } void Pong(const char sense, const char Players, const char Balls) /* This is the game of GregPONG which returns on a select-start action on any Detected controller */ /* sense - const char containing a mask which filters any Controllers that were not Detected */ /* Players - const char containing the number of players selected at the startup screen */ /* Balls - const char containing the number of Balls selected at the startup screen */ /* Controller Control Lines: time slices 0 and 1 Latch High. */ /* Even time slices Clock is low, Odd time slices Clock is High */ /* Controller Buttons are available on PINB during the following Time Slices: */ /* 2-A, 4-B, 6-Select, 8-Start, 10-Up, 12-Down, 14-Left, 16-Right */ /* Current game Speed walks a fine line of schedulability, if Timer0 clocked at 0x01 /* instead of the current 0x02 it would be unschedulable and very interesting to play */ { const char v1 = 8; // Normal Speed const char v2 = 14; // Speed with Button B pushed const char v3 = 4; // Speed modifier for Button A pushed char ControlState = 0; // Holds the number of the current time-slice char Start = 0; char Select = 0; char timing = 0; char Pause; char Buffer[4]; char Reset = 1; char Accel[4]; char BKey; char queued; unsigned char StartCycle = 0; // Counts the number of games played int Ballx[2]; int Bally[2]; int Velx[2]; int Vely[2]; char Smash[2]; int Paddles[4]; int PaddleVel[4]; unsigned int MusicTime = 0; char Scores[4]; int n; char temp; ClearScreen(); MusicState = 0; if(Balls & 0x02) outp(0x01, TCCR0); // Set Game Speed else outp(0x02, TCCR0); Balls = Balls & 0x01; for(n = 0; n < 4; n++) { // Initialize Starting Matrix Paddles[n] = 130; Scores[n] = 0; PaddleVel[n] = 0; Accel[n] = 0; } while(!(TIFR&0x02)); TIFR = 0x02; while(1) { if(Reset) { //ClearScreen(); PORTB = 0x40; GLib.PenColor = COLOR_DARK_GREY; if(Players < 4) GLib_FillRectangle(&GLib, 20, 20, 220, 2); if(Players < 3) GLib_FillRectangle(&GLib, 20, 240, 220, 2); if(Players < 2) GLib_FillRectangle(&GLib, 240, 20, 2, 220); GLib.PenColor = COLOR_WHITE; GLib_FillRectangle(&GLib, 20, Paddles[0], 2, 30); if(Players > 1) GLib_FillRectangle(&GLib, 240, Paddles[1], 2, 30); if(Players > 2) GLib_FillRectangle(&GLib, Paddles[2], 240, 30, 2); if(Players > 3) GLib_FillRectangle(&GLib, Paddles[3], 20, 30, 2); ControlState = 0; queued = 0; Reset = 0; Pause = 1; Scores[4]++; /**** Randomize Ball Placement and Remove Old Balls ****/ for(n = 0; n <= Balls; n++) { if(Players < 2) Smash[n] = 0; else Smash[n] = 4; GLib.PenColor = COLOR_BLACK; GLib_DrawCircle(&GLib, Ballx[n], Bally[n], 3); // Erase Ball n switch((StartCycle + (n << 1) & 0x03)) { case 1: Ballx[n] = 120; Bally[n] = 210; if(StartCycle & 0x04) Velx[n] = 4; else Velx[n] = -4; Vely[n] = -4; break; case 2: Ballx[n] = 210; Bally[n] = 120; Velx[n] = -4; if(StartCycle & 0x04) Vely[n] = 4; else Vely[n] = -4; break; case 3: Ballx[n] = 120; Bally[n] = 30; if(StartCycle & 0x04) Velx[n] = -4; else Velx[n] = 4; Vely[n] = 4; break; default: Ballx[n] = 30; Bally[n] = 120; Velx[n] = 4; if(StartCycle & 0x04) Vely[n] = -4; else Vely[n] = 4; break; } /* End Case */ GLib.PenColor = COLOR_WHITE; GLib_DrawCircle(&GLib, Ballx[n], Bally[n], 3); // Draw New Ball n } StartCycle++; MusicTime = music(3); } if(ControlState == 8) { if((~(PINB >> 1)) & sense) { if(!Start) { Start = 1; if(Select & 0x01) return; // If select->start action then return if(Pause) { // If Paused, unPause and Erase Scores GLib.PenColor = COLOR_BLACK; GLib_FillRectangle(&GLib, 30, Paddles[0] + 10, 30, 10); GLib_FillRectangle(&GLib, 210, Paddles[1] + 10, 29, 10); GLib_FillRectangle(&GLib, Paddles[2] + 10, 220, 30, 10); GLib_FillRectangle(&GLib, Paddles[3] + 10, 30, 30, 10); Pause = 0; GLib.PenColor = COLOR_WHITE; } else { Pause = 1; } } } else Start = 0; } if(ControlState == 6) { if((~(PINB >> 1)) & sense) { if(!(Select)) Select = 3; } else Select = 0; } /* Game Movement and Interaction */ if(!Pause) { switch(ControlState) { case 1: break; case 2: //AKey = (~(PINB)); // Capture all A-Buttons for Later Consumption temp = (~(PINB >> 1)) & sense; for(n = 0; n < Players; n++) { if(temp & 0x01) { Accel[n] = 5; } else if(Accel[n] > 0) Accel[n]--; temp = temp >> 1; } GLib.PenColor = COLOR_BLACK; GLib_DrawCircle(&GLib, Ballx[0], Bally[0], 3); // Erase Ball 0 GLib.PenColor = COLOR_WHITE; break; case 4: BKey = (~(PINB)); // Capture all B-Buttons for Later Consumption GLib_DrawCircle(&GLib, Ballx[0], Bally[0], 3); // Draw Ball 0 break; case 5: break; case 6: if(Balls) { GLib.PenColor = COLOR_BLACK; GLib_DrawCircle(&GLib, Ballx[1], Bally[1], 3);// Erase Ball 1 GLib.PenColor = COLOR_WHITE; } break; case 8: if(Balls) GLib_DrawCircle(&GLib, Ballx[1], Bally[1], 3);// Draw Ball 1 break; case 3: case 7: if(ControlState == 7) { if(Balls) n = 1; // Execute Ball 2 Physics else break; } else n = 0; //Execute Ball 1 Physics Ballx[n] += Velx[n]; Bally[n] += Vely[n]; if(Ballx[n] < 14) { if(Smash[n] != 0) Scores[Smash[n]]++; Scores[0]--; Reset = 1; } if(Ballx[n] > 246) { if(Smash[n] != 1) Scores[Smash[n]]++; Scores[1]--; Reset = 1; } if(Bally[n] > 246) { if(Smash[n] != 2) Scores[Smash[n]]++; Scores[2]--; Reset = 1; } if(Bally[n] < 14) { if(Smash[n] != 3) Scores[Smash[n]]++; Scores[3]--; Reset = 1; } if((Ballx[n] < (25 - Velx[n])) && (Bally[n] > Paddles[0] - 4) && (Bally[n] < (Paddles[0] + 34))) { if(Velx[n] < 0) Velx[n] = -Velx[n]; if(Accel[0] > 0 && Accel[0] < 5) { Velx[n] += v3; MusicTime = music(7); } else MusicTime = music(2); Vely[n] = Vely[n] + (PaddleVel[0] >> 2); Smash[n] = 0; } if((Ballx[n] > (235 - Velx[n])) && ((Players < 2) || ((Bally[n] > (Paddles[1]-4)) && (Bally[n] < (Paddles[1] + 34))))) { if(Velx[n] > 0) Velx[n] = -Velx[n]; if(Accel[1] > 0 && Accel[1] < 5) { Velx[n] -= v3; MusicTime = music(7); } else MusicTime = music(2); Vely[n] = Vely[n] + (PaddleVel[1] >> 2); if(Players > 1) Smash[n] = 1; } if((Bally[n] > (235 - Vely[n])) && ((Players < 3) || ((Ballx[n] > (Paddles[2]-4)) && (Ballx[n] < (Paddles[2] + 34))))) { if(Vely[n] > 0) Vely[n] = -Vely[n]; if(Accel[2] > 0 && Accel[2] < 5) { Vely[n] -= v3; MusicTime = music(7); } else MusicTime = music(2); Velx[n] = Velx[n] + (PaddleVel[2] >> 2); if(Players > 2) Smash[n] = 2; } if((Bally[n] < (25 - Vely[n])) && ((Players < 4) || ((Ballx[n] > (Paddles[3]-4)) && (Ballx[n] < (Paddles[3] + 34))))) { if(Vely[n] < 0) Vely[n] = -Vely[n]; Velx[n] = Velx[n] + (PaddleVel[3] >> 2); if(Accel[3] > 0 && Accel[3] < 5) { Vely[n] += v3; MusicTime = music(7); } else MusicTime = music(2); if(Players > 3) Smash[n] = 3; } break; case 9: for(n = 0; n < 4; n++) PaddleVel[n] = 0; if(Balls) { // Insert Ball Collision Code Here } break; case 10 : if(!(PINB & 0x02) && (Paddles[0] > (16 + v1))) { if(BKey & 0x02 && (Paddles[0] > (16 + v2))) queued = v2; else queued = v1; Paddles[0] -= queued; PaddleVel[0] = -queued; GLib_DrawLine(&GLib,20,Paddles[0],20,Paddles[0] + queued - 1); GLib_DrawLine(&GLib,21,Paddles[0],21,Paddles[0] + queued - 1); GLib.PenColor = COLOR_BLACK; GLib_DrawLine(&GLib,20,Paddles[0] + 31,20,Paddles[0] + 30 + queued); GLib_DrawLine(&GLib,21,Paddles[0] + 31,21,Paddles[0] + 30 + queued); GLib.PenColor = COLOR_WHITE; } if((Players > 1) && !(PINB & 0x04) && (Paddles[1] > (16+v1))) { if(BKey & 0x04 && Paddles[1] > (16 + v2)) queued = v2; else queued = v1; } else queued = 0; break; case 11 : if(queued > 0) { Paddles[1] -= queued; PaddleVel[1] = -queued; GLib_DrawLine(&GLib,240,Paddles[1],240,Paddles[1] + queued - 1); GLib_DrawLine(&GLib,241,Paddles[1],241,Paddles[1] + queued - 1); GLib.PenColor = COLOR_BLACK; GLib_DrawLine(&GLib,240,Paddles[1] + 31,240,Paddles[1] + queued + 30); GLib_DrawLine(&GLib,241,Paddles[1] + 31,241,Paddles[1] + queued + 30); GLib.PenColor = COLOR_WHITE; } break; case 12 : if(!(PINB & 0x02) && (Paddles[0] < 216-v1)) { if(BKey & 0x02 && Paddles[0] < 216-v2) queued = v2; else queued = v1; Paddles[0] += queued; PaddleVel[0] = queued; GLib_DrawLine(&GLib,20,Paddles[0] + 31 - queued,20,Paddles[0] + 30); GLib_DrawLine(&GLib,21,Paddles[0] + 31 - queued,21,Paddles[0] + 30); GLib.PenColor = COLOR_BLACK; GLib_DrawLine(&GLib,20,Paddles[0] - queued,20,Paddles[0] - 1); GLib_DrawLine(&GLib,21,Paddles[0] - queued,21,Paddles[0] - 1); GLib.PenColor = COLOR_WHITE; } if((Players > 1) && !(PINB & 0x04) && (Paddles[1] < 216-v1)) { if(BKey & 0x04 && (Paddles[1] < 216-v2)) queued = v2; else queued = v1; } else queued = 0; break; case 13 : if(queued > 0) { Paddles[1] += queued; PaddleVel[1] = queued; GLib_DrawLine(&GLib,240,Paddles[1] + 31 - queued,240,Paddles[1] + 30); GLib_DrawLine(&GLib,241,Paddles[1] + 31 - queued,241,Paddles[1] + 30); GLib.PenColor = COLOR_BLACK; GLib_DrawLine(&GLib,240,Paddles[1] - queued,240,Paddles[1] - 1); GLib_DrawLine(&GLib,241,Paddles[1] - queued,241,Paddles[1] - 1); GLib.PenColor = COLOR_WHITE; } break; case 14 : if((Players > 3) && !(PINB & 0x10) && (Paddles[3] > 16+v1)) { if(BKey & 0x10 && Paddles[3] > 16+v2) queued = v2; else queued = v1; Paddles[3] -= queued; PaddleVel[3] = -queued; GLib_DrawLine(&GLib,Paddles[3],20,Paddles[3] + queued - 1,20); GLib_DrawLine(&GLib,Paddles[3],21,Paddles[3] + queued - 1,21); GLib.PenColor = COLOR_BLACK; GLib_DrawLine(&GLib,Paddles[3] + 31,20,Paddles[3] + 30 + queued,20); GLib_DrawLine(&GLib,Paddles[3] + 31,21,Paddles[3] + 30 + queued,21); GLib.PenColor = COLOR_WHITE; } if((Players > 2) && !(PINB & 0x08) && (Paddles[2] > 16+v1)) { if(BKey & 0x08 && Paddles[2] > 16+v2) queued = v2; else queued = v1; } else queued = 0; break; case 15 : if(queued > 0) { Paddles[2] -= queued; PaddleVel[2] = -queued; GLib_DrawLine(&GLib,Paddles[2],240,Paddles[2] + queued - 1,240); GLib_DrawLine(&GLib,Paddles[2],241,Paddles[2] + queued - 1,241); GLib.PenColor = COLOR_BLACK; GLib_DrawLine(&GLib,Paddles[2] + 31,240,Paddles[2] + queued + 30,240); GLib_DrawLine(&GLib,Paddles[2] + 31,241,Paddles[2] + queued + 30,241); GLib.PenColor = COLOR_WHITE; } break; case 16 : if((Players > 3) && !(PINB & 0x10) && (Paddles[3] < 216-v1)) { if(BKey & 0x10 && Paddles[3] < 216-v2) queued = v2; else queued = v1; Paddles[3] += queued; PaddleVel[3] = queued; GLib_DrawLine(&GLib,Paddles[3] + 31 - queued,20,Paddles[3] + 30,20); GLib_DrawLine(&GLib,Paddles[3] + 31 - queued,21,Paddles[3] + 30,21); GLib.PenColor = COLOR_BLACK; GLib_DrawLine(&GLib,Paddles[3] - queued,20,Paddles[3] - 1,20); GLib_DrawLine(&GLib,Paddles[3] - queued,21,Paddles[3] - 1,21); GLib.PenColor = COLOR_WHITE; } if((Players > 2) && !(PINB & 0x08) && (Paddles[2] < 216-v1)) { if(BKey & 0x08 && Paddles[2] < 216-v2) queued = v2; else queued = v1; } else queued = 0; break; case 0 : if(queued > 0) { Paddles[2] += queued; PaddleVel[2] = queued; GLib_DrawLine(&GLib,Paddles[2] + 31 - queued,240,Paddles[2] + 30,240); GLib_DrawLine(&GLib,Paddles[2] + 31 - queued,241,Paddles[2] + 30,241); GLib.PenColor = COLOR_BLACK; GLib_DrawLine(&GLib,Paddles[2] - queued,240,Paddles[2] - 1,240); GLib_DrawLine(&GLib,Paddles[2] - queued,241,Paddles[2] - 1,241); GLib.PenColor = COLOR_WHITE; } break; default: break; } /* End Case */ } else { // If Paused Then... switch(ControlState) { case 10: itoa(Scores[0], Buffer, 10); GLib_Sprite_TextOut(&GLib, 30, Paddles[0] + 10, Buffer, 1); break; case 12: if(Players > 1) { itoa(Scores[1], Buffer, 10); if(Scores[1] < -9) GLib_Sprite_TextOut(&GLib, 210, Paddles[1] + 10, Buffer, 1); else GLib_Sprite_TextOut(&GLib, 220, Paddles[1] + 10, Buffer, 1); } break; case 14: if(Players > 2) { itoa(Scores[2], Buffer, 10); GLib_Sprite_TextOut(&GLib, Paddles[2] + 10, 220, Buffer, 1); } break; case 16: if(Players > 3) { itoa(Scores[3], Buffer, 10); GLib_Sprite_TextOut(&GLib, Paddles[3] + 10, 30, Buffer, 1); } break; default: break; } } /**** Audio COde - Also Simple isn't it? ****/ if(MusicState) { if(MusicTime <= killtime) TCCR2 = 0x18; if(MusicTime < 1) MusicTime = music(0); else MusicTime--; } else TCCR2 = 0x18; /**** NES Controller Code - Simple, isn't it? ****/ ControlState++; if(ControlState > 16) ControlState = 0; if(ControlState < 2) PORTB = 0x40; else PORTB = ((ControlState & 0x01) << 5); /**** Check if we make our Timing Restriction ****/ if((TIFR&0x02) && (timing < 0x0f)) timing++; if(ControlState == 0) { PORTF = ~timing; timing = 0; } /**** Check Timer0 to see if we are on track ****/ while(!(TIFR&0x02)); TIFR = 0x02; } } int main(void) { unsigned char sense, Players, Balls; /* Enable External SRAM */ outp(0x80,MCUCR); outp(0x00,XMCRA); outp(0x00,XMCRB); Init_NTSC(); // This function was not Modified Init_MCU(); /* initialize and clear display */ GLib_Init(&GLib); GLib_SetForeground(&GLib, 0); sei(); /* enable interrupts and allow display refresh */ ShowWelcomeScreen(); while(1) { sense = ControllerCheck(); /* Find out which Controller PORTS are in use */ GregDelay(100); Players = GetPlayers(sense); if(Players > 4) Player(sense); else { Balls = GetBalls(sense); Pong(sense, Players, Balls); } } while (1); }