5

I have a hobby project requiring adding some code to Super Mario Bros NES file.

Specifically I want to display custom text (at least 22 letters and numbers, but more likely above 100 characters) after completing each level. From what I see this cannot be done by simple text replacement inside the file. So I figure I'll write separate routine in machine code to display the text (using tiles from CHR part of the ROM). Then I plan to put this routine somewhere...

My questions are:

  1. Is there any simpler way to achieve this?

  2. If not: where can I add my code? From my understanding of this code: https://gist.github.com/1wErt3r/4048722 the game, after initialization, enters infinite loop and the entire logic happen in interrupt handler. My idea was to change the interrupt vector NMI pointer from original 0x8082 to some other address containing my code, check if this is the start-level screen and the level number is greater that one, display my text and jump to original 0x8082 address...

    • (a) Will this work?
    • (b) Where can I put my code? I noticed in hex editor that from 0x56b0 - 0x56d0 there are FFs only, I'm going to check if this is simply free space left. If not I would have to add additional 16K bank of program space (change header byte from 02 to 03 in NES file to indicate this) but I suspect that requires more adjustment?

Please advise if this approach is feasible.

UPDATE

I reduced the scope of this "project" to simply show "victory screen" after completing the first level (1-1). I can replace the texts showed on victory screen easily. The problem is to find the branch instruction that enters the victory routine and to enter it after the first level (instead of 8-4). Adittionally, some memory bytes and registers have to be set to the values the routine expects, because just jumping there after incrementing level counter crashes the game...

14
  • 1
    Interesting question. While 6502 coding should be on topic, the application sounds like someone needing specific NES ROM hacking, and modifying and distributing NES ROM files sounds awfully like copyright infringement.
    – Justme
    Commented Nov 11 at 17:55
  • 1
    @Justme The de facto standard in the romhacking community is to distribute these sorts of things as IPS-format patch files and rely on the downloader to have a copy of the game and something like sanni's Open Source Cartridge Reader.
    – ssokolow
    Commented Nov 12 at 6:10
  • 2
    @Justme I should point out that, in the case of SMB specifically, cartridges aren't the only place you can find a legally distributed ROM. For example, it's present as cut-but-not-deleted-content (i.e. obtainable by editing the inventory in your save game) in Animal Crossing and you can extract the ROM files from your copy if you've got a Wii with Homebrew Channel and CleanRip installed and a tool for exploring Gamecube disc images.
    – ssokolow
    Commented Nov 12 at 8:22
  • 1
    @ssokolow I get that, but a random AliExpress purchase is an unlikely legal source for an original high-profile Nintendo title.
    – Justme
    Commented Nov 12 at 11:05
  • 1
    @Justme ,ssokolow, First of all, this question is not about legal issues of NES file ownership. We are talking about patching some 6502 code as hobby excercise. Secondly, offtopic, I'll be happy to "legalize" my copy of NES file, for reasonable price, if you could point me to some product page where I can buy it or something that effectivile allow me to own original file: used, second market cartrige? I own Nintendo Switch from 2021 and used Nintendo 3DS - maybe these allow to download smb mario using some subscription connected to the devices?
    – PanJanek
    Commented Nov 13 at 6:03

2 Answers 2

3

SOLUTION

Finally: at file offset 0x3328 (routine called after the level is completed) I replaced the original instruction:

 JSR 9c03  ;20039c call subroutine LoadAreaPointer

with JSR to seemingly free space address (file:56AE, memory:D69E):

 JSR d69e  ;209ed6 run routine at free space where custom code in inserted

There, at 0x3328 I put:

lda #03    ;a903
sta $0770  ;8d5c07     LevelNumber=3, 075c
sta $0760  ;8d6007     AreaNumber=3, 0760
lda #07    ;a907
sta $075f  ;8d5f07     WorldNumber=7, 075
jmp $9c03  ;4c039c     JMP to address of original JSR jump (LoadAreaPointer)

This makes tha game to go to last level (8-4) after the first one. This is as far as I was willing to play with this problem. Not sure if 16 bytes taken by custom code at file offset 56AE are really free, but the hack works. This removes all the middle levels from the game, so, alternatively, middle levels map data could be used for custom code.

Final note

As I suspected, the version of ROM NES file I was working on is probably one of many and could have been already modified. In this version at offset 56AE there are 59 FF bytes that are somehow free. In more canonical versions this part of the file contains some data and the above hack probably will break the game. Still, by using different offset than 56AE (for example level 3-3 data that becomes unreachable anyway: 0x1E6F) the hack still works

4
  • When free space is crucial, your "patch" should be as short as possible, for example: Change "JSR 9c03" to "JSR d69e" and then: lda #03 ;a903 sta $075C ;8d5c07 LevelNumber=3 sta $0760 ;8d6007 AreaNumber=3 lda #07 ;a907 sta $075F ;8d5f07 WorldNumber=7 JMP 9c03 ;20039c overwritten original instruction
    – ufok
    Commented Nov 15 at 6:42
  • @ufok you mean to change my jmp to jsr and then return with rts, which is 2 bytes shorter? But that takes 2 bytes more of stack, which may be not safe...
    – PanJanek
    Commented Nov 15 at 8:04
  • 1
    change original JSR address to your patch, then insteed of "JSR 9c03; JMP b313" do only "JMP 9c03" (original JSR destination). Oryginal coroutine will execute RTS at end. Nothing will change on stack - even return address pushed on stack :) Second change is only one "lda #03" and changed order of STA. You will save 2 instructions (one LDA and JMP on end of your code) and keep oryginal stack values.
    – ufok
    Commented Nov 15 at 8:56
  • @ufok clever! I'll do that and update my post.
    – PanJanek
    Commented Nov 15 at 9:05
0

I don't know that there's going to be any space left over in a title like SMB.

ROM was pricey and using more of it required coding the game to use a mapper chip to address more. And SMB 1 didn't have any mapper chip and was known for being very long for the time.

Chances are Super Mario Brothers took every last byte it could and there's very little space in which to cram more code into the layout of ROM without rebuilding a lot of the game.

8
  • Interesting hypothesis, but have you checked if it’s true? Besides, it’s not necessarily a show-stopper, especially if the end result will only ever be run in an emulator. Commented Nov 11 at 19:29
  • Yeah, there's the other side of the argument: ROMs need to be some power of two in size, so it's very improbable that every byte is in use. Obviously if you knew you were targetting a 32kb ROM (or whatever Super Mario uses) then you'd keep adding content until you ran out of space — and probably until after you ran out of space, subsequently having to trim to get back under — but it's still unlikely you'd exactly hit the target.
    – Tommy
    Commented Nov 11 at 20:29
  • 40KB in this case (32KB 'Program' 8KB 'Character'.) I think I've seen an interview quote that they left 100B empty in the map until the end in case there were bugs, which seemed to imply that some of that got used later. My assumption is that there's no guarantee that the remaining spaces in the map are in a space large enough to fit a subroutine or that there's room and proximity to jump to that subroutine where you need it to run in that code. If you want hooks I'd look for a randomizer and how it got in.
    – davolfman
    Commented Nov 11 at 21:18
  • In fact I don't need levels after the first one, so I can overwrite them. Additonally, maybe it is easier to replace the victory screen and change the code to show the victory after the first level? this would still work for me.
    – PanJanek
    Commented Nov 12 at 7:42
  • 1
    @user3840170: I don't remember the exact full tune that was composed for the victory screen, but if music is stored using two bytes per note (I think it would more likely use one byte for short notes), putting in the full tune would have taken less than 100 extra bytes. It may be possible to achieve the stated objective if one removes parts of the game one isn't interested in (e.g. if one simplifies the title screen and does some graphic replacements), but such patching requires skill and experience beyond what someone asking about it would likely have.
    – supercat
    Commented Nov 12 at 17:10

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .