BFP Chapter 04: Room for one texture

Download sources for this chapter

Introduction

I’m starting to get some mixed feelings about this project.

First of all, I find it unacceptable to have spent so much time for a blank screen.

On the other hand, I know I’m so close to displaying something that I simply must do it today.

And then, I see this:

How on earth did they do it? I have my theories about most of the effects, but what about the giant rotating sprite?

I ran it on the emulator (they provide separate binaries for each emulator cause they also have found bugs in them). I opened the VDP debugger to see how they organize their sprites. In the most interesting parts I see no sprites at all. Maybe they swap them in and out quickly so they don’t appear in the debugger.

But enough thinking about the complex stuff. Back to today. Let’s put something on the screen!

A world made of tiles

tiles.png

All graphics entities of the VDP (sprites and backgrounds) are much like textures. They have a programmable size, in powers of two cells.

A “cell” is a 8×8 pixel region where each pixel is 4 bits – a palette entry. Each cell must choose one of the 4 palettes, so we are restricted to 16 colors per cell. Or 15 colors if you don’t count the transparency (color 0 of every palette is transparent).

VRAM contains cell information. You can organize the VRAM however you like. For every sprite and background, you write all the cells that compose it in VRAM one after another.

Then you assign the graphical entity its starting VRAM address. The entity holds the information for its size (you have specific sizes to choose from for backgrounds and sprites), which implies how many cells it will occupy in VRAM.

The sprite table

VRAM also holds the “sprite table”. This table has a fixed size (64 sprite records if I remember correctly) and it is actually a linked list. It starts with sprite 0, which is a record that contains sprite size, cell data offset in VRAM and a ‘link’ field that will enable the next sprite. I’m guessing they did this so that the programmer could insert and delete sprites fast.

You can put the sprite table wherever you like in VRAM and you tell the VDP where it is by setting register #5.

A first approach

The first thing we could try here is to somehow create a file with our intended VRAM contents, embed that file in the ROM and then copy from ROM to VRAM using a DMA transfer.

So, first of all, we need some 16-color paletted images. BMP files have a 4bpp mode so let’s try to make such a file with GIMP. In order to fill the screen, we need a 32×32 cell background, or a 256×256 bitmap. I like this one:test.png

In GIMP, I changed the mode to indexed, using an optimized palette of 15 colors. Then I added one more color for transparency (purple is commonly used for masks), which is not used in the image, and I put it at index 0, so the useful colors will be from 1 on. Manipulating the palette in GIMP is not that easy. Maybe there are better tools for the job, but right now, we have something to work with.

I exported the image as a 4bpp bmp. I then wrote a tool in C (bmp2cell) that reads the file and spits out a .cell file containing what I think is the correct layout of bytes to fill the VRAM with.

I then embedded the .cell file in my assemby source, using the “binary” directive. There’s also the “even” directive that will make sure what follows is at an even address, so as to avoid odd addressing issues:

	even
VRAMData:
	binary "test.cell"

DMA transfer

And now, to copy the data to VRAM. Here’s how you do a DMA memory to VRAM transfer, according to the manual:

DMATransfer.PNG

An image is missing (probably register #22), but I have all the information I need from the register descriptions above. It took some experiments, but I think this is it:

	
	VDPSetRegister  1, $5C		; Turn on "DMA" bit (M1)
	VDPSetRegister 15, $02		; R#15 : Auto-increment by 2 bytes
	VDPSetRegister 20, $40		; Copy length: 3FFF will copy 4000 words - NOT!
	VDPSetRegister 19, $00
	
	; bits 8-1 of source address (bit 0 ignored)
	VDPSetRegister 21, ((VRAMData >> 1) & $FF)
	VDPSetRegister 22, ((VRAMData >> 9) & $FF)	; bits 16-9
	; Rest of the bits (MSB must be 0 for memory->VRAM transfer)
	VDPSetRegister 23, ((VRAMData >> 17) & $7F)
	; 2 Writes to control, with splattered destination address
	defc destinationAddress = 0
	VDPWriteToControl #((((destinationAddress >> 8) & $3F) | $40) << 8) 	VDPWriteToControl #(((destinationAddress >> 14) & 3) | $80)

There are a million things to worry about here. First of all, after the Epictetus.jpgtransfer is complete, the “DMA” bit of register #1 is not automatically turned off as promised. The transfer did not work like in VRAM fill, where we put one increment less than we need. Now it seems to just copy the exact amount of words we specify. Finally, the word’s byte order is reversed (again). This might be normal.

I stoically reversed the byte order in my C program.

Also, before exporting the palette from the .bmp, I just wanted to test the DMA transfer, so I just made a grayscale palette by repeating the same set of instructions 16 times:

	VDPWriteToControl #$C000	; Give the command to write to palette entry 0
	VDPWriteToControl #$0000
	VDPWriteToData #$0000		; Write color to the data port

I really expected to see something recognizable on screen after running this. We had everything initialized to zero, so the backgrounds must already point to VRAM location 0. Here’s what I saw:

giberrish.PNG

Also, there was a cool flickering effect, I’m guessing that some frames pass while I change the palette.

Moving the sprite table out of the way

I think what we’re seeing here is the sprites. We set everything to zero indeed, so the sprite table must also be at VRAM 0, where we also put our bitmap data. So the sprites are just garbage. Let’s try putting the sprite table elsewhere. The best place I can think of right now is the bottom of VRAM.

The sprite table seems to occupy 512 bytes. So the sprite record must be 8 bytes, for 64 sprites. We must set the 7 most significant bits of the sprite table address to register #5, like this:

	0XXXXXXX (X = A15-A9)

So, to put the table at the bottom, we need to:

	VDPSetRegister 5, $7F

Let’s put that in our initialization routine.

Nothing happens, except the sprites have been cleared. Not all sprites. The emulator shows more than 64 sprite records. The manual mentions more sprites in case we use the “40 cell mode”, which I don’t remember what is at the moment. So let’s try to clear bit 9 of the sprite table address, effectively putting the table a little above the bottom so that more sprites will be cleared.

Again, no visible changes, but the sprite table has been fully cleared. I shouldn’t have expected more though. Clearing everything to zero will not set up the backgrounds to our liking.

So let’s keep on reading the damn manual…

Cell indices

It should have dawned on me sooner. We’ve already filled half our VRAM with the contents of just one background. There’s no room left for the second background, let alone sprites. So we can’t possibly define a background by laying out one cell after the other in VRAM. There’s simply not enough space.

The cells that we write in VRAM are reusable and you create backgrounds by laying out indices to cells.

To complicate things a tiny bit more, the indices are packed with other kinds of stuff, like priority, flipping and color palette selection. So we are constrained to 16 colors per referenced cell (and not per background) after all:

cellIndexing.PNG

So we must somehow reuse our cells. Too bad I’ve spent time on this tool that exports unique cells from a bmp. We’re going to have to search for another tool for graphics, but right now I will display what I have.

The situation is like this:

  • We have occupied the top half of our VRAM with our wonderful texture.
  • Therefore we need to fill backgrounds A and B with indices to those cells, in order from 0 to 1023 (32×32 cells for each background)
  • So every background occupies 2 KB in VRAM

The quickest thing I can think of right now is to have my bmp2cell tool output this index buffer along with the cell data and then point both backgrounds to the start of the index buffer. That should do the trick.

	/* This tool is going to die soon - add some dumb cell indices for my viewing pleasure */
	for (i = 0; i<1024; i++) {
 		dst[0] = i & 0xFF;
 		dst[1] = (i >> 8) & 7;
		dst += 2;
	}

Note: We point the backgrounds to look for indices in the upper half of the VRAM by setting VDP registers #2, #3 and #4progress.PNG

Now, can I find it in my heart to call this “progress”? I wish I could… Putting aside the urge to give up, let’s mess around with stuff a little bit more…

moreLikeIt

I seriously have no clue how I reached to that point. I kept flipping things around, but I’m almost certain, the current situation is exactly the same as before. Spooky.

Now, the only thing remaining is to flip high and low bytes in the image data and we’re done. Maybe I was being stoic for no good reason earlier:

Finally.PNG

From heaven to hell and back!

Also, we get to discover that the scrolling backgrounds wrap around. And what a happy coincidence that I chose a tilable texture for my first test!

The ROM header

Now that I’m satisfied to see my pixels on-screen, I think it’s time to revisit stuff I’ve been neglecting. And first of all, let’s make a proper header instead of copying Sonic’s:

http://www.zophar.net/fileuploads/2/10614uauyw/Genesis_ROM_Format.txt

Here’s a visual representation of the header:

ROMHeader.png

I’ll assume the top half part of the ROM header is given to the programmer to use it however he likes. For instance, check out the funny header TITAN put in their “Overdrive” demo.
Then there’s a series of strings (the green region) that are more or less self-explanatory.

Then we have the product type. We’ll use GM for “game”.

Then follows product code and version number. I’ll set them to ascii ‘0’.

After that, we have the checksum we already calculated in the romtool, in yellow.

Then we have a large string that will start with ‘J’, for “joypad support”, and the rest of it will be ascii spaces as we’re not going to use other peripherals.

Then, there’s the ROM capacity we already handle, in green (start address – always zero) and red (end address).

For the following two long-words, I’ll just copy the values from sonic. I don’t know what they do.

From then on, we have a large string of ascii spaces, with ‘JUE’ near the end, which means the game can be played in Japan, USA, Europe. I’m going to go with that as well.

So, let’s create a separate assembly file, where we’ll put the header data. How do we put ascii and binary data in vasm?

We do both with the same directive: “byte”. It takes any number of arguments that can be either integers or strings.

So let’s do that:

	; Think of something clever to say here
	ORG $0
	byte "IGNORED IGNORED IGNORED IGNORED "
	byte "IGNORED IGNORED IGNORED IGNORED "
	byte "IGNORED IGNORED IGNORED IGNORED "
	byte "IGNORED IGNORED IGNORED IGNORED "
	byte "IGNORED IGNORED IGNORED IGNORED "
	byte "IGNORED IGNORED IGNORED IGNORED "
	byte "IGNORED IGNORED IGNORED IGNORED "
	byte "IGNORED IGNORED IGNORED IGNORED "
	
	ORG $100
	byte "SEGA MEGA DRIVE "
	
	ORG $110
	byte "PSYFLEX 2016.JUL"
	
	ORG $120
	byte "GAME NAME                             (DOMESTIC)"
	
	ORG $150
	byte "GAME NAME                             (OVERSEAS)"

	; "GM" for "game", "AI" for "educational"
	ORG $180
	byte "GM"
	
	; Product ID and version
	ORG $182
	byte " 0000000-00"
	
	; Checksum
	ORG $18E
	byte 0, 0
	
	; I/O Support - 'J' = joypad
	ORG $190
	byte "J               "	
	
	; ROM capacity - this is handled by the romtool
	ORG $1A0
	byte $00, $00, $00, $00, $FF, $FF, $FF, $FF
	
	; RAM - No idea what this is - I'm putting the values from sonic
	ORG $1A8
	byte $00, $FF, $00, $00, $00, $FF, $FF, $FF
	
	; MODEM, MEMO and Country - Mostly spaces
	byte "                                "
	byte "                                "
	byte "JUE             "

I’m including the file ROMHeader.x68 on top of my main program.

Now let’s see how we tell vasm to produce a flat binary. You do that with the -Fbin argument.

Let’s try to create a binary first.

headerLooksOk.PNG

The header looks ok, but regen shuts down (crashes?) when I load the binary. Maybe it’s the broken checksum/rom capacity. Let’s fix that with a new tool. I’m going to call this romtool2 and put it in its own folder, because I want to leave romtool intact so that previous chapter setups will work too.

I’m considering creating a git repository for the whole project, but it’s going to be troublesome when (if) I post the series online. I will then have to go to specific revisions to grab the version of the tool that will work with a specific day’s test.

Wait a minute… That’s not troublesome at all!

gitInit.PNG

So, now that we have a git repository, we can just modify the romtool to only calculate the checksum and rom capacity.

And so we did. But regen still crashes.

I’m starting to believe that the first 256 bytes contain something important. Not so funny after all. Also I think I remember the first bytes in the address space are used for interrupt vectors. Yup, they are.

Interrupts

And now it’s time for our first cheat. I’m going to copy this guy’s code for the first 256 bytes:

Credit for this goes to Marc Haisenko. There are other people involved in figuring this out, whose names are listed in the link above. A recursive “thank you” to you all!

I’m adding a new file called “interrupts.x68”. Our handlers for now will be empty subroutines.

This also takes care of another item in our TO-DO list: “Assumption that nop-nop-bra is mandatory”. It isn’t. I know now because the second long word of the ROM is the code starting address. Sonic actually starts at $206, which means we can now safely remove the nop-nop-bra nonsense.

Unfortunately, vasm does not seem to have a “long” directive, so our version of the first 256 bytes is going to be as ugly as:

	word   ((genericInterrupt >> 16) & $FFFF), (genericInterrupt & $FFFF)		; Bus error

4leafClover.pngAnd now, let us pause for a moment to reflect upon our monumental luck, that the random addresses we copied from sonic’s ROM just HAPPENED to be somewhere safe.
Unbelievable. It’s a good thing we put that out of the way.

Trying other emulators

Next item in our list is to make sure we haven’t based our code on false assumptions, based on our reference “hardware”, which is actually an emulator. I tried the binary in 3 emulators just in case:

variousEmulators.PNG

Afterword

It might not be much, but this little demo brings us closer to our goal… which is kind of undefined… but definitely closer! Stay tuned as we are now almost ready to do some basic animation!

 

Previous Chapter TOC Next Chapter
Advertisements

One thought on “BFP Chapter 04: Room for one texture

  1. Pingback: BFP Chapter 05: Scrolling | Coder's Diary

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s