I've been looking into Super Mario Bros. 3 a good bit lately, and I've found out some things about it. I submitted a fair number of new addresses to the ROM and RAM maps (though some of the ones I submitted were there previously) and have learned at least enough about to put together a custom level in the game
, if not a particularly noteworthy one. I'm sure there are more addresses that I could submit and haven't, either due to uncertainty or laziness.
Beyond that...well, my notes are kind of disorganized and incomplete at this point, but I've figured out some of the Map16 tiles
(only for tileset 0 and page 0 so far, though), as well as some overworld tiles and objects (also sprites, though Insectduel already has a list of those). These are the overworld tiles I've figured out so far:
03: Level tile 1
04: Level tile 2
05: Level tile 3
06: Level tile 4
07: Level tile 5
08: Level tile 6
09: Level tile 7
0A: Level tile 8
0B: Level tile 9
44: Path, dot only
47: Path, horizontal
48: Path, vertical
4A: Path, bottom-right corner
50: Mushroom house
5F: Spiral tower
AE: Path, horizontal water
AF: Path, bottom-right corner water
B5: Path, vertical water
BB: Palm tree
E0: Mushroom house, alternate palette
E8: N-Spade game
Objects seem to be handled around $209AA6, with a pointer to the data at [$2B]. Usually, the first byte goes into $0E, the second goes into $0F, and the third goes into $0706, but some objects can be 4 or even 5 bytes (and from what I've seen, there's no real way to tell without looking at their code). There are four types of objects:
Normal object - aaabcccc ddddeeee ffffgggg, aaa < 110, ffff != 0:
- cccceeee -> $0700, 000dddd0 -> X ($0700 used to index [$2E],y later?)
- if the level is horizontal, X indexes a 16-bit table of Map16 pointers at $218200 (goes into $2E/$2F and #$7E goes into $30)
- if the level is vertical, X/2 indexes two 8-bit tables at $21823C and $21824C (goes into $2E/$2F)
- if b = 1, increment $2F
- pointers at $209C40, indexed by tileset number ($070A)
- object number: aaa * #$0F + ffff - 1
Extended object - aaabcccc ddddeeee ffffgggg, aaa < 110, ffff = 0:
- extended object number: aaa > 1 | $0706 (or aaa > 1 | gggg, or -aaagggg)
- shares most of the setup code with normal objects, except that the pointers are at $209C80 instead
GFX changer? - 110-aaaa bbbbcccc dddddddd:
- if aaaa = high byte of Layer 1 X position, bbbb -> $073C, cccc -> $073B, dddddddd -> $070A
Screen exit - 111-aaaa bbbbbbbb cccccccc:
- aaaa -> Y, bbbbbbbb -> $1F56,y, cccccccc -> $1F66,y
Yes, this game also does the thing where it uses completely separate subroutines for objects depending on tileset number, and it even does it for extended objects. It's worth noting that aside from tilesets 0F and higher, all of said routines are exactly the same. Multiplying by #$0F instead of #$10 is also a pain, since it makes it a lot harder to tell what object number is being used based on the bits that make it up. I suppose that's why we have macros, but if I ever do any serious hacking of this game, I'll almost certainly rewrite the object handling routine into something more sensible. I'm not sure what exactly the GFX change object does or where it's used. It's also worth noting that bit 4 of the first byte is completely unused in both special object types, so that could be used as a custom flag or something.
The level header, parsed starting at $20990B, uses 13 bytes in the format oo oo oo ss ss ss yyy-llll uxxcccpp Pssvtttt eeeggggg TTGGmmmm qqqqiiii bbbbbbbb.
oo oo oo ss ss ss yyy-llll uxxcccpp Pssvtttt eeeggggg TTGGmmmm qqqqiiii bbbbbbbb
oooooo = 24-bit pointer to object data for secondary room (stored to $1DFE-$1E00)
ssssss = 24-bit pointer to sprite data for secondary room (stored to $1E01-$1E03)
yyy = index to player starting Y position (used to index three tables at $21CE2A, $21CE32, and $21CE3A, which get stored to $55, $70, and $0216 respectively)
- = unused bit (never stored to anything or referenced)
llll = level length (stored to $22 and $0376)
u = unused bit (stored to $0562 unshifted, but never referenced)
xx = index to player starting X position (stored to $0426)
ccc = background color index (stored to $073C)
pp = foreground palette index (stored to $073B)
P = pipe end flag (if 0, entering a pipe will cause the level to end; stored to $0379 unshifted)
ss = vertical scroll type (0 = none unless flying or P-running, 1 = free, 2 or 3 = none; stored to $0424)
v = vertical level flag (stored to $0425)
tttt = tileset of secondary room (stored to $1EBA)
eee = entrance type (stored to $0545)
GGggggg = graphics set (seems to include all three background layers and sprites; stored to $1EBF)
TT = timer index (used to index a table at $21CE42, which gets stored to $05EE; also sets $05F3 if the timer is 000)
mmmm = music (used to index a table at $21CE46, which gets stored to $1061 and $1062 unless the index is #$07*)
qqqq = Layer 3 palette index (stored to $02BE)
iiii = Layer 3 image index (stored to $02BF)
bbbbbbbb = Layer 2 background index (stored to $0350)
*If the music index is #$07, $0727 and $1E are both 0, and the value of $7E3979,x where X = $0726 is #$A0, then it will use music value #$04; otherwise, it will use the normal value.
RAM address $070A seems to be the tileset. Though I obviously haven't checked every level in the game, I have observed the following values:
00 - overworld
01 - wooden grassland (1-1, 1-3, 3-3, 3-7, 3-9, 7-5, 7-8)
02 - fortress (1-Fortress, king's castle, 3-Fortress 1, 3-Fortress 2, 7-Fortress)
03 - athletic grassland (1-2, 1-Hammer Bro, 2-2, 2-Sand, 2-4, 2-5, 3-4, 3-8, 2-Pyramid, 7-3)
04 - athletic (1-4, 1-6, 3-2, 3-6, 7-7)
05 - Piranha Plant (7-Piranha Plant)
06 - underwater (3-1, 3-5, 7-4)
07 - mushroom house
08 - pipe (7-1, 7-6)
09 - desert (2-1, 2-Fortress, 2-Boomerang Bro, 7-2, 7-9)
0A - airship
0B - giant (4-1, 4-2, 4-4, 4-5)
0C - ice (6-1, 6-2, 6-3)
0D - sky (5-4)
0E - underground (1-5)
0F - "line up pictures" bonus
10 - ?
11 - ?
12 - ?
There is also an object set, which is a hidden value different from the tileset. Just like how some of the tilesets in SMW actually use the same objects as others (such as Switch Palace 1 and Ghost House 1), the same is true for SMB3, so while there are 19 different pointers to object routines, there are only 11 unique ones, which are used by different tilesets. I haven't written down every pointer for every individual object yet, but I'm getting there.
Object set Normal pointer Extended pointer Tileset indexes
0 $29C1EC $29C1EC 0
1 $24893D $2489EA 1
2 $268891 $26895F 2
3 $24ADE3 $24AEF3 3, E
4 $24E695 $24E75A 4, C
5 $25B06A $25B126 5, B, D
6 $25892F $2589FD 6, 7, 8
7 $25D540 $25D617 9
8 $26B045 $26B113 A
9 $239135 $23914A F, 10, 11
10 $23A569 $23A5E8 12
I've also found some VRAM handling stuff at $20AC81. Here's how it seems to be loaded from my observation. The unknown ones are presumably either tilemaps or handled by another routine.
$2000-$23FF: use pointer at $20A5A1/$20A601 indexed by $1EBF
$2400-$27FF: included in pointer at $20A5A1/$20A601 indexed by $1EBF unless $1EBF = #$0F and player 2 is active, in which case use data at $328000
$2800-$2FFF: use pointer at $20A631/$20A691 indexed by $1EBF unless the P-Switch is set, in which case use data at $33F000
$3000-$33FF: use pointer at $20A6C1/$20A721 indexed by $1EBF
$3400-$37FF: use pointer at $20A751 (bbhh00) indexed by the Layer 2 background unless the Layer 3 image is #$06, in which case use data at $3AC000
$3800-$3BFF: use pointer at $20A79D (bbhh00) indexed by the Layer 2 background unless the Layer 3 image is #$06, in which case use data at $3AC800
$3C00-$3FFF: use pointer at $20A7E9/$20A849 indexed by $1EBF
$4000-$43FF: use pointer at $20A879/$20A8D9 indexed by $1EBF
$4400-$47FF: use data at $32B800
$5800-$5FFF: use data at either $38A000, $38A400, $38A800, or $38AC00 depending on $02BF*
$6000-$7FFF: if $1EBF = #$16, use data at $398000 for this entire area; else, split it up
$6000-$63FF: use data at $3FB000
$6400-$67FF: use pointer at $20A909/$20A969 indexed by $1EBF
$6800-$6BFF: use pointer at $20A999/$20A9F9 indexed by $1EBF
$6C00-$6FFF: use pointer at $20AA29/$20AA89 indexed by $1EBF
$7000-$73FF: use pointer at $20AAB9/$20AB19 indexed by $1EBF
$7400-$77FF: use pointer at $20AB49/$20ABA9 indexed by $1EBF unless $1EBF = #$0A, in which case use pointer at $20AC69/$20AC71 indexed by $0727
$7800-$7BFF: use pointer at $20ABD9/$20AC39 indexed by $1EBF
$7C00-$7FFF: use data at $3FB800
*Values 02, 05, 07, 08, 0C, and 0E use $38A000; 09, 0B, and 0F use $38A400; 0A uses $38A800; 03 and 04 use $38AC00; and 00, 01, 06, and 0D skip this write.
Sprites are much
simpler than objects. As far as I can see, they're just 3 bytes each, with the first byte being the sprite number, the second its X position in the level (in terms of 16x16 blocks), and the third its Y position. If the first byte is FF, it marks the end of the sprite data. They seem to be processed around $299AE0. Sprites 00-B3 are normal sprites, B4-BB are generators, BC-D0 seem to be shooters (but they're handled oddly; all of their setup code is at $299D2D, and the only difference is what value gets stored to $1A15*), and D1-D6 are run-once sprites, several of which basically do one small thing and that's it.
*No, not even $1A15,x or $1A15,y. Apparently when loading a new shooter, the game moves all the data for the other shooters up 1 slot and then just stores everything to slot 0. Why they did it that way is anyone's guess, given that it's both confusing and inefficient. Shooter tables seem to be 8 bytes each and include $1A15, $1A1D, $1A25, $1A2D, $1A35, $1A3D, $1A57, $06DB, and $06E3, though I couldn't tell you what any of them do (other than $1A15 obviously being the shooter number); I haven't looked into shooters much. Interestingly enough, the Rocky Wrench actually counts as a shooter.
Other small things I've found out:
- There seems to be some palette setup stuff at $29C82B, but I couldn't summarize it concisely.
- $20F20B and $20FBC5 appear to be subroutines for transferring register mirrors.
- There are a lot of sprite subroutines and such in bank 27, though again, it's hard to sum them up and I don't know what a lot of them do.
- On that note, there's a third table of sprite pointers at $288438 after the init and main ones, and I have no idea what it does. It doesn't help that some sprites don't even seem to use it as a pointer.
I'm working on a hack! Check it out here