• Our software update is now concluded. You will need to reset your password to log in. In order to do this, you will have to click "Log in" in the top right corner and then "Forgot your password?".
  • Forum moderator applications are now open! Click here for details.
  • Welcome to PokéCommunity! Register now and join one of the best fan communities on the 'net to talk Pokémon and more! We are not affiliated with The Pokémon Company or Nintendo.

GBA ASM Programming

Shiny Quagsire

I'm Still Alive, Elsewhere
697
Posts
14
Years



GBA ASM Programming
From scratch and on Pokemon Games



Introduction

If you've ever had to learn about computers, most people say: "The computer uses 0's and 1's," and "Programmers write applications all in 0's an 1's." That's only partially true. Machine code usually is written in C/C++, which is then compiled to Assembly Code, and then 1's and 0's.

What is Assembly Language

ASM is a low-level language that (When compiled) processors use to interpret instructions. Here's how Wikipedia defines it:


Assembly language is a low-level programming language for computers, microprocessors, microcontrollers, and other integrated circuits. It implements a symbolic representation of the binary machine codes and other constants needed to program a given CPU architecture. This representation is usually defined by the hardware manufacturer, and is based on mnemonics that symbolize processing steps (instructions), processor registers, memory locations, and other language features. An assembly language is thus specific to a certain physical (or virtual) computer architecture.



The processor and ASM language the GBA uses is ARM 7, (which is a lot easier compared to Z80, the GBC processor, in my opinion). When you write ASM, All you can do is Add, Subtract, Multiply, load data, and store data. There is no magical "Change the name" command.

You may have noticed there is no divide. The problem is that there are no decimals in ARM 7 ASM, and division is generally harder on the processor to calculate. There are BIOS functions to divide, but there are some tricks to doing simple dividing that I'll explain later.


What We'll need:

  1. VisualBoyAdvance
  2. A compiler - To put our ASM into Hex
  3. VBA-SDL-H - For testing, or debugging, our ASM to see how it works or to find problems.
  4. A Brain XD


Let Me Try! Let Me Try!

I think the best way of learning is to let you see what you can do with this knowledge. So, first, download my ASM Pack, which includes:

  1. A compiler
  2. HackMew's Nifty Batch File - Thanks HackMew!
  3. Some ASM Code

With that downloaded and extracted, go into the command prompt. After all, the assembler is command prompt based.

If you don't know how to use the command prompt, just google 'Command Prompt Tutorials.' You'll find something ;)

When you open it, you'll see somthing similar to this:


Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.

C:\Documents and Settings\XXXXXX>


Now, navigate to the directory you placed it in. For me, it's in a folder on my desktop called ASM. Try to keep folder names short, and simple.

Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.

C:\Documents and Settings\Maxamillion>cd Desktop\ASM\tutorial

Now we need to assemble it, so type in this:
Code:
[b]thumb example1.asm example1.bin[/b]

Here's our output:

Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.

C:\Documents and Settings\Thomas>cd Desktop\ASM\tutorial
C:\Documents and Settings\Thomas\Desktop\ASM\tutorial>thumb example1.asm example1.bin
Assembled successfully.
Press any key to continue . . .


Obviously press any key, and we're done!


What a Piece of Junk!

OK, so we assembled our routine, but how do we use it?! First, open up our example1.bin we assembled into a hex editor. You should get something like this:


03 B5 02 48 80 88 02 49 08 80 03 BD 84 42 02 02 D0 70 03 02


Now, open your Pokemon ROM into a hex editor, and copy it to a free-space address ending with 0, 4, 8, or C. You may ask, "Why not 5? or 7?" Because these numbers aren't multiples of four, and it uses bit 0, a bit used to tell the game "Do you want to boot into Thumb or ARM?" So, once you have it copied, you'll need to set bit 0 to 1, or, simply put, add 1 to your offset. So if I placed it at 0x720000, I'd add 1, making it 0x720001.

With this inserted, how do we use it through a script? (Please note this is an ASM tutorial, and no questions regarding scripting will be answered, unless it's a problem with booting the ASM in the script.) The answer is simple. We use the magical callasm command. So, here's an example script to use, since you don't know how the ASM works yet.


#dynamic 0x800000

#org @start
lock
faceplayer
callasm 0xsomeoffset
buffernumber 0x0 lastresult
msgbox @msg 0x2
release
end

#org @msg
= Wow, your POKEMON is at level [buffer1]!


Once that's inserted, go ahead and go to VisualBoyAdvance and open your script. Here's what your result should look like:
ddj5za.png


But was that it's level? Let's check!
v7uqf9.png


There it is! But how'd the ASM return it? That will be explained in the next section.


Looking? Found someone you have, eh?
~Yoda


Alright, so we've had some fun, but we've never seen what's going on in the ASM, or the script. Now let's see how it works by looking at Pokemon data, then explain each command one by one in the ASM routine.

Pokemon Data
The Pokemon data can be explained in detail here. You can check it out to see where some things are. The stats and such don't start untill about byte 80.
The byte we are retrieving, is the level, 1 byte with a maximum of 256. Let's see how we load those bytes by opening the ASM file according to your game. They'll all be slightly different, but they all have the same commands at a glance.

.align 2
This aligns the ASM by a multiple of 2, since each instruction is 2 bytes long.

.thumb
This tells the assembler that we are using the thumb instruction set. If we were using ARM, we'd use .ARM

main:
This is label. Labels are ways to call parts of a routine without calculations or repeating things over and over. Labels are text (Without spaces), and end with a colon. This label is called "main"

push {r0-r1,lr}
This pushes registers r0, r1, and the link register into the stack.

Registers are like variables that store temporary values and information. Registers are 32 bits long, making the highest value possible FFFFFFFF.
The stack is a bit hard to explain. Think of 15 stacks of china plates. Each of them has a different design on them. When you push a register into the stack, it's like adding a new plate as the one on the top to the stack. Here's a visual representation from Wikipedia:
391px-Data_stack.svg.png

The Link Register tells the processor where to return from the parent routine that called this one.

ldr r0, pokemon_data
This loads a 32 bits at the label 'pokemon_data' into r0. These bits, or bytes contain the address to our party data.

ldrh r0, [r0]
Loads a 'half-word,' or 16 bits, (or 2 bytes) from the address at r0.

ldr r1, var
This loads the data at the label var, which is the location of LASTRESULT in the WRAM. (Work RAM)

strh r0, [r1]
This stores the half-word in r0 at the address in r1. But r0 has a byte, not a half-word! It stores a half-word, not because r0 has a half word, but variables are only 1 half-word long.

pop {r0-r1,pc}
This 'pops,' or removes the registers r0, r1, and pc from the top of the stack, leaving the original registers again. You may ask "Why not pop {r0-r1, lr}? Well, pc is our current position. When we pop this, It continues onto where it was before. The link register will reset by itself. Here's the image again to help explain:
391px-Data_stack.svg.png


.align 2
We need to make sure our variables are multiples of two, so the routine can access them.

pokemon_data:
Another label, giving our Pokemon data.

.word 0x02024284 + 0x54
Writes the word 0x02024284 + 0x54 at this spot.

var:
A label for our variable.

.word 0x020270B6 + (0x800D * 2)
The offset of the first variable plus 0x800D, the lastresult variable, x 2, since variables are 2 bytes long.



Returning Values at the Bit Level

Alright, we've checked code, we've disected code, now lets go even deeper into pokemon values!

What New Commands We'll need:
lsl rX, #0xV - This shifts the bits in rX left byte 0xV positions.
lsr rX, #0xV - This does the same as above, but shifts bits right.
ldrb rX, [rY, #0xV] - Loads a byte from the address at rY + 0xV into rX. The 0xV is optional though.
There are also other methods that you can use, and it's usually the same with all ldr commands.
ldrb rX, label - Loads a byte from a label.

What we'll be doing
I decided to make a tutorial with something to the bit level. After all, bits are hard to manipulate at first, but it's key to disecting some things.

Status Conditions
Status conditions are based on bits. Here's a quote from bulbapedia:
Bulbapedia said:

Status ailment

The Pokémon's status ailments are stored as follows:
  1. 0-2: Sleep bits. Indicates turns of sleep, so 111b = 7 turns, 101b = 5 turns, etc.
  2. 3: Poison
  3. 4: Burned
  4. 5: Frozen
  5. 6: Paralysis
  6. 7: Bad poison
Now, let's see how we can get, say, the sleeping bit perhaps. Let's write some code!

Down and Dirty!
Alright, lets get to work. First, create a brand new txt file, and rename it Status_[gameprefix].asm the game prefix should be like my example1_fr, or example1_rs. Try to keep names without spaces, and short and sweet.

Now, add this to the top of your file:
Code:
.align 2
.thumb

main:
		push {r0-r1,lr}
		ldr r0, pokemon_data
That is the start of our first code, but we need to load a byte at 80 bytes afterwards, or 0x50 bytes.
Code:
ldrb r0, [r0]
Now, lets say we wanted to get the sleeping bits. We'd have to shift everything over left by 5 spaces,leafing the three sleeping bits. And since it has the number of turns asleep, that could be what we return!
Code:
lsl r0, #0x5
lsr r0, #0x5
What this does is takes a value like:
10101010
turns it into this:
01000000
Then this:
010
Which is the sleep value!
And now we write it to the variable 0x800D
Code:
		ldr r1, var
		strh r0, [r1]
		pop {r0-r1,pc}
Now, take the bottom labels starting at the second .align 2 from the previous code file. Also, add "+ 0x50" to the end of pokemon_data.
For example, I'd add this:
Code:
.align 2

pokemon_data:
		.word 0x02024284 + 0x50
var:
		.word 0x020270B6 + (0x800D * 2)
Alright, lets give it a test run! Go ahead and compile it with thumb.bat. Here's an example script we can use.
Code:
#dynamic 0x800000

#org @start
lock
faceplayer
callasm 0xyouroffset
buffernumber 0x0 lastresult
msgbox @msg 0x2
release
end 

#org @msg
= That POKEMON sure is sleepy.\nIt's going to sleep [buffer1] turns!
Now attach that script to someone, and lets go!

282wro.png


Of course, my pokemon wasn't asleep, but it returned the value. Feel free to debug it if you want!

The Challenge of the Day!

Can you return the value of a burn? Perhaps poison? Return your ASM with an image!


Value Changing, Comparing, and Arithmetic Commands

In this tutorial, we're going to cover commands used to change value, compare, and do arithmetic functions, which are some of the most essential commands (In my opinion) to ARM7 ASM.

New Commands used in this Section:
mov rX, #0xY Moves the value in rX to 0xY.
add rX, rY, rZ Adds the contents of rY and rZ and places it on rX.
add rX, #0xY Adds 0xY to rX.
sub rX, rY, #0xZ Subtracts 0xZ from rY and places it on rX. It generally has the same syntax as adding.
mul rX, rY, rZ Multiplies rY and rZ and places the result on rX. rZ cannot be replaced with a hex number like #0x5.
cmp rX, #0xY Compares the value in rX to 0xY and stores the results to the flags. 0xY can also be a register.
teq rX, #0xY This is sort of a limited cmp command, that tests if rX is equal to 0xY.
b{condition} label Compares the flags depending on the condition, and if the result meets the condition, the processor goes to label.

Using Comparing Commands
Comparing commands work by subtracting 0xY from rX, and placing the results in the flags. It doesn't really return any result to a register. Here's a quote from ARM's website:
ARM.com said:
These instructions compare the value in a register with Operand2. They update the condition flags on the result, but do not place the result in any register.

The CMP instruction subtracts the value of Operand2 from the value in Rn. This is the same as a SUBS instruction, except that the result is discarded.
So, that's how it works. Unfortunatly, the flags are protected from access in thumb mode, but there is a command that can do it for us. It's the command "b".
Spoiler:

How 'b' works is it compares the flags to a condition. If it is met, it goes to another label. The conditions are as follows:
Code:
EQ	Equal
NE	Not equal
CS/HS	Higher or same (unsigned >= )
CC/LO	Lower (unsigned < )
MI	Negative
PL	Positive or zero
VS	Overflow
VC	No overflow
HI	Higher (unsigned <= )
LS	Lower or same (unsigned <= )
GE	Signed >=
LT	Signed <
GT	Signed >
LE	Signed <=
AL	Always (usually omitted, same as just "b" by itself.)
So bhi rX, rY would be like this equasion:
Code:
rX <= rY
If the equasion is true, it goes to the label. If not, it continues.

A Quick Example
For this, we won't have an active code sample that does something, simple because this is more for learning the commands. Instead, lets have some examples:
Code:
.align 2
.thumb

main:
		push {r0-r1}
		mov r0, #0x5 @Moves r0 to 5
		sub r0, #0x3 @Subtracts 3 from r0
		mov r1, #0x3 @Move r1 to 3
		mul r0, r1, r0 @multiplies r0 and r1, and places the result on r0
		cmp r0, #0x6 @Compares r0 to 6, which it should be
		beq end	       @If equal goes to the label 'end'
end:
		pop {r0-r1}

And that pretty much covers everything we need!



 
Last edited:

Orinjmate

The Orinj of the Mate
120
Posts
13
Years
Wow FINALLY someone has put up a tut on ASM! This should be useful to alot of people. Can you write an ASM script on a free space offset then in XSE use 'callasm 0xoffset' to make it run in the middle of the script?
 
29
Posts
13
Years
  • Seen Mar 14, 2011
This tutorial seems nice.

Could you just give me a few reasons why I need ASM. What can I do with it that I can't do with the normal tools out there, a-map, xse etc.
 
5,256
Posts
16
Years
You can do essentially anything with ASM, within the GBA limitations of course, if you know how to. ASM stands for assembly - what builds up the game.

Anyway, nice tutorial. I've only skimmed it, but I'll read it properly later.
 

miksy91

Dark Energy is back in action! ;)
1,480
Posts
15
Years
I had hard time understanding all those commands you used.
Spoiler:


Anyway, this tutorial was really worth reading :)
 

TB Pro

Old-timer
2,708
Posts
19
Years
I had hard time understanding all those commands you used.
Spoiler:


Anyway, this tutorial was really worth reading :)
ldr loads var into the specified register, here it is r1.
strh stores a half-word at r0 into r1.
pop just pops them all back, you pushed, now you have to pop.
 

chriskid198

Happy New Year!
159
Posts
13
Years
I'm wondering what types of scripting there is?

The type of scripting I use is very similar to this,

Code:
#dynamic 0x800000

#org @start
lock
faceplayer
callasm 0xyouroffset
buffernumber 0x0 lastresult
msgbox @msg 0x2
release
end 

#org @msg
= That POKEMON sure is sleepy.\nIt's going to sleep [buffer1] turns!

Would be a script that I would use.
 

Shiny Quagsire

I'm Still Alive, Elsewhere
697
Posts
14
Years
I'm wondering what types of scripting there is?

The type of scripting I use is very similar to this,

Code:
#dynamic 0x800000

#org @start
lock
faceplayer
callasm 0xyouroffset
buffernumber 0x0 lastresult
msgbox @msg 0x2
release
end 

#org @msg
= That POKEMON sure is sleepy.\nIt's going to sleep [buffer1] turns!

Would be a script that I would use.

pretty much, just replace youroffset woth the offset of your ASM + 1.
 
9
Posts
13
Years
  • Age 31
  • USA
  • Seen Dec 7, 2014
I'm a little confused about something. You said "Registers are 32 bits long, making the highest value possible FFFFFFFF." but your code shifts the status back and forth by only 5 bits. Wouldn't that return the same number? Don't you need to shift by 0x1d?

I dunno, bit shifting wasn't covered by my intro to programming course. I learned it on my own so I'm probably wrong.
 

HackMew

Mewtwo Strikes Back
1,314
Posts
17
Years
  • Seen Oct 26, 2011
If you've ever had to learn about computers, most people say: "The computer uses 0's and 1's," and "Programmers write applications all in 0's an 1's." That's only partially true. Machine code usually is written in Assembly Language.

Programmers don't write applications in binary. Since human beings are smart enough, however, they invented assembly, a language with human-friendly instructions that could get assembled into machine code, a CPU-friendly format.

ASM is a low-level language that processors use to interpret instructions.

CPUs are not ASM interpreters. In fact, they cannot understand ASM. You have to assemble your routines into machine code before they actually get working.

The processor and ASM language the GBA uses is ARM 7, which is a lot easier compared to Z80, the GBC processor.

On what assumptions?

You may have noticed there is no divide. The problem is that there are no decimals in ARM 7 ASM.

You know, there's still integer division.

#org @msg
= Hey, that Pokemon's ID is [buffer1]!

That's not the Pokémon's ID. That's the Pokémon Original Trainer (OT) ID.

Now, open your Pokemon ROM into a hex editor, and copy it to a free-space address ending with 0, 4, 8, or C. You may ask, "Why not 5? or 7?" Because these numbers aren't multiples of four, and it uses bit 0, a bit used to tell the game "Do you want to boot into Thumb or ARM?"

No, that's not the reason. You have to use a 4-byte offset for literal pools, so that they're properly aligned. Theoretically, if you don't have a literal pool, you would just need a 2-byte aligned offset (almost all THUMB instructions take 2 bytes, indeed).

Registers are like variables that store temporary values and information. Registers are 32 bits long, making the highest value possible FFFFFFFF.

Not necessarily. 0xFFFFFFFF is the highest, unsigned value. The highest signed value would be 0x7FFFFFFF.

Think of 15 stacks of china plates. Each of them has a different design on them.

Why would the plates be different?

ldr r0, pokemon_data
This loads a 32 bits at the label 'pokemon_data' into r0. These bits, or bytes contain the address to our party data.

ldr r1, var
This loads the data at the label var, which is the location of LASTRESULT in the WRAM. (Work RAM)

"pokemon_data" and "var" are not a label. They're symbols.

strh r0, [r1]
This stores the half-word in r0 at the address in r1. But r0 has a byte, not a half-word!

Actually r0 has 32 bits, or 4 bytes.

The link register will reset by itself.

It won't.

.word 0x020270B6 + (0x800D * 2)
The offset of the first variable plus 0x800D, the lastresult variable, x 2, since variables are 2 bytes long.

0x020270B6 is not the address of the first variable. Especially considering FR/LG are DMA-based and the address changes from time to time. No, 0x020270B6 is just a fictitious address I came up with to makes things easier to read.

Wow FINALLY someone has put up a tut on ASM! This should be useful to alot of people. Can you write an ASM script on a free space offset then in XSE use 'callasm 0xoffset' to make it run in the middle of the script?

What about the search function? I first published my ASM tutorial on February 14th, 2009.

You can pretty much do anything that's in another gba game. Your only limit is your imagination the gba's ram.

GBA RAM might be enough. But there are technical limits too.
Fix your spelling mistakes, and the smilies. Also, learn before teaching.

strh stores a half-word at r0 into r1.

Into the address which r1 is currently holding.

I'm a little confused about something. You said "Registers are 32 bits long, making the highest value possible FFFFFFFF." but your code shifts the status back and forth by only 5 bits. Wouldn't that return the same number? Don't you need to shift by 0x1d?

I dunno, bit shifting wasn't covered by my intro to programming course. I learned it on my own so I'm probably wrong.

Nice to see someone else noticed. Yeah, the code shiny quagsire posted is wrong. The sleep turns are the lowest 3 bits. So you do:

Code:
lsl r0, r0, #0x1D
lsr r0, r0, #0x1D

In other words, you shifts the status value by 0x1D bits (thus clearing all the bits except the sleep ones), and then you shift it back.
 
Last edited:

Shiny Quagsire

I'm Still Alive, Elsewhere
697
Posts
14
Years
OMG!!! YOU HAVE BEEN PWNED!!!

That's spam, and it's plain rude :(

Programmers don't write applications in binary. Since human beings are smart enough, however, they invented assembly, a language with human-friendly instructions that could get assembled into machine code, a CPU-friendly format.

You failed to read the rest.
That's only partially true. Machine code usually is written in Assembly Language.
[/QUOTE]

CPUs are not ASM interpreters. In fact, they cannot understand ASM. You have to assemble your routines into machine code before they actually get working.

I'm aware. Thanks for clarification.

On what assumptions?

Alright, fine. Be that way. It's just an opinion based on the Z80 I've seen.

You know, there's still integer division.
I was stating there is no command for it. And you failed to read the rest (Again...)
However, there are some tricks to dividing I'll explain later.

That's not the Pokémon's ID. That's the Pokémon Original Trainer (OT) ID.
And that's an old script waiting to be replaced.

No, that's not the reason. You have to use a 4-byte offset for literal pools, so that they're properly aligned. Theorically, if you don't have a literal pool, you would just need a 2-byte aligned offset (almost all THUMB instructions take 2 bytes, indeed).

We learn something new every day. :D I thought I read that somewhere, or maybe my mind was fooling be :p

Not necessarily. 0xFFFFFFFF is the highest, unsigned value. The highest signed value would be 0x7FFFFFFF.

It's the highest possible value whether unsigned or signed is it not?

Why would the plates be different?

It's like items on a menu. They aren't all just a different name with the same things. The plate's design represents how the values are different.

"pokemon_data" and "var" are not a label. They're symbols.

Here's a quote:
JPAN said:
Also, that Start: right there is marking the beginning of the code. It's not just an annotation like commentaries. That is a label. These are the name of the address of that instruction (the name we gave it, that is). While compiling, any instructions that make a reference to a label will be replaced with its respective address relative to its position. More on that when we talk about jumps.

Actually r0 has 32 bits, or 4 bytes.
If r0 has 000000ff, it would be a byte no? It contains a value equivalent to a byte, or if we want to, a half word.

It won't.

OK.

0x020270B6 is not the address of the first variable. Especially considering FR/LG are DMA-based and the address changes from time to time. No, 0x020270B6 is just a fictitious address I came up with to makes things easier to read.

OK, fine.


GBA RAM might be enough. But there are technical limits too.
Fix your spelling mistakes, and the smilies. Also, learn before teaching.

What mistakes? And I know my coding, just not extremely technical areas.

Into the address which r1 is currently holding.

That's what I meant.

Nice to see someone else noticed. Yeah, the code shiny quagsire posted is wrong. The sleep turns are the lowest 3 bits. So you do:

Code:
lsl r0, r0, #0x1D
lsr r0, r0, #0x1D

In other words, you shifts the status value by 0x1D bits (thus clearing all the bits except the sleep ones), and then you shift it back.

Wait, hows that work? If we load and shift left 0x1d, that would wipe out the byte. Or am I wrong again?
 

NarutoActor

The rocks cry out to me
1,974
Posts
15
Years
I think the problem with your plate analogy, is that the plates can have the same value.(or same design)
Also, do I sense jealousy from the great hack mew. :O

P.S. You left a quote bracket open. Always re-read your work before posting, and if there are brackets, utilize the preview post.
 
9
Posts
13
Years
  • Age 31
  • USA
  • Seen Dec 7, 2014
If r0 has 000000ff, it would be a byte no? It contains a value equivalent to a byte, or if we want to, a half word.

It's still a 32-bit variable. Even if you only use the first 8 bits, it's still has the other 24 bits.

What mistakes? And I know my coding, just not extremely technical areas.

The file name paths are getting replaced with that one smily. :\ <- that one

Wait, hows that work? If we load and shift left 0x1d, that would wipe out the byte. Or am I wrong again?

Form what I can tell, it would wipe out everything except the last three bits. And Bulbapedia says those last three are the sleep bits. So that's what you want.
 

Shiny Quagsire

I'm Still Alive, Elsewhere
697
Posts
14
Years
The routine is in my hack pack. Without it, the script is useless, and will most likely cause the game to crash.

Anyways, I've added a section on the core commands like arithmatic, branching, and other things like that.
 

TB Pro

Old-timer
2,708
Posts
19
Years
Routine?

*Looks at first post*
The routine is all the ASM *stuff* that you write down.

this is a routine:
Spoiler:
 

Shiny Quagsire

I'm Still Alive, Elsewhere
697
Posts
14
Years
The routine is all the ASM *stuff* that you write down.

this is a routine:
Spoiler:

Yes, and that routine was to explain the arithmatic commands, and doesn't do anything. Don't try compiling and inserting, because your game will freeze.
 
Back
Top