This year I've been working on a design for a 6502-based game console. I'm targeting a whole list of (admittedly arbitrary) requirements including sounds, custom controllers, cartridges, and most importantly...
color composite video. In addition, I'm trying to stick to parts still being made today (for reproducibility), and avoid black-box programmable components (for educational/challenge value, and not counting cartridge ROMs).
I recently hit a technical milestone I'm very excited about, but for the sake of context it seems prudent to start the story from the prototypes I did in the Winter/Spring. To cover, like, what kinds of problems I already ran into and learning that had to happen along the way.
I started by studying the schematics to the original arcade Pong to generate sync signals, then diverged to use the sync counters as addresses and feed the contents of an EEPROM through a shift register to output a black and white image on a TV screen:
Attachment:
00_hams.jpg [ 1.98 MiB | Viewed 1331 times ]
The circuit used a 28C64 (64 kbit) EEPROM for image storage as a stand-in for the dual-port RAM I'll eventually use. The image format was 1bpp 256x256 bitmap, which can be exported from Photoshop and de-headered with a simple script.
Attachment:
01_hamsboard.jpg [ 1.14 MiB | Viewed 1331 times ]
This was maybe like the third PCB I had ever designed, which is the excuse I now give for the flaws such as missing bypass caps and wacky traces. The circuit design itself also had the flaw that I was starting reading each row of memory at HSync, leaving the first several columns off-screen. I compensated by moving the image to the right, with wrap-around, and wiring the address pins so that the image would repeat horizontally. This roughly centered the image on screen, but showed extra tiling on the sides.
I next designed a version that tested out my ideas for a cartridge pinout. (Though without yet tackling the image positioning)
The circuit is mostly the same, but I forgot the autorouter and remembered the bypass caps.
Attachment:
02_gametankmini.jpg [ 680.37 KiB | Viewed 1331 times ]
On a whim I had included certain, er,
subtle design flourishes on the shell. It was shortly afterward I started calling my project the "GameTank"
Attachment:
03_gametankminiopen.jpg [ 1.27 MiB | Viewed 1331 times ]
Following this prototype I set video aside for a bit to work on address decoding and audio. But those will get separate a separate post.
During the last couple of months I had returned to video, with renewed obsession. Thinking about addressing made me think about what kind of memory I was going to use; and thinking about memory had me thinking in a more concrete way what kind of image specs I would target. I really wanted to add color, and to have more than one color. This ruled out 1bpp. I would have to either decrease resolution or increase memory. For a while I thought that I was going to do 2bpp and have four colors. I even made space for four palette registers in my memory map.
However, I decided that dealing with multiple pixels in a byte would introduce more overhead than I really wanted to contend with. Plus, I also had some DMA ideas brewing that I'll get into later. So ultimately I decided to go with 8bpp, and 128x128 resolution.
Simultaneous with the memory thinking, I also considered how to generate the color carrier. I learned that the color part of a composite signal is controlled by a 3.58MHz wave concurrent with the brightness information. That the TV set expects a short "burst" of this signal just after HSync, and that it compares the
phase of the carrier to the burst to determine the hue.
I read somewhere, though I can't find the source, that the NES generates its colors in the HSL composite domain rather than RGB, and it does this by clocking a shift register at 8x the color carrier frequency. This is the approach I ended up taking.
Attachment:
04_breadboard.jpg [ 2.39 MiB | Viewed 1331 times ]
Attachment:
05_breadboard_annotated.jpg [ 2.78 MiB | Viewed 1331 times ]
I rebuilt my monochrome video breadboard as a starting point. The new parts included a 28Mhz oscillator to replace the previous 14Mhz, a shift register, and an 8-way selector/multiplexer. The 28Mhz square wave clocked the shift register, and a 3.5Mhz division was used as the data. By changing the select bits on the multiplexer, I was successfully able to choose from eight phases. After some analog trial-and-error with resistors and opamps, I convinced the TV that this was indeed a color video signal.
Attachment:
06_testpattern.jpg [ 769.4 KiB | Viewed 1331 times ]
This pattern isn't just for being fancy, either. Between a pair of resistor-ladder DACs and the multiplexer, I had 3 bits for each of Hue and Luminosity; as well as 2 bits for saturation. By hooking these control bits up to the sync counters I could cycle through all possible values, and continue fiddling with resistors until I was satisfied with the range of colors. This small dashboard screen for backup cams was also the most temperamental of all the screens I'd tested on, so it made a good baseline for compatibility.
Finally, my most recent addition has been to connect a ROM and take a swing at the image positioning problem I had before. I connected two more 4040 counters on an additional breadboard, which followed the same clock as the sync counters but used HBlanking and VBlanking as their reset signals. This way their counts would be zero until reaching the "picture" part of the screen. Or at least a lot closer to it.
Attachment:
07_darkside.jpg [ 66.65 KiB | Viewed 1331 times ]
At first I thought I was going to have to write a custom converter to put photos into my hardware's 3:2:3 Hue-Saturation-Luminosity format. However, by generating a color table file I was able to leverage Photoshop once again. When converting an image to Indexed Color, Photoshop looks at each of the original pixels and finds the closest matching color in a provided table. This pixel gets saved to file as a single byte, normally interpreted as the index of the color in the table. Thus I generated a colors file containing the colors my board could display, ordered such that each one's index matched its HSL representation.
Currently the image only puts 80-ish pixels on the screen horizontally and 60-ish vertically. When I get home I next want to try shifting the horizontal address counter up by 8 to the 28Mhz and then back down by 5 with a 74HC161 in div-by-N configuration to get the image to more closely align to the screen.
After that I'll do a test with dual-port RAM then start prototyping the DMA scheme I alluded to earlier. Essentially I'm hoping to set off memory transfers between VRAM and a dedicated Graphics RAM bank that run independently of the 6502, transfer rectangles instead of contiguous memory ranges, and transfers at 1-2 cycles per byte instead of the cycles it takes for the CPU to fetch instructions, load and store registers, and perform loop comparisons. But that feels like a writeup for a separate post.
Thanks for reading!