Splitting Assets from Code

This first page will cover the important aspect of keeping your game assets separate from the game code (where assets are things like textures, models, sounds, etc...). Depending on how you've structured your project, or more importantly how far you are into it currently, this can be a relatively simple or relatively difficult task.

Start by looking at the original sample, before any modifications. It is a simple ROM with two rooms that can be switched between by pressing the A button, with a texture that is displayed in the middle.

If you take a look at the makefile, you will see that our textures ( and  ) are being compiled as C code and then being linked into the codesegment. It's not a big concern for this small project, but due to the 1MB IPL limit, this will become a problem as the project gets bigger. The idea here is that we want to have the assets elsewhere in the ROM, and we load them from the cartridge only as we need them.

So first and foremost, lets move our assets over to ROM.

Moving our assets to ROM
For simplification reasons, we're going to convert our textures to binary data, instead of keeping their current form as C arrays. You should be able to do this for pretty much any sort of assets your game will need, such as static display lists. The process of actually converting your data to binary form will not be covered here, as there are plenty of tools to do that for you already.

Once your assets are in a binary format, you must remove the original code files from your makefile (as they're redundant) as well as any 's relating to them. You can leave the array pointers in the code for the time being, as they'll be substituted later.

The actual process of putting your assets in ROM depends on your SDK setup:

Spec file = Your Spec file, before adding the assets to it, would look something like this: Adding in new raw assets is as simple as creating a new segment and specifying the  flag. For instance, having the data from  converted into binary form (with the name  ) and linking it to our ROM is as simple as: Do this for all the assets, and you're almost done. The next step is to open a C header file (or better yet, create a new one) and to create some  calls for your new segments: Remember that segment name I told you that was important and had to be unique? Whatever you set your segment name to, it needs to match the 's. Meaning, if you called your segment , then you would need to define the  's as   and   respectively.

If you want to know more about Spec files, the online manuals do not contain a lot of information about them. Instead, it is highly recommended that you check out the Specfile Format chapter of the N64 EXEGCC Compiler User Guide for more information. Linker script = TODO

Now that our assets are in ROM, we need to DMA them to be able to use them in our game.

Loading assets from ROM
Having the segment addresses for our data in ROM, it is relatively simple to DMA them from ROM. We know the starting address of our data thanks to the SegmentRomStart, and we can infer the size of the data by subtracting the start address from the end address.

With Nusys, the task is relatively trivial: If you're not using Nusys, then there's a bit more overhead involved:

Please note that the code on this wiki page does not replace the manuals. Data DMA has very strict requirements in terms of maximum size and buffer alignment, therefore you should look up the manual pages for  or   to make sure your DMA operation will succeed.

You're probably wondering from the above code what  is. You probably understand that the data needs to go somewhere (like a variable in your code), but there's two different approaches to having data buffers:

Having a buffer in the codesegment
The easiest method is to simply just have a global variable which will work as a "cache": The idea is, before you start rendering the level, you load any textures you need into your global cache (which can be any size you want, not just 4096 bytes). Then, when the data isn't needed anymore, you mark that part of the buffer as "empty" and you can overwrite it with new data.

You're probably wondering: "Hold on, this global buffer variable is part of the code... Won't it be subject to the exact same 1MB restriction we had before?". The answer is no, because initialized global variables are not loaded from ROM (as they're dynamically "created" when your code loads). The downside to this method is that you might not always have full control over where the data is initialized to in memory. The alternative would be:

Having a buffer somewhere in RAM
Our game has about 4MB of RAM to work with (8 if the Expansion Pak is available), and roughly 1.5 to 2MB would be occupied by the framebuffers, Z-Buffer, and the code itself. So why don't we instead make use of those 2 to 2.5MB we have free to store our assets there?

All we have to do is create a new C file, and in it we place a global buffer just like we did in the previous section. Except this time, we're not going to link this data to our codesegment, rather we'll tell the N64 to reserve this part of RAM for our buffer. We'll need to modify the makefile somewhat, as we'll need to compile our C file into an object file. Lets assume our buffer is in a C file called.

Your makefile should already have a dedicated  variable where you put all your C files, and then it gets compiled into one big   object file via   and then. The idea now is that you create a new variable, such as, and you place your buffer files here.

Now, you'll want to place the  in your makefile where it will compile the .o's, but not link them to the codesegment. Typically, this will be in the target section (such as  or  ). Example:

Since we're not linking the object file in our makefile, we'll need to do that manually afterwards: Spec file = In your spec file, all you need to do is add a new segment, but give it the  flag instead: You can use  in your spec file to include a header file with a list of address macros if you want to, and use that macro value in place of raw address numbers or the   keyword. Linker script = TODO

Once it's linked, you're all set! You just need to ensure your array is 'd somewhere and you can use it without any other changes to your code.

A useful trick, you can  the codesegment as well:

You can use this, for instance, to load your assets right after the code segment by loading them into the address at. You don't need to create an object file with your buffer as you can just write to any RAM address you seem fit (for instance, you can just define  globally, and then treat it as a array/buffer) but it sure helps as the compiler/linker can potentially catch buffer overflows and/or segment overlapping.

You can see an implementation of the buffer method described in this section here.

Managing large projects
After going through all of this code, you probably still have a few wiggling doubts: "How do I manage the loading of data in large projects? How can I tell what assets I have loaded, and what needs to be loaded? How do I tell if my caches are full, thus unable to read more data from the cart?". These questions are answered in the next chapter of the Code segmentation guide, which covers file systems.