C cross-compiler for the TPCPU virtual CPU
#pragma tpcpu mem_pow2 24
#include <stdio.h>
int main() {
printf("Hello, TPCPU!\n");
return 0;
}tpcpu-cc build hello.c # -> hello.bin
tpcpu-cc run hello.c # compile + runRequirements: Rust stable, libclang.
Windows support should work but haven't been tested by us (you would have to install clang/LLVM yourself!)
# Arch Linux
pacman -S clang
# Debian / Ubuntu
apt install libclang-dev
git clone https://github.com/vbxq/tpcpu-cc && cd tpcpu-cc
git submodule update --init --recursive
cargo install --path tpcpu_cc_drivertpcpu-cc supports two execution targets with different capabilities:
| Standalone | Phobos | |
|---|---|---|
| Runtime | Bare-metal TPCPU emulator | Phobos Workstation OS |
| I/O** | putchar / getchar only |
Terminal, files, directories, IPC |
fopen / fread** |
Always returns NULL / 0 | Full file I/O via syscalls |
| Directories | Not available | tpcpu_fs.h |
| Process / IPC | Not available | tpcpu_process.h, phobos.h |
| Default memory | 16 MiB | 512 MiB |
__PHOBOS__ |
not defined | defined automatically |
You can use standalone for quick iteration and programs that only need terminal I/O, and Phobos (OS) when you need a filesystem, command-line arguments, or IPC.
tpcpu-cc new hello
cd hello
make build # produces main.bin
make run # build + run in emulatorFor multi-file builds, edit the Makefile to include the other source file, use
make buildand run the emulator separately.
The emulator is located at.tpcpu/bin/tpcpu_emu, in the root of your project folder.
Build cache is in.tpcpu/cache/(keyed by ASM hash). Clear withrm -rf .tpcpu.
tpcpu-cc new myapp --target phobos
cd myapp
make install # compile and copy binary into the Phobos disk image
make phobos # boot Phobos Workstation
# then type: myappmake install uses tpcpu-cc --phobos-bin-path to locate the Phobos
bin/ directory automatically.
tpcpu-cc new <name> # scaffold project (standalone)
tpcpu-cc new <name> --target phobos # scaffold Phobos project
tpcpu-cc build main.c # compile to main.bin
tpcpu-cc build a.c b.c --output app.bin
tpcpu-cc build main.c --target phobos
tpcpu-cc run main.c # compile + run (single file only)
tpcpu-cc phobos # boot Phobos Workstation| Flag | Default | |
|---|---|---|
--target <t> |
standalone |
standalone or phobos |
--emit <mode> |
bin |
bin, asm (TPCPU assembly), c32 (IR) |
-o, --output <file> |
<first_input>.bin |
output path |
-D <name>[=value] |
preprocessor define | |
-I <dir> |
include search path | |
--no-libc |
skip automatic libc | |
--trom-asm |
TROM assembler (required for --optimize) |
|
--optimize |
peephole optimizer (requires --trom-asm) |
Both standalone and phobos projects ship with a Makefile.
Standalone:
make build # tpcpu-cc build $(SRCS) -> main.bin
make run # tpcpu-cc run $(SRCS) (single file only)
make clean # rm *.bin .tpcpu/Phobos:
make build # tpcpu-cc build $(SRCS) --target phobos -> main.bin
make install # build + copy binary to phobos_workstation/bin/<name>
make phobos # boot Phobos (tpcpu-cc phobos)
make clean # rm *.bin .tpcpu/#pragma tpcpu mem_pow2 <n> // total memory = 2^n bytes (5..32)
// standalone default: 24 (16 MiB)
// phobos default: 29 (512 MiB)
#pragma tpcpu stacksize <bytes> // default: 2097152 (2 MiB)
#pragma tpcpu init_fixedprec <n> // fixed-point precision (0..31, default: 15)Memory layout:
[code + globals] [stack -->] [<-- heap]
^ ^
stack_start 2^mem_pow2
All integers and pointers are 32-bit. double and long long are compile errors.
Types: char (1), short (2), int / long / pointer (4), float (4 IEEE 754 soft-float), struct (packed), union, enum
Expressions: arithmetic, bitwise, logical, all assignment forms, ++/--, ternary, casts, & / *, a[i] / a[i][j], . / ->, function pointers, sizeof, comma
Statements: if, while, do, for, switch, break, continue, return
Declarations: static, extern, const, typedef, arrays, structs with designated initializers (in-order), va_list / va_arg, setjmp / longjmp
Inline assembly: __asm__("instruction") or __tpcpu_asm("instruction"), emits verbatim
Not supported: double, long long, VLA, goto, bitfields, out-of-order designated initializers.
Headers live in include/ and are linked automatically.
| Header | Provides |
|---|---|
<stdio.h> |
printf / fprintf / sprintf, fopen / fclose / fread / fwrite / fseek, stdin / stdout / stderr |
<stdlib.h> |
malloc / free / realloc / calloc, atoi, strtol, strtof, qsort, rand |
<string.h> |
mem*, str*, strdup |
<math.h> |
sinf, cosf, sqrtf, powf, logf, and all *f variants no double |
<ctype.h> |
isdigit, isalpha, toupper, tolower, ... |
<stdarg.h> |
va_list, va_start, va_arg, va_end |
<setjmp.h> |
setjmp, longjmp |
On standalone,
fopenalways returnsNULL. Onlystdin/stdout/stderrwork. On Phobos, full file I/O is available via syscalls.
| Header | Provides |
|---|---|
<tpcpu_fs.h> |
Directory operations: tpcpu_mkdir, tpcpu_rmdir, tpcpu_dir_nentries, tpcpu_dir_entry, tpcpu_dir_exists |
<tpcpu_process.h> |
tpcpu_spawn, tpcpu_murder, tpcpu_getpid, tpcpu_ntasks |
<tpcpu_time.h> |
tpcpu_time_millis, tpcpu_time_seconds, tpcpu_time_micros |
<tpcpu_term.h> |
tpcpu_flush_stdout, tpcpu_clear_term |
<tpcpu_fg.h> |
tpcpu_claim_foreground, tpcpu_relinquish_foreground |
<phobos.h> |
struct kmailbox, IPC syscalls (__sys_declare_mailbox, __sys_sendmail, __sys_undeclare_mailbox) |
float is IEEE 754 single-precision implemented in software. All float operations compile transparently to __sf_* calls. Literals are converted to bit patterns at compile time.
float x = 1.5f; // 0x3FC00000 no runtime cost
float y = x * 2.0f; // lowers to __sf_mul(x, 2.0f)
int n = (int)x; // lowers to __sf_f2i(x)double is a compile error. Use float everywhere.
lua-tpcpu (examples/ports/lua54) is a
full Lua 5.4.6 port running on Phobos with all 10 standard libraries. It is the
primary real-world validation of the compiler.
cd examples/ports/lua54 && make install && make phobos
# in Phobos: lua -i -vThe C standard library lives in libc/ (.c source files compiled as part of
each program) and include/ (.h headers).
When building from source (cargo install --path tpcpu_cc_driver from the
repo root), the compiler finds these directories relative to the workspace root
automatically.
When installed as a standalone binary, the compiler embeds libc/,
include/, and the required vendor files at compile time and extracts them to
the platform data directory on first use:
| Platform | Default location |
|---|---|
| Linux | $XDG_DATA_HOME/tpcpu-cc -> ~/.local/share/tpcpu-cc |
| macOS | ~/Library/Application Support/tpcpu-cc |
| Windows | %LOCALAPPDATA%\tpcpu-cc |
Windows and macOS support haven't been tested yet.
To modify the libc (e.g. add a new function), edit the files in libc/ or
include/ in the repository, then reinstall with cargo install --path tpcpu_cc_driver. The updated files are re-embedded into the binary.
Environment variable overrides (take precedence over auto-detection):
| Variable | Effect |
|---|---|
TPCPU_CC_LIBC=<path> |
Use this directory as the libc source root |
TPCPU_CC_INCLUDE=<path> |
Use this directory as the include root |
TPCPU_CC_DATA_DIR=<path> |
Override the entire data directory |
cargo testflowchart TD
subgraph Input
C[C source files]
P[pragma extraction]
end
subgraph Frontend
CL[clang / libclang\nAST parsing]
LO[c32_lowering\nAST -> C32 IR]
end
subgraph IR["C32 IR"]
VAL[validation]
MULTI[multi-file\nreconcile + merge]
end
subgraph Backend
BE[c32_backend_tpcpu\nTPCPU assembly]
RT[c32_runtime_tpcpu_min\nlibc ASM bundling]
end
subgraph Assemble
BST[bootstrap assembler\ndefault]
TROM[as.trom\n--trom-asm]
end
subgraph Targets
SA[Standalone]
PH[Phobos]
end
C --> P --> CL --> LO --> VAL --> MULTI --> BE --> RT
RT --> BST & TROM
BST & TROM --> SA & PH
Public domain. vendor/TPCPU is public domain as well, examples/ports/lua54 is MIT.
