Starting ARM Asm : Part 01

PREV CONTENTS NEXT

DOWNLOAD THE CODE FOR THIS PART (PART 01)
This tutorial is Released under the MIT license.

Hello World in 28 Bytes.
A few notes before we start:

In BASIC (and ARM Assembler) numbers that begin with an & (Ampersand) character are Hexadecimal numbers. It is assumed that you have some familiarity with Hexadecimal numbers from working with a High Level programming language (eg C, BASIC, C++, Pascal, Spin, etc), or markup language (eg HTML color values).

Generally the parts of the code in BASIC that do not contribute to the assembly will be glossed over. This part will cover some of the things needed for working with creating Absolute executable files (FileType &FF8 [or 0xFF8 for C people]) by writing Assembly in BASIC.

While it is possible to have assembly used in a BASIC program, and thus not need to save the assembled code, this series of tutorials focuses on producing stand alone Absolute type executable files that run on there own.

Let us begin by showing a simple 4 instruction Hello World example program,and stepping through it.


REM>HelloSrc
REM Released under the MIT License.
REM See License/txt.
ON ERROR PRINT" ERROR LINE : ";ERL;" = ";REPORT$:END

DIM Code% 4096

FOR pass% = 4 TO 6 STEP 2
P%=&8000
O%=Code%
[OPT pass%
.start%
    ADR      R0,HelloStr%    ;We need the address of
                             ; text into R0 to call
    SWI      "OS_Write0"     ; the OS_Write0 SWI.


    MOV      R0,#0           ;No error, R0 = 0 when
    SWI      "OS_Exit"       ; we call OS_Exit SWI

.HelloStr%
    EQUS     "Hello World"+CHR$(0) ;Our string, with
                            ; a null byte at the end.
    ALIGN                         ;Keep alignment.
.CodeEnd%
]
NEXT


SYS "OS_File", 10, "HelloRun", &FF8,, Code%, O%

PRINT "File Size is : ";CodeEnd%-start%

END

At the beginning of the file we have the BASIC lines:


DIM Code% 4096

FOR pass% = 4 TO 6 STEP 2
P%=&8000
O%=Code%

The first of these lines reserves 4096 bytes of space with the variable code% pointing to it.

Then there is a FOR pass%, this sets the value of pass used later in the OPT statement to direct the assembly. Value of 4 means offset assembly, without checking for variable errors, this allows it to find all the labels in the first pass of assembly. The value of 6 is offset assembly with error checking.

in offset assembly the address the code is assembled to run at is different from the address where the assembled code is stored.

The P%=&8000 line sets the value of P% to &8000. The value of P% is the address that the code is assembled to execute at, and all standard RISC OS applications are ran at the hex address &8000

Beings as we are using offset assembly we need another variable to tell the BASIC assembler where to store the code it is assembling. This is what O% does, O% points to the location in memory where the assembled code is being stored. In our case into the space at Code% that we reserved earlier.


[OPT pass%

The opening square bracket tells BASIC that we are in assembly language code now, and will be until a line with a closing square bracket. This is how BASIC knows if it is supposed to be assembling code at the moment, and also makes some useful tricks possible, to make some assembly tasks simpler by using BASIC statements. We will get more into this in a later part of this tutorial series.

The value given to OPT tells the assembler how to assemble the code. We discussed the values above.


.start%
    ADR      R0,HelloStr%    ;We need the address of
                             ; text into R0 to call
    SWI      "OS_Write0"     ; the OS_Write0 SWI.

Now we get into the actual assembly language code finally. The .start% is a label, it just holds the location and does not produce any actual code.

Anything following a semicolon is treated as a comment and ignored by the assembler, until the end of line or until a colon character.

The ARM has 16 user registers named R0 through R15. For now just know they are 32-bit integer value registers. The ADR sudo op creates an instruction that puts the address into a register, using relative location so as to be position independent. In this case we are putting the address of our string into R0.

I should note that R15 is the program counter, so be careful with that. Also R14 is the link register, this will be explained in detail when we talk about subroutines in a later part. While it is not special of its own, R13 is used in RISC OS for the stack.

The next instruction is an SWI instruction. This puts the CPU into a privileged mode and and transfers control to a routine at a known location. This is used to preform system calls. In this case we are calling the SWI named OS_Write0, which displays the null terminated string that is pointed to by R0. Obviously the name does not fit in the instruction, RISC OS provides a way to translate the name to a number, BASIC uses this. OS_Write0 is SWI number 2, as such you could write SWI &02 instead of SWI "OS_Write0" and the code produced would be the same.


    MOV      R0,#0           ;No error, R0 = 0 when
    SWI      "OS_Exit"       ; we call OS_Exit SWI.

We are done so it is time to return control to the OS. We do this by calling SWI "OS_Exit" (SWI &11) with R0 set to the address of an error block, or if no error block R0 being a null pointer (containing a value of ZERO=0). We are not using an error block, so we set R0 to 0 by a MOV.

MOV transfers a value from one register to another, or from a limited immediate value into a register. The form we are using transfers from the immediate value (stored in the instruction word) of 0 into R0, making R0 equal 0

The final instruction is an SWI, that returns control to the OS and frees our memory slot, quitting our program.

After our little bit of code we have the data we use:


.HelloStr%
    EQUS     "Hello World"+CHR$(0) ;Our string, with
                               ; a null byte on end.
    ALIGN                     ;Keep alignment.
.CodeEnd%

Again the .HelloStr% is a label, just indicates the location where it is. Then we have the EQUS that indicates that we are storing string data at this location. The data is the string, any valid BASIC string can be used, so we take advantage of this to use the CHR$(0) statement to put the null terminator character on the end of our string.

The ALIGN directive makes sure that what follows it is word aligned. On the ARM CPU a word is 32-bits, so this aligns to a four byte boundary.


]
NEXT

The close bracket indicates the end of the assembly code, back to BASIC code now. The NEXT is the end of our FOR NEXT loop.


SYS "OS_File", 10, "HelloRun", &FF8,, Code%, O%

PRINT "File Size is : ";CodeEnd%-start%

END

Now we make a system call to save our assembled machine code into an Absolute file. OS_File with R0=10 writes a block of memory out as a typed file, with a pointer to the name string in R1, the filetype in R2, start address in R4, and the end address in R5. As our block of mem starts at Code%, and O% has been incremented to the end of the block, these are the addresses we use. Absolute filetype is &FF8.

The print statement is just to be informative. Tell us how many bytes in size the assembled file is.

PREV CONTENTS NEXT
DOWNLOAD THE CODE FOR THIS PART (PART 01)

This site created on RISC OS using !Zap.
Hosting for this site provided by David F
This page viewable with Any Browser
LAST UPDATED: Sept 25th 2021