When the project came to a close we had nearly obtained to our original goal of outputting a simple game to the TV. We origninally hoped to play something along the lines of an electronic air hockey but the issues with generating the required signals for TV output proved to be no trivial task. The Tic-Tac-Toe game gave us the opprotunityto make use of our character library and also gives an example of how the user could output to the screen in real time (the button presses for the game).

Tic Tac Toe: Code Here

            We used a standard keypad where the numbers 1-9 mapped to the positions of the game grid, a task running off the Timer0 interupt was used to detect a button press. After debouncing the button press, a function called 'taken' was called. This function checked to see if it was a valid button press 1-9 and that no other player currently occupied that square. Button A was used to reset after a draw or to start the next round after a win. If it was a valid move for one of the players, that players' "square occupied" array was updated and the function checked to see if that made them a winner. If it did, the score was updated, a flashing line was drawn through the winning combo and a short time later the score screen popped up. After pressing A to continue the loser then started the next round.

The Telly:

            The generation of the TV signals is what this project was really all about. This required very precise us timing intervals, which was accomplished by using Timer1 clear on CompA match interrupt set to go off every 64 us (which was the time it took to draw each line to the TV). Within this interrupt we toggled PORTC to encode the image information and PORTD for the syncing information. See the HW and Schematics page to see how this is all set up to generate the required output voltages.

            Upon entering the interrupt we first set PORTC.0 = 0V (since there should be no image data while syncing) and PORTD.5 was also set to 0V. We then checked what line we were on to see if it was a normal image line that starts off with an hsync or if we were at the bottom of the screen and had to output the vertical sync pulses. If it was an hsync we would wait an additional 4us (from the time that PORTD.5 = 0V) and then set PORTD.5 = 5v to accomplish the precisely timed hsync. The code would then jump to the display code.

            If, upon entering the interupt, it was decided that a vsync was necessary we jumped to the vsync loop. There was a counter in the loop which made it execute 5 times (we know the documentation says 3 vsyncs but what can i tell you...). After spitting out the vsyncs the line counter was now reset to 255 and the next time the interrupt was entered it started with the hsync(ideally to do the post-equalization pulses) and in our case image data. Because we used a char to keep track of the line number, a need for a vsync occured every 255 lines, after ouputting the 5 vsync pulses we seem to have chopped off the bottom 2 lines of our TV but that didn't seem to hurt the final results.

Image Display:

            Along with normal data display, there were 2 special cases at this point. Since each vertical sync is supposed to be preceeded by 3 pre-equalization pulses and followed by 13 post-equalization pulses, you need to check for when you are on these lines and simply output no data. We implemented the pre-equalization pulses which was enough to generate a steady TV output, if we had more time with the project the effects of the post-equalization pulses would be investigated. When you are not towards the very end the screen (pre-equalization pulses) or at the very top (post-equalizatoin pulses) you need to use the time between the hsync and the image data to load the registers with the data for that line of the screen. You have roughly 8us to do these computations before shifting your image rightward, in older TV's this 8us time was used to bring the beam back to the next line.

Realistic resolution:

            There are approximately 52 us of display time per scan line with 8 us to setup display information.  The scheme we used was to have a screen buffer in SRAM that held the state of each of our “pixels.”  Then in the 8 us we buffered a line from memory into registers.  Then the registers were shifted left one bit at a time and the carry-bit in SREG was outputted to PORTC.0 as the picture information of the signal.  We chose to buffer 8 bytes of memory which gave a horizontal resolution of 64.  The number of lines we could easily store and manipulate were 32.  So the total resolution was 64x32 (256 bytes). Our actual screen array was 512 bytes. This was because there was some strange bug in displaying our output. The display actually draws the first 256 bytes, draws black lines the rest of the screen and then vsyncs and draws the next 256 bytes. This added a nice feature that if the byte 0 was all black and byte 256 all white, it would flash in the same position on the screen. It wasn't what we were really going for but we ran with it.  The Mega163 only has 1024 bytes of SRAM.  The limiting factor to how much horizontal resolution is a function of how many registers you can safely load in the 8 us setup time.  The limit to vertical resolution is 262 (NTSC standard), but in our case was also limited by the amount of SRAM.  The screen array could not be much more than size 300 before we ran out of memory. 

            With 64 pixels horizontally, that gave about  0.8125 us per pixel to output.  We could have outputted pixels at a rate of 1 per 3 cycles (lsl, in, out) or every 0.375us, but then only half of the screen was used.  So we just added nop’s to extend the width of each pixel to fill the screen.  To enlarge the picture vertically, we just redrew a line 4 times which lengthened the pixels. 

Screen Buffer

            The screen buffer was 512 bytes in size.  Each bit (or pair of bits as explained above) represents a pixel.  A logic 1 outputs 5V to PORTC.0 and a logic 0 outputs 0V to PORTC.0.  So 0 is for black and 1 is for white.  The screen buffer is only edited in the slow C code.  The fast assembly code is used to buffer the output in registers before displaying.  

Character Library

            A character library was generated, using a 8 pixel x 8 pixel area for each character.  Each character is actually 6x6 pixels with a 1 pixel black buffer all around.  Each capital letter A-Z and number 0-9 can be drawn by call the appropriate function.  The parameter passed to the function tells the function the array offset of screen[ ] from which to start drawing.  With this method, 4 lines of 8 characters each can be displayed.   And also any character can occupy any of 192 positions on the screen.

Display Anomalies

             Yes, everything is not perfect in TV Land.  Many many hours were contributed to trying to clear up the screen anomalies.  These came in the form of a flashing set of pixels on the last line, stray pixels that flash, and screens that won't properly erase even thought the screen buffer is set to 0x00 (all black).  More than likely these were not due to synchronization problems but rather to how memory was accessed.  Programming in assembly provides almost no memory protection.  Even in C the programmer has little protection for harming vital memory.  It is our belief that this lack of protection and lack of experience using assembly to access memory is what has led to these problems with the display.  Almost everyone who sees our screenshots asks, "Why are there those white dots?"  A few stray pixels, albeit annoying, do not really take away from the project.