(Link to AcmlmWiki) Offline: thank ||bass
Register | Login
Views: 13,040,846
Main | Memberlist | Active users | Calendar | Chat | Online users
Ranks | FAQ | ACS | Stats | Color Chart | Search | Photo album
04-20-24 12:21 AM
0 users currently in SMW Hacking.
Acmlm's Board - I3 Archive - SMW Hacking - So You Wanna Learn ASM [ASM howto] New poll | |
Pages: 1 2 3 4Add to favorites | Next newer thread | Next older thread
User Post
Sukasa

Birdo
Not quite as active as before.
Xkeeper supporter
Xk > ||bass
I IP Banned myself! Twice!








Since: 11-17-05
From: Somewhere over there

Last post: 6271 days
Last view: 6270 days
Posted on 07-02-06 03:18 AM Link | Quote
NOTE: Edited post to only have main post table - side cells removed. Auto-updating users will still see them though... :/
(Credit goes to Glyph Pheonix for the original idea)
Yeah, this kind of a thread was quite helpful on the old board, so...

65816 ASM
Tutorial



Well, you're here to learn ASM, right? You can't find a good tutorial on google right? Or, you just needed some clarification, right? Good. Time to learn about ASM, how it works, and all that.

Now, you've heard about ASM and HDMA and DMA and all that stuff that hackers like BMF toss around, right? Well, Now you want to start to learn ASM, don'tcha?

First off


ASM stands for ASseMbly. Assembly is essentially a series of text commands, that are compiled into machine code to be inserted into a ROM.

Machine code is stuff like A9 30 8F 00 00 7E 60 and so on - not nice, right?

Right. Assembly is much easier to work with and learn.

So!

Your first Opcode


Your first opcode, eh? Well, are you ready kids?
Aye Aye, captain!

First off, Opcodes in 65816 ASM are ALWAYS one byte long. After that you have Anywhere from zero to four bytes that are part of the 1-byte opcode.

For example (you'll learn this opcode, and what it does, first)

LDA #$02 is two byte: 1 byte for the opcode, and one byte for #$02.

STA $7E0019 is 4 bytes: 1 byte for the opcode, and three bytes for $7E0019.

RTS is only one byte, becuase there is no data to go with the opcode, just the opcode itself.

So, the data is arranged sorta like this:

II (PP PP PP PP)

"II" is the opcode byte, and the "PP's" are the parameter byte, which vary from
opode to opcode, and aren't always there, depending on the opcode being used.
So, LDA #$02 would be two bytes: II PP, or $A9 $02. ($A9 is the opcode {LDA Immediate}, $02 is the parameter).

So, ready to learn your first opcode? Well, of course!

LDA (LoaD Accumulator)


LDA loads the accumulator with either an immediate vale (something like LDA #$40, where the accumulator is then set to $40), or set to a direct/indirect/long/whatever value, such as LDA $7E0019 (in which case the accumulator is loaded with the value stored at RAM address $7E0019).

Then, there is a second opcode you ned to know.

STA (STore Accumulator)


STA stores the contents of the accumulator to a RAM address (or a hardware register, but we'll get to those MUCH later on.).

STA is useful for storing the results of a math operation or copying small amounts of data.

Thirdly, There is also an important opcode you should know.

RTS (ReTurn from Subroutine)


RTS returns from a subroutine call (done by the JSR opcode).

It is very useful in returning from blocktool blocks when you are done (well, actually, it's the ONLY way)

Puttin' All that together


Well, now you know three opcodes.

  • LDA

  • STA

  • RTS


Let's look at how to put those together.

Say we want to make a cxustom block that makes you have a cape as soon as you touch it (for this example, assume there is no such block already).

Now, Mario's status is stored at $7E0019 (a RAM address),
and the cape status byte is $02, which means that we need to store #$02 to $7E0019. Here's how to do that in ASM.

LDA #$02
STA $7E0019
RTS


Now... The first line loaded the accumulator with #$02, which is that byte for having a cape.

The second line then stores that byte to $7E0019, which is where the game keeps track of mario's current status, instantly giving him a cape.

Lastly, the third line returns from the subroutine, finishing the code and preventing a crash.

Take only pictures, Leave only footprints


So, you made your very own custom block. Cool. Now, it's usually a good idea to not change the Accumulator or anything else when you make a block, because n ot doing so can have undesired effects.

NOTE: List to the following section carefully when you use the opcodes listed here, or your block will crash SMW!!!!!

First of all, When you want to save the content of the Accumulator, say if you didn't have ANY RAM where you could keep data that's in the accumulator, but you desperatly need to do some math, what do you do?

Simple! You make use of the stack.

...What's that? You don't know what th stack is? Silly me.

The stack is a section of (reserved ram? please let me know) that holds bytes that you push or pull from it with the corresponding opcodes. Think of it as a stack of books, to which you can add or remove blocks off the top. This is the stack.

OK, so back to the situtation involving the accumulator and math. To push the Accumulator, you use the opcode "PHA". To pull it to get it's original contents back, you pull it (PLA, thanks smallhacker).

IMPORTANT WARNING: However many times you push to the stack, your block must pull from it the EXACT same number of times before exiting it's routine. Failure to do so will CRASH SMW!


Smallhacker's advice

I.E. Push once, and you need to pull once.
Push nothing, and you may pull nothing.
Push 52 times, and you need to pull 52 times.

Why? Well, it's just how the SNES works. You see, the RTS command I showed you before finds out where to return to by pulling two bytes off the stack. Therefore, if you've pushed a different number of times that you've pulled, the RTS command will get the wrong return address, and SMW will crash.

Branching and Conditional operations


Now, what if you needed a block that made you super mario, but only when you are small mario? What do you do there?

Well, that's actually pretty easy. Thanks to a nice little opcode, call CMP (CoMPare), you can compare the contents of the accumulator to a set value, or a RAM address, and then get back a set of flags that certain other opcodes use to change where in the code yotu execute. It's like lookiong to see if mom is watching before you pur your hand in the cookie jar. After all, you wouldn't do it if she's looking, right?

Working in tandem with the CMP opcode are the Branch opcodes. There are close to a dozen of them, and they all divert your code to different sectiosn of code, depending on certain processor flags (don't worry about those just yet.)

So, the one Branch command we're worried about right now is the "BEQ" Opcode (Branch if EQual).

So, we know that $7E0019 is equal to 00 when you are small mario, so what you wan tto do is get the value of $7E0019, and then compare it to $00. Now, how do you use the branch command??????

When you're working in a text editor, such as notepad, the asnwer is labels.

Labels are like this:
Opcode 1
opcode 2
Label:
opcode 3
...


When you are coding, you can branch to labels by a method such as this:
BEQ Labelname

So... What you want to have coded so far is,
PHA //Good form to preserve the accumulator
LDA $7E0019
CMP #$00 //This is the compare instruction you just learned!


Now... in order to only make you big mario when you are small, you first need to type in the RTS command, and a label on the line above it. Note that labels are only used by the compiler and do not end up in the final .bin file!

PHA //Good form to preserve the accumulator
LDA $7E0019
CMP #$00 //This is the compare instruction you just learned!
//leave room to make you big mario and for the branch command
NoMakeBig:
RTS


And Finally, you need to add in the branch command, and then the lines that make you big.

PHA //Good form to preserve the accumulator
LDA $7E0019
CMP #$00 //This is the compare instruction you just learned!
BEQ NoMakeBig //See how this tells the SNES processor to branch to the instruction following the label "NoMakebig"?
LDA #$01
STA $7E0019
NoMakeBig:
RTS


There you go! Now your new block will make you big, but only if you are small, just like when you cross a midway point's bar!

The other Branch commands, and what they do.


BNE (Branch when Not Equal)
This command branches to another part of the code when you sya, compare The acuumulator with a RAM address, and the accumulator is 5 and the RAM address is 4. So... Not equal. Note that all branch commands are used the same way, Bxx Labelname, where "xx" is replaced by the two letters specific to a certain branch command.

BGE (Branch when GrEater than.)
BGE branches when the contents of the accumulator are more than what they are compared to.

BLT (Branch if Les Than)
BLT is the excat opposite of BGE - it branches when the accumulator is smaller than was it is compared to.

NOTE: BCS is the normal name for BGE. BCC = BLT, in case you ever see those names instead of BGE and BLT.



BMI (Branch if MInus)
This branches is the accumulator is over #$80, I believe. because of how compare works, if you compare one number to another larger than it, the BMI will trigger, otherwise the BPL (below) will trigger.

Somebody good with this opcode, please clarify this for me.



BPL (Branch if PLus)
This is the exact opposite of BMI, and it's action is discussed above.

Due to the nature of BVC and BVS, they are discussed at the end of the math chapter..



Doing math! !


So, you want to do math, huh? Well, with the 65c816, you can either add or subtract. You CANNOT multiply or divide with the 65c816, and I will cover the way to use the SNES's math Coprocessor in a later chapter.

Adding


To add two numbers togther, you use the ADC (ADd with Carry). Oh yeah, there is no straight add or subtract command in 65816 ASM. Only these semi-straight commands.

To use ADC, you first type in "CLC" (I will cover these knds of opcodes next chapter), then on the line below, you type in ADC xxx, where "xxx" is either a RAM address or an immediate value (Remember, #$xx).

Then, you accumulator will contain the two numbers added together.
NOTE: IF the two number equal more than #$FF, your answer will end up being #$100 less than it should be, so... #$80 + #$80 = #$00, and #$80 + #$90 = #$10. #$50 + #$40 = #$90. Get it?

SBC (SuBtract with Carry)
SBC does the opposite of ADC- it subtracts from the accumulator. Note that your accumulator is always the starting value, and the parameter for the command (the immediate value or RAM address) is always subtracted from that.

So,to use SBC you first need to use the command SEC. then, you add in (on the next line) SBC xxx, where "xxx" is an immediate value or a RAM addess, just like the ADC command.

SBC then stores in the accumulator the result of (Accumulator - SBC Parameter).

BVS and BVS


In any math operation, if your number either goes over FF in addition, or under $00 in subtraction, wrapping to the other value, A certain processor flag is set.

BVC (Branch when OVerflow Clear) will branch if the operation does not wrap from $FF to $00 or vice versa.

BVS (Branch when OVerflow Set) Is the opposite; it branches when the operation does wrap on the boundary, for $FF to $00 or vice versa.

Credit goes to DharkDaiz for the explanation of BVC and BVS.

The Processor flags: Part one


So... You saw how I used "CLC" and "SEC" in the last chapter, right? Well, now I suppose you're wondering what those commands are, am I right? Of course I am.

CLC stands for CLear Carry flag. Basicaly, it messes with one of 8 btis that the processor uses to keep track of what it is doing, i.e. the last results of a compare or a couple other things that I'll discuss later because they are more advanced. Basically, the carry flag is used during math as a sort of 9th bit, although it's not really usable in that you can't get it's value and use it in 8-bit math.

SEC does the same, except it messes with the carry flag in the opposite was of CLC. Oh, and SEC stands for SEt Carry flag.

The Stunted Twins: X And Y


Ah.. X and Y. These two little guys can't do math at all... Not are they as usable as the Accumulator. In fact, they cando only one thing each that the Accumulator can't do... Indexing. (Check a later chapter.)

Basically, the X and Y registers can hold values, and comparte them to RAM addresses, just like th eaccumulator. This is done by CPY and CPX (ComPare Y, ComPare X). Also, the X and Y value can use the stack, and are handy for keeping a certain value close at hand whilst keeping the Accumulator free for doing math and the like.

NOTE: Together, the Accumulator (henceforth to be known as "A"), X, and Y are known as the Registers in this tutorial

Counting by ones - Incrementing and Decrementing The Registers



Incrementing and decrementing a register by one can be done fairly easily, with a single instruction. this is true for the A, X, and Y registers. There are two instructions that do that for us.

First off, there is INC (INCrement). INC adds 1 to either A, X, Y, or a RAM value.

Secondly, there is DEC (Decremenr). DEC suntracts 1 from A, X, Y, or a RAM address.

Counting by ones, you say? Well, gosh darn it, what good does that do us?

A HELL of a lot of good. Counting by ones is especially good for loops, such as where you need to multiply two numbers together, and you can't use the multiplication registers.

So, we'll was that 7E0624 contains some number custom ASM put in that you need to multiply by $3. Well, what's a good way to do that? SIMPLE! A Loop!

Loops


Loops are sections of code that repeat a given number of times, before having execution move on somewhere else. Here is a simple loop that does absolutely nothing at all, save waste processor cycles:
PHA
LDA #$30
loop:
DEC A
CMP #$00
BNE loop
PLA
RTS


Now... follow how that works. First, you save the contents of A (it's good coding practice when hacking, in my opinion). Next, you load A with $30, and then thirdly, you subtract one from that.

Next, you see if you've hit $00 yet. if not, you go back to step three. if so, you pull the contents of A and then return.

Now... This is how that could carry over to a multiplication loop.

PHA
PHY //Push the Y register... Wer're using it in this code segment.
//Note that you coudl use X instead, it's all a matter of preference.
LDY $7E0624
LDA #$00
loop:
CLC
ADC #$03
DEY //This is DEC Y... Note that the
// C was simply changed into Y. The
//same holds true for X.
CPY #$00 //compare Y with $00
BNE loop
//Do something with the multiplied number.
PLY //Note order of instructions. However you push a set of registers,
PLA //You MUST pull them in the opposite fashion. Push , Y; Pull Y, A.
RTS


There you go, that's how use use loops and the decrement instruction. Note that increment could be used that way as follows:
PHA
PHY
LDA #$00
LDY #$00
loop:
CLC
ADC #$03
INY //Increment Y ... INC Y -> INY
CPY $7E0624
BNE loop
//DoStuffHere
PLY
PLA
RTS


Now, there is something else to consider here. Notice the CMP #$00/CPX #$00/etc.? you don't need them, and this is why: when you modify a register value, the processor flags are changed just like when you use CMP. IF you had X a tone, for example, and you decremented it, it would be zero and so the zero flag would be set. the zero flag is a processor flag. The same thing occurs if you CPX #$00 when X is zero, the zero flag is set because #$00 == #$00. BNE breaks when the zero flag is not set, or when the last operation gave you a number other than zero.

Bit Widths


OR: Counting to 65535



So, what are bit widths? Well, I'll tell you.

Bit widths control the maximum number one of the registers can hold. The registers can either hold an 8-bit number, or a 16-bit number.

So far, you have learned SNES ASM with only 8-bit numbers. However, there are times where 8-bit is not enough to hold the data you need. A Good example of this is the score. Say you need to make a block that becomes passable ONLY when your score is over 5000. Well, the score is stored in RAM as 500, and the extra zero is placed in by graphics.

But, here's you problem. an 8-bit register only goes up to 255, and 500 > 255. So, what do you do? Easy. you change either the accumulator,. or the X/Y register's bit width to 16-bit.

To do this, I'll use the accumulator as an example.

REP #$20 (REset Processor bits)

This command sets the processor bitflag for the accumulator's bit width to the 16-bit setting. Thus, you can load the score's full value into the accumulator and compare it.

NOTE: When you are in 16-bit mode, the immediate opcodes, like LDA #$66, become two-bytes long to the processor. Therefore, LDA #$55 when your accumulator is 16-bit will crash the SNES! Instead, add the ".W" string to the end of the number or opcode to tell your compiler that the immediate number is 16-bit, or simply use a value greater than $00FF.

Also, to reset the accumulator to 8-bit, use the command

SEP #$20 (SEt Processor bits)

This resets the accumulator to 8-bit mode.

Not that REP #$10 and SEP #$10 control the bit widths of BOTH the X and Y registers as one. You CANNOT have the Y register one bit width and the X register another, they MUST be the same.

It's a hardware thing.

What You'll Need


To write ASM, all you need is a text editor like Notepad, or it's equivalent, and an ASM compiler, such as SPASM, X816112f, or whatever compiler you prefer. http://www.zophar.net is a good place to find compilers, however you should pick the one that is best for you, and it might not be at zophar's! And for a complete listing of all opcodes:
65816ref.hlp

There is plenty more to learn about ASM, and over the next while I'll continue updating this with more info. However, for now I'm going to leave it at this, and edit in more info later on. Hope this helps a little in the meantime!


(edited by Sukasa on 12-17-06 08:24 PM)
Mike O'Shay

Red Koopa








Since: 03-16-06
From: Mushroom Kingdom?

Last post: 6469 days
Last view: 6270 days
Posted on 07-02-06 03:38 AM Link | Quote
Whoa... this is probably the most helpful ASM tutorial I've seen so far. I almost get sense out of it. All I think I need to know how: Is there a certain program needed for working with ASM, and if so, what program? Will a regular Hex Editor work, or is it completely different?
Sukasa

Birdo
Not quite as active as before.
Xkeeper supporter
Xk > ||bass
I IP Banned myself! Twice!








Since: 11-17-05
From: Somewhere over there

Last post: 6271 days
Last view: 6270 days
Posted on 07-02-06 03:39 AM Link | Quote
Notepad, or it's equivalent, and the ASM compiler of your choice.

*Updates first post*
Mike O'Shay

Red Koopa








Since: 03-16-06
From: Mushroom Kingdom?

Last post: 6469 days
Last view: 6270 days
Posted on 07-02-06 03:41 AM Link | Quote
Thanks. And to find an ASM Compiler, there's probably a link on the board, but are there any ones you'd suggest?

(Note: I both need these answers, yet I'm also typing them up in a way that'd be pretty good for them to be edited into the tutorial. )
Sukasa

Birdo
Not quite as active as before.
Xkeeper supporter
Xk > ||bass
I IP Banned myself! Twice!








Since: 11-17-05
From: Somewhere over there

Last post: 6271 days
Last view: 6270 days
Posted on 07-02-06 03:43 AM Link | Quote
Posted a link into the tutorial for ya.

EDIT: And I personally use X816112F. Just remember to add ".B" after the 3-letter instruction for 8-bit immediate values, and ".W' for 16-bit immediate values.


(edited by Sukasa + on 07-02-06 02:44 AM)
Mike O'Shay

Red Koopa








Since: 03-16-06
From: Mushroom Kingdom?

Last post: 6469 days
Last view: 6270 days
Posted on 07-02-06 03:44 AM Link | Quote
Thanks, this is really helpful. I might actually be able to get a decent hack out of my lazy, procrastinating self. o.O

Fantastic tutorial.
Smallhacker

Super Koopa
I AM A Group Of Officially Frustrated Younglings, G.O.O.F.Y. MEMBER








Since: 11-17-05
From: Söderhamn, Sweden

Last post: 6272 days
Last view: 6270 days
Skype
Posted on 07-02-06 07:34 AM Link | Quote
I belive this thread deserves to be *Stickied*.
Sukasa

Birdo
Not quite as active as before.
Xkeeper supporter
Xk > ||bass
I IP Banned myself! Twice!








Since: 11-17-05
From: Somewhere over there

Last post: 6271 days
Last view: 6270 days
Posted on 07-02-06 09:02 AM Link | Quote
Update: Added info on pushing and pulling.
Smallhacker

Super Koopa
I AM A Group Of Officially Frustrated Younglings, G.O.O.F.Y. MEMBER








Since: 11-17-05
From: Söderhamn, Sweden

Last post: 6272 days
Last view: 6270 days
Skype
Posted on 07-02-06 10:05 AM Link | Quote
You forgot to mention that the opcode for pulling is 'PLA'.
Mozeki

160








Since: 03-17-06
From: NY

Last post: 6452 days
Last view: 6452 days
Posted on 07-02-06 04:33 PM Link | Quote
OMG Thank you very much

ASM makes more sense after reading that tutorial, but I do have one question which if you can answer i'll be able to do some ASM threw soon HEX soon. What you said about the making the custom black and mario, you use the three opcodes and then return the subroutine. Am I suppose to gues which byte to use? Is there a something that breaks the bytes to different sections? I want to know cause I could use a different byte and that could screw the game up. So I guess my question is How do I find which byte is should use? and which byte does which so I don't screw up.
Sukasa

Birdo
Not quite as active as before.
Xkeeper supporter
Xk > ||bass
I IP Banned myself! Twice!








Since: 11-17-05
From: Somewhere over there

Last post: 6271 days
Last view: 6270 days
Posted on 07-02-06 10:24 PM Link | Quote
Actually, for this I used ASM in a text editor as an example. I;'ll make a link to a help file that lists out ALL of the opcodes in 65816 ASM and their hex equivalents. For now, Don't Use Hex! If you're new to ASM, I HIGHLY recommend using a compiler and notepad. it's much easier, and lets you get a handle on the language before trying to read and write it in hex. Also: If you want to change the byte that mario uses, change the line LDA #$02 to LDA #$xx, with xx being replaced by the status byte you want to use ($00 to $03) Also, just for you, the hex byte to change would be byte 0x1 (the first byte being 0x0), from $02 to whatever. Read back on how the bytes are arranged with the opcodes and all, after I add a bit mroe data to that.
spel werdz rite









Since: 11-19-05

Last post: 6271 days
Last view: 6270 days
Posted on 07-03-06 12:36 PM Link | Quote
I have a huge document giving a more in-depth talk of all the opcodes. Soon as I finish, I'll PM it to you Sukasa.
Sukasa

Birdo
Not quite as active as before.
Xkeeper supporter
Xk > ||bass
I IP Banned myself! Twice!








Since: 11-17-05
From: Somewhere over there

Last post: 6271 days
Last view: 6270 days
Posted on 07-03-06 01:20 PM Link | Quote
Cool. I won'tr use all of it right away, though. I waill add sections uch fasdter, but I want to ease people who don't know ASM into it, not try and bombard them with too much information (Like all the guides to ASM I tried to understand)
Giga_Bowser_009

Red Paragoomba


 





Since: 04-16-06

Last post: 6489 days
Last view: 6489 days
Posted on 07-04-06 10:38 AM Link | Quote
Not to be off-topic or anything but can the same thing be done with NES ASM? I've always wanted to make a custom block for SMB3 (? Block containing a Poison Mushroom is an example of a custom block.) This way, my hack will be done once I finish all the level designs. But anyway this is an excellent tutorial, I can use that to put in Giant Lakitus into Super Mario World.
VinceIP

Blue Octorok


 





Since: 05-29-06
From: Ohio, USA

Last post: 6438 days
Last view: 6438 days
Posted on 07-04-06 12:49 PM Link | Quote
I have a quick question before I dive into Assembler hacking. If I were making a custom SMW block, or even one for SMB3, would I be able to add a new one to the game, or would I need to replace a block already there?
Prince Kassad

320
As you wish.








Since: 06-30-06
From: nowhere

Last post: 6271 days
Last view: 6271 days
Posted on 07-04-06 12:59 PM Link | Quote
For SMW you can add as many custom blocks as you like. I don't know for SMB3 though.
Prince Kassad

320
As you wish.








Since: 06-30-06
From: nowhere

Last post: 6271 days
Last view: 6271 days
Posted on 07-04-06 02:31 PM Link | Quote
Originally posted by Sukasa +
http://www.zophar.net is a good place to find compilers.

Last time I looked, there is only one SNES ASM compiler on Zophar's Domain (and you already named it in your tutorial), so you might want to take out this sentence.
Sukasa

Birdo
Not quite as active as before.
Xkeeper supporter
Xk > ||bass
I IP Banned myself! Twice!








Since: 11-17-05
From: Somewhere over there

Last post: 6271 days
Last view: 6270 days
Posted on 07-05-06 06:29 PM Link | Quote
I coudl have sworn there were more compiles for SNES... I'll have to find a better site.

Anyways, added a bit on CMP and BEQ. Next I'm going to introduce more Break operations.

Now, the processor flag instructions (SEC, CLC, and all that)

After that, the X and Y registers!


(edited by Sukasa + on 07-06-06 12:15 PM)
spel werdz rite









Since: 11-19-05

Last post: 6271 days
Last view: 6270 days
Posted on 07-06-06 04:44 PM Link | Quote
Good, out of everything else, I'm not that good with X and Y.
Sukasa

Birdo
Not quite as active as before.
Xkeeper supporter
Xk > ||bass
I IP Banned myself! Twice!








Since: 11-17-05
From: Somewhere over there

Last post: 6271 days
Last view: 6270 days
Posted on 07-06-06 09:58 PM Link | Quote
Filled in incrementing, and X and Y registers, and all you'd need ot know about the processor flags for now.

Next up: Bit widths!
Pages: 1 2 3 4Add to favorites | Next newer thread | Next older thread
Acmlm's Board - I3 Archive - SMW Hacking - So You Wanna Learn ASM [ASM howto] |


ABII

Acmlmboard 1.92.999, 9/17/2006
©2000-2006 Acmlm, Emuz, Blades, Xkeeper

Page rendered in 0.024 seconds; used 474.80 kB (max 619.63 kB)