Skip to content

DosWorld/oc86

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

69 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

oberonc — Self-Hosted Oberon-07 Compiler for MS-DOS

A complete, self-hosting compiler toolchain written entirely in Oberon-07, targeting 16-bit MS-DOS real-mode on the 8086/8087.


What is this?

oberonc is a self-hosting Oberon-07 compiler that runs on MS-DOS (and on Linux/macOS via the xt emulator). It compiles itself — the entire toolchain is written in Oberon-07 and built from a single checked-in seed binary.

The compiler produces RDOFF2 object files, packs them into .om archive libraries, and links them into standard DOS MZ executables. All tools run natively on real MS-DOS hardware and under xt on modern systems.


Features

Language

  • Full Oberon-07 (Wirth 2007) with practical DOS extensions
  • LONGINT (32-bit signed), WORD (16-bit unsigned), REAL/LONGREAL (x87 FPU)
  • SET type (16-bit), CASE with ranges, multi-branch WHILE/ELSIF
  • POINTER TO RECORD with single inheritance, runtime IS type tests, NEW/DISPOSE
  • Implicit pointer dereference: p.f == p^.f, p[i] == p^[i]
  • Nested procedures, FORWARD declarations, procedural variables (FAR/NEAR)
  • INLINE procedures (raw bytes at call site), EXTERNAL (link to assembly)
  • Typeless VAR parameters (pass any variable by far address)
  • SYSTEM intrinsics: ADR, SEG, OFS, PTR, MOVE, FILL, LSL/LSR/ASR/ROR, AND/IOR/XOR, PORTOUT/PORTIN, Intr
  • Implicit INTEGERREAL/LONGREAL promotion in binary expressions
  • Constant folding for CONST, ARRAY dimensions, and CASE labels

Codegen & ABI

  • 8086 real-mode, large memory model (CS ≠ DS ≠ SS at all times)
  • Pascal calling convention (callee cleans), FAR/NEAR per-procedure
  • RDOFF2 object format with smart linking — only reachable modules emitted
  • Per-module code segments (~64 KB limit enforced), one combined data segment
  • Runtime array bounds checking ($R+ directive)
  • Dead-code and unused-symbol warnings (non-fatal, stderr)

Toolchain

  • All-in-one oc.exe: dep-scan + incremental compile + smart link — one command builds everything
  • Self-hosted: the compiler is written in Oberon and compiles itself; byte-stable fixpoint verified
  • 247-row manifest test suite + Section 17/18 unit tests, all run under xt
  • $M directive for stack and heap sizing; $L directive to embed assembly .rdf files

Quick start

Requirements: GNU make, xt DOS emulator (runs boot/oc.exe on Linux/macOS)

# Build everything from source
make

# Run the 247-row manifest test suite (fast, host-side)
make test

# Full DOS-side test suite including unit tests (~9 min under xt)
make testall

Compile and run a program:

# Write Hello.Mod
cat > Hello.Mod << 'EOF'
MODULE Hello;
IMPORT Out;
PROCEDURE Run*;
BEGIN Out.Open; Out.String("Hello, world!"); Out.Ln END Run;
END Hello.
EOF

# Compile + link in one step (output auto-named Hello.exe)
xt run --max=50000000 -c . bin/oc.exe -entry Run Hello.Mod

# Run it
xt run --max=5000000 -c . Hello.exe

On real MS-DOS:

oc.exe -entry Run Hello.Mod
Hello.exe

How it works

One command builds everything

bin/oc.exe is all-in-one: it scans transitive dependencies, recompiles only stale modules, and links the final executable — no separate linker or build-script generator needed.

# Compile-only (library mode)
xt run --max=50000000 -c . bin/oc.exe MyLib.Mod

# Compile + link to executable (auto-named MyProg.exe)
xt run --max=50000000 -c . bin/oc.exe -entry Run MyProg.Mod

Self-hosting

The compiler (src/oc/*.Mod), archive manager (src/olib/), and tools (src/tools/) are all written in Oberon-07. The build sequence:

boot/oc.exe  (checked-in seed binary, never modified by make)
  └─ src/lib/*.mod   →  bin/OBERON.OM   (standard library)
  └─ src/oc/*.Mod    →  bin/oc.exe      (compiler + linker)
  └─ src/olib/*.Mod  →  bin/olib.exe    (archive manager)

bin/oc.exe
  └─ src/tools/rdfgrep.Mod  →  bin/rdfgrep.exe
  └─ tests/testall.Mod      →  bin/testall.exe

Byte-stable fixpoint: boot/oc.exe → compile sources → oc_gen1.exe → compile same sources → oc_gen2.exe, and oc_gen1.exe == oc_gen2.exe byte-for-byte. This is verified by tests/test_selfhost.sh.


Directory layout

oberonc/
  boot/           Bootstrap MS-DOS binaries — immutable seeds
    oc.exe          All-in-one compiler/linker/dep-scan
    olib.exe        Archive manager
    OBERON.OM       Standard library archive
  src/
    oc/             Compiler + linker source (Oberon-07)
    olib/           Archive manager source (Oberon-07)
    lib/            Standard library source (Oberon-07 + SYS.ASM)
    tools/          rdfgrep.Mod, testall.Mod
  bin/            Built binaries: oc.exe, olib.exe, rdfgrep.exe, testall.exe, OBERON.OM
  tests/
    MANIFEST.TSV    247-row integration test manifest
    T*.Mod          Section 18 unit tests (Oberon)
    testall.Mod     DOS-side test driver
    Makefile.man    Host-side manifest runner
  docs/           Reference documentation
  MANUAL.MD       Programmer manual (language, ABI, library API)
  CLAUDE.md       Developer guide for Claude Code sessions

Standard library

All 13 modules are merged into bin/OBERON.OM after make.

Module Purpose
SYSTEM Runtime: alloc, halt, FPU detect, INT calls, bitwise ops
Out stdout: char, string, integer, hex, address, newline
Out87 REAL/LONGREAL formatted output (x87 FPU required)
In stdin: char, integer, long integer, token
Files Buffered file I/O: open, read, write, seek, close
Strings Length, Equal, Copy, Append, Pos, Insert, Delete, Extract
IO Raw rider-based I/O to any DOS file handle (stdout/stderr/file)
Mem Raw heap: Alloc(size) / Free(p) with 65 KB guard
Dos DOS interface: args, Exec, FindFirst, date/time, interrupts
Crt Terminal: cursor, clear, colour (INT 10h)
Math FPU math: sin, cos, sqrt, exp, ln, abs, …
Test Unit test framework: AddTest, RunTests, Assert*
RdfLoad Runtime RDOFF2 loader (dynamic code loading)

Language at a glance

MODULE Example;
IMPORT Out, Strings;

TYPE
  PNode = POINTER TO Node;
  Node  = RECORD val: INTEGER; next: PNode END;

VAR head*: PNode;

PROCEDURE Push*(val: INTEGER);
  VAR n: PNode;
BEGIN
  NEW(n); n^.val := val; n^.next := head; head := n
END Push;

PROCEDURE Pop*(): INTEGER;
  VAR v: INTEGER;
BEGIN
  v := head^.val; head := head^.next;
  RETURN v
END Pop;

PROCEDURE Run*;
  VAR i: INTEGER;
BEGIN
  Out.Open;
  FOR i := 1 TO 5 DO Push(i) END;
  WHILE head # NIL DO
    Out.Int(Pop(), 0); Out.Char(" ")
  END;
  Out.Ln
END Run;

END Example.
xt run --max=50000000 -c . bin/oc.exe -entry Run Example.Mod
xt run --max=5000000 -c . Example.exe
# → 5 4 3 2 1

Test suite

make test     # 247 manifest tests — compile-output checks (bytes, RDOFF structure, errors)
make testall  # full DOS-side: manifest + Section 17 executable + Section 18 unit tests
Suite Rows What it covers
MANIFEST.TSV 247 Codegen bytes, RDOFF structure, error messages, exit codes
Section 17 15+ Executable tests: types, control flow, procedures, records, pointers, SYSTEM
Section 18 9 Unit tests for compiler modules: Scan, Syms, Cgen, Rdoff, Tar, Def, Parser, TADDR, TFBUF

Documentation

File Contents
MANUAL.MD Language reference, ABI, library APIs, porting guide
docs/Porting.md Detailed porting guide for converting code from C/Pascal
docs/oberon07.ebnf Authoritative grammar for this dialect
docs/oberon-language.md Type sizes, calling convention, codegen patterns, SYSTEM intrinsics
docs/implementation-rules.md DOS/portability rules, .def file format, emit conventions
docs/build.md Detailed build instructions, oc.exe flags, linker internals
docs/tests.md Test suite reference: how to run, add, and interpret tests
docs/xt.md xt emulator reference: options, trace format, debugging
docs/rdoff2-spec.md RDOFF2 object file format specification

License

Public domain. See LICENSE.TXT.

About

Public domain Oberon-07 compiler for DOS , 8086/8087 16-bit real mode, large memory model.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors