comprehensive SMB1 disassembly
Moderator: Moderators
-
doppelganger
- Posts: 183
- Joined: Tue Apr 05, 2005 7:30 pm
Yes. It effectively shuffles around in memory which sprites get used for enemy, fireball, air bubble, block and misc objects so that one sprite object never stays in either a low or high-numbered sprite set for longer than a frame. The sprite offsets get shuffled every NMI (long, long after the DMA of sprite data to OAM), and all the sprite Y positions in the sprite data (except sprite #0) are moved offscreen by default. The sprite data is written to $0200-$02ff, and only the sprites actually being used are assigned onscreen Y positions. Then the sprite data is used on the next frame upon NMI.
The only sprites that do not get shuffled are the sprites used by the player (right after sprite #0), and sprite #0. I think the reasons are obvious.
I believe the whole thing about lower-numbered sprites overlapping higher-numbered ones is merely symptomatic of the rendering order of the sprite evaluation mid-scanline, and this may play some part in which sprites appear on the screen with the 8 sprites per scanline limitation. My knowledge of the NES PPU is not perfect, however.
The only sprites that do not get shuffled are the sprites used by the player (right after sprite #0), and sprite #0. I think the reasons are obvious.
I believe the whole thing about lower-numbered sprites overlapping higher-numbered ones is merely symptomatic of the rendering order of the sprite evaluation mid-scanline, and this may play some part in which sprites appear on the screen with the 8 sprites per scanline limitation. My knowledge of the NES PPU is not perfect, however.
Be whatever the situation demands.
Hmm, just as a let you know, I am trying to do the same thing you did for SMB2J. With your permission, I'm more or less copy-and-pasting a lot of your disassembly into this one, as I am noticing (especially with regards to direct interaction with the PPU and the joypads) a lot of the code is more or less the same (the UpdateScreen function for example I notice is exactly the same). I'm also stealing your label names for cross-compatibility as the two games are so similar. :p
I hope that's OK.
Now, this game has different "files" on the disk, and when it gets to certain points (World 5, the Princess/World 9, and World A), it loads a new file over on top of some of the old one (because there is not enough room for all of the data). I'm not sure if some of the functions are used multiple times yet (I'm just working on the "SM2MAIN " file for right now), but there are some interesting things. The "JumpEngine" is there, and they've got an interesting function for loading files, which I was spending time working up:
This is all FDS-specific, but I think I am starting to make progress on it. ^_^
I hope that's OK.
Now, this game has different "files" on the disk, and when it gets to certain points (World 5, the Princess/World 9, and World A), it loads a new file over on top of some of the old one (because there is not enough room for all of the data). I'm not sure if some of the functions are used multiple times yet (I'm just working on the "SM2MAIN " file for right now), but there are some interesting things. The "JumpEngine" is there, and they've got an interesting function for loading files, which I was spending time working up:
Code: Select all
;starting at MEM address $C0CA
ChkNumFiles:
tya
ldy $07F7 ;current loadlist to use
cmp NumFiles,Y
rts
VerificationData:
.byte $01
.byte $53, $4D, $42, $20 ;"SMB "
.byte $00, $00, $00, $00, $00
LoadList_Low:
.byte <LoadList1, <LoadList2, <LoadList3, <LoadList4
LoadList_High:
.byte >LoadList1, >LoadList2, >LoadList3, >LoadList4
LoadList1:
.byte $01, $05, $0F ;char data, main program (this and worlds 1-4), save
.byte $FF ;terminator
LoadList2:
.byte $20 ;program part 2 (worlds 5-8)
.byte $FF
LoadList3:
.byte $10, $30, $0F ;princess char data, data 3 (princess and world 9), save
.byte $FF
LoadList4:
.byte $40 ;data 4 (worlds a-d)
.byte $FF
NumFiles:
.byte $03 ;loadlist1
.byte $01 ;loadlist2
.byte $03 ;loadlist3
.byte $01 ;loadlist4
LoadFiles:
ldx $07F7 ;current loadlist to use
lda LoadList_Low,X
sta LoadList
lda LoadList_High,X
sta LoadList+1
jsr $E1F8 ;FDS_LoadFiles
DiskID:
.addr VerificationData
LoadList:
.addr LoadList1 ;rw (this gets written to)
rts
-
doppelganger
- Posts: 183
- Joined: Tue Apr 05, 2005 7:30 pm
doppelganger,
It would seem to make the most sense. Perhaps to avoid copyright issues, I should make people have to get a hold of the CHR data themselves.
Other than that, it seems like it's a pretty long task. ^_^ How long did it take you again?
EDIT: Though the RAM addresses are almost completely the same, there are some differences:
They got rid of the second player's score display and moved the GameTimerDisplay up, and that made room for the current load list and the A-D check.
The UpdateTopScore function was slightly changed due to this:
And here are the offsets, with World 9 added:
It would seem to make the most sense. Perhaps to avoid copyright issues, I should make people have to get a hold of the CHR data themselves.
Other than that, it seems like it's a pretty long task. ^_^ How long did it take you again?
EDIT: Though the RAM addresses are almost completely the same, there are some differences:
Code: Select all
DisplayDigits = $07d7
TopScoreDisplay = $07d7
ScoreAndCoinDisplay = $07dd
PlayerScoreDisplay = $07dd
GameTimerDisplay = $07ec
DigitModifier = $0134
FDS_WaitCycles = $077b ; currently waiting for IRQ
CurrentLoadList = $07f7 ; current files to load
World9Check = $07fa ; if set to #$ff, then meets qualifications for world 9
LetterWorld = $07fb ; currently in worlds a-d if set
The UpdateTopScore function was slightly changed due to this:
Code: Select all
UpdateTopScore:
ldx #$05
ldy #$05
sec
GetScoreDiff:
lda PlayerScoreDisplay,X
sbc TopScoreDisplay,Y
dex
dey
bpl GetScoreDiff
bcc NoTopSc
inx
iny
CopyScore:
lda PlayerScoreDisplay,X
sta TopScoreDisplay,Y
inx
iny
cpy #$06
bcc CopyScore
NoTopSc:
rts
Code: Select all
WorldAddrOffsets:
.byte World1Areas-AreaAddrOffsets, World2Areas-AreaAddrOffsets
.byte World3Areas-AreaAddrOffsets, World4Areas-AreaAddrOffsets
.byte World5Areas-AreaAddrOffsets, World6Areas-AreaAddrOffsets
.byte World7Areas-AreaAddrOffsets, World8Areas-AreaAddrOffsets
.byte World9Areas-AreaAddrOffsets
; see "SM2DATA4" for worlds A-D
AreaAddrOffsets:
World1Areas:
.byte $20, $29, $40, $21, $60
World2Areas:
.byte $22, $23, $24, $61
World3Areas:
.byte $25, $29, $00, $26, $62
World4Areas:
.byte $27, $28, $2A, $63
World5Areas:
.byte $2B, $29, $43, $2C, $64
World6Areas:
.byte $2D, $29, $01, $2E, $65
World7Areas:
.byte $2F, $30, $31, $66
World8Areas:
.byte $32, $35, $36, $67
World9Areas:
.byte $38, $06, $68, $07
-
doppelganger
- Posts: 183
- Joined: Tue Apr 05, 2005 7:30 pm
Re: the CHR data, that's what I did. But then again, it was all neatly tucked away in a CHR-ROM. You would probably need to make notes on where CHR data was stored depending on how haphazardly it's stored on the disk (or disk image).beneficii wrote:doppelganger,
It would seem to make the most sense. Perhaps to avoid copyright issues, I should make people have to get a hold of the CHR data themselves.
Other than that, it seems like it's a pretty long task. ^_^ How long did it take you again? :D
EDIT: Though the RAM addresses are almost completely the same, there are some differences:
Umm, removed the code here so it wouldn't clog the forum unnecessarily.
Re: how long it took me, I'd say it took me about four months to do the actual reverse-engineering, then a few weeks for the clean-up phase (I tend to work slowly). But what you have to bear in mind is that I did most of the work on SMB on my own (level data format, I had some help with, was able to verify the work others did on it).
It was pretty clever of them to use the extra space where player 2's score would have been for that. Perhaps the data at $0761-$0767 also got used for a different purpose? Perhaps not, but that's something to speculate on. Anyway it looks like your efforts are coming along nicely. :-)
Be whatever the situation demands.
loopy,
Interesting. One thing I noticed though, is that starting with World 4, the right-side up Pirhana plants did not change to red. In fact only the upside down Pirhana plants did. Did you take it out?
Still, very useful to distinguish between what's code and not. What was your methodology for the conversion?
Interesting. One thing I noticed though, is that starting with World 4, the right-side up Pirhana plants did not change to red. In fact only the upside down Pirhana plants did. Did you take it out?
Still, very useful to distinguish between what's code and not. What was your methodology for the conversion?
Re: CHR data, I've already mapped it out. It wasn't scattered all over the disk: Just stored in two files "SM2CHAR1" and "SM2CHAR2". The first file is from the range $0000-$1fff and is the full file more or less; the second file is just from the range $0760-$079f and contains the data for the Princess, replacing the door to the Princess's room data in the first file.doppelganger wrote:Re: the CHR data, that's what I did. But then again, it was all neatly tucked away in a CHR-ROM. You would probably need to make notes on where CHR data was stored depending on how haphazardly it's stored on the disk (or disk image).beneficii wrote:doppelganger,
It would seem to make the most sense. Perhaps to avoid copyright issues, I should make people have to get a hold of the CHR data themselves.
Other than that, it seems like it's a pretty long task. ^_^ How long did it take you again?
EDIT: Though the RAM addresses are almost completely the same, there are some differences:
Umm, removed the code here so it wouldn't clog the forum unnecessarily.
Re: how long it took me, I'd say it took me about four months to do the actual reverse-engineering, then a few weeks for the clean-up phase (I tend to work slowly). But what you have to bear in mind is that I did most of the work on SMB on my own (level data format, I had some help with, was able to verify the work others did on it).
It was pretty clever of them to use the extra space where player 2's score would have been for that. Perhaps the data at $0761-$0767 also got used for a different purpose? Perhaps not, but that's something to speculate on. Anyway it looks like your efforts are coming along nicely.
If you don't mind me using your stuff and loopy doesn't mind me using his stuff as bases for this, then perhaps it could be done a bit quicker. Right now, I'm just going a bit here and a bit there, as I haven't really come up with a good system (and probably won't) for putting this all on. I checked the "bankx" asm files in Loopy's NES implementation and they seem to resemble for each of the files the whole $C000-$DFFF range in the PRG-RAM. Part of this project is tracing out things and seeing how they work, then marking it in a memory map to see how it's done, so I can change it later; another part is reading through doppelganger's SMB1 disassembly and trying to find similar pieces of code in this game. Maybe by June-ish, I will get it done?
Regarding player 2 data, as far as I know, the space (in RAM) from $0761-$0767 is not used in anyway in SMB2J.
(Sorry for the double post, for some reason edit button isn't working.)
Here is the code I used to "break apart" and analyze the FDS file so to speak:
http://nesdev.com/bbs/viewtopic.php?t=3203
Here is the code I used to "break apart" and analyze the FDS file so to speak:
http://nesdev.com/bbs/viewtopic.php?t=3203
-
daniel3843
- Posts: 5
- Joined: Sun May 14, 2006 10:39 pm
-
daniel3843
- Posts: 5
- Joined: Sun May 14, 2006 10:39 pm
OK, regardless, I fixed the red piranha plant issue. And sigh, since there seems to be no easy way to upload this to the server, you can get it here. Extract it to a folder and run make.bat:
http://www.geocities.com/beneficii/smb2jsrc.zip
It was a bit to fix, though I only had to change 6000.asm. Basically, the original game would write to the PRG-RAM in a weird way. It wrote to 2 places:
B517
and
9FFE
B517 is part of a table that gets loaded (though the game checks that value specially) and 9FFE is part of the instruction at 9FFD: CMP #$21 (which gets to CMP #$13 when it's time to do the red plants). Basically, I created 2 new variables at $51 and $50: oldb517 and old9ffe (which can be found declared at the top of the 6000.asm file). I just did a whole bunch of redirecting to those 2 variables. It appears that in the normal course of the game $50 and $51 don't really get written to, and I've tested it through, but I could still get surprised. So the purpose is to make it easily modifiable. It needs to be somewhere on the zeropage for space reasons.
http://www.geocities.com/beneficii/smb2jsrc.zip
It was a bit to fix, though I only had to change 6000.asm. Basically, the original game would write to the PRG-RAM in a weird way. It wrote to 2 places:
B517
and
9FFE
B517 is part of a table that gets loaded (though the game checks that value specially) and 9FFE is part of the instruction at 9FFD: CMP #$21 (which gets to CMP #$13 when it's time to do the red plants). Basically, I created 2 new variables at $51 and $50: oldb517 and old9ffe (which can be found declared at the top of the 6000.asm file). I just did a whole bunch of redirecting to those 2 variables. It appears that in the normal course of the game $50 and $51 don't really get written to, and I've tested it through, but I could still get surprised. So the purpose is to make it easily modifiable. It needs to be somewhere on the zeropage for space reasons.