The current URL is datacrystal.tcrf.net.
Super Mario Bros./Notes: Difference between revisions
(Added notes on player physics) |
|||
(14 intermediate revisions by 6 users not shown) | |||
Line 1: | Line 1: | ||
{{ | {{Notes|game=Super Mario Bros.}} | ||
=Startup= | |||
Engine startup occurs in response to the CPU reset cycle, either due to power on or console reset. In the FDS versions of this engine, the startup procedure occurs after the FDS boot ROM has initialized the system hardware and its own state, so some stuff is chopped out or FDS state bits added. | |||
In all cases, this routine is resident at the lowest possible PRG address for a given platform, 0x8000 in Famicom and Vs. System releases, 0x6000 in FDS releases. It is called only by system reset on Famicom and Vs. System and by the FDS boot ROM on FDS, which retrieve this vector from 0xFFFD and 0xDFFD respectively. The startup process is primarily concerned with sanitizing the hardware state before the NMI handler begins execution of the game-proper. | |||
The actions taken include, in detail: | |||
*(Famicom and Vs. only) Disable maskable interrupts (sei). | |||
** The FDS boot ROM uses the 2A03's primary interrupt interface, games running on that hardware assume interrupts are set however they need to be. | |||
* (Super Mario Bros. and Vs. only) Establish CPU and PPU state and sync. | |||
** Clear decimal mode (cld), leftover from debugging, 2A03 doesn't have decimal mode. | |||
** Initialize PPU segmentation to OBJ low and BG high. | |||
** Initialize stack pointer to the very end (0x1FF). | |||
** Synchronize with the PPU by awaiting the interrupt bit on the status register twice. | |||
** As the later FDS versions drop this initialization, this may be considered vestigial in the FDS release of SMB. | |||
* (FDS only) Establish vertical mirroring. | |||
** Without a cartridge, the mirroring of an FDS title is provided by a bit in the control register. This is done in different places in startup in the different FDS releases. | |||
* Clear RAM. | |||
** RAM is cleared all the way up to a flag in the highest byte of working memory (0x7FF), except... | |||
** (Vs. Excluded) on all home consoles, the top score is checked (0x7D7), if it is numeric and aforementioned flag is set to a particular value (0xA5), RAM will instead only be cleared up to before the top score. This allows for a cold/warm-boot scenario in which scores and other critical data can retain state across a reset. | |||
* Initialize DMC. | |||
** Zero the data register and enable pulses, triangle, and noise. | |||
* Initialize engine state. | |||
** The system process ID (0x770) is set to the title screen on console and Vs. releases and the disk loader mode (to load the title sequence off disk) in the SMB2/ANN engine. | |||
** At this point the warm-boot flag is set. | |||
** This flag value (0xA5) is also used to seed the RNG (0x7A7). | |||
* Initialize PPU. | |||
** Blanking and color mode are setup. | |||
** The OAM DMA buffer (0x200-0x2FF) is initialized by pushing all of the sprites offscreen. | |||
** The left and right nametables are filled with a blank BG tile, their attributes are cleared, and the scroll positions zeroed. | |||
** A flag (0x774) is set to disable NMI display functionality. | |||
* (Vs. only) Initialize Vs. System state. | |||
** The Vs. System incorporates additional RAM (0x6000), this engine uses two 256-byte pages of it (0x6600-0x67FF) for various state such as high scores. These pages are cleared. | |||
** Saved data is loaded in from the high CHR bank (0x1E00) to some of this memory (0x6674-0x66FF). Presumably this replaces the warm-boot present in console versions. | |||
** The top score (0x66BA) is loaded into engine memory (0x7D7). | |||
** The DIP switch state is captured, interpreted, and stored for later use (0x6600-0x6606). | |||
* (FDS only) Enable maskable interrupts (cli). | |||
** SMB2 and ANN use the IRQ vector as part of media access. | |||
** Vs. SMB uses IRQ as well but anticipates different startup state than the FDS versions. | |||
** SMB for FDS still retains a nonsense IRQ vector, so this just establishes known state. | |||
* Enable non-maskable interrupts. | |||
** The interrupt bit of the low PPU control register is set, NMI may now occur. | |||
** The game has now started, as all of the rest of the engine's execution takes place in the NMI handler, frame by frame. The startup routine enters a tight loop. The SMB2/ANN version throws in reading the base of work RAM, maybe for timing reasons. | |||
=Vertical (Non-Maskable) Interrupt= | |||
The vertical interrupt is primarily responsible for frame-to-frame operations on this platform. In the case of this particular engine, the entire process is a frame-to-frame operation, with the NMI acting as the traffic controller for the game's execution as well as refreshing hardware state per-frame. The NMI follows the system startup routine in PRG and is vectored to by 0xFFFA and 0xDFFA depending on the platform of a given release. The interrupt bit of the PPU control register is disabled during the NMI handler to prevent overlap. | |||
The actions taken include the following. Some detail is given but excursions into other subsystems such as the sound engine will be localized to their own sections: | |||
==Blank Display== | |||
Display is blanked if a given flag (0x774) is set. | |||
==Sprite DMA== | |||
Sprite data is simply refreshed by writing the address of the DMA buffer to the DMA register. | |||
==BG Update Requests== | |||
The NMI handler includes a series of pointers to buffers containing data in the format described in [[#Stripe Image|Stripe Image]] below. An index in memory (0x773) indicates which buffer will be drawn to the screen this frame from this table of pointers. The requested data is written to the given nametable and/or attribute address, updating the screen. | |||
==Sound Cycle== | |||
The sound engine is cycled forward one frame. Details can be found in [[#Sound Engine|Sound Engine]] | |||
==Joypad Reading== | |||
Joypad reading in Super Mario Bros. is fairly conventional for Famicom, the request bit is flipped and then the value is read as strobed back from the controller, one button at a time. The buttons are stored in a byte as follows: ABsSUDLR with s being select. | |||
The order of bits for several other directional operations utilize the same directions as controller bits. For instance, motion directions are given as either %00000010 for left or %00000001 for right, as these values are often used in bitwise comparison with controller inputs. | |||
The buttons start and select are explicitly stripped out of the returned value if they were already encountered on a previous frame of joypad reading. | |||
==Pause Determination== | |||
Vs. SMB did not feature pausing, so this is removed in that version. The pause state is only checked while the game is either in the victory mode or in normal gameplay processing, but not the title screen or during a game over. A cooldown counter (0x777) is used for debouncing the start button press, if this counter has ticks, it is ticked down and nothing happens. Otherwise, if the start button has been pressed and it is not currently being debounced, set the debounce values and toggle the pause state (0x776). Debouncing values are cleared if no start press was detected, meaning the player has stopped pressing the button. The pause state will then be used later in the frame processing to control flow. | |||
==Top Score Determination== | |||
If the player's score (0x7DD) is found to have increased past the current top score (0x7D7), then it is copied to the top score. | |||
==System Timers== | |||
This engine features a series of timers (0x780) that are decremented on regular intervals by the NMI. These timers are only decremented when the game is not paused. Additionally, this decrement step can be explicitly disabled via a flag (0x747). A subset of timers (0x780-0x78E) are decremented every frame whereas the remaining timers are only decremented per countdown of another special timer (0x77F) every 20 frames for NTSC or 17 frames for PAL. This results in a (NTSC/PAL) resolution of roughly 16.7/20.0ms per tick of the first group of timers and 333.3/340.0ms per tick of the second group. The frame counter is also incremented as part of the timer ticks. As such, the frame counter is also stationary when the game is paused. Frame count, however, is only blocked by pause status, not other conditions that prevent timer ticks. | |||
==Entropy== | |||
Entropy is maintained as a 56-bit rotating buffer (0x7A7) in which bit 1 of the first two entries determines which binary value is rotated onto the buffer each frame. More specifically, if bit 1 of the first two bytes does not match, a 1 is rotated right into the buffer. Otherwise, a zero is rotated right into the buffer. This buffer was seeded with %10100101 in the first 8 bits in the system startup. | |||
==Sprite Overlap== | |||
If sprites are detected as overlapping, and the game is not paused, then sprites are first all moved offscreen. The sprites are then shuffled back on based on a prioritization algorithm. As the rendering routines for sprites will be run later in the frame, this is done by modifying memory at the sprite level, not in the DMA buffer. This ensures that only the maximum number of visible sprites are being drawn per scanline, any that cannot be drawn remain offscreen that frame. | |||
==Scroll Update== | |||
The scroll registers are updated by the NMI, although the values written are those requested by the previous frame. This frame the values calculated will be used to set the scroll parameters on the next frame. In the SMB2/ANN variant of the engine, this is instead done in the IRQ interrupt. | |||
==Subsystem Routine== | |||
Finally after all of these per-frame operations, a frames-worth of game logic will be processed. The game consists of a series of subsystems that then have a number of process states. The NMI jumps into the subsystem currently executing (0x770) and then into the specific process state of that subsystem (0x772). The subsystems include: | |||
* (SMB/Vs. only) Title screen | |||
* (SMB2/ANN only) Disk loader | |||
* (Vs. only) Player select | |||
* Gameplay | |||
* [[#Castle Victory Handler|Castle Victory]] | |||
* Game Over | |||
The nature of the specific process is determined by the subsystem itself, with the NMI simply jumping into it after all per-frame operations are complete. | |||
=IRQ= | |||
IRQ is only used by the SMB2/ANN version of the engine and the Vs. System release and for different purposes in each. In SMB2/ANN, the IRQ is to structure certain behaviors like scroll setting around media transfer status. In the Vs. System, the IRQ sets the CHR bank low automatically in response to a given status register bit. | |||
=Castle Victory Handler= | |||
The castle victory handler consists of a number of phases which comprise the actions taken between touching the axe at the end of a castle and progressing to the next course. SMB2/ANN feature a slightly different set of operations when loading the game ending. Where relevant these will be pointed out. | |||
==Bowser Routine== | |||
Before the handler truly takes over, it first runs a routine in the Bowser actor such that if Bowser is still alive, every four ticks: | |||
* Toggle his step animation frame. | |||
* Clear another metatile of the axe, chain, and bridge. | |||
* Request the blast and block breaking SFX on pulse 2 and noise respectively. | |||
After the last bridge segment is gone, Bowser begins to fall offscreen. Once he has reached scanline 224, he's considered dead and the victory handler-proper begins. Of note, if Bowser has been previously killed by fire, this routine exits immediately without entering the animation portion. As such, the axe, chain, and bridge remain on screen. | |||
==Initialization== | |||
A little setup is done first. Specifically in ANN, each castle houses a different "Toad" representing different All Night Nippon folks, and their data is initialized here. The screen position is captured (0x34) for later processing. Specific course victories are kept track of in SMB2, so these are added to a bitfield (0x7FA), and in both SMB2 and ANN, the ending routine should be loaded in course D as well, so in this case the course number is spoofed as course 8 for subsequent operations. Finally, the normal castle victory music is requested. | |||
==Scrolling== | |||
So long as the player has not reached the page boundary for the start of the final room of the castle, he walks forward at a normal speed automatically as the screen scrolls to the right. Once the player has reached column 96 of the next page and the screen has scrolled a full page, the scrolling process is complete. | |||
==Disk Access== | |||
For SMB2 and ANN, the castle for course 8 and course D loads an "enhanced" ending for the completion of the game. This derives in part from the ending of Vs. Super Mario Bros. The data for this ending is stored in file DATA3 on the disk and must be loaded in prior to running the ending. | |||
=Player physics/movement algorithm= | =Player physics/movement algorithm= | ||
* Signed vertical velocity in whole pixels at 0x009F | * Signed vertical velocity in whole pixels at 0x009F | ||
* Unsigned(?) vertical velocity in fractions of a pixel at 0x0433 | * Unsigned(?) vertical velocity in fractions of a pixel at 0x0433 | ||
* | * Gravity varies, see RAM map for 0x0709/070A | ||
* | |||
** The gravity ( | * Basic integration of vertical velocity (when player not grounded) | ||
** If the carry flag is set ( | ** The gravity (see above) is added to 0x0433 with ADC. | ||
** If the carry flag is set (addition into 0x0433 overflowed), a whole pixel (0x01) is added to 0x009F. | |||
** Note: 0x009F is clamped to a maximum positive value of 0x05, but 0x0433 is NOT cleared. This could lead to various round-off errors leading into the next jump (unconfirmed). | ** Note: 0x009F is clamped to a maximum positive value of 0x05, but 0x0433 is NOT cleared. This could lead to various round-off errors leading into the next jump (unconfirmed). | ||
* Jumps | |||
** An instant velocity impulse is set, e.g. 0xFB, propelling the sprite upward. | |||
** Gravity is also modified, e.g. set to 0x30 (weaker) when going up and then to 0xA8 (stronger) when falling down. | |||
** These values VARY based on jump power/horizontal velocity. See RAM map for some possible values. Exact algorithm yet unknown. | |||
= Hacking the End-of-Level Castles = | = Hacking the End-of-Level Castles = | ||
Line 159: | Line 295: | ||
C5 - Axe | C5 - Axe | ||
</pre> | </pre> | ||
=Stripe Image= | |||
This data format represents title screens and other static background layouts in a ''lot'' of games published by Nintendo. ''SMB'' uses it in ROM for the title screen and other strings (see [[Super Mario Bros.:ROM map|ROM map]]) and in RAM for the transfer buffer. | |||
An image consists of several packets, where each packet is as follows. A 00 byte follows the last packet in an image. | |||
<pre> | |||
0 VRAM destination address high byte (20-3F) | |||
1 VRAM destination address low byte | |||
2 Flags and length | |||
irnn nnnn | |||
||++-++++- Length minus 1 (00: 1 byte; 3F: 64 bytes) | |||
|+-------- Run (0: 1-64 literal bytes follow; 1: 1 byte follows repeated 1-64 times) | |||
+--------- Increment (0: add 1 to VRAM address after each byte; 1: add 32) | |||
3+ Data | |||
</pre> | |||
=Sound Engine= | |||
==Interface== | |||
The sound engine in Super Mario Bros. is cycled every frame as a part of the vertical interrupt. The engine makes use of several variables to determine what actions should be taken every frame. Among these are a series of bit-fields used for requesting different music or sound effects. These bit-fields are set to trigger the sound engine to attempt playing the requested content on the next frame. Music bit-fields influence multi-layer music tracks, while there are also bit-fields for sound effects made by specific APU channels. | |||
<div style="display:inline-grid"> | |||
{| class="wikitable" | |||
|+ General Music - 0xFB | |||
|- | |||
! Bit !! Music | |||
|- | |||
| 0 || Overworld | |||
|- | |||
| 1 || Underwater | |||
|- | |||
| 2 || Underground | |||
|- | |||
| 3 || Castle | |||
|- | |||
| 4 || Clouds/Bonus | |||
|- | |||
| 5 || Overworld Pipe Intro | |||
|- | |||
| 6 || Super Star | |||
|- | |||
| 7 || None | |||
|} | |||
</div> | |||
<div style="display:inline-grid"> | |||
{| class="wikitable" | |||
|+ Event Music - 0xFC | |||
|- | |||
! Bit !! Music | |||
|- | |||
| 0 || Death | |||
|- | |||
| 1 || Game Over | |||
|- | |||
| 2 || Normal Victory | |||
|- | |||
| 3 || Castle Victory | |||
|- | |||
| 4 || Alternate Game Over | |||
|- | |||
| 5 || Level Ending | |||
|- | |||
| 6 || Hurry Up | |||
|- | |||
| 7 || None | |||
|} | |||
</div> | |||
<div style="display:inline-grid"> | |||
{| class="wikitable" | |||
|+ Noise SFX - 0xFD | |||
|- | |||
! Bit !! SFX | |||
|- | |||
| 0 || Block Break | |||
|- | |||
| 1 || Flame | |||
|- | |||
| 2 || Wind (SMB2) | |||
|- | |||
| 3 || Undefined | |||
|- | |||
| 4 || Undefined | |||
|- | |||
| 5 || Undefined | |||
|- | |||
| 6 || Undefined | |||
|- | |||
| 7 || Skid (SMB2/ANN) | |||
|} | |||
</div> | |||
<div style="display:inline-grid"> | |||
{| class="wikitable" | |||
|+ Pulse 2 SFX - 0xFE | |||
|- | |||
! Bit !! SFX | |||
|- | |||
| 0 || Coin | |||
|- | |||
| 1 || Powerup Spawn | |||
|- | |||
| 2 || Vine Spawn | |||
|- | |||
| 3 || Blast | |||
|- | |||
| 4 || Tick | |||
|- | |||
| 5 || Powerup | |||
|- | |||
| 6 || One Up | |||
|- | |||
| 7 || Bowser Fall | |||
|} | |||
</div> | |||
<div style="display:inline-grid"> | |||
{| class="wikitable" | |||
|+ Pulse 1 SFX - 0xFF | |||
|- | |||
! Bit !! SFX | |||
|- | |||
| 0 || Jump (Big) | |||
|- | |||
| 1 || Bump | |||
|- | |||
| 2 || Stomp | |||
|- | |||
| 3 || Smack | |||
|- | |||
| 4 || Pipe Entry | |||
|- | |||
| 5 || Projectile | |||
|- | |||
| 6 || Pole Slide | |||
|- | |||
| 7 || Jump (Small) | |||
|} | |||
</div> | |||
==Music== | |||
Music bit-fields are checked for set bits from right to left, with the first such set bit triggering that music to play, with event music taking precedence over general music. For example, if 0xFB is 0x3B and 0xFC is 0x12, then the game over music will play since 0xFC is handled before 0xFB and the right-most set bit in 0xFC is the game over bit. | |||
===Structures=== | |||
Each music offset is then the offset from the start of the music table to the descriptor for that particular piece of music. This table contains 16 entries, 8 for the event music followed by 8 for the general music. | |||
A music entry then consists of an offset to the note length structure applicable to the track data, a pointer to the track data itself, and fields indicating where in the track data the pulse 1, triangle, noise, and, in the case of FDS, wavetable portions of the track start in the data. The pulse 2 portion is always the first, so an offset to pulse 2 is not stored. | |||
The length structures are a series of 8 possible tone lengths, with multiple such lists provided for different timing needs. There are several groups and different tracks may use different sets of lengths. The entries correspond with timing concepts such as whole notes, half notes, quarter notes, and so on. | |||
The notes themselves are indices into a table of timing values to supply to the timing registers of the given oscillators in the case of tonal sound and specific sound IDs in the case of the noise channel. The table of timing values is generally in chromatic order but does not include all possible notes. | |||
===Formats=== | |||
The track data itself is encoded in one of two formats depending on the channel: | |||
Pulse 2, triangle, and wave-table data are stored with one length indicator followed by multiple notes to be played at that length, then the next length indicator, and so on, until the end of that channel in the track which is indicated by a value of 0x00 in the case of the pulse 2 channel, with the triangle and wave-table channels expected to fill the same time as the pulse 2 channel. To indicate that a byte is a length indicator, its high bit is set, such that values 0x80-0x87 represent the 8 possible length values for a following series of notes. This format will be referred to as the "uncompressed" format. | |||
Pulse 1 and the noise channel are instead stored in a compressed format in which the length of a note as well as its ID are encoded in the same byte in the format "BCNNNNNA" where "00NNNNN0" is the identity of a given note and "ABC" is the length number for that note. A value of 0x00 may also be given for specifically the noise channel to indicate the end of that channel. As of this analysis, the behavior when pulse 2 and noise disagree on track ending has not been analyzed. The end result is every note has a length included in pulse 1 and noise formats, but a length is established for a run of notes for the other channels. For instance, to request that a note of ID 0x2C be played for length 5, the resulting byte would be 0x6D. This format will be referred to as the "compressed" format. | |||
===Processing=== | |||
Each frame, the uncompressed tracks are read for a single byte. If that byte is a length byte (indicated by the high bit set), the new length is saved and the next byte retrieved as a note. As for the compressed data, a byte is read every frame and decomposed into the length and note encoded therein. In either case, the length byte is then counted down each frame until it reaches zero, at which point a new read cycle happens. | |||
Some channels will additionally check first to determine if a sound effect is already being played on a given channel. If so, that frame of the channel will be read out but not sent to the APU as to keep the tracks in sync while not interfering with sound effects. | |||
===FDS Secondary Engine=== | |||
It is important to note that in the updated engine used in Super Mario Bros. 2 as well as All Night Nippon, a secondary sound engine is loaded in for the game ending sequence. This sound engine bears much similarity to the standard one, but has added provisions for handling FDS wave-table audio as well as trims out some specific checks that only applied to music being played at gameplay time. The original sound engine is still resident in memory when this secondary one is loaded in, and may even be used, so it does feature some checks to, for instance, determine if the conventional sound engine is playing a sound effect on a channel and to mute the music on that channel for that frame if possible. This also means that the memory used by the secondary sound engine, while comparable in function, is stored elsewhere. This encompasses, for instance, the pointers to the current track data and note lengths. | |||
{{Internal Data|game=Super Mario Bros.}} |
Latest revision as of 08:21, 20 November 2024
The following article is a Notes Page for Super Mario Bros..
Startup
Engine startup occurs in response to the CPU reset cycle, either due to power on or console reset. In the FDS versions of this engine, the startup procedure occurs after the FDS boot ROM has initialized the system hardware and its own state, so some stuff is chopped out or FDS state bits added.
In all cases, this routine is resident at the lowest possible PRG address for a given platform, 0x8000 in Famicom and Vs. System releases, 0x6000 in FDS releases. It is called only by system reset on Famicom and Vs. System and by the FDS boot ROM on FDS, which retrieve this vector from 0xFFFD and 0xDFFD respectively. The startup process is primarily concerned with sanitizing the hardware state before the NMI handler begins execution of the game-proper.
The actions taken include, in detail:
- (Famicom and Vs. only) Disable maskable interrupts (sei).
- The FDS boot ROM uses the 2A03's primary interrupt interface, games running on that hardware assume interrupts are set however they need to be.
- (Super Mario Bros. and Vs. only) Establish CPU and PPU state and sync.
- Clear decimal mode (cld), leftover from debugging, 2A03 doesn't have decimal mode.
- Initialize PPU segmentation to OBJ low and BG high.
- Initialize stack pointer to the very end (0x1FF).
- Synchronize with the PPU by awaiting the interrupt bit on the status register twice.
- As the later FDS versions drop this initialization, this may be considered vestigial in the FDS release of SMB.
- (FDS only) Establish vertical mirroring.
- Without a cartridge, the mirroring of an FDS title is provided by a bit in the control register. This is done in different places in startup in the different FDS releases.
- Clear RAM.
- RAM is cleared all the way up to a flag in the highest byte of working memory (0x7FF), except...
- (Vs. Excluded) on all home consoles, the top score is checked (0x7D7), if it is numeric and aforementioned flag is set to a particular value (0xA5), RAM will instead only be cleared up to before the top score. This allows for a cold/warm-boot scenario in which scores and other critical data can retain state across a reset.
- Initialize DMC.
- Zero the data register and enable pulses, triangle, and noise.
- Initialize engine state.
- The system process ID (0x770) is set to the title screen on console and Vs. releases and the disk loader mode (to load the title sequence off disk) in the SMB2/ANN engine.
- At this point the warm-boot flag is set.
- This flag value (0xA5) is also used to seed the RNG (0x7A7).
- Initialize PPU.
- Blanking and color mode are setup.
- The OAM DMA buffer (0x200-0x2FF) is initialized by pushing all of the sprites offscreen.
- The left and right nametables are filled with a blank BG tile, their attributes are cleared, and the scroll positions zeroed.
- A flag (0x774) is set to disable NMI display functionality.
- (Vs. only) Initialize Vs. System state.
- The Vs. System incorporates additional RAM (0x6000), this engine uses two 256-byte pages of it (0x6600-0x67FF) for various state such as high scores. These pages are cleared.
- Saved data is loaded in from the high CHR bank (0x1E00) to some of this memory (0x6674-0x66FF). Presumably this replaces the warm-boot present in console versions.
- The top score (0x66BA) is loaded into engine memory (0x7D7).
- The DIP switch state is captured, interpreted, and stored for later use (0x6600-0x6606).
- (FDS only) Enable maskable interrupts (cli).
- SMB2 and ANN use the IRQ vector as part of media access.
- Vs. SMB uses IRQ as well but anticipates different startup state than the FDS versions.
- SMB for FDS still retains a nonsense IRQ vector, so this just establishes known state.
- Enable non-maskable interrupts.
- The interrupt bit of the low PPU control register is set, NMI may now occur.
- The game has now started, as all of the rest of the engine's execution takes place in the NMI handler, frame by frame. The startup routine enters a tight loop. The SMB2/ANN version throws in reading the base of work RAM, maybe for timing reasons.
Vertical (Non-Maskable) Interrupt
The vertical interrupt is primarily responsible for frame-to-frame operations on this platform. In the case of this particular engine, the entire process is a frame-to-frame operation, with the NMI acting as the traffic controller for the game's execution as well as refreshing hardware state per-frame. The NMI follows the system startup routine in PRG and is vectored to by 0xFFFA and 0xDFFA depending on the platform of a given release. The interrupt bit of the PPU control register is disabled during the NMI handler to prevent overlap.
The actions taken include the following. Some detail is given but excursions into other subsystems such as the sound engine will be localized to their own sections:
Blank Display
Display is blanked if a given flag (0x774) is set.
Sprite DMA
Sprite data is simply refreshed by writing the address of the DMA buffer to the DMA register.
BG Update Requests
The NMI handler includes a series of pointers to buffers containing data in the format described in Stripe Image below. An index in memory (0x773) indicates which buffer will be drawn to the screen this frame from this table of pointers. The requested data is written to the given nametable and/or attribute address, updating the screen.
Sound Cycle
The sound engine is cycled forward one frame. Details can be found in Sound Engine
Joypad Reading
Joypad reading in Super Mario Bros. is fairly conventional for Famicom, the request bit is flipped and then the value is read as strobed back from the controller, one button at a time. The buttons are stored in a byte as follows: ABsSUDLR with s being select.
The order of bits for several other directional operations utilize the same directions as controller bits. For instance, motion directions are given as either %00000010 for left or %00000001 for right, as these values are often used in bitwise comparison with controller inputs.
The buttons start and select are explicitly stripped out of the returned value if they were already encountered on a previous frame of joypad reading.
Pause Determination
Vs. SMB did not feature pausing, so this is removed in that version. The pause state is only checked while the game is either in the victory mode or in normal gameplay processing, but not the title screen or during a game over. A cooldown counter (0x777) is used for debouncing the start button press, if this counter has ticks, it is ticked down and nothing happens. Otherwise, if the start button has been pressed and it is not currently being debounced, set the debounce values and toggle the pause state (0x776). Debouncing values are cleared if no start press was detected, meaning the player has stopped pressing the button. The pause state will then be used later in the frame processing to control flow.
Top Score Determination
If the player's score (0x7DD) is found to have increased past the current top score (0x7D7), then it is copied to the top score.
System Timers
This engine features a series of timers (0x780) that are decremented on regular intervals by the NMI. These timers are only decremented when the game is not paused. Additionally, this decrement step can be explicitly disabled via a flag (0x747). A subset of timers (0x780-0x78E) are decremented every frame whereas the remaining timers are only decremented per countdown of another special timer (0x77F) every 20 frames for NTSC or 17 frames for PAL. This results in a (NTSC/PAL) resolution of roughly 16.7/20.0ms per tick of the first group of timers and 333.3/340.0ms per tick of the second group. The frame counter is also incremented as part of the timer ticks. As such, the frame counter is also stationary when the game is paused. Frame count, however, is only blocked by pause status, not other conditions that prevent timer ticks.
Entropy
Entropy is maintained as a 56-bit rotating buffer (0x7A7) in which bit 1 of the first two entries determines which binary value is rotated onto the buffer each frame. More specifically, if bit 1 of the first two bytes does not match, a 1 is rotated right into the buffer. Otherwise, a zero is rotated right into the buffer. This buffer was seeded with %10100101 in the first 8 bits in the system startup.
Sprite Overlap
If sprites are detected as overlapping, and the game is not paused, then sprites are first all moved offscreen. The sprites are then shuffled back on based on a prioritization algorithm. As the rendering routines for sprites will be run later in the frame, this is done by modifying memory at the sprite level, not in the DMA buffer. This ensures that only the maximum number of visible sprites are being drawn per scanline, any that cannot be drawn remain offscreen that frame.
Scroll Update
The scroll registers are updated by the NMI, although the values written are those requested by the previous frame. This frame the values calculated will be used to set the scroll parameters on the next frame. In the SMB2/ANN variant of the engine, this is instead done in the IRQ interrupt.
Subsystem Routine
Finally after all of these per-frame operations, a frames-worth of game logic will be processed. The game consists of a series of subsystems that then have a number of process states. The NMI jumps into the subsystem currently executing (0x770) and then into the specific process state of that subsystem (0x772). The subsystems include:
- (SMB/Vs. only) Title screen
- (SMB2/ANN only) Disk loader
- (Vs. only) Player select
- Gameplay
- Castle Victory
- Game Over
The nature of the specific process is determined by the subsystem itself, with the NMI simply jumping into it after all per-frame operations are complete.
IRQ
IRQ is only used by the SMB2/ANN version of the engine and the Vs. System release and for different purposes in each. In SMB2/ANN, the IRQ is to structure certain behaviors like scroll setting around media transfer status. In the Vs. System, the IRQ sets the CHR bank low automatically in response to a given status register bit.
Castle Victory Handler
The castle victory handler consists of a number of phases which comprise the actions taken between touching the axe at the end of a castle and progressing to the next course. SMB2/ANN feature a slightly different set of operations when loading the game ending. Where relevant these will be pointed out.
Bowser Routine
Before the handler truly takes over, it first runs a routine in the Bowser actor such that if Bowser is still alive, every four ticks:
- Toggle his step animation frame.
- Clear another metatile of the axe, chain, and bridge.
- Request the blast and block breaking SFX on pulse 2 and noise respectively.
After the last bridge segment is gone, Bowser begins to fall offscreen. Once he has reached scanline 224, he's considered dead and the victory handler-proper begins. Of note, if Bowser has been previously killed by fire, this routine exits immediately without entering the animation portion. As such, the axe, chain, and bridge remain on screen.
Initialization
A little setup is done first. Specifically in ANN, each castle houses a different "Toad" representing different All Night Nippon folks, and their data is initialized here. The screen position is captured (0x34) for later processing. Specific course victories are kept track of in SMB2, so these are added to a bitfield (0x7FA), and in both SMB2 and ANN, the ending routine should be loaded in course D as well, so in this case the course number is spoofed as course 8 for subsequent operations. Finally, the normal castle victory music is requested.
Scrolling
So long as the player has not reached the page boundary for the start of the final room of the castle, he walks forward at a normal speed automatically as the screen scrolls to the right. Once the player has reached column 96 of the next page and the screen has scrolled a full page, the scrolling process is complete.
Disk Access
For SMB2 and ANN, the castle for course 8 and course D loads an "enhanced" ending for the completion of the game. This derives in part from the ending of Vs. Super Mario Bros. The data for this ending is stored in file DATA3 on the disk and must be loaded in prior to running the ending.
Player physics/movement algorithm
- Signed vertical velocity in whole pixels at 0x009F
- Unsigned(?) vertical velocity in fractions of a pixel at 0x0433
- Gravity varies, see RAM map for 0x0709/070A
- Basic integration of vertical velocity (when player not grounded)
- The gravity (see above) is added to 0x0433 with ADC.
- If the carry flag is set (addition into 0x0433 overflowed), a whole pixel (0x01) is added to 0x009F.
- Note: 0x009F is clamped to a maximum positive value of 0x05, but 0x0433 is NOT cleared. This could lead to various round-off errors leading into the next jump (unconfirmed).
- Jumps
- An instant velocity impulse is set, e.g. 0xFB, propelling the sprite upward.
- Gravity is also modified, e.g. set to 0x30 (weaker) when going up and then to 0xA8 (stronger) when falling down.
- These values VARY based on jump power/horizontal velocity. See RAM map for some possible values. Exact algorithm yet unknown.
Hacking the End-of-Level Castles
1) Get some graphing paper, and start cordoning 2x2 squares. Make a 5x5 block of such squares (10x10 overall).
2) Reading right-to-left, from the top row down, you've got the metatile defined by $17DF, $17E0, $17E1, etc......, $17F7.
3) This linear string of 25 bytes can be represented on your graph paper like this:
5 4 3 2 1 10 9 8 7 6 15 14 13 12 11 20 19 18 17 16 25 24 23 22 21
The first byte in this string codes for the upper-rightmost metatile, the second byte codes for the metatile directly to #'s left, etc.
4) The large castles at the end of every X-3 level (and the beginning of most X-1 levels) are simply the small castle stacked on top of another, slightly taller one. This taller castle is 6 metatiles high and five metatiles wide, and is coded in a similar right-to-left, top-down fashion. Its linear byte sequence begins at $17F8.
5) The little side-sections on the X-3 castle type are actually the same background image that occurs throughout World 8-3. This image is 1 metatile wide and eleven metatiles high (although it only appears to be six metatiles high since the first five bytes are assigned transparent "00" values). Its coding begins at $13CE and ends at $13D8.
Mario Palette Information
Main Palettes
There are four main palettes each with a unique set of 8 colors defined (background, pipe, cloud, brick, mario, green enemy, red enemy, brown enemy)
- Water
- Ground
- Underground
- Castle
4 alternate palettes
- Night (sets background)
- Snow (sets background, pipe color)
- Night/Snow 1 (sets background, pipe colors)
- Night/Snow 2 (uses castle palette for everything but background)
Other
- Mushroom type sets pipe color (over rides all other pipe color settings)
- Bowser has special palette
Metatile list
00 - Blank 01 - Black Metatile 02 - Bush Left 03 - Bush Middle 04 - Bush Right 05 - Mountain Left 06 - Mountain Left Bottom/Middle Center 07 - Mountain Middle Top 08 - Mountain Right 09 - Mountain Right Bottom 0A - Mountain Middle Bottom 0B - Bridge Guardrail 0C - Chain 0D - Tall Tree Top, Top Half 0E - Short Tree Top 0F - Tall Tree Top, Bottom Half 10 - Warp Pipe End Left, Points Up 11 - Warp Pipe End Right, Points Up 12 - Decoration Pipe End Left, Points Up 13 - Decoration Pipe End Right, Points Up 14 - Pipe Shaft Left 15 - Pipe Shaft Right 16 - Tree Ledge Left Edge 17 - Tree Ledge Middle 18 - Tree Ledge Right Edge 19 - Mushroom Left Edge 1A - Mushroom Middle 1B - Mushroom Right Edge 1C - Sideways Pipe End Top 1D - Sideways Pipe Shaft Top 1E - Sideways Pipe Joint Top 1F - Sideways Pipe End Bottom 20 - Sideways Pipe Shaft Bottom 21 - Sideways Pipe Joint Bottom 22 - Seaplant 23 - Blank, Used On Bricks Or Blocks That Are Hit 24 - Flagpole Ball 25 - Flagpole Shaft 26 - Blank, Used In Conjunction With Vines 40 - Vertical Rope 41 - Horizontal Rope 42 - Left Pulley 43 - Right Pulley 44 - Blank Used For Balance Rope 45 - Castle Top 46 - Castle Window Left 47 - Castle Brick Wall 48 - Castle Window Right 49 - Castle Top W/ Brick 4A - Entrance Top 4B - Entrance Bottom 4C - Green Ledge Stump 4D - Fence 4E - Tree Trunk 4F - Mushroom Stump Top 50 - Mushroom Stump Bottom 51 - Breakable Brick W/ Line 52 - Breakable Brick 53 - Breakable Brick (Not Used) 54 - Cracked Rock Terrain 55 - Brick With Line (Power-Up) 56 - Brick With Line (Vine) 57 - Brick With Line (Star) 58 - Brick With Line (Coins) 59 - Brick With Line (1-Up) 5A - Brick (Power-Up) 5B - Brick (Vine) 5C - Brick (Star) 5D - Brick (Coins) 5E - Brick (1-Up) 5F - Hidden Block (1 Coin) 60 - Hidden Block (1-Up) 61 - Solid Block (3-D Block) 62 - Solid Block (White Wall) 63 - Bridge 64 - Bullet Bill Cannon Barrel 65 - Bullet Bill Cannon Top 66 - Bullet Bill Cannon Bottom 67 - Blank Used For Jumpspring 68 - Half Brick Used For Jumpspring 69 - Solid Block (Water Level, Green Rock) 6A - Half Brick (???) 6B - Water Pipe Top 6C - Water Pipe Bottom 6D - Flag Ball (Residual Object) 80 - Cloud Left 81 - Cloud Middle 82 - Cloud Right 83 - Cloud Bottom Left 84 - Cloud Bottom Middle 85 - Cloud Bottom Right 86 - Water/Lava Top 87 - Water/Lava 88 - Cloud Level Terrain 89 - Bowser's Bridge C0 - Question Block (Coin) C1 - Question Block (Power-Up) C2 - Coin C3 - Underwater Coin C4 - Empty Block C5 - Axe
Stripe Image
This data format represents title screens and other static background layouts in a lot of games published by Nintendo. SMB uses it in ROM for the title screen and other strings (see ROM map) and in RAM for the transfer buffer.
An image consists of several packets, where each packet is as follows. A 00 byte follows the last packet in an image.
0 VRAM destination address high byte (20-3F) 1 VRAM destination address low byte 2 Flags and length irnn nnnn ||++-++++- Length minus 1 (00: 1 byte; 3F: 64 bytes) |+-------- Run (0: 1-64 literal bytes follow; 1: 1 byte follows repeated 1-64 times) +--------- Increment (0: add 1 to VRAM address after each byte; 1: add 32) 3+ Data
Sound Engine
Interface
The sound engine in Super Mario Bros. is cycled every frame as a part of the vertical interrupt. The engine makes use of several variables to determine what actions should be taken every frame. Among these are a series of bit-fields used for requesting different music or sound effects. These bit-fields are set to trigger the sound engine to attempt playing the requested content on the next frame. Music bit-fields influence multi-layer music tracks, while there are also bit-fields for sound effects made by specific APU channels.
Bit | Music |
---|---|
0 | Overworld |
1 | Underwater |
2 | Underground |
3 | Castle |
4 | Clouds/Bonus |
5 | Overworld Pipe Intro |
6 | Super Star |
7 | None |
Bit | Music |
---|---|
0 | Death |
1 | Game Over |
2 | Normal Victory |
3 | Castle Victory |
4 | Alternate Game Over |
5 | Level Ending |
6 | Hurry Up |
7 | None |
Bit | SFX |
---|---|
0 | Block Break |
1 | Flame |
2 | Wind (SMB2) |
3 | Undefined |
4 | Undefined |
5 | Undefined |
6 | Undefined |
7 | Skid (SMB2/ANN) |
Bit | SFX |
---|---|
0 | Coin |
1 | Powerup Spawn |
2 | Vine Spawn |
3 | Blast |
4 | Tick |
5 | Powerup |
6 | One Up |
7 | Bowser Fall |
Bit | SFX |
---|---|
0 | Jump (Big) |
1 | Bump |
2 | Stomp |
3 | Smack |
4 | Pipe Entry |
5 | Projectile |
6 | Pole Slide |
7 | Jump (Small) |
Music
Music bit-fields are checked for set bits from right to left, with the first such set bit triggering that music to play, with event music taking precedence over general music. For example, if 0xFB is 0x3B and 0xFC is 0x12, then the game over music will play since 0xFC is handled before 0xFB and the right-most set bit in 0xFC is the game over bit.
Structures
Each music offset is then the offset from the start of the music table to the descriptor for that particular piece of music. This table contains 16 entries, 8 for the event music followed by 8 for the general music.
A music entry then consists of an offset to the note length structure applicable to the track data, a pointer to the track data itself, and fields indicating where in the track data the pulse 1, triangle, noise, and, in the case of FDS, wavetable portions of the track start in the data. The pulse 2 portion is always the first, so an offset to pulse 2 is not stored.
The length structures are a series of 8 possible tone lengths, with multiple such lists provided for different timing needs. There are several groups and different tracks may use different sets of lengths. The entries correspond with timing concepts such as whole notes, half notes, quarter notes, and so on.
The notes themselves are indices into a table of timing values to supply to the timing registers of the given oscillators in the case of tonal sound and specific sound IDs in the case of the noise channel. The table of timing values is generally in chromatic order but does not include all possible notes.
Formats
The track data itself is encoded in one of two formats depending on the channel:
Pulse 2, triangle, and wave-table data are stored with one length indicator followed by multiple notes to be played at that length, then the next length indicator, and so on, until the end of that channel in the track which is indicated by a value of 0x00 in the case of the pulse 2 channel, with the triangle and wave-table channels expected to fill the same time as the pulse 2 channel. To indicate that a byte is a length indicator, its high bit is set, such that values 0x80-0x87 represent the 8 possible length values for a following series of notes. This format will be referred to as the "uncompressed" format.
Pulse 1 and the noise channel are instead stored in a compressed format in which the length of a note as well as its ID are encoded in the same byte in the format "BCNNNNNA" where "00NNNNN0" is the identity of a given note and "ABC" is the length number for that note. A value of 0x00 may also be given for specifically the noise channel to indicate the end of that channel. As of this analysis, the behavior when pulse 2 and noise disagree on track ending has not been analyzed. The end result is every note has a length included in pulse 1 and noise formats, but a length is established for a run of notes for the other channels. For instance, to request that a note of ID 0x2C be played for length 5, the resulting byte would be 0x6D. This format will be referred to as the "compressed" format.
Processing
Each frame, the uncompressed tracks are read for a single byte. If that byte is a length byte (indicated by the high bit set), the new length is saved and the next byte retrieved as a note. As for the compressed data, a byte is read every frame and decomposed into the length and note encoded therein. In either case, the length byte is then counted down each frame until it reaches zero, at which point a new read cycle happens.
Some channels will additionally check first to determine if a sound effect is already being played on a given channel. If so, that frame of the channel will be read out but not sent to the APU as to keep the tracks in sync while not interfering with sound effects.
FDS Secondary Engine
It is important to note that in the updated engine used in Super Mario Bros. 2 as well as All Night Nippon, a secondary sound engine is loaded in for the game ending sequence. This sound engine bears much similarity to the standard one, but has added provisions for handling FDS wave-table audio as well as trims out some specific checks that only applied to music being played at gameplay time. The original sound engine is still resident in memory when this secondary one is loaded in, and may even be used, so it does feature some checks to, for instance, determine if the conventional sound engine is playing a sound effect on a channel and to mute the music on that channel for that frame if possible. This also means that the memory used by the secondary sound engine, while comparable in function, is stored elsewhere. This encompasses, for instance, the pointers to the current track data and note lengths.
Internal Data for Super Mario Bros.
| |
---|---|