(Some technical notes for the terminally curious) ############################################################################## SNESAdvance.EXE ############################################################################## All data required to build SNES Advance is internal to SNESAdvance.EXE. You can insert your own data by placing any of the following files in the same directory as SNESAdvance.EXE: snesadvance.bin =emulator binary background.bin =256 color background (240x160) background.pal =palette for background.bin font.bin =16 color 16x16 font (ascii chars 32-??) font.pal =font palette Gfx data (except font.pal) must be LZ77 compressed with a VRAM-safe compression algorithm. (GBACrusher does this, if you can find it) ############################################################################## SNESAdvance.GBA format ############################################################################## ???: snesadvance.bin 4 bytes: background.bin size ???: background.bin 4 bytes: background.pal size ???: background.pal 4 bytes: font.bin size ???: font.bin 4 bytes: font.pal size ???: font.pal 4 bytes: total number of roms ???: rom data (repeat until EOF) rom data -------- 32 bytes: rom title 4 bytes: rom size 4 bytes: rom CRC32 8 bytes: rom flags 4 bytes: autoscroll address 1 4 bytes: autoscroll address 2 4 bytes: autoscroll scale 4 bytes: autoscroll (signed) offset ???: rom contents (no header) rom flags --------- bit0: 0=Mode20 (LoROM), 1=Mode21 (HiROM) bit1: 0=NTSC, 1=PAL bit2: 0=SRAM disable, 1=SRAM enable bit3: 0=scroll by (addr1) 1=scroll by (addr1)-(addr2) bit4: 1=autoscroll value is a 16 bit number bits 16-31: "more settings" bits 32-63: "even more settings" ############################################################################## More about rom flags ############################################################################## ---- "More settings" (fix priority) 000ZYXNNNNNNNNNN N=blank tile number X=double BG1 Y=double BG2 Z=force BG3 low priority The SNES allows you to set the priority of individual tiles in a background layer; unfortunately the GBA doesn't. This tries to fix the problem by using two GBA backgrounds to simulate one on the SNES. Lower priority tiles are placed in one layer, higher priorities in the other. You can use this trick for BG1 or BG2 but not both, since the GBA has only one extra background to spare. "Blank tile number" should be self-explanatory, this should be set to the number of a blank tile (often 0, but not always) when you use the double BG stuff . You can use VisualBoyAdvance's tile viewer to find this (char base 0x6000000, click on a tile to see its number). ---- "Even more settings" (fix corrupt gfx) OBJC BG3C BG2C BG1C XY00NNNN XY00NNNN XY00NNNN XY00NNNN N=size decrease X=from top/bottom Y=priority Part of the SNES Advance debug info shows how SNES VRAM is laid out, representing it with a 32-bit bitmap; 1 bit per 2Kb of VRAM (read the Rom Patching section below for more info on using the debug version). BG?C, OBJC is tile data for backgrounds/sprites. BG?S is screen data (contains the tile layout of each background). When different data types overlap, SNES Advance has no way of knowing how they should be interpreted. This gives you a way to fix the problem by allowing you to resize VRAM areas. Example: BG1C=0000FFFF BG2C=0000FFFF BG3C=000FF000 BG1+2 both use 16-color tiles so are safe to overlap; BG3 uses 4-color tiles. By default, BG3C overwrites the last part of BG1+2C. To make it go the other way (give full 32Kb to BG1+2, and 8Kb to BG3) you can either give BG1/2C higher priority (0x00004040) or resize BG3C (0x00840000). 0x00004040 = 00000000 00000000 01000000 01000000 ^ ^ | | give BG1+2 priority --------+--------+ 0x00840000 = 00000000 10000100 00000000 00000000 ^ ^^^^ | | | +--subtract 4 2Kb 'blocks' +--------from bottom of BG3C Another example: BG1C=0FFFF000 OBJC=0000FF00 Both are 16-color, but because sprite and background tiles are stored in different areas on the GBA, these can't overlap. OBJC overwrites BG1C here; if it needs to go the other way, use 0x04000000 or 0x00000040. ############################################################################## Autoscrolling ############################################################################## The SNES display field is larger than the GBA's. To overcome this problem, a technique called autoscrolling is used. The goal of autoscrolling is to automatically move the screen to stay centered on the main character. The general formula for autoscrolling is: scroll position = [Addr1] * scale% + offset OR = ([Addr1]-[addr2]) * scale% + offset (In detail) Addr1: SNES address pointing to the vertical position of main character. Addr2: Subtracted from the first value (explained below). Scale: How much the above value will affect the scroll position. Offset: Moves the scroll position by a fixed amount. The values referenced by addr1,2 can be either 8 or 16 bits in size. Some games only store the player's position relative to the entire playfield rather than the visible screen. For example, the player's Y position might range from 0-2047 instead of 0-239 if a game's map is particularly large, rendering the first autoscrolling method useless. If this is the case, you can correct the problem with so-called "difference" autoscrolling by finding where the game stores the map's scroll position and subtracting that from the player position. If you find that the screen jumps periodically as you move up and down using standard autoscrolling, you probably need this. (sorry if that was confusing, it's hard to explain) ############################################################################## SNESAdvance.DAT ############################################################################## SNES Advance stores rom settings in snesadvance.dat. Lines are formatted like this: CRC32|title|flags1|flags2|autoscroll1|autoscroll2|scale|offset[|patches] All values are stored in hexdecimal. Patches modify the rom as it's saved. Multiple patches are separated by a comma. Example: 29573026|Whatever|0|0|0|0|0|0|0080=40,1080=012345,AAAA=42 Meaning: rom[0x0080]=0x40 rom[0x1080]=0x01 rom[0x1081]=0x23 rom[0x1082]=0x45 rom[0xAAAA]=0x42 ############################################################################## Rom patching ############################################################################## There are two reasons you would want to patch a ROM. One is to skip over code that's preventing a game from running. For example, a game might lock up when waiting for a response from the SPC (which isn't emulated yet) so you would patch it to skip past this. The other reason for patching is to speed up a game. All SNES games need to synchronize themselves with the TV's refresh rate. When a game has finished processing the current frame, it enters an idle state - a period when the CPU does nothing except wait for the next frame to begin. "Speed hacking" is a way to tell the emulator that processing is done for the current frame, so it can skip to the next and not waste time emulating these idle loops. The debug build of SNES Advance is very useful for creating both kinds of patches; you can use it to follow the execution of the SNES CPU. Run the debug version of SNES Advance using your favorite GBA emulator and go to address 0x2030000 in its memory viewer. The SNES CPU state is dumped to memory: 2030000 = A reg 2030004 = X reg 2030008 = Y reg 203000C = D reg 2030010 = S reg 2030014 = PC reg 2030018 = Flags 203001C = Cycle counter 2030020 = Next 8 bytes at PC 2030028 = Rom offset 203002C = Frame counter L button: Step one instruction at a time R (hold): Run continuously (faster) L+R (hold): Run continuously (slower) --- Example 1: Build a SNES Advance rom of Super Ghouls'N'Ghosts. Make sure it's _NOT_ already patched (rename SNESADVANCE.DAT to something else, or move it to a different folder). Start the game up in VisualBoyAdvance. Go to VBA's Tools menu and choose Memory Viewer. Select Automatic Update, 8-bit, enter 2030000 into the address box, and click the Go button. Now click back to the main VBA window. Hold the R button for a while (GBA R, not keyboard R). You'll see lots of numbers changing in the memory window, eventually they'll fall into a repeating pattern ... it seems the game has gotten stuck. Press the L button repeatedly, you'll see that it's in a 3-instruction loop. At 2030020 you should see D0, AD, 3A, D0, AD 3A ... etc. SNES Advance doesn't have a disassembler, but it does show which bytes it's executing, so it can be disassembled by hand. Looking at 2030020 again, the byte stream is AD 40 21 3A D0 FA. An easy way to figure out where the loop starts is to switch to 32-bit view and watch the PC (at 2030014). Keep pressing L and watch the pattern. When the PC reaches its lowest value, you should be at the start of the loop. Using a table of 65816 opcodes, begin translating: AD 40 21 = LDA $2140 3A = DEC A D0 FA = BNE -6 Now it should be easy to see what needs to be done: if the branch is removed, it will get rid of the endless loop. Press L some more, to advance the PC to the offending instruction (push L until D0 FA is at 2030020). Switch to 32-bit view. At 2030028, you should see 000001D1; this is the rom location we're looking at (in other words, this where we want to modify the rom). We want to replace D0 FA with two NOP instructions (EA EA). Open SNESADVANCE.DAT (a new one should have been created) in a text editor. You should see a line in it that looks something like this: 6AABA901|Super Ghouls 'N Ghosts|0|0|0|0|0 Change it to look like this: 6AABA901|Super Ghouls 'N Ghosts|0|0|0|0|0|1D1=EAEA Close VBA, rebuild your rom, and run it again. If you did it correctly, it shouldn't freeze up anymore. Most of the time, you'll be patching sound routines just like this one. These always involve reading $214X, so they aren't very hard to spot. Be careful though; try to make sure a game is truly stuck and not just running slowly. --- Example 2: This time, we're making a speed hack. Build a SNES Advance rom of Super Mario World (again, making sure it's not already patched). Start running it until the intro starts playing (Mario is running around), then open the memory viewer like before. What you're trying to find is a loop that doesn't seem to be doing anything (idle loop, explained above). You're more likely to find these just before VBLANk. Switch to 32-bit view, you should see "LINE" in the right column, followed by a number. This number shows the current scanline. Hold L+R and wait until the line number gets near 000000F0, but not past it. Switch back to 8-bit view, and start pressing L and watch for a repeating pattern. In this case, it's a simple 2-instruction loop: A5 10 = LDA $10 F0 FC = BEQ -4 Unlike the first example, we don't want to get rid of the loop, but make it end sooner. To advance the emulator to the next event (VBlank, IRQ, whatever) two new instructions were added to SNES Advance. Opcode $42 works like a relative branch. The high nibble of the next byte is the branch type, low nibble is the branch offset (always a negative branch). For example, you'd replace D0FC with 42DC, or 80FA with 428A. Note that you can only use this when the branch is no larger than 16 bytes. Opcode $DB is another relative branch, in the range of +- 64. The 8th bit of the branch offset determines the branch type: 0 for BNE, 1 for BEQ. For example, D0EE would be replaced with DB7E. For Super Mario World, F0Fc is replaced with 42Fc, and the patch string will look like this: B19ED489|Super Mario World|0|0|0|0|0|6D=42 Note that this is a very simple example, other games have complex loops that are hard to find or patch correctly. Also, some games do calculations inside their idle loops (such as random number generators). Hacking these will alter the game behavior. Take Batman Returns for example - if you look carefully, you can see that using the provided hack alters the pattern of falling snow in the title screen. Some games can be hacked in more than one place. The $42, $DB opcodes sometimes don't branch far enough, and you'll need to make more extensive modifications. More tips: Search for hacks when not much is happening on-screen. You can be sure that more time is being spent inside idle loops here, increasing your chance of finding them. Games made by the same company may use similar code, so once you figure out how one works, doing more will be easier. Here are some common code sequences: (CAPCOM) This is characterized by an inner loop surrounded by an unconditional outer loop. baca: 9c 81 02 stz $0281 ad 7f 02 lda $027f 8d 80 02 sta $0280 a0 00 ldy #0 bad5: be 56 00 ldx $0056,y e0 04 cpx #4 b0 0d bcs $bae9 18 clc 98 tya 69 18 adc #$18 a8 tay ce 80 02 dec $0280 d0 ef bne $bad5 4c ca ba jmp $baca << patch to DBE2 (BEQ $baca) bae9: (KONAMI) 81C3: 22 95 99 00 jsl $009995 80 FA bra $81C3 << patch to 428A ... 9995: A0 00 1F ldy #$1f00 5A phy 2B pld A5 16 lda $16 F0 08 beq $99a6 ... 99a6: A6 2C ldx $2c D0 03 bne $99ad 4C 49 9A jmp $9a49 ... 9a49: 6B rtl (another KONAMI) - a9 00 00 LDA #$0000 5b TCD a5 42 LDA $42 d0 06 BNE + a5 6c LDA $6C 65 38 ADC $38 85 6c STA $6C + 80 f0 BRA - << patch to 4280