Banner
The Base Rom Level Design Contest has begun! Visit its contest subforum to learn more!
Views: 771,415,475
Time:
16 users online: Aguni_, Alxetiger,  Dakras Hayashi, Epic_Manky, Evernn, Fullcannon, GbreezeSunset, luanshn, MeteorSmasher4, MiracleWater, Parahax,  Ryaa, Ultima, xyz600sp, Yung Gotenks, Zandro - Guests: 33 - Bots: 62Users: 40,525 (1,860 active)
Latest: XCB
Tip: If you plan on making long levels, be sure to include multiple midway points. A patch for this can be found in the Patches section.Not logged in.
In-Depth Patch Creation Tutorial
Forum Index - SMW Hacking - SMW Hacking Help - Tutorials - In-Depth Patch Creation Tutorial
Pages: « 1 »

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,
  • 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, and only if you find unknown information, you should submit it to the ROM map. There are a few exceptions, though, but only if they really useful.

Step 2: Let's get serious.

Warning: This chapter is pretty long. It is even divided by subchapters.

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. 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
	LDA #$05	;\ Play "get 1up" SFX
	STA $1DF9	;/
RTL

.no_item
	INC $0DC2	; Incrase 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		; Incrase 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 needed 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 changed it sometime).
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 as "Labelname", obviously) and after the orgs you put either freecode or freedata (depending if the patch can be used safely in banks +$40 are or not, see chapter 4). 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
	LDA #$05	;\ Play "get 1up" SFX
	STA $1DF9	;/
RTL

.no_item
	INC $0DC2	; Incrase 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		; Incrase 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 would be 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 complaine 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 or dl (exception: you want to clean something directly) and even then, it'll only clean the expanded area. Be carefull 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 ti 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. 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 + 2 + 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
	LDA #$05	;\ Play "get 1up" SFX
	STA $1DF9	;/
RTL

.no_item
	INC $0DC2	; Incrase 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		; Incrase 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 take a closer look, you 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 (and one cycle) if you LDA $0DFB (3 bytes, 4 cycles) at first, PHA (1 byte, 3 cycles) it and put a PLA in front of each RTL(3 bytes but 4 cycles because only one is executed) (totally, 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
	LDA #$05	;\ Play "get 1up" SFX
	STA $1DF9	;/
	LDA $0DFB	; Restored
RTL

.no_item
	INC $0DC2	; Incrase 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		; Incrase 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

Checking $008F32 even further, you notice that LDA $0DFB isn't even needed at all. The code to add lives to the life counter and the decrase the coins - and such our code - only gets executed if $13CC (coins to yet add to, decrease each frame) isn't zero. And because of that, there is a small trick but let's go to the part where we NOP out/ skip the exessive bytes:

The few bytes after the JSL still can be executed as code and need to be skiped/ NOP'd out. That's not hard. 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.
Using the least efficient restoring to show how you restore more then one byte:
Code
lorom

org $008F2F
autoclean JSL new_reward
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
	LDA #$05	;\ Play "get 1up" SFX
	STA $1DF9	;/
	LDA $0DFB	; Restored
RTL

.no_item
	INC $0DC2	; Incrase 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		; Incrase 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. As explained above, the code to decrase the coins by 100 only gets executed if $13CC isn't 0 and $13CC gets decrased every frame. And because the coins rarely go over 100 (in fact, it shouldn't even), you simply can STZ $0DFB 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
	LDA #$05	;\ Play "get 1up" SFX
	STA $1DF9	;/
RTL

.no_item
	INC $0DC2	; Incrase 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		; Incrase 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.

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 to. JMP and JML don't use that funcion.

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

There is also an 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 programm bank to allow 16-bit addressing of a specific 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 and enhancement chips addresses ($2100-$7FFF, not everything is used) and still access the data in the current bank in absolute addressing.
Registers and most enhantement chips addresses are even only located in the mirrors.
Of course, you still can use 24-bit/ long addressing, even to the mirror area i.e. e.g. acessing $2100 through $002100 is not impossible.

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

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 likely data because these don't require any RAM mirrors) into banks $40+.

Sometimes, it is more efficient to not change the bank. A long address only takes one more cycle and byte than an absolute adress 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 always assume 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. You need to put a .l right after the opcode (e.g. LDA.l $FEDBCA).

Step 5: V-blank and H-blank.

There are cases where you want to create a patch which changes graphics or some other stuff. However, you can't easily do this. 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.
With NMI, you can access v-blank whereas an interrupt request (IRQ) is used for h-blank. And then, there is f-blank which is caused by turning the screen off (setting bit 7 of $2100).
Keep in mind that you need to wait for h-blank. To do that, use BIT $4212 : BVC $FB.

NMI starts at $00816A and IRQ starts at $008374.

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 NMI and IRQ 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 runs parallelly to the SNES so a few stuff are incompatible there.

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.

Some important/ helpful information and other stuff:

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 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
  • freespaceuse prints, how much freespace the patch uses
  • dec(value) and hex(value) display the value you entered in the braches (can be combined with the others).


Major changes on the first 512KB

It is also possible to totally 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 by 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 addition freespace is accessed through a code in the original ROM, fine, but autoclean don't work in freespace area.
Instead you use prot instead. It must be put right after a freespace command and the labels have to be seperated by a comma so you get something like this: prot label1, label2, ..., label 80 (yes, 80 is the limit, but you can have up to 128 freespace commands and you likely won't need that many freespace comands 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.

Preprocessor function

Similar to compilers, assembler features them too (or at least something similar) and Asar is not an exception.

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 I think they can be only done that way if they hasn't been used before or they can be every time but the codes after it uses new value, I think?

To do: Wait for someone to clarify me.

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.

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 do 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


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 ifs after, that's it).
else: If the condition is not true, Asar will assamble 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. The exception is if the code is put on the same line as the if (e.g. if *condition* : *code* work).
Even the boolean conditions are the same/ 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
That way, you can controll patches more easily.

It is even mandetory 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 "}") . Remember that while Asar ignores them, they are not threated 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 also only exists for purely aesthetic reasons. They don't do anything. If you want to, you can use header and fastrom (both do nothing) for these brackets instead.

Read from the ROM directly

Another of Asar strongest features are readX($yyyyyy) where X is any value from 1-4 and controlls 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 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 use prot, though.

Note: You can also put the file directly to an address, though the use it is rarer.

Preversing 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.

For more information on Asar's functions, read xkas' readme and Asar's manual (coming if you download the assambler).

SA-1

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

if read1($00FFD5) == $23
	!SA1 = 1
	sa1rom
else
	!SA1 = 0
endif

; Example usage
if !SA1
	; SA-1 base addresses
	!Base1 = $3000
	!Base2 = $6000
	!Base3 = $000000
else
	; Non SA-1 base addresses
	!Base1 = $0000
	!Base2 = $0000
	!Base3 = $800000
endif

Now you just add a "|!Base1" to addresses from $0000-$00FF and "|!Base2" to addresses from $0100-$1FFF (without quotes) at specific addresses i.e. addresses like "$0063" (or any other address from $7E0000-$7E00FF) and $1025" (or any other address from $7E0100-$7E1FFF) become "$0063|!Base1" and $1025|!Base2", respectively.
"!Base3" on other side is used for ROM addresses. On regular ROMs which are 4MB large or smaller, the whole ROM (minus the last 512KB*) are mirrorred through the banks +$80. Said mirror is also called the FastROM area. In order to achive FastROM, the codes have to run there (for some reason, don't ask me).
SA-1 ROMs doesn't feature FastROM because the SA-1 ROM type is something of an ExHiROM, not to mention that SA-1 runs much faster than FastROM.
Note: SNES's speed is still uneffected, though, 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 "!Base1". That is because direct page is already set to $3000.
Note: Sprites and some other addresses use completely different addresses on SA-1. Please refer to "Sprite Remap.txt" in the folder "docs" and the read me. Those addresses also can't be or'd.

For SA-1-only patches, you need to (or can but this one work 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
	+


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.)

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.

And Alcaro, Ladida and WYE, anything major to complain?

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. >_>
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}

--------------------
Okay, my layout looks ugly.
Pages: « 1 »
Forum Index - SMW Hacking - SMW Hacking Help - Tutorials - In-Depth Patch Creation Tutorial

The purpose of this site is not to distribute copyrighted material, but to honor one of our favourite games.

Copyright © 2005 - 2019 - SMW Central
Legal Information - Privacy Policy - Link To Us


Total queries: 9

Menu

Follow Us On

  • YouTube
  • Twitch
  • Twitter

Affiliates

  • Talkhaus
  • SMBX Community
  • GTx0
  • Super Luigi Bros
  • ROMhacking.net
  • MFGG
  • Gaming Reinvented