Language…
11 users online: cardboardcell, DanMario24YT, Golden Yoshi, Green,  idol,  Kevin, Kid Power Games, Metal-Yoshi94, Pink Gold Peach, ShoopDaWhoop, XxCharlesJustRobloxR - Guests: 208 - Bots: 213
Users: 65,085 (2,304 active)
Latest user: Gpds2024

In-Depth Patch Creation Tutorial

ASM CodingASM Patches

MarioFanGamer's in-depth Tutorial for Creating Patches.

Hello, my name is MarioFanGamer and I teach to you how to create patches.

What you need:

  • a text editor,
  • a SMW ROM,
  • an assembler (Asar),
  • knowledge, how to insert patches, obviously,
  • SMW all.log or any other SMW disassembly,
  • the ROM Map (rather optional but pretty helpful) and
  • 65c816 ASM knowledge (at least some basics).


As you see, this tutorial covers how to create patches for the assembler Asar in the programming language 65c816 ASM with the help of all.log and the ROM Map.

Creating Patches:

Step 1: Creating hex edit patches.

This tutorial covers how to do this and if you understand, how to do it, you're done with step.

Important note:
It is recommend to not submit hex edit to the patches section. The reason is pretty simple: They can be done by anyone who has got at least some knowledge creating of these. In additions, it's also recommend to mostly write in ASM rather entering the bytes manually unless you need to change values on one byte and/or change tables. Instead, submit them to the Tweaks section. There are a few exceptions, though, but only if the hex edits in question are on a large scale.

Step 2: Let's get serious.

Warning: This chapter is pretty long. It is even divided by sub-chapters.

Foreword

Now, since you know how to create hex edit patches, we'll go a bit deeper and create patches which does a more complex job. To start with it, you need to find an idea and try to materialize it.
Pro tip: Don't start with something complex. Start rather easy and try to create more complex patches later on. The only exception is if you're experienced with sprites, especially if you made complex sprites (no blocks because the more complex ones usually requires patches, sprites / generators or LevelASM) but this tutorial also covers for beginners.

Finding a good spot to hijack

Enough talking. Let's say, you want to create patch which changes the reward for getting 100 coins. The reward becomes a mushroom but if you are already big it comes to the item box and if that is not empty, you then get a 1up instead. Code is here:
Code
	LDA $19		;\ Check, if Mario is small
	BEQ .small	;/
	LDA $0DC2	;\ Check, if Mario has got nothing in the item box
	BEQ .no_item	;/
	INC $18E4	; Increment lives queue
RTL

.no_item
	INC $0DC2	;  Increase reserve item
	LDA #$0A	;\
	STA $1DF9	;| Play "get item" SFX...
	LDA #$0B	;| ... and play "item to item box" SFX
	STA $1DFC	;/
RTL

.small
	INC $19		;  Increase powerup
	LDA #$02	;\ Get mushroom animation
	STA $71		;/
	LDA #$2F	;\
	STA $1496	; | Set up animation and lock timer
	STA $9D		;/
	LDA #$0A	;\ Play "get item" SFX
	STA $1DF9	;/
RTL


For that, we need to know where is the code for the reward and what to do. Go to the ROM map and search something for the life reward.
If you searched correctly, you notice that $008E1A handles the status bar and $008F2C handles the number of necessary coins for an 1up.
This means that around this address, there is the code for the 1up reward.
Now, open all.log and search a good spot for the hijack. The perfect spot would be address $008F2C. To put a code into this, create a new ASM file and use this:
Code
lorom

org $008F2F

org $xxxxxx is the position of the program counter or the ROM address you want to edit and lorom means that the ROM type is... well, a lorom (one of the various SNES mappings). On xkas, you also need to put header on top of each file unless you're using an unheadered ROM but Asar ignores it and check for the extension instead (.sfc for unheadered and .smc for headered).

Okay, can we go on? And where should I put the code? Below the org? No! You'll overwrite important codes. Instead, you need to put it into freespace. That's the expanded area (assuming, the ROM is expanded, which... likely is).
There are also plenty of unused space in the vanilla ROM (especially in banks $0E and $0F but these areas are used by LM and a few other tools like AddmusicK) but it is not always the best idea (in fact, some patches used to do that but that was changed at some point).
To access the freespace area, you need to jump to that place. This is done by an autoclean JSL Labelname (needn't to be called "Labelname", obviously) and after the orgs you put either freecode or freedata (more details later). Here it is, what would look like if you did it correctly:
Code
lorom

org $008F2F
autoclean JSL new_reward

freedata
new_reward:
	LDA $19	;\ Check, if Mario is small
	BEQ .small	;/
	LDA $0DC2	;\ Check, if Mario has got nothing in the item box
	BEQ .no_item	;/
	INC $18E4	; Increment lives queue
RTL

.no_item
	INC $0DC2	; Increase reserve item
	LDA #$0A	;\
	STA $1DF9	;| Play "get item" SFX...
	LDA #$0B	;| ... and play "item to item box" SFX
	STA $1DFC	;/
RTL

.small
	INC $19		; Increase powerup
	LDA #$02	;\ Get mushroom animation
	STA $71		;/
	LDA #$2F		;\
	STA $1496		; | Set up animation and lock timer
	STA $9D			;/
	LDA #$0A	;\ Play "get item" SFX
	STA $1DF9	;/
RTL


Now, we'll take a closer look at some commands:
freespace is one of the main features of Asar. It searches automatically for freespace and it also puts the RATS tag automatically. It must be always put with either ram or noram. The alternatives for these are freecode and freedata (the former is the same as freespace ram and the other would be not to hard to guess). See section 4 for more details on these.
It can be also attached with align, cleaned and static. The former means that the code is always put at the beginning of a bank, the middle means that Asar won't complain that freespace is 'leaking' (only, and I repeat, only use it if Asar complains about freespace leaks even though it actually shouldn't) and with the latter, the freespace area can neither move nor change its size once the patch is applied.

If you use freespace, you always must use autoclean. This one will clean the freespace block. It must be always put into the first 512KB area (banks $00-$0F), always in front of a JML, JSL, dl or a given address and even then, it'll only clean the expanded area. Be careful if the area Asar cleans is located at the end of a freespace block: It gets some problems there.
Also, only one autoclean is needed unless you use a code which jumps to different freespace areas.
Newer versions of Asar allows to enter a value after freespace which means that Asar will search for that number in the ROM for freespace but since freespace is by default $00 (and Asar is set to that number anyway), you likely won't need this on SMW.

Restoring and NOP'ing/BRA'ing out


This also is not ready yet. There are still two things which must be done first: The overwritten code must be restored and you need to NOP out unused bytes. A JSL takes up to four bytes. This means you need to take a look at $008F2F and you notice, you replaced INC $18E4. However, this one just takes three bytes. You need take a look at the next opcode which is a LDA $0DFB.
That can't be simple replaced, though, because this is an opcode which loads A. As such, you need to preserve it (either by scratch RAM or stack) but it is not always recommend since you likely waste some bytes and/or cycles. Depending on how many bytes you need to restore, an often better alternative is to copy a few more lines up to the point where you can simply leave A as it is. We use various ways for comparison, starting the latter.

Taking a look at $008F32 reveals this:
Code
	LDA $0DFB
	SEC : SBC #$64
	STA $0DFB

This means, the restored code takes up nine bytes and 4 + 1 + 2 + 4 = 12 bytes. You then place it at the beginning of the custom code like this:
Code
lorom

org $008F2F
autoclean JSL new_reward

freedata
new_reward:
	LDA $0DFB	;\
	SEC : SBC #$64	; | Restored
	STA $0DFB	;/
	LDA $19	;\ Check, if Mario is small
	BEQ .small	;/
	LDA $0DC2	;\ Check, if Mario has got nothing in the item box
	BEQ .no_item	;/
	INC $18E4	; Increment lives queue
RTL

.no_item
	INC $0DC2	; Increase reserve item
	LDA #$0A	;\
	STA $1DF9	;| Play "get item" SFX...
	LDA #$0B	;| ... and play "item to item box" SFX
	STA $1DFC	;/
RTL

.small
	INC $19		; Increase powerup
	LDA #$02	;\ Get mushroom animation
	STA $71		;/
	LDA #$2F		;\
	STA $1496		; | Set up animation and lock timer
	STA $9D			;/
	LDA #$0A	;\ Play "get item" SFX
	STA $1DF9	;/
RTL

However, if you take a closer look, you will notice that you can optimise the code in two ways:
  • Because the restored piece takes up to nine bytes, a LDA $xxxx takes up three bytes and there are three RTLs, you can simple put LDA $0DFB in front of these. With that, you save up to 8 cycles because the code only executes one LDA $xxxx.
  • A PHA uses three, a PLA four cycles. In other words, you can save two bytes (at the cost of adding 7 cycles) if you put LDA $0DFB (3 bytes, 4 cycles) at first, then PHA (1 byte, 3 cycles) it and put a PLA in front of each RTL (3 bytes but 4 cycles only because only one is executed) which results in total 10 bytes, 11 cycles.

That means (using the former way), you get this:
Code
lorom

org $008F2F
autoclean JSL new_reward

freedata
new_reward:
	LDA $19	;\ Check, if Mario is small
	BEQ .small	;/
	LDA $0DC2	;\ Check, if Mario has got nothing in the item box
	BEQ .no_item	;/
	INC $18E4	; Increment lives queue
	LDA $0DFB	; Restored
RTL

.no_item
	INC $0DC2	; Increase reserve item
	LDA #$0A	;\
	STA $1DF9	;| Play "get item" SFX...
	LDA #$0B	;| ... and play "item to item box" SFX
	STA $1DFC	;/
	LDA $0DFB	; Restored
RTL

.small
	INC $19		; Increase powerup
	LDA #$02	;\ Get mushroom animation
	STA $71		;/
	LDA #$2F		;\
	STA $1496		; | Set up animation and lock timer
	STA $9D			;/
	LDA #$0A	;\ Play "get item" SFX
	STA $1DF9	;/
	LDA $0DFB	; Restored
RTL

The few bytes after the JSL still can be executed as code and need to be skipped / NOP'd out. That isn't hard to do. All what you need to do is to put some NOPs and likely a BRA if there are at least 2 bytes after the JSL (note that a BRA takes even while doing nothing three cycles whereas two NOPs four). How you are actually doing doesn't matter but I use the BRA way with the help of labels for the tutorial because that's the easiest to handle.
Using the least efficient restoring to show how to restore more then one byte:
Code
lorom

org $008F2F
autoclean JSL new_reward
BRA $00

freedata
new_reward:
	LDA $19	;\ Check, if Mario is small
	BEQ .small	;/
	LDA $0DC2	;\ Check, if Mario has got nothing in the item box
	BEQ .no_item	;/
	INC $18E4	; Increment lives queue
	LDA $0DFB	; Restored
RTL

.no_item
	INC $0DC2	; Increase reserve item
	LDA #$0A	;\
	STA $1DF9	;| Play "get item" SFX...
	LDA #$0B	;| ... and play "item to item box" SFX
	STA $1DFC	;/
	LDA $0DFB	; Restored
RTL

.small
	INC $19		; Increase powerup
	LDA #$02	;\ Get mushroom animation
	STA $71		;/
	LDA #$2F	;\
	STA $1496	; | Set up animation and lock timer
	STA $9D	;/
	LDA #$0A	;\ Play "get item" SFX
	STA $1DF9	;/
	LDA $0DFB	; Restored
RTL


Now, you don't actually need to NOP out  / branch over the unused bytes in this case. If you look around the code, you'll notice that the code which decreases the coins by 100 only gets executed if $13CC isn't 0 and $13CC gets decremented every frame. And because the coins rarely go over 100 (in fact, they shouldn't even), you simply can put a STZ $0DFB into the now unused area and still have three bytes left like here:
Code
lorom

org $008F2F
autoclean JSL new_reward
	STZ $0DFB
BRA Skip

org $008F38
Skip:

freedata
new_reward:
	LDA $19		;\ Check, if Mario is small
	BEQ .small	;/
	LDA $0DC2	;\ Check, if Mario has got nothing in the item box
	BEQ .no_item	;/
	INC $18E4	;  Increment life queue
	LDA #$05	;\ Play "get 1up" SFX
	STA $1DF9	;/
RTL

.no_item
	INC $0DC2	;  Increase reserve item
	LDA #$0A	;\
	STA $1DF9	;| Play "get item" SFX...
	LDA #$0B	;| ... and play "item to item box" SFX
	STA $1DFC	;/
RTL

.small
	INC $19		;  Increase powerup
	LDA #$02	;\ Get mushroom animation
	STA $71		;/
	LDA #$2F	;\
	STA $1496	; | Set up animation and lock timer
	STA $9D		;/
	LDA #$0A	;\ Play "get item" SFX
	STA $1DF9	;/
RTL

You see, restoring codes is sometimes a bit tricky but hey, that's optimising codes for you!

Step 3: Branching a hijack.

Branching is a bit more complicated. You can't easily create a subroutine. Instead, you use a JML. This also means that you can't use a RTL as it's designed to be used with a JSL. Instead, you just use a second or more JMLs.

As you probably know, the main difference between a JML and a JSL (and JMP and JSR, respectively) is that JSL and JSR pushes the address of the next opcode minus one*. That way, RTSs and RTLs can determine, where to jump. JMP and JML don't use that function.
This changes both their uses (the former can use code which is easily reused, the latter not) as well as their speed (accessing the stack is slow so any subroutine and return opcode is slower than two jump opcodes).

*Yes, that means that you can replace the old address with a new one but that way is pretty ugly and too impractical anyway.

There is also another advantage: You don't need to NOP out rubbish so you can simply jump to non-rubbish directly.

Step 4: Bank change and RAM mirrors.

When you created sprites, you'll often use this code:
Code
PHB
PHK
PLB
JSR Main
PLB
RTL

Main:

This one is used for changing the program bank to allow 16-bit addressing of the current bank. The data bank doesn't get updated when you use JML, JSL and RTL so you have to do it manually.
The main downside of the "data bank wrapper" is that you can't put the code into banks +$40, though. That is because banks $40+ don't features RAM mirrors unlike as banks $00-$3F.

RAM mirrors allows us to use 16-bit/absolute addressing* for WRAM (or rather for $7E0000-$7E1FFF, the rest is unmirrored), registers, enhancement chips addresses and the occasional cartridge RAM such as SRAM, depending on the mapping, ($2100-$7FFF, not everything is used) and still access the data in the current bank in absolute addressing.
Registers and most enhancement chips addresses are even only located in the mirrors.**
That one is very important because for one, 24-bit/long addressing always take one more byte than absolute addressing but more importantly, the 65816, the language of the CPU, doesn't support long addressing for every opcode such as indexed Y.

*The bank for direct page/8-bit addressing, however, is always $00, no matter whether it is in the RAM mirror banks or not or even in RAM.

**Of course, they still are mapped, more specifically in bank $00 as well as every other bank which has got the WRAM mirrors which means you still can use long addressing for these stuff and e.g. access $2100 through $002100.

There is a reason why freecode and freedata exists. Again, freecode tries put the code into the first 2MB whereas freedata tries to put the code (or more likely, data because these don't require any RAM mirrors) into banks $40+.

Sometimes, it is more efficient to not change the bank or change it to a bank to a different location (e.g. banks $7E/$7F). A long address only takes one more cycle and byte than an absolute address at the same conditions and direct page always uses bank $00.
If one use few long addresses (i.e. 4 or 7, depending whether the wrapper uses a JSR or not) at tables in the same freespace area and the opcodes aren't non-24-bit only or, for obvious reasons, one don't even use these (like the example patch), then it's better to not change the data bank, not to mention that it also allows you to use freedata.

Note: Asar generally assumes that the data bank is set to the same value as the bank byte of the code even if it is not actually the case. As such, it uses 16-bit addresses to tables by default. This can be fixed by either putting a .l right after the opcode (e.g. LDA.l Label) or use bank noassume which disables the label optimisation for ROM addresses.

Step 5: V-blank and H-blank.

There are cases where you want to create a patch which changes graphics or do some other stuff. However, you can't easily do this within the main code. Instead, you have to make use of blanks. There are three of them: F-blank, v-blank and h-blank and each of them can access all (f-/v-blank) / most (h-blank) registers without many problems.
These are somewhat tricky to access which is why the SNES provides you a way to easily access v- and h-blank with the use of interrupts. There also is f-blank, which disables the screen for the time being altogether, but it results in black scanlines on the screen which should be avoided as much as you can and therefore isn't part of the scope of this tutorial.

Interrupts are generally separated in two kinds: Regular interrupts aka Interrupt ReQuests (IRQ) and Non-Maskable Interrupts (NMI) which mostly differ on whether they can be blocked by the processor or not (IRQ can be, NMI can't). These simply tell that the currently running code should halt and jump to a different code.

On the SNES, the NMI is hardcoded to fire whenever v-blank fires off and for most intents and purposes, you can treat them synonymously. This is where large parts of the screen is refreshed, considering only there you can access OAM and VRAM.
IRQ, on the other hand, is programmable, both by hardware (e.g. SA-1 makes use of interrupts, for example) but also by software in the sense that you can set, at which scanline and at which horizontal position the IRQ should be fired (though SMW only fires IRQ at a specific scanline, not at a specific pixel). The primary purpose of IRQ on the SNES is therefore to run code in h-blank such as how SMW uses it to keep the status bar in place.
Keep in mind that you need to wait for h-blank if you use IRQ. To do that, use
Code
-	BIT $4212
	BVS -
-	BIT $4212
	BCS -

which causes the CPU to wait until h-blank is active and then inactive again. The rest is timing where you waste some time (don't use $4212, though) until the electron beam is aligned correctly (just before h-blank, to be precise) and prepare for the IRQ writes. You then can start writing to the registers.
As you can see, handling IRQ to write to h-blank is very difficult and should be done so with care, though it could have been improved if SMW didn't just fire IRQ at a specific scanline but at a specific pixel instead (simplified speaking, of course).

NMI starts at $00816A and IRQ starts at $008374. Hijacking is limited and you also need to trace the code so you hijack the correct place so be careful with that. In fact, you're typically better off using UberASM for NMI, though you still need to hijack IRQ manually unless you're using new versions of SA-1 Pack (i.e. from version 1.32 onwards) in which case you just have to set the pointer, where the IRQ code runs.

Important note: The codes must as fast run as possible. Okay, it can be a bit slower but still not too much. That is because v- and h-blank time is limited, else you get screen flickering and/or black scanlines (depending which blank got overflown).
Because of that, if you have got the choice between size and cycles (e.g. rolled or unrolled loops), use the latter.
Another thing you should keep in mind is that NMI and IRQ are interrupts i.e. they temporarily halt the main loop until they finish which can easily cause crashes (particularly scratch RAM).

And if you are one of the ZSNES users: Don't use it, at least for testing these stuff. It never was really good about accuracy and the registers are one of the reasons. Use BSNES instead.
Same goes for SNES9x users here but this one is at least more forgiving.

Ultimately, the punchline is that handling v- and particular h-blank requires great care. Feel free to ask in the forums or on Discord on whether you hijacked IRQ correctly as well as look how other patches handle IRQ.

Some important/helpful information and other stuff:

This is just a minor chapter which explains some of Asar's more important features. Read the manual (located in docs in Asar's ZIP) for more information.

Displaying text on the console window.

One of the most effective assembler commands are the print commands. It outputs the text you have written in the ASM file. It's mostly used for debugging but there a few other uses for regular users too. There are also following subcommands:
  • pc is used to determine the current PC when print was used, useful to find out where the code is and to determine the position of subroutines
  • bytes display the totally assembled bytes (even outside of freespace, use generally freespaceuse instead)
  • reset bytes just resets the byte counter for written bytes
  • freespaceuse prints, how much freespace the patch uses
  • dec(value) and hex(value) display the value you entered in the brackets as a decimal and hexadecimal number, respectively (can be combined with the others).


Major changes on the first 512KB

It is also possible to overwrite some codes of the original game because you either replace them with a different code, said code becomes through your patch unused or you use (as explained) some unused parts as freespace. In these cases, it is recommend to use warnpc $xxxxxx. When you overstep that address, the assembler will warn you that your code is too large. This doesn't work when the PC is already set behind the overstepped area for obvious reasons.

Clean freespace in freespace area

You can put more then one freespace command in a patch. How to clean it is simple except it actually isn't. If the additional freespace is accessed through a code in the original ROM, fine, but autoclean doesn't work in freespace area.
Instead you use prot. It must be put right after a freespace command and the labels have to be separated by a comma so you get something like this: prot label1, label2, ..., label80 (yes, 80 is the limit, but you can have up to 128 freespace commands and you likely won't need that many freespace commands anyway). Confused? Take a look at this code:
Code
org $009642
autoclean JSL RandomCode

freecode
prot RandomData
RandomCode:
; Code

freedata
RandomData:
; Data

That's how prot works.

Note: A freespace block cannot clean itself. It has to be cleaned from somewhere else i.e.
Code
freespace
prot Labelname
Labelname:

doesn't work.

Running code every frame

From time to time, you'll end up in a situation where you have to run a code for every frame and just need a place to inject it. Sounds easy, right? Except not really: Most of these patches can work just as well for UberASM which allows you to write codes for every frame. Sure, the insertion will be difficult for normal users (yet) but once hijack conflicts arise, the trouble will be worse. Way worse. This is especially important for NMI hijacks.

Here are some examples on if you have to hijack the level main code:
  • Consider whether the hijack is really necessary. There are instances where the code has to run late in the frame (and UberASM doesn't support late levelASM yet) but 99.9% of the time, you can resort to UberASM instead.
  • Be careful with your hijacks. There are easy ways to cause conflicts with major resources. Here is a list of hijacks which run almost every frame in levels:
    • $00A1E4 is where the level code really starts (before that is the message box checker which shouldn't be hijacked). However, most of it is the goal and pause code.
    • $00A242 is the last frame which runs without pause but it's also hijacked by UberASM so that's out.
    • $00A295 mostly contains JSLs which are good hijacks but these never run during the Mode 7 bosses.
    • $00A2A9 is where code runs for every frame (when not paused) in every instance of a level (and the title screen). Check out the individual subroutines for more potential hijack locations.
    • $008E1A is the status bar routine which gives you more code to hijack. However, custom status bars are very common which makes it somewhat of a bad location to hijack.
  • $008DC4 used to be very common hijack but it's something which you should never hijack for no reasons. Not only was it a far too common hijack (to the point conflicts could easily arise with multiple patches) but it was even a very bad spot to hijack to begin with. As a homework lesson, it's something you should figure out on your own.
  • Are you really sure you need to hijack the code?
Altogether, the primary purpose of patches is to change the game's behaviour, not to add more to the game. UberASM is mostly sufficient for the latter.

Preprocessor function

Similar to compilers, assembler features them too (or at least something similar) and Asar is not an exception. Keep in mind that this section only scratches the surface and you should read Asar's manual (included in the included docs) for more information.

Defines and Macros

You can skip that if you already worked with blocks and sprites.

They practically exists in all compilers and assemblers and work pretty similar with some exceptions. They are defined like this: !Define = 1 (the "!" is mandatory and basically acts like #define in various compilers). For spaces, you have to put quotes around the arguments like here: !Define = "PHP : PHB : PLB". That way, you can change mass variables and addresses pretty easily and also allows a more user-friendly-to-edit-patch.

It is also possible to redefine defines (as seen on some patches) but be careful with that as that should be used sparingly (e.g. when you define LoROM and SA-1 defines, see below).

Macros work a bit differently. You have to enter macro, a space, the name of your macro, brackets with your defines, on the next lines your code and finally, end the macro with endmacro like here:
Code
macro YourMacro(yourFirstDef, yourSecondDef, etc.)
*your code*
endmacro

Remember that the defines inside the code has to be put between the smaller than and greater than equal signs like <yourFirstDef>.
Labels inside macros have to start with a question mark (i.e. instead of entering yourLabel:, you enter ?yourLabel: instead). This only applies for labels to mark the (relative) position of the macro code - labels outside of macros needn't to be prefixed with a "?" because their position is absolute, not relative. Sub-, plus and minus labels in macros work just fine.
This is because macros tell the assembler to put the same code with the given parameters as it is.

The callers for these macros are the percent-sign, macro name and then inside brackets the values / strings you enter like this: %YourMacro(*yourFirstDef*, *yourSecondDef*, etc.).

If you want to have for example a macro which does the org-autoclean-JSL-NOP on a single line you use this:
Code
macro autocleanJSL(hijack, destination, amount)
org <hijack>
autoclean JSL <destination>
NOP <amount>
endmacro


Lastly, you can also define labels by hand. For many parts, it's pure semantics except you can't redefine labels and that the values are always treated as 24-bit values. Nonetheless, it does make it clear in the code what is definitively an address (particularly ROM addresses) and what is a define which can be changed by a user.

Code
HurtPlayer = $00F5B7
KillPlayer = $00F5B7


Conditionals

Asar uses just like many compilers a kind of C-similar conditional syntax. Even the commands are pretty similar:
if: If the number/result it is zero or any negative number, Asar will skip the code on the same line / up to the next endif (if there aren't any nested ifs, that is).
else: If the condition is not true, Asar will assemble this code instead.
elseif: If the condition on the main if is not true, Asar will check if the condition on that line is true instead.
endif: This must be used at the end. The exception is if the code is put on the same line as the if (e.g. if *condition* : *code* works).
Even the boolean conditions are the same or at least similar:
  • X == Y - X is equal to Y
  • X != Y - X is unequal to Y
  • X > Y - X is smaller than Y
  • X < Y - X is greater than Y
  • X >= Y - X is equal to or smaller than Y
  • X <= Y - X is equal to or greater than Y
  • X && Y - If X and Y are true
  • X || Y - If X or Y are true
  • not(X) - Inverts X

That way, you can control patches more easily.

It is even mandatory for SA-1 bastards hybrids.

Note: Prior to Asar 1.42, elseif was a bit broken.

For readability, you can, if you want to, use the pointy brackets ("{" and "}") like in the C-like languages. Remember that while Asar ignores them, they are not treated as whitespace. As such, you can't put them "in the middle of opcodes" i.e. NOP : { : RTS works but LDA {$19} doesn't.

Source: Asar's manual.


Note: These brackets only exists for purely aesthetic reasons. They don't do anything.

Read from the ROM directly

Another of Asar strongest features are readX($yyyyyy) where X is any value from 1-4 and controls the amount of bytes Asar reads and yyyyyy is any ROM address. Use it to determine if the specific address got hijacked/used or not, to get the position of a code of an already existing hijack or if autoclean is used on a movable area.

Include files

Asar has got way to attach multiple files within a patch. These are called incsrc and incbin. The difference between them are that the former includes a file to assemble (e.g. tables in Asar format, additional codes, files with macros and definitions, etc.) whereas the latter put the included data to the ROM as it is (e.g. graphics, already compiled codes, etc.).

tl;dr To include files, use incsrc for codes and incbin for binary data.

Speaking of incbin files:
You can also do also this: incbin file.bin -> Labelname. It kind of work as a macro for pushpc : freedata align : Labelname: incbin file.bin : pullpc, but the implied freedata at that opcode works a bit differently: You can include a file with a size of up to 65536 bytes or in different words: 2 SNES LoROM banks. You still have to manually use prot, though.

Note: You can also insert the file directly to an address, though the use of it is rarer because of the difficulty of insertion.

Preserving the current pc's value

If there are many ASM files (e.g. through a pack) and you need to hijack a different position other then the main patch, you should use pushpc and pullpc so you don't have to use multiple freespace blocks.

SA-1

It is also possible to create patches for the enhancement chip SA-1. For a hybrid, use e.g. this at the beginning:
Code
lorom

if read1($00FFD5) == $23
	!sa1 = 1
	; SA-1 base addresses
	!db = $3000
	!addr = $6000
	!bank = $000000
	sa1rom
else
	!sa1 = 0
	; Non SA-1 base addresses
	!db = $0000
	!addr = $0000
	!bank = $800000
endif

Now you just add a |!db to addresses from $0000-$00FF and |!addr to addresses from $0100-$1FFF. For example, $0063 (or any other address from $7E0000-$7E00FF) and $1025 (or any other address from $7E0100-$7E1FFF) become $0063|!dp and $1025|!addr, respectively.
!bank is used for ROM instead of RAM addresses. On regular ROMs which are 4 MiB large or smaller, the whole ROM (minus the last 512 KiB*) are mirrored through the banks +$80 (actually, the opposite is the case: The ROM data actually exists in banks $80-$FF and are mirrored through $00-$7D but for all intents and purpose, let's treat like the opposite is the case). Said mirror is also called the FastROM area which boosts the SNES a bit (by around 1/4 of its speed) if enabled. In order to achieve FastROM (besides activation), the codes have to run there (for some reason, don't ask me).
SA-1 ROMs don't feature FastROM because the SA-1 ROM type is something of an ExHiROM (meaning it can be up to 8 MiB large) so the FastROM speed boost is unusable, though SA-1 runs much faster than FastROM anyway so you ultimately don't need FastROM.
Note: SNES's speed is still unaffected but that shouldn't be a problem in most cases.

*Don't worry, they're not lost. It's just that these bytes are only located at the FastROM area. And yes, the reason is SRAM and WRAM.

Note: Direct page (addresses like $42) needn't to be or'd with |!db. That is because direct page is already set to $3000 and is only necessary for direct page switching as well as indirect jumps.
Note: Sprites and some other addresses use completely different addresses on SA-1. Please refer to SA-1 Pack's readme for more information. These addresses also can't be easily or'd as a result.

For SA-1-only patches, you need to (or can but this one works too) use assert and so check if the ROM is SA-1. The format is following: assert *condition*, "Text" where *condition* is an if-like... well, condition where, if the condition is 0, assert gets triggered and "Text" is the output.

How do I get the current level number?

There are also stuff which are based of the level number. For this, you need to save the level number which is done by this code:
Code
org $05D8B7
	BRA +
	NOP #3		;the levelnum patch goes here in many ROMs, just skip over it
+
	REP #$30
	LDA $0E		
	STA !level
	ASL		
	CLC		
	ADC $0E		
	TAY		
	LDA.w $E000,Y
	STA $65		
	LDA.w $E001,Y
	STA $66		
	LDA.w $E600,Y
	STA $68		
	LDA.w $E601,Y
	STA $69		
	BRA +
ORG $05D8E0
	+

You probably know this from UberASM and in fact, this is where it was taken from.

Here is a libray with some ASM codes and macros.

RATS Tag

If you want to, you can write into freespace directly but depending on the patch, you need or needn't to use a RATS tag to protect the data. Here is the RATS tag:
Code
db "S","T","A","R"
;db "ST","AR"	; This could be used instead too
;db $53,$54,$41,$52	; Or that
dw Code_End-Code_Start-1
dw Code_End-Code_Start^$FFFF-1

(Note: "S","T","A","R" has to be entered that way because Asar will complain if you have entered "STAR". That is no bug but rather a check if the patch was originally made for xkas. Newer versions will remove that check alongside Xkas compatibility in general.)

Last notes:

I hope, this tutorial gets useful at some point. I know, it doesn't look very deep but remember that there aren't many functions with patches than like sprites or musics and that you have got much freedom instead.
Basically, all of these stuff are enough to a create full patch whereas with sprites, you need to take care of this and that and w/e and then, there is this and that and w/e function.

The reason why I have created this tutorial is because most tutorials were created before Asar existed and a few more stuff. In addition, they don't go very deep.

Changelogs (in DD.MM.YY format):
15.08.16:
Improved a few things and added defines, macros and conditions. Thanks to Ladida for pointing out for them.

21.08.16 (-ish):
Fixed some minor errors.

05.11.16:
Added a chapter, how to handle prot, incsrc and incbin.
Fixed spelling on some words, especially the goddamn "label"-"lable"-misspell. >_>

05.03.21:
(Wow, four and half years since I have written the tutorial.) Fixed spelling and grammar, added a proper examples to chapter 3, rewrote half of chapter 5, added information to define labels, added information about hijacking code, replaced <tt> with <code> and <kbd>.
Originally posted by MarioFanGamer
And Alcaro, Ladida and WYE, anything major to complain?

lol


excellent tutorial, great job. very in-depth indeed #thp{O_O2}


a few nitpicks:

step 2 is... pretty long. you should probably simplify it, subdivide it, or split it for consistency (every other section isn't as long). for example, you cover code optimization in there when it can be its own section

i think you should cover conditionals, defines, and macros. you used them in explaining SA-1 but didnt touch upon them beforehand. considering they are valuable tools in patch creation, i think they should get an earlier mention

when you discussed bank changing, you kinda implied code should not go in banks $40+. as long as you dont change the data bank, use long addresses for any table accesses, etc, then code is fine there (you probably meant to say this in there but forgot)

also typo here:

Quote
Keep in mind that you need to wait for IRQ. To do that, use BIT $4212 : BVC $FB.

hblank, not irq :P (as in, youre already in irq and need to wait for hblank)

also not sure what you mean here:

Quote
Another thing you should keep in mind is that NMI and IRQ runs parallelly to the SNES so a few stuff are incompatible there.

everything runs parallel to the snes ?_?


again, great job #smw{:TUP:}
Originally posted by Ladida
Originally posted by MarioFanGamer
And Alcaro, Ladida and WYE, anything major to complain?

lol

Yeah, you (plural) are the one which I likely expect to note the errors (okay, WYE not really on SMWC but on the German board). I especially look at Alcaro. I even think, he even looks most of the time at my home when ever I post something to see if I have spread misinformations...

Originally posted by Ladida
step 2 is... pretty long. you should probably simplify it, subdivide it, or split it for consistency (every other section isn't as long). for example, you cover code optimization in there when it can be its own section

Yeah, that is one of the things I really should improve. At least, I added some sub-subchapters even though it's not the best way. #ab{<_<}

To do: Improve it even more.

Originally posted by Ladida
i think you should cover conditionals, defines, and macros. you used them in explaining SA-1 but didnt touch upon them beforehand. considering they are valuable tools in patch creation, i think they should get an earlier mention
[...]
Quote
Keep in mind that you need to wait for IRQ. To do that, use BIT $4212 : BVC $FB.

hblank, not irq :P (as in, youre already in irq and need to wait for hblank)

Done! #ab{:D}

Originally posted by Ladida
when you discussed bank changing, you kinda implied code should not go in banks $40+. as long as you dont change the data bank, use long addresses for any table accesses, etc, then code is fine there (you probably meant to say this in there but forgot)

Not quiet because mentioned RAM mirror, programm bank, the reason why codes in banks +$40 can get messed up, etc. but you're not quite unright. I will improvise it.

Originally posted by Ladida
also not sure what you mean here:

Quote
Another thing you should keep in mind is that NMI and IRQ runs parallelly to the SNES so a few stuff are incompatible there.

everything runs parallel to the snes ?_?

But I talked that the CPU (or what ever it handles that), NMI and IRQ shares the same RAM, most registers and some stuffer so codes can get messed up. At least, that what I've read.

I should write that information in that tutorial, shouldn't I?

Originally posted by Ladida
again, great job #smw{:TUP:}

Thank you! #w{=D}
In an empty ASM file with notepad (or any text editor, in fact). There is no dedicated IDE for 65c816 ASM so you have to do this all by hand.
Originally posted by banshee
I stupidly assumed Asar would just look in its own folder.

Because that's what Asar is really doing (but the other option also works, especially since you can just drag and drop the files into the command line). It might have been a typo or something else but you definitively you don't need the full path for Asar.
Is this an xkas patch tutorial?

EDIT: Forgot the release of the date.
Its old

koopas are AWESOME,,,
Originally posted by Koop the Koopa
Is this an xkas patch tutorial?

EDIT: Forgot the release of the date.
Its old


Quoting part of the very beginning of the tutorial
Originally posted by tutorial
an assembler (Asar)


I'll let you figure out the answer from that.
Originally posted by Ninja Boy
Originally posted by Koop the Koopa
Is this an xkas patch tutorial?

EDIT: Forgot the release of the date.
Its old


Quoting part of the very beginning of the tutorial
Originally posted by tutorial
an assembler (Asar)


I'll let you figure out the answer from that.

Both.
Asar is compatible with xkas.
Just add ;@xkas on top of the asm.

koopas are AWESOME,,,
Originally posted by Koop the Koopa
Both.
Asar is compatible with xkas.
Just add ;@xkas on top of the asm.

Yeah, but almost no one creates patches for Xkas, only for Asar. It only makes stuff more difficult (no automatic freespace searching, no functions, no other QoL in Asar).
Originally posted by MarioFanGamer
Originally posted by Koop the Koopa
Both.
Asar is compatible with xkas.
Just add ;@xkas on top of the asm.

Yeah, but almost no one creates patches for Xkas, only for Asar. It only makes stuff more difficult (no automatic freespace searching, no functions, no other QoL in Asar).

I agree.

Nice tutorial btw.
#smw{:TUP:}

koopas are AWESOME,,,
The tutorial has been updated where most of the formatting has been changed as well as how certain chapters (particularly interrupts and blanking) have been changed.

ASM CodingASM Patches