News
Documents
Source Code
Downloads
Message Board


Sega Megadrive Assembly Programming Basics

Copyright, Lewis Bassett, December 2005


Chapter 6 - VDP - Tiles, Colours and Planes

Section 3 - VRAM

VRAM is the main part of the VDP. It's where all the graphics data is kept. It's where the map of Plane A and B are kept. It's also where the sprite table is kept. Understanding about VRAM is essential.

VRAM is short for VDP RAM, and is the main internal memory used by the VDP. Just like any other type of memory, VRAM is addressable. The address '$0000' refers to the first byte in VRAM, '$0001' refers to the second byte, and '$A000' refers to the four thousand nine hundred and sixtieth byte. In total, VRAM contains 65535 bytes, so the maximum address is '$FFFF'.

VRAM is used by the VDP, which means that our main program, which is executed by the M68000, cannot access VRAM directly.

I'll explain about Plane A and Plane B in the next section. The sprite table will be explained in the next chapter. Right now, I'm going to explain about the Megadrive's graphic format, and how it's stored in VRAM.


The Megadrive Graphic Format

Every graphic that is drawn on to any part of the screen is made up from tiles. Tiles are simply little squares, that when put together, make up a much larger picture. A tile is made up from 64 pixels, arranged in eight lines of eight pixels.

So what is a pixel? It's simply a 4-bit (one nybble) value that tells the VDP to draw a single dot, by using a specified colour from CRAM. Each tile can be drawn by using any one single palette from CRAM, so in total, sixteen colours are available to each tile. This means that each single pixel can have one of sixteen values, where each value represents a different pallete colour. For example, '0' will draw that pixel using the first colour in the pallete, '1' will draw it using the second colour, '2' will use the third colour, etc, 'F' will draw that pixel using the last colour in that pallete.

Here's an example of a tile:

	dc.l	$00000000;
	dc.l	$11111111;
	dc.l	$12222221;
	dc.l	$12233221;
	dc.l	$12233221;
	dc.l	$12222221;
	dc.l	$11111111;
	dc.l	$00000000;
Assuming this tile used the following pallete, this is how it would look, once it's drawn to the screen:

Hang on a second! What happenned to the black pixels?

As I mentioned in the last section, the first colour of every pallete is the transparency colour. Any pixel that is set as '0' will be drawn as transparent, which means it'll display whatever is on the layer behind it. If it's a sprite, it'll display Plane A, if it's Plane A, it'll display Plane B, and if it's Plane B, it'll display the background colour.

Remember, one tile is just a tiny part of a picture. To make big pictures, multiple tiles are stored into VRAM, and then they are arranged into a larger image when they're drawn onto the screen later.

For example, let's take a look at how the artwork for Sonic, from Sonic the Hedgehog, is stored in VRAM:

As you can see, it's all jumbled up!

When the Sonic is drawn onto the screen later on, each of the tiles is arranged into the right order, onto the screen. Like this:

Now, as I explained, all images are made up from little tiles that are then arranged in order later, when they're actually displayed onto the screen. I'll explain how this is done in the next section. Don't get confused, these actual colours are not stored with the tile in VRAM. The 64 pixel tiles simply contain references to pallete positions. When the tiles are draw onto the screen later, we can determine which pallete the tile will be drawn with, and the image will be automatically generated by the VDP, using the colours contained in the appropriate pallete in CRAM.

Back in the day, Sega would provide software developers with special software, which would be used by artists to design the graphics. The software would then automatically generate pallete definitions and tiles, based on the original graphic.

The coolest thing about the Megadrive is that the same tile can be drawn using different combinations of colours, without the need to redraw the tiles. Since tiles are really just references to different colours in a pallete, we can redefine a the pallete it uses, or instruct the VDP to draw it with a different pallete, and the image will be displayed using completely different colours. If you've played Sonic & Knuckles, remember the part in Sandopolis Zone, where the lights go out and everything goes dark? This is done simply by redefining the palletes, so that they use dark colours. When the lights go back on, the pallete is simply changed back!

Throughout this document, we'll be using predrawn graphics, but we will try out a few of these different tricks.


Writing to VRAM

VRAM is used by the VDP, and is actually part of its internal hardware. The M68000, which is the processor which executes our ROM, doesn't have direct access to VRAM. Just like when writing to CRAM, we have to send an encoded command to the VDP control port, which is at $c00004, to tell the VDP to copy any data we send it into the VRAM. Any data we want to copy into VRAM then gets copied into the VDP data port, which you'll recall is at $c00000.

Again, because VDP commands are quite complex, I have prepared another subroutine that we can use in our programs to generate a VDP command, to copy data into VRAM, 'on the go'. This subroutine is also contained in the VDPCommands module. Just like before, make sure the module is included at the end of your program.

The subroutine we'll be using this time is called 'GetVRAMWriteCommand'. This subroutine takes an address in VRAM, and generates a VDP command to copy data into this address.

Here is how the subroutine is used:

	move.w	#$0000,		d0;
	bsr	GetVRAMWriteCommmand;
	move.l	d0,		$c00004;
The 'GetVRAMWriteCommand' subrouine requires that the register D0 contains the address in VRAM that we want to copy the data to. This address can be anything from '$0000' to '$FFFF'. After this subroutine has been executed, register D0 now contains a VDP command, which is then copied into the VDP control port ($c00004).

After the new command has been copied into the VDP control port, we can now begin copying some tiles into VRAM, by sending them to the VDP data port, located at address $c00000.

In the following example, three tiles are written into VRAM, by using the 'GetVRAMWriteCommand' subroutine:

	move.w	#$0000,		d0;
	bsr	GetVRAMWriteCommand;
	move.l	d0,		$c00004;

	move.l	#$00000000,	$c00000;
	move.l	#$00000000,	$c00000;
	move.l	#$00000000,	$c00000;
	move.l	#$00000000,	$c00000;
	move.l	#$00000000,	$c00000;
	move.l	#$00000000,	$c00000;
	move.l	#$00000000,	$c00000;
	move.l	#$00000000,	$c00000;

	move.l	#$00000000,	$c00000;
	move.l	#$00111100,	$c00000;
	move.l	#$01222210,	$c00000;
	move.l	#$12222221,	$c00000;
	move.l	#$12222221,	$c00000;
	move.l	#$01222210,	$c00000;
	move.l	#$00111100,	$c00000;
	move.l	#$00000000,	$c00000;

	move.l	#$11111111,	$c00000;
	move.l	#$23232323,	$c00000;
	move.l	#$44444444,	$c00000;
	move.l	#$56565656,	$c00000;
	move.l	#$77777777,	$c00000;
	move.l	#$89898989,	$c00000;
	move.l	#$aaaaaaaa,	$c00000;
	move.l	#$bcbcbcbc,	$c00000;

First of all, I've copied the address '$0000' into register D0. This is so that the 'GetVRAMWriteCommand' subroutine generates a command that tells the VDP to start copying our tiles into the start of VRAM. After the subroutine has been executed, I've copied the new command into the VDP control port. Now I can begin copying the tiles.

Each tiles is a sixty four pixel square arranged in eight lines of eight pixels, so I've written eight long words for each tile. Notice how the first tile is completly blank. By default, the Megadrive will use the first tile to generate the display for any part of the screen that has not been defined. Since we haven't yet defined any of the screen, we need the first tile to be blank, otherwise the VDP will display a screen full of the first tile, which we don't want.

Note, in order for us the write to VRAM, the VDP autoincrement register must be set.

Now, in reality, the code I've shown you above is VERY bad. Appart from anything else, the actual tile definitions are contained inside the code itself. This means, if anyone wants to change the graphics, they'll have to re-write half of the program! This isn't good at all, so we'll have to re-write the code so that it copys predefined tiles that are contained in the actual ROM. For this, we'll use a DBRA loop.

First, the tiles must be defined in ROM somewhere:

Tiles:
	dc.l $00000000
	dc.l $00000000
	dc.l $00000000
	dc.l $00000000
	dc.l $00000000
	dc.l $00000000
	dc.l $00000000
	dc.l $00000000

	dc.l $00000000
	dc.l $00111100
	dc.l $01222210
	dc.l $12222221
	dc.l $12222221
	dc.l $01222210
	dc.l $00111100
	dc.l $00000000

	dc.l $11111111
	dc.l $23232323
	dc.l $44444444
	dc.l $56565656
	dc.l $77777777
	dc.l $89898989
	dc.l $aaaaaaaa
	dc.l $bcbcbcbc
Now, when we're ready to copy them into VRAM, we can use a DBRA loop to copy the data to VRAM:
	move.w	#$0000,		d0;
	bsr	GetVRAMWriteCommand;
	move.l	d0,		$c00004;

	move.w	#47,		d1;
	lea	Tiles(pc),		a1;
	move.l	#$c00000,		a2;

CopyTiles:
	move.w	(a1)+,		(a2);
	dbra	d1,		CopyTiles;

First of all, I've used the 'GetVRAMWriteCommand' to generate a VDP command, which we can use to copy our tiles to the start of VRAM. The command is then sent to the VDP control port.

Before the DBRA loop can be executed, the number of times it should loop round must be specified. In this case, it's 48 (written as '47'), since we are copying 96 bytes (three tiles, which are each 32 bytes) as 48 words. Next, I've loaded the address of our tile definitions, in ROM, into register A1. The address of the VDP data port has been written to register A2, so that it now points to it.

Now, the program performs the loop, labeled as 'CopyTiles', forty eight times. Each time, the address of the tiles (in ROM) is incremented, so that it points to the next word along. The control port is NOT incremented; remember, the VDP takes care of this for us, since we've set the VDP auto-increment register earlier on. When this loop is finished, all three tiles are in VRAM, ready to be used by the VDP to draw some graphics.

This code is now MUCH faster and more efficient. It's also easier for us to maintain, because now, if we ever need to change any of the graphics, we only have to change the tile definitions instead of aload of code.

Now that I've explained all about VRAM and the Megadrive's graphic format, we can begin to actually generate some sort of display. In the next section, I'll explain how our programs can use the VDP to draw some graphics onto Plane A and Plane B, by using tiles in VRAM. After this, I'll go through a simple graphics demo with you, which we'll write completly from scratch.



Chapter 6 - Section 2 Contents Chapter 6 - Section 4


Designed & maintained by Lewis AS Bassett
SEGA, Megadrive, Genesis, Sonic the Hedgehog, etc are all owned by Sega Enterprises Ltd