Since this comes up time and time again, I figured I'd lay out a little FDS information that may be helpful to others analyzing titles, or in fact creating new FDS content. This is a description of the pattern observed in the Nintendo titles Super Mario Bros., The Legend of Zelda, and Doki Doki Panic, so at the very least may point to a trend or pattern in their work. However, I suspect other developers implemented similar systems.
On the initial startup, the FDS looks at byte $19 in disk block one (superblock) and loads any files into memory having a file ID equal to or less than this number. In all analyzed titles, this value was $0F, and the files meeting the criteria ranged from the entire game in the case of straight NROM ports like SMB to just the title sequence and core game functionality as in Zelda and DDP. Files include a header describing their origin address, with low addresses ($0000-$3000) describing VRAM destinations and high addresses (>=$6000) being in PRG on the RAM adapter. Among the files is always "KYODAKU-" which is the BG mapping for the copyright screen that pops up on successful load of an FDS title. It acts as a security check, being compared with the copy in the boot ROM. An aside, I won't detail it here as it is described elsewhere but DDP features an added security feature in the form of a hidden file and a special copy of the file loading routine to read it. This is absent in other titles so serves as a curiosity isolated to Doki Doki Panic at present.
This initial load state is then typically overwritten as the program executes, with boot ROM routines being used to swap in files as they are needed ($E1F8) as well as write files back in the case of save data ($E239). All three analyzed engines share core functionality in interfacing with the drive, things like a subroutine to wait for the disk to be ejected by the user, another to await detection of an inserted disk, yet another to display some sort of "please wait" onscreen. In Zelda and DDP, the disk loader then includes a routine to step over a series of file tables, with each one describing the full set of files needed from the disk, in order, to create the memory map for that particular area. In Zelda, these configurations are per "level" (dungeons + overworld) with a couple extra lists for endgame and save management. In DDP, they are instead the chapters and stages of the ending sequence. The list consists of the IDs of each file that should be loaded. The files are loaded sequentially, such that those later in the list overwrite those earlier in the case of overlaps. In both cases, the pointer to the list is then modified at runtime to change the load target. Since PRG is in RAM, the new pointer is written directly into the code.
The case with SMB and SMB2/ANN is a tad different. Owing to being a straight NROM port, SMB has no appreciable file management. SMB2/ANN on the other hand only have a few primary memory mapping states: clusters of courses and then some endgame material, so the behavior is essentially "unrolled" in that there's one copy of the file load per target file rather than code to modify the table pointer in PRG. The PRG map in the SMB2/ANN variant of this engine has been adjusted to allow for contiguous replacement regions for these purposes.
This sort of overlay system for instance allows multiple dungeons in Zelda to share particular subsets of objects and data while having smaller overlays for their differences. DDP isn't so meticulous, there's just one file per chapter. In all cases, the resident material tends to start early in PRG, although not at $6000, with the very tip of the memory region often featuring a small overlay , probably due to its predictable address. It then cuts somewhere around the middle, the boundary with the swappable material between execution modes. Then towards the end of PRG tends to be the sound engine and other APU bits, most of which is also resident throughout the lifecycle of a title. Particularly, DDP swaps some audio data out for the ending sequences, Zelda has three main sequences for overworld, dungeon 1-8, and final dungeon music, and SMB2/ANN then has a whole second copy of the sound engine and some extra sequences for the ending scene.
This points to an important practical point in analyzing FDS titles. The disk loader, at least primary, has to be in one of the mapped files from the initial load such that it can be used to bootstrap the rest of the game. Finding this table then gives one a view into what files are expected to be resident in memory at any given time. Identifying the permanently resident areas then also yields where the sections of PRG are that are context sensitive. Further analysis of flow may be needed if entry into a particular state may expect to follow another, this may imply expectations of resident data not explicitly laid out in the file load table.
So key things to keep in mind with loading FDS files:
On the initial startup, the FDS looks at byte $19 in disk block one (superblock) and loads any files into memory having a file ID equal to or less than this number. In all analyzed titles, this value was $0F, and the files meeting the criteria ranged from the entire game in the case of straight NROM ports like SMB to just the title sequence and core game functionality as in Zelda and DDP. Files include a header describing their origin address, with low addresses ($0000-$3000) describing VRAM destinations and high addresses (>=$6000) being in PRG on the RAM adapter. Among the files is always "KYODAKU-" which is the BG mapping for the copyright screen that pops up on successful load of an FDS title. It acts as a security check, being compared with the copy in the boot ROM. An aside, I won't detail it here as it is described elsewhere but DDP features an added security feature in the form of a hidden file and a special copy of the file loading routine to read it. This is absent in other titles so serves as a curiosity isolated to Doki Doki Panic at present.
This initial load state is then typically overwritten as the program executes, with boot ROM routines being used to swap in files as they are needed ($E1F8) as well as write files back in the case of save data ($E239). All three analyzed engines share core functionality in interfacing with the drive, things like a subroutine to wait for the disk to be ejected by the user, another to await detection of an inserted disk, yet another to display some sort of "please wait" onscreen. In Zelda and DDP, the disk loader then includes a routine to step over a series of file tables, with each one describing the full set of files needed from the disk, in order, to create the memory map for that particular area. In Zelda, these configurations are per "level" (dungeons + overworld) with a couple extra lists for endgame and save management. In DDP, they are instead the chapters and stages of the ending sequence. The list consists of the IDs of each file that should be loaded. The files are loaded sequentially, such that those later in the list overwrite those earlier in the case of overlaps. In both cases, the pointer to the list is then modified at runtime to change the load target. Since PRG is in RAM, the new pointer is written directly into the code.
The case with SMB and SMB2/ANN is a tad different. Owing to being a straight NROM port, SMB has no appreciable file management. SMB2/ANN on the other hand only have a few primary memory mapping states: clusters of courses and then some endgame material, so the behavior is essentially "unrolled" in that there's one copy of the file load per target file rather than code to modify the table pointer in PRG. The PRG map in the SMB2/ANN variant of this engine has been adjusted to allow for contiguous replacement regions for these purposes.
This sort of overlay system for instance allows multiple dungeons in Zelda to share particular subsets of objects and data while having smaller overlays for their differences. DDP isn't so meticulous, there's just one file per chapter. In all cases, the resident material tends to start early in PRG, although not at $6000, with the very tip of the memory region often featuring a small overlay , probably due to its predictable address. It then cuts somewhere around the middle, the boundary with the swappable material between execution modes. Then towards the end of PRG tends to be the sound engine and other APU bits, most of which is also resident throughout the lifecycle of a title. Particularly, DDP swaps some audio data out for the ending sequences, Zelda has three main sequences for overworld, dungeon 1-8, and final dungeon music, and SMB2/ANN then has a whole second copy of the sound engine and some extra sequences for the ending scene.
This points to an important practical point in analyzing FDS titles. The disk loader, at least primary, has to be in one of the mapped files from the initial load such that it can be used to bootstrap the rest of the game. Finding this table then gives one a view into what files are expected to be resident in memory at any given time. Identifying the permanently resident areas then also yields where the sections of PRG are that are context sensitive. Further analysis of flow may be needed if entry into a particular state may expect to follow another, this may imply expectations of resident data not explicitly laid out in the file load table.
So key things to keep in mind with loading FDS files:
- Check the byte at $19 in the header, this will indicate which file IDs your map starts with
- Find the routines which call boot ROM call $E1F8
- The pointer to the file list is the second address word after this call, and is often modified at runtime
- The files listed sequentially in the list then represent the memory overlay state
Statistics: Posted by segaloco — Thu Jan 02, 2025 1:30 am — Replies 0 — Views 61