Graphics Programming

Eric Ball Wrote (on AA)

You can't really think of the 7800 in terms of bitmaps like the Apple ][ or any other computer or console which uses a frame buffer for graphics. You certainly could allocate a chunk of RAM to use as a bitmap, set up some static display lists with entries pointing to the RAM, then have your program update the RAM like a frame buffer. Unfortunately, I think you'd find there isn't enough RAM or CPU time to do that method justice.

As Yoda says, "you must unlearn what you have learned."

7800 graphics data is stored as bytes, not pixels, with each byte representing 2 to 8 pixels depending on the graphics mode. The simplest two graphics modes are called 160A (2 bits/pixel, 160 pixels/line) and 320A (1 bit/pixel 320 pixels/line). The remaining four graphics modes don't have MSB to LSB = left to right bit to pixel mapping.

So say we want a 160A sprite which looks like a 4x4 pixel circle in 3 colors (to give a nice highlight):
0 1 2 0
1 1 3 2
1 1 1 1
0 1 1 0
which would translate to
        DC.B    %00011000, %01011110, %01010101, %00010100

Color = 0 translates to transparent or background (at least for 160A), assuming you have "Kangaroo Mode" turned off.

Well, not really. Due to how the 7800 handles graphics, the data is stored with each line on a separate page & upside down, thus:
        ORG     $F000
BALL
        DC.B    %00010100
        ORG     $F100
        DC.B    %01010101
        ORG     $F200
        DC.B    %01011110
        ORG     $F300
        DC.B    %00011000


Now, to display this in the center of the screen, you would create the following in RAM:
DLL
        DC.B    16, >DL0, <DL0
        DC.B    16, >DL0, <DL0
        DC.B    16, >DL0, <DL0
        DC.B    16, >DL0, <DL0
        DC.B    16, >DL0, <DL0
        DC.B    16, >DL0, <DL0
        DC.B    16, >DL0, <DL0
        DC.B    7, >DL0, <DL0
        DC.B    4, >DL1, <DL1; num lines, display list pointer
        DC.B    16, >DL0, <DL0
        DC.B    16, >DL0, <DL0
        DC.B    16, >DL0, <DL0
        DC.B    16, >DL0, <DL0
        DC.B    16, >DL0, <DL0
        DC.B    16, >DL0, <DL0
        DC.B    16, >DL0, <DL0
        DC.B    8, >DL0, <DL0

DL1
        DC.B    <BALL, 31, >BALL, 78; graphics pointer, 32-width, X-pos
DL0
        DC.B    0,0; end of display list


For things like backgrounds there are a couple of options. The first is to store the background as a large sprite. The other is to use "character tiles", where the display list entry points to a series of bytes, which are then used as the LSB of the sprite pointer. This requires more DMA cycles, but uses less ROM. You can also easily update the tile list instead of the display list entries.