#include // Disable register saving, registers critical to the display driver // Are saved before running any code in C, and then reloaded after doing that. #pragma savereg- // Do not allow the compiler to assign global variables to the registers #pragma regalloc- // Some color definitions #define black 0x00 #define blue 0b00001000 #define red 0b00000100 #define green 0b00000011 #define darkgreen 0b00000001 #define yellow 0b00000111 #define purple 0b00001100 #define turq 0b00001011 #define white 0b00001111 // 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 /********Program Definitions*************/ // Program letter locations #define redLocationX 5 #define redLocationY 30 #define greenLocationX 15 #define greenLocationY 30 #define blueLocationX 25 #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 #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]; //= {0xF0, 0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0}; //{0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00}; // functions void writePixel(char x, char y, char color); int getByteLoc(char x, char y); char readPixel(char x, char y); void invertPixel(char x, char y); void textureWrite(char x, char y, char textureIndex); void writeChar(char x, char y, char letter, char color); void paintTime(void); char pollButtons(void); void cursorInvert(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[756]={ //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}; //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 paintTime(); #asm programLine2: #endasm cursorInvert(); #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*****/ // PortA will be used for input, from switches // Note: A 1 on a port bit corresponds to an unpressed switch DDRA = 0x00; // 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 /////////////////////////////////////////// //////////Program Initializations////////// /////////////////////////////////////////// ///////////////////////////////////// //////////Initialize Screen////////// ///////////////////////////////////// writeChar(redLocationX, redLocationY, rLetter, red); writeChar(greenLocationX, greenLocationY, gLetter, green); writeChar(blueLocationX, blueLocationY, bLetter, blue); } void main(void) { initialization(); // Turn on interrupts #asm sei #endasm // Sleep until interrupt, when returning from interrupt, sleep while(1) { #asm("sleep"); } } void paintTime(void) { char buttonPress; char currentColor; // The combination of red, green and blue buttonPress = pollButtons(); switch (buttonPress) { case inputUp: { switch (screenTab) { case 1: // on screen { if (cursorLocationY == 1) break; else { writePixel(cursorLocationX, cursorLocationY, holdPixel); cursorLocationY--; holdPixel = readPixel(cursorLocationX, cursorLocationY); invertPixel(cursorLocationX, cursorLocationY); break; } } case 2: // on red { if (redColor == red) break; // if already red, do nothing redColor = red; // if not red, make red break; } case 3: // on green { if (greenColor == green) break; greenColor++; break; } case 4: // on blue { if (blueColor == blue) break; blueColor = blue; break; } } break; } case inputDown: { switch (screenTab) { case 1: // on screen { if (cursorLocationY == 48) break; else { writePixel(cursorLocationX, cursorLocationY, holdPixel); cursorLocationY++; holdPixel = readPixel(cursorLocationX, cursorLocationY); invertPixel(cursorLocationX, cursorLocationY); break; } } case 2: // on red { if (redColor == 0) break; // if already 0, do nothing redColor = 0; // if not 0, make 0 break; } case 3: // on green { if (greenColor == 0) break; greenColor--; break; } case 4: // on blue { if (blueColor == 0) break; blueColor = 0; break; } } break; } case inputLeft: { if (screenTab == 1) { if (cursorLocationX == 1) break; else { writePixel(cursorLocationX, cursorLocationY, holdPixel); cursorLocationX--; holdPixel = readPixel(cursorLocationX, cursorLocationY); invertPixel(cursorLocationX, cursorLocationY); break; } } } case inputRight: { if (screenTab == 1) { if (cursorLocationX == 64) break; else { writePixel(cursorLocationX, cursorLocationY, holdPixel); cursorLocationX++; holdPixel = readPixel(cursorLocationX, cursorLocationY); invertPixel(cursorLocationX, cursorLocationY); break; } } } case inputSet: { if (screenTab == 1) { currentColor = redColor + greenColor + blueColor; writePixel(cursorLocationX, cursorLocationY, currentColor); break; } } case inputClear: { if (screenTab == 1) { writePixel(cursorLocationX, cursorLocationY, 0); break; } } case inputTab: { screenTab++; if (screen > 4) screenTab = 1; switch (screenTab) { case 1: // on screen { invertPixel(blueColorX + 1, blueColorY + 1); invertPixel(cursorLocationX, cursorLocationY); break; } case 2: // on red { writePixel(cursorLocationX, cursorLocationY, holdPixel); invertPixel(redColorX + 1, redColorY + 1); break; } case 3: // on green { invertPixel(redColorX + 1, redColorY + 1); invertPixel(greenColorX + 1, greenColorY + 1); break; } case 4: // on blue { invertPixel(greenColorX + 1, greenColorY + 1); invertPixel(blueColorX + 1, blueColorY + 1); break; } } } } // Switch button poll } void cursorInvert(void) { cursorCount++; if (cursorCount == cursorCountMax) { invertPixel(cursorLocationX, cursorLocationY); cursorCount = 1; } } /************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)); } 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 ); } } void writePixel(char x, char y, char color) { unsigned char localPixel; unsigned char temp; localPixel = color; temp = screen[getByteLoc(x,y)]; if (x%2) // upper nibble { screen[getByteLoc(x,y)] = (temp & 0xF0) | (localPixel << 4); } else // lower nibble { screen[getByteLoc(x,y)] = (temp & 0x0F) | (localPixel & 0x0F); } } void invertPixel(char x, char y) { unsigned char temp; temp = screen[getByteLoc(x,y)]; // Take only the part of the byte we want, then invert the bits on that nibble // lower nibble is even, upper is odd, if odd, there is a remainder if (x%2) // upper nibble (x%2) = 1 if x is odd { screen[getByteLoc(x,y)] = (temp & 0xF0) ^ 0xF0; } else // lower nibble { screen[getByteLoc(x,y)] = (temp & 0x0F) ^ 0x0F; } } // 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] = (tempPixel & ~characters[letterIndex]) | (characters[letterIndex] & localPixel); byteLoc++; //32 letterIndex++; // 3,4 tempPixel = screen[byteLoc]; screen[byteLoc] = (tempPixel & ~characters[letterIndex]) | (characters[letterIndex] & localPixel); byteLoc++; //31 letterIndex++; //5,6 tempPixel = screen[byteLoc]; screen[byteLoc] = (tempPixel & ~characters[letterIndex]) | (characters[letterIndex] & localPixel); byteLoc = byteLoc + 30; letterIndex++; //7,8 tempPixel = screen[byteLoc]; screen[byteLoc] = (tempPixel & ~characters[letterIndex]) | (characters[letterIndex] & localPixel); byteLoc++; letterIndex++; //9,10 tempPixel = screen[byteLoc]; screen[byteLoc] = (tempPixel & ~characters[letterIndex]) | (characters[letterIndex] & localPixel); byteLoc++; letterIndex++; //11,12 tempPixel = screen[byteLoc]; screen[byteLoc] = (tempPixel & ~characters[letterIndex]) | (characters[letterIndex] & localPixel); byteLoc = byteLoc + 30; letterIndex++; //13,14 tempPixel = screen[byteLoc]; screen[byteLoc] = (tempPixel & ~characters[letterIndex]) | (characters[letterIndex] & localPixel); byteLoc++; letterIndex++; //15,16 tempPixel = screen[byteLoc]; screen[byteLoc] = (tempPixel & ~characters[letterIndex]) | (characters[letterIndex] & localPixel); byteLoc++; letterIndex++; //16,18 tempPixel = screen[byteLoc]; screen[byteLoc] = (tempPixel & ~characters[letterIndex]) | (characters[letterIndex] & localPixel); byteLoc = byteLoc + 30; letterIndex++; //19,20 tempPixel = screen[byteLoc]; screen[byteLoc] = (tempPixel & ~characters[letterIndex]) | (characters[letterIndex] & localPixel); byteLoc++; letterIndex++; //21,22 tempPixel = screen[byteLoc]; screen[byteLoc] = (tempPixel & ~characters[letterIndex]) | (characters[letterIndex] & localPixel); byteLoc++; letterIndex++; //23,24 tempPixel = screen[byteLoc]; screen[byteLoc] = (tempPixel & ~characters[letterIndex]) | (characters[letterIndex] & localPixel); byteLoc = byteLoc + 30; letterIndex++; //25,26 tempPixel = screen[byteLoc]; screen[byteLoc] = (tempPixel & ~characters[letterIndex]) | (characters[letterIndex] & localPixel); byteLoc++; letterIndex++; //27,28 tempPixel = screen[byteLoc]; screen[byteLoc] = (tempPixel & ~characters[letterIndex]) | (characters[letterIndex] & localPixel); byteLoc++; letterIndex++; //29,30 tempPixel = screen[byteLoc]; screen[byteLoc] = (tempPixel & ~characters[letterIndex]) | (characters[letterIndex] & localPixel); byteLoc = byteLoc + 30; letterIndex++; //31,32 tempPixel = screen[byteLoc]; screen[byteLoc] = (tempPixel & ~characters[letterIndex]) | (characters[letterIndex] & localPixel); byteLoc++; letterIndex++; //33,34 tempPixel = screen[byteLoc]; screen[byteLoc] = (tempPixel & ~characters[letterIndex]) | (characters[letterIndex] & localPixel); byteLoc++; letterIndex++; //35,36 tempPixel = screen[byteLoc]; screen[byteLoc] = (tempPixel & ~characters[letterIndex]) | (characters[letterIndex] & localPixel); byteLoc = byteLoc + 30; letterIndex++; //37,38 tempPixel = screen[byteLoc]; screen[byteLoc] = (tempPixel & ~characters[letterIndex]) | (characters[letterIndex] & localPixel); byteLoc++; letterIndex++; //39,40 tempPixel = screen[byteLoc]; screen[byteLoc] = (tempPixel & ~characters[letterIndex]) | (characters[letterIndex] & localPixel); byteLoc++; letterIndex++; //41,42 tempPixel = screen[byteLoc]; screen[byteLoc] = (tempPixel & ~characters[letterIndex]) | (characters[letterIndex] & localPixel); } //TextureWrite will overwrite whatever is on the screen at the time it is written // It writes an 8x8 texture to SRAM void textureWrite(char x, char y, char textureIndex) { unsigned int byteLoc; unsigned char i; //for index unsigned int index; byteLoc = getByteLoc(x,y); index = (int)textureIndex*8; // x -> Y+7 // y -> Y+6 // textureIndex -> Y+5 // byteLoc -> R16,R17 // i -> R18 // index -> R19,R20 for (i=0;i<8;i++) { //1,2 screen[byteLoc] = textures[index]; byteLoc++; //32 index++; //3,4 screen[byteLoc] = textures[index]; byteLoc++; //31 index++; //5,6 screen[byteLoc] = textures[index]; byteLoc++; //30 index++; //7,8 screen[byteLoc] = textures[index]; byteLoc = byteLoc + 29; index++; } } /*** Program Functions ***/ char pollButtons(void) { unsigned char buttons; char ready; char readyCount; if (ready) { buttons = PINA; ready = 0; return buttons; } else { readyCount = readyCount + 1; if (readyCount == readyCountMax) { ready = 1; readyCount = 0; } return 0; } }