I want to take a look at itemTextPointerTable
in bank 1 today. It contains all text strings for the names of power-ups.
The contents of itemTextPointerTable
are in “SRC/data/itemNames.asm”. In there, three structures exist. Let's go from the bottom to the top.
The third structure is a list of strings. We can see that each of the strings are 16 characters long and have a label.
The second structure is a character map. Character maps are a way to convert characters in a string to bytes of data. Which byte a character needs to be depends on where the font is loaded in VRAM, and by consequent, what tile id is used for that character. The strings in the third structure are using this itemNames
character map to become data.
The first structure is a table of pointers to the strings in the list of strings. Pointer tables are useful when the distance between each entry's label is irregular. It stores the address to each entry so that you can know where they are and access them. In this case, there is always $10 bytes between each string, so the pointer table is not needed. All you'd have to do to access any string is to start at the first string and add $10 however many times you need to reach the one you want.
Challenge: Go ahead and modify some strings. What happens if a string has more than 16 characters? What if a string has less than 16 characters? 0 characters?
Have you ever noticed that the item text is not centered? The Game Boy's screen is 20 tiles wide, so given that the 16 characters in the string are drawn starting at the left edge of the screen, 4 tiles are left untouched on the right. Let's fix that by increasing the quantity of characters drawn.
Challenge: Find where itemTextPointerTable
is used to draw the item names on the window layer. Personally, I use Visual Studio Code with the “ASM Code Lens” extension to see every reference to a label. It can't handle local labels correctly, but it works good enough.
Let's look at what the code does. All of the code is setup for a call to beginGraphicsTransfer
, to transfer the text during V-Blank.
First, the bank in the switchable ROM bank slot is switched to bank 1, so that itemTextPointerTable
becomes mapped to Game Boy memory. It is impossible to access itemTextPointerTable
without doing this. This bank number is also saved to vramTransfer_srcBank
so that the correct bank can be loaded when the transfer occurs.
Then, the index of the string to load is extracted from the door token's low nybble. As this is done, hl
is pushed to stack. The stack is a list of 16-bit values in RAM. Much like a stack of objects in real life, you can add things to the top of it (pushing values), or retrieve things from the top (popping values). hl
is pushed to stack so that hl
's value can be changed in the code that follows, all while preserving the current value of hl
for later use.
The index is loaded into de
, multiplied by 2 (because each pointer is 2 bytes) and then added to the address of itemTextPointerTable
. Now hl
contains the address to the pointer for the string we want.
The pointer is then dereferenced, that is, the value of the pointer is loaded into hl
.
This value is then used as the source address for the graphics transfer.
The constant vramDest_itemText
from “SRC/ram/vram.asm” is used as the destination address, and $0010 is used as the length. beginGraphicsTransfer
is called and hl
's value is restored.
We can change the length to $0014, and increase the size of all strings to 20 characters. Now the text can be properly centered.
Extra challenge: Remove the pointer table, and have the text drawing code calculate where the strings's labels are by itself. You can use repeated addition, but try using multiiplication next.