//Matt Pokrzywa - mjp63 //James Du - jsd46 //ECE 476 Final Project: Light Cycles Code #include #pragma savereg- #pragma regalloc- //color definitions #define black 0x00 #define blue 0b00001000 #define green 0b00000100 #define red 0b00000011 #define darkgreen 0b00000001 #define yellow 0b00000111 #define purple 0b00001100 #define turq 0b00001011 #define white 0b00001111 #define begin { #define end } //game constants #define initxdec 4 #define initx2dec 59 #define initydec 22 #define initxfix 0x0400 #define initx2fix 0x3b00 #define inityfix 0x1600 #define velxfix 0x0002 #define velyfix 0 #define acc 0x0001 #define rCount 1000 #define aCount 25 //controller moves #define NONE 0 #define LEFT 1 #define RIGHT 2 #define UP 3 #define DOWN 4 #define ADCTIME 100 //debounce states #define NOT_TURNING 0 #define TURN 1 #define TURNING 2 #define STILL_TURNING1 3 #define STILL_TURNING2 4 #define STILL_TURNING3 5 // Letter definitions #define aLetter 10 #define bLetter 11 #define cLetter 12 #define dLetter 13 #define eLetter 14 #define fLetter 15 #define gLetter 16 #define hLetter 17 #define iLetter 18 #define jLetter 19 #define kLetter 20 #define lLetter 21 #define mLetter 22 #define nLetter 23 #define oLetter 24 #define pLetter 25 #define qLetter 26 #define rLetter 27 #define sLetter 28 #define tLetter 29 #define uLetter 30 #define vLetter 31 #define wLetter 32 #define xLetter 33 #define yLetter 34 #define zLetter 35 #define oneNum 36 #define twoNum 37 /********Program Definitions*************/ // Program letter locations #define redLocationX 6 #define redLocationY 30 #define greenLocationX 16 #define greenLocationY 30 #define blueLocationX 26 #define blueLocationY 30 // Program Color locations #define redColorX 5 #define redColorY 40 #define greenColorX 15 #define greenColorY 40 #define blueColorX 25 #define blueColorY 40 // Button definitions #define inputUp 128 #define inputDown 64 #define inputLeft 32 #define inputRight 16 #define inputSet 8 #define inputClear 4 #define inputTab 2 // Slows down input #define readyCountMax 15 // Cursor flash speed #define cursorCountMax 15 unsigned int resetCount; char resetCount2; char s, charx, chary, finish, adcCount; char charx2, chary2, nextx1, nextx2, nexty1, nexty2; char X1in, Y1in, X2in, Y2in; //raw A to D number char state; char time1; char curDir, dir, curDir2, dir2; char dbstate, dbstate2; int x,y,vx,vy,a1,a2; int x2,y2,vx2,vy2; #asm ; Port definitions ; ***** I/O REGISTER DEFINITIONS ***************************************** ; NOTE: ; Definitions marked "MEMORY MAPPED"are extended I/O ports ; and cannot be used with IN/OUT instructions .equ PORTA = 0x1b .equ DDRA = 0x1a .equ PINA = 0x19 .equ PORTB = 0x18 .equ DDRB = 0x17 .equ PINB = 0x16 .equ PORTC = 0x15 .equ DDRC = 0x14 .equ PINC = 0x13 .equ PORTD = 0x12 .equ DDRD = 0x11 .equ PIND = 0x10 .equ TCNT0 = 0x32 .equ TCNT1L = 0x2c .equ TCNT1H = 0x2d ; ***** CPU REGISTER DEFINITIONS ***************************************** .def XH = r27 .def XL = r26 .def YH = r29 .def YL = r28 .def ZH = r31 .def ZL = r30 ; ; Display Driver Register Definitions ; ; registers 0-7 are reserved for pixels. ; register 9 holds the last line 272 or 273 .def lastLine = R9 ; Registers 16 and 19 and up are used for compare checks, therefore, counters need to be ; Above register 16, as 16 is the general purpose register, for moving things ; and loading I/O space registers ; register 17 is reserved for the pixel counter, it is also used for switching ; the MCUCR when int1 is active .def pixelCount = R17 ; registers 24 and 25 are reserved for the line counter .def lineCountL = R24 .def lineCountH = R25 ; register 20 is used for determining if we are on Vertical pulses. ; not needed once in the display code .def timerReset = R20 ; register 21 is used for counting how many times a certain line is displayed ; This is compared with lineRepeated, to see if we move to the next line .def lineCopy = R21 ; register 26 and 27 will be used for accessing the screen memory, aka register X ; Note that the only variables that matter in between the end of the display code ; and the start of the program code is the line counter, and the last line variable. ; Therefore, these must be pushed to the stack before running any program code ; in C. All other registers are reset at some point before the display code begins. ; ;End Display Driver Definitions ; ; ; Program Register Definitions ; ; register 11 holds the moveCounter, slowing down the polling of the input .def moveCounter = R11 ; registers 12 and 13 hold the red and green intensity values .def redHold = R12 .def greenHold = R13 ; register 22 is used for telling where the cursor is, screen, R, G, or B .def cursorLocation = R22 ; register 23 is used for holding what the current pixel is .def holdPixel = R23 ; register 18 is used for counting how long the cursor has been a certain color .def cursorCounter = R18 ; register 28 and 29, aka register Y, is used for pointing to where in the screen ; memory the cursor is located ; ; End Program Register Definitions ; ; Variable definitions .equ lastPixel = 32 ; The last pixel in a line .equ startLine = 60 ; First line of Display .equ endLine = 240 ; Last line of Display .equ lineRepeated = 4 ; How many times a line has to be repeated .equ lineSubtract = 32 ; When I reach the end of a line, how much I have to subtract ; if the line has to be repeated .equ waitTime = 190 ; Wait time, for back porch to finish .equ lastLineConst = 272 ; How many lines there are in a screen, during even frames ; ; Macros ; ; Load screen memory to registers 0-7. Too slow to be used between each group of pixels ; Use loadPixel to load individual pixels after they have been displayed. ; loadScreen MACRO takes 16 cycles to complete, or 1us .MACRO loadScreen ld r0, X+ ld r1, X+ ld r2, X+ ld r3, X+ ld r4, X+ ld r5, X+ ld r6, X+ ld r7, X+ .ENDMACRO ; Load an individual pixel. This is used for loading a new pixel while the old is ; being displayed. ; loadPixel MACRO takes 2 cycles to complete, or 1/8us .MACRO loadPixel ld @0, X+ .ENDMACRO ; Blasts out 16 pixels (8 bytes) .MACRO lineBlast pixelBlast r0 nopDelay pixelBlast r1 nopDelay pixelBlast r2 nopDelay pixelBlast r3 nopDelay pixelBlast r4 nopDelay pixelBlast r5 nopDelay pixelBlast r6 nopDelay pixelBlast r7 nopDelay .ENDMACRO ; Blast out two pixels (one byte) from memory .MACRO pixelBlast out PORTC, @0 swap @0 nop nop nop nop nop nop out PORTC, @0 loadPixel @0 inc pixelCount .ENDMACRO .MACRO nopDelay ; Adding or subtracting nops will increase or decrease pixel length ; Based on 16MHz nop nop nop nop nop nop nop nop .ENDMACRO .Macro enableSleep ;loads the MCUCR with the sleep enable bit plus what it had before ;that being registers on falling interrupts ldi r16, 0b10001010 out MCUCR, r16 .ENDMACRO .Macro disableSleep ldi r16, 0b00001010 out MCUCR, r16 .ENDMACRO ; ;End ASM Macros ; #endasm // initialize screen array unsigned char screen[1536]; // functions void writePixel(char x, char y, char color); int getByteLoc(char x, char y); char readPixel(char x, char y); void writeChar(char x, char y, char letter, char color); void drawBorders(void); void switchADC(void); void reset(void); void getDir1(void); void getDir2(void); void debounce1(void); void debounce2(void); void init_game(void); void lightcycle(void); /**********Program Variables**********/ char holdPixel; // Holds the previous color value char cursorLocationX; // Holds the X value of the cursor position char cursorLocationY; // Holds the Y value of the cursor position char redColor; // Holds the value of red char greenColor; char blueColor; char screenTab; // Stores where we are on the screen(paint area, R G or B) char cursorCount; // stores how many frames the cursor has remained the same color //5x7 characters flash char characters[798]={ //0 0x0F,0xFF,0x00, 0xF0,0x00,0xF0, 0xF0,0x0F,0xF0, 0xF0,0xF0,0xF0, 0xFF,0x00,0xF0, 0xF0,0x00,0xF0, 0x0F,0xFF,0x00, //1 0x00,0xF0,0x00, 0x0F,0xF0,0x00, 0x00,0xF0,0x00, 0x00,0xF0,0x00, 0x00,0xF0,0x00, 0x00,0xF0,0x00, 0x0F,0xFF,0x00, //2 0x0F,0xFF,0x00, 0xF0,0x00,0xF0, 0x00,0x00,0xF0, 0x00,0x0F,0x00, 0x00,0xF0,0x00, 0x0F,0x00,0x00, 0xFF,0xFF,0xF0, //3 0xFF,0xFF,0xF0, 0x00,0x0F,0x00, 0x00,0xF0,0x00, 0x00,0x0F,0x00, 0x00,0x00,0xF0, 0x00,0x00,0xF0, 0x0F,0xFF,0x00, //4 0x00,0x0F,0x00, 0x00,0xFF,0x00, 0x0F,0x0F,0x00, 0xF0,0x0F,0x00, 0xFF,0xFF,0xF0, 0x00,0x0F,0x00, 0x00,0x0F,0x00, //5 0xFF,0xFF,0xF0, 0xF0,0x00,0x00, 0xFF,0xFF,0x00, 0x00,0x00,0xF0, 0x00,0x00,0xF0, 0xF0,0x00,0xF0, 0x0F,0xFF,0x00, //6 0x0F,0x00,0x00, 0xF0,0x00,0x00, 0xF0,0x00,0x00, 0xFF,0xFF,0x00, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0x0F,0xFF,0x00, //7 0xFF,0xFF,0xF0, 0x00,0x00,0xF0, 0x00,0x0F,0x00, 0x00,0xF0,0x00, 0x0F,0x00,0x00, 0xF0,0x00,0x00, 0xF0,0x00,0x00, //8 0x0F,0xFF,0x00, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0x0F,0xFF,0x00, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0x0F,0xFF,0x00, //9 0x0F,0xFF,0x00, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0x0F,0xFF,0xF0, 0x00,0x00,0xF0, 0x00,0x00,0xF0, 0x00,0x0F,0x00, //A 0x0F,0xFF,0x00, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xFF,0xFF,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, //B 0xFF,0xFF,0x00, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xFF,0xFF,0x00, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xFF,0xFF,0x00, //C 0x0F,0xFF,0x00, 0xF0,0x00,0xF0, 0xF0,0x00,0x00, 0xF0,0x00,0x00, 0xF0,0x00,0x00, 0xF0,0x00,0xF0, 0x0F,0xFF,0x00, //D 0xFF,0xFF,0x00, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xFF,0xFF,0x00, //E 0xFF,0xFF,0xF0, 0xF0,0x00,0x00, 0xF0,0x00,0x00, 0xFF,0xFF,0xF0, 0xF0,0x00,0x00, 0xF0,0x00,0x00, 0xFF,0xFF,0xF0, //F 0xFF,0xFF,0xF0, 0xF0,0x00,0x00, 0xF0,0x00,0x00, 0xFF,0xFF,0xF0, 0xF0,0x00,0x00, 0xF0,0x00,0x00, 0xF0,0x00,0x00, //G 0x0F,0xFF,0x00, 0xF0,0x00,0xF0, 0xF0,0x00,0x00, 0xF0,0x0F,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0x0F,0xFF,0x00, //H 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xFF,0xFF,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, //I 0x0F,0xFF,0x00, 0x00,0xF0,0x00, 0x00,0xF0,0x00, 0x00,0xF0,0x00, 0x00,0xF0,0x00, 0x00,0xF0,0x00, 0x0F,0xFF,0x00, //J 0x00,0xFF,0xF0, 0x00,0x0F,0x00, 0x00,0x0F,0x00, 0x00,0x0F,0x00, 0x00,0x0F,0x00, 0xF0,0x0F,0x00, 0x0F,0xF0,0x00, //K 0xF0,0x00,0xF0, 0xF0,0x0F,0x00, 0xF0,0xF0,0x00, 0xFF,0x00,0x00, 0xF0,0xF0,0x00, 0xF0,0x0F,0x00, 0xF0,0x00,0xF0, //L 0xF0,0x00,0x00, 0xF0,0x00,0x00, 0xF0,0x00,0x00, 0xF0,0x00,0x00, 0xF0,0x00,0x00, 0xF0,0x00,0x00, 0xFF,0xFF,0xF0, //M 0xF0,0x00,0xF0, 0xFF,0x0F,0xF0, 0xF0,0xF0,0xF0, 0xF0,0xF0,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, //N 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xFF,0x00,0xF0, 0xF0,0xF0,0xF0, 0xF0,0x0F,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, //O 0x0F,0xFF,0x00, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0x0F,0xFF,0x00, //P 0xFF,0xFF,0x00, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xFF,0xFF,0x00, 0xF0,0x00,0x00, 0xF0,0x00,0x00, 0xF0,0x00,0x00, //Q 0x0F,0xFF,0x00, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xF0,0x0F,0x00, 0x0F,0xF0,0xF0, //R 0xFF,0xFF,0x00, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xFF,0xFF,0x00, 0xF0,0xF0,0x00, 0xF0,0x0F,0x00, 0xF0,0x00,0xF0, //S 0x0F,0xFF,0xF0, 0xF0,0x00,0x00, 0xF0,0x00,0x00, 0x0F,0xFF,0x00, 0x00,0x00,0xF0, 0x00,0x00,0xF0, 0xFF,0xFF,0x00, //T 0xFF,0xFF,0xF0, 0x00,0xF0,0x00, 0x00,0xF0,0x00, 0x00,0xF0,0x00, 0x00,0xF0,0x00, 0x00,0xF0,0x00, 0x00,0xF0,0x00, //U 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0x0F,0xFF,0x00, //V 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0x0F,0x0F,0x00, 0x00,0xF0,0x00, //W 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xF0,0xF0,0xF0, 0xF0,0xF0,0xF0, 0xF0,0xF0,0xF0, 0x0F,0x0F,0x00, //X 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0x0F,0x0F,0x00, 0x00,0xF0,0x00, 0x0F,0x0F,0x00, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, //Y 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0xF0,0x00,0xF0, 0x0F,0x0F,0x00, 0x00,0xF0,0x00, 0x00,0xF0,0x00, 0x00,0xF0,0x00, //Z 0xFF,0xFF,0xF0, 0x00,0x00,0xF0, 0x00,0x0F,0x00, 0x00,0xF0,0x00, 0x0F,0x00,0x00, 0xF0,0x00,0x00, 0xFF,0xFF,0xF0, //1 0x00, 0xFF, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x0F, 0xFF, 0xF0, //2 0x00, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0x0F, 0x00, 0x0F, 0x00, 0x00, 0xFF, 0x00, 0x0F, 0xF0, 0x00, 0xFF, 0x00, 0x0F, 0xFF, 0xFF}; //8x8 textures flash char textures[96]={ // Black&White Checkers 0xFF,0x00,0xFF,0x00, 0x00,0xfF,0x00,0xfF, 0xF0,0xF0,0xFF,0x00, 0x0F,0x0F,0x00,0xfF, 0xF0,0xF0,0xFF,0x00, 0x0F,0x0F,0x00,0xfF, 0xF0,0xF0,0xFF,0x00, 0x0F,0x0F,0x00,0xfF, // GREEN&BLUE CHECKERS 0x38,0x38, 0x83,0x83, 0x38,0x38, 0x83,0x83, 0x38,0x38, 0x83,0x83, 0x38,0x38, 0x83,0x83, // Red&Black Checkers 0x40,0x40, 0x04,0x04, 0x40,0x40, 0x04,0x04, 0x40,0x40, 0x04,0x04, 0x40,0x40, 0x04,0x04}; interrupt [EXT_INT0] void ext_int0(void) { #asm ; Interrupt 0 is used for catching the falling pulses from horizontal and Vertical pulses ; It will also update the line count. ;Disable sleep disableSleep ;increment line counter ADIW lineCountH:lineCountL, 1 ; if lineCount = lastLine, reset counters cpi lineCountH, 1 in r16, SREG sbrs r16, 1 rjmp endSync cp lineCountL, lastLine breq resetCounters rjmp endSync endSync: ; loop until timer0 says sync pulse has been completed ; check line number rjmp lineCheck resetCounters: ldi lineCountL, 1 ; Reset lineCountL ldi lineCountH, 0 ; Reset lineCountH ; Change lastLine number, 272 -> 273 or 273 -> 272 mov r16, lastLine cpi r16, low(lastLineConst) breq addLine subtractLine: dec lastLine rjmp continueReset addLine: inc lastLine continueReset: ; Reset the pointers to point to the start of screen memory ldi XH, HIGH(_screen) ldi XL, LOW(_screen) ldi lineCopy, 1 rjmp endSync lineCheck: cpi lineCountL, startLine brlo programCode cpi lineCountL, endLine + 1 brsh programCode jmp startDisplay ;;;;;;;;;PROGRAM CODE;;;;;;;;;;; programCode: ; Save lineCountH and L and save the lastLine variable ; push these to the stack push lineCountL push lineCountH push lastLine ; Do some lineCount checking, to see what line we are on, for running program code ; end line is 240, right now cpi lineCountL, endLine +1 brsh programLine1 cpi lineCountL, endLine +2 brsh programLine2 cpi lineCountL, endLine +3 brsh programLine3 cpi lineCountL, endLine +4 brsh programLine4 cpi lineCountL, endLine +5 brsh programLine5 cpi lineCountL, endLine +6 brsh programLine6 ; Add more if necessary ; if no program code to run, just pop the registers back jmp popRegisters ; Call C program functions programLine1: #endasm //light cycle program called here lightcycle(); #asm programLine2: #endasm #asm programLine3: #endasm #asm programLine4: #endasm #asm programLine5: #endasm #asm programLine6: #endasm #asm popRegisters: pop lastLine pop lineCountH pop lineCountL jmp endInterrupt ;;;;;;;;;;;END PROGRAM CODE;;;;;;;;;; ;;;;;;;;START DISPLAY DRIVER CODE;;;;;;; startDisplay: ; At the start of the display, the first 30 lines will not be displayed. ; This means that I have the full 63.5us to use for coding. ; This amounts to 1,016 clock cycles, which should be plenty, if code is optimized ; in assembly preLine: ; Load screen memory into the registers loadScreen ;zero timer0 ldi r16, 0x00 out TCNT0, r16 ; loop to wait for visible portion of the screen waitForVisible: in r16, TCNT0 cpi r16, waitTime brlo waitForVisible pixelBlasting: lineBlast ;8->16 lineBlast ;16->32 lineBlast ;24->48 lineBlast ;32->64 returnFromInt: ; If there is any line cleanup code to do, do it now ldi r16, 0x00 out PORTC, r16 ;reset portC to black level cpi lineCopy, lineRepeated brsh nextLineGroup ; if displayed the same line lineRepeated times, then nextLineGroup SBIW XH:XL, lineSubtract + 8 ; else, reset pointer back to beginning of line memory position inc lineCopy ; increment lineCopy enableSleep rjmp endInterrupt ; Reset line counter, then return ; Pointer points to next line, so does not have to be changed. nextLineGroup: SBIW XH:XL, 8 ldi lineCopy, 1 enableSleep rjmp endInterrupt ;;;;;;;;;;END DISPLAY DRIVER CODE;;;;;;;;; endInterrupt: #endasm } interrupt [EXT_INT1] void ext_int1(void) { /////////////////////////////////////////////////// //////////Syncing with the Sync Generator////////// /////////////////////////////////////////////////// // Based on the fact that vertical pulses and the equalization pusles // occur at a rate twice that the horizontal pulses // Search for when this happens, and then display x lines after that occurs, // x being when you want to start displaying to the tv, with a certain offset, so we get // to the top line of the tv screen. #asm ; Disable sleep while in interrupt disableSleep ; if timer has not been reset yet, reset timer cpi timerReset, 0xFF BREQ checkTime rjmp exitInt1 checkTime: ; if low(timer) > 50us in r16, TCNT1L cpi r16, 100 ; At 2MHz timer, need 100 cycles to get 50us BRLO checkForInit ; lineCount = 1 ldi lineCountL, 1 ldi lineCountH, 0 rjmp exitInt1 checkForInit: ; if lineCount == 1 cpi lineCountL, 1 brne exitInt1 ; Load the start of memory into the pointers ldi XL, LOW(_screen) ldi XH, HIGH(_screen) ; Set line repeat counter to one ldi lineCopy, 1 ; Disable int1, enable int0 ldi r16, 0b01000000 out GICR, r16 ;reenable sleep enableSleep reti exitInt1: ; LOW(Timer1) = 0, Timer Reset ldi r16, 0x00 out TCNT1L, r16 ;Set timerReset to show that timer has been reset before ldi timerReset, 0xFF ;reenable sleep enableSleep reti #endasm } // Initialization code void initialization(void) { /*****Port Initialization*****/ // PortB will be used to display out to the LEDs, for debugging purposes // Note: A 0 on a port bit corresponds to a lit LED DDRB = 0xFF; // Start with LEDs half on, half off PORTB = 0xF0; // PortC will be used to output to the TV DDRC = 0xFF; // Init at black level PORTC = 0x00; // PortD will be used to take in the sync pulses, the external interrupt on bit2 is active // One interrupt will be for line counting, the second interrupt will be for initial sync with the generator DDRD = 0x00; /*****Interrupts and Timers*****/ // Disable interrupts on timers TIMSK = 0; // Set Timer0 Control Register, no prescaler TCCR0 = 0x01; //Turn off PWM and other in Timer1 Control RegisterA TCCR1A = 0x00; // Set Timer1 Control RegisterB, at clock speed/8 = 2MHz TCCR1B = 0x02; // Set interrupts 0 and 1 to register on falling edge (horizontal sync is a falling edge, followed by a rising edge) // Idle sleep mode also set MCUCR = 0b00001010; // Enable external interrupt 1 to seach for sync pulses. GICR = 0b10000000; /*****Variable Initialization*****/ ////////////////////////////////////////////////// //////////Display Driver Initializations////////// ////////////////////////////////////////////////// #asm ; Initialize line counter at 0 ; Int1 will set line count to 1, when it finishes ldi lineCountL, 0 ldi lineCountH, 0 ; Set the timerReset to 0, to show that timer has not been reset yet ldi timerReset, 0x00 ; Set the last line variable to whatever the last line starts at ldi r16, low(lastLineConst) mov lastLine, r16 ; Initialize the lineCopy variable at 1 ldi lineCopy, 1 #endasm ///////////////////////////////////// //////////Initialize Screen////////// ///////////////////////////////////// drawBorders(); } void main(void) { //init video and game initialization(); init_game(); // Turn on interrupts #asm sei #endasm // Sleep until interrupt, when returning from interrupt, sleep while(1) { #asm("sleep"); } } /************DISPLAY FUNCTIONS***************/ // Upper left corner of the screen is x,y coordinates (1,1) int getByteLoc(char x, char y) { unsigned int byteLocation; // store the actual location of the pixel in memory // find the memory location, given the x,y coordinates return byteLocation = ((unsigned int)((unsigned int)(y-1) * 32) + ((x - 1)>>1)); } //Return the color of specified pixel char readPixel(char x, char y) { unsigned char localPixel; localPixel = screen[getByteLoc(x,y)]; if (x%2) // upper nibble { return (localPixel >> 4); } else // lower nibble { return (localPixel & 0x0F ); } } //write pixel of specified color void writePixel(char x, char y, char color) { unsigned char localPixel; unsigned char temp1; localPixel = color; temp1 = screen[getByteLoc(x,y)]; if (x%2) // upper nibble { screen[getByteLoc(x,y)] = (temp1 | 0xF0) & ((localPixel << 4) | 0x0F); } else // lower nibble { screen[getByteLoc(x,y)] = (temp1 | 0x0F) & (0xF0 |localPixel); } } // Can only be used on even columns, meaning x = an even number void writeChar(char x, char y, char letter, char color) { unsigned char tempPixel; // Stores the pixel taken from the screen array unsigned char localPixel; // Stores the color the letter should take unsigned int letterIndex; unsigned int byteLoc; // letterIndex = letter*21; // x -> Y+9 // y -> Y+8 // letter -> Y+7 // color -> Y+6 // tempPixel -> R16 // localPixel -> R17 // letterIndex -> R18,R19 // byteLoc -> R20,R21 #asm LDD R26,Y+7 LDI R30,LOW(21) MUL R30,R26 MOV R18,R0 MOV R19,R1 #endasm localPixel = color; byteLoc = getByteLoc(x,y); //Unrolled the for loop that would display put the // 1,2 tempPixel = screen[byteLoc]; screen[byteLoc] = characters[letterIndex] & ((localPixel << 4) | localPixel); byteLoc++; //32 letterIndex++; // 3,4 tempPixel = screen[byteLoc]; screen[byteLoc] = characters[letterIndex] & ((localPixel << 4) | localPixel); byteLoc++; //31 letterIndex++; //5,6 tempPixel = screen[byteLoc]; screen[byteLoc] = characters[letterIndex] & ((localPixel << 4) | localPixel); byteLoc = byteLoc + 30; letterIndex++; //7,8 tempPixel = screen[byteLoc]; screen[byteLoc] = characters[letterIndex] & ((localPixel << 4) | localPixel); byteLoc++; letterIndex++; //9,10 tempPixel = screen[byteLoc]; screen[byteLoc] = characters[letterIndex] & ((localPixel << 4) | localPixel); byteLoc++; letterIndex++; //11,12 tempPixel = screen[byteLoc]; screen[byteLoc] = characters[letterIndex] & ((localPixel << 4) | localPixel); byteLoc = byteLoc + 30; letterIndex++; //13,14 tempPixel = screen[byteLoc]; screen[byteLoc] = characters[letterIndex] & ((localPixel << 4) | localPixel); byteLoc++; letterIndex++; //15,16 tempPixel = screen[byteLoc]; screen[byteLoc] = characters[letterIndex]& ((localPixel << 4) | localPixel); byteLoc++; letterIndex++; //16,18 tempPixel = screen[byteLoc]; screen[byteLoc] = characters[letterIndex]& ((localPixel << 4) | localPixel); byteLoc = byteLoc + 30; letterIndex++; //19,20 tempPixel = screen[byteLoc]; screen[byteLoc] = characters[letterIndex]& ((localPixel << 4) | localPixel); byteLoc++; letterIndex++; //21,22 tempPixel = screen[byteLoc]; screen[byteLoc] = characters[letterIndex]& ((localPixel << 4) | localPixel); byteLoc++; letterIndex++; //23,24 tempPixel = screen[byteLoc]; screen[byteLoc] = characters[letterIndex]& ((localPixel << 4) | localPixel); byteLoc = byteLoc + 30; letterIndex++; //25,26 tempPixel = screen[byteLoc]; screen[byteLoc] = characters[letterIndex]& ((localPixel << 4) | localPixel); byteLoc++; letterIndex++; //27,28 tempPixel = screen[byteLoc]; screen[byteLoc] = characters[letterIndex]& ((localPixel << 4) | localPixel); byteLoc++; letterIndex++; //29,30 tempPixel = screen[byteLoc]; screen[byteLoc] = characters[letterIndex]& ((localPixel << 4) | localPixel); byteLoc = byteLoc + 30; letterIndex++; //31,32 tempPixel = screen[byteLoc]; screen[byteLoc] = characters[letterIndex]& ((localPixel << 4) | localPixel); byteLoc++; letterIndex++; //33,34 tempPixel = screen[byteLoc]; screen[byteLoc] = characters[letterIndex]& ((localPixel << 4) | localPixel); byteLoc++; letterIndex++; //35,36 tempPixel = screen[byteLoc]; screen[byteLoc] = characters[letterIndex]& ((localPixel << 4) | localPixel); byteLoc = byteLoc + 30; letterIndex++; //37,38 tempPixel = screen[byteLoc]; screen[byteLoc] = characters[letterIndex]& ((localPixel << 4) | localPixel); byteLoc++; letterIndex++; //39,40 tempPixel = screen[byteLoc]; screen[byteLoc] = characters[letterIndex]& ((localPixel << 4) | localPixel); byteLoc++; letterIndex++; //41,42 tempPixel = screen[byteLoc]; screen[byteLoc] = characters[letterIndex]& ((localPixel << 4) | localPixel); } //draw game arena borders void drawBorders(void) begin int i = 0; for(i = 1; i < 65; i++) begin writePixel(i,1,blue); writePixel(i,45,blue); end for(i = 2; i < 45; i++) begin writePixel(1, i, blue); writePixel(64, i, blue); end end //ADC multiplexer void switchADC(void) begin //do one of the 4 readings if (state == 1) begin Y1in = ADCH; state = 2; ADMUX.0 = 1; ADMUX.1 = 0; end else if (state == 2) begin X1in = ADCH; state = 3; ADMUX.0 = 0; ADMUX.1 = 1; end else if (state == 3) begin Y2in = ADCH; state = 4; ADMUX.0 = 1; ADMUX.1 = 1; end else if (state == 4) begin X2in = ADCH; state = 1; ADMUX.0 = 0; ADMUX.1 = 0; end //start next read ADCSR.6=1; end //reset game state void reset(void) begin charx = initxdec; chary = initydec; charx2 = initx2dec; chary2 = initydec; x = initxfix; y = inityfix; x2 = initx2fix; y2 = inityfix; vx = velxfix; vy = velyfix; vx2 = -velxfix; vy2 = velyfix; end //decodes controller direction for p1 void getDir1(void) begin if (X1in > 90) curDir = LEFT; else if (X1in < 50) curDir = RIGHT; else if (Y1in < 50) curDir = UP; else if (Y1in > 90) curDir = DOWN; else curDir = NONE; end //decodes controller direction for p2 void getDir2(void) begin if (X2in > 90) curDir2 = LEFT; else if (X2in < 50) curDir2 = RIGHT; else if (Y2in < 50) curDir2 = UP; else if (Y2in > 90) curDir2 = DOWN; else curDir2 = NONE; end //controller 1 state machine void debounce1(void) begin switch (dbstate) begin case NOT_TURNING: if (curDir != NONE) begin dbstate = TURN; dir = curDir; end break; case TURN: if (dir == curDir) begin //modify directions and velocity as needed if (dir == LEFT) begin if (vy == 0) begin vy = -vx; vx = 0; end else if (vx == 0) begin vx = vy; vy = 0; end end else if (dir == RIGHT) begin if (vy == 0) begin vy = vx; vx = 0; end else if (vx == 0) begin vx = -vy; vy = 0; end end else if (dir == UP) begin //accelerate (speed up) if (vy == 0) begin if (vx > 0) vx = vx + acc; else vx = vx - acc; end else if (vx == 0) begin if (vy > 0) vy = vy + acc; else vy = vy - acc; end end else if (dir == DOWN) begin //decelerate (speed down) if (vy == 0) begin if (vx > 0) if (vx - acc != 0) vx = vx - acc; else if (vx + acc != 0) vx = vx + acc; end else if (vx == 0) begin if (vy > 0) if (vy - acc != 0) vy = vy - acc; else if (vy + acc != 0) vy = vy + acc; end end dbstate = TURNING; end else dbstate = NOT_TURNING; break; case TURNING: if (curDir != dir) dbstate = STILL_TURNING1; break; case STILL_TURNING1: if (curDir == dir) dbstate = TURNING; else dbstate = STILL_TURNING2; break; case STILL_TURNING2: if (curDir == dir) dbstate = TURNING; else dbstate = STILL_TURNING3; break; case STILL_TURNING3: if (curDir == dir) dbstate = TURNING; else dbstate = NOT_TURNING; break; end getDir1(); end //controller 2 state machine void debounce2(void) begin switch (dbstate2) begin case NOT_TURNING: if (curDir2 != NONE) begin dbstate2 = TURN; dir2 = curDir2; end break; case TURN: if (dir2 == curDir2) begin //modify directions and velocity as needed if (dir2 == LEFT) begin if (vy2 == 0) begin vy2 = -vx2; vx2 = 0; end else if (vx2 == 0) begin vx2 = vy2; vy2 = 0; end end else if (dir2 == RIGHT) begin if (vy2 == 0) begin vy2 = vx2; vx2 = 0; end else if (vx2 == 0) begin vx2 = -vy2; vy2 = 0; end end else if (dir2 == UP) begin //accelerate (speed up) if (vy2 == 0) begin if (vx2 > 0) vx2 = vx2 + acc; else vx2 = vx2 - acc; end else if (vx2 == 0) begin if (vy2 > 0) vy2 = vy2 + acc; else vy2 = vy2 - acc; end end else if (dir2 == DOWN) begin //decelerate (speed down) if (vy2 == 0) begin if (vx2 > 0) if (vx2 - acc != 0) vx2 = vx2 - acc; else if (vx2 + acc != 0) vx2 = vx2 + acc; end else if (vx2 == 0) begin if (vy2 > 0) if (vy2 - acc != 0) vy2 = vy2 - acc; else if (vy2 + acc != 0) vy2 = vy2 + acc; end end dbstate2 = TURNING; end else dbstate2 = NOT_TURNING; break; case TURNING: if (curDir2 != dir2) dbstate2 = STILL_TURNING1; break; case STILL_TURNING1: if (curDir2 == dir2) dbstate2 = TURNING; else dbstate2 = STILL_TURNING2; break; case STILL_TURNING2: if (curDir2 == dir2) dbstate2 = TURNING; else dbstate2 = STILL_TURNING3; break; case STILL_TURNING3: if (curDir2 == dir2) dbstate2 = TURNING; else dbstate2 = NOT_TURNING; break; end getDir2(); end //initialize game variables void init_game(void) begin resetCount = rCount; resetCount2 = rCount; adcCount = aCount; finish = 0; state = 1; ADMUX = 0b00100000; //enable ADC and set prescaler to 1/128*16MHz=125,000 //and clear interupt enable //and start a conversion ADCSR = 0b11000111; X1in = 70; Y1in = 70; X2in = 70; Y2in = 70; charx = initxdec; chary = initydec; charx2 = initx2dec; chary2 = initydec; nextx1 = charx; nexty1 = chary; nextx2 = charx2; nexty2 = chary2; x = initxfix; y = inityfix; x2 = initx2fix; y2 = inityfix; vx = velxfix; vy = velyfix; vx2 = -velxfix; vy2 = velyfix; //draw pixel here! writePixel(charx,chary,red); writePixel(charx2,chary2,green); end void lightcycle(void) begin if (!finish) begin //poll adc if (adcCount == 0) begin switchADC(); debounce1(); debounce2(); adcCount = aCount; end else begin adcCount--; end //update position x = x + vx; y = y + vy; charx = (char)(x >> 8); chary = (char)(y >> 8); x2 = x2 + vx2; y2 = y2 + vy2; charx2 = (char)(x2 >> 8); chary2 = (char)(y2 >> 8); //collision conditions if (nextx1 != charx || nexty1 != chary) begin if (readPixel(charx,chary) != black) begin writeChar(16, 20, pLetter, green); writeChar(22, 20, twoNum, green); writeChar(32, 20, wLetter, green); writeChar(38, 20, iLetter, green); writeChar(44, 20, nLetter, green); writeChar(50, 20, sLetter, green); finish = 1; end end //collision conditions else if (nextx2 != charx2 || nexty2 != chary2) begin if (readPixel(charx2,chary2) != black) begin writeChar(16, 20, pLetter, red); writeChar(22, 20, oneNum, red); writeChar(32, 20, wLetter, red); writeChar(38, 20, iLetter, red); writeChar(44, 20, nLetter, red); writeChar(50, 20, sLetter, red); finish = 1; end end //if no collision write new position to screen writePixel(charx,chary,red); writePixel(charx2,chary2,green); nextx1 = charx; nexty1 = chary; nextx2 = charx2; nexty2 = chary2; end else begin //pause and reset game if (resetCount > 0) begin resetCount--; end else begin #asm JMP 0 #endasm resetCount = rCount; reset(); end end end