Assembler programming on the Raspberry Pi

Assembler on Pi

© Lead Image © elenabsl,

© Lead Image © elenabsl,

Article from Issue 259/2022

Talk to your Raspberry Pi in its native assembler language.

Assembler programs run directly on the computer's hardware, which means they can reach nearly the maximum achievable speed of execution. Because assembler program code is very low level, writing the code is more complicated, but it is still the best choice for some tasks, especially on a computer such as the Raspberry Pi with its limited resources. Before you can start creating programs, however, you need to plumb the depths of the CPU and peripheral architecture.

Machine Code

To begin, it makes sense to clarify some terms. The CPU only understands machine code – zeros and ones or, more precisely, voltage levels that represent zeros and ones. Each command in machine code has a human-readable abbreviation that is easy to remember. These abbreviations are known as mnemonics and act as assembler commands. Assembler code is specific to a CPU architecture, which means that code for a Raspberry Pi (ARM) will not run on a PC (x86).

Programming in assembler on the Raspberry Pi can be approached in two ways: First, you can create an image in which you package the code and then boot the small-board computer (SBC) from that image to run the program. In other words, you degrade the Raspberry Pi to a microcontroller. With this method, the Pi runs without an operating system. Although you have full access to everything, you don't even get a shell.

The second way is to run the assembler program on the Raspberry Pi itself, which gives you the luxury of an operating system with everything that entails; however, you are limited in terms of direct access to the hardware. The second method was used for the example in this article.


A Raspberry Pi 3 with the current Raspberry Pi OS Lite provides the basis for my experiments. I will use Raspberry Pi Imager [1] to prepare the SD card, after which, I can boot from the card and get started right away, because all the tools needed for coding in assembler are included in the image. That said, an additional action provides more convenience and flexibility (see the "Activating SSH" box).

Activating SSH

To remove the need for an additional monitor and keyboard, I recommend working on the Raspberry Pi over SSH. To get the service running correctly on first boot requires some minor intervention. To begin, create an empty /boot/ssh file on the SD card; the SSH daemon will then launch automatically at boot time.

If needed, redirect the output of the X server over SSH from the Raspberry Pi to the desktop PC with the -X option. This works best if you are also using Linux on the desktop computer. If your router supports local name resolution, use the

ssh -X pi@raspberrypi@local

command to open the connection. All graphical output from programs then end up on the desktop computer. If the local DNS does not work, use the IP address of the Raspberry Pi, which you can look up from the list of connected devices on the router.

No Hello World

When you start working with a new programming language, the traditional approach is create a "Hello World" program; however, it takes a fair amount of assembler code and some understanding of strategies to generate even this simple output. Therefore, the first small assembler program only outputs the return code on the console, which indicates the status of a program on exiting. Bash stores this value in the $? variable, which you read with echo $?.

A return code of 0 means that the previously executed command ran without error; a value greater than 0 indicates an error. Listing 1 shows the example program 42.s, so named because the return code value 42 is the result. (Note that the title of this article is 42 in binary-coded decimal (BCD) encoding, which represents each decimal section from 0 to 9 as 4 bits (i.e., half a byte – or a nibble, if you prefer.)

Listing 1


.global main  /* Entry point for the program */
  mov r0, #42 /* Move value 42 to register r0 */
  bx lr       /* Return to calling program */

Assembler comprises relatively simple commands that do nothing more than move bytes back and forth, manipulate them, or react to a status bit, which makes it extremely important to document the code thoroughly and to use mnemonic identifiers where possible.

Labels are used in programming languages to mark points in the source code that serve as jump targets. The compiler exchanges the label for a physical memory address at build time, which clarifies the massive advantage of using labels: You do not need to calculate laboriously where in memory a particular command is located. Moreover, with each additional command you insert, all the addresses below it would move.

As in many other programming languages, you need to specify the starting point for a program in assembler. In Java and C the corresponding function is main(); in assembler you define the global label main. The label must precede the first line of code you want to execute, as shown in the assembler program in Listing 1.

The first line defines main globally so that the linker can find it. That label is then used in the second line, immediately followed by the first command, the mov command (short for move), which is used to move values (e.g., to store constant values in a register or to transfer the content of one register to another). To move values from registers into RAM or load them from RAM, you need the str (store register) and ldr (load register) commands instead. A register is a memory location on the CPU. An overview of the registers on the Raspberry Pi is shown in Table 1.

Table 1

ARM CPU Registers





General registers without a special function



Frame pointer register



General register without a special function



Stack pointer



Link register



Program counter

The program status register acts as the CPU's internal control register. The states of the individual bits tell you what results specific CPU register operations return. The commands for conditional jumps react to these bits. One well-known control bit is the zero bit (bit 30), which indicates that the value of an arithmetic logic unit (ALU) of a CPU, wherein all calculations and comparisons are performed, is 0.

In the example in Listing 1, the mov command stores a value of 42 in the CPU register r0. When the program ends, the operating system reads the value of register r0 and stores it in shell variable $?.

The bx command in the last line causes the CPU to continue the program at a different memory address – in this case, the address found in register lr. This register stores the address for program calls that the computer has to make after the program terminates.

The only question that now remains is how to generate an executable program from the assembler code. The workflows required to do this are very similar to the process of compiling C programs:

$ as -o 42.o 42.s
$ gcc 42.o
$ ./a.out
$ echo $?

The first line creates an object file from the assembler source code, which is then bound in the next line to the operating system to obtain an executable file. Unless you specify otherwise, this file is named a.out. You can execute the a.out program as usual. The final command shows that the program returns the value 42.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • ARM64 Assembly and GPIO

    Reading, writing, and arithmetic with the Raspberry Pi in ARM64 assembly language.

  • GPIO on Linux Devices

    The general purpose input/output interface is not just for small-board computers anymore: You can use GPIO on your Linux desktop or laptop, too, through the USB port.

  • Kitchen Timer

    A simple kitchen helper with two timers assists budding chefs in coping with dishes that are unlikely to be ready at the same time.

  • Go on the Rasp Pi

    We show you how to create a Go web app that controls Raspberry Pi I/O.

  • Bash Web Server

    With one line of Bash code, you can create a Bash web server for quickly viewing the output from Bash scripts and commands.

comments powered by Disqus
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters

Support Our Work

Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.

Learn More