Forge supports multiple compilation targets with different capabilities.
| Target Triple | Architecture | Format | Hosted | Use Case |
|---|---|---|---|---|
x86_64-unknown-linux-gnu |
x86_64 | ELF64 | ✅ | Native Linux apps |
x86_32-unknown-linux-gnu |
i686 | ELF32 | ✅ | 32-bit Linux apps |
x86_16-boot |
8086 | Flat (512B) / raw stage images | ❌ | Boot sectors, multi-stage OSes |
x86_32 raw (.fld) |
i686 | Raw flat binary (FORMAT raw) |
❌ | 32-bit kernels in boot chains (e.g. examples/os32) |
x86_64 raw (.fld) |
x86_64 | Raw flat binary (FORMAT raw) |
❌ | 64-bit kernels in boot chains (e.g. examples/os64) |
forgec input.dev -o output --target <triple>
| Flag | Description |
|---|---|
--target <triple> |
Target triple (default: x86_64-unknown-linux-gnu) |
-o <file> |
Output file (default: input name) |
--freestanding |
No hosted runtime, custom entry point |
--linker <path> |
Custom target config (.fld linker descriptor) |
--help |
Show all options |
Target: x86_64-unknown-linux-gnu
forgec hello.dev -o hello --target x86_64-unknown-linux-gnu
./hello
Direct Linux syscalls via syscall instruction:
write (1), read (0), exit (60)mmap (9), munmap (11)socket (41), connect (42)Full support: std.io, std.mem, std.string, std.math, std.alloc, std.fmt, std.volatile, std.gc, std.hal (freestanding)
Target: x86_32-unknown-linux-gnu (Rust target: i686-unknown-linux-gnu)
float32/float64 not supported# Requires 32-bit toolchain
rustup target add i686-unknown-linux-gnu
sudo apt-get install gcc-multilib g++-multilib libc6-dev-i386
cargo build --release --target i686-unknown-linux-gnu
./target/i686-unknown-linux-gnu/release/forgec hello.dev -o hello --target x86_32-unknown-linux-gnu
./hello
int 0x80 instructionwrite (4), read (3), exit (1)float32, float64)std.gc — importing it is a clean codegen error)std.alloc; first-fit with splitting, 4-byte headers)| Module | Support |
|——–|———|
| std.io | ✅ |
| std.mem | ✅ |
| std.string | ✅ |
| std.math | ✅ (int only) |
| std.alloc | ✅ |
| std.fmt | ✅ |
| std.volatile | ✅ |
| std.hal | ✅ (freestanding) |
| std.gc | ❌ |
Beyond the three built-in presets, .fld linker descriptors select FORMAT
raw for x86_32 and x86_64 — a bare flat binary (no ELF, no boot
signature) linked at the LOAD address (default 0x100000) and loaded by a
stage-2 loader:
examples/os32
and examples/targets/x86_32-raw.fld_dev_enter_long_mode): see examples/os64Both raw formats are freestanding: the reference-driven _dev_* helpers
(_dev_outb, _dev_inb, _dev_outw, _dev_inw, _dev_outl, _dev_inl,
_dev_iret, _dev_sti, _dev_cli, _dev_halt) are exposed through
std.hal.
Target: x86_16-boot
0x55AA signature) or bare raw
stage images via a .fld descriptor0x7C00 (BIOS load address; override per-stage with the
.fld LOAD directive)# Build boot sector
./target/release/forgec examples/bootloader.dev -o boot.bin --target x86_16-boot
# Build a stage-2 image loaded elsewhere (LOAD 0x9000)
./target/release/forgec src/boot/loader.dev -o loader.raw --linker examples/os/os-loader.fld
# Run in QEMU
qemu-system-x86_64 -fda boot.bin -nographic
flat images: 512 bytes total (including 0x55AA signature); raw
images are limited only by what the loading stage can readalloc, gc)0x00000 - 0x003FF : Interrupt Vector Table (IVT)
0x00400 - 0x004FF : BIOS Data Area
0x00500 - 0x07BFF : Free (conventional memory)
0x07C00 - 0x07DFF : Boot sector (load address)
0x07E00 - 0x9FFFF : Free
| Module | Support |
|---|---|
std.io |
✅ (BIOS teletype/serial) |
std.mem |
✅ |
std.string |
✅ |
std.math |
✅ (int only) |
std.alloc |
❌ |
std.fmt |
✅ |
std.volatile |
✅ |
std.hal |
✅ (word/byte I/O, INT nn, PIC; no _dev_outl/_dev_inl) |
std.gc |
❌ |
std.runtime |
❌ |
@freestanding
pub def _start() -> void:
puts("Hello, Forge bootloader!")
_dev_halt()
def puts(msg: ptr[char]) -> void:
unsafe:
var p = msg
var c = _dev_load_char(p)
while c != 0:
_dev_bios_teletype(c)
_dev_serial_putc(c)
p = p + 1
c = _dev_load_char(p)
extern def _dev_bios_teletype(c: char) -> void
extern def _dev_serial_putc(c: char) -> void
extern def _dev_load_char(p: ptr[char]) -> char
extern def _dev_halt() -> void
FORMAT flat: pads to 510 bytes + adds 0x55AA signatureFORMAT raw: emits the image as-is, string addresses fixed up against
the stage’s LOAD addressThe ForgeOS example shows all of it in one project: a
flat boot sector loading a raw loader at 0x9000, which loads a raw
kernel at 0x7C00 — no inline assembly anywhere.
# Build
forgec examples/bootloader.dev -o boot.bin --target x86_16-boot
# Run in QEMU
qemu-system-x86_64 -fda boot.bin -nographic
# Expected output:
# SeaBIOS...
# Booting from Floppy...
# Hello, Forge bootloader!
# x86_64 host → x86_32 target
cargo build --release --target i686-unknown-linux-gnu
./target/i686-unknown-linux-gnu/release/forgec input.dev -o out --target x86_32-unknown-linux-gnu
# x86_64 host → x86_16 target (uses x86_64 forgec)
./target/release/forgec input.dev -o out --target x86_16-boot
# Conditional compilation not yet supported
# Use separate entry files or build scripts
# build_x86_64.dev
# build_x86_32.dev
# build_boot.dev
| Target | Status |
|---|---|
x86_64-apple-darwin |
Planned (Mach-O) |
x86_64-pc-windows-gnu |
Planned (COFF) |
riscv64-unknown-elf |
Planned |
aarch64-unknown-linux-gnu |
Planned |
wasm32-unknown-unknown |
Planned |