Why I Use #3: Assembler, C and PL/SW, for a Machine Rust Can't Target
1036 words • 6 min read • Abstract

Rust is what I build tools in, and APL, Lisp and Forth are what I demo ideas in. This post is about a third group: the low-level languages I use to write operating-system and programming-language code for small embedded processors. They are assembler, C and PL/SW, and the reason I need them is a processor Rust cannot target.
| Resource | Link |
|---|---|
| The processor | COR24, by MakerLisp; Embedded #2: COR24-RS |
| Assembler | sw-cor24-x-assembler (cor24-asm, Rust) |
| C | sw-cor24-x-tinyc (tc24r, Rust) |
| PL/SW | sw-cor24-plsw (compiler in C); PL/S, its history |
| Written in PL/SW | SWTOS, live; Made Visible #1 |
| Rust on COR24, the hard way | Rabbit-hole #1 |
| Try them | COR24 demo hub |
| The series | Why I Use: every post |
| Comments | Discord |
A machine Rust does not support
COR24 is a 24-bit RISC soft CPU designed by MakerLisp: a processor defined in an FPGA, here on the COR24-TB board with 1 MB of SRAM. Rust’s compiler has no COR24 target. Rabbit-hole #1 got Rust code onto COR24 anyway, by compiling for a 16-bit target Rust does support and translating the result, but that is an experiment, not a way to write an operating system.
So for COR24 I write in assembler, C and PL/SW. The assembler and the C compiler are written in Rust; PL/SW’s compiler is written in C and built with that C compiler. Each of them ends in the same place: COR24 assembler text, which the assembler turns into the bytes the CPU runs.
Assembler
At the bottom is assembler, where every instruction is written by hand. A countdown that prints 5, 4, 3, 2, 1 to the UART, from the assembler’s own test programs:
la r2,-65280 ; 0xFF0100 UART data
lc r0,5 ; counter = 5
_loop: mov r1,r0
add r1,48 ; + '0'
sb r1,0(r2) ; send to UART
add r0,-1 ; r0 -= 1
ceq r0,z
brf _loop ; if r0 != 0, loop
cor24-asm, written in Rust, assembles .s files into loadable images, raw bytes and listings. It began as the byte-for-byte reference for a self-hosted COR24 assembler, as24, that runs on COR24 itself.
C
C is the portable layer above it. tc24r is a C compiler for COR24 written in Rust, after the design of chibicc: a recursive-descent parser that emits assembly directly. It handles 24-bit int, char, pointers, arrays, structs, unions, function pointers and the usual control flow, and its 61 demos all pass. A C program reaches the hardware the way C always has, through a pointer to a memory-mapped register:
// Turn on LED D2 (active-low: write 0 to bit 0)
void led_on() {
*(char *)0xFF0000 = 0;
}
PL/SW
PL/SW is the one I designed. The first post in this series started with IBM’s PL/S (its history) and its macros that ran during compilation to write the code to be compiled. PL/SW, the Programming Language for Software Wrighter, is a freestanding systems language for COR24, inspired by PL/S, that carries that idea on. A macro declares what it takes and generates code, here three instructions of COR24 assembler:
/* ?LED_SET(VAL(n)) -- write a byte to LED MMIO register */
MACRODEF LED_SET;
REQUIRED VAL(expr);
GEN DO;
'la r2,16711680';
'lc r0,{VAL}';
'sb r0,0(r2)';
END;
END;
and a program calls it like a statement of the language:
MAIN: PROC;
CALL UART_PUTS(ADDR(MSG));
?LED_SET VAL(0);
END;
PL/SW also has BASED record templates for laying records over memory, controlled inline assembly, and conditional compilation, and it writes human-readable assembler with each source line as a comment. Its compiler is itself written in C, compiled by tc24r, and runs on COR24, fed its source over the UART; its linker tools are Rust.
What they are for
The SWTOS microkernel is the test of all three. It is a clean-room microkernel inspired by MINIX’s message passing, written in PL/SW and assembler, and it runs on the COR24-TB board and in an emulator in your browser. Made Visible #1 drew its storage. And the COR24 Forth kernel began as hand-written assembler before most of it moved into Forth (Embedded #3).
Where PL/SW is headed. Today PL/SW’s compiler is C, built with my COR24 C compiler. My longer-term goal is to port it to my future HLASM for COR24, sw-cor24-hlasm, a high-level assembler with macros, written in COR24 assembler.
Rust writes the first tools, and C, compiled by one of them, writes PL/SW’s. Assembler, C and PL/SW are what those tools turn into a running machine.
Part 3 of the Why I Use series. View all parts
Comments or questions? SW Lab Discord or YouTube @SoftwareWrighter.