![]() |
![]() |
Sega Megadrive Assembly Programming BasicsCopyright, Lewis Bassett, December 2005
Section 4 - Plane A & Plane B Now that I've explained how to store tiles into VRAM and how to assign colours to each of these tiles by storing colours in CRAM, I'm now going to explain how to draw these tiles onto Plane A and Plane B. First, let's go over how the Megadrive generates the display again. All display is handled by the Video Display Processor (VDP), which is a second processor that works independantly from the M68000. The VDP generates all display by using four main layers: the background colour, Plane B, Plane A, and the sprite table. The background colour is almost always the first colour of the first pallete, and is the bottom layer of the screen. If everything in front of the background colour is transparent, then the background colour will be displayed. This is why the background colour is often also called the transparency colour. On PAL systems, the background colour is also used for the top and bottom borders of the screen. On top of the background colour is Plane B. Plane B is a large scrollable field that has tiles on it. Plane B is usually used for the background of a game. If an image uses transparent pixels, then the background colour will be displayed underneath those pixels. On top of Plane B is Plane A. Plane A is also a large scrollable field, with tiles on it. Plane A is mostly used for platforms, and generally anything that is displayed on top of Plane B. If a tile on Plane A has transparent pixels, then Plane B will be displayed where those pixels are. At the very top of the screen is the sprite table. Sprites are small objects, usually made up from four or so tiles, which can be moved around on top of Plane A. I'll explain more about sprites in the next chapter. Plane A and Plane B are usually alot larger than the screen. If we want to display a part which the screen doesn't cover, then we need to tell the VDP to scroll Plane A or Plane B, so that it can be covered by the screen. I'll explain this a little later. Plane A and Plane B are always the same size as each other. Plane A and Plane B are generated by using maps. A map is basically a list of tiles, in order that they are to be displayed, which are stored in VRAM. Plane A and Plane B each use a seperate map. Each time the VDP generates a frame of display, it will read the map contained in VRAM, and use it to display Plane A or Plane B by using the tiles specified.
So how exactly does a map look? Really, a map is just a list of numbers, which are each one word (16-bits) in size, often called pointers. Each of these words simply points to a tile in VRAM, by it's position, so that the VDP can draw the appropriate tile, in the appropriate place. Each word is used to generate one eight by eight pixel part of the screen, so in total, many pointers are used to completely fill Plane A or Plane B. How many words are needed depends on how big the particular plane is. Remember, we determined the size of both Plane A and Plane B by setting the sixteenth VDP register. In our module we made earlier, named 'VDPInit.asm', we set Plane A and Plane B to both be 32 tiles wide, and 32 tiles high. Since each tile is drawn onto a plane with one word (16-bit) value, our maps for both Plane A and Plane B will be made up from 1024 pointers each! That means that each map is 2048 (or 2 Kilobytes) each in size. Remember, a tile can be drawn onto a position by using one 16-bit pointer. Each pointer simply contains the number of the tile to draw into that position. Each tile is numbered, depending on the order it is contained at the start of VRAM. For example, the very first tile in VRAM (usually a blank tile) is referred to as '$0000', the second tile is referred to as '$0001', and the third as '$0002'. You get the idea. So how exactly do we tell the VDP where to draw a tile? Well, we don't exactly. Each word of the map actually represents a position on Plane A or Plane B. The first word represents the very first eight by eight pixel space at the top left corner of the plane. The next word along represents the very next eight by eight pixel space, on the same row, but eight pixels along. Because in our case, Plane A and Plane B have already been set as 32 tiles long and 32 tiles high, the thirty thrid pointer of the map represents the first eight by eigth pixel on the second row from the top. So if we set the sixty fifth 16-bit pointer to '$0004', the VDP will draw the fifth tile at the very left end of the third row! In an actual program, a definition of a map might look like this: dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000;Wow! That's alot of pointers isn't it? Remember, because we set the size of both Plane A and Plane B as 32 tiles high and 32 tiles long (which by the way, is the smallest size), we require 1024 pointers in total, for each plane. This means that one pointer is used to draw one tile into one eight by eight pixel position on either Plane A or Plane B. Another map definition, just like this one, would be used for the other plane, so that both Plane A and Plane B are drawn. Each of these words represents the position of a tile in VRAM. The VDP uses these one word pointers to draw a specified tile into a position on the plane. Back in the day, Sega would provide special software to generate a map definition like the one above. It would be the job of level designers to decide where each tile would be drawn. Maps like this one are drawn into VRAM by our program code. Remember in Section 1 of this chapter, when we set some of the VDP registers. The second VDP register contains the address of the map used to draw Plane A. The fourth register contains the address of the map used to draw Plane B. Both registers contain addresses in VRAM, which means our program code must copy these maps into VRAM before the VDP can draw anything onto the screen. In our module we made, named 'VDPInit.asm', we told the VDP that it could find the map for Plane A at VRAM address $A000, and the map for Plane B at VRAM address $C000. There are some other more advanced things that our one word (16-bit pointers) can do, and I'll explain these a little later.
Remember, as I said, the VDP expects the find two maps in VRAM. One map is for Plane A, and should be at VRAM address $A000. The other map is for Plane B, and should be at VRAM address $C000. We told the VDP that it could find the two maps at these addresses by setting VDP registers two and four. Okay, so the first thing we need to do is define two maps inside our ROM somewhere, out of the way of the main program code. Program data, including maps, graphics, palletes, sound and colours, are usually defined somewhere at the end of the ROM, after all of the main program code. Here is an example definition for two maps, one for Plane A and one for Plane B: PlaneAMap: dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; PlaneBMap: dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000; dc.w $0000, $0001, $0002, $0003, $0004, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005; dc.w $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0005, $0004, $0003, $0002, $0001, $0000;These two maps would be placed at the end of the ROM, after the main program code. In this example, both maps are exactly the same. In real life, they'd obviously be different from eachother. Both maps are made up from 1024 pointers, which means they'd be used to make up a display of thirty two tiles high and thirty two tiles wide. Okay, so now that we have two map definitions, we have to write them to VRAM, at the addressed we specified earlier when we set the VDP registers. For this example, I'm going to assume that there are already some tiles in VRAM and some colours in CRAM. To write the maps to VRAM, we'll use two seperate DBRA loops, one for each map. Before we can perform the loop, we must first setup some address registers, and also declare the ammount of times the loop must be run. lea PlaneAMap(pc), a1; move.l #1024, d1; move.l #$c00000, a2; move.w #$A000, d0; bsr GetVRAMWriteCommand; move.l d0, $c00004;First of all, I've pointed register A1 to the map defintion of Plane A, defined as 'PlaneAMap', as we'll use this in our DBRA loop. Next, I've written '1024', the ammount of one word pointers in PlaneAMap, to register D0; we'll need this number to specify how many times to execute the DBRA loop. I've then pointed register A2 to the VDP control port, so that we can use it inside our DBRA loop. As a final preperation, a command must be written to the VDP command port, to request that the VDP copies everything we'll write the VDP dataport into VRAM, where our map should be. I've done this by writing the VRAM address $A000 (where the map for Plane A should be) into register D0, so that the 'GetVRAMWriteCommand' subroutine, contained in the module 'VDPCommands.asm', can generate a 32-bit VDP command to write to this VRAM address. Finally, the new 32-bit VDP command is written into the VDP control port, located at address $C00004. Now the program is ready to start copying our map into VRAM, by writing each 16-bit pointer into the VDP data port.
CopyPlaneAMap: move.w (a1)+, (a2); dbra d1, CopyPlaneAMap;First of all, notice that I've specified that the program must copy one word at a time, by using 'move.w'. This is important, because we only want to copy one 16-bit pointer at a time. Also, notice that I've also told the processor to increment the address contained inside register A2 each time aswell. This is important too, since we don't want to keep copying the same 16-bit pointer! The address of the VDP dataport must not be incremented though. Intead, we've set the sixteenth VDP register to increment each address by one word, after each write to VRAM. Okay, after this DBRA loop has finished the map for Plane A should now be in VRAM, at address $A000. Providing there's some tiles at the start of VRAM and some colours in CRAM, the VDP will now generate an arrangement of tiles and display them on Plane A! To copy our map of Plane B from ROM into VRAM, we use the exact same procedure: lea PlaneBMap(pc), a1; move.l #1024, d1; move.l #$c00000, a2; move.w #$C000, d0; bsr GetVRAMWriteCommand; move.l d0, $c00004; CopyPlaneAMap: move.w (a1)+, (a2); dbra d1, CopyPlaneBMap;The only differences this time is that we're copying PlaneBMap, word by word, into VRAM address $C000. After both of these subroutines have been executed, the VDP will now display an arrangement of tiles on both Plane A and Plane B, provided that there are some tiles in VRAM, and some colours in CRAM. If you understand so far, then don't worry about writing this code out and assembling in. In the next section, we will write a simple graphics demo from scratch and assemble it, and I'll go though it all with you as we write it.
You'll recall earlier, I mentioned that there are some more advanced features that can be done with tile pointers. I'll explain them briefly now, and explain their effects to you. One of the coolest features of the Megadrive is that the VDP can actually flip a tile as it is displayed onto Plane A or Plane B. This is done by specifying a bit in the 16-bit pointer. Remember, each pointer in a map is 16-bits in size. The format of this bit is as follows:
First of all, you'll notice that bits 0-10 (the eleven least significant bits) are labelled as 'N'. These eleven bits are used to specify the number of the tile that the VDP should draw onto either Plane A or Plane B. Normally, when we define a map (like earlier), the rest of the bits would all be set to zero, which means we can use plain numbers as tile pointers. However, if we want to use some of the more advanced features of the Megadrive's VDP, then we need to start looking at these 16-bit (one word) pointers on a binary level. The next thing to notice is that bit 11 (the twelth bit) is labelled as 'H'. Normally, this bit is set as '0'. However, if this bit is set as '1', then the VDP will flip the tile (that the pointer is pointing to) horizontally when it is drawn. The best thing about this is that it doesn't actually affect the original tile either, the tile is simply flipped as it is drawn onto the screen! As you can imagine, this can be very useful. For example, when making the game Sonic the Hedgehog, SEGA only drew sprites of Sonic in one direction. Everytime Sonic faces the other way in the game, the original tiles are flipped as they are drawn onto the screen. Very useful, don't you think? Bit 12 (the thirteenth bit) is labelled as 'V'. This bit works exactly the same as the last bit, except it tells the VDP to flip the tile vertically when it is drawn. Remember the old upside down TV trick in Sonic the Hedgehog 2? When Sonic is running down a step slope or a wall, the VDP flips the tiles as Sonic is draw onto the screen. There is a bug in the game that allows the player to become an upside down TV monitor when in debug mode, because the game forgets to clear the verticle flip bit. The next two bits, bits 13 and 14 (the fourteenth and fifteenth bits) are used to specify what pallete should be used to draw the tile. Remember in the last section, I mentioned that the same tile can be draw using any of the four palletes. For another example, in Sonic the Hedgehog, everything above water is drawn using the normal pallete. However, and tiles that are beneath the water line are drawn using a different pallete. When both bits are set as '00' (like in the example maps earlier), the tiles are drawn using the first pallete. When the bits are set as '01', the second pallete is used. If the two bits are set to '10', then the third pallete is used. Believe it or not, if the two bits are set as '11', then the final pallete is used to draw that specific tile. The final sixteenth bit (bit 15) is the priority bit. When it is set, the VDP will display that tile over any sprites that have not got their priority bit set. Remember in Sonic the Hedgehog, when Sonic goes through a tunnel? The tunnel is displayed over the top of him, because when those tiles are drawn, they have their priority bit set. Now for a few examples: dc.w $082D;Tile number 45 is represented normally (like before) as '$002D' (in hexadecimal). In the example above, I've also set the 'H' bit, which means when the tile is drawn, it'll be flipped horizontally. So, to flip a tile horizontally, we simply add '$0800' to the original tile pointer. This will set the horizontal flip bit, whilst leaving the original tile pointer completly unchanged. This can be done like: move.w #$0001, d0; Move a 16-bit pointer into d0 addi.w #$0800, d0; Add $0800 to the pointer, to set the horizontal flip bit.Quite simple really! The same technique can also be used to set the other bits, albiet by adding different hex values to the original 16-bit pointer instead. To set the verticle flip bit, simply add '$1000' to the original tile pointer. If you wanted to set both the horizontal and the verticle flip bit in one instruction, then you can actually add '$1800' to the original 16-bit pointer. By defualt, tiles are drawn using the first pallete. This can be changed, by adding either '$2000' if the tile is to be drawn using the second pallete, '$4000' if the tile is to be drawn with the third pallete, or '$6000' if the tile is to be drawn using the fourth pallete. Tiles can only be drawn by using one pallete at a time, although it is entirely possible to flash between two palletes very quickly (much faster than the human eye could notice) in order to create a blend of colours. This is out of the scope of this document, but keep checking the source code section for a demo. Finally, if you want to set the priority bit, then add '$8000' to the original 16-bit pointer. Setting this bit will mean that the Megadrive will display the tile above anything else on the screen that does not have it's priority bit set. This can be used for various effects. I think I have suitable explained the way that the Megadrive displays tiles onto Plane A and Plane B. If you don't quite understand everything at this point, don't worry. In the next section I'm going to work though a simple demonstration program that will display some images on both Plane A and Plane B. This will consolidate all of the techniques that I have explained in this chapter. |