last updated: 2026-10-30
Work in progress; Coming soon! Code will be available when the book is published.

Do you remember the 80s? Clearly the music was better, but also a whole generation was hooked by electronics and computers. They had fun by moving bits and bytes and their enthusiasm helped to drive the development of technology forward. I think that the core of this is still true today, and moving bits and bytes is an absolute basic skill to understand programming and how computers and microcontrollers work. And Assembler is the most natural approach to do this.
Learning RISC-V assembly is future-proof, providing a robust and portable foundation for an enormous range of modern hardware.
We need people in Europe who understand the basics! We need more RISC-V chips produced in Europe to get more resilient. RISC-V is Open Source! and we need open source in Europe to stay competitive. AI will help to create better code, but only if humans know the basics and can control the code!
This is not a classic RISC-V Assembler book! We use the approach Learning by doing in all the chapters. But we also have three Make It chapters, where you can do some soldering and 3D printing if you want.
This book targets students, teachers and engineers who want to go further than simulations and makers who want to better understand the RP2350 and perhaps do some cool and very fast stuff at the hardware level.
The book is divided into three parts, each concluding with a "Make It" chapter. We cover bare metal programming and debugging, starting with digital and analog in- and output (push-buttons, LEDs, potentiometer, music with PWM). We look at clocks, timers and internal and external interrupts. But we also cover in detail memory and Direct Memory Access, the RP2350 PIO state machines (WS2812b and I2S interface) and even dual core communication. Gradually we build our own SDK with many subroutines organized in libraries. This allows you to make Assembly programming nearly as easy as C, but with more control.
In the "Make it" chapters you can build a real serial terminal with HDMI monitor and USB Keyboard (Pico2Terminal), a Pong Game for 2 players with sound (Pico2Pong) and a MIDI controlled wavetable Oscillator (Pico2MCWO).
Chapter 1 starts with the "Hello World" program for microcontrollers. To do so, we install the RISC-V GCC toolchain and OpenOCD. We convert a Pico2 board to a Debug Probe that we use to program (flash) our code and to ... debug. We will explain every step in great detail.
The first program (01_toggle.S) toggles a GPIO and the second program (01_toggle_led.S) uses a small delay subroutine to toggle an LED.
Chapter 2 is used to tidy up. We use the preprocessor to organize the code in different files and subroutines. We also learn to debug with GDB, explain the importance of a good alignment and the subsystem resets of the Pico2. This chapter goes into a lot of details. If you are eager to progress, you can skip the explanations (GDB, alignment, objdumps ...) and read them later. Essential are the new organization, the deasserting of resets and the script to compile and program the controller.
The program (02_toggle_tidy.S) still toggles a GPIO (or LED) but it already uses 2 definitions files and 3 libraries.
Chapter 3 is about clocks. We activate the crystal to get a more stable system clock and use the PLLs to create a higher system clock of 150{nbsp}MHz and a USB clock of 48{nbsp}MHz. For our peripherals, we set the peripheral and ADC clock to 48{nbsp}MHz.
The program (03_toggle_xosc_pll.S) still toggles the LED but the cores are now running at 150{nbsp}MHz. We also output the system clock, or if we need to all other clocks on GPIO15.

03_toggle_xosc_pll.SIn Chapter 4, we activate the serial interface and learn to use both UARTs. We communicate at a speed of 1000000{nbsp}bit/s (8N1) with a terminal program on a computer. We write a lot of subroutines to print bytes, half-words and words in decimal and hex, so we can display the contents of memory and registers.
The first program (04_serial_echo.S) echoes the keys typed in a terminal program (left video) and the second program (04_serial_print.S) prints text from memory, bytes from memory, register values and memory addresses in hexadecimal (right video). Both use UART0 (GPIO0 and GPIO1).
04_serial_echo.S 04_serial_print.SPart 1 closes with Chapter 5, the first "Make it" chapter. Here we condense everything we learned and build a modern computer terminal called Pico2Terminal. It uses a USB keyboard as input device and an HDMI monitor as output device. A minimal terminal program takes commands from the keyboard and displays e.g., the contents of memory on the monitor.



Here a video that shows the results of the program 05_pico2terminl.S.
05_pico2terminl.SIn Chapter 6 we will interact with the controller through switches or push-buttons (digital in) and potentiometers (analog in). The internal ADC will also be used to get and display the system voltage and the temperature (internal temperature sensor of the RP2350).
Here a video that shows the results of the program 06_din_adc.S.
06_din_adc.SChapter 7 is about timers and PWM (analog out). We use the two system timers TIMER0 and TIMER1 and the the RISC-V platform timer to create better delay subroutines with microseconds ticks. They are also used to write micros and millis subroutines to measure time and write non-blocking software. With PWM timers we can dim the light and even make noise :).
We create in this chapter 4 programs: 07_timers.S, 07_pwm_led.S,07_pwm_sound.S and 07_pwm_sound2.S.
07_pwm_led.S07_pwm_sound.S 07_pwm_sound2.SThe more challenging Chapter 8 is about interrupts. These enable the microcontroller to respond immediately (in real-time) to events. This makes it possible to write better, simpler and more efficient programs. We use them not only to program the perfect boiled egg timer, but also to react very quickly to GPIO and UART input. In this chapter, we make real music with interrupts and Direct Digital Synthesis (DDS).
Beneath TIMER0 and TIMER1, we have a RISC-V Platform Timer (RPT) available in the Pico2. We create in this chapter the program 08_interrupts_rpt.S that use the RISC-V Platform Timer to trigger an alarm after some time. Then we add a polled push-button to start the timer (08_interrupts_rpt_2.S) and finally we use an external interrupt for the push-button (08_interrupts_gpio.S). To test a second external interrupt, we use an UART1 interrupt to echo a character (08_interrupts_uart.S).
Finally, after an introduction to DDS and MIDI notes, we write a program that uses DDS, PWM and an RPT interrupt to play all the piano MIDI notes (08_interrupts_dds.S). You remember the music from the 80s? We play, using a crunchy square wave, a cool synth track from Harold Faltermeyer used for the film Beverly Hills Cop called Axel F.
08_interrupts_dds.S Song Axel_F: 08_interrupts_dds_song.SPart 1 closes with Chapter 5, the first "Make it" chapter. Here we condense everything we learned and build a modern computer terminal called Pico2Terminal. It
And finally Part 2 closes with the "Make it" Chapter 9 Everybody knows PONG.
We create Pico2Pong (09_pico2pong.S) in color and with sound and so consolidate what you have learnt in the previous chapters.



09_pico2pong.SChapter 10 is about memory and Direct Memory Access (DMA). We take a thorough look at the RISC-V memory map, in order to understand how the stack works, to enhance our startup code and to use DMA to copy memory blocks very fast, and to copy memory data to the peripherals, both without involving a core.
In the program 10_memory.S, we test our new startup code that copies the ".data" and ".bss" sections to SRAM and initialises the ".bss" variables with zeros. We adjust the linker file and the programming script accordingly. In 10_dma_m2m.S, we copy one memory block to another using one DMA channel.
Next, we copy a memory wavetable (sine) directly to the PWM peripheral using one DMA channel with 10 ring transfers (10_pwm_m2pwm_ring.S). No core is involved! To auto- restart the transfers without CPU intervention, we re-trigger a DMA channel via a second control DMA channel (10_dmax2_m2pwm.S).
10_pwm_m2pwm_ring.S The annoying beep: 10_dmax2_m2pwm.SChapter 11 is the biggest chapter. It is about using the RP2350 PIO (Programmable Input/Output) hardware feature that provides programmable state machines that can manipulate GPIO pins very fast and independently of the CPU. In addition to manipulating pins (even 8 pins in parallel) we create an interface to control WS2812b RGB LEDs and an Inter-IC Sound (I2S) interface.
After toggling a GPIO (11_pio_toggle.S) with a PIO state machine, we create an RGB LED WS2812b interface and blink 5 RGB LEDs (011_pio_ws2812b.S).
011_pio_ws2812b.SWe push this further by adding DMA to feed a PIO state machine directly with a memory wavetable. We output the 8-bit values in parallel through 8 GPIOs to a DAC (11_pio_dma_outx8) and change the frequency in little steps and play our song (11_pio_dma_outx8_song).
11_pio_dma_outx8.S 11_pio_dma_outx8_songI2S is a high quality (48k-384k samples) serial stereo audio interface. We create an I2S interface with one PIO state machine and use wavetables with 2048 samples and 16-bit signed values. A first test outputs a neat stereo signal (11_pio_dma_i2s_48k.S), but again we can't change the frequency easily. To do so we need a ping pong buffer and DDS. Now we can change the frequency at will.
We output an 1 kHz square wave signal with a hard and a smoothed wavetable. To avoid aliasing (ripples) we can augment the sample rate. The second picture shows the highest piano note with 384k samples (11_pio_dma_i2s_384k.S).

11_pio_dma_i2s_dds_48k.S 11_pio_dma_i2s_dds_384k.SIn Chapter 12 we wake up the second sleeping beauty's h(e)art1 (core1). We will see four different methods by which cores can communicate with each other without race conditions. In a producer/consumer example we use e.g. the new General Exclusive Monitor of the RP2350 and a doorbell interrupt.
The first program 12_two_cores.S uses the hardware FIFO buffers to communicate between cores and allows both cores to blink the onboard LED. This program is blocking, so the second program does the same using doorbell interrupts (12_two_cores_irq.S).
We want to pass data from a producer (core1) to a consumer (core0). Core1 gets a potentiometer value and passes this to core0, which uses PWM to dim the onboard LED. To do
so, we use double buffering (ping-pong buffer) with a doorbell interrupt, which
ensures there will be no race condition when using SRAM memory (12_two_cores_irq_pp.S).
The last two examples use the new General Exclusive Monitor of the RP2350 and a doorbell interrupt to pass data. The first program passes a 32-bit word (12_two_cores_irq_gem.S). The second program uses 8-bit from the word to pass data (24-bit) from two 64-word buffers (12_two_cores_irq_pp.S).
And finally Chapter 13, the last "Make It" called Pico2MCWO. In this chapter we write a large program that uses a lot of what we learned in all the previous chapters. We create a MIDI Controlled Wavetable Oscillator (MCWO) with two oscillators (Base of a synthesizer), 3 pots, 2 push-buttons and 5 WS2812b LEDs.



In the video I first the sweep through the 8 wavetables (stereo mode), than the wavetable of the second channel changes and finally I change loudness and the pitch of the second channel.
013_pico2mcwo.SThe Pico2MCWO is not perfect yet, but it has many possibilities to tweak a sound and is really a good base for a homemade synth.