News: 11 March 2016 - Forum Rules

Author Topic: Super Mario Bros 2 USA hacking questions?  (Read 1926 times)

tygerbug

  • Full Member
  • ***
  • Posts: 101
    • View Profile
Super Mario Bros 2 USA hacking questions?
« on: April 11, 2022, 06:31:40 am »
http://datacrystal.romhacking.net/wiki/Super_Mario_Bros._2:ROM_map
0x01F2F0 to 0x01F30F (000020) = Tiles to use for Character animation Tiles
0x01427F to 0x------ = Bonus Game graphic placement


WORK IN PROGRESS:
https://drive.google.com/file/d/1WmnktVdIPMQMqQ90Th4Erl8OYzfMINov/view?usp=sharing


I'm doing a hack of Super Mario Bros 2 USA right now.

There's a lot of info out there about the game, but I have some specific questions.

---

A few things about the white eye highlights. This is an additional (animated!) sprite placed over the player's face in most situations.

For some reason, Mario's eye highlight sprite is mirrored in two of its eight animation frames. I'm not sure why.

Characters have "large" and "small" versions as in Super Mario Bros. I'd like the "small" versions to be taller than in SMB2.

So how and where can I change the placement of the "eye highlight" sprite to be higher when the character is in "small mode?"

The Toad character does not have an eye highlight sprite. Is it possible to change the construction so that Toad does have an eye highlight sprite, and Luigi does not? Alternately, can we swap the way that Toad and Luigi control and play?

---

My "Mario and Toad" characters are taller than in the regular SMB2, as I wanted to use as much sprite space as possible. In the original, the characters have varying heights, and Mario and Toad are shorter. The "small" versions of all characters are also taller than usual.

This gets odd when a character picks up an item and carries it over its head.

So, how and where can I change the height of a carried item so that the carried item is carried higher?

---

I have other, simpler questions. It's stuff I may be able to figure out myself, but I haven't yet, so I'll ask a few things anyway.

I think Birdo uses the same palette as generic enemies (and a few different palettes, at that). Where are these palettes? I'd like Birdo to be blue (not pink) throughout, preferably without affecting other characters. I will also be changing other boss palettes.

Where is the construction for the title screen and bonus chance screens?

Is it possible to extend the space for text in the title screen "Story" intro? No worries if not.
« Last Edit: April 19, 2022, 06:25:51 pm by tygerbug »

Bavi_H

  • Jr. Member
  • **
  • Posts: 35
    • View Profile
    • Robert Hart's Homepage
Re: Super Mario Bros 2 USA hacking questions?
« Reply #1 on: April 11, 2022, 10:25:16 am »
I don't have any answers, but here is a possible suggestion and a thought:

If you are familiar with using an NES emulator debugger and how to convert CPU addresses into NES file offsets, I found this Super Mario Bros. 2 disassembly very helpful. When you compile the code, the assembler also combines all the source files into one master listing with CPU addresses added in front of every line of code.

Have you considered which version you want to use, US Revision 0 or US Revision 1? Some ROM locations might have changed between the versions. The Cutting Room Floor article Super Mario Bros. 2 section "Revisional Differences" explains the gameplay differences. I would typically use US Revision 1 since it fixes a bug in the game. But if you ever want to use controller movies to automatically play the game, be aware that tool assisted speedruns seem to prefer US Revision 0, perhaps because it's one frame faster on the BONUS CHANCE screens.


April 11, 2022, 01:10:34 pm - (Auto Merged - Double Posts are not allowed before 7 days.)

Where is the construction for the title screen and bonus chance screens?

I looked at the disassembly and found (in both Revision 0 and Revision 1):
The title screen layout data starts at NES file offset hex 162F.
The Bonus Chance screen layout data starts at NES file offset hex 14150.

(The format of the layout data is described here: "Basically, a buffer is like this:")


Steps I used to find these offsets from the disassembly:


1. In the root folder, the file smb2.asm is the main overview and includes all the other files. It has comments that give an overview of what is in each bank pair (the game always uses banks in pairs). So look here first for an idea where to go next.
  • Looking over the comments, I saw they say the title screen is in Bank 0 & 1, and the Bonus Chance screen is in Bank A & B.

2. In the src folder, there are files for each bank pair. For example, Bank 0 & 1 is in prg-0-1.asm and Bank A & B is in prg-a-b.asm.

3. After you follow the instructions to build the code for Revison 0 or Revision 1, then in the bin folder, the assembler will create the master listing file smb2.lst. (I built both Revison 0 and Revison 1 and renamed the smb2.lst file created each time with a unique name so I could look at both versions.) Once you have the smb2.lst file built, you can find the matching code in the smb2.lst file by searching for matching text like "TitleLayout:" or "BonusChanceLayout:". Make a note of the CPU address shown at the beginning of the line.
  • In these cases, both the Revison 0 and Revison 1 listings had the same addresses:
    TitleLayout: CPU address 961F
    BonusChanceLayout: CPU address 8140

4. Once you know the bank and the CPU address, you can convert it into an NES file offset. Here's a little table based on notes I had previously made:

Code: [Select]
bank: the bank pair
CPU: the first CPU address used by this bank pair
NES: the first NES file offset used by this bank pair

bank  CPU   NES
 0&1  8000  00010
 2&3  8000  04010
 4&5  8000  08010
 6&7  8000  0C010
 8&9  8000  10010
 A&B  8000  14010
 C&D  8000  18010
 E&F  C000  1C010
  • "TitleLayout:" in Bank 0 & 1 is CPU address 961F. To convert to an NES file offset:
    Subtract the first CPU address for bank 0&1, 8000, to get 161F.
    Add the first NES file offset for bank 0&1, 10, to get 162F.

    "BonusChanceLayout:" in Bank A & B is CPU address 8140. To convert to an NES file offset:
    Subtract the first CPU address for bank A&B, 8000, to get 140.
    Add the first NES file offset for bank A&B, 14010, to get 14150.


April 11, 2022, 10:18:03 pm - (Auto Merged - Double Posts are not allowed before 7 days.)

how and where can I change the placement of the "eye highlight" sprite to be higher when the character is in "small mode?"

I investigated (see my process below) and found the following:

In either revision, changing the value at NES file offset 1F38B from 08 to a smaller number will move the small characters higher up the screen.

I was investigating starting from the eye sprite and tracing where it gets its Y screen position set. I found code that checks if the character is small, and if so, it adds 8 to the Y screen position. When I changed the 8 to a smaller number, not only did the eyes move higher, the entire character moved higher! For example when I changed it from 8 to 4, the small characters now hover 4 pixels off of the ground. Maybe that will help you?

Code: [Select]
Using the emulator Mesen, I started the game with Mario, let him fall down to land on the first platform, then paused the emulator.

In PPU Viewer, Sprite Viewer page, I pointed to Mario's eyes and see it is Tile Index D5.

(I unpaused and repaused several times to look at the sprite at various points in time. I noticed the Sprite Index changes, but the Tile Index is always D5.)

I believe NES games often use CPU address 200 to 2FF (part of the NES console's RAM at CPU addresses 0 to 7FF) as a buffer that gets copied to sprite memory (OAM memory) every frame.

In the disassembly, I peeked in the file ram.asm and a comment at the top confirms Super Mario Bros. 2 does this too: "0200-02FF: Sprite OAM DMA area".

I visited NesDev wiki and found the PPU OAM page ( https://www.nesdev.org/wiki/PPU_OAM ) to refresh myself with the sprite memory contents. (The sprite data is in groups of 4 bytes: Y position, tile index, attributes, X position.)

In the debugger, I set a write breakpoint to catch writes to CPU addresses $200 to $2FF with a value of $D5.
(Here's a screenshot of a write breakpoint with different values to give you an idea: https://rnhart.net/dizzy-breakpoint-mesen.png
Imagine the screenshot is for type CPU Memory, range $200 to $2FF, and condition "value == $D5".)

The debugger stopped at CPU address F408. I looked this address up in the listing file.

(I know that CPU address C000 to FFFF are always from bank pair E & F, and bank pair E & F is always mapped to CPU address C000 to FFFF, so CPU address F408 will only appear once in the listing. So I can just search for "0F408" in the left column of the listing file.)

F408  STA SpriteDMAArea + 1, Y  ; Mem[SpriteDMAArea + Y + 1] = A

I looked above that spot in the code and noticed the following.

F403  LDX CurrentCharacter      ; X = Mem[CurrentCharacter]
F405  LDA CharacterEyeTiles, X  ; A = Mem[CharacterEyeTiles + X]
F408  STA SpriteDMAArea + 1, Y  ; Mem[SpriteDMAArea + Y + 1] = A

The code appears to be in a routine that starts at F31A called "RenderPlayer:"

I found the location of "CharacterEyeTiles":

F2E0  ; Specific to each character
F2E0  CharacterEyeTiles:
F2E0  .db $D5 ; Mario
F2E1  .db $D9 ; Luigi
F2E2  .db $FB ; Toad
F2E3  .db $D7 ; Princess

I also noticed similar data above it:

F2D5  ; Tiles to use for eye sprite. If $00, this will use the character-specific table
F2D5  CharacterFrameEyeTiles:
F2D5  .db $00 ; Walk1
F2D6  .db $00 ; Carry1
F2D7  .db $00 ; Walk2
F2D8  .db $00 ; Carry2
F2D9  .db $FB ; Duck
F2DA  .db $FB ; DuckCarry
F2DB  .db $00 ; Jump
F2DC  .db $FB ; Death
F2DD  .db $FB ; Lift
F2DE  .db $00 ; Throw
F2DF  .db $FB ; Climb


Looking just a tiny bit above the previous code I was looking at, I noticed it is checking this "character-specific" use...

F3FE  LDA CharacterFrameEyeTiles, X  ; A = Mem[CharacterFrameEyeTiles + X]
F401  BNE $F408                      ; If A is not zero, skip over the next two instructions (Goto F408)
F403  LDX CurrentCharacter           ;     X = Mem[CurrentCharacter]
F405  LDA CharacterEyeTiles, X       ;     A = Mem[CharacterEyeTiles + X]
F408  STA SpriteDMAArea + 1, Y       ; Mem[SpriteDMAArea + Y + 1] = A


Where does the Y register get loaded with the value it currently has?
In the debugger I used the "step back" button many times carefully paying attention to the instruction to see if it was storing something in Y. I found Y was loaded at this point:

FB25  TAY  ; Y = A

I found the same point in the listing. I didn't analyze the code in detail, but the comments in this area suggest the code is finding an "sprite slot", so I'm just guessing that Y gets set with some kind of "sprite slot" value.

Ok, that sounds good.

Based on the sprite memory contents (the sprite data is in groups of 4 bytes: Y position, tile index, attributes, X position), I expect something will load Mem[SpriteDMAArea + Y] with the y position of the eyes somewhere.

In the debugger, I click the "step into" button many times and follow along with the same instructions in the listing. I find this spot:

F39B LDA byte_RAM_0        ; A = Mem[0]
F39D STA SpriteDMAArea, Y  ; Mem[SpriteDMAArea + Y] = A

Ok, now where does Mem[0] get its value loaded?

I look backward in the listing (I don't bother with the debugger this time). I find this spot:

F362  LDA PlayerScreenYLo
F365  STA byte_RAM_0

I start analyzing some of the code between those two spots. Redacting some parts I didn't fully understand and didn't analyze further for now, my main finding is when a size check is true, the code adds 8 to the player's y position to move their sprite 8 pixels lower:

F362  LDA PlayerScreenYLo      ; \
F365  STA byte_RAM_0           ; / Mem[0] = Mem[PlayerScreenYLo]

[...]

F372  LDA PlayerCurrentSize    ; \
F375  BEQ loc_BANKF_F382       ; / If Mem[PlayerCurrentSize] = 0, Goto [a later spot...] (skipping the following addition code)

F377  LDA byte_RAM_0           ; \
F379  CLC                      ; |
F37A  ADC #$08                 ; |
F37C  STA byte_RAM_0           ; / Mem[0] = Mem[0] + 8

[...]

F394  loc_BANKF_F394:          ; \
F394  JSR FindSpriteSlot       ; / Find a sprite slot value and store it into Y.

[...]

F39B  LDA byte_RAM_0           ; \
F39D  STA SpriteDMAArea, Y     ; / Store the "Y position" into the sprite buffer area.


The exact position of the #$08 value is CPU address F37B (which is NES file offset 1F38B).

I wanted to test if changing that value would move the eyes to a higher position.

In the debugger, I right clicked on the "F37A ADC #$08" instruction and chose "Edit in Memory Viewer".
This went to the first byte of the instruction (CPU address F37A).
I moved over one byte to the 08 value (CPU address F37B) and changed it to 04.

I let Mario get hit so he became small and...

Well, Mario's eyes did move up a little bit, along with the rest of Mario! He's hovering 4 pixels above the ground when he's small now!
I reset and tested each of the other characters and they did the same thing.

Maybe that will help you?

I'm taking a break from looking at it for now.
« Last Edit: April 11, 2022, 10:35:24 pm by Bavi_H »

NesDraug

  • Sr. Member
  • ****
  • Posts: 259
    • View Profile
Re: Super Mario Bros 2 USA hacking questions?
« Reply #2 on: April 12, 2022, 06:37:52 am »
I'm my hack of smb2 (The Beatles Adventures in Pepperland) I just wanted to get rid of the eyes.

Put in ea ea ea at Adress: 0x1f3ad

That's my two cents. Maybe the surrounding values handles the placement.
✍️ Blog: https://www.tumblr.com/blog/nesdraug

💀 Join my crypt on discord: https://discord.gg/sPb9zTZqvr

Bavi_H

  • Jr. Member
  • **
  • Posts: 35
    • View Profile
    • Robert Hart's Homepage
Re: Super Mario Bros 2 USA hacking questions?
« Reply #3 on: April 12, 2022, 10:34:12 am »
to get rid of the eyes [...] Put in ea ea ea at Adress: 0x1f3ad

Yep. NES file offset 1F3AD corresponds to CPU Address F39D in Bank E & F (see the conversion table in the middle of my previous post). This is the instruction that writes the Y-coordinate of the eyes (see "my process" inset text at the bottom of in my previous post):

F39D  99 00 02  STA SpriteDMAArea, Y
F3A0            [next instruction]


Changing the three bytes starting at that point to EA corresponds to NOPing out that instruction:

F39D  EA  NOP
F39E  EA  NOP
F39F  EA  NOP
F3A0      [next instruction]




how and where can I change the placement of the "eye highlight" sprite to be higher when the character is in "small mode?"

Now I've got a fresh mind, I continued looking at those following instructions:

Code: [Select]
F39B  LDA byte_RAM_0           ; A = Mem[0] ; The player Y-position
F39D  STA SpriteDMAArea, Y     ; Store A as eyes Y-position
F3A0  STA SpriteDMAArea + $20  ; Store A as player body a Y-position
F3A3  STA SpriteDMAArea + $24  ; Store A as player body b Y-position
F3A6  LDA byte_RAM_0           ; A = Mem[0]
F3A8  CLC                      ; \
F3A9  ADC #$10                 ; |
F3AB  STA byte_RAM_0           ; / Mem[0] = Mem[0] + 16
F3AD  LDA byte_RAM_1           ; \
F3AF  ADC #$00                 ; |
F3B1  BNE $F3BB                ; / Something like if the addition overflows, goto F3BB (skip the next part)
F3B3  LDA byte_RAM_0           ; A = Mem[0] ; Now the player Y-position + 16
F3B5  STA SpriteDMAArea + $28  ; Store A as player body c Y-position
F3B8  STA SpriteDMAArea + $2C  ; Store A as player body d Y-position
F3BB  [next instruction]       ;

So if we want to shift the eyes higher than the body, we have to store a different value in instruction F39D than the others.
Scrolling through the disassembly listing, it says there's some free space at FB36:

FB36  ; Unused space in the original ($FB36 - $FDFF) [...]
FB36  .pad $FE00, $FF


Time to write a little patch.

Pause the emulator.
In the Mesen Debugger, Goto FB36 (Ctrl+G, FB36)
Right click, choose "Edit selected code", and enter the following code:

Code: [Select]
LDA $6F6    ; A = Mem[PlayerCurrentSize]
BEQ normal  ; If tall, goto "normal"

SEC         ; \
LDA $0      ; |
SBC #10     ; / A = original Y position - 10
BCC restore ; If underflow, goto "restore" (skip next instruction)

STA $200, Y ; Store A as eyes y-position.

restore:
LDA $0      ; Reload A with original Y position.
JMP $F3A0   ; Jump back to original instruction flow.

normal:
LDA $0      ; Reload A with original Y position.
STA $200, Y ; Store A as eyes y-position.
JMP $F3A0   ; Jump back to original instruction flow.

[You can change the #10 to whatever number of pixels you want to shift the eyes upward.]

In the Mesen Debugger, Goto F39D (Ctrl+G, F39D)
Right click, choose "Edit selected code", and enter the following code:

JMP $FB36

The code changes above work with either Revision.
The code changes above correspond to these hex edits:

NES file offset hex: values hex

1FB46: AD F6 06 F0 0F 38 A5 00 E9 xx 90 03 99 00
       02 A5 00 4C A0 F3 A5 00 99 00 02 4C A0 F3

1F3AD: 4C 36 FB


(where xx is the number of pixels to shift the eyes upward)



Is it possible to change the construction so that Toad does have an eye highlight sprite, and Luigi does not?

In my previous post, I noticed the following parts of the disassembly:

Code: [Select]
F2D5  ; Tiles to use for eye sprite. If $00, this will use the character-specific table
F2D5  CharacterFrameEyeTiles:
F2D5  .db $00 ; Walk1
F2D6  .db $00 ; Carry1
F2D7  .db $00 ; Walk2
F2D8  .db $00 ; Carry2
F2D9  .db $FB ; Duck
F2DA  .db $FB ; DuckCarry
F2DB  .db $00 ; Jump
F2DC  .db $FB ; Death
F2DD  .db $FB ; Lift
F2DE  .db $00 ; Throw
F2DF  .db $FB ; Climb

F2E0  ; Specific to each character
F2E0  CharacterEyeTiles:
F2E0  .db $D5 ; Mario
F2E1  .db $D9 ; [Actually Princess*]
F2E2  .db $FB ; Toad
F2E3  .db $D7 ; [Actually Luigi*]

Tile $FB corresponds to a blank tile. (*And I discovered the original disassembly comments have Luigi and Princess mixed up.)

If you want to take Luigi's eyes away and give Toad some eyes, you can try swapping the values at CPU addresses F2E2 and F2E3 (NES file offsets 1F2F2 and 1F2F3). Or try other modifications to the values at those four CPU addresses F2E0 to F2E3 (NES file offsets 1F2F0 to 1F2F3). Works in both Revisions.

tygerbug

  • Full Member
  • ***
  • Posts: 101
    • View Profile
Re: Super Mario Bros 2 USA hacking questions?
« Reply #4 on: April 13, 2022, 02:19:58 am »
  Thanks for your input, everyone.


WORK IN PROGRESS:
https://drive.google.com/file/d/1WmnktVdIPMQMqQ90Th4Erl8OYzfMINov/view?usp=sharing



  Swapping the values at CPU addresses F2E2 and F2E3 (NES file offsets 1F2F2 and 1F2F3) absolutely does switch the eye construction of the characters. Toad loads a blank sprite for his eyes as noted, identical functionality which is normally dummied out.


  The disassembly also includes a few bug fixes, such as the infamous missing eighth frame of tile animation.

  Does the disassembly also note the "height" of the characters when carrying things? It feels like this could be fixed at that level.

  A subroutine could also be called to allow for an extra two pages of text in the intro.


C0A5 - First level's Birdo palette


   Turning to the title screen for awhile ...

   I wish I could say that there's no unusual coding here, but of course some tricks are done to avoid presenting a full nametable.

   Looking at the ASM disassembly, which is very well commented, various sections of the screen are drawn individually, as their own sections.

https://github.com/Xkeeper0/smb2/blob/master/src/prg-0-1.asm

My intended layout looks something like this, for the frame and characters, and then for the title text:

Code: [Select]
:: :: :: :: :: :: :: :: :: :: 61 60 62 64 64 64 64 64 64 64 64 64 64 64 64 64 64 63 65 :: :: ::
:: :: :: :: :: 68 6C 6B 70 6F 73 72 ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; 74 :: :: ::
:: :: :: :: 67 66 6A 69 6E 6D 71 ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; 81 :: :: ::
:: :: :: :: 77 76 7B 7A 7F 7E 80 ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; 81 :: :: ::
:: :: :: :: 75 ;; 79 78 7D 7C ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; 81 :: :: ::
:: :: :: 82 85 84 89 88 8B ;; ;; XX ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; 81 :: :: ::
:: :: :: A3 ;; 83 87 86 8A ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; 81 :: :: ::
:: :: :: A3 ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; 81 :: :: ::
:: :: :: A3 ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; 81 :: :: ::
:: :: :: A3 ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; 81 :: :: ::
:: :: :: A3 ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; 81 :: :: ::
:: :: :: A3 ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; 81 :: :: ::
:: :: :: A3 ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; 81 :: :: ::
:: :: :: A3 ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; 81 :: :: ::
:: :: :: A3 ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; 81 :: :: ::
:: :: :: A3 ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; 81 :: :: ::
:: :: :: A3 ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; 81 :: :: ::
:: :: :: A3 ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; 8D 91 90 95 94 97 :: :: ::
:: :: :: A3 ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; 8C 8F 8E 93 92 96 :: :: ::
:: :: :: A3 ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; 98 9B 9A 9F 9E A1 :: :: ::
:: :: :: A3 ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; 9D 9C A0 :: :: ::
:: :: :: A3 ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; A7 A6 A9 :: :: ::
:: :: :: A2 ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; A5 A4 A8 :: :: ::
:: :: :: AA AB AD AD AD AD AD AD AD AD AD AD AD AD AD AD AD AD AD AD AD AD AD AD AC AF :: :: ::

;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ED E1 DE ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;;
;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;;
;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; 01 05 04 09 08 0D 0C 11 10 15 14 ;; ;; ;; ;; ;; ;; ;; ;; ;; ;;
;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; 00 03 02 07 06 0B 0A 0F 0E 13 12 ;; ;; ;; ;; ;; ;; ;; ;; ;; ;;
;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; 16 18 17 1A 19 1D 1C 21 20 22 ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;;
;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; 1B ;; 1E 1E 1B 1F 1E ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;;
;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; DA E7 DD ;; ;; ED E1 DE ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; ;;
;; ;; ;; ;; ;; ;; ;; ;; ;; 23 ;; ;; ;; ;; ;; ;; ;; ;; ;; ;; 25 24 ;; ;; ;; ;; ;; ;; ;; ;; ;; ;;
;; ;; ;; ;; ;; ;; ;; 27 2B 2A 2F 2E 33 32 37 36 3B 3A 3F 3E 43 42 47 46 4A ;; ;; ;; ;; ;; ;; ;;
;; ;; ;; ;; ;; ;; ;; 26 29 28 2D 2C 31 30 35 34 39 38 3D 3C 41 40 45 44 49 48 ;; ;; ;; ;; ;; ;;
;; ;; ;; ;; ;; ;; ;; 4B 4D 4C 4F 4E 51 50 53 52 55 54 57 56 59 58 5B 5A 5E 5D ;; ;; ;; ;; ;; ;;

This translates to this code:

XX, YY = X/Y Position in Background (rows 20-23, in 8x8 tiles)
SS = Number of tiles to display horizontally (or vertically depending on the hex number used)
DD = Tile IDs

Code: [Select]
TitleLayout:
; red lines, vertical, left
.db $20, $00, $DE, $FD
.db $20, $01, $DE, $FD
.db $20, $02, $DE, $FD
.db $20, $03, $DE, $FD
; red lines, vertical, right
.db $20, $1C, $DE, $FD
.db $20, $1D, $DE, $FD
.db $20, $1E, $DE, $FD
.db $20, $1F, $DE, $FD
; red lines, horizontal, top
.db $20, $03, $5D, $FD
.db $20, $23, $5D, $FD
.db $20, $43, $5D, $FD
.db $20, $63, $5D, $FD
; red lines, vertical, bottom
.db $23, $63, $5D, $FD
.db $23, $83, $5D, $FD
.db $23, $A3, $5D, $FD

; ornate frame, top, 162F
.db $20, $6A, $03, $61, $60, $62
; .db $20, $7B, $02, $63, $65
.db $20, $85, $07, $68, $6C, $6B, $70, $6F, $73, $72
.db $20, $A4, $07, $67, $66, $6A, $69, $6E, $6D, $71
.db $20, $C4, $07, $77, $76, $7B, $7A, $7F, $7E, $80
.db $20, $E4, $06, $75, $FE, $79, $78, $7D, $7C
.db $21, $03, $06, $82, $85, $84, $89, $88, $8B
.db $21, $25, $04, $83, $87, $86, $8A

; ornate frame, lines down
.db $21, $23, $D0, $A3
.db $20, $BC, $D0, $81

; ornate frame, corners
.db $20, $7B, $02, $63, $65
.db $20, $9C, $01, $74
.db $23, $23, $01, $A2
.db $23, $43, $02, $AA, $AB

; ornate frame, lines horizontal?
.db $20, $6D, $4E, $64
.db $23, $45, $56, $5F

; ornate frame, bottom, characters
.db $22, $97, $06, $8D, $91, $90, $95, $94, $97
.db $22, $B7, $06, $8C, $8F, $8E, $93, $92, $96
.db $22, $D7, $06, $98, $9B, $9A, $9F, $9E, $A1
.db $22, $FA, $03, $9D, $9C, $A0
.db $23, $1A, $03, $A7, $A6, $A9
.db $23, $3A, $03, $A5, $A4, $A8
.db $23, $5B, $02, $5E, $99

; THE THIEF AND THE COBBLER
.db $20, $CF, $03, $ED, $E1, $DE
.db $21, $0B, $0B, $01, $05, $04, $09, $08, $0D, $0C, $11, $10, $15, $14
.db $21, $2B, $0B, $00, $03, $02, $07, $06, $0B, $0A, $0F, $0E, $13, $12
.db $21, $4B, $0A, $16, $18, $17, $1A, $19, $1D, $1C, $21, $20, $22
.db $21, $6D, $07, $1B, $FE, $1E, $1E, $1B, $1F, $1E
.db $21, $8C, $08, $DA, $E7, $DD, $FE, $FE, $ED, $E1, $DE
.db $21, $A9, $01, $23
.db $21, $B4, $02, $25, $24
.db $21, $C7, $12, $27, $2B, $2A, $2F, $2E, $33, $32, $37, $36, $3B, $3A, $3F, $3E, $43, $42, $47, $46, $4A
.db $21, $E7, $13, $26, $29, $28, $2D, $2C, $31, $30, $35, $34, $39, $38, $3D, $3C, $41, $40, $45, $44, $49, $48
.db $22, $07, $13, $4B, $4D, $4C, $4F, $4E, $51, $50, $53, $52, $55, $54, $57, $56, $59, $58, $5B, $5A, $5D, $5C

; (C) 1988
;                  (C)  111  999  888  888
.db $22, $E9, $05, $F8, $D1, $D9, $D8, $D8 ; (C) 1988

; NINTENDO
;                  NNN  III  NNN  TTT  EEE  NNN  DDD  OOO
.db $22, $EF, $08, $E7, $E2, $E7, $ED, $DE, $E7, $DD, $E8

.db $23, $CA, $04, $80, $A0, $A0, $20
.db $23, $D1, $0E, $80, $A8, $AA, $AA, $A2, $22, $00, $00, $88, $AA, $AA, $AA, $AA, $22
.db $23, $E3, $02, $88, $22
.db $23, $EA, $04, $F0, $F8, $F2, $F0
.db $00



   It's fairly straightforward once you get used to drawing only the important parts of each line, and ignoring the background and some repeated tiles.

  My intended code is shorter than Nintendo's at this point, but I don't think that breaks the ROM.

  Annoyingly the nametable palettes are also laid out this way, and not very understandable, but after some confusion I think I got my desired result.

  Having more space in the code than expected, and having 35 blank tiles used elsewhere for the "Bonus Chance" game, I've added an additional character to the title screen -- she takes up 35 tiles.



   My question now is, where is the code which blanks out part of the screen for the story text?

   Due to the additional artwork on my title screen, I need to blank out more of the screen than is currently being blanked out.
« Last Edit: April 19, 2022, 06:26:09 pm by tygerbug »

Bavi_H

  • Jr. Member
  • **
  • Posts: 35
    • View Profile
    • Robert Hart's Homepage
Re: Super Mario Bros 2 USA hacking questions?
« Reply #5 on: April 20, 2022, 10:41:24 pm »
where is the code which blanks out part of the screen for the story text?

Hello tylerbug. I used breakpoints to help find the relevant code. You can read the beginning part of my process in the following inset text:

Code: [Select]
In Mesen, I started Super Mario Bros. 2, waited for the title screen to completely appear, then paused the emulator.

I couldn't remember the PPU addresses for the screen tiles, so I peeked in the PPU Viewer, Nametable Viewer page. Pointing at the top-left tile of currently scrolled area says the PPU Address is $2000. The bottom-right tile of the currently scrolled area is PPU Address $23BF.

Now over to the Debugger, I set a breakpoint to catch writes to PPU Addresses $2000 to $23BF. I unpaused the emulator and waited for the screen clearing point to start. The debugger stopped at the following code

ED3B  LDA ($F0),Y
ED3D  STA PpuData_2007

(In the disassembly listing, this code is in the routine "UpdatePPUFromBufferWithOptions:")

In the Memory Viewer, I saw CPU Memory $F0 and $F1 contain $01 and $03, meaning they point to memory address $0301.

So now I want to figure out where memory address $301 is getting loaded.

In the Debugger, I removed the breakpoint. I "power cycled" the game and once again paused when the title screen had completely appeared.

In the Debugger, I set a breakpoint for writes to CPU Memory $301, then unpaused the emulator.

This time the debugger stopped at the following code:

EC51  LDA $11
EC53  BNE $EC5B
EC55  STA $0300
EC58  STA $0301

I unpaused the code, but it kept getting stopped at this point. I figured out this code is writing zeros to memory locations $300 and $301, which I'm not interested in, so I modified the breakpoint to only catch writes where the value is not zero (in Mesen's breakpoint edit window, I added a condition of "value != 0") and unpaused the emulator.

This time the debugger stopped at the following code

9AD0  LDA $14
9AD2  STA $0301
9AD5  LDA $15
9AD7  STA $0302
9ADA  LDA $16
9ADC  STA $0303

The bottom part of Mesen's debugger window tells me that Banks 0 & 1 are mapped into this part of memory at the time.

I stopped taking detailed notes about the process here, but I gradually examined more and more of the surrounding code in steps, sometimes going further down in the code, and sometimes going further up in the code. And, for example, at one point I set another breakpoint to catch writes to address $14 to tell where it was getting initialized.

I started building notes about the code I saw. [I sometimes referenced the disassembly listing, but some parts of the listing are using names for the memory locations that didn't make sense for this part of the code. The memory locations are obviously reused for other purposes elsewhere in the code, and the names make sense during those other parts of the code, but the names are confusing in this part of the code.]

Here is the relevant code I found with my notes:

Code: [Select]
; Part A. Clear memory locations 0 to 255 ($00 to $FF) to zero:

9A53  LDA #$00    ; A = 0
9A55  TAY         ; Y = 0
9A56  STA $0000,Y ; Mem[Y] = 0
9A59  INY         ; Y = Y + 1 (If Y was 255, Y will wrap back to zero)
9A5A  BNE $9A56   ; If Y is not zero, Goto $9A56

[...later on...]


; Part B. Set up some memory locations:

9AA0  LDA #$03  ; \
9AA2  STA $10   ; / Mem[$10] = $03
9AA4  LDA #$25  ; \
9AA6  STA $02   ; / Mem[$02] = $25
9AA8  LDA #$20  ; \
9AAA  STA $14   ; / Mem[$14] = $20
9AAC  LDA #$C7  ; \
9AAE  STA $15   ; / Mem[$15] = $C7
9AB0  LDA #$52  ; \
9AB2  STA $16   ; / Mem[$16] = $52


; Part C.
; When Mem[$17] is 0, wait at the title screen for 8 seconds
; When Mem[$17] is not 0, waits one NMI (which also paints tiles in the 301 buffer), then goes to 9AF3:

9AB4  JSR $9608 ; Gosub $9608 ("WaitForNMI_TitleScreen:") [NMI paints tile layout in 301 buffer, if any.]
9AB7  LDA $17   ; A = Mem[$17]
9AB9  BNE $9AF3 ; If non-zero, Goto $9AF3
9ABB  INC $10   ; Mem[$10] = Mem[$10] + 1
9ABD  LDA $10   ; A = Mem[$10]
9ABF  AND #$0F  ; A = A AND $F
9AC1  BEQ $9AC6 ; If zero, skip next instruction
9AC3  JMP $9B4D ; Else non-zero, Goto $9B4D (check for START button press, if none goes back to 9AB4)
9AC6  DEC $02   ; Mem[$2] = Mem[$2] - 1
9AC8  LDA $02   ; \
9ACA  CMP #$06  ; |
9ACC  BNE $9B4D ; / If Mem[$2] <> 6, Goto $9B4D (check for START button press, if none goes back to 9AB4)


; Part D. Set up some more memory locations:

9ACE  INC $17   ; Mem[$17] = Mem[$17] + 1
9AD0  LDA $14   ; \
9AD2  STA $0301 ; / Mem[$301] = Mem[$14]
9AD5  LDA $15   ; \
9AD7  STA $0302 ; / Mem[$302] = Mem[$15]
9ADA  LDA $16   ; \
9ADC  STA $0303 ; / Mem[$303] = Mem[$16]
9ADF  LDA #$E6  ; \
9AE1  STA $15   ; / Mem[$15] = $E6
9AE3  LDA #$54  ; \
9AE5  STA $16   ; / Mem[$16] = $54
9AE7  LDA #$FB  ; \
9AE9  STA $0304 ; / Mem[$304] = $FB
9AEC  LDA #$00  ; \
9AEE  STA $0305 ; / Mem[$305] = $00
9AF1  BEQ $9B4D ; Since zero, will always goto "check for START button press, if none goes back to 9AB4"


; Part E:

9AF3  LDA $14   ; \
9AF5  STA $0301 ; / Mem[$301] = Mem[$14]
9AF8  LDA $15   ; \
9AFA  STA $0302 ; / Mem[$302] = Mem[$15]
9AFD  LDA $16   ; \
9AFF  STA $0303 ; / Mem[$303] = Mem[$16]
9B02  LDA #$FB  ; \
9B04  STA $0304 ; / Mem[$304] = $FB
9B07  LDA #$00  ; \
9B09  STA $0305 ; / Mem[$305] = $00
9B0C  LDA $15   ; \
9B0E  CLC       ; |
9B0F  ADC #$20  ; |
9B11  STA $15   ; |
9B13  LDA $14   ; |
9B15  ADC #$00  ; |
9B17  STA $14   ; /  increment (Mem[$14] high, Mem[$15] low) by $20
9B19  CMP #$23  ; \
9B1B  BCC $9B4D ; / If (Mem[$14] high, Mem[$15] low) < $2300 Goto $9B4D (check for START & back to 9AB4)
9B1D  [next instruction]

----------------

; Check for start button press:

9B4D  LDA Player1JoypadPress     ; \
9B4F  AND #ControllerInput_Start ; / If START pressed, A is non-zero. If START not pressed, A is zero.
9B51  BEQ $9B56                  ; If A is zero (START not pressed), skip next instruction
9B53  JMP $9C1F                  ; Else A is non-zero (START was pressed), Goto $9C1F
9B56  JMP $9AB4                  ; A was zero (START not pressed), Goto $9AB4

--------------

Here are some notes about memory contents after parts of code execute:

Code: [Select]
after  contents of     Mem locations
 part  301 buffer      14  15  16  17  comment
-----  --------------  --  --  --  --  ----------------------
    A  00              00  00  00  00
    B                  20  C7  52
    C                                  8 seconds elapsed
    D  20 C7 52 FB 00  20  E6  54  01
    C                                  NMI painted the buffer
    E  20 E6 54 FB 00  21  06
    C                                  NMI painted the buffer
    E  21 06 54 FB 00  21  26
    C                                  NMI painted the buffer
    E  21 26 54 FB 00  21  46
...                                    ...and so on...
    E  22 C6 54 FB 00  22  E6
    C                                  NMI painted the buffer
    E  22 E6 54 FB 00  23  06          end of loop but eventually
                                         NMI paints buffer?

As you know the $FB value is for a blank tile. In the code, if you change the first time the $FB value appears to $DA (for an "A" tile) and change the second time $FB appears to $DB (for a "B" tile), then when the code "clears" the screen, you will see a line of A's with one less character on left and right (to avoid overwriting a curly part of the original frame), then many lines of B's that extend one more character left and right compared to the line of A's.

I copied your "TitleLayout:" bytes to a text editor then cleaned it up so it just had bare hex values and nothing else. In Mesen's memory viewer, I went to the place for the "TitleLayout:" (CPU Memory $961F) and pasted the hex values in. The title screen then loads your layout with the default Super Mario Bros. 2 tiles. I can get a general idea of your layout, but I can't tell how best to proceed:

If you just need to extend the layout of blank $FB tiles, we can maybe figure out relevant values to change in the code to extend the layout of blank $FB tiles to cover more space to the left, right, or bottom top.

But if you also need to paint some other tiles, for example to replace parts of a character with a frame border, more advanced code modification may be needed.


Text renderings

_: the $FB blank tile
#: other solid color tiles, like red or black
letters or numbers: text tiles
*: all other tiles


Here is my text rendering of your title screen layout:

Code: [Select]
################################
################################
################################
##########*******************###
####_*******________________*###
####*******_________________*###
####*******____THE__________*###
####*#****__________________*###
###******__***********______*###
###*_****__***********______*###
###*_______**********_______*###
###*_________*#*****________*###
###*________AND##THE________*###
###*_____*__________**______*###
###*___******************___*###
###*___*******************__*###
###*___*******************__*###
###*________________________*###
###*________________________*###
###*________________________*###
###*___________________******###
###*___________________******###
###*___________________******###
###*_____c1988_NINTENDO___***###
###*______________________***###
###*______________________***###
###**************************###
################################
################################
################################

When the screen is cleared, here are the "A" and "B" lines that it clears:

Code: [Select]
################################
################################
################################
##########*******************###
####_*******________________*###
####*******_________________*###
####***AAAAAAAAAAAAAAAAAA___*###
####*#BBBBBBBBBBBBBBBBBBBB__*###
###***BBBBBBBBBBBBBBBBBBBB__*###
###*_*BBBBBBBBBBBBBBBBBBBB__*###
###*__BBBBBBBBBBBBBBBBBBBB__*###
###*__BBBBBBBBBBBBBBBBBBBB__*###
###*__BBBBBBBBBBBBBBBBBBBB__*###
###*__BBBBBBBBBBBBBBBBBBBB__*###
###*__BBBBBBBBBBBBBBBBBBBB__*###
###*__BBBBBBBBBBBBBBBBBBBB__*###
###*__BBBBBBBBBBBBBBBBBBBB__*###
###*__BBBBBBBBBBBBBBBBBBBB__*###
###*__BBBBBBBBBBBBBBBBBBBB__*###
###*__BBBBBBBBBBBBBBBBBBBB__*###
###*__BBBBBBBBBBBBBBBBBBBB***###
###*__BBBBBBBBBBBBBBBBBBBB***###
###*__BBBBBBBBBBBBBBBBBBBB***###
###*__BBBBBBBBBBBBBBBBBBBB***###
###*______________________***###
###*______________________***###
###**************************###
################################
################################
################################
« Last Edit: April 21, 2022, 12:06:47 pm by Bavi_H »

tygerbug

  • Full Member
  • ***
  • Posts: 101
    • View Profile
Re: Super Mario Bros 2 USA hacking questions?
« Reply #6 on: April 21, 2022, 04:12:51 am »

Here are some notes about memory contents after parts of code execute:

Code: [Select]
after  contents of     Mem locations
 part  301 buffer      14  15  16  17  comment
-----  --------------  --  --  --  --  ----------------------
    A  00              00  00  00  00
    B                  20  C7  52
    C                                  8 seconds elapsed
    D  20 C7 52 FB 00  20  E6  54  01
    C                                  NMI painted the buffer
    E  20 E6 54 FB 00  21  06
    C                                  NMI painted the buffer
    E  21 06 54 FB 00  21  26
    C                                  NMI painted the buffer
    E  21 26 54 FB 00  21  46
...                                    ...and so on...
    E  22 C6 54 FB 00  22  E6
    C                                  NMI painted the buffer
    E  22 E6 54 FB 00  23  06          end of loop but eventually
                                         NMI paints buffer?

As you know the $FB value is for a blank tile.


Thanks for doing all that work and research, I appreciate it.

I think I completely understand the format of the above code you found in the memory, since it's formatted similarly to how the title screen itself is drawn. If that code is in the ROM I could make minor changes to it and get my desired result.

I don't think that code is in the ROM directly though?

I don't really understand the assembly code you posted. (Or at least it's not as easy a read.)

If that memory code is in the ROM as hex, that's another matter, and would be easy to edit. It's not ...

There is some code around 1AC4 which seems to affect the blanking, such as 1Af0 and 1Af4. Also 1ABE and 1AC1. It's all in a strange order though.

I am able to move the starting point and affect the length of the lines. The end point seems to still be fixed (around 22 FC, depending on line length).


Judging from the code you posted, it just stops outright at the "23" portion of the layout, without much wiggle room in the code to fix that. (It stops at "23 00" but only the "23" is written. To extend the blanking downward we would need to also write a different number into the "00" spot somehow.)

At least changing the starting point helps a lot in terms of making this presentable.

I suppose I could also move the design elements up so that they're easier to blank out.
« Last Edit: April 21, 2022, 03:22:05 pm by tygerbug »

Bavi_H

  • Jr. Member
  • **
  • Posts: 35
    • View Profile
    • Robert Hart's Homepage
Re: Super Mario Bros 2 USA hacking questions?
« Reply #7 on: April 21, 2022, 11:54:05 am »
contents of
301 buffer
--------------
20 C7 52 FB 00
20 E6 54 FB 00
21 06 54 FB 00
21 26 54 FB 00
...
22 C6 54 FB 00
22 E6 54 FB 00

I don't think that code is in the ROM directly though? [...] If that memory code is in the ROM as hex, that's another matter, and would be easy to edit. It's not ...

You are correct, those resultant layout buffers do not exist as those byte sequences in the NES file. The code loads the bytes individually in various different instructions. And for the third line onward, the code increments the starting PPU address value for the first two buffer bytes and reloads those each frame.


In the following code snippets:

Code: [Select]
9AA8 A9 20     LDA #$20  ; \
9AAA 85 14     STA $14   ; / Mem[ $14] = $20 (later copied to Mem[$301])
9AAC A9 C7     LDA #$C7  ; \
9AAE 85 15     STA $15   ; / Mem[ $15] = $C7 (later copied to Mem[$302])
9AB0 A9 52     LDA #$52  ; \
9AB2 85 16     STA $16   ; / Mem[ $16] = $52 (later copied to Mem[$303])

9AE7 A9 FB     LDA #$FB  ; \
9AE9 8D 04 03  STA $0304 ; / Mem[$304] = $FB
9AEC A9 00     LDA #$00  ; \
9AEE 8D 05 03  STA $0305 ; / Mem[$305] = $00

9ADF A9 E6     LDA #$E6  ; \
9AE1 85 15     STA $15   ; / Mem[ $15] = $E6 (later copied to Mem[$302])
9AE3 A9 54     LDA #$54  ; \
9AE5 85 16     STA $16   ; / Mem[ $16] = $54 (later copied to Mem[$303])

9B02 A9 FB     LDA #$FB  ; \
9B04 8D 04 03  STA $0304 ; / Mem[$304] = $FB
9B07 A9 00     LDA #$00  ; \
9B09 8D 05 03  STA $0305 ; / Mem[$305] = $00

you can locate specific addresses that define the eventual buffer values, then modify them to expand the blank region up, left, or right:

Code: [Select]
CPU: CPU address hex
NES: NES file offset hex

CPU   NES   val  layout buffer meaning           comment
----  ----  ---  ------------------------------  ---------------------------------------------------------------
9AA9  1AB9  20   starting PPU address high byte
9AAD  1ABD  C7   starting PPU address low byte   decrease by 1 to move "A" line left, decrease by $20 to move up
9AB1  1AC1  52   layout type and length          increase to lengthen "A" line
9AE8  1AF8  FB   tile used to blank
9AED  1AFD  00   end of buffer mark

9AA9  1AB9  20   starting PPU address high byte  (same code as "A" line, used for both "A" and "B" lines)
9AE0  1AF0  E6   starting PPU address low byte   decrease by 1 to move "B" lines left, decrease by $20 to move up
9AE4  1AF4  54   layout type and length          increase to lengthen "B" lines
9B03  1B13  FB   tile used to blank
9B08  1B18  00   end of buffer mark

I originally thought expanding the blank region down would be similar, but I now realize the comparison at the end of the loop is only checking the high byte of the PPU address. To expand down 1 to 7 rows, we would need to patch in more instructions to also check the low byte of the PPU address.


There is some code around 1AC4 which seems to affect the blanking, such as 1Af0 and 1Af4.

Correct! In my first post in this thread, I included a table to convert CPU addresses to NES file offsets. The NES file offset 1AC4 you found is the same as CPU address 9AB4 when Bank 0 & 1 is active, which is the beginning of the Part C code I found. And the 1AF0 and 1AF4 NES file offsets you found match up with some of the locations in my list above.


I don't really understand the assembly code you posted.

Sorry for the confusing assembly code. If I haven't already mentioned them, here are some possible documents you can look over when you are ready to explore 6502 assembly language more. I hope these documents aren't too confusing, I'm always looking for better documents to help people learn.

Easy 6502 -- An introduction to 6502 assembly language with examples you run in a simulator.
6502 Instruction Set
NMOS 6502 Opcodes


Some basic information:

Code: [Select]
"LDA #$n" means "load the accumulator with value n".
"STA $n" means "store the accumulator into the memory location $n".

(If you use Mesen's debugger, when you hover over an instruction name, a tool tip will appear to remind you of the instruction definition.)

The # means a value follows. Without the # it means it's an address location.
The $ means a hex number follows. Without the $ it means it's a decimal number.

At the beginning of the assembly code lines, the hex number at the beginning is the CPU address of the beginning of the instruction.

In some listings there are two or three more hex values after the CPU address that indicate the values at that CPU address and following CPU addresses that actually represent the instruction. Be aware that one assembly operation like "LDA" is represented by different byte values to indicate the style of operand that follows, called the "addressing mode" of the instruction. For example, "A9 nn" means "LDA #$n" with a value operand, and "A5 nn" or "AD nn nn" means "LDA $n" with a memory address operand.

The hex values at the beginning of the line indicating the CPU address and values are only present in debuggers or the "listing file" output from an assembler building the code. In original source code, the CPU address isn't shown on every line.

I am more familiar with C or JavaScript style code, so while I am examining code to understand what it is doing, I "translate" the assembly code into a C or JavaScript style pseudocode in the comments after the semicolons. For example, in my pseudocode comments "Mem[$m] = $v" means "store the value $v into the memory location $m". If you have ever seen C or JavaScript style code before, those comments might help explain some of the assembly code. If not, I apologize if they add more confusion.


And here's some NES information sources:

NesDev Wiki
Nintendo Entertainment System Documentation
« Last Edit: April 21, 2022, 12:20:15 pm by Bavi_H »

tygerbug

  • Full Member
  • ***
  • Posts: 101
    • View Profile
Re: Super Mario Bros 2 USA hacking questions?
« Reply #8 on: April 21, 2022, 02:24:02 pm »

I've reread your notes on the assembly and it makes a little more sense now.

Quote
Judging from the code you posted, it just stops outright at the "23" portion of the layout, without much wiggle room in the code to fix that. (It stops at "23 00" but only the "23" is written. To extend the blanking downward we would need to also write a different number into the "00" spot somehow.)

At least changing the starting point helps a lot in terms of making this presentable.

I suppose I could also move the design elements up so that they're easier to blank out.



I've done a build moving design elements upward. It's a compromise but a minor one.


EDIT:
https://drive.google.com/file/d/1WmnktVdIPMQMqQ90Th4Erl8OYzfMINov/view?usp=sharing
https://drive.google.com/file/d/1RLKH5YW9HiIF6hYA16F6LJadHDAm7XOH/view?usp=sharing
https://github.com/kmck/smb2/blob/master/src/prg-c-d.asm
https://github.com/Xkeeper0/smb2/blob/master/src/prg-c-d.asm


Working on the ending. Lot of problems with it at the moment. I am editing PRG C-D.ASM in the disassembly.

Animation of Subcons when freed from jar is very fast and jerky. Looks glitchy.

(Have replaced Subcons with something where the jerkiness doesn't matter as much.)

Ending (Mario dreaming):

Would like cast roll to start a lot sooner.

Nametable and sprites do not clear when scene of Mario dreaming loads. Colors of the player characters from the "contribution screen" are still visible throughout the end credits, which is bizarre. I have written some code to draw over the screen but this does not affect those colors, oddly. The color attributes of the nametable don't change. They might be sprites, as they have sprite colors. But they appear behind the background.

(EDIT: They are sprites. Construction is at 2cF9 and is just the previous construction used on the celebration screen. This means I can't dummy it out.)

(I need to clear RAM 210-247 at the very least, with FF.)

Would like the small sprites of the player characters to remain onscreen throughout.

I have slowed down the frame rate of the "Mario dreaming" animation to 50. I may slow it down further, since my ending is a slide show. One effect of this is that the "Cast list" takes a lot longer to start scrolling. There's a long wait, and I'd like to skip that so that it scrolls much earlier. I don't need the "Mario waking up" animation even.

Screen currently glitches out when the "Mario waking up" animation plays (or possibly when he goes back to sleep, I can't tell), with a glitched palette and nametable. It didn't do that before, so I must have screwed something up somewhere. It loads the code for "MarioDream_BubbleSprites" as palette data. Maybe some pointer got screwed up.

(EDIT: I've figured this out. "Mario Do Nothing" is actually called, but doesn't run because it starts with the terminating byte .db $00 ... I removed this byte so that code is running at some point.)

My code for drawing the "bed" is a different (shorter) length than the original, and I've removed the Bubble drawing code, but I also added code to the unused routine "Mario Does Nothing" to keep things the same length.










My version has 12 very different images, rather than the similar images of Mario in the original.

Because of that, I've removed all code about the bubble and whatnot, because at this point I've only allocated two background tiles to draw the rest of the background. One being blank and one having a little pattern as you can see here.

Mario's layout also allowed for some background elements to be included. That's not true here when I'm using as much space as possible, with only the upper left and right corners dummied out.

There are little sprite versions of the players, but you can also see the four little colored figures standing as colored blocks over where the cutscene should be. I need to remove those somehow, I don't know why that's happening. (EDIT: They are sprites. Hm.)

I would also like the "cast" roll to start a lot sooner.

(The removal of the bubble code is probably causing a pointer to be misplaced which causes the wrong data to be loaded later, when Mario does his waking up animation, or right afterward. It loads the code for "MarioDream_BubbleSprites" as palette data. The code is the same length overall, but is padded during an unused routine to achieve this.)


(I think I've figured this out. "Mario Do Nothing" is actually called, but doesn't run because it starts with the terminating byte .db $00 ... I removed this byte so that code is running at some point.)



Here's another question. There's lots of enemy behavior and construction in PRG 2-3.ASM. For some enemies, a palette number is specified here. For others which use the first palette (red/blue), there's no palette specified. Can I change that somehow?

I would like Birdo to always use Palette 2 ... that seems to be in the ASM code, but building it and copy/pasting it breaks the game unfortunately.

(Ah, the ASM build is off by several digits. I've manually put some changes in the right place and will experiment.)

(EDIT: I've edited Birdo to use Palette 2 throughout, and Cobrat to use Palette 3. There's no options for Ostro it seems, which is a big shame, or for the common enemies.)

Wart, FryGuy, Clawgrip and Mouser seem to load specific palettes for their boss rooms so that's less of a problem. Although it's usually palette 1 which is also used for the health meter and so on, so the player has a strangely colored health meter during the fight.



Notes on throwing:

At 1F31A in the ROM is a construction for the player's throwing animation. I needed a unique sprite of the player's head during the throwing animation, mainly for when the player is small. Normally this uses the same head as walking, which becomes a big problem when you only have eight pixels of height in the leg area to show that the character is throwing. It would be easier to show this in the head area when small.

You could replace the throwing feet with an alternate throwing head. But you don't need to. There's a redundant (Doki Doki Panic styled) sprite of the Albatoss' head at the right of every version of the character's action sheet. It's not used in the game, and can be replaced.

So in the construction, 3C 3E replaces 00 02, and a throwing head sprite replaces the Albatoss head.

I would also like to use the "throwing" version of the head during half of the walk cycle, which again allows for much easier animation when the character is small, as more than the feet can be affected.

This construction is at 1F2F5.

The rest of the Doki Doki Panic Albatoss animation is on the next CHR bank, near Birdo's sprites.

This two-frame animation is different from the eight-frame animation in the US release (only seven of which are used, accidentally), although if it were used it would allow for a ten-frame animation.

So this is also redundant and could be used for something. Not sure what. Maybe as another side for the mirrored items on this sheet, if you don't want these items to be mirrored. Or for Birdo's egg (at 5A40), which is also used as clouds throughout, and is therefore difficult to change to anything else. (Mine is a playing card, so the clouds also became playing cards, which I might as well undo now.)

I'd also like to change crowd behavior when Wart is defeated. There are six animation metasprites for the celebrating Subcons, and this animation relies on them all looking the same. I drew three different crowd members and am looking to alter the code to allow for that. (Possibly 27C3, 27E6, 29AA in the ROM for the celebrating.)

Note that there are lots of unused graphics in the scene celebrating Wart's defeat. There is a cell and lock, money with numbers, and a bunch of items commonly used in the game. Theoretically this allows for a lot of extra graphics.
« Last Edit: April 28, 2022, 02:35:41 pm by tygerbug »

tygerbug

  • Full Member
  • ***
  • Posts: 101
    • View Profile
Re: Super Mario Bros 2 USA hacking questions?
« Reply #9 on: April 28, 2022, 02:34:51 pm »
UPDATE:

Here's a summary of stuff I haven't figured out yet:

The sprites and nametable do not clear when the ending scene (normally Mario dreaming) loads. They need to clear.

I can wipe the nametable but the four "player" sprites from the previous screen do not wipe until the cast roll starts, so there are four big garbage sprites onscreen.

I need to clear RAM 210-247 basically, probably with FF.

I need the cast roll to start sooner also, by a lot. The frames of the ending cutscene are held longer in my version, which means the Cast Roll doesn't start until way too late. I'm sure there's some kind of timer counting the frames somewhere, or counting subroutines that could be skipped.


EDIT: Shauing, from Adventures In Pepperland, has figured this out! Working on it now.


Also: I would like to edit Ostro to always use palette 2 or 3. That doesn't seem to be an option for Ostro or most common enemies.

(Judging from SMB2 editors, I think common enemies just exist in multiple forms, but Ostro doesn't.)

I would also like FryGuy (and his flames) and Clawgrip (and his rocks) to always use palette 2 or 3 instead of palette 1.

This is less of a problem since they're self-contained, but it's clear that I've edited palette 1 in these scenes.

(Palette 0 is player palette, palette 1 is red/white and used for lots of game elements.)

It would also be nice for the end credit palette (which uses palette 0) to be editable. Apart from forcing white, it uses the same palette as is loaded at the beginning of the scene, with no code to alter the other colors. I could probably just not use Palette 0 for the characters though; two of them have similar coloring.

(I wouldn't mind changing the background palettes halfway through as well.)



https://drive.google.com/file/d/1WmnktVdIPMQMqQ90Th4Erl8OYzfMINov/view?usp=sharing
https://drive.google.com/file/d/1RLKH5YW9HiIF6hYA16F6LJadHDAm7XOH/view?usp=sharing
https://github.com/kmck/smb2/blob/master/src/prg-c-d.asm
https://github.com/Xkeeper0/smb2/blob/master/src/prg-c-d.asm



Here's something unrelated.

I've been working on a fairly simple hack of Bomberman. The title screen graphic tiles are presented in full without any other trickery, which made things easier when assembling a complex title screen. The title screen text is coded backward for some reason.

I'd like to change the attributes on the screen to allow different palettes to be used. (I'd like to change palettes of the player character at right.)

Unfortunately this is coded in a limiting way. There's code around 1EFA, ending at 1F07. I think it's just two short commands which draw the attribute data (some 00s and some 55s). Which is very limiting, and I'm really not sure how to edit this and get the result I'd like, which is more complex than just dividing the screen vertically. Maybe it's just not possible.


Hmm
https://github.com/re4mat/bomberman-nes/



ANOTHER unrelated thing ...


I'm also working on a hack of the Zapper game Gumshoe as "Inspector Gadget."

There's a couple things I'm stuck on. The main one is:

I would like to switch which CHRs are loaded for the title screen's background- either that or switch what's loaded for the last level. It currently loads bank 7 (the eighth bank) for both. I'd like to load bank 8 or 9 to allow for a complex custom title screen.

EDIT: Fixed by Cyneprepou4uk!  It is working in FCEUX and Nestopia but not Mesen for some reason.

EDIT: This was a mapper issue -- He chose to switch from Mapper 66 to Mapper 11 as a solution.

(I will also have to do some hijacks to allow for a more complex title screen construction, currently at 19B23. Not sure how to do that.)

EDIT:  I've solved this, although it needs more work!

The title screen construction is currently at 19B23 / 9B13, via a partial disassembly.

At 1AD0B / ACFB is some empty space.

At 187CB in the ROM, I can change 139B to FBAC to point to the new title location.

Now, there is some code which displays the top score onscreen which I need to find.

EDIT: Got it. This is at 18823.


Also:

Around 162D0 is some tile layout for the ending scene where you confront the villain. It's a strange coding I've seen before, where the tiles are in 4x4 blocks. Only the tiles and their palettes are listed here, not their locations onscreen. I'm not sure where that is. I was planning to include the villain's cat as 3x2 tiles, either on the desk or above it. The layout includes a 2x4 (tall) section, but nothing 3x2, and I don't know where the code is to set tile position onscreen, so currently part of the cat is missing.

Around 105CA is some ending text, and other text around 19C14. I think there are separate text pointers also somewhere, which makes it hard to rewrite the ending text unless it's all the same length. Changing length broke the text display.

ALSO: There is an unused music track for (Gumshoe's) level 2, which is basically just "Walking On Sunshine." It's used in the arcade VS version. It's apparently in the NES version as track #1, and unused. I'd like to put it back in, for the heck of it.

I think we could also make the game easier and more interesting if every bird, or most birds, dispensed powerups like the goony-looking Lucky Birds do. They're very rare in the game, turning up very rarely outside of Level 1's bonus area which has five, so you don't have much of a chance to get their powerups in the game.

The idea that you need 5 pink potions to become green is ridiculous -- 2 pink potions would be better, as there is a chance it might happen at all.

Quality of life tweaks: "The part right before the diamond in the sea level broke me. Spent maybe 30 minutes trying to get past the projectiles with zero bullets. Would be cool to have more balloons there, and also early in level 4, if that's possible."
« Last Edit: May 15, 2022, 07:07:54 am by tygerbug »

bogaabogaa

  • Full Member
  • ***
  • Posts: 237
    • View Profile
Re: Super Mario Bros 2 USA hacking questions?
« Reply #10 on: May 08, 2022, 07:38:35 am »
This was a topic here and I made a patch for the eye possition. https://www.romhacking.net/forum/index.php?topic=30389.msg393290#msg393290

I did not read the progress but you need to write code for the eye postiotion and I think I explained how to change the offset in my patch.
CV ROM DiscordServer
https://discord.gg/PvFgxRg

tygerbug

  • Full Member
  • ***
  • Posts: 101
    • View Profile
Re: Super Mario Bros 2 USA hacking questions?
« Reply #11 on: May 15, 2022, 07:10:28 am »
UPDATED -- things I haven't figured out yet:

SUPER MARIO BROS 2 USA:

I would like to edit Ostro (Ostrich character) to always use palette 2 or 3. That doesn't seem to be an option for Ostro or most common enemies.

(Judging from SMB2 editors, I think common enemies just exist in multiple forms, but Ostro doesn't.)

I would also like FryGuy (and his flames) and Clawgrip (and his rocks) to always use palette 2 or 3 instead of palette 1.

This is less of a problem since they're self-contained, but it's clear that I've edited palette 1 in these scenes.

(Palette 0 is player palette, palette 1 is red/white and used for lots of game elements.)

It would also be nice for the end credit palette (which uses palette 0) to be editable. Apart from forcing white, it uses the same palette as is loaded at the beginning of the scene, with no code to alter the other colors. I could probably just not use Palette 0 for the characters though; two of them have similar coloring.

(I wouldn't mind changing the background palettes halfway through as well.)

https://drive.google.com/file/d/1WmnktVdIPMQMqQ90Th4Erl8OYzfMINov/view?usp=sharing
https://drive.google.com/file/d/1RLKH5YW9HiIF6hYA16F6LJadHDAm7XOH/view?usp=sharing
https://github.com/kmck/smb2/blob/master/src/prg-c-d.asm
https://github.com/Xkeeper0/smb2/blob/master/src/prg-c-d.asm



BOMBERMAN:

I'd like to change the attributes on the title screen to allow different palettes to be used. (I'd like to change palettes of the player character at right.)

Unfortunately this is coded in a limiting way. There's code around 1EFA, ending at 1F07. I think it's just two short commands which draw the attribute data (some 00s and some 55s). Which is very limiting, and I'm really not sure how to edit this and get the result I'd like, which is more complex than just dividing the screen vertically. Maybe it's just not possible.



GUMSHOE:

Around 162D0 is some tile layout for the ending scene where you confront the villain. The tiles are in 4x4 blocks. Only the tiles and their palettes are listed here, not their locations onscreen. I'm not sure where that is. I was planning to include the villain's cat as 3x2 tiles, either on the desk or above it. The layout includes a 2x4 (tall) section, but nothing 3x2, and I don't know where the code is to set tile position onscreen, so currently part of the cat is missing.

Around 105CA is some ending text, and other text around 19C14. I think there are separate text pointers also somewhere, which makes it hard to rewrite the ending text unless it's all the same length. Changing length broke the text display.

There is an unused music track for (Gumshoe's) level 2, which is basically just "Walking On Sunshine." It's used in the arcade VS version. It's apparently in the NES version as track #1, and unused. I'd like to put it back in, for the heck of it.

I think we could also make the game easier and more interesting if every bird, or most birds, dispensed powerups like the goony-looking Lucky Birds do. They're very rare in the game, turning up very rarely outside of Level 1's bonus area which has five, so you don't have much of a chance to get their powerups in the game.

The idea that you need 5 pink potions to become green is ridiculous -- 2 pink potions would be better, as there is a chance it might happen at all.

Quality of life tweaks: "The part right before the diamond in the sea level broke me. Spent maybe 30 minutes trying to get past the projectiles with zero bullets. Would be cool to have more balloons there, and also early in level 4, if that's possible."