Assembler programming on the Raspberry Pi
Flash
Now that you have seen a simple assembler program, it's time for a more sophisticated project. The good old flash program is a good candidate; it simply flashes a single LED. The positive contact of the LED is connected to GPIO21 (BCM notation) with a 1kilohm series resistor. The connection is routed out on header pin 40, which is handy because you have a GND on header pin 39 right next door. This pin is connected to the negative terminal on the LED.
Of course, the program presents several challenges to those used to programming with high-level languages. The GPIOs of the Raspberry Pi support extremely versatile use, which means it is not easy to address them correctly in an assembler program. Forty-one registers, each with a length of 32 bits, control the 54 GPIO pins of the chip installed on the Pi. (Not all of these GPIOs are accessible from the header; some of them are used internally by the Pi.) However, this still leaves you with a huge number of options for controlling the individual GPIOs. A detailed description of how the GPIOs work can be found in the BCM2835 peripherals data sheet [2] (pages 89-109).
Now that you know how to address the GPIOs, another problem raises its head. The Raspberry Pi's operating system prevents direct access to hardware resources. If you try to access the hardware directly, you will see a Segmentation Fault error message, which indicates that a memory protection violation occurred but gives you no additional clues as to where exactly the error occurred. Fortunately, the Raspberry Pi OS developers have provided a way to access the GPIOs without directly accessing the corresponding memory addresses. The operating system offers a driver to a special character file, /dev/mem
, that is a mirror of main memory. A good description of this can be found on the Sonoma State University website [3].
The first block of the program in Listing 2 contains the definition of various constants with assigned values, which offers several advantages: On the one hand, it lets you use meaningful names in the program, and on the other hand, it lets you to load the registers with 32-bit values.
Listing 2
flash.s
01 .globl main 02 .equ GPIOVAL, 0x200000 // Register value for the GPIO 21(BCM) 03 .equ GPFSEL2, 0x08 // Offset address for setting the GPIO mode 04 .equ GPIO_OUTPUT,0x08 // Define GPIO as an output 05 .equ GPFSET0, 0x1c // Offset register set 06 .equ GPFCLR0, 0x28 // Offset register clear 07 .equ TIME, 0x8000000 // Wait value 08 main: 09 ldr r0,=gpiomem 10 ldr r1,=0x101002 // Open for reading and writing 11 mov r7, #5 12 svc #0 13 mov r4, r0 14 mov r0, #0 15 mov r1, #4096 16 mov r2, #3 17 mov r3, #1 18 mov r5, #0 19 mov r7, #192 20 svc #0 21 // r0 Contains the base address of the mapped GPIO memory area 22 ldr r1, =GPIO_OUTPUT // GPIO21 23 str r1, [r0,#GPFSEL2] // set as output 24 ldr r2, =TIME // Wait in r2 25 ldr r1, =GPIOVAL // Register value in r1 for GPIO21 26 // Infinite loop 27 loop: 28 str r1, [r0,#GPFSET0] // Switch LED on 29 mov r10, #0 // Set r10 to 0 30 wait_on: // Increment r10 to TIME 31 add r10, r10, #1 32 cmp r10, r2 33 bne wait_on 34 str r1, [r0,#GPFCLR0] // Switch LED off 35 mov r10, #0 // Set r10 to 0 36 wait_off: // Increment r10 to TIME 37 add r10, r10, #1 38 cmp r10, r2 39 bne wait_off 40 b loop 41 42 .data 43 44 gpiomem: .asciz "/dev/gpiomem"
The mov
command can only move values up to a certain size directly to registers. The next large block of instructions opens the /dev/gpiomem
device and saves the base address of the mapped memory in register r0
.
Supervisor calls are used in the svc
block; put simply, these are something like calls to existing operating system programs. (The "Supervisor Calls" box provides additional information.) Initially, it is important that you have the option of accessing the GPIO from the address in r0
.
Supervisor Calls
Supervisor calls (syscalls) are functions provided by the operating system to perform certain tasks. Each syscall has its own number with which it is called. On the Raspberry Pi, the numbers with the matching names can be output on the terminal with the
cat /usr/include/arm-linux-gnueabihf/asm/unistd-common.h
command. You need the svc #0
command to execute a syscall in assembler, but you can also execute syscalls from high-level languages. When doing so, the number of the syscall must be in the r7
register. Depending on the syscall, the registers r0
to r6
contain the associated parameters. The return value of the call always ends up in register r0
. You can access the documentation for the individual syscalls in a terminal window by typing:
man 2 <name>
The 2
here indicates that you only want to search section 2 of the documentation.
The flash program starts in line 22 by first setting the mode for GPIO21 to output. It then loads the wait value into register r2
(line 24), and line 25 stores the bit combination for switching GPIO21 on and off in register r1
.
The GPIO registers work in a fairly simple way, and each of them has a specific task (set GPIO, clear GPIO, enable pullup, etc.). Each single bit in the registers corresponds to a GPIO pin. If you set the bit corresponding to GPIO21 in register GPFCLR0
(register for clearing GPIOs), the GPIO drops off to 0, which is why the program loads the combination for GPIO21 into register r1
.
Now all you need to do later is alternately move the r0
register to the GPFSET0
and GPFCLR0
GPIO registers (lines 28 and 34). The command means: Store the contents of r1
at the memory address that results from adding the contents of r0
to the constant from GPFSET0
and GPFCLR0
, respectively. The two wait loops increment the value in r0
until it reaches the value of TIME
(r2
).
This procedure of creating a wait is not very smart, because one CPU core is counting continuously. You can use the top
command to look at the CPU usage when the program is running. A CPU time-optimized program would use timers and interrupts, but it would be considerably more complicated in that case.
Finally, line 40 contains an unconditional jump command that jumps back to the loop
label, thus running the program for all eternity.
Where To Go Next
Now that you are knee-deep in assembler programming, you might want to look into the subject in detail. I would recommend a tutorial, such as the one you can find on the Think in Geek website [4]. It explains the basics from A to Z, with many useful tips.
You can easily enter simple examples, like the ones from this article, in an editor such as Nano. However, if you are working on more complex projects, you will want a more powerful editor to make your life easier.
Several possible ways to upload files to the Raspberry Pi are at your disposal. I mounted the Raspberry Pi over SSH in the Ubuntu file manager. This simple approach usually works fine on a LAN. With more complex projects it doesn't make much sense to build all the files manually. Even the old-fashioned make
will save you time and overhead.
Before you start to implement a function, always have a look at the list of syscalls. In many cases you will find something suitable. When using syscalls, you can assume that they do not contain any errors, which is worth its weight in gold, especially in assembler programming.
Conclusions
In this article I was only able to scratch the surface of assembler programming, and many details remain open. Getting started with this programming language is not difficult, and the individual commands are not complicated. Once you have looked into the CPU architecture, the meaning of assembler code is fairly easy to understand.
The tricky part begins as soon as you start using assembler to solve problems that are typically tackled in high-level languages. Even a small program will quickly grow to a few hundred commands. The advantages are the minimal code footprint and maximum execution speed, if programmed correctly.
Infos
- Raspberry Pi Imager: https://www.raspberrypi.org/software/
- BCM2835 data sheet: https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
- GPIO access by gpiomem: https://bob.cs.sonoma.edu/IntroCompOrg-RPi/sec-gpio-mem.html
- ARM assembler tutorial: https://thinkingeek.com/arm-assembler-raspberry-pi/
« Previous 1 2
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
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.
News
-
Fedora KDE Approved as an Official Spin
If you prefer the Plasma desktop environment and the Fedora distribution, you're in luck because there's now an official spin that is listed on the same level as the Fedora Workstation edition.
-
New Steam Client Ups the Ante for Linux
The latest release from Steam has some pretty cool tricks up its sleeve.
-
Gnome OS Transitioning Toward a General-Purpose Distro
If you're looking for the perfectly vanilla take on the Gnome desktop, Gnome OS might be for you.
-
Fedora 41 Released with New Features
If you're a Fedora fan or just looking for a Linux distribution to help you migrate from Windows, Fedora 41 might be just the ticket.
-
AlmaLinux OS Kitten 10 Gives Power Users a Sneak Preview
If you're looking to kick the tires of AlmaLinux's upstream version, the developers have a purrfect solution.
-
Gnome 47.1 Released with a Few Fixes
The latest release of the Gnome desktop is all about fixing a few nagging issues and not about bringing new features into the mix.
-
System76 Unveils an Ampere-Powered Thelio Desktop
If you're looking for a new desktop system for developing autonomous driving and software-defined vehicle solutions. System76 has you covered.
-
VirtualBox 7.1.4 Includes Initial Support for Linux kernel 6.12
The latest version of VirtualBox has arrived and it not only adds initial support for kernel 6.12 but another feature that will make using the virtual machine tool much easier.
-
New Slimbook EVO with Raw AMD Ryzen Power
If you're looking for serious power in a 14" ultrabook that is powered by Linux, Slimbook has just the thing for you.
-
The Gnome Foundation Struggling to Stay Afloat
The foundation behind the Gnome desktop environment is having to go through some serious belt-tightening due to continued financial problems.