20 users online: Banddy, BearAndBoarFan, bucketofwetsocks, containercore, Daffster, Darknubs, ForthRightMC, GangsterJerby,  idol, Lady Rozeldix, Macrophaje, NerDose, Nextalis,  NopeContest, PhantasyReal, Royal Nico TS, slopcore, The Cursed Goomba, WhiteYoshiEgg, Zavok - Guests: 90 - Bots: 168 Users: 55,592 (2,458 active)
Latest user: giovannemendes
Not logged in.
Posts by NewPointless
NewPointless' Profile - Posts by NewPointless
Pages: « 1 2 3 4 5 6 7 8 9 10 »
It's really cool to see hacks for non-SMW stuff.


Nice. Thanks for that little bit of history, and good luck on your hack.

Nice, I love chiptune stuff

Looks like some cool levels #smrpg{cool}

This look really fun, and the stuff with the bubbles is very impressive.

Originally posted by qantuum
Please please please add Darkbloom's Punishment Zone Trailer

Seconded. The trailer is hilarious,and so much work has been put into this collab.

Awesome to see some Amiga stuff get ported

My favorite level is the library. Great stuff!

I like the level design style. Sign me up for kaizo mode.

I hung this up on my refrigerator. I'm proud of you.

This is incredible. I can't wait until I finish up some of my projects and start working on sprites using your tools.

Epic Dracula castle

Neat. It looks really cool.

Amazing sprites! This is a big deal. Great job.

2D Mario tier list

Yo great job!

Looks like a cool collabs. I see some talented creators #smrpg{y}

The ASM of Kuso Ecstasy: An Intermediate ASM Tutorial

"You can do anything with ASM."

But what if you're bad at it, like me? With just a basic knowledge of ASM, you can still do a lot of creative and interesting things, all on your own. There's a vast sea of content that's different from vanilla by just "one little thing."

The basic concept of Kuso Ecstasy is to wonder, "What did hackers make before Lunar Magic? With just emulator cheats and a hex editor, what kind of gimmicks could they come up with? Though most of this early play and creation is lost, it's interesting to think about the hackers that used legacy tools to just change "one little thing." #wario{-_-?}

Some examples of these little changes we might think about are:

+RAM value changes
+Jumping to or deleting subroutines
+Changing the value in a table, or a constant, with a hex edit

"Walk before you can run. Run before you can fly."

This tutorial is a "beyond the basics" tutorial. I will assume that you can do the following things:

+Insert custom ASM with GPS, PIXI, UberASM, and Asar.
+Counting in hex and binary.
+Use basic opcodes like LDA, STA, JSL, RTL, etc.
+Know about the basics of the SNES processor like accumulator, X and Y registers, zero flag, ROM and RAM, etc.

If you are not clear about these things, here are some resources to learn:

+Converter for hex, binary, and decimal: https://www.rapidtables.com/convert/number/hex-to-decimal.html
+Technical documentation on 65c816 (SNES) ASM: http://www.6502.org/tutorials/65c816opcodes.html
+More ASM documentation: https://wiki.superfamicom.org/65816-reference
+ASM Workshop: https://www.smwcentral.net/?p=section&a=details&id=20949
+Ersanio's ASM tutorial: https://www.smwcentral.net/?p=section&a=details&id=15073
+The README for the tool you are trying to use

Interesting topics we will cover:

+What the labels in custom block ASM do
+Checking for controller inputs
+Finding and working with freeRAM
+Making a hex edit apply to a single level

Using UberASM to imitate Game Genie codes and emulator cheats

Emulators have tools that allow you to find and modify RAM values, presumably with the intention of making your own Game Genie codes, like giving yourself 99 lives.

The downside of this is that you can't use cheats to modify a ROM, generate a patch, and distribute it to your friends. So let's look into using UberASM to imitate these kinds of changes.

Using the RAM map

RAM map: https://www.smwcentral.net/?p=memorymap&game=smw®ion=ram

The RAM map is a site here on SMW Central that documents useful RAM addresses and what they do. You can enter the address, type of thing modified by the address (like sprite, overworld, player, etc.), or you can enter a description. So we can type "fireball" in the description, and we have a host of RAM addresses that affect different kinds of fireballs. The third address listed says:

Originally posted by RAM map
$7E141E 1 byte Yoshi
Yoshi has wings flag.
The only possible value for this address in the original is #$02,
but setting to #$01 will allow the player to throw fireballs if on Yoshi,
even if he is not fiery.
This will, however, disable flight as well as Yoshi's tongue attack.
It's recalculated each frame.

So if you want to create a gimmick around fireballs, this is a good way of beginning a search into how fireballs work.

Creating and applying an UberASM patch

To create an UberASM patch, we need to decide when it runs, write our ASM, and then return. Skipping a step or doing this incorrectly may result in the ROM crashing. To go back to our RAM address we've found in the previous step, let's create a gimmick that allows us to shoot fireballs when mounted on Yoshi, no matter what power-up state we have.

STEP 1, WHEN TO RUN: The documentation in the RAM map says that, "It's recalculated each frame," so our ASM is going to have to run every frame. This means we use the main: label. If we want the code to run at the beginning of a level, we use the init: label.

STEP 2, ASM TO EXECUTE: The documentation also says that "...setting to #$01 will allow the player to throw fireballs if on Yoshi, even if he is not fiery." So our ASM will set the value at this address to #$01. The address we will use will be written as $141E. We can omit the 7E portion of the address because ASM executed in UberASM has RAM mirroring. If the address begins with 7E00, we can omit the 00 as well.

STEP 3, RETURNING: We return from UberASM with a long return, RTL.

main:             ;run every frame
	LDA #$01  ;load the constant #$01 (one) into the accumulator
	STA $141E ;store it in RAM address $(7E)141E
	RTL       ;do a long return

Now save this patch to the level/ folder, add it to list.txt, and apply the patch to your ROM. Test it without loading from a save state, or your ROM will crash.

Examples of interesting RAM values. Try looking up some of these values in the RAM map to get more information:

$13-14: Frame counters
$15-18: Controller inputs
$19: Player power-up state
$9D: Sprites locked flag
$1490: Star power timer
$13ED: Whether player is sliding
$13E0: Player image
$1401: Screen scroll timer
$1471: Flag set by certain solid sprites
$1747, $173D: Player's fireball speed
$185C: Disable Mario's interaction with objects
$18D4-$18D5: number of berries eaten by Yoshi
$1DF9-1DFC: SPC700 I/O Ports. Write values to play music/SFX.

There are tons of interesting RAM addresses, so feel free to explore the memory map!

Custom blocks with GPS and Blockreator

Blockreator is a useful tool for creating an effect associated with a certain block. It can be done with very little ASM knowledge and then inserted into a ROM with GPS. With a little knowledge of labels and ASM, these effects can be tweaked to create new gimmicks. Documentation on the different labels and what they do is hard to come by, so I will explain what each one does.

Creating a block with Blockreator

To make a block with Blockreator, we need to decide what kind of collisions will execute the code, any further conditions, what ASM will be executed, and then return. We will then insert the block to our ROM with GPS.

By collisions, I mean whether the block activates when Mario touches it, when a fireball or cape touches it, a sprite touches it, and where and when the block needs to be touched to activate. Here are the different kind of collisions that GPS supports:

MarioBelow: executes when Mario touches a block (usually solid) from below.
MarioAbove: executes when Mario touches a block (usually solid) from above.
MarioSide: executes when Mario touches a block (usually solid) from the side.
SpriteV: executes when (most) sprites touch a block from above or below.
SpriteH: executes when (most) sprites touch a block from the side.
Cape: executes when hit with a cape spin.
Fireball: executes when touched by a player fireball.
MarioCorner: executes when Mario touches a block (usually solid) at the corner.
MarioBody: executes when Mario's body is inside a block (usually not solid).
MarioHead: executes when Mario's head is inside a block (usually not solid).
WallFeet: executes when Mario touches the block (usually solid) with his feet while wall running.
WallBody: executes when Mario is inside a block (usually not solid) while wall running.

More information can be found in the ASM Workshop, section 1.12.1 GPS/blocks on page 31. https://www.smwcentral.net/?p=section&a=details&id=20949

So as an example, let's make a passable block that plays the "wrong" buzzer when Mario press A while inside it.

STEP 1, WHEN TO RUN: We want this block to execute during the MarioHead and MarioBody labels. Since we will be optimizing our code so that it is only written once, just select "Mario body" under the events list in Blockreator.

STEP 2, FURTHER CONDITION: This block will only execute if Mario is touching it and presses A. Select the Misc. tab on the far right, then "If the player just pressed a button..." and click the "<- ADD" button. Then a dialogue will pop up, select "Button: A"

STEP 3, ASM TO EXECUTE: Now under the Misc. tab, select "Play a sound effect (bank 3)" and then "<- ADD". Now select "Wrong". Now since we used a condition, click the button "Insert group end".

A list of sound effects for each bank is located here: https://www.smwcentral.net/?p=viewthread&t=6665

Optimizing a custom block

Near the top of the code generated by Blockreator, it looks like this:

JMP MarioBelow : JMP MarioAbove : JMP MarioSide
JMP SpriteV : JMP SpriteH
JMP Cape : JMP Fireball
JMP MarioCorner : JMP MarioBody : JMP MarioHead
JMP WallFeet : JMP WallBody

So depending on whether this code is executing from sprite collision, Mario collision, or whatever, it will jump to one of the labels we've defined. Since we want to run the same code for multiple types of collision, we will just create one label and replace all the labels that we want to go there, like so:

db $37

JMP Return : JMP Return : JMP Return
JMP Return : JMP Return
JMP Return : JMP Return
JMP Return : JMP Buzzer : JMP Buzzer
JMP Return : JMP Return

	LDA $18					; \ If the player has pressed the A button...
	AND #$80				; |
	BEQ Return				; /
	LDA #$2A				; \ Play the "wrong" sound effect.
	STA $1DFC|!addr				; /
Return:						; > --------
print "Play the buzzer if the player presses A while inside this block."

The value after db controls whether the WallFeet and WallBody labels will execute. Use $42 if you don't want these labels, and $37 if you do. Make sure you comment out the respective JMP commands if you use $42.

Now save this block to the blocks/ folder in GPS, add it to the list (with an act as of 25 for a passable block), and run GPS. Reload your ROM if it's currently in Lunar Magic so that the print command works to enable the custom tool tip in Lunar Magic.

Note that this block is not only a stupid idea, but also Mario has to be completely inside of it because of how block clipping works. Like if Mario is standing on the ground, inside this block, it won't work. You can assign the Buzzer label to different values at the top of the block ASM to get different clipping values if you so choose. #wario{o_O?}

Making modified sprites with sprite disassemblies

A disassembly is some ASM that has been ripped from vanilla SMW and made into a form that's more usable and readable. One way to make use of disassemblies is to modify a vanilla sprite and insert it as a custom one with PIXI.


Instead of thinking about what a sprite does and what it could do, look at the disassembly first. Try to understand at least some of the code. Sometimes unique ideas can arise out of idiosyncrasies in the ASM. For example, sprites store different values for leftward and rightward speeds, so a sprite that moves in different speeds depending on its direction is much simpler to code than one might expect. If we go to the mega mole disassembly by imamelia, we can look in the ASM file mega_mole_BF.asm, and see the label and values:

db $10,$F0

But how do we know how this works? We have to read the ASM in the disassembly. By searching for "Speed" in the ASM file, this code turns up:

LDY !157C,x		; index the speed table by the direction
LDA Speed,y		;
STA !B6,x		; set the sprite X speed

You can look up $157C in the RAM map, and it tells us that it's a table that begins at this address, and it's frequently used to store each sprite's horizontal direction. It's indexed by the value in the X register, which usually is set to the sprite number, meaning that the actual address that's loaded is $157C + X. This value, which can be #$00 for right or #$01 for left, is then used to index the Speed table which is set at the top of the disassembly. It loads the first value ($10) for right-moving moles, and the second value ($F0) for left-moving moles. It then stores it to the sprite X speed table $B6, which is again indexed by the sprite number.

If you get lost while reading a disassembly, I like this document for explaining what the opcodes do and how other features of the SNES work like registers and flags: http://www.6502.org/tutorials/65c816opcodes.html

This useful thread https://www.smwcentral.net/?p=viewthread&t=13448&page=1&pid=195610 explains, among other things, how movement values work in SMW. Let's increase only the rightward movement by changing the ASM like so:

db $30,$F0 ;$10,$F0

I like to leave the vanilla values to the side, commented out, as a reference. Now save this file, along with the json, to the sprites/ folder in PIXI, add it to the list.txt in the same directory as your ROM, and run PIXI to apply it to your ROM.


What is a subroutine?

A subroutine is a small block of ASM that is used frequently. We can jump or branch to this subroutine and return from it for ease of use.

Adding a subroutine to a sprite disassembly

We can find subroutines in the SMW ROM map, which works just like the RAM map. The only difference is that we are searching the ASM in the ROM for useful blocks of code that we can branch to. Here's a very useful subroutine:

Originally posted by ROM map
$00F606 35 bytes Subroutine (JSL)
Death Subroutine (JSL to it to kill Mario).

ADVANCED NOTE: If the subroutine you're jumping isn't the "JSL" type, it won't return properly to the sprite code that you jumped from, and it will crash the ROM. Jumping to a subroutine that ends in a short return (RTS) requires special ASM. Type !ar jslrts in the #bot channel on Discord for more information.

Let's open up the ASM file for the swimming cheep-cheep by yoshicookiezeus. https://www.smwcentral.net/?p=section&a=details&id=3569

Near the bottom of the disassembly, we find this code that executes when Mario kicks a flopping fish:

CODE_01B12A:		LDA #$10			;\ set time to display kicked graphic (?)
			STA !RAM_KickImgTimer		;/
			LDA #$03			;\ play sound effect
			STA $1DF9 			;/
			%SubHorzPos()		;\ set sprite x speed depending on direction
			LDA KickedXSpeed,y		; |
			STA !sprite_speed_x,x		;/
			LDA #$E0			;\ set sprite y speed
			STA !sprite_speed_y,x		;/
			LDA #$02			;\ set sprite status (02 = killed)
			STA $14C8,x			;/ 
			STY !RAM_MarioDirection		; make Mario face same direction as sprite
			LDA #$01			;\ give Mario 20 points
			JSL $02ACE5			;/
			RTS				; return

Well we're going to replace it with this:

			JSL $00F606

You can leave the original code, commented out below, if you like. Now a flopping fish will kill Mario when he touches it, no matter what. #wario{o_o!}

Interesting subroutines:

$00F606: Kill the player
$00F5B7: Hurt the player
$01ACF9: Call RNG, outputs a random 16-bit value to RAM address $7E148D
$0294C1: Cape Mario smashes the ground

As before, you should look these addresses up in the ROM map for further information about what they do, and potentially reading a full disassembly of SMW at that address.

Running a subroutine on a button press

As I said before, RAM address $7E0015-$7E0018 are changed by player input. Since there are so many inputs, and SMW checks whether many inputs are newly pressed on the current frame or held from a previous frame, we need to load the relevant address into the accumulator and then check to see whether a certain button is pressed. Next we branch away to a return if it is not, and execute our ASM if all the conditions are met.

This is going to be an L/R gimmick, so we'll use address $18.

Originally posted by RAM map
$7E0018 1 byte I/O
Controller buttons newly pressed this frame.
Format: axlr----.
a = A; x = X; l = L; r = R, - = null/unused.

So what this means is that pressing the R button on the current frame will set the 5th bit (counting from the right) to 1, and pressing L will set the 6th bit. The other bits we want to ignore. To do this we will use the AND opcode. It checks each bit in the accumulator and the operand, and it will set the bit to 1 if they are both 1, or 0 in all other cases. Since we want to only consider bits 5 and 6, we will use 00110000 as our operand. This translates to #$30 in hex.

NOTE: Here is a website for converting from binary to hex. https://www.rapidtables.com/convert/number/hex-to-decimal.html You can also use the Windows calculator in programming mode.

	LDA $18     ;load relevant address for controller inputs
	AND #$30    ;use the AND opcode with an operand of 00110000
	BEQ +       ;branch away if the value in the accumulator is #$00 (neither button was pressed)
	LDA #$09    ;\play sound
	STA $1DFC   ;/
	JSL $0294C1 ;make a ground pound effect
+                   ;this is where it branches to if L or R aren't pressed
	RTL         ;don't forget to return

Now you can insert this with UberASM in a level to trigger the cape ground pound effect when the player presses L or R.

Hex edits

A hex edit means to directly edit a small number of hex values at a certain ROM address. Since this affects the ROM, which cannot be changed while the game is running, it will affect every level. We can find addresses to edit on the memory map, but I found this one by reading the disassembly.

The 2 bytes at $00EABB control the right and left speed you get from jumping off a wall while wall running. Normally, they are #$20 and #$E0, but we're going to increase them to give Mario a mega boost.

To make a hex edit, we will first set the address we want to modify, then write the hex value. Then we will save it as an ASM file and apply it to our ROM with Asar. You should keep a record of the hex edits you've applied to your ROM, as well as any freeRAM addresses and the ROM addresses that are modified.

Here is our hex edit:

org $00EABB ;go to this ROM address
db $39, $C1 ;directly write two values, one byte at a time

Now just use Asar to apply it to a ROM and test it.

Relevant hex edits

Here is a repository of some useful hex edits that you might consider: https://www.smwcentral.net/?p=viewthread&t=94884&page=1&pid=1504067#p1504067

Making hex edits that apply to a single level

This stuff is much more advanced, so bear with me as I explain some things a bit more in-depth, and take your time if need be. #wario{:/}

Doing a hijack.

A hijack is overwriting a piece of code with a jump to freespace, where a longer patch executes, and ends in a return. With most hex edits, you would just overwrite a constant in the ROM somewhere like a table, but with a hijack, you have to overwrite code, so that it's actually executed instead of just read by some code.

So using our example before, we're going to find which code loads the value at $00EABB (the wall run jump speed). I use All.log++: SMWDisC for my disassembly: https://www.smwcentral.net/?p=section&a=details&id=21822 By using a CTRL-F command with 00EABB, we can see that it is loaded at $00EB48. This is the code, according to the disassembly:

CODE_00EB48:        B9 BB EA      LDA.W DATA_00EABB,Y       
CODE_00EB4B:        85 7B         STA RAM_MarioSpeedX

If you aren't clear about how the disassembly works, here's a quick course. The column on the left is for the labels and tells you whether it is code, data, a special label, or something else. The column in the middle is the raw hex for the ROM at that address. The top line is $B9, $BB, and $EA. $B9 tells the SNES that it should use the LDA.W addr, Y opcode, and $BB $EA tells it to load from address $EABB from the current data bank, in big endian format. The column on the right shows the mnemonics for the opcodes and any comments that have been added to the disassembly.

What does this code do exactly? It loads the value from the table at $00EABB depending on the Y register, which is loaded with #$00 or #$01 depending on what wall Mario is running on. Then it stores this value to a RAM address, conveniently defined as Mario's X speed by the disassembly.

What we want to do is jump to freespace, write our ASM, and then return.

STEP 1, HIJACKING: There are two ways to jump to freespace. We can JSL or JML. If we JSL, then ending the subroutine with an RTL will bring us right back to where we came from. If we JML, we will have to return to a different address in the ROM when the subroutine is over. When we jump to freespace, we will use the autoclean command, which tells Asar to make a note about which address in freespace is being used, how much memory is taken up, and how the code gets there. This uses a system called RATS tags. Without the autoclean command, Asar will find new freespace every time you apply the patch and constantly take up more and more space in the ROM.

Let's JSL, because we're not doing anything fancy. We just want to replace this code with very similar code if it's a certain level. We need to know how many bytes a JSL command takes up in the ROM, because you cannot insert code into the ROM. You can only overwrite it, or else it would change every ROM address after it, which breaks the ROM completely. JSL and JML both take up 4 bytes, which spills over into the next line and leaves 1 byte. For the last byte, we will just overwrite it with an NOP opcode, which performs no operation. I use this reference for the number of bytes each opcodes takes up: https://wiki.superfamicom.org/65816-reference

If there's a label in the middle of your hijack, you need to make sure that the ROM doesn't jump or branch there ever, or your ROM will most likely crash or glitch. So we will do a CTRL-F command and enter 00EB4B (the second line of the vanilla code), to thankfully find that the ROM doesn't try to go there directly.

The next part of a hijack is moving to freespace. This means that we will tell Asar to write to another address in the ROM that isn't used anywhere else. Asar has built-in functionality for doing this. The two most basic commands are freedata and freecode. The difference is that freecode is in a data bank that has RAM mirroring so you can access RAM addresses without long addressing, and freedata doesn't. Other freespace options are in the Asar manual in the Freespace section. Since we are going to be accessing a freeRAM value, we should use freecode. The freedata command would be for writing things like tables or graphics. The Asar manual recommends to generally use freecode for all code.

So let's write some ASM, but just for the hijack portion of our code.

org $00EB48                  ;go to this address
autoclean JSL bigwallrunjump ;do a returnable jump to the label bigwallrunjump
                             ;use the autoclean command so it doesn't find new freespace every time it's applied
NOP                          ;perform a no operation opcode (does nothing) to overwrite the last byte

	freecode             ;begin writing to freespace with RAM mirroring

STEP 2, ASM TO EXECUTE: The first thing we'll write to freespace is the custom table we'll use. The original table at $00EABB is $20, $E0. We want to use faster values, so we will define a custom table like this:

newwalljumpspeed: db $39, $C1 ;this is the custom table we use only when the freeRAM is set

Next, we'll start our code. We've already decided in our hijack that the label that our code begins will be named bigwallrunjump, so we'll place that next.

Next we will check to see if our freeRAM value is set. We will write an UberASM patch later to actually set it during a certain level. If the freeRAM is not set, we will branch to a label that executes the original code. If it is set, it will proceed to execute our custom code.

But what is freeRAM and how do you find it? FreeRAM is a RAM value that isn't used by vanilla SMW. Some freeRAM is automatically cleared (set to #$00) at certain times during the game. Also, keep in mind that various custom resources like patches and tools will use freeRAM. That's why you should keep track of all the freeRAM values you use in one way or another, because if they conflict it will create a bug. To find freeRAM, go to the SMW RAM map and type "empty" in the description field. On the second page, we see this entry:

Originally posted by RAM map
$7E18BB 1 byte Empty
Cleared on reset, titlescreen load, overworld load and level load.

So this address will get cleared on level load, and it won't clear in the middle of a level. This is what we want. Level load happens before initialization, so when the player enters our level, it will get cleared, then we will set it during initialization with an UberASM patch, and then it will get cleared again when they enter another level. So the RAM at this address will only be set for one level.

Now we write our custom code. The most important thing to keep in mind here is that we are not in the same data bank as the hijack. The ROM address of the hijack started with 00, and now we are in some other data bank that's far away in the ROM. Strangely enough, if we execute some code like LDA $1234, it will read from address $001234, instead of using the data bank that the code is executing in.

So if we want to read from a custom table that we define in freespace (which we did), we will have to use the LDA.L opcode to explicitly use long addressing. Long addressing specifies the data bank we want to read from instead of using the data bank that the SNES assumes we're executing from. One drawback of long addressing is that there is no LDA opcode with long addressing that uses the Y register as an index, which is what the vanilla code uses. But we can use the TYX and TXY commands to swap the X and Y registers before and after our code.

Another way to deal with data banks in freespace is to remember the bank we came from, look up the current data bank, execute our code, and then recall the original bank before we return. We can do this with the stack. So we can do:

LDA.L newwalljumpspeed,X


LDA.W newwalljumpspeed,Y

STEP 3, RETURNING: The vanilla code will then return, and the chocolate code will branch to the return.

So here is what we get:

org $00EB48                      ;go to this address
autoclean JSL bigwallrunjump 	 ;do a returnable jump to the label bigwallrunjump
                             	 ;use the autoclean command so it doesn't find new freespace every time it's applied
NOP                         	 ;perform a no operation opcode (does nothing) to overwrite the last byte
	freecode             	 ;begin writing to freespace with RAM mirroring

newwalljumpspeed: db $39, $C1    ;this is the custom table we use only when the freeRAM is set

bigwallrunjump:              	 ;this is the label that our code begins at
	LDA $18BB                ;check freeRAM that resets on level load
	BEQ skip                 ;if it is clear (#$00), branch to the vanilla code, otherwise execute chocolate code
	TYX                      ;since we are using long addressing to read data in freespace while it thinks we are in
	                         ;data bank 00, we have to swap the X and Y registers because there is no LDA.L addr,Y
	LDA.L newwalljumpspeed,X ;load the value from our custom table in freespace
	TXY                      ;swap the X and Y registers back so that when it returns, everything will be the way 
	                         ;we found it
	BRA return               ;branch to the return
skip:                        	 ;vanilla code
	LDA.W $00EABB,Y     	 ;it thinks we are in data bank 00, so we can LDA.W
	STA $7B              	 ;this code executes whether we are executing chocolate or vanilla. It's Mario's X speed
	RTL                 	 ;we got here with a JSL, so RTL to get back to where we came from

Now to write our UberASM that sets a freeRAM value for a certain level, which uses things we've already learned:

	LDA #$01
	STA $18BB

Just apply the patch with Asar, save the UberASM to level/, add it to list.txt, run UberASM, and then test it in an emulator without a save state.

More advanced techniques

TECHNIQUE 1, MINIMIZING THE HIJACK FOOTPRINT: JSL and JML use 4 bytes, so at minimum the footprint of the hijack in the ROM is at least 4 bytes if you use these opcodes. If you absolutely can't overwrite more than 3 bytes, you can use JMP or JSR. However, these opcodes are limited to the current bank. So you would JMP/JSR to another hijack in the current bank and then JML or JSL to freespace. Obviously, this technique is not only more complex and time-consuming, it requires much more knowledge of specific subroutines and where/when they execute.

TECHNIQUE 2, OPTIMIZING A HIJACK: If your hijack is quite large, however, you can make it run faster by replacing an excessive number of NOP commands with a BRA. BRA is 3 cycles, and each NOP is 2 cycles. You can replace:

NOP #4


BRA optimize
NOP #2

A BRA command is 2 bytes and NOP is 1 byte, so just subtract 2 from the number of NOP commands. Then put a label to branch to.

Closing words

I hope that using this tutorial, people are able to expand their ASM knowledge and start to create unique and original gimmicks or features in their hacks!


1. Dark Clouds: Really unique atmosphere that fits incredibly well. Port sounds great.

2. MinishFlames: really incredible port. Very good fit for the level but I wouldn't call it a bullseye. Too tense.

3. Crooked Ladder: I love this port. I get more of a magical adventure vibe than a ghost cave.

4. Frost: Amazing composition. The arrangement really holds it back. Excellent fit for the level.

5. Stay Frosty: Big and ambitious port for such a short time frame. Maybe a bit too ambitious for the SNES. It's hard to capture the original's sense of space. I don't think that the quirkiness or disjointed rhythm are a good fit for this level.

6. Slippery Scary Subterranean: Move over Equinox! Really great mood. I like how you filled out the spectrum with so few instruments. The slow, dark atmosphere doesn't fit the aesthetic of the level.

7. Echoes on the Ice: This track has an eerie ambiance and quirky epicness that is really stunning, but it doesn't fit the dynamic tides and chasing sprites of this level. Points for originality.

8. FF6Cave: Very immersive and interesting port. This track has a feeling of stasis and timelessness that I don't think fits the P switch run.

9. Lovers of Aether: Not mixed very well. Sometimes the most interesting part is very hard to hear. Fits the level reasonably well. Very chill vibe but rhythmically interesting.

10. Busy Flare: really cool port, a bit intense and busy for this level

11. Mega Man X4 - Magma Dragoon: A little too athletic for this level. Good port.

12. Romancing SaGa 2 - Heartful Tears: Could use vibrato. Overly sentimental for this level.

13. Labyrinth Road: It's great to hear custom tracks. The arrangement lacks treble and the harmonic motion is very predictable. Decent fit for the level.

14. Volcano's Heart - Very creative. The mood and atmosphere is very scattered. Percussion lacks variety.

15. Lava Stage: fits the theme well. Port is a touch repetitive and lacks interest.

16. Floe: Use of stereo space and noise is really original. It conveys a very confusing mood, which is interesting, but it doesn't work in a traditional style level. Maybe an abstract type setting.

17. 180-60hoSPC: some tuning issues and lack of clarity overall in the mix. overly intense for a skull raft level

Pages: « 1 2 3 4 5 6 7 8 9 10 »
NewPointless' Profile - Posts by NewPointless