This episode of Deep Dive will touch upon doLoadScreen (and, subsequently, doLoadScreen2). These are huge subroutines, so we’re not gonne dissect them line by line. Instead, we’ll mainly go through the order in which things get done. This might explain why, for example, your HUD value doesn’t update, or a triggered tile is not being destroyed. Either way, let’s go!
There are two subroutines that handle screen loading: doLoadScreen
and doLoadScreen2
. They do similar things, except doLoadScreen2 loads data for the screen that is in the “other” (invisible) nametable. As you may know, the NES has four nametables, of which the NESmaker mapper acively uses two through vertical mirroring (the other two nametables are mirrors of the two main nametables). This allows for easier horizontal scrolling. But that’s something for a different post, probably. Let’s zoom in on doLoadScreen for now.
What exactly happens in doLoadScreen?
Let’s list everything that is happening here, with added line numbers for easier following.
- [line 6] Reset/remove non-persistent objects
First, all non-persistent objects (which usually is every object except for the player) are being removed / inactivated through a subroutinedoClearAllMonsters
. - [line 16-42] Load screen table data
This is where all information for the current screen is being loaded from the appropriate ROM bank. Loaded data includes the screen type, palette data, screen flags, path data, warp and continue points, etcetera. Look at the subroutinedoLoadScreenData
to see all table data that’s being loaded. - [line 48] Extra screen load
If you want to load extra custom data for your screen, this is the place to do it. This line includes the extra screen load assembly file which you can customize for your own needs. For example, if you want to replenish player health every time a new screen loads, you can reset the number of health points in this file, so the HUD will automatically update those later. - [line 50-65] Load palettes
This is where the script loads the palette values. First, it loads the background palette values, then it waits a frame (presumably because palettes are loaded through a PPU buffer, which needs to be sent to the PPU and cleared before adding additional PPU data, although I think this could be done differently and faster), and finally it loads the sprite’s four subpalettes one by one. - [line 67-379] Load background CHR data
Now, we’re gonna load the actual graphics into the PPU. This happens through a big switch-like construction which checks the screen type that is used (you know, that Double Main / No Hud setting in your NESmaker UI). Based on the current screen type, it calls the LoadChrData macro a couple of times to transfer the right graphics to the PPU. - [line 385-413] Load sprite CHR data
This does the same, but for sprite graphics. First, the game objects are being transferred, and then the monster graphics based on which monster group applies to this screen. - [line 417-467] Load nametable data
We now have the graphics loaded in the PPU, but we have not yet loaded which graphic should go where on the screen. This is what is happening now: we’re loading nametable data from the bank and into the PPU, based on if we’re on an 8×8 (line 450) or 16×16 (line 464) screen. - [line 468-480] Load attribute data
All the correct tiles are now loaded on the screen. You won’t see it yet because screen rendering should still be off right now. If you could see it, you’d notice that the tiles may have the wrong colors applied. That’s because we haven’t loaded the attribute data yet. So that’s what the subroutine will do now. - [line 481-493] Load collision data
The screen is done, background tile wise at least. We’re now going to load collision data into RAM, so the game knows which tiles are solid, walkable or lethal, for example. - [line 495-516] Load player and continue data
This is where we tell the game where to spawn the player object and, if applicable, the continue screen and location data. - [line 517] Draw HUD
This is pretty self-explanatory. The script now draws the HUD graphics on screen. This overwrites the tile data that was already drawn earlier at the HUD’s spot – but not the collision data. Also, take note that the attributes are overwritten as well, so the screen will use background palette 3 there. Also-also, since the attribute values are overwritten completely for every 32×32 hud area, this means that your HUD should either use only even values, or the HUT palette should be applied to your screen in the Overworld editor. - [line 522] Post screen load
The screen is completely done drawing now. Anything you want to make happen between loading the screen and starting the actual level, you should put here. For example, draw sprites for some extra flair, or handle triggered tiles to overwrite their initial type and graphic on screen. - [line 524-530] Enable screen
We’re completely done with this screen now! Let’s tell the game to show our new screen on the next frame, by writing the appropriate bytes (draw background, draw sprites, do not hide background and sprites on the left side of the screen) to soft2001, which is a variable that gets written to the PPU mask (address $2001, hence the variable name) in VBlank.
That’s it! The old screen has been replaced with the new screen, all settings are in the right spots and the game can continue.
What about doLoadScreen2?
For scrolling modules, it is necessary to preload parts of the next and previous screen before starting the level. In NESmaker, this actually happens before drawing the active screen, by calling subroutine doLoadScreen2
. This does a few things that doLoadScreen does as well, but not everything. Here’s a short list of what happens where – without going into the same level of detail as I did above.
- [line 2-42] Load nametable data from the left half of the next screen
- [line 43-55] Load attribute data from the left half of the next screen
- [line 59-74] Load nametable data from the right half of the previous screen
- [line 75-90] Load attribute data from the right half of the previous screen
- [line 92-107] Load collision data from the left half of the next screen
- [line 109-128] Load collision data from the right half of the previous screen, and we’re done.
Lines 131-132 have a monster position offset table, which I think is no longer actively used in NESmaker, so you could remove those lines and save 4 bytes of ROM.
Welcome to the new screen
So there you have it! All steps that NESmaker take to update the visuals and data during screen transitions.