Skip to content

Faxanadu level data - Part II



Last time I explained that a screen in the NES game Faxanadu is made of 16x13 blocks and how the block data is stored in the ROM file. In today's update I'm going to describe how these blocks are displayed by the PPU (Picture Processing Unit).
The PPU has two pattern tables that are used to hold the objects to be displayed. The first one is used for sprites, the second one (which is relevant for today's update) is used for everything else. Each of these two pattern tables can hold 16x16 = 256 patterns.
Faxanadu divides the 2nd pattern table (pictured on the left) into two parts: One ranging from offset 0x00 to 0x7F and one ranging from offset 0x80 to 0xFF. Strictly speaking the exact start of the 2nd part depends on the screen to be displayed, but more about that later. The upper half of the 2nd pattern table contains patterns for things like the health bar or the text if you're talking to an NPC. The lower half of the 2nd pattern table contains the patterns used for painting the background of a screen.
The connection between the blocks from the screen data and the pattern table is relatively simple. Each block of 16x16 pixels is made up of four tiles of 8x8 pixels (TSA tiles). Each of the eight levels introduced last time contains four arrays which hold the four TSA tilesets for the upper-left, upper-right, lower-left and lower-right tiles of a block. Finding out which tiles belong to a block is straight-forward, the number of a block is used as an index into these four arrays. To find the arrays please refer to Vagla's document about Faxanadu internals.
Bringing the individual tiles onto the screen works exactly the same way. Each tile number is an index into the 2nd PPU pattern table. One might now believe that the values in the tile arrays are always >= 0x80 but that's not the case. There's one exception. The value 0 is once in a while used for tiles in which case the very first tile in the 2nd PPU pattern table is used for that tile. Fortunately this tile is always solid black and never changes.

I've made a picture of the entire process. The screen data contains blocks, the block consists of four tiles which are looked up in the tile arrays using the block number as an index. The tile numbers are used as indices into the pattern table.

Let's talk about the exceptions now. In the last update we've already seen that there are 8 different levels. There are however 9 different tilesets, different screens within one level can have different tilesets. In practice however the only screens within one level that have different tilesets are the screens of level 6, the inside of buildings. This level uses three of the nine tilesets, probably to provide for more detailed buildings. If you're quick with the math you might have realized that if the buildings use three tilesets there should be a total of ten tilesets, after all there are 7 other levels. That's not the case though because two levels, the maze before the final boss and the last level, share one tileset.
The screens of level 6 differ from the screens of other levels in one other significant aspect: The tiles of these screens are not loaded to offset 0x80 of the second PPU pattern table. They are loaded to offset 0xA0 instead, leaving the tiles between 0x80 and 0x9F unchanged. The unchanged tiles are not used in the buildings though, doing so would probably cause undefined behaviour because it's possible for buildings to be located in all other levels and therefore the exact patterns in the offsets 0x80 - 0x9F vary.
To know which patterns to load for which screen it's necessary to look into the internal structures of the Faxanadu code. There are three important arrays in the Faxanadu ROM file that dictate which patterns to load for a given screen.
The first of these arrays (located at 0x3CF17 in the ROM file) contains the offsets where the 9 pattern sets can be found.

0003CF17 0080 0098 0088 0090 00B8 00A0 00A8 00AE ................
0003CF27 00B4 ..

The first pattern set can be found at offset 0x8000, the second pattern set can be found at 0x9800 and so on. Of course these are memory offsets. To find them in the file it's necessary to add 0x8010 to the offsets because they're all located in ROM bank 4 (4 * sizeof(rom header) + sizeof(file header)).

The second array follows right after the first one at offset 0x3CF29. It contains the pattern table offsets to which the patterns are loaded. The higher 4(?) bits are used to address the pattern table (0 or 1), the lower 4(?) bits contain the pattern table offset divided by 0x10. The only values to be found in an original Faxanadu ROM file are 0x18 and 0x1A which are used to load the patterns to offsets 0x80 or 0xA0 of the 2nd pattern table.

0003CF29 1818 1818 1818 1A1A 1A .........

The last array (offset: 0x3CF32) contains the number of tiles (divided by 0x10) of each pattern set. Values to be found in original Faxanadu ROM files are 8, 6 and 4 meaning 0x80, 0x60 and 0x40 patterns are loaded into the pattern table depending on the current screen.

0003CF32 0808 0808 0808 0606 04 .........

These three tables are accessed only from one single place in the Faxanadu code, the subroutine beginning at offset 0xCEB8 and ending at offset 0xCF06 in ROM bank 15 which is coincidentally exactly the code before the three arrays.

One questions remains: How are these tables used, what's the connection between a screen and the tables? This question won't be answered for several updates because information from the door location/destination tables is used as an index into these arrays. To end this update I quickly want to list which pattern set belongs to which level though.

Index Offset Pattern Table Offset Number of patterns Level
0 $8000 $80 $80 First town
1 $9800 $80 $80 Tree world
2 $8800 $80 $80 First world
3 $9000 $80 $80 Fog world
4 $B800 $80 $80 Last world
5 $A000 $80 $80 Towns
6 $A800 $A0 $60 Buildings (King, Church, Doctor)
7 $AE00 $A0 $60 Buildings (Tavern, Blacksmith, Keys, Home, Bakery)
8 $B400 $A0 $40 Buildings (Martial arts, mana seller)

Trackbacks

No Trackbacks

Comments

Display comments as Linear | Threaded

No comments

Add Comment

Enclosing asterisks marks text as bold (*word*), underscore are made via _word_.
Standard emoticons like :-) and ;-) are converted to images.
BBCode format allowed
Form options

Submitted comments will be subject to moderation before being displayed.