Toorum's Quest II - ATmega328P based retro video game with 256 color graphics

The mistake you did, Nick, with your 17cycles and the 9th bit was not the hardware but not checking your software.

A 16MHz AVR can do a heck of a lot more than displaying 20char/per line VGA.

PetriH:
Yes, there are a few tricks at play :slight_smile: There can only be three sprites per scanline but I do multiplexing so that I have different set of three sprites per scanline. There can be virtually any number of sprites on the screen vertically. Also, collectable items like gold and hearts are technically background tiles. I just animate the tiles, i.e. swap their tile pointers in RAM. I divide the screen vertically to regions and scan one region each frame. If the tile is a heart or gold I update its pointer. This way updating the tiles is quite efficient.

Does it render directly from tiles? (ie. no charmap...)

janost:
The mistake you did, Nick, with your 17cycles and the 9th bit was not the hardware but not checking your software.

A 16MHz AVR can do a heck of a lot more than displaying 20char/per line VGA.

I would be interested to hear how, excluding external hardware, given that 20 characters is 160 pixels (at 8 pixels per character).

According to my calculations as described here you have 31.74 µS for each horizontal scan line (using 525 lines at 60 Hz).

(1/60) / 525 * 1e6 = 31.74 uS

Divide that by 800 pixels for one line including the pulse width (96 pixels) the back porch (48 pixels) and the front porch (16 pixels).

((1/60) / 525 * 1e9) / 800 = 39.68  nS

So that is 39.68 nS per pixel.

Now clearly we can't clock out a pixel every 39.68 nS with a CPU clocking at 62.5 nS per clock pulse.

You can't clock out a pixel in a single clock cycle (I don't think, unless you clock out a fixed value), so you need two clock cycles for the SPI hardware to do it. Now the closest you get then is:

 125 / 39.68 = 3.15

So, rounding up, four "VGA" pixels in that time. That is, each pixel is stretched 4 x horizontally.

So if you can demonstrate where my calculations are wrong, and you can send "a heck of a lot more" I would be pleased to hear it.

PetriH:
The hardest part was getting sprites to work. I had to painfully cycle count the scanline routine so that it fetches tile data and masks sprites on top of the scanline while outputting a pixel every 6 cycles.

I've been trying to guess your timing figures, and from the quoted 104 horizontal resolution, and the NTSC standard of 51.5 µS for the visible portion of the line I am estimating that you have 8 clock cycles per pixel:

 8 * 62.5 * 104 = 52000 nS

Does that sound right? It can't, of course, because you said a pixel every 6 clock cycles, so where did I go wrong?

I thought I'd do quick search on the rgb / ntsc chip...

http://belogic.com/uzebox/index.asp

It seems there's quite a lot going on!

fungus:
Does it render directly from tiles? (ie. no charmap...)

I have a 13x10 buffer of tile pointers in RAM. Each entry in the buffer points to the start of a tile in ROM. Storing addresses rather than tile indices is much faster..

I'm not filling the entire scanline with graphics. I output 104 pixels, 1 pixel per 6 cycles. That leaves time just enough time for processing sprites etc. In the 80s they did the same trick and called the longer horizontal blank "borders" :slight_smile:

Also, one consideration is that using 8 cycles per pixel vs. 6 cycles per pixel results in blockier graphics, which I wanted to avoid. With 6 cycles and doubled scanlines, the pixels are closer to being squares rather than wide rectangles.

Note that the actual implementation is much more complex than this: there is actually three "threads" of execution going on on each scanline. One thread does the background tiles, one thread blends sprites on top of the tiles, and one thread outputs the pixels. Because I have to output a pixel every 6th cycle, the threads have to be interleaved.

cjdelphi:
I thought I'd do quick search on the rgb / ntsc chip...
http://belogic.com/uzebox/index.asp
It seems there's quite a lot going on!

Uzebox is an impressive feat indeed, but they also use a more powerful microcontroller with double amount of RAM and ROM, and almost twice the clock frequency. My project was based on Arduino UNO. We both use the same AD725 chip for RGB -> NTSC though.

PetriH:
Uzebox is an impressive feat indeed

Yep. I hadn't seen those before. They look ideal for a project I'm thinking about (I need 240x224 graphics for it) but they're really really expensive. $74 for a kit? Ouch!

I can get two Raspberry Pis +change for that (or even an Arduino Uno+Gameduino).

New version with music and sound effects!

I also wrote a technical writeup and released the source code:

I'll happily answer any questions about the design or source code.

I know that at least Nick was interested to see the source code, so I'm bumping this one time in case the update was unnoticed.

A VGA line is 256pixels long using an 8MHz pixelclock.
That is 32characters minus a few for sync and porch.

24 or 25 char per line should be possible.
It's not "a heck of alot more" but reaching Arcade Graphics level.

I just saw this on Hackaday. Well done.

PetriH:
I know that at least Nick was interested to see the source code, so I'm bumping this one time in case the update was unnoticed.

I just got my AD725 chip in the mail yesterday so I'll try to get your demo working. Thanks for all the info.

OK, I got that to work more-or-less. Proof:

The screen is slightly noisy because I had a long cable run to the TV and also assembled it on a breadboard. One slight snag, I don't have an NES controller so I can't actually move my character around. I'll order one from eBay, but what sort do I want exactly? Just a classic NES controller?

Errata:

  • On the circuit 4FSC pin number is not marked (it is pin 3).
  • You might mention that AD725 pins 9 and 11 are NC.
  • On the Atmega328 the AUD pin number is not marked (it looks like it is pin 11).

By the way, I didn't have a 14.31818 MHz oscillator (waiting on a delivery) so I used the 17.734475 MHz one which had arrived. I tied pin 1 to Gnd rather than +5V (to indicate PAL) and it works fine.

I haven't tested the sound yet.

It's ultra cool however. I'm really impressed you managed to squeeze this sort of performance out of it. Once I get my controller I'll be able to give it a real test.

Got the sound working, that sounds good. Maybe I can make an NES emulator with a Wii nunchuk. Hmm, I wonder what the NES protocol is? I could have a second Arduino reading the nunchuk and outputting the appropriate serial stream.

Hmm, a bit of Google-fu seems to indicate that the emulator needs to respond to a /LATCH and then respond with 8 bits upon receiving a clock signal. An SPI slave configuration might achieve that quite nicely.

Oh well, that might be tomorrow's project. :slight_smile:

Wow, you got it running already! That was fast! To my knowledge you're the first one.

It's hard to say from the photo but it looks like the colors are somehow wrong, too much red in highlights. Maybe the color signals are wired incorrectly or maybe the resistor values are inaccurate. Or maybe it's just the photo? Also, please change aspect ratio of your TV to 4:3, it looks much better that way.

About the image quality in general. I couldn't get a high quality video signal until I built it on PCB. On PCB the signal is very clean. I guess the breadboard and all the wires cause too much interference.

Yes, the project uses a standard NES controller (not SNES). The NES controller has a 8-bit shift register, so it's a matter of reading the bits one by one. See gamepad.cpp for details.

Thank you for the corrections. I will update the schematic.

Oh, I just realized that you're running the AD725 in PAL mode but the video signal is synced to NTSC. Your TV is probably confused about this and tries to interpret the colors as if it were receiving a NTSC signal? This might be the culprit for the strange colors… or maybe not, I would expect the colors to be totally off...

It's hard to say from the photo but it looks like the colors are somehow wrong, too much red in highlights.

Well, I have cough not put in all the resistors yet. I was testing to make sure it worked. I haven't put in the 3k ones. I note that you only have 2 x 3k resistors, and therefore the blue signal won't be quite the same as the red and green ones (no doubt so you could output everything through PORTD).

Oh, I just realized that you're running the AD725 in PAL mode but the video signal is synced to NTSC.

I was pleasantly surprised that it worked at all. I expected to have to wade through the code and made modifications to allow for the different pixel rate.

I couldn't get a high quality video signal until I built it on PCB.

I must confess that my PCB building skills are almost non-existent. I managed to make once a while back but couldn't drill the holes into it in any sort of straight line.

The NES controller has a 8-bit shift register, so it's a matter of reading the bits one by one. See gamepad.cpp for details.

My controller emulator is not exactly working, I am debugging that right now.

Oh, that explains it :slight_smile:

Blue has only 2 bits so that colors fit into 8 bits -- the human eye is least responsive to blue, so that's why red and green have more bits.

btw. the values of resistors are critical to get linear distribution of color bits. Resistors values are 806, 1.58K and 3.16K. Each value is approximately x2 the previous one. You can use other values as long as they follow the same pattern: x1, x2, x4.