return to Choose a Color Scheme: Dark
Print Mode

Learn Multi platform ARM Assembly Programming... For the Future!

This page is Under Construction.... 'Learn ARM is planned for 2020'

We've covered a wide variety of chips in these tutorials, but now it's time to look at one of the most modern... the ARM

Powering everything from the Gameboy Advance to the Nitendo Switch and Iphone,  the arm is NOT a classic machine... it's not even a systems with 'legacy roots' like the 8086... the ARM...

The ARM is 32 bit CPU from the ground up, with a very logical bytecode structure and higly optimized for low power situations - the arm is widely believed to be the future of computing.

In this series, we'll take a look at the ARM cpu on the GBA and as always, learn about the CPU from the ground up


If you want to learn ARM get the Cheatsheet! it has all the ARM7 commands, it covers the commands, and options like Bitshifts and conditions as well as the bytecode structure of the commands!
We'll be using the excellent VASM for our assembly in these tutorials... VASM is an assembler which supports Z80, 6502, 68000, ARM and many more, and also supports multiple syntax schemes...

You can get the source and documentation for VASM from the official website HERE

What is the ARM and what are 32 'bits' You can skip this if you know about binary and Hex (This is a copy of the same section in the Z80 tutorial)
The ARM is a 32-Bit processor with a 32 bit Address bus!... 
What's a bit... well, one 'Bit' can be 1 or 0
four bits make a Nibble (0-15)
two nibbles (8 bits) make a byte (0-255)
two bytes (16 bits) make a word (0-65535)

And what is 65535? well that's 64 kilobytes ... in computers Kilo is 1024, because four bytes is 1024 bytes
64 kilobytes is the amount of memory a basic 8-bit system can access

With the ARM we actually have some serious memory resources available to us, both in RAM or ROM!

if you're looking to develop serious games or software, you probably want to use C++, but looking at assembly lets us see how the hardware really works, and that's the point of these tutorials!

Numbers in Assembly can be represented in different ways.
A 'Nibble' (half a byte) can be represented as Binary (0000-1111) , Decimal (0-15) or  Hexadecimal (0-F)... unfortunately, you'll need to learn all three for programming!

Also a letter can be a number... Capital 'A'  is stored in the computer as number 65!

Think of Hexadecimal as being the number system invented by someone wit h 15 fingers, ABCDEF are just numbers above 9!
Decimal is just the same, it only has 1 and 0.

In this guide, Binary will shown with a % symbol... eg %11001100 ... hexadecimal will be shown with & eg.. &FF.

Assemblers will use a symbol to denote a hexadecimal number, some use $FF or #FF or even 0x, but this guide uses & - as this is how hexadecimal is represented in CPC basic
All the code in this tutorial is designed for compiling with WinApe's assembler - if you're using something else you may need to change a few things!
But remember, whatever compiler you use, while the text based source code may need to be slightly different, the compiled "BYTES' will be the same!
Decimal 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ... 255
Binary 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111   11111111
Hexadecimal 0 1 2 3 4 5 6 7 8 9 A B C D E F   FF

Another way to think of binary is think what each digit is 'Worth' ... each digit in a number has it's own value... lets take a look at %11001100 in detail and add up it's total

Bit position 7 6 5 4 3 2 1 0
Digit Value (D) 128 64 32 16 8 4 2 1
Our number (N) 1 1 0 0 1 1 0 0
D x N 128 64 0 0 8 4 0 0
128+64+8+4= 204            So %11001100 = 204 !

If a binary number is small, it may be shown as %11 ... this is the same as %00000011
Also notice in the chart above, each bit has a number, the bit on the far right is no 0, and the far left is 7... don't worry about it now, but you will need it one day!

If you ever get confused, look at Windows Calculator, Switch to 'Programmer Mode' and  it has binary and Hexadecimal view, so you can change numbers from one form to another!
If you're an Excel fan, Look up the functions DEC2BIN and DEC2HEX... Excel has all the commands to you need to convert one thing to the other!

But wait! I said a Byte could go from 0-255 before, well what happens if you add 1 to 255? Well it overflows, and goes back to 0!...  The same happens if we add 2 to 254... if we add 2 to 255, we will end up with 1
this is actually usefull, as if we want to subtract a number, we can use this to work out what number to add to get the effect we want

Negative number -1 -2 -3 -5 -10 -20 -50 -254 -255
Equivalent Byte value 255 254 253 251 246 236 206 2 1
Equivalent Hex Byte Value FF FE FD FB F6 EC CE 2 1

All these number types can be confusing, but don't worry! Your Assembler will do the work for you!
You can type %11111111 ,  &FF , 255  or  -1  ... but the assembler knows these are all the same thing! Type whatever you prefer in your ode and the assembler will work out what that means and put the right data in the compiled code!

The ARM-32 Registers
The arm technically has 27 general purpose 32 bit registers, but all but 16 are hidden from the user...

Main Registers:
32 Bit Registers Use cases
 R0  R0 Accumulator 
R1 R1 Base
R2 R2 Count
R3 R3 Data 
R4 R4 Used by String commands
R5 R5 Used by String commands
R6 R6 Stack
R7 R7 Alt Stack
R8 R8 Flags
R9 R9 Current running address
R10 R10
R11 R11
R12 FP Data Frame
R13 SP Stack Pointer
R14 UK R15 Save Area
R15 PC System Program Counter

Added later

32 Bit Registers Use Cases
CSPR CSPR System Program Counter

    PC Flags: NZCVIF------------------------MM

CSPR Flags: NZCV--------------------IFTMMMMM

      Name Meaning
N Negative Signed Less Than
Z Zero Zero
C Carry Carry / Not Borrow / Rotate Extend
V oVerflow Overflow
I IRQ Disable 1=disable
F FIQ disable  1=disable
T Thumb mode V4 only
M Mode 00=user 01=FIQ 10=IRQ 11=Supervisor
Equivalent commands
Z80 command Description Command
RET Return from linked branch MOV pc,lr
DEC r1 Decrement r1 and set flags SUBS r1,r1,#1
Push all Push all+ return address STMFD sp!,{r0-r12, lr}
Pop all.. RET
Pop all + return LDMFD sp!,{r0-r12, pc}
LDIR r12=src
     LDMIA r12!, {r0-r11}
     STMIA r13!, {r0-r11}
     CMP r12, r14
     BNE loop

Addressing Modes
Mode Format Details
Immediate #n Fixed value of n ADD R0,R0,#1
Register Rn value in register Rn ADD R0,R0,R1
Register Shifted Rn, shft #n Shift Register by #n using shifter shft
Options: LSL #n, LSR #n, ASR #n, ROR #n, RRX
note: RRX can only shift 1 bit
MOV R0,R1,ROR #2
Register indirect [Rn] value from address in register Rn MOV R0,[R1]
Register indirect with constant offset [Rn,#n] value from address in register Rn+n MOV R0,[R1,#4]
Register indirect with register offset [Rn,Rm] value from address in register Rn+Rm MOV R0,[R1,R2]
Register indirect with scaled Register offset Rn, [Rm,shft #n] value from address in register Rn+(Rm*#n) MOV R0,[R1,R2, LSL #2]
Register indirect with Preincrement [Rn,#n]!  /  [Rn,Rm]! value from address in register Rn+n... set Rn=Rn+n LDR R0,[R1,#4]!
Register indirect with Postincrement [Rn],#n  /  [Rn],Rm value from address in register Rn... set Rn=Rn+n

Command format

Command Dest, Source, Param, Shifts

Command {COND}{B}{S} Dest, rd, [rs,off]{!}
B= byte transfer
!= update reg Rs
S= update conditional flags

• post-indexed offset.
The syntax of the four forms, in the same order, are:
• zero offset
    op{cond}type Rd, [Rn]
• pre-indexed offset
    op{cond}type Rd, [Rn, Offset]{!}
• program-relative
    op{cond}type Rd, label
    op{cond}type Rd, [Rn], Offset
op is either  LDR or  STR .
cond is an optional condition code
type must be one of:
    SH for Signed Halfword ( LDR only)
    H for unsigned Halfword
    SB for Signed Byte ( LDR only).
Rd is the ARM register to load or save.
Rn is the register on which the memory address is based.
Rn must not be the sameas  Rd , if the instruction is either:
    • pre-indexed with writeback
    • post-indexed.

label is a program-relative expression.  label must be within 255 bytes of the current instruction.
Offset is an offset applied to the value in  Rn
! is an optional suffix. If  ! is present, the address including the offset is written back into  Rn . You cannot use the  ! suffix if  Rn is r15.

Zero offset
The value in  Rn is used as the address for the transfer.

Pre-indexed offset
The offset is applied to the value in  Rn before the transfer takes place. The result is used as the memory address for the transfer. If the  ! suffix is used, the result is written back into  Rn .

This is an alternative version of the pre-indexed form. The assembler calculates the offset from the PC for you, and generates a pre-indexed instruction with the PC as  Rn .You cannot use the  ! suffix.
Post-indexed offset
The value in  Rn is used as the memory address for the transfer. The offset is applied to the value in  Rn after the transfer takes place. The result is written back into  Rn .
Offset syntax
Both pre-indexed and post-indexed offsets can be either of the following:
#expr  {-}Rm
- is an optional minus sign. If  - is present, the offset is subtracted from  Rn . Otherwise, the offset is added to  Rn .
expr is an expression evaluating to an integer in the range –255 to +255. This is often a numeric constant 
Rm is a register containing a value to be used as the offset.
The offset syntax is the same for LDR and STR, doublewords on page 4-15.\

These instructions are available in ARM architecture v4 and above.
LDREQSH r11,[r6] ; (conditionally) loads r11 with a 16-bit halfword from the address in r6. Sign extends to 32 bits.
LDRH r1,[r0,#22] ; load r1 with a 16 bit halfword from 22 bytes above the address in r0. Zero extend to 32 bits.
STRH r4,[r0,r1]! ; store the least significant halfword from r4 to two bytes at an address equal to contents(r0)  plus contents(r1). Write address back into r0.
LDRSB r6,constf ; load a byte located at label constf. Sign extend.

Learn Assembly for the Greatest Classic Processors:  Z80 - 6502 - 68000
Visit to get my games and their source code! | Support me on patreon