Now that I can execute anything on a gameboy that I want using pokemon as entry point, see my last 3 part blog series on details for that, I wanted to check out something that somebody found out about not all that long ago with 2 hardware registers, ff76 and ff77. If you were to look in any official documentation those would not be mentioned or used anywhere, and most old unofficial documentation just lists them as reading as "0". But what somebody seemingly noticed is that they are in fact not always 0 but represent the current volume of the 4 instruments that make up the gameboy audio output, and released a demo for that:
When I was writing my own gameboy emulator:
I did also implement those registers how I imagined they would be on real hardware and ran that demo above on it, which works just fine:
Of course at that time, I had no way of actually checking it out on an actual gameboy color as I had no flashcart or anything similar to run my own code on it, but now that I can run quite a lot of code, up to 1122 bytes to be exact using pokemon, I decided to write my own sound test with a builtin visualizer!
If you want to see both part of the installation and it then running, check out this video, just for like the first 2 minutes see the input viewer on the top left actually installing my code and after that I boot up into my little program:
Now the installer is identical to the one I previously described that installed my cart dumper, this time it just installs a different payload, at this point in time a much bigger one too, taking up 1110 bytes out of the 1122 I have available, at this point in time I really pushed as much functionality into it as I possibly could. Of course, I released everything up onto my github as well:
See the readme at the bottom of that if you want to try it out yourself, its usable for both english and german versions of pokemon yellow and you can listen to a total of 50 music tracks, all sorted after the official japanese game soundtrack.
Let me go through what exactly I all put into just over that one kilobyte, because it is rather interesting to see how much 1kb can truly be
The code starts of very simple and just disables all the games drawing functions, so I can draw my own screen into place, that is just done by setting all the variables related to drawing to be in a disabled state:
After that, I just wait for the screen to be at the very bottom of drawing so I can safely disable the screen, ready to clear out all of the game background with just a plain white background:
It is quite important to wait for the screen to actually be at the bottom because it can actually damage the screen otherwise if you just cut it off in the middle of processing the current frame so of course I dont want to risk anything
Right after that is done, I draw in the little top part with program name and controls:
And I draw on this text to be exact:
The cool thing about text in this case is that I of course boot everything from within a game already so I dont have to make up any form of bitmap tiles for letters or anything, pokemon already provides a perfectly good tileset for me to re-use, so I just have this separate file that assigns each character to whatever tile number the game uses for its characters:
and the compiler I'm using, rgbasm, just automatically converts all characters into those bytes which is pretty cool!
Following that bit of text on top are of course, the volume meters themself:
And yes, I only draw these once, taking up the whole line, how I actually make them seemingly "move" once music plays I'll go over a bit later
This next bit of code really is only needed if you were to use that glitch item to boot my program outside of the shop, the black bars I'm using rely on that tile around the shop to make it look like you are inside a house being black, once you go outside though it gets replaced with some overworld tile, so to make sure it does not look messed up I just make sure I replace that tile always to just be black:
So now with the top bit of text in place I just also place in the text above the volume meters just with the names of the instrument:
...those names you can also find at the bottom of course:
So now the basic screen is prepared, so I turn the screen back on with that new information and start doing something with a special graphics feature of the gameboy, the "window":
In this case I just move it out of the way for right now, it will become very interesting a bit later
Next up it is time to enable vblank and lyc (line compare) interrupts:
Vblank is the signal of the screen being done drawing and at the bottom, this is important for the cpu to know as the music routine that we of course want to use for this gets executed every time a frame is done drawing.
Line compare is a signal you can set yourself, you can essentially just tell the graphics processor on which line of the screen you want a signal given to the cpu, I will use that later to actually make the volume bars move by just getting a signal on the line right before the volume bars get drawn, more on that later.
So now, I am pretty much ready for my main loop, the last thing now is to of course, start up the music!
This function started out very small but got quite a bit bigger in the current version as I not only tell the game which song to play, but also draw the title of the track on screen!
We start off with this bit:
Since this function can get called while something is already playing, the first command to the game is to stop whatever is currently being played, simple enough. That is followed by this bit:
which again, waits for the screen to be done drawing and at the bottom, and then I draw in a little play/stop icon in front of the track title, this depends on if the play function was called with a valid music number or with a number I chose (128 in this case) to stop the music from playing when pressing the B button. In the case of it being the B button that is all this function does and it jumps to the end, but of course in this first case it is called with the first track number, so it continues to here:
I had to get a little creative in trying to now select from a list of track titles to draw onto the screen, and I came up with that bit of code. You see, I specifically chose each title to be exactly 8 characters long, this is because it is very easy to multiply a value by 2 in code as all you have to do for that is shift the current number bits left once, which is done with the command "sla c" in this case, which is the current song number, I shift over that number 3 times, so that means it does the number*2*2*2, which is of course, number*8! I then just add that new number onto the list of names I prepared here:
And that gives me the exact name of the song title I want, which I then just draw on screen!
The next bit is pretty much the same but not for the track name, but for the track number I have to give to the game to start playing back what I want:
In this case I just have to multiply the track number by 2 once, as the track number I give the game is only 2 bytes and kept in this list:
So now that I told the game what I want to play and I have drawn the title on screen, I just clear the signals I may have gotten in this time from the graphics processor and enable the ability to actually get signals now as I am now ready to finally start with the main loop:
So finally, after all that preparation we are actually ready to do something, and the very first thing is the "halt" command:
This simply puts the processor to sleep for now until we get a signal from the graphics processor that it finished drawing or it hit one of the lines we specify, now there are lots of lines that I actually do specify in total:
Over the course of just one frame, the gameboy being 60 frames per second of course, I in total look out for 8 separate lines it is drawing to get into updating something, those lines are the one right before the first volume bar, the second right after it so you can read the name of the 2nd instrument, then again right before the 2nd volume bar, then right after the 2nd volume and so on for all 4 instruments, making up those 8 separate lines.
Now what happens right before a volume bar is first of all setting the next line we want a signal for of course:
Then we get the volume level of that particular instrument, more on how we actually get that later:
And now finally for the special "window" feature I've mentioned earlier!
So to avoid trying to draw the current volume bar level every single frame I decided to instead use a "window", which basically lays on top of the background where so far everything is we have drawn, the text and the volume bars. The window always goes from the right of the screen to the left, and you can specify the exact pixel you want that window to be scrolled to between that, so all I do is have a list of window positions:
I have calculated these for each of the 16 possible volumes a instrument can have to overlap the background so it looks like as if that black bar is actually the one moving according to the volume, but in reality I am actually moving a window exactly opposite to that, on full volume, it is completely off-screen on the right, and on no volume, it is moved all the way to the left, giving that cool illusion and saving a lot of time you would have to take otherwise to draw things!
So all this bit of code does:
is take the current volume level, add into onto that list of window positions and writes that now determined window position right before the graphics processor starts drawing the first line of that volume bar, making it just in time!
The bit of code after the volume bar is rather boring, all it does is set the next line it wants to get a signal for and reset the window position to be off-screen, so you can read the name of the next instrument, otherwise it would just be covered by the window if there was no volume:
Also you may see a small wait function here, that is simply because the cpu in this case would otherwise set the window position before the graphics processor is done drawing the last volume bar line, which could result in it being glitchy.
Now, the last bit of code that is interesting here is the one that happens after all 4 bars have been drawn, because this is actually where I update the current button inputs, switch songs if needed and also actually get the volume levels.
It starts out just like the others, setting the next line signal we want and reset the window position, in this case not just for the next instrument name, but for when it starts drawing the next frame so the top screen text is visible:
Followed by this little bit:
Here I just call the game function to update the button inputs and then grab the current value for the ones pressed, if A, B, Left or Right are pressed, I jump into handling those inputs:
It may look long at first but really all it does is if B is pressed send that value of 128 I mentioned earlier to the play song function to stop it, if A is pressed just send the current song number to the play song function again to (re)start playback, if left is pressed subtract 1 from the song number and if it is smaller than 0, just set it to the highest song number to loop it around, and if right is pressed, add 1 to the song number and if it is bigger than the highest song number, set it back to 0 to loop it around, and then just call the play song function with that new song number.
So this just leaves us with the last case, no buttons are pressed and we are done with a frame, it is finally time to call these officially unused registers, starting with ff76:
These are responsible for the first 2 instruments and I found out that just reading it once would often times lead to it reporting no volume, this has a simple reason with how audio works, to create a frequency you can hear, it has to turn on and off the volume output very fast at whatever frequency the tone you can hear has, so of course it can very easily happen for the processor to just read it right at the point where it is turned off right now. I noticed that this happens really, really often, so I in fact for every frame read the first 2 instruments a total of 256 times!
As much as that may sound, it is in fact still not enough for certain songs and you can get slightly flickering volume bars. To be honest I cannot do a whole lot about that, you see the gameboy processor is rather slow, so if I were to try and read it any more then there would not be enough time anymore to even draw the next frame, in fact, sometimes there already is not enough time and on certain frames all volume bars will appear to look full because of that, that luckily only happens when the song currently playing sets all 4 instruments at once anyways so they all get a burst of volume in that case so they would all actually appear as full, so this is not so much an issue as it just is a bit of a small technical detail
Now for ff77 representing the other 2 instruments, I read far less, only 64 times for every frame:
This is because of for one the very limited time I have already and also because really they are not nearly as problematic as the first 2 instruments in the way they work so even if you were to read them more often, they would not look much different.
After it is done reading those 2 registers for so 256 and 64 times respectively, it stores the biggest number it got for both into the volume variable then used for the next frame once it hits the start of the volume bars.
What a bit of code to go through and describe in detail, now you know just how much 1kb of data can actually be and how much thought went into something that may seem so small, had a lot of fun coming up with code that would even fit into that space, I had to go over it and optimize things several times to somehow stay within that small 1kb space, one of the big space eater here being the all the track titles, even though I already cut them very short to 8 characters per title, they still make up 400 bytes of the 1122 available, there are a total of 50 music tracks you can listen to overall, combine that with the text for all the channel names and the text on top of the image and it goes up to 486 bytes of just pure text!
If you actually made it this far then thanks for reading.
You need to be logged in to comment