Think In Geek

In geek we trust

ARM assembler in Raspberry Pi – Chapter 1

In my opinion, it is much more beneficial learning a high level language than a specific architecture assembler. But I fancied learning some ARM assembler just for fun since I know some 386 assembler. The idea is not to become a master but understand some of the details of what happens underneath.

Introducing ARM

You will see that my explanations do not aim at being very thorough when describing the architecture. I will try to be pragmatic.

ARM is a 32-bit architecture that has a simple goal in mind: flexibility. While this is great for integrators (as they have a lot of freedom when designing their hardware) it is not so good for system developers which have to cope with the differences in the ARM hardware. So in this text I will assume that everything is done on a Raspberry Pi Model B running Raspbian (the one with 2 USB ports and 512 MB of RAM).

Some parts will be ARM-generic but others will be Raspberry Pi specific. I will not make a distinction. The ARM website has a lot of documentation. Use it!

Writing assembler

Assembler language is just a thin syntax layer on top of the binary code.

Binary code is what a computer can run. It is composed of instructions, that are encoded in a binary representation (such encodings are documented in the ARM manuals). You could write binary code encoding instructions but that would be painstaking (besides some other technicalities related to Linux itself that we can happily ignore now).

So we will write assembler, ARM assembler. Since the computer cannot run assembler we have to get binary code from it. We use a tool called, well, assembler to assemble the assembler code into a binary code that we can run.

The tool to do this is called as. In particular GNU Assembler, which is the assembler tool from the GNU project, sometimes it is also known as gas for this reason. This is the tool we will use to assemble our programs.

Just open an editor like vim, nano or emacs. Our assembler language files (called source files) will have a suffix .s. I have no idea why it is .s but this is the usual convention.

Our first program

We have to start with something, so we will start with a ridiculously simple program which does nothing but return an error code.

1
2
3
4
5
6
7
8
/* -- first.s */
/* This is a comment */
.global main /* 'main' is our entry point and must be global */
.func main   /* 'main' is a function */
 
main:          /* This is main */
    mov r0, #2 /* Put a 2 inside the register r0 */
    bx lr      /* Return from main */

Create a file called first.s and write the contents shown above. Save it.

To assemble the file type the following command (write what comes after $ ).

1
$ as -o first.o first.s

This will create a first.o. Now link this file to get an executable.

1
$ gcc -o first first.o

If everything goes as expected you will get a first file. This is your program. Run it.

1
$ ./first

It should do nothing. Yes, it is a bit disappointing, but it actually does something. Get its error code this time.

1
2
$ ./first ; echo $?
2

Great! That error code of 2 is not by chance, it is due to that #2 in the assembler code.

Since running the assembler and the linker soon becomes boring, I’d recommend you using the following Makefile file instead or a similar one.

1
2
3
4
5
6
7
8
9
10
11
# Makefile
all: first
 
first: first.o
	gcc -o $@ $+
 
first.o : first.s
	as -o $@ $<
 
clean:
	rm -vf first *.o

Well, what happened?

We cheated a bit just to make things a bit easier. We wrote a C main function in assembler which only does return 2;. This way our program is easier since the C runtime handled initialization and termination of the program for us. I will use this approach all the time.

Let’s review every line of our minimal assembler file.

1
2
/* -- first.s */
/* This is a comment */

These are comments. Comments are enclosed in /* and */. Use them to document your assembler as they are ignored. As usually, do not nest /* and */ inside /* because it does not work.

3
.global main /* 'main' is our entry point and must be global */

This is a directive for GNU Assembler. A directive tells GNU Assembler to do something special. They start with a dot (.) followed by the name of the directive and some arguments. In this case we are saying that main is a global name. This is needed because the C runtime will call main. If it is not global, it will not be callable by the C runtime and the linking phase will fail.

4
.func main   /* 'main' is a function */

Another GNU assembler directive. Here we state that main is a function. This is important because an assembler program usually contains instructions (i.e. code) but may also contain data. We need to explicitly state that main actually refers to a function, because it is code.

6
main:          /* This is main */

Every line in GNU Assembler that is not a directive will always be like label: instruction. We can omit label: and instruction (empty and blank lines are ignored). A line with only label:, applies that label to the next line (you can have more than one label referring to the same thing this way). The instruction part is the ARM assembler language itself. In this case we are just defining main as there is no instruction.

7
    mov r0, #2 /* Put a 2 inside the register r0 */

Whitespace is ignored at the beginning of the line, but the indentation suggests visually that this instruction belongs to the main function.
This is the mov instruction which means move. We move a value 2 to the register r0. In the next chapter we will see more about registers, do not worry now. Yes, the syntax is awkward because the destination is actually at left. In ARM syntax it is always at left so we are saying something like move to register r0 the immediate value 2. We will see what immediate value means in ARM in the next chapter, do not worry again.

In summary, this instruction puts a 2 inside the register r0 (this effectively overwrites whatever register r0 may have at that point).

8
    bx lr      /* Return from main */

This instruction bx means branch and exchange. We do not really care at this point about the exchange part. Branching means that we will change the flow of the instruction execution. An ARM processor runs instructions sequentially, one after the other, thus after the mov above, this bx will be run (this sequential execution is not specific to ARM, but what happens in almost all architectures). A branch instruction is used to change this implicit sequential execution. In this case we branch to whatever lr register says. We do not care now what lr contains. It is enough to understand that this instruction just leaves the main function, thus effectively ending our program.

And the error code? Well, the result of main is the error code of the program and when leaving the function such result must be stored in the register r0, so the mov instruction performed by our main is actually setting the error code to 2.

That’s all for today.

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedIn

, , ,

26 thoughts on “ARM assembler in Raspberry Pi – Chapter 1

  • Jonathan Hinchliffe says:

    Really excellent tutorial.

  • Fernando says:

    Nice! I’ve been doing a bit of reading before about registers and general CPU functioning (basically, reading some chapters about ‘Computer design’), and I think that everything makes much more sense with this bit of background.

    I can’t stop thinking about how I’d interpreted this same Introduction with any idea – I think it’d sound like magic!

    Again, still nice. I’d carry on with the next part!

  • […] Artikel zur Assembler-Programmierung im MagPi! Für den weiteren Einstieg in das Thema gibt es bei ThinkGeek noch einen 12-Teiligen kostenfreien Kurs. Yeah, das Wochenende ist damit wohl […]

  • Barret says:

    I’m getting this error when I try to assemble the file with
    $ as -o first.o first.s

    first.s:7: Error: expecting operand after ','; got nothing
    first.s:8: Error: no such instruction: `bx lr'

    • rferrer says:

      Are you running that command on the Raspberry Pi? This is ARM assembler, so you must use an ARM assembler like the one in the Raspberry Pi.

      I get the very same error when trying to assemble first.s on my x86_64 machine. It will not work there, obviously.

      • Barret says:

        Oh yeah, duh. Is there a way to cross compile code on a x86_64 machine for ARM? I’ve tried Google, but with little concrete instructions.

        • rferrer says:

          You can use crosstol-ng. Last time I checked it was pretty easy to make a cross toolchain for Raspberry Pi. Anyways, you won’t be able to run the code unless you copy it to the Raspberry Pi. I think it is easier to assemble directly there.

  • Fernando says:

    Hi!

    I’ve found elsewhere that there are some directives such as “.req” and “.unreq” that let us work with registers as if they were variables.

    I haven’t seen that in this course, so I just add this quick comment hoping it is useful for someone else!

    P.S: Complete list of directives for ARM can be found here, http://sourceware.org/binutils/docs-2.19/as/ARM-Directives.html

  • carnivion says:

    Hi, rferrer. Thanks for this tutorial. I’m very beginning at arm assembly. I’m wondering how to build the developing environment? Do you use any IDE? Since I read above paragraphs, it’s seems using an IDE with debugging feature, and output the error code into “output” panel or some other kind of that.

    • rferrer says:

      Hi,

      I’m not using any IDE.

      As stated above, a text editor like vim, nano or emacs will do.

      To build the programs you may want to use the makefile shown in the post.

      Kind regards.

      • carnivion says:

        Great! I run it ok! Thanks rferrer, and another question, I’m also getting along with baking Pi OS by Cambridge. Can I use in-built as command instead of ...none-eabi... thing?

        • rferrer says:

          Hi,

          yes as long as the tool as --version says somehting like This assembler was configured for a target of `arm-linux-gnueabihf'..

  • Eddie Walsh says:

    Hi, Thanks for doing this Tutorial, as I am a complete beginner, so my first question is how do I Launch GNU Assembler, but then you say to open a Text editor like vim, nano, or emacs, so where do I get one of them, again Thanks, Regards ED

    • rferrer says:

      Hi Eddie,

      if you are using Raspbian and using the graphical environment, you will see an icon on your desktop that reads “LXTerminal”. Double-click on it and it will open a terminal window where you can type commands (in that window you may use the right-button to open a popup-menu with the usual options for copy/paste, etc).

      Now open an editor, given that you are a beginner you’ll probably want to use ‘nano’ which is the easiest of the three. In the terminal type

      nano first.s

      and copy-paste the text of Our first program shown above in the post. Then press Ctrl-X to exit (in nano this is depicted as ^X). Before exiting nano will ask you about keeping the file, say Yes and press Return to use the proposed filename (which is the one we specified in the command line, i.e. first.s).

      Now copy paste the following commands shown above (note that the in the post $ at the beginning of the line is just an representation of the prompt and you do not have to type it). Press Return after each line. (Note that I am repeating here the commands in the post for clarity)

      as -o first.o first.s
      gcc -o first first.o

      The first line invokes the GNU Assembler (to assemble the code into machine code) and the second one invokes the C compiler just to link the program and generate an executable file called ‘first’. Then you can run it.

      ./first; echo $?

      You should see a 2, which is the exit code of our very first program.

      If you feel intimidatd by the command line, you may want to invest some time reading a tutorial like this one. It is not specific to Raspberry but nevertheless will give you enough information so you can feel comfortable in the terminal. Regarding the Makefile, you may want to read this short introduction to make which will allow you to understand how to use the Makefile shown at the end of the post.

      Kind regards,

      • Eddie Walsh says:

        Hi rferrer, Thanks again, I tried again to follow your instructions from your reply, but when you say “Copy” and “Paste” you did not say from where ??, so I went back to the orig Tutorial and at last got to the end, so I will check out the 2 more links you suggested and hopefully I will get it ??, again can not Thank you enough, Regards ED

        • rferrer says:

          Hi Eddie,

          maybe I wasn’t clear enough. I meant to copy and paste from the code of the first program (you’ll find in the body of this post, above the comments block of this page) into the ‘nano’ editor.

          Kind regards,

          • Eddie Walsh says:

            Hi rferrer, I thaught that was what you meant, but if I try your Tutorial at same time as Nano on Pi it virtually slows to a crawl, so I run the Tutorial on my Desk Top pc, or again am I doing something wrong, any help appreciated, Regards Ed

          • rferrer says:

            Hi Eddie,

            you can connect to the Raspberry Pi from your laptop using the SSH protocol. Make sure that both the laptop and the Raspberry are connected to the same router.

            This way you can have your Raspberry turned on but you do not need to connect a keyboard nor a display.

            This tutorial explains the process very thoroughly.

  • Chria says:

    Thank you for this great tutorial. This mini hello world assembler program puts me on the right track. :)

  • […] the ARM assembly tutorial series from http://thinkingeek.com/2013/01/09/arm-assembler-raspberry-pi-chapter-1/ is an interesting stuff to learn about ARM assembly and has about 21 chapters about various […]

  • SaberWriter says:

    Great article. Thanks for writing it up. This was the kind of start I was looking for with my RPi.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>