Patching SNES ROMs Directly from Assembly

I enjoy embedded development and this page is here to help pass along some thoughts and practical experience I gained, while helping my son make code and data modifications to one of the SNES ROM games called Dragon Quest III. I modified an assembler/linker tool so that I could encourage him to stop hand-coding and start using a symbolic assembler tool. I hope my very small effort may be helpful to someone else.

My son is working on a long, drawn out SNES ROM patching project. He's changing the level-up logic and adding some additional pop-up displays and input methods. This involves everything from small patches of specific subroutines and data tables to larger and new subroutines and expanded and new data tables. It represents months of disassembly, simulation, code and data table changes and additions, testing, and so on. He started out using Lua as his "tool of choice." He hand-assembles all of the code into data bytes, using Lua to patch things but not to assemble things for him. As the project has become larger and larger, he is ending up wasting lots of time hand-assembling code; making errors in the process that would have to be uncovered later through simulation and execution of the resulting ROM. It's not unusual to start out small and resort to hand-coding. But as a project blooms, the coding "error-rate" quickly becomes crippling. In trying to encourage my son to move to an assembler tool, I modified an existing one to make it a little easier to apply to SNES ROM files. And perhaps my small efforts may also help someone else.

Some of the links below are intended to be helpful but aren't directly useful for writing assembly code. For example, I've included a link to HashTab and Hasher so that you may more easily generate MD5 codes that are useful in verifying that a file you have is the same file that someone else has. I've included links to a couple of simulators that may be useful. Some links for documentation on the WDC 6502 and its derivatives are included, plus some links to documentation on the kind of assembly code source formats that Merlin32 and ASMPATCH accepts.

I'm releasing the newest modified version of Merlin32, ASMPATCH v2.0. I've given the MD5 hash code for the ZIP file. (The ZIP file includes a hash.txt file inside, providing hash codes for the included executable.) Version v1.0(d) supports an enhanced linker file and patches ROMs directly from the assembled source code. It only modifies the ROM when directives, code, and data items generate one or more bytes. You can use ORG and DS in your assembly source code in order to skip over areas and leave them unmodified, while still creating symbols representing addresses you can use elsewhere. (In a more sophisticated assembler/linker toolset, this would be a feature that is normally available. But those often have a longer learning curve, too. This is a very simple assembler/linker tool that is very easy to use and yet works quite well for ROM patching.)

     Related Pages

  Linker concepts needed to understand ASMPATCH's approach to patching ROMs ASMPATCH: ROM Patching Concepts  
  Modifications used to create the ASMPATCH tool from Merlin32 source code ASMPATCH: Modifications to Merlin32  
  Quick command line DISASM tool (64 bit) DISASM: Disassembler tool description  

 

Why ASMPATCH, in Particular?

There are quite a few tools available for writing SNES assembly code for the Western Design Center's 65c816 core. And better ones, depending upon your criteria. My particular interest was to find an assembler tool that accepted some well-documented input source code, was itself in open source and could be easily re-compiled using free tools on Windows, supported multiple source files, symbolics, macros, the usual linking/relocation features, and most especially could be used to develop code for mapping hardware used with ROMs. The source code also needed to be easy to understand and modify. It would be a big plus if the assembler/linker project was also currently in active support. I was also on a bit of a short schedule for all this and had to be able to see immediately (or nearly so) that the tool could be adapted per the above requirements. It couldn't be an overly complex study to get there.

These details helped me eliminate some of the tools, each for one or more reasons, and readily brought me to the Merlin32 tool found at a web site managed by Antoine Vignau and Olivier Zardini. The source code was available, the tool did a fair job of duplicating an earlier Merlin 16+ commercial product that was well documented already, it supported relocation and linking services, was in active support, and the source code was easily understood and dropped right into Microsoft's Visual Studio without a single problem at all. It also could already support mapping hardware, even though I'm sure it was never intended for that. If you are curious about the exact details for the changes I made to it, feel free to look at Using the ASMPATCH Assembler/Linker/Patcher. I describe the changes there.

The assembly source code accepted by ASMPATCH may be familiar to those writing code for the Apple IIgs. But there are some oddities I haven't attempted to change, yet. There is a difference between an LDA used for a direct page address operand and an LDA used for an absolute 16-bit address operand. ASMPATCH currently stays with the original Merlin16+ syntax, which uses a ':' with one and not the other to distinguish them. Some may instead expect that the number of hex digits determines this, but that's not a reliable approach when you are using symbolics. It may be possible to examine the upper bits, instead. If 0, then assume a direct page address. Otherwise, assume an absolute address. But this doesn't leave as much control in the hands of the assembly code writer. Regardless of arguments like these, I've left the syntax entirely alone. It follows the Merlin16+ syntax for the Apple IIgs, for good or bad, right now.

If you are already busy using a different assembler tool and don't prioritize my goals in the same way, then this page won't be of much interest. But if you are just starting out, struggling to begin using an assembler/linker tool for your own ROM work, then the information here may be useful.

 

The following links are discussed below and/or otherwise included here for convenience:

Assembly Tool Sites and Downloads

  ASMPATCH: My Modified Version of Olivier Zardini's Merlin32 Assembler/Linker Tool
(~145kb, executable, but in ZIP format, v2.0, MD5=E9632405F789350972CE84CAD8AAE2F7)
Download ASMPATCH v2.0 from my hosting site  
  DISASM: My Quick Disassembler Tool
(~16kb, executable, but in ZIP format, v2.0, MD5=8024BB592547D37B01A892302F475C6D)
Download DISASM v1.0 from my hosting site  
  ASMPATCH source code (modified version of Merlin32)
(~120kb, source code in ZIP format, v2.0, MD5=8B04921A66D6001488834916BA9BD9FB)
Download ASMPATCH v2.0 source code from my hosting site  
  Olivier Zardini's Merlin32 Assembler/Linker Tool Website (I started with the source code supplied here) Brutal Deluxe Software's Merlin 32 web page  
 

Merlin32/Merlin16 Assembly Source Code Documentation

  Merlin 8/16 Assembler Manual -- required reading to write assembly source code (~12Mb) Download Merlin 8/16 Assembler PDF Manual  
  Merlin 16+ Supplementary Manual -- required reading to write assembly source code (~6Mb) Download Merlin 16+ Supplementary PDF Manual  
  "Programming the 65816: Including the 6502, 65C02, and 68802"
by David Eyes and Ron Lichty, redistributed by WDC at the WDC site (~50Mb, full resolution)
Download full res PDF of "Programming the 65816"  
  "Programming the 65816: Including the 6502, 65C02, and 68802"
by David Eyes and Ron Lichty, copyrighted to WDC in 2007 (~2Mb, .edu site, clean text and searching)
Download clean text PDF of "Programming the 65816" (.edu)  
  "Programming the 65816: Including the 6502, 65C02, and 68802"
by David Eyes and Ron Lichty, copyrighted to WDC in 2007 (~2Mb, nesdev site, clean text and searching)
Download clean text PDF of "Programming the 65816" (nesdev)  
 

Simulator Tools

  BSNES-Plus Simulator Website
(based on BSNES, open source, some focus on debugging tools, active support still exists in 2016)
BSNES-Plus Simulator web page  
  byuu's Higan Simulator Website
(open source, still active in 2016; you will need 7zip to unpack the download there)
Higan Simulator's web page  
 

Other Potentially Related Tools

   7zip Download Website (open source, use with the Higan emulator) 7zip Download web page  
   ImplBits' "HashTab" Website (application for Windows) ImplBits' "HashTab" web page  
   den4b's "Hasher" Website (application for Windows) den4b's "Hasher" web page  
 

Related Pages for the ASMPATCH Tool

  User's 'manual' for linker files used by the ASMPATCH tool Using the ASMPATCH Assembler/Linker/Patcher  

 

Example Using ASMPATCH

Let me drive right into an example, where I will use some prior knowledge to update an existing SNES ROM and add a "feature" to it. The SNES ROM is the Dragon Quest III (J), with applied translation patches for English (hereinafter referred to as DQ3.) If you have this, the MD5 code for it should be 1DFF04776BDE5AAE152835C0DB436F37. If not, just follow along anyway.

When in the field or in a town in DQ3, you can press "A" (as assigned to your keyboard) to open up some information on the screen. One bit of information is the amount of gold on hand. Let's say you want to expand this to include your current (X,Y) map position, too.

There is an easy way to do this. But it takes some knowledge about how DQ3 was organized inside. Luckily, I have that. In DQ3, they've written code to display the gold. But because they also display other information in other boxes, they chose to include a general-purpose subroutine that makes empty information boxes of any size. Let's call this general-purpose subroutine MAKEINFOBOX. This function needs to have some information in order to properly prepare an empty box; such as the height and width of the box and its location (x and y) on the screen. Because some specific sized and positioned empty box also needs to be filled up with something, a specialized function pointer to a subroutine is also needed. MAKEINFOBOX will subsequently call it, after the empty box is prepared and displayed. Because of all this information and because it is used in many ways and in many places, they chose to use a specific kind of "data structure" to help tell MAKEINFOBOX what it should do in each varying circumstance. In this case, when the DQ3 program wants to display the gold on the screen, it supplies MAKEINFOBOX with a reference to one of the several different data structures and then calls it to display the gold. MAKEINFOBOX then examines that data structure and does its job.

To change the pre-existing behavior to a new one, we will need to change the data structure they earlier designed. And, we will need to supply a new specialized function, so that the additional information we want can be added into the new, larger box. We will be using some pre-existing ROM functions, too. So we will also need to tell the assembler where to find them in the ROM. Finally, the assembler needs to know where to find the X and Y field position RAM locations used by DQ3 to track that information as the game unfolds. But that's all -- just three basic steps. Two files will be needed for this: the source code file and the linker file. The linker file will tell the assembler about the ROM to be patched and the name of the source code file. The source code file does all the rest, telling the assembler about the updated data and needed code.

Here is the linker file (I called it GOLD.M32):

            RMI     A1.SMC
            RMO     A2.SMC
            MAP     4MB $C00000 $FFFFFF
            MAP     2MB $400000 $5FFFFF
            
            SEG     GOLD
            ROM     4MB
            ORG     $C00000
            ASM     GOLD.ASM

It's not much. RMI and RMO directives specify the input ROM file's name (the one you want to patch) and the output ROM file's name (the one you want to create, using the input ROM to start out.) Those two are nothing more than a simple way of specifying what you want to do with your ROM files. If you only specify the RMI command, then all the patches will be directly applied to that ROM and no copy will be made. The two MAP directives as shown describe the ROM file layout for DQ3. In this case, they specify the memory sections that were set up when the English translation patches were added (it used to be a 4Mb ROM but was converted to a 6Mb ROM once the language patch was added.) That would be typical of any 6Mb ROM. But you can adapt the MAP commands to fit any useful ROM file format I know about, right now. (This includes those ROM environments where A15 [an "address line"] is stripped out and the upper address lines are "lane shifted" down by one position.) It's not important that the names of the regions look like numbers. I used '4MB' and '2MB' as semi-descriptive names for the ROM sections, but they could be any name you want to use and they don't have to include digits.

The remaining lines collectively describe one patch segment. The SEG directive is used to start a new patch segment. (You may also use the older directive name, DSK, if you prefer.) The patch segment name isn't terribly important. But it needs to be unique. The ROM directive identifies which "MAP" segment is supposed to receive the patch segment. In this case, it specifies the first section, since that is the only one getting patched in this example. The ORG directive specifies where to place the patch within the named ROM MAP section. This is useful for relocatable assembly source code files, but in this case the source code specifies the locations so this value doesn't matter much since it will get over-ridden by the source code. (The only remaining way it matters is that it sets the lowest allowable address. So none of the generated bytes from GOLD.ASM can use addresses lower than this. But that isn't a problem as the 4MB segment doesn't accept anything lower, either.) The ASM directive just lists out the assembly files to be applied in a patch segment. You can list more than one ASM per named SEG segment, if you want to use mutliple assembly source code files for a single patch. Here, I only use one ASM because that's all I needed.

For additional details on linker files, see Using the ASMPATCH Assembler/Linker/Patcher.

Now here's the source code file (should be named GOLD.ASM, since the above linker file uses that name):

            MX      %00        ; 16-bit X, Y and accumulator registers

      ; The following are special values for characters we need to display.

CHR_G       EQU     $97
CHR_X       EQU     $23
CHR_Y       EQU     $24

      ; Establish the existing ROM locations for needed functions.

            ORG     $C34EF2
GETGOLD     ANOP
            ORG     $C32C9E
PRINTNUM    ANOP
            ORG     $C32B8D
PUTCHAR     ANOP
            ORG     $C35512
NEWLINE     ANOP
            ORG     $C34EEB
STORENUM    ANOP
            ORG     $C300FC
GOLDDATASTR ANOP
            ORG     $C3F400
PATCHSPACE  ANOP

      ; Establish the existing RAM locations for the field X and Y values.

            ORG     $7EC725
POS_FIELDX  DS      $2         ; Player's Town or Field X (2-byte word value)
            DS      $3E        ; (skip over span of intervening 62 bytes of data)
POS_FIELDY  DS      $2         ; Player's Town or Field Y (2-byte word value)

      ; The following macro modifies the pre-existing gold info box description data.

ADJBOX      MAC
            ADR     {#]2+#]1&#$1F}.{#]3*#$20+#]1&#$3E0}.{#]4*#$400+#]1&#$7C00}.{#]5*#$8000+#]1&#$F8000}.{#]1&#$F00000}
            EOM

      ; Update the gold display box information.

            ORG     $C300FC
            ADJBOX  $51A615;0;-2;0;2
            DS      6
            DA      NEWDISP    ; 2-byte address (no bank) of specialized function

      ; This is our specialized function needed to display the gold and the X,Y position.
      ; Upon entry here, the empty box is already displayed and the print position is
      ; correctly set to the upper left corner of the box. Also, the data bank is already
      ; set to $7E, so our LDA instructions below work correctly when using only 16-bit
      ; addresses (the lower 16-bits of the 24-bit address for POS_FIELDX and POS_FIELDY.)

            ORG     $C3F400
NEWDISP     JSL     GETGOLD    ; Fetch the value of gold on hand, making it ready to print.
            LDX     #$0006     ; Ask for 6 decimal digits.
            JSL     PRINTNUM   ; Print the value, now (gold.)
            LDA     #CHR_G     ; Ask to print a "G".
            JSL     PUTCHAR    ; Print a character (the "G".)
            JSL     NEWLINE    ; Advance to the next line in the box.
            LDA     POS_FIELDX ; Fetch the 16-bit X position of our character.
            JSL     STORENUM   ; Save the value, making it ready to print.
            LDX     #$0006     ; Ask for 6 decimal digits.
            JSL     PRINTNUM   ; Print the value, now (X position.)
            LDA     #CHR_X     ; Ask to print an "X".
            JSL     PUTCHAR    ; Print a character (the "X".)
            JSL     NEWLINE    ; Advance to the next line in the box.
            LDA     POS_FIELDY ; Fetch the 16-bit Y position of our character.
            JSL     STORENUM   ; Save the value, making it ready to print.
            LDX     #$0006     ; Ask for 6 decimal digits.
            JSL     PRINTNUM   ; Print the value, now (Y position.)
            LDA     #CHR_Y     ; Ask to print a "Y".
            JML     PUTCHAR    ; Print a character (the "Y") and return.

That's it. At this point, I just type:

ASMPATCH GOLD.M32

Prior to that, I had placed the DQ3 ROM file in the same directory and named it A1.SMC. (You can see that name in the RMI directive in step 4.) So this command then generates A2.SMC from the A1.SMC file, plus all the patches we just wrote above. You can now actually load up and run A2.SMC and it should do what I just described for you. Try it out! Well, perhaps before you try it out, see if the MD5 code is FD27A00D1553DE6B30F5B7FA1C71799B. That's what I get for my resulting ROM file.

You could also write:

ASMPATCH -V GOLD.M32

The -V makes ASMPATCH "verbose," which means it will generate very verbose assembler listing files for you if you want them. You can use it, or not. It's optional. But at times it may help to see what was generated.

There is also a -Q ("quiet") mode, which eliminates a lot of the extraneous messages but keeps the more important ones.

Here's the sample output from the above run using -Q mode:

asmpatch v 2.0, (c) Brutal Deluxe 2011-2016 and (c) New World Computing Services 2016
  + Loading Link file...
  + Assemble project files for Segment #01 :
        - GOLD.ASM
  + Link project files for Segment #01...
  + Copying ROM 'A1.SMC' to ROM 'A2.SMC'...
  + Patching ROM 'A2.SMC' ...
    o Applied segment C3/00FC to C3/00FE ($3 bytes) of patch GOLD to ROM section 4MB
    o Applied segment C3/0105 to C3/0106 ($2 bytes) of patch GOLD to ROM section 4MB
    o Applied segment C3/F400 to C3/F443 ($44 bytes) of patch GOLD to ROM section 4MB

The most important part are the three lines after "+ Patching ROM 'A2.SMC' ...", which indicates the exact patching details taking place. These should match your expectations. As you can also see, there wasn't that much changed or added to the ROM.

 

Example Notes

I used some EQU values inside of GOLD.ASM to specify a few character values I needed. Better practice would be to provide a file where many more of these kinds of useful values can be archived and shared with other assembly source code files. You might create a file called CHAR.INC, for example, and place one EQU directive for each and every character you may ever want to use. (The values are not ASCII, in general.) Then, in each assembly file that may need one or more of them, use the PUT directive (found in the documentation at the top of this page) to include all those EQU directives so that your assembly source code can then use them, freely. I did what was expedient here. But it is usually better to place these into a file you can share with other source code files. I also put some information about the ROM; existing subroutine and data locations. If you had the complete source code extracted from the ROM, and if that source code could be used in order to completely re-create the original ROM, you wouldn't need to add these. The assembler/linker toolset would automatically generate all those labels for you from all of that incredible amount of source code needed to generate the original ROM. But the fact of life is that you almost never will have all of that source code. It is a lot easier to write, JSL PRINTNUM, than it is to have to remember to instead write, JSL $C32C9E! So use the assembler directives to help you, here. (ANOP isn't strictly required. You can use the label without ANOP and achieve the same results. You may also use EQU instead of the ORG/ANOP combination.)

 

More to Come?

Yes. But this is a sampler. Hopefully, the above example doesn't seem too complex. Most of the difficulty in doing something similar is about learning how the code and data is set up for some game. That takes a little time to achieve and the above skips over that part of the work. But it does give you an idea of how easy it is to make a modification, if you have the detailed knowledge of how a game operates inside. ROM hackers gain this information in a variety of ways, with disassembly and debugger tools being a few of several. But ASMPATCH permits you to apply your knowledge quickly and easily.

 

Last updated 5/17/2016, 16:00 UT. You may contact me at mail: jonk@infinitefactors.org