Skip to content

techfury90/ObjectRISC

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

530 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Object RISC

A 1986 alternate-history CPU architecture, designed in detail and implemented enough to run.

Object RISC is a single-issue 32-bit load-store RISC in the style of its era — MIPS R2000, SPARC v1, the family — with two non-conventional commitments:

  1. A separate object register file. Sixteen 64-bit object registers hold references — opaque, capability-bearing pointers to objects in a global namespace. Integer registers cannot be dereferenced as objects, and objects cannot be smuggled through integer memory. The capability invariant is enforced statically at the ISA level.
  2. Crossbar interconnect as a primitive. A SEND instruction ships a message across an N-port crossbar to another processor's service object, where firmware dispatches it as a fresh task. Multiprocessor isn't an afterthought; it's in the ISA.

What's in here

This repository contains the architecture documentation, an assembler, a simulator, a wire-level crossbar daemon, a graphical terminal device, and a validation suite.

Architecture documentation (the seven volumes)

All under docs/:

Volume File Subject
I docs/OVERVIEW.md Architectural pitch and shape
II docs/INSTRUCTION_SET.md The ISA
III docs/OBJECT_SYSTEM.md References, descriptors, capabilities
IV docs/INTERCONNECT_PROTOCOL.md Crossbar wire format
V docs/REFERENCE_IMPLEMENTATION.md OR-1000 / OR-XBAR-1 reference designs
VI docs/SYSTEM_FIRMWARE_INTERFACE.md Firmware primitive ABI
VII docs/PROGRAMMING_PRACTICE.md ABI, idioms, worked example

docs/CONTRACT.md pins what the architecture spec leaves to the integrator: the .orx binary format, the loader's initial task state, and the host-side semantics of the firmware primitives the simulator implements directly.

docs/HISTORY.md traces the project's evolution — three passes on Volume I, the Apollo correction, the toolchain's contract-first dispatch, the validation suite, multi-CPU, the wire-level crossbar, multi-process, the manual revision, and the phases of Ouroboros — the OS layer growing on top: privilege modes, trap delivery, tasks/scheduler, supervisor handlers, timer interrupts, the C-level task API, the shell becoming a real supervisor that spawns guests as child tasks on its own CPU, focus-switchable interactive guests (the editor), and program arguments + cwd passing.

Combined PDFs: docs/ObjectRISC.pdf (Computer Modern, 72 pp) and docs/ObjectRISC-Palatino.pdf (TeX Gyre Pagella, 79 pp).

Toolchain

The asm / sim / devices / launcher tools are pure Python 3.10+, standard library only — no build step. The C compiler is a vendored pcc and needs ./configure && make once (see tools/cc/arch/orisc/README.md).

Tool Purpose
tools/asm/asmorisc Assembler — .s.orx, or .s.oro with -r
tools/ld/orld Linker — .oro and .ora inputs → .orx
tools/ld/oar Archiver — bundle .oro files into .ora archives
tools/sim/simorisc Simulator — runs .orx binaries, single or multi-CPU
tools/sim/oriscbar Standalone wire-level crossbar daemon
tools/devices/oriscterm Tk-based terminal device that connects to oriscbar
tools/devices/linkbootd Python-side link-boot server
tools/devices/hostfsd Host-filesystem server (open/read/write/close, optional jail)
tools/oriscrun Launcher: spawns crossbar + devices + CPU processes
tools/cc Vendored pcc with an Object RISC backend (arch/orisc/)
tools/cc/lib C library (liborisc.ora) — console I/O, string/memory, hostfs, terminal, tasks (task.c), .orx loader (orx.c)

Validation

132 tests across fourteen categories — integer, logical, memory, control flow, object registers, object memory, firmware, traps (including privileged-instruction enforcement and supervisor-mode handler delivery), CALL (including EPERM gating), golden programs, multi-CPU (with link boot), loadable modules, receive queues, and tasks (TaskCreate / TaskWait / TaskYield / timer preemption):

python3 tools/sim/tests/validation/runner.py

Plus 12 device/shell integration tests under tools/devices/tests/ — the shell, hostfsd, oriscterm, the multi-task and concurrent-children libc demos, and shell-as-supervisor running guest .orx files end-to-end.

Quick taste

Most of the toolchain is pure Python — no build step. The C compiler is the exception: a vendored pcc that needs ./configure && make once. Bootstrap script handles it:

tools/cc/build.sh        # produces /tmp/pcc-build/cc/{cpp,ccom}/orisc-*

Then make builds liborisc, the shell, and every program in ouroboros/programs/ — and make boot drops you into Ouroboros in a Tk window:

make            # build everything (incremental)
make boot       # build + run Ouroboros
make clean      # nuke build/

After pcc is bootstrapped, examples/cc/run_c.sh and the tools/devices/tests/ scripts find the binaries automatically. Override the location with PCC_BUILD=/path/to/build if /tmp isn't where you want it.

Hello world (assembly, doesn't need pcc):

tools/asm/asmorisc tools/asm/examples/hello.s -o /tmp/hello.orx
tools/sim/simorisc /tmp/hello.orx
# Hello, world!

Two CPUs talking over the crossbar (single-process):

tools/sim/simorisc --processors 2 --trace ping-pong.orx

Hello world on a graphical terminal connected to a real wire-format crossbar in its own process:

examples/run_hello_terminal.sh

A generic link-boot loader: CPU 0 (master) ships an 8-instruction module to a CPU running a content-free linkboot.orx, which discovers the master via an announce SEND, copies the module across the wire, maps it executable, and JRs in:

examples/linkboot/run.sh
# [xbar] oriscbar READY ...
# [cpu1] Booted!

See examples/linkboot/README.md for the announce/boot protocol, the unrolled-OLW copy strategy, and the limits.

C compiled by our in-tree pcc port. Build pcc once (cd tools/cc && ./configure --target=orisc-unknown-none && make into a build dir of your choice — /tmp/pcc-build is the default), then run any .c through the pipeline:

examples/cc/run_c.sh examples/cc/hello.c     # Hello, world!
examples/cc/run_c.sh examples/cc/factab.c    # 1!..7! table
examples/cc/run_c.sh examples/cc/primes.c    # primes < 50
examples/cc/run_c.sh examples/cc/fizzbuzz.c  # 1..20 fizzbuzz
examples/cc/run_c.sh examples/cc/pascal.c    # Pascal's triangle
examples/cc/run_c.sh examples/cc/hello_or.c  # __or in C
examples/cc/run_c.sh examples/cc/print_or.c  # __or + inline asm
examples/cc/run_c.sh examples/cc/inspect.c   # OEQ/OISN/OLEN/OTAG/OHOME/OCAP
examples/cc/run_c.sh examples/cc/print_clean.c     # __or via calling convention
examples/cc/run_c.sh examples/cc/print_via_or_arg.c # callee takes __or arg
examples/cc/run_c.sh examples/cc/or_callee_inspect.c # OISN/OLEN inside __or callee
examples/cc/run_c.sh examples/cc/strings_demo.c    # exercises liborisc string fns

Three interactive C demos that use oriscterm's keyboard, mouse, and graphics capabilities — each opens a Tk window:

examples/cc/run_kbd_echo.sh    # echoes every keystroke (codepoint + mods)
examples/cc/run_paint.sh       # arrow + letter keys → vector drawing on canvas
examples/cc/run_mouse_paint.sh # mouse: click drops dot, drag draws stroke

A demo that reads an actual host file via hostfsd and prints it to the simulated console:

examples/cc/run_host_cat.sh    # cat README.md from inside an emulated CPU

Ouroboros — the OS layer

Built on top of the architecture, phases 24–30 of HISTORY record the growth of an in-tree OS layer named Ouroboros — named (after a brief stop at "OROS" = Object RISC OS) for the snake-eating-its-tail symbolism: the OS runs on the architecture whose toolchain self-hosts; traps return where they came from via ERET; and the VM/CMS analogy that motivates the design (CP = firmware, CMS = Ouroboros) is itself a recursive shape.

What's wired so far:

  • Privilege modes (Vol II §13): user / supervisor / firmware, enforced at decode for LCTRL / SCTRL / ERET / WAIT / TLB ops, and at CALL dispatch via per-primitive minimum-mode gating.
  • Trap delivery (Vol II §14.1): on a trap the CPU populates EPC / Cause / BadVAddr, saves the current mode into Status, switches to firmware, and jumps to VECBASE + offset[cause]. ERET reverses it. A supervisor-installable handler primitive (InstallTrapHandler, #0x520) bypasses the firmware-only VECBASE for guest OSes.
  • Tasks & scheduler (Vol VI §4): Task is a first-class swappable execution context with its own register file, PC, privilege state, and address-space mappings. TaskCreate / TaskExit / TaskResume / TaskSuspend / TaskYield / TaskCurrent / TaskWait are wired; cooperative round-robin scheduler picks the next runnable on every yield/exit.
  • Per-task address spaces. pcc-generated code that assumes the flat CONTRACT.md §2 layout (CODE_VA / DATA_VA / STACK_TOP) Just Works in spawned children — they each get a fresh layout pointing at their own code/data/stack objects.
  • Timer interrupt path. STATUS.IE + COMPARE raises external-interrupt (cause 0x01) when the cycle counter crosses the compare value; deliver_trap auto-clears IE so handlers don't re-fire mid-handler.
  • C libc API. tools/cc/lib/task.c exposes a multi-child task_t handle abstraction over an OREF-backed task table. tools/cc/lib/orx.c loads a .orx from disk via hostfsd, sets up code/data/stack objects, and TaskCreates it as a child task — that's how the shell's run command works under Phase 30.

The Ouroboros shell

The MVP shell is no longer just an interactive front-end — it's the supervisor. cmd_run calls orx_run which loads the .orx itself, allocates code/data/stack objects, and TaskCreates the guest as a child task on the shell's own CPU. No separate spawn-server, no spare-CPU pool, no chunked-boot trip across the crossbar — just a parent task forking a child.

Commands: help / cat / more / ls / cd / pwd / echo / run / cycles / time / exit. Command history (up/down arrow recall), backspace with visual undo, --More-- paginator on more and help, paths resolved against the shell's cwd (prompt mirrors it: /sub>). The build-date banner is shifted back 40 years (alternate-history conceit).

Launch:

make boot       # build + start Ouroboros in a Tk window

make builds the shell into build/shell.orx and every program in ouroboros/programs/ into build/programs/*.orx. make boot symlinks build/programs/ into the hostfsd jail at /programs/, so they're immediately runnable from inside the shell:

/> ls /programs
count.orx
dhry.orx
edit.orx
exit42.orx
hello.orx
hello_term.orx
/> run /programs/hello.orx
[exited 0]
/> run /programs/exit42.orx
[exited 42]
/> run /programs/edit.orx hello.c
... (full-screen editor opens; F1 to return focus, ^X to quit)

hello / count / exit42 use print_str / print_int, which go through firmware ConsoleWrite to host stdout (the terminal where you launched make boot, not the Tk oriscterm window). The [exited N] line is what the shell prints back to the Tk window via term_print after orx_run returns.

To add a new program: drop a .c file in ouroboros/programs/ and rerun make. The Makefile picks it up automatically — there's a uniform %.c → %.orx pattern rule.

The print_str-vs-term_print split is documented in ouroboros/programs/README.md.

Dhrystone v2.1 — the canonical 1984 benchmark, runs from inside the shell. Reports cycle count + dhry/s + DMIPS at the OR-1000's nominal 16/20 MHz clock rates from Vol I §3 (VAX 11/780 = 1.0 DMIPS reference):

/> run /programs/dhry.orx
Dhrystone Benchmark, Version 2.1 (Object RISC port)
Iterations: 5000
...
Cycles per iteration: 4074
16 MHz: ~3927 dhry/s   = ~2.2 DMIPS
20 MHz: ~4909 dhry/s   = ~2.7 DMIPS

The hello_or.c and print_or.c variants use the __or qualifier to control the OR file directly from C: register __or void *p __asm__("o5") binds a C variable to a named Object Register slot. Assignments between __or variables compile to omov; assigning 0 compiles to onull. Combined with extended inline asm — asm("olw %0, 0(%1)" : "=r"(out) : "r"(or_var)) — you can read or write through OR pointers, invoke firmware primitives via call #N, and otherwise treat OR slots as first-class C lvalues.

The __or calling convention is wired both ways: callers pass __or args in O1..O4 (print_clean.c), and pure-C callees that take __or parameters can use them inside the body without explicit register binding (print_via_or_arg.c, or_callee_inspect.c). OL/OS-via-OR as native pcc patterns (rather than via inline asm) and __or returns in O1 are still pending — see tools/cc/arch/orisc/README.md for the backend's status and TODO.

The demos collectively exercise: recursion, loops with conditionals, if/else-if chains, stack-allocated arrays of int and char, computed array indexing, pointer arithmetic, string literals, integer arithmetic (add/sub/mul/div/mod/and/or/xor), bitwise operations, multi-file linkage (programs link against the liborisc.ora C library for print_str / print_int / strlen / memcpy / etc.), and external calls.

Each program links against crt0.s (provides .entry _start, calls main, TaskExits with main's R2 as the exit code) and console_io.s (a small bridge to firmware ConsoleWrite — temporary until the C compiler grows the __or qualifier and OR-file patterns). Both live in tools/cc/arch/orisc/.

Parallel π(N) across K+1 CPUs, results streamed live to the terminal. The coordinator partitions [2..N] into K+1 equal ranges, dispatches work to the workers via SEND with a derived reply cap, computes its own range, and prints each worker's count and elapsed cycles as the reply arrives:

examples/run_parallel_primes.sh               # defaults: N=2000, w=3
examples/run_parallel_primes.sh -N 5000 -w 5
examples/run_parallel_primes.sh -N 10000 -w 8

# Parallel pi(10000) across 9 CPUs:
# CPU 0: 186 primes in 41270 cycles
# CPU 1: 145 primes in 59893 cycles
# CPU 2: 139 primes in 71274 cycles
# CPU 3: 133 primes in 79765 cycles
# CPU 4: 129 primes in 86472 cycles
# CPU 5: 127 primes in 93499 cycles
# CPU 6: 126 primes in 99069 cycles
# CPU 7: 122 primes in 103186 cycles
# CPU 8: 122 primes in 108465 cycles
# Total: pi(10000) = 1229

w is capped at 10 (the program's worker-dispatch jump table has ten entries — one per OR slot O6..O15 holding worker service refs at boot). At larger w the result lines visibly arrive out of order because the OS schedules each CPU process independently and they finish at different rates.

Status

Object RISC is a designed and implemented exercise in alternate- history computer architecture, not a production system. It is complete enough to design programs against, write them in assembly, and run them — single-CPU, multi-CPU, distributed across processes, and talking to a graphical device — but it does not pretend to be silicon. The reference implementation lives in Python.

License

MIT — see LICENSE.

The window manager's OPEN LOOK chrome fonts (Lucida Sans / Lucida Sans Typewriter and the OPEN LOOK glyph font, baked into ouroboros/wm_fonts.h) are third-party bitmap assets, © 1989 Sun Microsystems and © 1985, 1986 Bigelow & Holmes, used under the OpenLook CD-ROM's royalty-free license. See NOTICE for the required attributions.

About

A vibe-coded alternate history architecture

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages