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
-
So Long Neofetch and Thanks for the Info
Today is a day that every Linux user who enjoys bragging about their system(s) will mourn, as Neofetch has come to an end.
-
Ubuntu 24.04 Comes with a “Flaw"
If you're thinking you might want to upgrade from your current Ubuntu release to the latest, there's something you might want to consider before doing so.
-
Canonical Releases Ubuntu 24.04
After a brief pause because of the XZ vulnerability, Ubuntu 24.04 is now available for install.
-
Linux Servers Targeted by Akira Ransomware
A group of bad actors who have already extorted $42 million have their sights set on the Linux platform.
-
TUXEDO Computers Unveils Linux Laptop Featuring AMD Ryzen CPU
This latest release is the first laptop to include the new CPU from Ryzen and Linux preinstalled.
-
XZ Gets the All-Clear
The back door xz vulnerability has been officially reverted for Fedora 40 and versions 38 and 39 were never affected.
-
Canonical Collaborates with Qualcomm on New Venture
This new joint effort is geared toward bringing Ubuntu and Ubuntu Core to Qualcomm-powered devices.
-
Kodi 21.0 Open-Source Entertainment Hub Released
After a year of development, the award-winning Kodi cross-platform, media center software is now available with many new additions and improvements.
-
Linux Usage Increases in Two Key Areas
If market share is your thing, you'll be happy to know that Linux is on the rise in two areas that, if they keep climbing, could have serious meaning for Linux's future.
-
Vulnerability Discovered in xz Libraries
An urgent alert for Fedora 40 has been posted and users should pay attention.