Here is a brief review of how the original C64 handled graphics modes. The bit mapped screen had a resolution of 320x200 pixels. Every pixel was represented by 1 bit. So the entire bit mapped screen took up exactly 8K of RAM. that was for black and white. If you wanted color, you could use an additional 1K of RAM and define "color cells" on the screen. These cells divided the screen up into areas that were 8x8 pixels. Within each cell you could define a foreground and background color. However, this severly limited the types of graphics you could have on the screen because it was difficult to place different colored objects next to each other. However, at the time this was considered a good way to add color to a system without having to allocate much RAM.
Well, the DTV has plenty of RAM and several new video modes have been added. This document will cover the 8 bpp (stands for 8 bits per pixel) video mode. I will describe how to access the mode both from BASIC and Machine Language. From a perspective of simplicity, the layout of this mode is wonderfull. Each byte in RAM corresponds to a single pixel and you simply change that byte anywhere from 0 to 255 to tell it what color to be. Calculating what memory address you need for a certain pixel is also simplified. you simply take your X and Y coordinates, and do this formula: mem=(Y*320)+X and there you have it. Very simple.
The hard part comes into getting the information to the screen. As was stated before, the original C64 graphics modes only used between 8K and 9K and your CPU could directly write to that area of RAM. However, using 8 bpp means using 64K of RAM just for the screen. So it has to be placed in upper memory. But that means the CPU can't see it. There are two solutions. The DTV has a DMA controller. You can "map" areas of upper memory in and out of lower memory. That way the CPU can see bits of the memory at a time. I don't like this solution because it makes it complicated to write code which knows where a particular pixel is on the screen. However, screens writes would be faster this way. The way I'm going to show you uses the DMA controller to copy bits of RAM from one area of memory to another. You can copy as little as one byte if you want.
First, lets learn how to turn the graphics mode on. The following POKE statements in BASIC will activate the mode, and map the screen to RAM location $010000.
POKE 53311,1 : REM Enable extended features
POKE 53308,85 : REM Enable linear mode
POKE 53324,08 : REM Step = 8
POKE 53321,00 : REM Screen location LOW
POKE 53322,00 : REM Screen location MID
POKE 53323,01 : REM Screen location HIGH
$D03F = #$01 ; Enable extended features
$D03C = #$55 ; Enable linear mode
$D011 = #$5B
$D016 = #$18
$D04C = #$08 ; Step = 8
$D049 = #$00 ; Screen location LOW
$D04A = #$00 ; Screen location MID
$D04B = #$01 ; Screen location HIGH
There is one more minor detail before you can start sending things to the screen. Color 16 through 255 will already be what they need to be. But colors 0 through 15 are still configured to look like the C64's original color palette. The first 16 colors can be mapped to be any color you want. However, we need them to be grayscale. All the rest of colors 16 through 255 are varios colors with no grays. So the only colors we are missing are the grays. So you can execute this command to define them:
|FOR Z = 0 TO 15:POKE 53760+Z,Z:NEXT Z|
Now your graphics mode is turned on and you palette is defined. We're ready to start sending it data through the DMA controller. The DMA controller is very easy to operate. Basically, you just fill in the numbers of what you want it to do, and tell it to go.
POKE 54016, 00 : REM SOURCE LOW
POKE 54017, 192 : REM SOURCE MID
POKE 54018, 64 : REM SOURCE HIGH
POKE 54019, 00 : REM DESTINATION LOW
POKE 54020, 00 : REM DESTINATION MID
POKE 54021, 65 : REM DESTINATION HIGH
POKE 54022, 01 : REM SOURCE STEP LOW
POKE 54023, 00 : REM SOURCE STEP HIGH
POKE 54024, 01 : REM DESTINATION STEP LOW
POKE 54025, 00 : REM DESTINATION STEP HIGH
POKE 54026, 01 : REM LENGTH LOW
POKE 54027, 00 : REM LENGTH HIGH
$D300 = #$00 : REM SOURCE LOW
$D301 = #$C0 ; SOURCE MID
$D302 = #$40 ; SOURCE HIGH
$D303 = #$00 ; DESTINATION LOW
$D304 = #$00 ; DESTINATION MID
$D305 = #$41 ; DESTINATION HIGH
$D306 = #$01 ; SOURCE STEP LOW
$D307 = #$00 ; SOURCE STEP HIGH
$D308 = #$01 ; DESTINATION STEP LOW
$D309 = #$00 ; DESTINATION STEP HIGH
$D30A = #$01 ; LENGTH LOW
$D30B = #$00 ; LENGTH HIGH
This will setup the DMA controller to get ready to copy 1 byte of data from location $00C000 to $010000. Since we've set our screen to be located at $010000, that means whatever byte you have stored at $00C0000 will become the first pixel on the screen. That may look like a lot of things to set just to write one pixel to the screen. But you only have to set most of these once, and you will only have to change the values you need as you go along. So, for instance if you were only going to be plotting one pixel at a time , you could just modify the 3 destination addresses as needed and leave the rest alone. however, I will note that it will be significantly faster to allow the DMA controller to move large chunks of data at once, when possible. But now how do we tell it to go?
|POKE 54047,13 : REM START TRANSFER||$D31f = #$0D ; START TRANSFER|
That will do it. As soon as you change this bit, the transfer will happen. If you are transfering large chunks of data, it is important to wait for the DMA controller to finish before changing any values. There is a register you can watch to see if it is done. However, I've found so far that in BASIC, the DMA controller is always done before you can write to it again. That won't be the case in machine language, though. Just read bit 0 of 54047 ($D31F). If the bit is 1 it is still busy and if it is 0 then it is finished.
|WAIT 54047,1,1 : REM WAIT FOR DMA TO FINISH||(NEED ML ROUTINE HERE)|
You may have noticed on the source byte that it was 64 instead of 0 and the destination was 65 instead 01. That is because the bits 6 and 7 of the source and destination HIGH byte are used tell the DMA transfer whether the information is comming to/from RAM or ROM. Look at the following illustration: