;This is the display driver code, written in assembly ; included is areas for writing program code, unrelated to the driver. ; These areas are marked by ;;;;;Program Code;;;;; ; Anything can be written there, so long as it takes less than 1000 cycles ; There are 29 blank lines, which may be used for code. ; Program code must be split up into segments, if it will take longer than ; 1000 cycles. Do line checks to jump to portions of the code at specific lines. ; This will allow for breaking up the code. ; Registers 0-7, 17, 18, 19, 21, 26 and 27 are reserved for the display code. ; Do not initialize register 20 with anything, but otherwise, it may be used ; once the display code starts, as it is just used for sync. ; Do not use the above registers when writing a program. .INCLUDE "m32def.inc" ; Register Reservations and new register definitions ; register 16 is a general purpose register ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;Display Driver Register Definitions;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; registers 0-7 are reserved for pixels. ; register 9 holds the last line 270 or 271 .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. .def timerReset = R20 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;Register 20 may be redefined after int1 has been completed;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;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 startOfMemory = 0x0060 ; The start of the screen array .equ endOfMemory = 0x0660 ; The end of the screen array ; This is based on size of screen, if that changes, change this variable .equ screenSize = 1536 ; Size of the screen, in bytes .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 = 224 ; Change number until .equ lastLineConst = 272 .equ moveSpeed = 15 .equ belowPaintable = 0x04E0 ; Below the paintable area of the screen .equ redCursorPosition = 0x05A3 ; Where the red cursor position is in SRAM .equ greenCursorPosition = 0x05AC .equ blueCursorPosition = 0x05B5 .equ redPreviewPosition = 0x04E5 ; Where the color previews are, in SRAM .equ greenPreviewPosition = 0x04EE .equ bluePreviewPosition = 0x04F6 ;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;Program Variables;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;; .equ blinkAt = 60 ; When cursor counter reaches blinkAt, switch cursor color ;;;;;;;;;;;;;;;; ;;;;;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 8 pixels .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 a single pixel from memory .MACRO pixelBlast out PORTC, @0 loadPixel @0 inc pixelCount .ENDMACRO .MACRO nopDelay ; Adding or subtracting nop's will increase or decrease pixel length ; Based on 16MHz nop nop nop nop nop nop 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 ;;;;;;;;;;;;;;;;;;;;;; ;;;;;Data Segment;;;;; ;;;;;;;;;;;;;;;;;;;;;; .DSEG ; Start of Data Segment ; Reserve screen memory in the data segment .ORG startOfMemory screen: .BYTE screenSize ; Reserve screenSize bytes for the video memory redColor: .Byte 1 ; holds the intensity of each color greenColor: .Byte 1 blueColor: .Byte 1 screenLocation: .BYTE 2 ; holds the location of the cursor in the screen ; used when tabbed out to colors ;;;;;;;;;;;;;;;;;;;;;; ;;;;;Code Segment;;;;; ;;;;;;;;;;;;;;;;;;;;;; .CSEG ; Start of Code Segment ;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;Interrupt Vectors;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;; .org 0x0000 jmp main ; Reset Handler .org 0x0002 jmp EXT_INT0 ; IRQ0 Handler .org 0x0004 jmp EXT_INT1 ; IRQ1 Handler ; Interrupt 0 is used for catching the falling pulses from horizontal and Vertical pulses ; It will also update the line count. EXT_INT0: ;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, 270 -> 271 or 271 -> 270 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 ///////////////////////////////// programCode: ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;Program Code Start;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Have lines 241->255, then must check for lineCountH ; Try to stay out of the beginning, as in lines 1-20 ; Only code there if the code is very very quick, as there is half ; the time to work with. ; These lines interrupt faster, because of the equalization pulses cpi lineCountL, endLine+2 BREQ inputControl /* Use whatever portions are necessary for more program code. cpi lineCount, endLine + 1 BREQ cpi lineCount, endLine + 2 BREQ cpi lineCount, endLine + 3 BREQ cpi lineCount, endLine + 4 BREQ */ reti ; Run through inputs, only take one at a time ; jump to input action, then do it inputControl: mov r16, moveCounter cpi r16, moveSpeed brlo endCleanup ldi r16, 0 mov moveCounter, r16 in r16, PINA sbrs r16, 7 ; skip if unpressed (a set bit is an unpressed switch) rjmp inputUp sbrs r16, 6 rjmp inputDown sbrs r16, 5 rjmp inputLeft sbrs r16, 4 rjmp inputRight sbrs r16, 3 rjmp inputSet sbrs r16, 2 rjmp inputClear sbrs r16, 1 rjmp inputTab ; If no input, can write more code here, or just return from the interrupt endCleanup: ; increments cursor counter, inverts, if necessary inc moveCounter inc cursorCounter cpi cursorCounter, blinkAt brlo end call invertCursor rjmp end end: reti ; Moves the cursor up inputUp: ; Check to see if we are on the screen, or on a color cpi cursorLocation, 1 BREQ screenUp cpi cursorLocation, 2 BREQ redUp cpi cursorLocation, 4 BREQ greenUp cpi cursorLocation, 8 BREQ blueUp ; if somehow none of these checks pass, jump to endCleanup jmp endCleanup screenUp: ; First check to make sure we aren't at the top of the screen ; The top of the screen is positions 0x0060 to 0x007F ldi r16, 0x00 cpse YH, r16 ; If these are equal, it may be at top, so skip move up rjmp moveUp cpi YL, 0x90 BRSH moveUp ; if same or higher than 0x0080 then jump to move up rjmp dontMoveUp ; load the previous pixel back into SRAM, move the cursor, then load the new pixel ; into the register moveUp: ST Y, holdPixel ; Reload old pixel color SBIW YH:YL, 32 ; Change pointer to look at new pixel ld holdPixel, Y ; Store new pixel color call invertCursor ldi cursorCounter, 0x00 ; Reset cursorCounter dontMoveUp: jmp endCleanUp redUp: ldi ZH, high(redColor) ldi ZL, low(redColor) ; load red color byte ld r16, Z ; check if already at max red color cpi r16, 0b01110000 breq endRed ;if not at max color, then add 1 to red color, meaning, 0b00010000 ldi r19, 0b00010000 add r16, r19 ; store this color in redColor st Z, r16 ; load this color into redPreview area ldi ZH, high(redPreviewPosition) ldi ZL, low(redPreviewPosition) st Z+, r16 st Z+, r16 st Z, r16 ADIW ZH:ZL, 30 st Z+, r16 st Z+, r16 st Z, r16 ADIW ZH:ZL, 30 st Z+, r16 st Z+, r16 st Z, r16 endRed: jmp endCleanup greenUp: ldi ZH, high(greenColor) ldi ZL, low(greenColor) ; load green color byte ld r16, Z ; check if already at max green color cpi r16, 0b00001111 breq end ;if not at max color, then add 1 to green color, meaning, 0b00000001 ldi r19, 0b00000001 add r16, r19 ; store this color in greenColor st Z, r16 ; load this color into greenPreview area ldi ZH, high(greenPreviewPosition) ldi ZL, low(greenPreviewPosition) st Z+, r16 st Z+, r16 st Z, r16 ADIW ZH:ZL, 30 st Z+, r16 st Z+, r16 st Z, r16 ADIW ZH:ZL, 30 st Z+, r16 st Z+, r16 st Z, r16 endGreen: jmp endCleanup blueUp: ldi ZH, high(blueColor) ldi ZL, low(blueColor) ; load blue color byte ld r16, Z ; check if already at max blue color cpi r16, 0b10000000 breq endBlue ;if not at max color, then add 1 to blue color, meaning, 0b10000000 ldi r19, 0b10000000 add r16, r19 ; store this color in blueColor st Z, r16 ; load this color into bluePreview area ldi ZH, high(bluePreviewPosition) ldi ZL, low(bluePreviewPosition) st Z+, r16 st Z+, r16 st Z, r16 ADIW ZH:ZL, 30 st Z+, r16 st Z+, r16 st Z, r16 ADIW ZH:ZL, 30 st Z+, r16 st Z+, r16 st Z, r16 endBlue: jmp endCleanup inputDown: ; First check to see where the cursor is located cpi cursorLocation, 1 BREQ screenDown cpi cursorLocation, 2 BREQ redDown cpi cursorLocation, 4 BREQ greenDown cpi cursorLocation, 8 BREQ blueDown ; First check to make sure we aren't at the bottom of the screen ; Bottom of the screen is positions 0x04C0 to 0x04DF screenDown: ldi r16, 0x04 ; If these are equal, it may be at the bottom, so don't move down cpse YH, r16 rjmp moveDown cpi YL, 0xC0 BRSH dontMoveDown ; If greater than the bottom left corner, don't move moveDown: ST Y, holdPixel ; Reload old pixel into old cursor location ADIW YH:YL, 32 ; Change pointer to look at new pixel ld holdPixel, Y ; Load new pixel call invertCursor ldi cursorCounter, 0x00 ; Reset cursorCounter dontMoveDown: jmp endCleanup ////// redDown: ldi ZH, high(redColor) ldi ZL, low(redColor) ; load red color byte ld r16, Z ; check if already at min red color cpi r16, 0b00000000 breq endRedDown ;if not at min color, then subtract 1 to red color, meaning, 0b00010000 ldi r19, 0b00010000 add r16, r19 ; store this color in redColor st Z, r16 ; load this color into redPreview area ldi ZH, high(redPreviewPosition) ldi ZL, low(redPreviewPosition) st Z+, r16 st Z+, r16 st Z, r16 ADIW ZH:ZL, 30 st Z+, r16 st Z+, r16 st Z, r16 ADIW ZH:ZL, 30 st Z+, r16 st Z+, r16 st Z, r16 endRedDown: jmp endCleanup greenDown: ldi ZH, high(greenColor) ldi ZL, low(greenColor) ; load green color byte ld r16, Z ; check if already at min green color cpi r16, 0b00000000 breq endGreenDown ;if not at min color, then subtract 1 to green color, meaning, 0b00000001 ldi r19, 0b00000001 add r16, r19 ; store this color in greenColor st Z, r16 ; load this color into greenPreview area ldi ZH, high(greenPreviewPosition) ldi ZL, low(greenPreviewPosition) st Z+, r16 st Z+, r16 st Z, r16 ADIW ZH:ZL, 30 st Z+, r16 st Z+, r16 st Z, r16 ADIW ZH:ZL, 30 st Z+, r16 st Z+, r16 st Z, r16 endGreenDown: jmp endCleanup blueDown: ldi ZH, high(blueColor) ldi ZL, low(blueColor) ; load blue color byte ld r16, Z ; check if already at min blue color cpi r16, 0b00000000 breq endBlueDown ;if not at min color, then subtract 1 to blue color, meaning, 0b10000000 ldi r19, 0b10000000 sub r16, r19 ; store this color in blueColor st Z, r16 ; load this color into bluePreview area ldi ZH, high(bluePreviewPosition) ldi ZL, low(bluePreviewPosition) st Z+, r16 st Z+, r16 st Z, r16 ADIW ZH:ZL, 30 st Z+, r16 st Z+, r16 st Z, r16 ADIW ZH:ZL, 30 st Z+, r16 st Z+, r16 st Z, r16 endBlueDown: jmp endCleanup ////// inputLeft: ; First check to make sure we are on the screen cpi cursorLocation, 1 BREQ screenLeft ; if not on the the screen, jump to endCleanUp jmp endCleanup screenLeft: ; First check to make sure we aren't on the left of the screen ; Left of the screen is any position with lower hex byte of ldi r16, 0x00 cp YL, r16 BREQ dontMoveLeft ; don't move if lower byte equal to 0x00 ldi r16, 0x20 cp YL, r16 BREQ dontMoveLeft ; don't move if lower byte equal to 0x20 ldi r16, 0x40 cp YL, r16 BREQ dontMoveLeft ; don't move if lower byte equal to 0x40 ldi r16, 0x60 cp YL, r16 BREQ dontMoveLeft ; don't move if lower byte equal to 0x60 ldi r16, 0x80 cp YL, r16 BREQ dontMoveLeft ; don't move if lower byte equal to 0x80 ldi r16, 0xA0 cp YL, r16 BREQ dontMoveLeft ; don't move if lower byte equal to 0xA0 ldi r16, 0xC0 cp YL, r16 BREQ dontMoveLeft ; don't move if lower byte equal to 0xC0 ldi r16, 0xE0 cp YL, r16 BREQ dontMoveLeft ; don't move if lower byte equal to 0xE0 ; if the lower byte is not equal to any of those listed above ; then it must not be on the left side of the screen moveLeft: ST Y, holdPixel ; Reload old pixel into old cursor location SBIW YH:YL, 1 ; Change pointer to look at new pixel ld holdPixel, Y ; Load new pixel call invertCursor ldi cursorCounter, 0x00 ; Reset cursorCounter dontMoveLeft: jmp endCleanup inputRight: ; first check to make sure we are on the screen cpi cursorLocation, 1 BREQ screenRight ; if not on the screen, right is meaningless, so jmp endCleanup jmp endCleanup screenRight: ; First check to make sure we aren't on the right of the screen ; Right of the screen is any position with lower hex byte of 00, 20, 40, 60, or 80, A0, C0, E0 ldi r16, 0x1F cp YL, r16 BREQ dontMoveRight ; don't move if lower byte equal to 0x00 ldi r16, 0x3F cp YL, r16 BREQ dontMoveRight ; don't move if lower byte equal to 0x20 ldi r16, 0x5F cp YL, r16 BREQ dontMoveRight ; don't move if lower byte equal to 0x40 ldi r16, 0x7F cp YL, r16 BREQ dontMoveRight ; don't move if lower byte equal to 0x60 ldi r16, 0x9F cp YL, r16 BREQ dontMoveRight ; don't move if lower byte equal to 0x80 ldi r16, 0xBF cp YL, r16 BREQ dontMoveRight ; don't move if lower byte equal to 0xA0 ldi r16, 0xDF cp YL, r16 BREQ dontMoveRight ; don't move if lower byte equal to 0xC0 ldi r16, 0xFF cp YL, r16 BREQ dontMoveRight ; don't move if lower byte equal to 0xE0 ; if the lower byte is not equal to any of those listed above ; then it must not be on the right side of the screen moveRight: ST Y, holdPixel ; Reload old pixel into old cursor location ADIW YH:YL, 1 ; Change pointer to look at new pixel ld holdPixel, Y ; Load new pixel call invertCursor ldi cursorCounter, 0x00 ; Reset cursorCounter ; Cursor located at a non-movable area, do not move the cursor dontMoveRight: jmp endCleanup ; switches between screen and colors inputTab: lsl cursorlocation cpi cursorlocation, 16 brsh tabToScreen tabCursor: cpi cursorLocation, 1 breq tabScreen cpi cursorLocation, 2 breq tabRed cpi cursorLocation, 4 breq tabGreen cpi cursorLocation, 8 breq tabBlue tabScreen: st Y, holdPixel ; Restore Pixel ldi ZH, high(screenLocation) ldi ZL, low(screenLocation) ; Change where cursor is pointing to ld r16, Z+ ; Get low byte mov YL, r16 ; Restore low byte ld r16, Z ; Get high byte mov YH, r16 ; Restore high byte ld holdPixel, Y ; Save new pixel call invertCursor ; Invert pixel color jmp endCleanup tabRed: st Y, holdPixel ; Restore Pixel ldi ZH, high(screenLocation) ldi ZL, low(screenLocation) ; Save current screen cursor location mov r16, YL ; Get low byte st Z+, r16 ; Save low byte mov r16, YH ; get high byte st Z, r16 ; save high byte ; show that red is selected ldi YH, high(redCursorPosition) ldi YL, low(redCursorPosition) ; Save old pixel (should be black) ld holdPixel, Y ; load new pixel value ldi r16, 0xFF st Y, r16 jmp endCleanup tabGreen: ; restore old pixel st Y, holdPixel ; show that Green is selected ldi YH, high(greenCursorPosition) ldi YL, low(greenCursorPosition) ; Save old pixel (should be black) ld holdPixel, Y ; load new pixel value ldi r16, 0xFF st Y, r16 jmp endCleanup tabBlue: ; restore old pixel st Y, holdPixel ; show that Blue is selected ldi YH, high(blueCursorPosition) ldi YL, low(blueCursorPosition) ; Save old pixel (should be black) ld holdPixel, Y ; load new pixel value ldi r16, 0xFF st Y, r16 jmp endCleanup tabToScreen: ldi cursorLocation, 1 rjmp tabCursor InputSet: ; first check to make sure we are on the screen cpi cursorLocation, 1 BREQ doSet ; if not on the screen, right is meaningless, so jmp endCleanup jmp endCleanup doSet: ; Pull in the red, green and blue values ldi ZH, high(redColor) ldi ZL, low(redColor) ld r16, Z+ mov redHold, r16 ld r16, Z+ mov greenHold, r16 ld r16, Z ; Add the registers together add r16, greenHold add r16, redHold ; Store this value in holdPixel mov holdPixel, r16 ; place this color value in SRAM st Y, r16 ;end jmp endCleanup inputClear: ; first check to make sure we are on the screen cpi cursorLocation, 1 BREQ doClear ; if not on the screen, right is meaningless, so jmp endCleanup jmp endCleanup doClear: ; Clear holdPixel ldi holdPixel, 0x00 ; Clear SRAM at cursor position st Y, holdPixel ;end jmp endCleanup ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;Program Code End;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;; reti ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;Syncing with the Sync Generator;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; The idea of the syncing up, is to initially sync up, ; the not need this code anymore. The Code determines ; when the horizontal pulses begin (after vertical pulses ended) ; so this interrupt determines when line 1 begins. ; The code accomplishes this by determining if the pulse that has been ; seen is a vertical or horizontal pulse. A horizontal pulse is a fall ; in voltage level, followed by a rise 4.7us later. A vertical pulse is pretty much ; the same thing, except a rise followed by a fall. Initially, the interrupt ; is set to interrupt on rising edges. After an interrupt occurs, the interrupt ; will switch from rising to falling edge, and then reset the timer counting the ; length of the pulse to zero. The interrupt is set to rising first so that ; this reset can occur. Now the rising and falling switches are done with every ; interrupt, and when a falling interrupt happens, the time is checked between ; that interrupt and the previous one (giving pulse length). The checks are done ; for the length of a vertical pulse. ; Interrupt 1 is used for syncing with sync generator EXT_INT1: ;Disable sleep while in interrupt disableSleep ; if timer hasn't been reset yet, reset timer cpi timerReset, 0xFF BREQ checkTime rjmp exitInt1 ; end 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 ; else 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 GIMSK, r16 ; Flip LEDs to opposite of what it is ;ldi r16, 0x0F ;out PORTB, 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 ;end lineCounter ==1 ;end timer > 50us main: initialization: ; Initialize stack ldi r16, LOW(RAMEND) out SPL, r16 ldi r16, HIGH(RAMEND) out SPH, r16 ;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;Initialize I/O;;;;; ;;;;;;;;;;;;;;;;;;;;;;;; initializePorts: ; PortA will be used for input, from switches ; Note: A 1 on a port bit corresponds to an unpressed switch ldi r16, 0x00 out DDRA, r16 ; 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 ldi r16, 0xFF out DDRB, r16 ; Start with LEDs half on, half off ldi r16, 0xF0 out PORTB, r16 ; PortC will be used to output to the TV ldi r16, 0xFF out DDRC, r16 ldi r16, 0x00 out PORTC, r16 ; 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 ldi r16, 0x00 out DDRD, r16 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;Enable interrupts and timers;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Disable interrupts on timers ldi r16, 0x00 out TIMSK, r16 ; Set Timer0 Control Register to count at clock speed/8 or 2MHz, counts at every half microsecond ldi r16, 0x01 out TCCR0, r16 ;Turn off PWM and other in Timer1 Control RegisterA ldi r16, 0x00 out TCCR1A, r16 ; Set Timer1 Control RegisterB to count at clock speed/8 ldi r16, 0x02 out TCCR1B, r16 ; 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 ldi r16, 0b00001010 out MCUCR, r16 ; Enable external interrupt 1 to seach for sync pulses. ldi r16, 0b10000000 out GIMSK, r16 InitializeVariables: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;Display Driver Initializations;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;Program Initializations;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Initialize cursor pointer at the upper left corner ldi YH, high(screen) ldi YL, low(screen) ; Initialize cursorCounter at 1 ldi cursorCounter, 1 ; Initialize cursor on the screen ldi cursorLocation, 1 ; Initialize holdPixel ldi holdPixel, 0x00 ; Initialize moveCounter ldi r16, 0 mov moveCounter, r16 ; Initialize redColor ldi r16, 0 ldi XH, high(redColor) ldi XL, low(redColor) st X, r16 ; Initialize greenColor ldi r16, 0 ldi XH, high(greenColor) ldi XL, low(greenColor) st X, r16 ; Initialize redColor ldi r16, 0 ldi XH, high(greenColor) ldi XL, low(greenColor) st X, r16 ;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;Initialize Screen;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;; ldi ZH, HIGH(screen) ldi ZL, LOW(screen) ; First set screen to black loadScreenLoop: ldi r16, 0 st Z+, r16 cpi ZH, high(endOfMemory) brlo jumper cpi ZL, low(endOfMemory) brlo jumper rjmp titleLoad jumper: jmp loadScreenLoop titleLoad: ; Set pixels if I want, on the screen, 0x04E0 is the bottom of the ; screen writable portion ldi XH, high(belowPaintable) ldi XL, low(belowPaintable) ldi ZH, high(title*2) ldi ZL, low(title*2) ldi r17, 1 titleLoop: LPM r16,Z adiw ZH:ZL, 1 st X+, r16 inc r17 cpi r17, 192 brlo titleLoop ; Enable interrupts, this should be the last part of the initialization sequence. continueInit: sei ; Once interrupts have been enabled, wait for the sync up of the device waitForSync: sleep rjmp waitForSync 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 ; When coming from he initialization of int0, ~4 clock cycles have already been used preLine: ldi pixelCount, 0x01 ; At the start of the line, so at first pixel ; 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 lineBlast ;16 lineBlast ;24 lineBlast ;32 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 reti ; 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 reti ;;;;;;;;;;;;;;;;;;;;; ;;;;;Subroutines;;;;; ;;;;;;;;;;;;;;;;;;;;; invertCursor: LD r16, Y ; load pixel into register ldi r19, 0xFF EOR r16, r19 ST Y, r16 ; store the pixel back into memory ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;Data Segment in Flash;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; title: .db 0 , 16, 16, 0 , 0, 0, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 .db 0 , 16, 0 , 16, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 .db 0 , 16, 0 , 16, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 .db 0 , 16, 16, 0 , 0, 0, 0, 0, 0, 4, 0, 4, 4, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 .db 0 , 16, 0 , 16, 0, 0, 0, 0, 0, 4, 0, 0, 4, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 .db 0 , 16, 0 , 16, 0, 0, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0