Category: tutorials

  • Super Metroid Beam Editing Guide

    Presented by JAM of Metroid Construction

    To see JAM’s developed sets on RA, click here. You can also follow him on YouTube.

    Original Link (now down): https://old.metroidconstruction.com/docs/JAMbeameditingguide.txt

    July 2010

    This guide explians of how to edit beams. Keep in mind that this guide will helps to find pointer and edit data, but not contain the pointers for every exiting beam/missile.

    Our target is bank $93. Imagine a tree structure. It has common roots, 3 trees, big branches, small branches and leafes. Every leaf is a sprite. Some leafes and branches are completly identical.

    Level 0 (roots) is level of projectile type related
    Level 1 (tree) is certain beam/missile related (for each type)
    Level 2 (big branch) is shooting position related (for each beam, many related to a few beams)
    Level 3 (small branch) is size and position related (for each shooting position)
    Level 4 (leaf) is sprite related (for every frame projectile is flying)

    Level 0

    We start from common roots (level 0) and we can select 3 trees.

    For normal shots array starts at 983C1;
    for charged — at 983D9;
    for misc (missiles/super/bombs/etc.) — at 983F1.

    In my Spazer + Plasma mix patch these arrays are relocated and extended:
    normal shots array — to 9C3A1;
    charged shots array — to 9C3C1;
    missiles/super/bombs/etc. — to 9EFEA.

    Every entry is a word. Every word is a pointer to the level 1.

    Level 1

    So, for Power Beam (value 0000) pointer will be located at 83C1 and bytes there are 31 84. It means, it’s value of pointer is 8431 and it leads to $93:8431.
    For Wave Beam (value 0001) pointer will be located at 83C3 (83C3+00012) and bytes there are B5 84. It means, it’s value of pointer is 84B5 and it leads to $93:84B5. For Ice Beam (value 0002) pointer will be located at 83C7 (83C3+00022) and bytes there are 9F 84. It means, it’s value of pointer is 849F and it leads to $93:849F.

    … etc up to value 000B (included)
    In Spazer + Plasma mixing patch this goes up to value 000F (included)

    For charged shots, for charged Power Beam the pointer will be located at 83D9 there are 39 85. It means, it’s value of pointer is 8539 and it leads to $93:8539.
    … etc up to value 100B (included)
    In Spazer + Plasma mixing patch this goes up to value 100F (included)

    Same for misc, but the values of shots are starts from 0100 (Missile) and incremented by 0100 every word.
    I mean, pointer for shot with value 0100 (Missile) is located at 983F1.
    Pointer for shot with value 0200 (Super Missile) is located at 983F3 (983F1+0002).

    Let’s suppose, we fire with non-charged Spazer (value 0004).
    Level 0 : 83C1
    Level 1 : 83C1+0004*2=83C9
    Level 2 : 8447 (pointer at 83C9)

    Level 2

    When jumping to 8447, we see a lot of bytes. Let’s group them by 2:
    28 00
    77 89
    93 89
    AF 89
    CB 89
    E7 89
    E7 89
    03 8A
    1F 8A
    3B 8A
    77 89

    First word is a damage value for current beam (0028). This value is stored in the moment of shooting. Another 10 words are pointers to the level 3.

    We have shot type (non-charged), we have beam type (Spazer). Now we need to select a firing direction. Direction is based on aiming value at the moment of shooting.
    0: facing right, shooting up
    1: facing right, shooting up-right
    2: facing right, shooting right
    3: facing right, shooting down-right
    4: facing right, shooting down
    5: facing left, shooting down
    6: facing left, shooting down-left
    7: facing left, shooting left
    8: facing left, shooting up-left
    9: facing left, shooting up

    Why there are 2 values for aiming up and down? Well, the game loads the damage value first and then just loads aiming value from memory, increments it (to skip damage value), multiplies it by 2 and get pointer to level 3.

    Level 2 pointer is leading to damage value directly.
    Level 3 pointer is level 2 pointer + (aiming + 1) * 2

    If we are facing right and shooting right, aiming value will be 2. And pointer to the level 3 will be located at 8447 + (2 + 1) * 2 = 8447 + 3 * 2 = 8447 + 6 = 844D
    Word at 9844D is 89AF and this is a pointer to level 3.

    Level 3

    Here we can set actual radius of projectile, delay in frames to display current graphic before loads next graphic and pointer to sprite code. Data is grouped by 8 bytes except last entry. Let’s see what we have at 989AF:
    989AF: 02 00 42 D8 08 0C 00 00
    989B7: 02 00 6E D8 08 0C 01 00
    989BF: 02 00 CE D8 08 14 02 00
    989C7: 39 82 BF 89

    In general, we have:
    DD DD SS SS XX YY EE EE
    DD DD SS SS XX YY EE EE

    39 82 LL LL

    Where DD — delay in frames,
    SS — sprite code pointer
    XX — X radius in pixels
    YY — Y radius in pixels
    EE — entry number
    LL — pointer to loop

    39 82 is a pointer to instruction 8239, which loads 2 bytes after pointer as argument and jumps to this address. In this case, first 2 entries are used to display the sprite when it coming out of cannon and 3rd entry is used to display full-sized sprite until the beam will go offscreen or collide with something.

    As for actual X and Y radiuses, they are used to calculate collisions with objects. Visible beam sprite can be bigger or smaller.

    Note, that for the first 2 entries when the beam is coming out, it have X radius — 8 pixels and Y radius — C pixels. When the beam is came out, it have X radius 8 pixels and Y radius — 14 pixels. Just remember how Spazer shot fired to the right is acting and you’ll understand.

    And also note, that these are collision radiuses, not actual width and height. To get width and height, multiply values by 2.

    Since we have pointer to sprite code, let’s go to level 4.

    Level 4

    There is a sprite code and nothing more. It contains of amount of entries (2 bytes) and of entries itself (5 bytes each). Let’s see what we have at 9D842 (shooting part):
    02 00
    F8 01 FC 30 6C
    00 00 FC 30 6C

    In general, we have:
    AA AA
    XX PX YY TT FT
    XX PX YY TT FT

    AA — Amount of entries. 2 bytes are used for this. Each entry uses 5 bytes. So, tilemap size: AA5+2. If AA = 2, the total size of tilemap is 25+2=10+2=12 bytes.
    XX — X position of block/tile
    PX — Complex
    YY — Y position of block/tile
    TT — Tile number to display
    FT — Complex

    PX is better write in binary form
    Bits: 76543210

    If bits 7, 6 and 1 are set, then print block (selected tile and 3 more tiles to fit square made of 16*16 pixels. For example, if selected tile is $C0 and bits 7, 6 and 1 are set, then the result will be printing tile $C0, printing tile $C1 to the right of it, printing tile $D0 below tile $C0 and printing tile $D1 to the right of tile $D0).

    Else (if bits are clear) print single tile.

    Bit 0 is used as high bit for X position. Thus, you can set X from 0000 to 01FF.

    FT should also be written in binary form
    Bits: 76543210

    Bits 7 and 6 are used for flipping. 7 — for vertical flip, 6 — for horizontal flip.

    Bits 5 and 4 are used for layering. 5 — for draw the block/tile in front of layers 1 and 2, 4 — ??????

    Bits 3, 2 and 1 are used to determine used pallette row (uncormimed)?

    Bit 0 is used as high bit for tile number. Thus, you can select 512 tiles but it’s better to use this bit for direct addressing only as Missiles and Super Missiles do. I tried to use direct addressing for the beams and it looks fine in some emulators until any FX3 is appearing. When it happens, graphics from Layer 3 will overwrite your beam graphics and you’ll shoot garbled mess for all beams using this beam until reloading your game.

    Note that X ranges from 0000 to 01FF while Y ranges from 00 to FF. As for title screen (and possibly other graphic things such as asteroid belt, Ceres station in space etc.), and center of coordinates is the center of screen. NOT upper-left corner.

    TT is tile number value staring from offset $D5200. It can be from 30 to 37 (if beam was shot) or any value from 00 to FF for missiles/super/etc. That’s because Missiles, Super Missiles, Bombs etc. are using direct pointers to tiles and beams are using indirect pointers, determined by array at $843B1, depending on beam type. Every entry is a word. Every word is a pointer for graphics in bank $9A for this beam.

    For the Power Beam pointer is 00 F2 (leads to $D7200), for the Wave Beam pointer is 00 F6 (leads to $D7600), for the Ive Beam pointer is 00 F4 and so on up to beam value 000B (included).

    Tilemap for some beams can be the same. So, if you change value at $843B2 from F2 to F4 you’ll use Ice Beam graphic for Power Beam. When you fire Power Beam, the Ice Beam shot will come out but still using the palette of Power Beam. Palettes you can change by projectiler.

    You can also change pointers to free space (black squared in TLP in bank $9A) and use unique graphics for each beam combination, like D7300 or D7500.

    Note that you can use only 8 tiles for each beam combination (including changed and uncharged form).

    Return to our example.

    02 00
    F8 01 FC 30 6C
    00 00 FC 30 6C

    For Spazer graphic pointer is located at $843B1 + 0004 * 2 = 843B9. It leads to D7A00. So, tile 30 will be the first tile located at D7A00, 31 the second tile and so on.

    Let’s also see the final phase of shooting.

    Return to level 3.

    989AF: 02 00 42 D8 08 0C 00 00
    989B7: 02 00 6E D8 08 0C 01 00
    989BF: 02 00 CE D8 08 14 02 00
    989C7: 39 82 BF 89

    Let’s view CE D8 as a level 4 pointer.

    9D8CE:
    06 00
    F8 01 EC 30 6C
    F8 01 FC 30 6C
    F8 01 0C 30 6C
    00 00 0C 30 6C
    00 00 EC 30 6C
    00 00 FC 30 6C

    Change all 30s to 34 and regular Spazer shot fire to the right will be displayed as changed one (only displayed, damage value is still from uncharged Spazer).

    Change all 30s to 33 and regular Spazer shot fire to the right will looks strange. All tiles will looks like “|” instead of “-” and whe whole shot will be:
    ||

    ||

    ||

    instead of:

    Array locations

    In general, these arrays are consists of pointers or values. Each word (or byte in some arrays) is related to certain beam. Words are ordered by currently eqipped beam values:
    0000 (Power Beam)
    0001 (Wave Beam)
    0002 (Ice Beam)
    0003 (Ice + Wave Beams)
    0004 (Spazer Beam)
    0005 (Spazer + Wave Beams)
    etc.
    So, first word (or byte) in each array related to Power Beam, next word (or byte) for Wave Beam and so on…

    Sound

    Sound array is located at 8428F.
    In Spazer + Plasma mixing patch it’s relocated to 87FC0 and extended.
    Each word is a number of sound from Library 1 to play when beam is shot. Check Kej’s RandomRoutines guide to see full list.

    Behaviour

    Behaviour array is located at 8396E.
    In Spazer + Plasma mixing patch it’s relocated to 87F80 and extended.
    Each word is a pointer to code in bank $90 for execute.

    Effect is based on pointer:
    AEF3: strike solid surfances.
    B0E4: go through walls (for uncharged wave). Trails keeps for a 3 frames.
    B0C3: go through walls (for uncharged wave). Trails keeps for a 4 frames.

    This array is the root of all evil. Extending it was enough to make Spazer + Plasma combo works. And selecting Ice + Spazer + Plasma in original game make use value at 8398A as a pointer, which is not a pointer, jumping to 82D16 and executing code there, starting from wrong operator in the middle of subroutine.

    Graphic

    Graphic array is located at 843B1.
    In Spazer + Plasma mixing patch it’s relocated to 87F40 and extended.
    Each word is a pointer in bank $9A to load graphics.

    Note that you can use separate graphics for each beam combination, as there is a free space in bank $9A. Go to $D0000 in TLP and check it out.

    Palette

    Graphic array is located at 843C9.
    In Spazer + Plasma mixing patch it’s relocated to 87F20 and extended.
    Each word is a pointer in bank $90 to load palette.

    Note that you can use separate palette for each beam combination, as there is a free space in bank $90. Each beam uses $20 bytes for a palette (single palette row, 16 colors).

    Special Beam Attack values

    Arrays related to Special Beam Attack values is located at 84C21.
    In Spazer + Plasma mixing patch it’s relocated to 87F00 and extended.
    Each word is a Power Bomb value to decrement when executing Special Beam Attack.

    You can play with values and, for example make Plasma Special Attack use 5 Power Bombs. Although, code wasn’t written to use more than 1 power bombs, so if you have 1 or 2 power bombs, Plasma Special Attack still will be executed.

    If you know ASM code a bit, you can try to mess with code at 84CC0 to improve it, like display message “Not Enough Ammo” and prevent you from using Special Beam Attack if you haven’t got enough Power Bombs. Or use Missiles, Super Missiles or even Energy instead of Power Bombs.

    Special Beam Attack

    Special Beam Attack is located at 84CF0.
    In Spazer + Plasma mixing patch it’s relocated to 87EA0 and extended.
    Each word is a pointer to code in bank $90 to execute.

    Effect is based on pointer:
    CD18: do nothing.
    CD9B: execute Ice Special Attack aka Ice Ring (4 charged shots will make protective ring).
    CD1A: execute Wave Special Attack aka Wave Cross (4 charged shots will fly from screen corners to opposite corners).
    CE14: execute Spazer Special Attack aka Spazer Rain (2 shots will fly around you and then 6 shot will make a rain).
    CE98: execute Plasma Special Attack aka Plasma Sphere (4 sphears will fly around you).

    If you skilled enough you can write your own special attack for each beam combination.

    Starting speed

    Starting speed array is located at 842D0.
    In Spazer + Plasma mixing patch it’s relocated to 87EC0 and extended.
    This array is a bit different. For each beam, 2 words are used: one for vertical/horizontal speed and one for diagonal speed.

    Cooldown array 1

    Cooldown array for holding fire button is located at 84283.
    In Spazer + Plasma mixing patch it’s extended, overwriting the next array (which is also repointed, of course).
    Each byte is delay in frames between shots when you’re holding fire button.

    Cooldown array 2

    Cooldown array for tapping fire button is located at 84254.
    In Spazer + Plasma mixing patch it’s extended. Maybe, something was overwrited.
    Each byte is delay in frames between shots when you’re tapping fire button.

    Cooldown array 3

    Cooldown array for charged shots is located at 84264.
    In Spazer + Plasma mixing patch it’s extended.
    Each byte is delay in frames of how long your gun will be cooling down after firing the charged shot.

    If you have any questions, feel free to PM me.

    Credits

    Please give me a credit if you found this guide useful.

    Thanks

    DSO for finding beam color priority. It helps me understood of how beam arrays are stored.
    Kejardon for the RAM Map and other docs that helps a lot.

    Related Post

    This post has been viewed 8 time(s).

  • Porting NES Games to SNES

    By Rumbleminze on April 15, 2024. Reproduced with permission.

    Rumbleminze, a fan of all things retro tech, is co-author of the Kid Icarus Randomizer romhack and whose recent work includes porting NES games to run natively on the SNES. You can follow these and other projects via Rumbleminze’s IAGithub, YouTube, or Ko-Fi.

    Intro

    This [post] is going to go over the process I use to port NES games to the Super Nintendo. Something to keep in mind while we go through this process: I’m by no means an expert on all these things. But I do know what’s worked for me. If you have ideas on ways to improve it, I’d love to hear them! Please reach out to me on Bsky.

    Why though?

    The most frequent question I get is “why?”. There are a lot of great reasons for porting games from the NES to the SNES. Here’s a few:

    1. eliminate slowdown
    2. eliminate sprite flicker
    3. allow for MSU-1 enhancements like CD quality music and videos
    4. Take advantage of FastROM cartridge chips
    5. Up-rez-ing the sprites to 16-bit
    6. better video output on SNES instead of NES
    7. Much, much more room for romhacks/enhancements
    8. Most importantly, it’s fun!! Finding ways to port these games is, to me, like solving a big puzzle.

    The Overall Process

    To accomplish my ports, I follow this general approach:

    1. Take the original NES ROM, and break it up into it’s requisite memory banks by creating .asm files for each bank.
    2. Use a buildable SNES game skeleton, with the NES games memory banks mapped to various convienent banks in the SNES game.
    3. Hook up the SNES skeleton to jump to the NES initialization/nmi at the proper times
    4. Slowly replace routines of the NES byte code with decompiled assembly, aquired from running the NES game in Mesen2.
    5. Find and modify the NES specific logic and tile graphics to work on the nes. Especially things like:
      • All writes to Video Memory
      • Deal with differences in screen resolution / BG size / Mirroring
      • Update audio to use Membler’s 2A03 emulator
      • Properly handle bank switching

    [Editor’s note: for the remainder of this post, covering tools, goals, and limitations, please visit Rumbleminze’s blog.]

    This post has been viewed 31 time(s).

  • Anatomy of a Hack

    A “ROM hacking 101” tutorial by Cubear

    Cubear’s projects include the fan-favorite DressCode mod for Final Fantasy V, and numerous MSU-1 hacks including for Donkey Kong Country 2 and Live A Live, just to name a few. You can follow Cubear’s work via YouTubeX. or Patreon.

    What is a hack? (game modification)

    There’s hacks that adjust data, and there’s hacks that adjust code. (or code and data)

    This document is about the latter. No shade on the former, it’s valid, but it’s not what I do so I can’t comment upon the process.

    In essence, an ASM hack, assembly hacking, or whatever you want to call it, is a modification of the game’s internal code. You write a bit of code to accomplish a task, you insert it into the game, and voila! You’ve probably broken everything.  Don’t worry, it’s normal and it happens to everybody.

    All this sounds pretty simple, but it takes a bit of getting used to.. for instance, once your code is written, how do you get the game to run it? When?

    Well, to get the game to run your code you’re gonna have to hook.

    So what’s a hook? It’s a place in the code where something is working in a manner you desire, somewhere convenient, somewhere that runs when the function you are trying to alter is running, perhaps.

    So let’s look at a sample bit of code…

    PHA
    ASL
    TAX
    LDA $3100,x
    STA $3E
    PLA
    RTS

    It doesn’t matter what this is doing or not doing. I wrote it at random to be roughly representative of code you might encounter in basically any 6502 game.

    Let’s say the information in $3100,x or $3E were interesting to you. Maybe it’s a map load, or a song’s track number or a spell or item being used…  whatever you want to change, you could change it here.

    So let’s say that you’re changing the number, maybe only on a specific number, like the spell number 0x56, you want to replace with 0x6E, but otherwise let the values pass unchanged.

    Your code might look like:

    CMP #$56  Compare value in A with 0x56
    BNE +     Branch forward if it is not equal
    LDA #$6E  Load 0x6E into A
    +                 Branches merge here

    All in all a pretty simple thing, but unless you tell the game to run your code, it won’t run it!

    So now what we need is a hook.

    On hooks:

    In a ROM, every instruction is an array of bytes. Different instructions will be different sizes, in bytes.

    For example, let’s look at where we’d like to hook.

    PHA – 1 byte. 
    ASL, 1. 
    TAX, 1. 
    LDA #3100,x...?  3 bytes.  
    STA $3E, 2 bytes 
    PLA, RTS? Both 1 again.

    Now, on SNES there are two different sizes of “hook”

    (maybe 3 but we’ll ignore hooking with branches for now since they’re often impractical)

    JMP, JSR are 3 bytes.
    JML, JSL are 4 bytes.

    Shifting bytes around in a ROM is a really hard task. So let’s not do it. Instead, we will overwrite the bytes in the ROM with new bytes.

    So, from looking at where we want to hook, LDA $3100,x seems the smart place. If we can get away with a 3-byte hook, we replace just that one instruction, 3 bytes into 3 bytes, everything is great.

    So let’s do it using JSR.. and you return from JSR with RTS.

    So now the code looks like:

    PHA
    ASL
    TAX
    JSR bankC0free - - - - (jumps to) - - - - ↓
                                            bankC0free:
    					 CMP #$56
    					 BNE +        
    					 LDA #$6E 
    					 +                
    					 RTS
       ↓ - - - - - - (returns to) - - - - - - -
    STA $3E
    PLA
    RTS

    In this way we can run a lot more code during those three bytes than the program originally intended.

    Oh! but there’s a bit of a problem! We erased an instruction and it’s not being replaced!  This means the game will NEVER perform the LDA $3100,x instruction. Let’s correct that. You almost always need to move the code you replaced in order to place your hook, you need to move it into your hook.

    So now our code should look like this:

    PHA
    ASL
    TAX
    JSR bankC0free - - - - (jumps to) - - - - ↓
                                            bankC0free:
    					  LDA $3100,x
    					  CMP #$56
    					  BNE +        
    					  LDA #$6E 
    					  +                
    					  RTS
       ↓ - - - - - - (returns to) - - - - - - -
    STA $3E
    PLA
    RTS

    This will do exactly what we wanted to do in the first place. Compare the value being loaded from $3100,x with 0x56, if it IS 0x56, replace it with 0x6E, and if not, pass the loaded value through unchanged.

    Perfect! But… what if there’s no free space in the same bank? What if we need to use 24-bit addressing, what if we need that 4 byte hook? (this will come up VERY often)

    then it’s time to find 4 bytes to hook with.

    We could hook 4 bytes with

    TAX
    LDA $3100,x

    one byte + three bytes.. four bytes. Nice and simple. Just need to put TAX into your new code area as well.

    Let’s say that’s not an option for whatever reason. What if you need a 4 byte hook but the things around only add up to 5 or 6…? for instance,

    LDA $3100,x 
    STA $3E

    That’s five bytes. If you replace 5 bytes with 4 bytes, the 5th byte remains and it will completely ruin your day.  Luckily there’s a 1 byte instruction you can always overwrite dangling bytes with.

    NOP (no operation)

    Let’s try it now.

    So now your hook area will look like:

    PHA
    ASL
    TAX
    JSL bankFAfree - - (jumps long to) - - - - ↓
                                             bankFAfree:
    					  LDA $3100,x
    					  CMP #$56
    					  BNE +        
    					  LDA #$6E 
    					  + 
    					  STA $3E               
    					  RTL
       ↓ - - - - - - (returns to) - - - - - - -
    NOP
    PLA
    RTS

    So the code we’re replacing got split up in our code block.  This is just because we want to modify what was being written to $3E, so it needs to happen after our code, so it just goes at the END of our code space.

    Alright so now that we know what a hook looks like, how do we write one for ASAR?

    Well, now WHERE in the rom the code we’re replacing is is very important.

    ASAR’s org instruction tells ASAR where to place code, so let’s say LDA $3100,x is in ROM at $C082CE

    Our hook for asar looks like:

    org $C082CE
    JSL bankFAfree
    NOP

    and then we also need to tell ASAR where to put our new code, let’s say FA8000 is completely empty.

    org $FA8000
    bankFAfree:
    LDA $3100,x
    CMP #$56
    BNE +        
    LDA #$6E 
    + 
    STA $3E               
    RTL

    and that’s it. That’s how you hook with ASAR, that’s how you figure out where to hook, that’s everything about hooking to run your own code in a game. Apply your ASM with ASAR and it’ll insert your hook and your code for you.

    This post has been viewed 50 time(s).