//JezzBall TV Video Game with Mouse Input // //Programmers: Arthur Zhang (ayz2) // Yewen Ying (ydy2) // //ECE 476 SP 2005 Final Project //D.5 is sync:1000 ohm + diode to 75 ohm resistor //D.6 is video:330 ohm + diode to 75 ohm resistor //B.3 is sound and should have a 10k resistor to gnd //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 #pragma regalloc- //I allocate the registers myself #pragma optsize- //optimize for speed #include #include #include #include //used for generating random numbers #include //definitions to make our code more readable #define POINT_CLEARING_START 24 #define POINT_CLEARING_END 75 #define lineTime 1018 #define ScreenTop 30 #define ScreenBot 230 #define VERTICAL 63 #define HORIZONTAL 64 #define Blank 62 #define MAX_BOUNDARIES 10 #define MAX_ERASE 5 #define CURSOR_SIZE 7 #define SS PORTB.4 #define PERCENT_INDEX 39 #define LIVES_INDEX 40 #define SMALLBLANK_INDEX 12 #define CLEARING_START 12 #define CLEARING_END 89 #define ARROW 65 #define CLOCK 66 #define EXCLAIM 41 #define STARTHORIZTOP 30 #define STARTVERTLEFT 30 #define SOUNDVERTLEFT 30 #define LIVESVERTLEFT 30 #define SCOREVERTLEFT 30 #define CLEAR_ENTIRE_SCREEN_START 0 #define CLEAR_ENTIRE_SCREEN_END 100 #define CURSOR_X_MAX 120 #define CURSOR_X_MIN 0 #define CURSOR_Y_MAX 83 #define CURSOR_Y_MIN 11 #define WIN_NOTES_LENGTH 6 #define LOSE_NOTES_LENGTH 5 #define BALL_HIT_WALL 50 #define BALL_HIT_CUT 60 #define TRIANGLE 67 //macro to test whether a specific point is inside of a boundary given //the index to the boundary and the index to the ball #define inBoundary(index,count) (boundaryPoint1X[index] < ballPosX[count] && boundaryPoint2X[index] > ballPosX[count]) && (boundaryPoint1Y[index] < ballPosY[count] && boundaryPoint2Y[index] > ballPosY[count]) //NOTE that v1 to v8 and i must be in registers! register char v1 @4; register char v2 @5; register char v3 @6; register char v4 @7; register char v5 @8; register char v6 @9; register char v7 @10; register char v8 @11; register int i @12; #pragma regalloc+ //State machine names enum {START_SCREEN=0, GAME_OPTIONS, GAME_PLAYING, WIN_STATE, LOSE_SCREEN, BEAT_GAME, HIGH_SCORE, POINT_STATE,POINT_CLEARING_STATE,SET_UP_LEVEL,CLEAR_SCREEN, CLEAR_ENTIRE_SCREEN, DRAW_LINES}; enum {START=1, SOUND, LIVES, SCORE}; //used so that we can have a new random seed every time we want to generate random numbers unsigned int randomSeed; //counter variables that are used in for loops char count, count2, hsCount; //syncing variables for tv signal generation char syncON, syncOFF; int LineCount; //used to store the time used in time bonus int time; //EEPROM variables to store high score eeprom int highScore=0; //tv variables char screen[1600], t, ts[10]; //Game Parameters unsigned int currentScore; char currentLives; bit soundOnOff; //Bar parameters, there are two positions that are moving at all times char clickedPositionX, clickedPositionY, movingPositionOneX, movingPositionOneY, movingPositionTwoX, movingPositionTwoY; bit clickedDirection ; // 0 for horizontal and 1 for vertical //ball variables char curNumOfBalls; char curLevel; char ballPosX[10], ballPosY[10], ballVelX[10], ballVelY[10]; //cursor variables char cursorPosX, cursorPosY, newCursorPosX, newCursorPosY, newButton, button; char isMenuClicked; unsigned char sendDirection; //clickedOnOff corresponds if there is a current click going on (whether or not a line is being drawn across) //movingLine0 and movingLine1 are to check if there is a line moving across on either side, they are seperate //and balls need to hit both of them to stop both lines bit clickedOnOff, movingLine0, movingLine1; //flash variables to save space on the MCU flash char titleJezzBall[]="JEZZBALL"; flash char titleECE476[]="ECE476"; flash char loseStr[]="YOU LOSE"; flash char successStr[]="SUCCESS"; flash char livesStr[] = "LIVES:"; flash char scoreStr[] = "SCORE:"; flash char areaStr[] = "AREA LEFT:"; flash char levelStr[] = "LVL"; flash char timeBonusStr[] = "TIME BONUS:"; flash char fillBonusStr[] = "FILL BONUS:"; flash char newHighScoreStr[] = "NEW HIGH SCORE"; flash char congratsStr[] = "YOU WIN"; //boundaries are only made up of rectangles (area enclosed by lines with no balls in it) //the first boundary is the main screen, and there is only 1 boundary because there are no cuts //if there is no boundary inside of it then the value it holds is -1 //boundaryPoint1 holds the top left corner of a rectangle, boundaryPoint2 holds the bottom right corner point char boundaryPoint1X[MAX_BOUNDARIES]; char boundaryPoint2X[MAX_BOUNDARIES]; char boundaryPoint1Y[MAX_BOUNDARIES]; char boundaryPoint2Y[MAX_BOUNDARIES]; //these hold the current boundaries that should be erased, //the maximum number of boundaries that can be queued up to be erased is 5 char eraseBoundaryPointX1[MAX_ERASE]; char eraseBoundaryPointX2[MAX_ERASE]; char eraseBoundaryPointY1[MAX_ERASE]; char eraseBoundaryPointY2[MAX_ERASE]; //this is the current line that will be erased inside of a boundary that should be erased char curErasingX1,curErasingY1,curErasingX2,curErasingY2; //this stores the state behind the cursor so that it doesn't erase anything on the screen char stateSavedBehindCursor[CURSOR_SIZE]; char currentDirection; //0 for horizontal for vertical char tempBallNextX, tempBallNextY;//used for checking if it hits a drawing line char indexBoundaryWithCut;//used for when a cut is made, this is the index of boundary that has a cut in it char indexNewBoundary;//used to for finding a new index that is empty bit ballInCut;//a 1 if there is a ball in the boundary //the lineSucceed variables determine whether the both directions of cut have succeeded //we need to have two lines drawing out individually because one line can stop before the other bit line0Succeed, line1Succeed, cutEndHitsBall; //these holds the number of boundaries that are queued up to be erased char boundsToBeErased; char nextEraseIndex; //the next index in the erasing array that is open char nextBoundToBeErasedIndex; //this is the next boundary that is queued up to be erased int currentArea; //the current amount of area that is left char currentAreaPercentage,newAreaPercentage; //used to calculate the different that a cut has made //total area is 77*125 to be drawn in flash int totalArea = 9625; char clearingIndex;//this is the current line that is being cleared on the screen (during clearing //state) //music variables char musicT; char currentNote;//current note playing char noteCount; //used as the index of which note to play char isPlaying; //whether or not the win/lose notes are being played //holds the specific notes that should be played for a win or a loss flash char loseNotes[] = {48,48,51,51,54,54}; flash char winNotes[] = {48,36,30,0,36,30,30}; //State Machine Variables //stateAfter variables hold states which it will go to after it has cleared the screen char mainState, stateAfterPointState,stateAfterClear,stateAfterClearEntireScreen; char pointClearingIndex;//this is the index for clearing the screen to display points //Variables for debouncing //how long it has been held down for, //when released will get reset unsigned int leftButtonHeld; unsigned int rightButtonHeld; bit leftButtonPressed; bit rightButtonPressed; //time bonus and fill bonus are caclculated at the end of the level int timeBonus; int fillBonus; //Point plot lookup table //One bit masks flash char pos[8]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01}; flash char bitmap[68][7]={ //0 0b01110000, 0b10001000, 0b10011000, 0b10101000, 0b11001000, 0b10001000, 0b01110000, //1 0b00100000, 0b01100000, 0b00100000, 0b00100000, 0b00100000, 0b00100000, 0b01110000, //2 0b01110000, 0b10001000, 0b00001000, 0b00010000, 0b00100000, 0b01000000, 0b11111000, //3 0b11111000, 0b00010000, 0b00100000, 0b00010000, 0b00001000, 0b10001000, 0b01110000, //4 0b00010000, 0b00110000, 0b01010000, 0b10010000, 0b11111000, 0b00010000, 0b00010000, //5 0b11111000, 0b10000000, 0b11110000, 0b00001000, 0b00001000, 0b10001000, 0b01110000, //6 0b01000000, 0b10000000, 0b10000000, 0b11110000, 0b10001000, 0b10001000, 0b01110000, //7 0b11111000, 0b00001000, 0b00010000, 0b00100000, 0b01000000, 0b10000000, 0b10000000, //8 0b01110000, 0b10001000, 0b10001000, 0b01110000, 0b10001000, 0b10001000, 0b01110000, //9 0b01110000, 0b10001000, 0b10001000, 0b01111000, 0b00001000, 0b00001000, 0b00010000, //A 0b01110000, 0b10001000, 0b10001000, 0b10001000, 0b11111000, 0b10001000, 0b10001000, //B 0b11110000, 0b10001000, 0b10001000, 0b11110000, 0b10001000, 0b10001000, 0b11110000, //C 0b01110000, 0b10001000, 0b10000000, 0b10000000, 0b10000000, 0b10001000, 0b01110000, //D 0b11110000, 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b11110000, //E 0b11111000, 0b10000000, 0b10000000, 0b11111000, 0b10000000, 0b10000000, 0b11111000, //F 0b11111000, 0b10000000, 0b10000000, 0b11111000, 0b10000000, 0b10000000, 0b10000000, //G 0b01110000, 0b10001000, 0b10000000, 0b10011000, 0b10001000, 0b10001000, 0b01110000, //H 0b10001000, 0b10001000, 0b10001000, 0b11111000, 0b10001000, 0b10001000, 0b10001000, //I 0b01110000, 0b00100000, 0b00100000, 0b00100000, 0b00100000, 0b00100000, 0b01110000, //J 0b00111000, 0b00010000, 0b00010000, 0b00010000, 0b00010000, 0b10010000, 0b01100000, //K 0b10001000, 0b10010000, 0b10100000, 0b11000000, 0b10100000, 0b10010000, 0b10001000, //L 0b10000000, 0b10000000, 0b10000000, 0b10000000, 0b10000000, 0b10000000, 0b11111000, //M 0b10001000, 0b11011000, 0b10101000, 0b10101000, 0b10001000, 0b10001000, 0b10001000, //N 0b10001000, 0b10001000, 0b11001000, 0b10101000, 0b10011000, 0b10001000, 0b10001000, //O 0b01110000, 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b01110000, //P 0b11110000, 0b10001000, 0b10001000, 0b11110000, 0b10000000, 0b10000000, 0b10000000, //Q 0b01110000, 0b10001000, 0b10001000, 0b10001000, 0b10101000, 0b10010000, 0b01101000, //R 0b11110000, 0b10001000, 0b10001000, 0b11110000, 0b10100000, 0b10010000, 0b10001000, //S 0b01111000, 0b10000000, 0b10000000, 0b01110000, 0b00001000, 0b00001000, 0b11110000, //T 0b11111000, 0b00100000, 0b00100000, 0b00100000, 0b00100000, 0b00100000, 0b00100000, //U 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b01110000, //V 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b01010000, 0b00100000, //W 0b10001000, 0b10001000, 0b10001000, 0b10101000, 0b10101000, 0b10101000, 0b01010000, //X 0b10001000, 0b10001000, 0b01010000, 0b00100000, 0b01010000, 0b10001000, 0b10001000, //Y 0b10001000, 0b10001000, 0b10001000, 0b01010000, 0b00100000, 0b00100000, 0b00100000, //Z 0b11111000, 0b00001000, 0b00010000, 0b00100000, 0b01000000, 0b10000000, 0b11111000, //bold characters //we were going to use bold characters but decided not to in //the final version //A 0b01111000, 0b11001100, 0b11001100, 0b11001100, 0b11111100, 0b11001100, 0b11001100, //B 0b11111000, 0b11001100, 0b11001100, 0b11111000, 0b11001100, 0b11001100, 0b11111000, //C 0b01111000, 0b11001100, 0b11000000, 0b11000000, 0b11000000, 0b11001100, 0b01111000, //D 0b11111000, 0b11001100, 0b11001100, 0b11001100, 0b11001100, 0b11001100, 0b11111000, //E 0b11111100, 0b11000000, 0b11000000, 0b11111100, 0b11000000, 0b11000000, 0b11111100, //F 0b11111100, 0b11000000, 0b11000000, 0b11111100, 0b11000000, 0b11000000, 0b11000000, //G 0b01111000, 0b11001100, 0b11000000, 0b11011100, 0b11001100, 0b11001100, 0b01111000, //H 0b11001100, 0b11001100, 0b11001100, 0b11111100, 0b11001100, 0b11001100, 0b11001100, //I 0b01111000, 0b00110000, 0b00110000, 0b00110000, 0b00110000, 0b00110000, 0b01111000, //J 0b00111100, 0b00011000, 0b00011000, 0b00011000, 0b00011000, 0b11011000, 0b01110000, //K 0b11001100, 0b11011000, 0b11110000, 0b11100000, 0b11110000, 0b11011000, 0b11001100, //L 0b11000000, 0b11000000, 0b11000000, 0b11000000, 0b11000000, 0b11000000, 0b11111100, //M 0b10001100, 0b11011100, 0b10101100, 0b10001100, 0b10001100, 0b10001100, 0b10001100, //N 0b11001100, 0b11001100, 0b11101100, 0b11111100, 0b11011100, 0b11001100, 0b11001100, //O 0b01111000, 0b11001100, 0b11001100, 0b11001100, 0b11001100, 0b11001100, 0b01111000, //P 0b11111000, 0b11001100, 0b11001100, 0b11111000, 0b11000000, 0b11000000, 0b11000000, //Q 0b01111000, 0b11001100, 0b11001100, 0b11001100, 0b11001100, 0b11011000, 0b01101100, //R 0b11111000, 0b11001100, 0b11001100, 0b11111000, 0b11110000, 0b11011000, 0b11001100, //S 0b01111100, 0b11000000, 0b11000000, 0b01111000, 0b00001100, 0b00001100, 0b11111000, //T 0b11111100, 0b00110000, 0b00110000, 0b00110000, 0b00110000, 0b00110000, 0b00110000, //U 0b11001100, 0b11001100, 0b11001100, 0b11001100, 0b11001100, 0b11001100, 0b01111000, //V 0b11001100, 0b11001100, 0b11001100, 0b11001100, 0b11001100, 0b01011000, 0b00100000, //W 0b10001100, 0b10001100, 0b10001100, 0b10101100, 0b10101100, 0b10101100, 0b01011000, //X 0b11001100, 0b11001100, 0b01011000, 0b00100000, 0b01011000, 0b11001100, 0b11001100, //Y 0b11001100, 0b11001100, 0b11001100, 0b01011000, 0b00110000, 0b00110000, 0b00110000, //Z 0b11111100, 0b00001100, 0b00011000, 0b00110000, 0b01100000, 0b11000000, 0b11111100, //end bold characters //blank 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, //Vertical Cursor 0b01000000, 0b11100000, 0b01000000, 0b01000000, 0b01000000, 0b11100000, 0b01000000, //horizontal cursor 0b01000100, 0b11111110, 0b01000100, 0b00000000, 0b00000000, 0b00000000, 0b00000000, //Arrow 0b10000000, 0b11000000, 0b11100000, 0b11110000, 0b11111000, 0b00100000, 0b00010000, //CLOCK 0b01111100, 0b10010010, 0b10010010, 0b10011010, 0b10000010, 0b10000010, 0b01111100, //Triangle 0b10000000, 0b11000000, 0b11100000, 0b11110000, 0b11100000, 0b11000000, 0b10000000 }; //================================ //3x5 font numbers, then letters //packed two per definition for fast //copy to the screen at x-position divisible by 4 flash char smallbitmap[42][5]={ //0 0b11101110, 0b10101010, 0b10101010, 0b10101010, 0b11101110, //1 0b01000100, 0b11001100, 0b01000100, 0b01000100, 0b11101110, //2 0b11101110, 0b00100010, 0b11101110, 0b10001000, 0b11101110, //3 0b11101110, 0b00100010, 0b11101110, 0b00100010, 0b11101110, //4 0b10101010, 0b10101010, 0b11101110, 0b00100010, 0b00100010, //5 0b11101110, 0b10001000, 0b11101110, 0b00100010, 0b11101110, //6 0b11001100, 0b10001000, 0b11101110, 0b10101010, 0b11101110, //7 0b11101110, 0b00100010, 0b01000100, 0b10001000, 0b10001000, //8 0b11101110, 0b10101010, 0b11101110, 0b10101010, 0b11101110, //9 0b11101110, 0b10101010, 0b11101110, 0b00100010, 0b01100110, //: 0b00000000, 0b01000100, 0b00000000, 0b01000100, 0b00000000, //= 0b00000000, 0b11101110, 0b00000000, 0b11101110, 0b00000000, //blank 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, //A 0b11101110, 0b10101010, 0b11101110, 0b10101010, 0b10101010, //B 0b11001100, 0b10101010, 0b11101110, 0b10101010, 0b11001100, //C 0b11101110, 0b10001000, 0b10001000, 0b10001000, 0b11101110, //D 0b11001100, 0b10101010, 0b10101010, 0b10101010, 0b11001100, //E 0b11101110, 0b10001000, 0b11101110, 0b10001000, 0b11101110, //F 0b11101110, 0b10001000, 0b11101110, 0b10001000, 0b10001000, //G 0b11101110, 0b10001000, 0b10001000, 0b10101010, 0b11101110, //H 0b10101010, 0b10101010, 0b11101110, 0b10101010, 0b10101010, //I 0b11101110, 0b01000100, 0b01000100, 0b01000100, 0b11101110, //J 0b00100010, 0b00100010, 0b00100010, 0b10101010, 0b11101110, //K 0b10001000, 0b10101010, 0b11001100, 0b11001100, 0b10101010, //L 0b10001000, 0b10001000, 0b10001000, 0b10001000, 0b11101110, //M 0b10101010, 0b11101110, 0b11101110, 0b10101010, 0b10101010, //N 0b00000000, 0b11001100, 0b10101010, 0b10101010, 0b10101010, //O 0b01000100, 0b10101010, 0b10101010, 0b10101010, 0b01000100, //P 0b11101110, 0b10101010, 0b11101110, 0b10001000, 0b10001000, //Q 0b01000100, 0b10101010, 0b10101010, 0b11101110, 0b01100110, //R 0b11101110, 0b10101010, 0b11001100, 0b11101110, 0b10101010, //S 0b11101110, 0b10001000, 0b11101110, 0b00100010, 0b11101110, //T 0b11101110, 0b01000100, 0b01000100, 0b01000100, 0b01000100, //U 0b10101010, 0b10101010, 0b10101010, 0b10101010, 0b11101110, //V 0b10101010, 0b10101010, 0b10101010, 0b10101010, 0b01000100, //W 0b10101010, 0b10101010, 0b11101110, 0b11101110, 0b10101010, //X 0b00000000, 0b10101010, 0b01000100, 0b01000100, 0b10101010, //Y 0b10101010, 0b10101010, 0b01000100, 0b01000100, 0b01000100, //Z 0b11101110, 0b00100010, 0b01000100, 0b10001000, 0b11101110, //special added characters //% symbol 0b10101010, 0b00100010, 0b01000100, 0b10001000, 0b10101010, //lives symbol a ball 0b01000100, 0b10101010, 0b01000100, 0b01000100, 0b10101010, 0b10001000, 0b10001000, 0b10001000, 0b00000000, 0b10001000 }; //================================== //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) { randomSeed++; //start the Horizontal sync pulse PORTD = syncON; //update the curent scanline number LineCount ++ ; //begin inverted (Vertical) synch after line 247 if (LineCount==248) { syncON = 0b00100000; syncOFF = 0; } //back to regular sync after line 250 if (LineCount==251) { syncON = 0; syncOFF = 0b00100000; } //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) { //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; // #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 //load 16 registers with screen info #asm push r14 push r15 push r16 push r17 push r18 push r19 push r26 push r27 ldi r26,low(_screen) ;base address of screen ldi r27,high(_screen) add r26,r12 ;offset into screen (add i) adc r27,r13 ld r4,x+ ;load 16 registers and inc pointer ld r5,x+ ld r6,x+ ld r7,x+ ld r8,x+ ld r9,x+ ld r10,x+ ld r11,x+ ld r12,x+ ld r13,x+ ld r14,x+ ld r15,x+ ld r16,x+ ld r17,x+ ld r18,x+ ld r19,x pop r27 pop r26 #endasm delay_us(4); //adjust to center image on screen //blast 16 bytes to the screen #asm ;but first a macro to make the code shorter ;the macro takes a register number as a parameter ;and dumps its bits serially to portD.6 ;the nop can be eliminated to make the display narrower .macro videobits ;regnum BST @0,7 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,6 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,5 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,4 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,3 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,2 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,1 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 BST @0,0 IN R30,0x12 BLD R30,6 nop OUT 0x12,R30 .endm videobits r4 ;video line -- byte 1 videobits r5 ;byte 2 videobits r6 ;byte 3 videobits r7 ;byte 4 videobits r8 ;byte 5 videobits r9 ;byte 6 videobits r10 ;byte 7 videobits r11 ;byte 8 videobits r12 ;byte 9 videobits r13 ;byte 10 videobits r14 ;byte 11 videobits r15 ;byte 12 videobits r16 ;byte 13 videobits r17 ;byte 14 videobits r18 ;byte 15 videobits r19 ;byte 16 clt ;clear video after the last pixel on the line IN R30,0x12 BLD R30,6 OUT 0x12,R30 pop r19 pop r18 pop r17 pop r16 pop r15 pop r14 #endasm } } #pragma warn+ //================================== //plot one point //at x,y with color 1=white 0=black 2=invert #pragma warn- inline void video_pt(char x, char y, char c) { #asm ; i=(x>>3) + ((int)y<<4) ; the byte with the pixel in it push r16 ldd r30,y+2 ;get x lsr r30 lsr r30 lsr r30 ;divide x by 8 ldd r12,y+1 ;get y lsl r12 ;mult y by 16 clr r13 lsl r12 rol r13 lsl r12 rol r13 lsl r12 rol r13 add r12, r30 ;add in x/8 ;v2 = screen[i]; r5 ;v3 = pos[x & 7]; r6 ;v4 = c r7 ldi r30,low(_screen) ldi r31,high(_screen) add r30, r12 adc r31, r13 ld r5,Z ;get screen byte ldd r26,y+2 ;get x ldi r27,0 andi r26,0x07 ;form x & 7 ldi r30,low(_pos*2) ldi r31,high(_pos*2) add r30,r26 adc r31,r27 lpm r6,Z ld r16,y ;get c ;if (v4==1) screen[i] = v2 | v3 ; ;if (v4==0) screen[i] = v2 & ~v3; ;if (v4==2) screen[i] = v2 ^ v3 ; cpi r16,1 brne tst0 or r5,r6 tst0: cpi r16,0 brne tst2 com r6 and r5,r6 tst2: cpi r16,2 brne writescrn eor r5,r6 writescrn: ldi r30,low(_screen) ldi r31,high(_screen) add r30, r12 adc r31, r13 st Z, r5 ;write the byte back to the screen pop r16 #endasm } #pragma warn+ //we use inline functions for speed //================================== // put a 7x5 character on the screen // c is index into bitmap inline void video_putchar(char x, char y, char c) { v7 = x; for (v6=0;v6<7;v6++) { v1 = bitmap[c][v6]; v8 = y+v6; video_pt(v7, v8, (v1 & 0x80)==0x80); video_pt(v7+1, v8, (v1 & 0x40)==0x40); video_pt(v7+2, v8, (v1 & 0x20)==0x20); video_pt(v7+3, v8, (v1 & 0x10)==0x10); video_pt(v7+4, v8, (v1 & 0x08)==0x08); } } //puts a 7X7 character onto the screen //must be a capital letter //to bold, add 26 to the input character c inline void video_putbigchar(char x, char y, char c) { v7 = x; for (v6=0;v6<7;v6++) { v1 = bitmap[c][v6]; v8 = y+v6; video_pt(v7, v8, (v1 & 0x80)==0x80); video_pt(v7+1, v8, (v1 & 0x40)==0x40); video_pt(v7+2, v8, (v1 & 0x20)==0x20); video_pt(v7+3, v8, (v1 & 0x10)==0x10); video_pt(v7+4, v8, (v1 & 0x08)==0x08); video_pt(v7+5, v8, (v1 & 0x04)==0x04); video_pt(v7+6, v8, (v1 & 0x02)==0x02); } } //this sets the current note that should be played //used to play the sound effects inline void PlayNote(char noteToBePlayed) { currentNote = noteToBePlayed; if (soundOnOff)//if the sound is on then reset musicT to 0 to play the note musicT = 0; } //this function puts back the state that was saved behind the cursor //this is to ensure that the cursor doesn't interfere with the logic //of the game inline void video_putbackstate() { v7 = cursorPosX; if (mainState==GAME_OPTIONS) { for (v6=0;v6<7;v6++) { v1 = stateSavedBehindCursor[v6]; v8 = cursorPosY+v6; video_pt(v7, v8, (v1 & 0x80)==0x80); video_pt(v7+1, v8, (v1 & 0x40)==0x40); video_pt(v7+2, v8, (v1 & 0x20)==0x20); video_pt(v7+3, v8, (v1 & 0x10)==0x10); video_pt(v7+4, v8, (v1 & 0x08)==0x08); video_pt(v7+5, v8, (v1 & 0x04)==0x04); video_pt(v7+6, v8, (v1 & 0x02)==0x02); } } //the different cursors must put back different parts of the state //because they are different sizes else if (currentDirection==VERTICAL) { for (v6=0;v6=0x30 && str[i]<=0x3a) video_putchar(x,y,str[i]-0x30); else if(str[i] == 0x20) video_putchar(x,y, Blank); else video_putchar(x,y,str[i]-0x40+9); x = x+6; } } //================================== //the following functions are used because the variables are stored //in flash and require specific functions to print them out //we were unable to pass in pointers using the normal functions //so we developed these as a quick fix //================================== //print the title inline void video_putTitleJezzBall(char x, char y) { char i ; for (i=0; titleJezzBall[i]!=0; i++) { if (titleJezzBall[i]>=0x30 && titleJezzBall[i]<=0x3a) video_putchar(x,y,titleJezzBall[i]-0x30); else video_putchar(x,y,titleJezzBall[i]-0x40+9); x = x+6; } } //print ECE 476 inline void video_putTitleECE476(char x, char y) { char i ; for (i=0; titleECE476[i]!=0; i++) { if (titleECE476[i]>=0x30 && titleECE476[i]<=0x3a) video_putchar(x,y,titleECE476[i]-0x30); else video_putchar(x,y,titleECE476[i]-0x40+9); x = x+6; } } inline void video_putLoseStr(char x, char y) { char i ; for (i=0; loseStr[i]!=0; i++) { if (loseStr[i]>=0x30 && loseStr[i]<=0x3a) video_putchar(x,y,loseStr[i]-0x30); else video_putchar(x,y,loseStr[i]-0x40+9); x = x+6; } } inline void video_putWinStr(char x, char y) { char i ; for (i=0; successStr[i]!=0; i++) { if (successStr[i]>=0x30 && successStr[i]<=0x3a) video_putchar(x,y,successStr[i]-0x30); else video_putchar(x,y,successStr[i]-0x40+9); x = x+6; } } //================================== // put a small character on the screen // x-cood must be on divisible by 4 // c is index into bitmap inline void video_smallchar(char x, char y, char c) { char mask; i=((int)x>>3) + ((int)y<<4) ; if (x == (x & 0xf8)) mask = 0x0f; //f8 else mask = 0xf0; screen[i] = (screen[i] & mask) | (smallbitmap[c][0] & ~mask); screen[i+16] = (screen[i+16] & mask) | (smallbitmap[c][1] & ~mask); screen[i+32] = (screen[i+32] & mask) | (smallbitmap[c][2] & ~mask); screen[i+48] = (screen[i+48] & mask) | (smallbitmap[c][3] & ~mask); screen[i+64] = (screen[i+64] & mask) | (smallbitmap[c][4] & ~mask); } //================================== // put a string of small characters on the screen // x-cood must be on divisible by 4 inline void video_putsmalls(char x, char y, char *str) { char i ; for (i=0; str[i]!=0; i++) { if (str[i]>=0x30 && str[i]<=0x3a) video_smallchar(x,y,str[i]-0x30); else if(str[i] == 0x20) video_smallchar(x,y, 12); else video_smallchar(x,y,str[i]-0x40+12); x = x+4; } } //================================== //plot a line //at x1,y1 to x2,y2 with color 1=white 0=black 2=invert //NOTE: this function requires signed chars //Code is from David Rodgers, //"Procedural Elements of Computer Graphics",1985 inline void video_line(char x1, char y1, char x2, char y2, char c) { int e; signed char dx,dy,j, temp; signed char s1,s2, xchange; signed char x,y; x = x1; y = y1; dx = cabs(x2-x1); dy = cabs(y2-y1); s1 = csign(x2-x1); s2 = csign(y2-y1); xchange = 0; if (dy>dx) { temp = dx; dx = dy; dy = temp; xchange = 1; } e = ((int)dy<<1) - dx; for (j=0; j<=dx; j++) { video_pt(x,y,c) ; if (e>=0) { if (xchange==1) x = x + s1; else y = y + s2; e = e - ((int)dx<<1); } if (xchange==1) y = y + s2; else x = x + s1; e = e + ((int)dy<<1); } } //================================== //return the value of one point //at x,y with 0= no point and !=0 as a white point inline char video_set(char x, char y) { //The following construction //detects exactly one bit at the x,y location i=((int)x>>3) + ((int)y<<4) ; return ( screen[i] & 1<<(7-(x & 0x7))); } //=============================== //these functions put the small chars out using specific variables //stored in flash memory //=============================== inline void video_putScore(char x, char y) { char i ; for (i=0; scoreStr[i]!=0; i++) { if (scoreStr[i]>=0x30 && scoreStr[i]<=0x3a) video_smallchar(x,y,scoreStr[i]-0x30); else if(scoreStr[i] == 0x20) video_smallchar(x,y, 12); else video_smallchar(x,y,scoreStr[i]-0x40+12); x = x+4; } } inline void video_putArea(char x, char y) { char i ; for (i=0; areaStr[i]!=0; i++) { if (areaStr[i]>=0x30 && areaStr[i]<=0x3a) video_smallchar(x,y,areaStr[i]-0x30); else if(areaStr[i] == 0x20) video_smallchar(x,y, 12); else video_smallchar(x,y,areaStr[i]-0x40+12); x = x+4; } } inline void video_putLevel(char x, char y) { char i ; for (i=0; levelStr[i]!=0; i++) { if (levelStr[i]>=0x30 && levelStr[i]<=0x3a) video_smallchar(x,y,levelStr[i]-0x30); else if(levelStr[i] == 0x20) video_smallchar(x,y, 12); else video_smallchar(x,y,levelStr[i]-0x40+12); x = x+4; } } inline void video_putLives(char x, char y) { char i ; for (i=0; livesStr[i]!=0; i++) { if (livesStr[i]>=0x30 && livesStr[i]<=0x3a) video_smallchar(x,y,livesStr[i]-0x30); else if(livesStr[i] == 0x20) video_smallchar(x,y, 12); else video_smallchar(x,y,livesStr[i]-0x40+12); x = x+4; } } inline void video_putLose(char x, char y) { char i ; for (i=0; loseStr[i]!=0; i++) { if (loseStr[i]>=0x30 && loseStr[i]<=0x3a) video_smallchar(x,y,loseStr[i]-0x30); else if(loseStr[i] == 0x20) video_smallchar(x,y, 12); else video_smallchar(x,y,loseStr[i]-0x40+12); x = x+4; } } inline void video_putSuccess(char x, char y) { char i ; for (i=0; successStr[i]!=0; i++) { if (successStr[i]>=0x30 && successStr[i]<=0x3a) video_smallchar(x,y,successStr[i]-0x30); else if(successStr[i] == 0x20) video_smallchar(x,y, 12); else video_smallchar(x,y,successStr[i]-0x40+12); x = x+4; } } inline void video_putTimeBonus(char x,char y) { char i ; for (i=0; timeBonusStr[i]!=0; i++) { if (timeBonusStr[i]>=0x30 && timeBonusStr[i]<=0x3a) video_smallchar(x,y,timeBonusStr[i]-0x30); else if(timeBonusStr[i] == 0x20) video_smallchar(x,y, 12); else video_smallchar(x,y,timeBonusStr[i]-0x40+12); x = x+4; } } inline void video_putFillBonus(char x,char y) { char i ; for (i=0; fillBonusStr[i]!=0; i++) { if (fillBonusStr[i]>=0x30 && fillBonusStr[i]<=0x3a) video_smallchar(x,y,fillBonusStr[i]-0x30); else if(fillBonusStr[i] == 0x20) video_smallchar(x,y, 12); else video_smallchar(x,y,fillBonusStr[i]-0x40+12); x = x+4; } } inline void video_putNewHighScore(char x, char y) { char i ; for (i=0; newHighScoreStr[i]!=0; i++) { if (newHighScoreStr[i]>=0x30 && newHighScoreStr[i]<=0x3a) video_smallchar(x,y,newHighScoreStr[i]-0x30); else if(newHighScoreStr[i] == 0x20) video_smallchar(x,y, 12); else video_smallchar(x,y,newHighScoreStr[i]-0x40+12); x = x+4; } } inline void video_putCongrats(char x, char y) { char i ; for (i=0; congratsStr[i]!=0; i++) { if (congratsStr[i]>=0x30 && congratsStr[i]<=0x3a) video_smallchar(x,y,congratsStr[i]-0x30); else if(congratsStr[i] == 0x20) video_smallchar(x,y, 12); else video_smallchar(x,y,congratsStr[i]-0x40+12); x = x+4; } } //this function randomizes all the ball positions to begin with depending on the global variable curNumOfBalls // void randomnizeBalls() { srand(randomSeed);//randomly set the seed to the randomSeed (which is incremented in timer1 for(count=0;count=0;v6--) { v7 = cursorPosX + v6; if(video_set(v7, v8)!=0) { // video_pt(v7+10,v8,1); stateSavedBehindCursor[v5] |= v4; } v4 <<=1 ;//shift to the left } } } else if (currentDirection==VERTICAL) { for (v5=0;v5<7;v5++) { stateSavedBehindCursor[v5] = 0b00000000; //clear initially v8 = cursorPosY+v5; v4=0x08; for(v6=2;v6>=0;v6--) { v7 = cursorPosX + v6; if(video_set(v7, v8)!=0) { // video_pt(v7+10,v8,1); stateSavedBehindCursor[v5] |= v4; } v4 <<=1 ;//shift to the left } } } else { for (v5=0;v5<3;v5++) { stateSavedBehindCursor[v5] = 0b00000000; //clear initially v8 = cursorPosY+v5; v4=0x02; for(v6=6;v6>=0;v6--) { v7 = cursorPosX + v6; if(video_set(v7, v8)!=0) { // video_pt(v7+10,v8,1); stateSavedBehindCursor[v5] |= v4; } v4 <<=1 ;//shift to the left } } } } //this function is used to clear variables to ensure a clean //start to each new level inline void clearVariables() { t=0; time=100; //initialize states mainState = START_SCREEN; clickedPositionX = 0; clickedPositionY = 0; movingPositionOneX = 0; movingPositionOneY = 0; movingPositionTwoX = 0; movingPositionTwoY = 0; clickedDirection = 0; // 0 for horizontal and 1 for vertical movingLine0 = 0; movingLine1 = 0; //initialize and draw cursor position //cursor variables button=0; newButton=button; clickedOnOff = 0; currentArea = totalArea; boundaryPoint1X[0] = 0; boundaryPoint1Y[0] = 11; boundaryPoint2X[0] = 126; boundaryPoint2Y[0] = 89; for(count =1; count< MAX_BOUNDARIES; count++) { boundaryPoint1X[count] = -1; boundaryPoint1Y[count] = -1; boundaryPoint2X[count] = -1; boundaryPoint2Y[count] = -1; } for(count=0;count movingPositionTwoX)) { return count; } } } //returns the index of a boundary hit for a horizontal cut inline char IndexOfBoundaryHitForHorizLine() { //since its a horizontal line only check whether left and right have hit for(count=0;count movingPositionTwoY)) { return count; } } } //finds the index of the first open boundary inline char IndexOfOpenBoundary() { for(count=0;count boundaryPoint1X[count] && cursorPosY+3 > boundaryPoint1Y[count] && cursorPosX+1 boundaryPoint1X[count] && cursorPosY+1 > boundaryPoint1Y[count] && cursorPosX+3= movingPositionOneX && tempBallNextX <= movingPositionTwoX) { currentLives--; movingLine0 = 0; movingLine1 = 0; video_line(movingPositionOneX, clickedPositionY, movingPositionTwoX, clickedPositionY, 0); PlayNote(BALL_HIT_CUT); } } else //vertical direction { //if it has hit the line if(tempBallNextX == clickedPositionX && tempBallNextY >= movingPositionOneY && tempBallNextY <= movingPositionTwoY) { currentLives--; movingLine0 = 0; movingLine1 = 0; video_line(clickedPositionX, movingPositionOneY, clickedPositionX, movingPositionTwoY, 0); PlayNote(BALL_HIT_CUT); } } } //if the next spot that the ball is suposed to be in is blank then update the position if(video_set(ballPosX[count] + ballVelX[count], ballPosY[count] + ballVelY[count]) == 0 ) { //then update the position of ball ballPosX[count] += ballVelX[count]; ballPosY[count] += ballVelY[count]; } else { //depending on whether the pixels next to the ball are white or not //it changes the direction of the ball if(video_set(ballPosX[count] + ballVelX[count], ballPosY[count]) == 0 && video_set(ballPosX[count], ballPosY[count] + ballVelY[count]) == 0) { /*sprintf(ts,"%02d",count); video_putsmalls(4+count*12,93,ts); */ ballVelX[count] = -ballVelX[count]; ballVelY[count] = -ballVelY[count]; } else { //if the pixel in the X direction is set, then reverse the x direction //(means its a line in the vertical direction if(video_set(ballPosX[count] + ballVelX[count], ballPosY[count]) != 0) { ballVelX[count] = -ballVelX[count]; } //if the pixel in teh Y direction is set, then reverse the y direction if(video_set(ballPosX[count], ballPosY[count] + ballVelY[count]) != 0) { ballVelY[count] = -ballVelY[count]; } PlayNote(BALL_HIT_WALL); //play a not to indicate that the ball hit the wall } } video_pt(ballPosX[count],ballPosY[count],1) ;//draw the ball back } } //this function gets the input from the second MCU using SPI inline void getMouseInput() { sendDirection=(unsigned char)(currentDirection==VERTICAL)+1; SS=0; SPDR=sendDirection; //have to send something on the SPI, this doens't matter //get the first byte, the x position while(!(SPSR&0b10000000));//waits until the transfer of data is complete //status indicates complete in SPSR register newCursorPosX=(unsigned) SPDR; SS=1;//reset SPI SS=0; SPDR=sendDirection; //get the second byte, the y position while(!(SPSR&0b10000000));//waits until the transfer of data is complete newCursorPosY=(unsigned) SPDR; SS=1; SS=0; SPDR=sendDirection; while(!(SPSR&0b10000000));//waits until the transfer of data is complete newButton=(unsigned) SPDR; SS=1; leftButtonPressed = 0; rightButtonPressed = 0; //left button is pressed //this code is for debouncing of the button presses //holding the button down will increment the respective buttonHeld variable if(newButton & 0x01) { leftButtonHeld++; } else if(newButton & 0x02) { rightButtonHeld++; } else { //if any of the buttons are released, check whether it has been held down if(leftButtonHeld > 0)//if the left button was being held down { leftButtonHeld = 0; rightButtonHeld = 0; rightButtonPressed = 0; leftButtonPressed = 1; } else if(rightButtonHeld >0)//if the right button was being held down { leftButtonHeld = 0; rightButtonHeld = 0; leftButtonPressed = 0; rightButtonPressed = 1; } } } //this function updates the cursor position from the current mouse position //it also checks whether or not a button was pressed to start to generate a cut //it has the logic to check if a cut was complete or it hit a ball and //if the cut has completed, it calculates the new boundaries and sets up //any boundaries to be erased inline void UpdateAndDisplayCursor() { //update the cursor positions cursorPosX=newCursorPosX; cursorPosY=newCursorPosY; button=newButton; //these set the boundaries to be speficfic to the orientations of the cursor //the vertical cursor can go closer to the right and left side //the horizontal cursor can go closer to the top and bottom if (currentDirection==VERTICAL) { if (cursorPosX>CURSOR_X_MAX+4) cursorPosX=CURSOR_X_MAX+4; // limit cursor to within the screen if (cursorPosXCURSOR_Y_MAX) cursorPosY=CURSOR_Y_MAX; if (cursorPosYCURSOR_X_MAX) cursorPosX=CURSOR_X_MAX; // limit cursor to within the screen if (cursorPosXCURSOR_Y_MAX+4) cursorPosY=CURSOR_Y_MAX+4; if (cursorPosYCURSOR_X_MAX-20) cursorPosX=CURSOR_X_MAX-20; // limit cursor to within the screen if (cursorPosXCURSOR_Y_MAX-20) cursorPosY=CURSOR_Y_MAX-20; if (cursorPosY=STARTHORIZTOP && cursorPosY<=STARTHORIZTOP+7 && cursorPosX>=STARTVERTLEFT && cursorPosX<=STARTVERTLEFT+60) return START; if (cursorPosY>=STARTHORIZTOP+10 && cursorPosY<=STARTHORIZTOP+17 && cursorPosX>=SOUNDVERTLEFT && cursorPosX<=SOUNDVERTLEFT+54) return SOUND; if (cursorPosY>=STARTHORIZTOP+20 && cursorPosY<=STARTHORIZTOP+27 && cursorPosX>=LIVESVERTLEFT && cursorPosX<=LIVESVERTLEFT+42) return LIVES; if (cursorPosY>=STARTHORIZTOP+30 && cursorPosY<=STARTHORIZTOP+37 && cursorPosX>=SCOREVERTLEFT && cursorPosX<=SCOREVERTLEFT+60) return SCORE; return 0; } //This function plays the lose sound, it has certain dependencies before it can be called //need to set noteCount = 0; //need to set isPlaying = 1; //need to set currentNote = loseNotes[0]; //need to set musicT = 0 //cannot be playing any other music at the time void PlayLose() { //if its playing and the overall sound is turned on if(isPlaying && soundOnOff) { musicT++; if(currentNote > 0) TCCR0 = 0b00011100; OCR0 = currentNote; if(musicT == 8) { //check if the current note playing is less than the amount of notes played if(noteCount < LOSE_NOTES_LENGTH) { //update the current note currentNote = loseNotes[++noteCount]; musicT = 0; } else { isPlaying = 0; } } } else { //turn off the sound TCCR0 = 0; } } //This function plays the win sound, it has certain dependencies before it can be called //need to set noteCount = 0; //need to set isPlaying = 1; //need to set currentNote = winNotes[0]; //need to set musicT = 0 //cannot be playing any other music at the time void PlayWin() { //if its playing and the overall sound is turned on if(isPlaying && soundOnOff) { musicT++; if(currentNote > 0) TCCR0 = 0b00011100; OCR0 = currentNote; if(musicT == 8) { //check if the current note playing is less than the amount of notes played if(noteCount< WIN_NOTES_LENGTH) { //update the current note currentNote = winNotes[++noteCount]; musicT = 0; } else { isPlaying = 0; } } } else { //turn off the sound TCCR0 = 0; } } //this function updates and displays the cursor during the menu screen //it updates in the new position given by the mouse and updates the options //when the mouse is clicking over the correct area inline void UpdateAndDisplayArrow() { cursorPosX=newCursorPosX; cursorPosY=newCursorPosY; button=newButton; //boundary conditions for the cursor if (cursorPosX>CURSOR_X_MAX) cursorPosX=CURSOR_X_MAX; video_putbigchar(STARTVERTLEFT-8, STARTHORIZTOP+(isMenuClicked-1)*10, Blank); isMenuClicked=checkMenuClicked(); //this puts the triangle indicating which menu option the mouse is currently hovering over if (isMenuClicked) video_putbigchar(STARTVERTLEFT-8, STARTHORIZTOP+(isMenuClicked-1)*10, TRIANGLE); //this portion updates the logic for each of the options selected if (leftButtonPressed && isMenuClicked){ leftButtonPressed=0; switch(isMenuClicked) { //to start the game set up certain variables case START: clearVariables(); //setup variables curLevel=1; curNumOfBalls=1; currentLives--;//this is for displaying purposes, we display "extra" lives currentDirection=VERTICAL; mainState=CLEAR_ENTIRE_SCREEN; stateAfterClearEntireScreen=DRAW_LINES; clearingIndex=CLEAR_ENTIRE_SCREEN_START; currentScore = 0; break; case SOUND: musicT = 16; soundOnOff^=1; //upon a click, turn the sound on or off if(soundOnOff) { sprintf(ts, "ON "); video_puts(SOUNDVERTLEFT+36,STARTHORIZTOP+10, ts); } else { sprintf(ts, "OFF"); video_puts(SOUNDVERTLEFT+36,STARTHORIZTOP+10, ts); } break; case LIVES: //increment the current number of lives currentLives++; if (currentLives>5) currentLives=1; sprintf(ts, "%d", currentLives); video_puts(LIVESVERTLEFT+36, STARTHORIZTOP+20, ts); break; case SCORE: //go see the current high score hsCount=0; stateAfterClear = HIGH_SCORE; mainState = CLEAR_SCREEN; clearingIndex= CLEARING_START; break; } } } //this function staggers the putting of the menu options over a few frames //it draws big chars in the proper places inline void video_putmenu() { if (hsCount==0) { video_putTitleJezzBall(37,5); hsCount++; } else if(hsCount==1) { sprintf(ts, "START GAME"); video_puts( STARTVERTLEFT, STARTHORIZTOP, ts); hsCount++; } else if (hsCount==2) { if(soundOnOff) { sprintf(ts, "SOUND ON"); video_puts(SOUNDVERTLEFT,STARTHORIZTOP+10, ts); } else { sprintf(ts, "SOUND OFF"); video_puts(SOUNDVERTLEFT,STARTHORIZTOP+10, ts); } hsCount++; } else if (hsCount==3) { sprintf(ts, "LIVES %d", currentLives); video_puts( LIVESVERTLEFT,STARTHORIZTOP+20, ts); hsCount++; } else if (hsCount==4) { sprintf(ts, "HIGH SCORE"); video_puts(SCOREVERTLEFT,STARTHORIZTOP+30, ts); hsCount++; } else if (hsCount==5) mainState = GAME_OPTIONS; } //================================== // set up the ports and timers void main(void) { PORTC = 0xFF; initialize(); PORTC.0 = 0; //reset music to be off musicT = 20; 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 //Code here can be as long as //a total of 60 lines x 63.5 uSec/line x 8 cycles/uSec if (LineCount==231) { //first read in data from the mouse getMouseInput(); //main state machine switch(mainState) { case START_SCREEN: //start screen prints out JEZZBALL and menu options video_putmenu(); break; case GAME_OPTIONS: //put back the current state behind the cursor video_putbackstate(); //update the position of the arrow and check if any option is selected UpdateAndDisplayArrow(); //save the state behind the arrow video_savestate(); //draw the arrow again video_putarrow(); break; case DRAW_LINES: //draw the lines around the screen staggered over 2 frames //only can draw 3 large lines per frame #define width 126 if(hsCount == 0) { video_line(0,0,0,99,1); video_line(width,0,width,99,1); video_line(0,0,width,0,1); hsCount++; } else { //top line & bottom lines video_line(0,99,width,99,1); video_line(0,11,width,11,1); video_line(0,89,width,89,1); mainState = SET_UP_LEVEL; } break; case SET_UP_LEVEL: //prints out stuff to the screen for the level //top left corner put the lives string video_putLives(4,4); //top right put level string video_putLevel(52,4); sprintf(ts,"%-2d",curLevel); video_putsmalls(68,4,ts); //bottom left put score string video_putScore(4,93); video_putbigchar(105,2,CLOCK); //bottrom right put area remaining video_putArea(68,93); randomnizeBalls(); //randomize ball positions and velocity mainState = GAME_PLAYING; break; case GAME_PLAYING: //TCCR0 bits: //bit 7 no force: //bit 3 CTC on: //bit 5,4 toggle OC0 pin //bit 6 no PWM //bit 2-0 prescaler 256 so 1 tick is 16 microsec //if a tone is queued up it will play it here if(musicT < 1 && soundOnOff) { musicT++; if(currentNote > 0) TCCR0 = 0b00011100; OCR0 = currentNote; } else//turn off when not queued up { TCCR0 = 0; } //put back state video_putbackstate(); //erase boundaries that are queued up to be erased ErasingBoundary(); DrawBalls(); //update ball positions and draw UpdateAndDisplayCursor(); //update cursor and logic for lines //save state video_savestate(); //put back cursor video_putcursor(); //calculate points, score, win //time decremented for time bonus if (++t>59) { t=0; if(time>0) time--; sprintf(ts,"%3d",time); video_putsmalls(112,4,ts); } //calculate the new percentage newAreaPercentage = (char) (((long) ((long)currentArea*100))/(long)totalArea); //if the percentage changed, then change the score if(newAreaPercentage < currentAreaPercentage) { currentScore += (currentAreaPercentage - newAreaPercentage)*curLevel*5; currentAreaPercentage = newAreaPercentage; } sprintf(ts,"%3d",currentAreaPercentage); //draw the % symbol (specially created) video_putsmalls(108,93,ts); video_smallchar(120,93,PERCENT_INDEX); //if the current amount of lives is less than 0 then lose if(currentLives < 0) { //clear the screen first and then go to lose state mainState = POINT_CLEARING_STATE; stateAfterPointState = LOSE_SCREEN; pointClearingIndex = POINT_CLEARING_START; } //display lives for(count=0;count<5;count++) { if(count>=currentLives) { video_smallchar(28+(count<<2),4,SMALLBLANK_INDEX); } else { video_smallchar(28+(count<<2),4,LIVES_INDEX); } } //print current score sprintf(ts,"%05d",currentScore); video_putsmalls(28,93,ts); break; case WIN_STATE: //if the player is only level 10 and they win, the go to BEAT GAME if(curLevel == 10) { mainState = BEAT_GAME; } else { //put the sucess string out video_putSuccess(48,30); PlayWin();//play the win notes //afterwards check if the left button is pressed to go on to the //next state if(leftButtonPressed) { leftButtonPressed = 0; clearVariables(); //clear variables for new level curLevel++; curNumOfBalls++; //clear level first and then go to next level stateAfterClear = SET_UP_LEVEL; mainState = CLEAR_SCREEN; clearingIndex= CLEARING_START; musicT=16;//turn off music TCCR0 = 0; } } break; case LOSE_SCREEN: PlayLose();//play losing sound video_putLose(48,30);//put lose string out //check if they achieved a new high score, if so update //and display new high score string if(currentScore > highScore) { //print out new high score. video_putNewHighScore(32,70); video_smallchar(88,70, EXCLAIM); highScore = currentScore; } //wait for a mouse click before going to next state if(leftButtonPressed) { leftButtonPressed = 0; //clear the screen first before going to the start screen stateAfterClearEntireScreen = START_SCREEN; mainState = CLEAR_ENTIRE_SCREEN; clearingIndex= CLEAR_ENTIRE_SCREEN_START; curLevel = 1; curNumOfBalls = 1; currentLives = 4; musicT=16; TCCR0 = 0; } break; case CLEAR_SCREEN: //this state clears the screen (not clearing the top and bottom) //if it has reached the end of the clearing if(clearingIndex == CLEARING_END) { //go to the state after clearing mainState = stateAfterClear; stateAfterClear = -1; cursorPosX = 20;//set cursor to be in a position that we know to be black cursorPosY = 20; clearState();//clear state behind cursor to remove lingering pixels hsCount=0; } else { //continue to clear 3 lines at a time for(count=0;count<3;count++) { if(clearingIndex < CLEARING_END) { video_line(1,clearingIndex,125,clearingIndex,0); clearingIndex++; } } } break; case CLEAR_ENTIRE_SCREEN: //this clears the entire screen //if it has finished clearing if(clearingIndex == CLEAR_ENTIRE_SCREEN_END) { //go to the state it is supposed to go after clearing mainState = stateAfterClearEntireScreen; stateAfterClearEntireScreen = -1; cursorPosX = 20;//set mouse position to be in a known "black" area cursorPosY = 20; clearState();//clear state behind mouse hsCount=0; } else { //clear 3 lines at a time per frame for(count=0;count<3;count++) { if(clearingIndex < CLEAR_ENTIRE_SCREEN_END) { video_line(0,clearingIndex,126,clearingIndex,0); clearingIndex++; } } } break; case BEAT_GAME: PlayWin();//play the win notes video_putCongrats(36,30); //put out new winning message //many exclaimation marks for beating the game =P video_smallchar(64,30, EXCLAIM); video_smallchar(68,30, EXCLAIM); video_smallchar(72,30, EXCLAIM); video_smallchar(76,30, EXCLAIM); video_smallchar(80,30, EXCLAIM); //if they have a high score, update high score if(currentScore > highScore) { //print out new high score. video_putNewHighScore(32,70); video_smallchar(88,70, EXCLAIM); highScore = currentScore; } //wait until user clicks left mouse button to go to next screen if(leftButtonPressed) { leftButtonPressed = 0; stateAfterClearEntireScreen = START_SCREEN; mainState = CLEAR_ENTIRE_SCREEN; clearingIndex= CLEAR_ENTIRE_SCREEN_START; curLevel = 1; curNumOfBalls = 1; currentLives = 4; musicT=16; OCR0=0; } break; case HIGH_SCORE: //display current high score over multiple frames if (hsCount==0) { sprintf(ts, "CURRENT HI"); video_puts(10,20, ts); hsCount++; } else if (hsCount==1) { sprintf(ts,"GH SCORE"); video_puts(70, 20, ts); hsCount++; } else if (hsCount==2) { sprintf(ts, "%d", highScore); video_puts(50, 30, ts); hsCount++; } if (leftButtonPressed) { leftButtonPressed=0; mainState=CLEAR_SCREEN; stateAfterClear = START_SCREEN; clearingIndex= CLEARING_START; } break; case POINT_CLEARING_STATE: //clear a clear an area for points if(pointClearingIndex >= POINT_CLEARING_END) { //go to next state after clearing part of the screen mainState = POINT_STATE; } else { //clear 4 lines at a time for(count=0;count<4;count++) { video_line(30,pointClearingIndex,93,pointClearingIndex,0); pointClearingIndex++; } } break; case POINT_STATE: //display the points that have been won at the end of the level video_putTimeBonus(32,40); video_putFillBonus(32,50); //if its a loss, there is no bonus gained if(stateAfterPointState == LOSE_SCREEN) { timeBonus = 0; fillBonus = 0; } else { //calculate time bonus timeBonus = time*curLevel/2; } sprintf(ts,"%4d",timeBonus); video_putsmalls(76,40,ts); //calculated fill bonus if(currentAreaPercentage < 25) { fillBonus = (25-currentAreaPercentage)*curLevel*5; } else { fillBonus = 0; } //put out the strings for the bonuses sprintf(ts,"%4d",fillBonus); video_putsmalls(76,50,ts); currentScore += timeBonus + fillBonus; video_putScore(32,60); sprintf(ts,"%5d",currentScore); video_putsmalls(72,60,ts); //set up music variables noteCount = 0; isPlaying = 1; if(stateAfterPointState == LOSE_SCREEN) { currentNote = loseNotes[0]; } if(stateAfterPointState == WIN_STATE) { currentNote = winNotes[0]; } musicT = 0; //go to the state after the point state mainState = stateAfterPointState; stateAfterPointState = -1; break; default: break; } } //line 231 } //while } //main