RISC-V Assembly Language Programming Stephen Smith instant download
RISC-V Assembly Language Programming Stephen Smith instant download
or textbooks at https://ebookmass.com
https://ebookmass.com/product/risc-v-assembly-language-
programming-stephen-smith/
https://ebookmass.com/product/mips-assembly-language-programming-
robert-britton/
https://ebookmass.com/product/the-art-of-assembly-language-
programming-using-pic-technology-core-fundamentals-theresa-schousek-2/
The Art of Assembly Language Programming Using PIC®
Technology : Core Fundamentals. Theresa Schousek
https://ebookmass.com/product/the-art-of-assembly-language-
programming-using-pic-technology-core-fundamentals-theresa-schousek/
https://ebookmass.com/product/digital-design-and-computer-
architecture-risc-v-edition-sarah-harris-david-harris/
https://ebookmass.com/product/programming-for-absolute-beginners-
using-the-javascript-programming-language-1st-edition-jonathan-
bartlett/
https://ebookmass.com/product/the-awk-programming-language-2nd-
edition-aho/
Maker Innovations Series
Jump start your path to discovery with the Apress Maker Innovations
series! From the basics of electricity and components through to the
most advanced options in robotics and Machine Learning, you’ll forge a
path to building ingenious hardware and controlling it with cutting-
edge software. All while gaining new skills and experience with
common toolsets you can take to new projects or even into a whole new
career.
The Apress Maker Innovations series offers projects-based learning,
while keeping theory and best processes front and center. So you get
hands-on experience while also learning the terms of the trade and how
entrepreneurs, inventors, and engineers think through creating and
executing hardware projects. You can learn to design circuits, program
AI, create IoT systems for your home or even city, and so much more!
Whether you’re a beginning hobbyist or a seasoned entrepreneur
working out of your basement or garage, you’ll scale up your skillset to
become a hardware design and engineering pro. And often using low-
cost and open-source software such as the Raspberry Pi, Arduino, PIC
microcontroller, and Robot Operating System (ROS). Programmers and
software engineers have great opportunities to learn, too, as many
projects and control environments are based in popular languages and
operating systems, such as Python and Linux.
If you want to build a robot, set up a smart home, tackle assembling
a weather-ready meteorology system, or create a brand-new circuit
using breadboards and circuit design software, this series has all that
and more! Written by creative and seasoned Makers, every book in the
series tackles both tested and leading-edge approaches and
technologies for bringing your visions and projects to life.
More information about this series at
https://link.springer.com/bookseries/17311.
Stephen Smith
This work is subject to copyright. All rights are solely and exclusively
licensed by the Publisher, whether the whole or part of the material is
concerned, specifically the rights of translation, reprinting, reuse of
illustrations, recitation, broadcasting, reproduction on microfilms or in
any other physical way, and transmission or information storage and
retrieval, electronic adaptation, computer software, or by similar or
dissimilar methodology now known or hereafter developed.
The publisher, the authors, and the editors are safe to assume that the
advice and information in this book are believed to be true and accurate
at the date of publication. Neither the publisher nor the authors or the
editors give a warranty, expressed or implied, with respect to the
material contained herein or for any errors or omissions that may have
been made. The publisher remains neutral with regard to jurisdictional
claims in published maps and institutional affiliations.
This Apress imprint is published by the registered company APress
Media, LLC, part of Springer Nature.
The registered company address is: 1 New York Plaza, New York, NY
10004, U.S.A.
This book is dedicated to my beloved wife and editor Cathalynn Labonté-
Smith.
Introduction
The heart of every computer and smart device is a Central Processing
Unit (CPU). Historically, each CPU has been produced by a single
company which tightly controls the instruction set and internal
workings of the CPU. RISC-V (pronounced Risk Five) is a new approach
based on open-source principles. Anyone can implement a RISC-V CPU
using a standardized instruction set without requiring any special
licensing or royalty payments. As a result, RISC-V CPUs can be at a
much lower cost than proprietary CPUs and have exploded in the low-
cost microcontroller space. Now that more powerful RISC-V CPUs are
appearing, leading to Single Board Computers (SBCs) similar to the
Raspberry Pi, along with inexpensive low-end laptops and tablets, this
book presents the inner workings of typical RISC-V CPUs and details
the open-source Assembly Language instruction set they all implement.
Assembly Language is the native, lowest level way to program a
computer. Each processing chip has its own Assembly Language. This
book teaches programming RISC-V CPUs either running 32- or 64-bits.
Learning how a computer works, and mastering Assembly
Language, is an excellent way to get into the nitty-gritty details. These
low-cost microcontrollers and SBCs provide ideal platforms to learn
advanced concepts in computing.
Even though all of these devices are low-powered and compact,
they’re still sophisticated computers, many of which are capable of
running the full Linux operating system. Due to the RISC-V instruction
set standard, learning to program on one RISC-V processor is directly
applicable to all RISC-V processors.
In this book, we cover how to program RISC-V processors at the
lowest level, operating as close to the hardware as possible. Readers
will learn the following:
How to format instructions and combine them into programs, as well
as details of the operative binary data formats
How to program RISC-V instruction set extensions, such as floating-
point instructions
How to control integrated hardware devices by reading and writing
to the hardware control registers directly
How to interact with the Linux operating system and microcontroller
SDKs
The simplest way to learn these tasks is with a RISC-V SBC such as
the Starfive Visionfive 2. The book also details how to emulate the RISC-
V CPU on an Intel/AMD computer using the QEMU emulator, as well as
how to program the Espressif ESP32-C3 microcontroller. All the tools
needed to learn Assembly Language programming are open source and
readily available.
This book contains many working programs to play with, use as a
starting point, or study. The only way to learn programming is by doing
it, so do not be afraid to experiment, as it is the only way to learn.
Even if Assembly Language programming isn’t used in your day-to-
day life, knowing how the processor works at the Assembly Language
level and knowing the low-level binary data structures will make you a
better programmer in all other areas. Knowing how the processor
works will let you write more efficient C code and can even help with
Python programming.
Enjoy this introduction to Assembly Language. Learning it for one
processor family helps with learning and using any other processor
architectures encountered throughout a programmer’s career.
Any source code or other supplementary material referenced by the
author in this book is available to readers on GitHub
(https://github.com/Apress). For more detailed information, please
visit https://www.apress.com/gp/services/source-code.
Acknowledgments
No book is ever written in isolation. I want to especially thank my wife
Cathalynn Labonté-Smith for her support, encouragement, and expert
editing.
I want to thank all the good folks at Apress who made the whole
process easy and enjoyable. A special shout-out to Nirmal Selvaraj, my
production editor, who kept the whole project moving quickly and
smoothly. Thanks to Miriam Haidara, the acquisitions editor, for mobile
tech and maker programs, who got the project started. Thanks to
Stewart Watkiss, my technical reviewer, who helped make this a far
better book.
Table of Contents
Chapter 1:Getting Started
History and Evolution of the RISC-V CPU
What You Will Learn
Ten Reasons to Learn Assembly Language Programming
Running Programs on RISC-V Systems
Coding a Simple “Hello World” Program
Hello World on the Starfive Visionfive 2
Programming Hello World in the QEMU Emulator
About Hello World on the ESP32-C3 Microcontroller
Summary
Exercises
Chapter 2:Loading and Adding
Computers and Numbers
Negative Numbers
About Two’s Complement
RISC-V Assembly Instructions
CPU Registers
RISC-V Instruction Format
About the GCC Assembler
Adding Registers
32-bits in a 64-bit World
Moving Registers
About Pseudoinstructions
About Immediate Values
Loading the Top
Shifting the Bits
Loading Larger Numbers into Registers
More Shift Instructions
About Subtraction
Summary
Exercises
Chapter 3:Tooling Up
GNU Make
Rebuild a Project
Rule for Building .S files
Define Variables
Build with CMake
Debugging with GDB
Preparation to Debug
Setup for Linux
Set Up gdb for the ESP32-C3
Debugging with GDB
Summary
Exercises
Chapter 4:Controlling Program Flow
Creating Unconditional Jumps
Understanding Conditional Branches
Using Branch Pseudoinstructions
Constructing Loops
Create FOR Loops
Code While Loops
Coding If/Then/Else
Manipulating Logical Operators
Using AND
Using XOR
Using OR
Adopting Design Patterns
Converting Integers to ASCII
Using Expressions in Immediate Constants
Storing a Register to Memory
Why Not Print in Decimal?
Performance of Branch Instructions
Using Comparison Instructions
Summary
Exercises
Chapter 5:Thanks for the Memories
Defining Memory Contents
Aligning Data
About Program Sections
Big vs.Little Endian
Pros of Little Endian
About Memory Addresses
Loading a Register with an Address
PC Relative Addressing
Loading Data from Memory
Combining Loading Addresses and Memory
Storing a Register
Optimizing Through Relaxing
Converting to Uppercase
Summary
Exercises
Chapter 6:Functions and the Stack
About Stacks
Jump and Link
Nesting Function Calls
Function Parameters and Return Values
Managing the Registers
Summary of the Function Call Algorithm
Uppercase Revisited
Stack Frames
Stack Frame Example
Macros
Include Directive
Macro Definition
Labels
Why Macros?
Using Macros to Improve Code
Summary
Exercises
Chapter 7:Linux Operating System Services
So Many Services
Calling Convention
Finding Linux System Call Numbers
Return Codes
Structures
About Wrappers
Converting a File to Uppercase
Building .S Files
Opening a File
Error Checking
Looping
Summary
Exercises
Chapter 8:Programming GPIO Pins
GPIO Overview
In Linux, Everything is a File
Flashing LEDs
Moving Closer to the Metal
Virtual Memory
In Devices, Everything is Memory
Registers in Bits
GPIO Enable Registers
GPIO Output Set Registers
More Flashing LEDs
GPIOTurnOn in Detail
Root Access
Summary
Exercises
Chapter 9:Interacting with C and Python
Calling C Routines
Printing Debug Information
Register Masking Revisited
Calling Assembly Routines from C
Packaging the Code
Static Library
Shared Library
Embedding Assembly Language Code inside C Code
Calling Assembly from Python
Summary
Exercises
Chapter 10:Multiply and Divide
Multiplication
Examples
Division
Division by Zero and Overflow
Example
Example:Matrix Multiplication
Vectors and Matrices
Multiplying 3x3 Integer Matrices
Summary
Exercises
Chapter 11:Floating-Point Operations
About Floating Point Numbers
About Normalization and NaNs
Recognizing Rounding Errors
Defining Floating Point Numbers
About Floating Point Registers
The Status and Control Register
Defining the Function Call Protocol
Loading and Saving FPU Registers
Performing Basic Arithmetic
Calculating Distance Between Points
Performing Floating-Point Conversions
Floating-Point Sign Injection
Comparing Floating-Point Numbers
Example
Summary
Exercises
Chapter 12:Optimizing Code
Optimizing the Uppercase Routine
Simplifying the Range Comparison
Restricting the Problem Domain
Tips for Optimizing Code
Avoiding Branch Instructions
Moving Code Out of Loops
Avoiding Expensive Instructions
Use Macros
Loop Unrolling
Delay Preserving Registers in Functions
Keeping Data Small
Beware of Overheating
Summary
Exercises
Chapter 13:Reading and Understanding Code
Browsing Linux &GCC Code
Comparing Strings
Code Created by GCC
Reverse Engineering and Ghidra
Summary
Exercises
Chapter 14:Hacking Code
Buffer Overrun Hack
Causes of Buffer Overrun
Stealing Credit Card Numbers
Stepping Through the Stack
Mitigating Buffer Overrun Vulnerabilities
Do Not Use strcpy
PIE Is Good
Poor Stack Canaries Are the First to Go
Preventing Code Running on the Stack
Tradeoffs of Buffer Overflow Mitigation Techniques
Summary
Exercises
Appendix A:The RISC-V Instruction Set
Appendix B:Binary Formats
Appendix C:Assembler Directives
Appendix D:ASCII Character Set
Appendix E:Answers to Exercises
Index
About the Author
Stephen Smith
is a software architect, located in
Gibsons, BC, Canada. He’s been
developing software since high school, or
way too many years to record. He is an
expert in Artificial Intelligence and
Assembly Language programming,
earned his Advanced HAM Radio License,
and enjoys mountain biking, hiking, and
nature photography. He volunteers for
Sunshine Coast Search and Rescue. He is
the author of Raspberry Pi Assembly
Language Programming: ARM Processor
Coding, Programming with 64-Bit ARM
Assembly Language: Single Board
Computer Development for Raspberry Pi
and Mobile Devices, and RP2040 Assembly
Language Programming: ARM Cortex-M0+ on the Raspberry Pi Pico, all
published by Apress. Also, he writes his popular technology blog, at
smist08.wordpress.com.
About the Technical Reviewer
Stewart Watkiss
is a keen maker who has created
numerous physical computing projects
using a variety of computers and
microcontrollers. He is author of the
Apress titles Learn Electronics with
Raspberry Pi and Beginning Game
Programming with Pygame Zero. He
studied at the University of Hull, where
he earned a master’s degree in
electronics engineering, and more
recently at Georgia Institute of
Technology, where he earned a master’s
degree in computer science.
Stewart also volunteers as a STEM ambassador, helping teach
programming and physical computing to schoolchildren and at
Raspberry Pi events. He has created numerous resources for those
wanting to learn more about electronics and computing which are
available on his website (www.penguintutor.com).
© The Author(s), under exclusive license to APress Media, LLC, part of Springer
Nature 2024
S. Smith, RISC-V Assembly Language Programming, Maker Innovations Series
https://doi.org/10.1007/979-8-8688-0137-2_1
1. Getting Started
Stephen Smith1
(1) Gibsons, BC, Canada
Most people are familiar with Intel or AMD microprocessors lying at the
heart of their desktop, laptop, or server, and, similarly, most cell phones
and tablets use ARM microprocessors. RISC-V is the new entry built
around open-source concepts. Before getting into the details of RISC-V,
let’s first look at the history and evolution of modern microprocessors.
Name Description
RV32I Base Integer Instruction Set, 32-bit
RV32E Base Integer Instruction Set (embedded), 32-bit
RV64I Base Integer Instruction Set, 64-bit
Name Description
M Standard Extension for Integer Multiplication and Division
A Standard Extension for Atomic Instructions
F Standard Extension for Single-Precision Floating-Point
Name Description
D Standard Extension for Double-Precision Floating-Point
Zicsr Control and Status Register (CSR) Instructions
Zifencei Instruction-Fetch Fence
G Shorthand for the IMAFDZicsr_Zifencei base and extensions
C Standard Extension for Compressed Instructions
Working at this low level is technical and time-consuming, so why
would a programmer want to write Assembly Language code?
3. Place the SD card in the Visionfive 2 and turn the Visionfive on.
4. If you are using a hardwired Internet connection, you can skip this
step. When it boots to Linux, login, the password is “starfive.” Run
the Setup program and select your Wifi and enter the password.
ssh user@starfive.local
6. Next, the file system needs to be resized. If you don’t do this, you
can’t install any programs, since the base image has little free space.
This is a technical step and you should follow the instructions from
the Quick Start Guide carefully as you are deleting and recreating
disk partitions.
7. Now we perform the long step of installing the extra software that
includes web browsers and the development tools required for this
book. Use the scp command to copy the
“install_package_and_dependencies.sh” script to the computer and
run it. Enter the scp command from the host computer:
scp install_package_and_dependencies.sh
user@starfive.local:/home/user
chmod +x install_package_and_dependencies.sh
./install_package_and_dependencies.sh
9. Part of the previous shell script installs the GNU development tools.
If using a different Linux distribution of SBC, this may need to be
done manually using the command:
10. After the computer reboots, login normally. There are now web
browsers, office tools, and software development tools.
11. Install a simple GUI text editor to use for programming. Use a
terminal-based text editor like vi if you wish. GEdit is a good
simple choice that can be installed with the following command:
With the computer setup, we are ready to write, assemble, and run a
simple “Hello World” program. In this chapter, we won’t worry about
the details of how this program works, rather we are ensuring we can
assemble, link, and run programs. We will examine how everything
works in detail in the following chapters.
Either download the source code from the Apress Github site or
type in the program in Listing 1-1 and save it as HelloWorld.S, where
capitalization is important.
#
# Risc-V Assembler program to print "Hello RISC-V
World!"
# to stdout.
#
# a0-a2 - parameters to linux function services
# a7 - linux function number
#
.data
helloworld: .ascii "Hello RISC-V World!\n"
Listing 1-1 The Linux version of the Hello World program
Anything after a hash sign (#) is a comment. The code lines consist
of an optional label, followed by an opcode, possibly followed by several
parameters. To compile and run this program, create a file called build
that contains the contents of Listing 1-2:
as -o HelloWorld.o HelloWorld.S
ld -o HelloWorld HelloWorld.o
Listing 1-2 Build file for Hello World
After saving the build file, convert it to an executable file with the
following command:
chmod +x build
Now compile the program by running the build file and then the
resulting executable program with following commands:
./build
./HelloWorld
Figure 1-1 shows the result of running this. bash -x is used to show
the commands being executed.
8. Now we are ready to run QEMU with the rather long command:
qemu-system-riscv64 ^
-machine virt ^
-cpu rv64 ^
-m 2G ^
-device virtio-blk-device,drive=hd ^
-drive file=ubunturv.img,if=none,id=hd ^
-device virtio-net-device,netdev=net ^
-netdev
user,id=net,hostfwd=tcp::2222-:22 ^
-bios fw_jump.elf ^
-kernel uboot.elf ^
-append “root=LABEL=rootfs
console=ttyS0” ^
-nographic
3. From the downloaded Ubuntu image, extract the img file from the
xz file with
4. Expand the image, so there is some disk space to play around with
5. Now we are ready to run with the rather long qemu command:
qemu-system-riscv64 \
-machine virt \
-cpu rv64 \
-m 2G \
-device virtio-blk-device,drive=hd \
-drive file=ubunturv.img,if=none,id=hd \
-device virtio-net-device,netdev=net \
-netdev
user,id=net,hostfwd=tcp::2222-:22 \
-bios /usr/lib/riscv64-linux-
gnu/opensbi/generic/fw_jump.elf \
-kernel /usr/lib/u-boot/qemu-
riscv64_smode/uboot.elf \
-object rng-
random,filename=/dev/urandom,id=rng \
-device virtio-rng-device,rng=rng \
-append “root=LABEL=rootfs
console=ttyS0” \
-nographic
This will also install GDB and a few other GCC-related tools.
Next, either type the HelloWorld.S program from the previous
section into vi to save it to the new image or copy the program to this
image using scp:
as -o HelloWorld.o HelloWorld.S
ld -o HelloWorld HelloWorld.o
./HelloWorld
#
# Risc-V Assembler program to print "Hello RISC-V
World!"
# to the Espressif SDK monitor program through the
microUSB.
#
# a0 - address of helloworld string
#
app_main:
la a0, helloworld # load address of
helloworld
call puts # Call sdk to output
the string
j app_main # loop forever
.data
helloworld: .asciz "Hello RISC-V World!"
Listing 1-3 Espressif version of the Hello World program
We build the program with:
idf.py build
The first time CMD runs, it takes a long time since it needs to build the
entire SDK as part of the process. On subsequent builds, it is much
quicker as it only needs to build the files that have changed. Figure 1-3
shows the output of the build process on one of these following builds.
Figure 1-3 Building the Espressif version of the Hello World program
And then run it with the following:
Figure 1-4 shows the end of the download process, followed by the
start of the program.
Figure 1-4 Running Hello World on the Espressif RISC-V microcontroller
Note The required com port might not be com4, to check the
correct port, run the Windows Device Manager, open the “Ports
(COM & LPT)” section, and note which COM port is associated with
the “Silicon Labs CP210x USB to UART Bridge.”
Summary
In this chapter, we introduced the RISC-V processor and Assembly
Language programming along with why Assembly Language is used. We
covered how to set up three possible development environments for
Assembly Language programming using RISC-V.
In Chapter 2, “Loading and Adding,” we will start to understand the
details of the programs presented, including the basics of Assembly
Language instructions and CPU registers. We’ll learn how numbers are
represented in a computer and how basic integer addition and
subtraction work.
Exercises
1. Setup your RISC-V development environment. Whether it is on a
Linux Single Board Computer (SBC) such as the Starfive Visionfive
2, running in the QEMU emulator or using a microcontroller like the
Espressif ESP32-C3.
2. Modify the “Hello RISC-V World!” string to a different string. For the
Linux versions, remember to modify the string length as well.
© The Author(s), under exclusive license to APress Media, LLC, part of Springer
Nature 2024
S. Smith, RISC-V Assembly Language Programming, Maker Innovations Series
https://doi.org/10.1007/979-8-8688-0137-2_2
The goal of this chapter is to load various integer values into the CPU
and perform basic addition and subtraction operations on these values.
This sounds simple, but at the low level of Assembly Language, this can
get complicated. In this chapter, we will go step-by-step through the
various forms of the add, sub, and Shift instructions, to lay the
groundwork on how all instructions work, especially in the way they
handle parameters. This is so that in subsequent chapters, we can
proceed at a faster pace as we encounter the rest of the RISC-V
instruction set.
Before getting into the various Assembly Language instructions, we
will discuss how the RISC-V CPU represents positive and negative
numbers.
1011 = 1 ∗ 23 + 0 ∗ 22 + 1 ∗ 21 + 1 ∗ 20
= 1 ∗ 8 + 0 ∗ 4 + 1 ∗ 2 + 1
= 8 + 0 + 2 + 1
= 11 (decimal)
This is great for computers, but we use four digits for the decimal
number 11 rather than two digits. The big disadvantage for humans is
that writing out binary numbers is tiring, because they take up so many
digits.
Computers are incredibly structured, so all their numbers are the
same size. When designing computers, it doesn’t make sense to have a
variety of differently sized numbers, so a few common sizes have
become standard as described in the following paragraph.
A byte is eight binary bits or digits. In our preceding example with
four bits, there are 16 possible combinations of 0s and 1s. This means
four bits can represent the numbers 0 to 15. This means it can be
represented by one base 16 digit. Base 16 digits are represented by the
numbers 0 to 9 and then the letters A–F for 10–15. We can then
represent a byte (eight bits) as two base 16 digits. We refer to base 16
numbers as hexadecimal (Figure 2-1).
E6 = e ∗ 161 + 6 ∗ 160
= 14 ∗ 16 + 6
= 230 (decimal)
= 1110 0110 (binary).
We call a 32-bit quantity a word, and it is represented by four bytes.
You might see a string like B6 A4 44 04 as a representation of 32 bits of
memory, or one word of memory, or perhaps the contents of one
register. If we use a 64-bit RISC-V processor, then a word is still 32-bits,
and 64-bit quantities are referred to as doublewords, and we would see
a string of eight bytes to represent one doubleword of data.
If this is confusing or scary, do not worry. The tools will do all the
conversions for you. It’s just a matter of understanding what is
presented to you on the screen. Also, if an exact binary number needs to
be specified, that is usually done in hexadecimal, although all the tools
accept all the formats.
A handy tool is the Linux Gnome Calculator (Figure 2-2). The Gnome
Calculator has a Programming Mode which shows a numbers
representation in multiple bases at once. This calculator is included
with Ubuntu Linux with the Gnome desktop. To install it on the
Visionfive 2, enter the following command line:
ebookmasss.com