Points of Required Attention™
Please chime in on a proposed restructuring of the ROM hacking sections.
Views: 88,481,818
Main | FAQ | Uploader | IRC chat | Radio | Memberlist | Active users | Latest posts | Calendar | Stats | Online users | Search 04-25-24 01:04 PM
Guest: Register | Login

0 users currently in ROM Hacking Archives | 1 guest

Main - ROM Hacking Archives - 65816 ASM tutorial New thread | Thread closed

Pages: 1 2

Sukasa
Posted on 02-20-07 06:47 PM (rev. 5 of 10-09-09 03:56 PM) Link | Quote | ID: 2266


Red Birdo
Level: 92

Posts: 16/2112
EXP: 7688808
Next: 68129

Since: 02-19-07

Last post: 4447 days
Last view: 3218 days
(Credit goes to Glyph Pheonix for the original idea)
Yeah, this kind of a thread was quite helpful on the old board, so...
NOTE: I tried to finish this up as best I could from the old board. If there are any problems, I'm adfraid I won't be able to fix them until editpost.php is completed. Until then, please hang tight

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 the stack is? Silly me.

The stack is a section of (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, but only 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 into the Accumulator, you pull it (PLA, thanks smallhacker). There are other Push/Pull opcodes, but those are for later.


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
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 than 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 put 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 "BNE" Opcode (Branch if Not 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:
BNE 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!
BNE 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.

BEq (Branch when EQual)
This command branches to another part of the code when you say, compare The acuumulator with a RAM address, and the accumulator is 5 and the RAM address is 5 as well. So... 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 or equal to 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 at one, 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!

Please post any questions you'd like answered or any additions/corrections you might have for this tutorial.

Bio
Posted on 02-20-07 09:43 PM Link | Quote | ID: 2385


Red Paragoomba
Level: 19

Posts: 4/58
EXP: 34991
Next: 786

Since: 02-19-07

Last post: 5833 days
Last view: 5696 days
(from SMW central, this is about the different adressing mode)

first of all , there is immediate adressing mode, it is used when an opcode should affect the next value insted of a RAM/ROM adress

here an example code of how to use it

lda #$9f ;load #$9F into A
and #$08 ;perform logical AND with A and #$08
ldy #$65 ;load #$65 into Y

secondly, there is long adressing mode, long adressing is 3 byte long, allowing it to reach any SNES bank, however, it is more limited, bigger and slower than the other

an example of long adressing:

lda #$9f ;load #$9F into A
and #$08 ;perform logical AND with A and #$08
ldy #$65 ;load #$65 into Y
sta $7E0019; store A to RAM $7E0019
LDA $7E0d0C; load the value located at RAM $7E0d0c in A
STA $7Fefff; store A to RAM $7FEFFF


the third one is absolute adressing, abslute adrssing is like long adressing, but only 2-byte long, the highest byte is replaced by the value in the data bank register. However, since RAM adress 7E0000-7E1FFF is mirrored in 0000-1FFF of every ROM bank

previous code, optimised with the use absolute adressing

lda #$9f ;load #$9F into A
and #$08 ;perform logical AND with A and #$08
ldy #$65 ;load #$65 into Y
sta $0019; store A to RAM $7E0019
LDA $0d0C; load the value located at RAM $7E0d0c in A
STA $7Fefff; store A to RAM $7FEFFF

the data bank register can be changed by only one instruction(PLB), therefor, a value can be written to it using something like this:

LDA #$80
PHA
PLB

this code should change the data bank register

the fourth one is direct adressing mode, it is similar to absolute adressing, except that the value is only 1-byte long and that the highest byte is set by another register(called direct page), since direct page appear to be alway 0 in SMW, this adressing mode should be used to acess RAM adress 7E0000- 7E00FF

previous code, optimised with the use direct adressing

lda #$9f ;load #$9F into A
and #$08 ;perform logical AND with A and #$08
ldy #$65 ;load #$65 into Y
sta $19; store A to RAM $7E0019
LDA $0d0C; load the value located at RAM $7E0d0c in A
STA $7Fefff; store A to RAM $7FEFFF

the next one is indexed with X or Y, it add the value of X or Y to the destination of the opcode. since exemple are the best, here one:
LDX #$04
lda $0DB2,x

in that case the adress that LDA will load from will be $XX:ODB2+04

Finally, the last one is indirect and indirect long, they are noticable because they have () around their pointer([] for long). This one is weird, instead of acessing the data, it, interpret the data as a point to the real data. since this is quite complex, here an example:
$7E00000= 52 8D $7E00015 = b0 9C 0D

lda ($00)
sta [$15]


the lda will load from adress $XX:8D52 because 7E00000 is 52 8D (the pointer is byte-shifted). the sta will store at adress $0D9CB0 since it's indirect long, so the pointer is 3 byte.

If you have any question just ask in this thread

Hamtaro126
Posted on 06-17-07 12:38 AM Link | Quote | ID: 46164


Cheep-cheep
Level: 33

Posts: 16/194
EXP: 212822
Next: 16357

Since: 05-02-07
From: Shelton, WA

Last post: 2472 days
Last view: 2314 days
Posted by Sukasa
Also, I wrote a little program for writing 65c816 ASM: 65_IDE.exe Anyways, please post any questions you'd like answered or any additions/corrections you might have for this tutorial.


Sukasa: If you are still here, please make a fix. Because it requires AS_IFCE1.OCX. I looked for it on google and many websites said it's ''Bad for your PC!'' and is ''Malware''. After you fix it, upload it.

____________________
Mah boi, romhacking is what all true warriors strive for!

I wonder what's for dinner?

Sukasa
Posted on 06-17-07 01:50 AM Link | Quote | ID: 46179


Red Birdo
Level: 92

Posts: 272/2112
EXP: 7688808
Next: 68129

Since: 02-19-07

Last post: 4447 days
Last view: 3218 days
Odd... Anyways, I'll upload a .zip of it and update. thanks for pointing that out

Luigiownsmario
Posted on 07-22-07 09:54 PM Link | Quote | ID: 56863


Micro-Goomba
Level: 9

Posts: 1/11
EXP: 2854
Next: 308

Since: 07-22-07
From: Hiding somewhere on yoshi's island.

Last post: 6108 days
Last view: 6100 days
Could you please give me a link to a good snes compiler, i couldn't find one at zophar.net. thanks in advance.

____________________

Sukasa
Posted on 07-22-07 09:58 PM Link | Quote | ID: 56864


Red Birdo
Level: 92

Posts: 370/2112
EXP: 7688808
Next: 68129

Since: 02-19-07

Last post: 4447 days
Last view: 3218 days
Hm. You could google "WLA-DX" for a SNES ROM compiler, but I use x112f from zophar's for small little code segments, etc... It's a little difficult to use (You need to add ".B" and ".W" to LDA/LDY/CMP/etc.) but otherwise it's nice.

Luigiownsmario
Posted on 07-22-07 10:00 PM Link | Quote | ID: 56866


Micro-Goomba
Level: 9

Posts: 2/11
EXP: 2854
Next: 308

Since: 07-22-07
From: Hiding somewhere on yoshi's island.

Last post: 6108 days
Last view: 6100 days
Thanks.

____________________

GameGenie81
Posted on 01-30-08 09:33 AM (rev. 2 of 04-22-08 09:42 PM) Link | Quote | ID: 75594


Koopa
Level: 24

Posts: 32/100
EXP: 78089
Next: 36

Since: 08-15-07
From: Milwaukie, OR

Last post: 5432 days
Last view: 5386 days
So, if I know how to do this, then I may be all set here:

$90F9:A0 05 - LDY #$05: This tells the game to load #$05 into Y.
$9106:AC 4E 07 - LDY $074E = #$01: This tells the game to load the value stored at RAM address $074E into Y.
$9109:AD 43 07 - LDA $0743 = #$00: This tells the game to load the value stored at RAM address $0743 into A.
$9113:85 FB - STA $00FB = #$80: This tells the game to store the value located at RAM address $00FB into A.

Also, what does ORA mean?

~Ben (GameGenie81)



____________________
We are a nation with no geographic boundaries, bound together through our beliefs. We are like-minded individuals, sharing a common vision, pushing toward a world rid of color lines.

carsr4carpeople1
Posted on 02-18-08 08:16 PM Link | Quote | ID: 78165


Red Goomba
Level: 16

Posts: 1/40
EXP: 19593
Next: 663

Since: 11-22-07
From: Illinois, USA

Last post: 5343 days
Last view: 4823 days
I dont understand the concept of the stack.. why exactly would you need to push/pull from it?

____________________

Come join #NekoCentral on IRC!

blackhole89
Posted on 02-18-08 09:17 PM (rev. 2 of 02-18-08 09:23 PM) Link | Quote | ID: 78174


The Guardian
Moloch whose eyes are a thousand blind windows!
Level: 124

Posts: 1435/4196
EXP: 21531381
Next: 305220

Since: 02-19-07
From: Ithaca, NY, US

Last post: 470 days
Last view: 83 days



Visualize the stack as a stack of paper. "Pushing" is an equivalent of putting a sheet on top of it, "pulling" equals taking the top sheet off.

This has several advantages from a programming model point of view. It's a very trivial operation (push = write to offset at stack pointer, decrement stack pointer; pull = read from offset pointed to by stack pointer, increment it) and allows you to structure your code conveniently since it provides a facility of storing and retrieving data without the possibility of collisions - as long as you don't overstep memory boundaries, you are guaranteed to get a yet-unused space to write data to and to be able to retrieve it a later point.
Most notably, it allows for the most common implementation of subroutines which works, in its simplest version, by pushing the current instruction pointer onto the stack and overwriting it with the subroutine address; the return opcode pulls the calling function's instruction pointer from the stack. This allows a subroutine to operate cleanly and return to the original location regardless of what it does itself and where it is called from.

____________________



Sukasa
Posted on 02-19-08 03:32 PM Link | Quote | ID: 78315


Red Birdo
Level: 92

Posts: 658/2112
EXP: 7688808
Next: 68129

Since: 02-19-07

Last post: 4447 days
Last view: 3218 days
Posted by GameGenie81
So, if I know how to do this, then I may be all set here:

$90F9:A0 05 - LDY #$05: This tells the game to load #$05 into Y.
$9106:AC 4E 07 - LDY $074E: This tells the game to load the value stored at RAM address $074E into Y.
$9109:AD 43 07 - LDA $0743: This tells the game to load the value stored at RAM address $0743 into A.
$9113:85 FB - STA $FB: This tells the game to store the value in A to RAM address $FB.
Fixed, but this one is a doozy. It also uses the direct page register, which I don't think I cover in this tutorial.
Also, what does ORA mean?

~Ben (GameGenie81)




ORA means "OR Bits with A". For example, #$00 OR #$80 would be #$80, while #$82 OR #$80 would be #$82. to view it in binary, 00000000 OR 10000000 is 10000000, and 10000010 OR 10000000 is 10000010.

Heian
Posted on 02-19-08 03:56 PM Link | Quote | ID: 78320


Red Koopa
Level: 27

Posts: 101/126
EXP: 111881
Next: 4278

Since: 03-08-07

Last post: 5846 days
Last view: 5845 days
Newbie question: what would 11110000 OR 00001110 be? 11111110, because you get 1 if the corresponding bit in either value is 1?

blackhole89
Posted on 02-19-08 04:00 PM Link | Quote | ID: 78321


The Guardian
Moloch whose eyes are a thousand blind windows!
Level: 124

Posts: 1438/4196
EXP: 21531381
Next: 305220

Since: 02-19-07
From: Ithaca, NY, US

Last post: 470 days
Last view: 83 days



Yes, assuming it's a bitwise OR.
(In C/C++ boolean OR, it'd be 1, but I doubt that's what you mean anyway)

____________________



Heian
Posted on 02-19-08 04:18 PM Link | Quote | ID: 78322


Red Koopa
Level: 27

Posts: 102/126
EXP: 111881
Next: 4278

Since: 03-08-07

Last post: 5846 days
Last view: 5845 days
No, I was indeed talking about the bitwise ORA that was being asked about by Ben. This thread is looking very useful to ASM beginners like myself.

Question (and this might be hopelessly oversimplified...): If you want to display some value on the screen, and you know its address in the ROM, how do you go about doing that? Let's say that a baseball game displays a batter's average (stored at 0xA0000 and A0001) and his home runs (0xA0004), and you know where the font graphics for those digits are stored in the ROM. Now you want to show his speed (0xA0006) in addition -- what do you do?

blackhole89
Posted on 02-19-08 04:30 PM Link | Quote | ID: 78323


The Guardian
Moloch whose eyes are a thousand blind windows!
Level: 124

Posts: 1439/4196
EXP: 21531381
Next: 305220

Since: 02-19-07
From: Ithaca, NY, US

Last post: 470 days
Last view: 83 days



Knowing that and where they are being stored in the ROM is not nearly as useful as knowing that and where they are stored in the PPU (from having been uploaded before), which allows you to deduce the tile numbers.
Graphics in SNES are all but a trivial manner, so, instead of explaining it in own, possibly confusing from my exhaustion words, I'll just refer you to this invaluable document.

____________________



Heian
Posted on 02-19-08 04:40 PM Link | Quote | ID: 78324


Red Koopa
Level: 27

Posts: 103/126
EXP: 111881
Next: 4278

Since: 03-08-07

Last post: 5846 days
Last view: 5845 days
Now there's a challenging read! I'll have a look at that; thanks!

MathOnNapkins
Posted on 02-22-08 09:17 PM (rev. 2 of 02-22-08 09:18 PM) Link | Quote | ID: 78585


Super Koopa
Level: 62

Posts: 317/842
EXP: 1935368
Next: 49318

Since: 02-19-07
From: durff

Last post: 4487 days
Last view: 4010 days
Just to elaborate a bit, anything in Qwertie's doc that seems incomplete or hard to understand can be better understood by reading Anomie's documents (register document specifically.) Qwertie's doc is the major one I learned on, but I'm aware of some of its flaws too :/. And for something neither document really makes things crystal clear, and you'll have to mess with it yourself to truly understand how it works.

____________________
Zelda Hacking Forum
hobbies: delectatio morosa

Thanatos-Zero
Posted on 11-27-08 08:02 PM Link | Quote | ID: 95027


Nipper Plant
Level: 45

Posts: 10/423
EXP: 652792
Next: 7372

Since: 11-25-08
From: Germany - Rheinlandpfalz - Wittlich - Zur Phillippsburg 25

Last post: 1090 days
Last view: 1052 days
I have just tried to read all this, but I got a blackout, by all this information . If this continues, I will never be able to expand my abilities to hack. I can only hack palettes with a Hex-Searcher. It´s so much frustating.

____________________
I was a prisoner enclosed in the void, were everything might end up just as myself.
There I was called the death in the void, but after a long sleep my drill was ready to pierce the void.
I came back... to guide those who are in doubt and to crush any corrupted mind.
"Just who in the hell do you think I am?!".

GameGenie81
Posted on 12-15-08 12:47 AM Link | Quote | ID: 96778


Koopa
Level: 24

Posts: 88/100
EXP: 78089
Next: 36

Since: 08-15-07
From: Milwaukie, OR

Last post: 5432 days
Last view: 5386 days
What do all these definitions mean? Pardon me for repeating this here again... but I do need better analysis pertinent to this.

Video game: Super Mario Bros. (Nintendo, N.E.S., 1985)

ChkAreaType:
$9106: AC 4E 07 - LDY $074E
$9109: AD 43 07 - LDA $0743
$910C: F0 02 - BEQ $9110
$910E: A0 04 - LDY #$04

StoreMusic:
$9110: B9 E7 90 - LDA $90E7,Y
$9113: 85 FB - STA $FB (this senses the ROM page of $FB00-$FBFF)
$9115: 60 - RTS

What goes into the "Y" register that causes any of the following?
If "4E" in 0x1117 is changed to "8C" - Water music plays all the time, except for the pipe intro and cloud scenes. The value of RAM address $078C is #$00.
If "4E" in 0x1117 is changed to "70" - Ground music plays all the time (bar pipe intro and cloud scenes). The value of RAM address $0770 is #$01.
If "4E" in 0x1117 is changed to "61" - Underground music plays all the time (bar pipe intro and cloud scenes). The value of RAM address $0761 is #$02.
Default byte "4E" (in 0x1117) senses all six pieces of BGM (the bytes that span from 0x10F7 to 0x10FC; which are stored according to LDA "absolute, Y" instruction in 0x1120) play in their proper scenes.

In other words, what values of the RAM addresses, when I play with bytes in ROM addresses 0x1117-1118 (or any other absolute ROM instruction), may arise once I do the more complicated math?

Thank you,
Ben

____________________
We are a nation with no geographic boundaries, bound together through our beliefs. We are like-minded individuals, sharing a common vision, pushing toward a world rid of color lines.

Iceguy
Posted on 07-25-09 07:57 AM Link | Quote | ID: 111143

Newcomer
Level: 8

Posts: 2/8
EXP: 1660
Next: 527

Since: 07-25-09

Last post: 5384 days
Last view: 5387 days
I know this tutorial was made back in 2007, but there's still quite a few bugs:

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

You preserved the accumulator, but you didn't pull it back, so you need to pull a PLA right before the NoMakeBig.

"You CANNOT multiply or divide with the 65c816"

ASL and LSR both work to multiply/divide an address by 2 each time you use it.

"These two little guys can't do math at all... "

You can do INX/INY/DEX/DEY and you have also showed that in your tutorial, but you can't use do INX $RAM or DEX $RAM (same with Y). They're good for loops though.

Pages: 1 2


Main - ROM Hacking Archives - 65816 ASM tutorial New thread | Thread closed

Acmlmboard 2.1+4δ (2023-01-15)
© 2005-2023 Acmlm, blackhole89, Xkeeper et al.

Page rendered in 0.032 seconds. (340KB of memory used)
MySQL - queries: 117, rows: 145/146, time: 0.019 seconds.