Copyright (c) 2026 Muhammad Anisur Rahman. All rights reserved.
03-porting-guide.md says what a port is and what the contract is. This one says what each existing port actually did, so a new one can copy the nearest relative instead of starting from the contract.
Read the lineage first. Most of these ports were not written from scratch. Two families account for most of the work, and a new port that belongs to one of them is a much smaller job than one that does not.
8086 -> 80386 -> x86_64 arm (ARM7/9) -> arm64
16-bit 32-bit 64-bit ARMv4T/v5 AArch64
The upgraded ports keep the thread ABI byte-faithful with their ancestor. That is deliberate and it is why the lineage matters: the context layout, the save order and the entry conventions carry forward, so the new port inherits a design that has already been debugged rather than inventing one.
| Port | Architecture | Status | Board | Built with |
|---|---|---|---|---|
arm64 |
AArch64 | active | qemu_virt | aarch64-none-elf-gcc |
riscv |
RV32 / RV64 | active | qemu_virt | riscv64-unknown-elf-gcc |
x86_64 |
x86-64 | active | qemu_uefi | host gcc (mingw) |
x86_32 |
IA-32 | active | qemu_uefi | host gcc -m32 (mingw) |
avr32a |
AVR32 (modern) | active | EVK1100 | avr32-gcc (discontinued) |
80386 |
Intel 32-bit | archive | — | base of x86_64 |
8086 |
Intel 16-bit | archive | — | base of the x86 lineage |
arm |
ARM7TDMI / ARM922T | archive | — | base of arm64 |
mips |
MIPS R3000/R4000 | archive | — | family reference |
microblaze |
Xilinx MicroBlaze | archive | — | FPGA soft core |
ppc405 |
PowerPC 405 | archive | — | |
avr32 |
AVR32 (original) | archive | — | distinct from avr32a |
Active means the port has a board, a live toolchain and is exercised. Archive means the port exists and compiles but has no board — it is a starting point for a port, not something you can boot today.
| If your target is… | Start from | Because |
|---|---|---|
| 64-bit RISC, MMU, multi-core | arm64 |
The most modern port; SMP substrate lives here |
| 64-bit RISC, no MMU | riscv |
Same shape, simpler memory story |
| x86 of any width | x86_64, then look back at 80386 |
The lineage is byte-faithful; the ancestor shows why a field exists |
| A soft core on FPGA | microblaze |
A soft core with a board, and the closest starting point for one |
| 32-bit RISC, classic | mips |
The family reference, written to be read |
| Something with 16-bit segments | 8086 |
The only port that has faced that |
Where a port reaches core registers from C with inline assembly, give each accessor its own private scratch global, never one shared between them. An interrupt landing between the store and the caller's read must not be able to clobber the value through a shared variable. Copy that pattern, not just the instruction sequence.
Where multi-core actually works. If you are porting to something with more than one core, read 10-smp-manual.md and this port together.
One trap, documented in include/KERNEL.H and worth repeating: the saved status word is restored into SPSR_EL1, a real architectural register, where bits [9:6] are DAIF. So a software flag parked in one of those bits is not a spare bit — bit 8 is the SError mask. That is why AArch64 gets no PIC status bit, where an architecture with a plain software status word can afford one. Check what your architecture already means by a bit before using it.
Both widths from one source. If your target is 32-bit, this is the port that shows how to keep a 64-bit-capable layout honest on a 32-bit machine.
The x86-64 port is the only target where the integer model differs from every other port, and it caused three real bugs. Long is not pointer-sized everywhere. Anything that stores a pointer in a long, or assumes sizeof(long) == sizeof(void*), works on every other port and breaks here. When porting: check every cast between an integer and a pointer, and every struct field that holds an address.
Both x86 boards build with the host gcc, and that is deliberate, not a stand-in for a missing cross compiler. They are UEFI applications — PE32+ using the Microsoft x64 ABI — which is exactly what mingw produces. An x86_64-elf- style ELF cross compiler would be the wrong tool on both counts, output format and ABI.
The port works. The compiler does not ship any more. Treat it as buildable only if you already have the toolchain; do not choose it as a model for new work.
8086, 80386, arm, mips, microblaze, ppc405, avr32 compile and are structurally complete, but none has a board. What they are for is being read: each shows one architecture family's answer to the same questions, and the two that are ancestors (8086→80386, arm) define the layout the modern ports inherited.
Arch headers live under include/CPU/<arch>.
The generic order is in 03-porting-guide.md. What experience with these thirteen adds:
after the first two was built this way.
bugs live here than everywhere else combined, and they present as anything — a corrupted variable in an unrelated task, a return to the wrong address, a fault minutes later. See "Register save sets — where ports go wrong" in 03-porting-guide.md.
tuwa_bsp.h. It is compiler-checked, so amissing or mistyped entry point is a build error rather than a silent hang. 04-bsp-guide.md covers it.
it. bootMessage() is a polled loop over one character-out function — that is the whole requirement, and it must not use interrupts.
The kernel, scheduler, memory manager, IPC, message queues, mutexes, events, timers, filesystem and IP stack are architecture-independent and ship as a library. A port supplies the context switch, the interrupt entry, the timer tick and the console — and inherits the rest.