B BedRockdocs
Release 1.1
BedRock

BedRock Language Documentation

Master Technical Reference — Revision 1.0  |  Compiler Source: lexer.rs, parser.rs, ast.rs, codegen/mod.rs

Welcome to the BedRock language documentation. BedRock is a systems programming language that compiles to bare-metal MIPS-32 big-endian machine code. Every language construct maps directly and transparently to a fixed sequence of hardware instructions — no runtime, no OS abstraction, no magic.

This documentation covers the complete language specification, compiler internals, hardware interfacing primitives, and hard-won guidance on the compiler's known enforcement behaviors and edge cases.

TOC Documentation Sections

1.1 "No Magic"

In BedRock, every operation maps to a known, fixed sequence of machine instructions. There are no hidden runtime allocations, no garbage collector, no implicit type coercions, and no virtual dispatch. What you write is exactly what executes.

This is enforced by design — the table below shows the exact 1-to-1 mapping for common constructs:

BedRock ConstructMachine Code Behavior
let x = 5;Exactly one sw instruction to a pre-assigned static address. No stack frame creation, no heap allocation.
let arr = [1, 2, 3];Compiler statically assigns a base address in the DATA segment, emits three sw instructions during initialization. All accesses are compile-time pointer arithmetic.
fn foo() { ... }Exactly one prologue (addiu $sp, -32; sw $ra, 28($sp)) and one epilogue (lw $ra, 28($sp); addiu $sp, +32; jr $ra). Nothing else.
Auditability: Every BedRock construct has a 1-to-1 mapping to MIPS-32 big-endian machine code that you can audit, verify, and predict. Use a hex editor on any .bin output to confirm.

1.2 "Byte-Level Validation"

Numbers in BedRock are unsigned 64-bit integers at parse time, collapsed to 32-bit at code generation. All memory addresses are 32-bit values. There are no floating-point types, no signed arithmetic in the type system, and no implicit widening or truncation.

The compiler validates output at the byte level by:

  • Encoding all instructions as big-endian 32-bit words via to_be_bytes()
  • Writing a raw .bin file with zero padding assumptions
  • Optionally writing a 1.44 MB floppy image (bedrock_os.img) by embedding the binary at sector 0

1.3 "Direct Hardware Control"

BedRock provides five mechanisms for hardware access that map directly to bare-metal MIPS operations. There is no operating system abstraction layer.

ConstructHardware OperationMIPS Instruction
poke(addr, val)Write 32-bit word to physical addresssw $val, 0($addr)
peek(addr)Read 32-bit word from physical addresslw $dest, 0($addr)
outb(port, val)Memory-mapped I/O writesw $val, 0($port)
inb(port)Memory-mapped I/O readlw $dest, 0($port)
asm("hex")Emit raw machine instructionDirect word emission
No OS Layer: BedRock programs run on the bare metal and are responsible for their own memory layout. Incorrect addresses cause undefined hardware behavior — not a safe exception.

2.1 Keywords

The following identifiers are reserved and cannot be used as variable or function names.

KeywordCategoryDescription
fnDeclarationFunction definition
letDeclarationLocal/static variable declaration
rootDeclarationHardware/environment constant declaration
returnControl FlowReturn from function
ifControl FlowConditional branch
elseControl FlowAlternative branch
whileControl FlowConditional loop
loopControl FlowInfinite loop
breakControl FlowExit innermost loop
asmHardwareEmit raw machine instruction
outbHardwareMemory-mapped I/O write
inbHardwareMemory-mapped I/O read
pokeHardwareDirect memory write
peekHardwareDirect memory read
inHardwareWait for keyboard input (alias for WaitKey)
includePreprocessorInclude another source file (textual substitution)

2.2 Operators

Arithmetic Operators

SymbolTokenMIPS Instruction
+Plusaddu
-Minussubu
*Starmult + mflo
/Slashdiv + mflo
^Caretxor

Bitwise Operators

SymbolTokenMIPS Instruction
&Ampersandand
|Pipeor
^Caretxor

Comparison Operators

SymbolTokenMIPS Sequence
==EqEqxor + sltiu $d, $d, 1
!=NotEqxor + sltu $d, $0, $d
>Greaterslt (operands swapped)
<Lessslt
>=GreaterEqslt + xori $d, $d, 1
<=LessEqslt (swapped) + xori $d, $d, 1

Assignment

SymbolTokenDescription
=EqualAssignment (not comparison — use == for equality testing)

2.3 Literals

Integer Literals

BedRock supports decimal and hexadecimal integer literals. All integers are stored as u64 during lexing.

literals.brBedRock
123456
// Decimal let x = 255; // Hexadecimal (0x prefix, case-insensitive) let vga_base = 0xB8000; let stack_top = 0x80020000;

String Literals

Strings are delimited by double quotes, stored in the DATA segment as null-terminated byte arrays (C-style). Escape sequences are not processed — see §8.8.

let message = "Hello, BedRock!";

Array Literals

Arrays are comma-separated lists of integer literals only enclosed in [ ]. Variables and expressions are not permitted as elements — see §8.2.

let palette = [0xFF0000, 0x00FF00, 0x0000FF];

2.4 Comments & Include Directive

comments.brBedRock
1234567
// Single-line comment — from // to end of line /* Multi-line comment spans multiple lines */ include "vga.br"; include "keyboard.br";

The include directive is a preprocessor feature handled before lexing. It performs textual substitution of the named file's content into the current file. Include paths are resolved relative to the source file's directory and processed recursively.

3.1 Address Space

BedRock uses a flat 32-bit address space. The layout is determined by three root declarations:

Root SymbolDefault ValueDescription
BASE0x80000000Code segment start address
DATABASE + 0x10000Static data segment start address
STACKBASE + 0x20000Initial stack pointer value
layout.brBedRock
123
root BASE = 0x80000000; root DATA = 0x80010000; root STACK = 0x80020000;

3.2 Segment Layout

┌─────────────────────────────────┐ ← BASE (e.g. 0x80000000) │ BOOTLOADER (3 instructions) │ NOP, li $sp STACK, J main ├─────────────────────────────────┤ │ FUNCTION BODIES │ Compiled first, before main code ├─────────────────────────────────┤ │ MAIN CODE │ Global statements execute here ├─────────────────────────────────┤ │ HALT (infinite jump-to-self) │ ├─────────────────────────────────┤ │ (gap) │ ├─────────────────────────────────┤ ← DATA (e.g. 0x80010000) │ STATIC DATA │ Arrays, strings (4-byte aligned) └─────────────────────────────────┘ ← STACK (grows downward from here)

3.3 let — Static Variable

let declares a static variable. In global scope, the compiler allocates a fixed address in the DATA segment and emits sw (store word) instructions to initialize it. Variables do not occupy stack space in global scope.

1234567
// Global scope — allocated in DATA segment let counter = 0; // Address N let limit = 100; // Address N+4 // Function scope — stored on stack frame fn compute() { let result = 0; // Stack offset, lw/sw via $sp }
Rule: A let-declared variable's address is resolved entirely at compile time. There is no dynamic allocation.

3.4 root — Hardware / Environment Constant

root declares a compile-time constant mapping a name to a fixed 32-bit address or value. Unlike let, a root symbol is never dereferenced. When used in an expression, it evaluates to the raw address value itself — not the value stored at that address.

DeclarationExpression BehaviorUse Case
let x = 5Loads value 5 from memory addressVariables, counters, state
root ADDR = 0xB8000Loads the literal value 0xB8000 — no memory dereferenceHardware addresses, config constants
12
root VGA = 0xB8000; poke(VGA, 0x0741); // Writes 0x0741 to physical address 0xB8000
Restriction: The right-hand side of a root declaration must be a literal integer. Expressions are not evaluated. See §8.1.

3.5 Stack Frame Structure

Every function call creates a 32-byte stack frame:

$sp + 28 ← Saved $ra (return address) $sp + 24 ← (reserved) $sp + 20 ← (reserved) $sp + 16 ← (reserved) $sp + 12 ← (reserved) $sp + 8 ← (reserved) $sp + 4 ← (reserved) $sp + 0 ← (base of frame) $sp + 32 ← First parameter (arg 0, passed by caller) $sp + 36 ← Second parameter (arg 1) ...

Prologue (emitted automatically)

addiu $sp, $sp, -32 // Allocate 32-byte frame sw $ra, 28($sp) // Save return address

Epilogue (emitted automatically)

lw $ra, 28($sp) // Restore return address addiu $sp, $sp, 32 // Deallocate frame jr $ra // Return to caller nop // Delay slot

3.6 Register Allocation

BedRock uses a pool of 8 temporary registers ($t0$t7, MIPS registers 8–15) for all expression evaluation. Registers are allocated from the front of the pool and freed back to the rear (FIFO with free-list).

RegisterMIPS NumberRole
$sp29Stack pointer
$ra31Return address (saved by JAL)
$v02Function return value
$a0–$a34–7Function arguments (caller convention)
$t0–$t78–15Temporary expression registers (pool of 8)
Register Exhaustion: If alloc_reg() finds the pool empty, it silently returns register 8 ($t0), potentially corrupting a live value. See §8.9 for mitigation.

4.1 Variable Declarations

variables.brBedRock
12345
let x = 10; let total = x + 5; let mask = 0xFF & total; root VGA = 0xB8000; // Compile-time constant root BASE = 0x80000000;

4.2 Arrays & Strings

Array Definition & Access

Arrays are statically allocated in the DATA segment. Only integer literals are permitted as element values. The compiler scales all indices by 4 (sll idx, idx, 2) — each element is a 32-bit word.

1234567
let scores = [100, 200, 150, 75]; let vga_row = [0x0720, 0x0720, 0x0720]; let v = scores[0]; // Read element 0 let v = scores[i]; // Read at index i scores[0] = 999; // Assign to element 0 scores[i] = scores[i] + 1; // Increment element i

String Definition

Strings are stored as null-terminated byte arrays in the DATA segment, padded to the next 4-byte boundary.

let greeting = "Hello, World!"; // 14 bytes: 13 chars + '\0'

4.3 Arithmetic & Expressions

Precedence rules: * and / bind tightly (handled in parse_term). All other operators — +, -, ==, !=, >, <, &, |, ^ — share the same lower precedence level (parse_expression). Use parentheses liberally — see §8.6.

123456789
let sum = a + b; let diff = a - b; let prod = a * b; let quot = a / b; let xored = a ^ b; let anded = a & b; let ored = a | b; let eq = (a == b); // 1 if equal, 0 otherwise let flag = (x & 0xFF) == 0x41; // Always use parentheses

4.4 Function Definitions

functions.brBedRock
1234567891011121314151617
fn add(a, b) { return a + b; } fn clear_screen() { let i = 0; while (i < 2000) { poke(VGA + i * 2, 0x0720); i = i + 1; } } fn factorial(n) { if (n <= 1) { return 1; } return n * factorial(n - 1); } let result = add(10, 20);
Rules: Functions must be defined at the top level (no nested definitions). Up to 4 arguments map to $a0$a3. Return value is always in $v0 — see §8.4 and §8.5.

4.5 Control Flow

if / else

12345
if (x == 0) { poke(VGA, 0x0720); } else { poke(VGA, 0x0741); }

while

12345
let i = 0; while (i < 80) { poke(VGA + i, 0x0720); i = i + 1; }

loop / break

An infinite loop. Exits only via break or a return from the enclosing function.

123456
loop { let key = in; if (key == 0x1C) { break; } // Enter key process_key(key); } // break emits: beq $0, $0, offset (patched after loop)

4.6 Inline Assembly

Emits a single 32-bit MIPS instruction directly into the code stream. The argument must be the exact 32-bit encoding as a hex string — without the 0x prefix.

12345
asm("00000000"); // NOP asm("0000000C"); // SYSCALL asm("42000018"); // ERET (return from exception) asm("00084080"); // sll $t0, $t0, 2 (shift left by 2) asm("000840C0"); // sll $t0, $t0, 3 (shift left by 3)
No shift operators (<<, >>) exist in BedRock's grammar. Use asm for all shift operations. See §8.10.

5.1 Compiler Pipeline

The compiler operates in a single pass with eight sequential phases:

PhaseNameAction
Phase 0Root Symbol CollectionResolves BASE, DATA, STACK addresses
Phase 1Bootloader EmissionNOP, li $sp STACK, J <main_start> (patched later)
Phase 2Static Data AllocationAssigns addresses to arrays and strings in DATA segment
Phase 3Function Code GenerationEmits function bodies with prologue/epilogue
Phase 4Jump PatchBack-patches the Phase 1 J instruction to point past functions
Phase 5Static Data InitializationEmits sw instructions to write array/string data into memory
Phase 6Main Code GenerationEmits all non-root, non-function, non-static-data statements
Phase 7HaltEmits J <self> infinite loop

5.2 Load Immediate (emit_li)

All constant loading uses the following pattern based on the value's bit width:

If upper 16 bits == 0: ori $reg, $0, lower16 // Single instruction Else: lui $reg, upper16 // Load upper half ori $reg, $reg, lower16 // OR in lower half (if non-zero)
ExamplesMIPS
123456
// Loading 0x0041 → single ori ori $t0, $0, 0x0041 // Loading 0xB8000 → lui + ori lui $t0, 0x000B ori $t0, $t0, 0x8000

5.3 Binary Operation Code Generation

All binary operations follow the pattern: allocate regs → gen left → gen right → emit instruction → free regs.

Arithmetic Encodings

BedRockMIPS EncodingNotes
a + baddu $d, $s, $t0x00000021 | ...
a - bsubu $d, $s, $t0x00000023 | ...
a * bmult $s, $t + mflo $dResult from LO register
a / bdiv $s, $t + mflo $dQuotient from LO register
a ^ bxor $d, $s, $t0x00000026 | ...
a & band $d, $s, $t0x00000024 | ...
a | bor $d, $s, $t0x00000025 | ...

Comparison Encodings

BedRockMIPS SequenceResult
a == bxor $d,$s,$t + sltiu $d,$d,11 if equal
a != bxor $d,$s,$t + sltu $d,$0,$d1 if not equal
a < bslt $d,$s,$t1 if s < t
a > bslt $d,$t,$sOperands swapped
a >= bslt $d,$s,$t + xori $d,$d,1Invert lt
a <= bslt $d,$t,$s + xori $d,$d,1Invert gt

5.4 Control Flow Code Generation

if Statement

gen_expr(condition, cond_reg) beq $cond_reg, $0, <else_offset> // Branch if condition == 0 (false) nop ... then_body ... j <end> // Only if else exists ... else_body ... end:

while Loop

start: gen_expr(condition, cond_reg) beq $cond_reg, $0, <after_loop> nop ... body ... j start nop after_loop:

loop (Infinite)

start: ... body ... j start nop after_loop: ← break patches jump here

Function Call (jal)

addiu $sp, $sp, -16 sw $a0, 0($sp) // arg 0 sw $a1, 4($sp) // arg 1 jal <function_address> nop // delay slot // Return value is in $v0

5.5 Memory Operations

BedRockMIPSDescription
poke(addr, val)sw $val, 0($addr)32-bit store to physical address
peek(addr)lw $dest, 0($addr)32-bit load from physical address
outb(port, val)sw $val, 0($port)MMIO write (identical to sw)
inb(port)lw $dest, 0($port)MMIO read (identical to lw)

5.6 Binary Output Format

The compiler emits a flat binary of 32-bit big-endian MIPS instructions:

codegen/mod.rsRust
self.code.iter() .flat_map(|&instr| instr.to_be_bytes().to_vec()) .collect()
Output FileDescription
<source>.binRaw binary of the compiled program (flat MIPS words)
bedrock_os.img1,474,560-byte floppy disk image (1.44 MB), binary at offset 0, first sector = 512 bytes

6.1 Direct Memory Writes — poke

poke writes a 32-bit value to an absolute physical address. No bounds checking occurs.

vga_write.brBedRock
12345
root VGA = 0xB8000; // Write 'A' (0x41) with white-on-black attribute (0x07) to VGA cell 0 poke(VGA, 0x0741); poke(VGA + 2, 0x0720); // Clear next cell

Machine Code Generated

lui $t0, 0x000B // Load VGA high word ori $t0, $t0, 0x8000 // VGA = 0xB8000 ori $t1, $0, 0x0741 // value sw $t1, 0($t0) // poke

6.2 Direct Memory Reads — peek

peek reads a 32-bit value from an absolute physical address and returns it as an expression value (in $v0 / the allocated register).

12
let cell = peek(VGA); // Read VGA cell 0 let status = peek(0x80030000); // Read status register

6.3 I/O Port Access — outb / inb

BedRock implements I/O as MMIO. outb and inb are functionally identical to poke and peek at the machine code level — both emit sw and lw instructions respectively. The distinction is semantic only, signaling programmer intent.

12345
// Send command byte to UART port outb(0x3F8, 0x80); // UART: Set DLAB bit // Read byte from UART data register let data = inb(0x3F8);

6.4 VGA Text Mode — Practical Reference

VGA text mode buffer begins at physical address 0xB8000. Each character cell is 2 bytes.

Byte 0 (even): ASCII character code Byte 1 (odd): Attribute byte Bits [7:4] = background color Bits [3:0] = foreground color Standard 80×25 text mode = 2,000 cells × 2 bytes = 4,000 bytes total

Color Constants (Foreground)

ColorValueColorValue
Black0x0Light Gray0x7
Blue0x1Dark Gray0x8
Green0x2Light Blue0x9
Cyan0x3Light Green0xA
Red0x4Light Cyan0xB
Magenta0x5Light Red0xC
Brown0x6White0xF

Cell Address Formula

cell_address = 0xB8000 + (row * 80 + col) * 2 attribute = (background << 4) | foreground cell_value = (attribute << 8) | ascii_code

Example — Print 'H' in White on Black at (0,0)

root VGA = 0xB8000; poke(VGA, 0x0748); // 0x07=white-on-black, 0x48='H'

Example — Clear Entire Screen

12345678
root VGA = 0xB8000; fn cls() { let i = 0; while (i < 4000) { poke(VGA + i, 0x0720); i = i + 2; } }

6.5 Keyboard Input — in

The keyword in (used as an expression) reads from the keyboard input address 0x80020000 (hardcoded in the compiler). Use inb with an explicit port address to override.

12345
let key = in; // lw from 0x80020000 // Override with explicit address root KBD_DATA = 0x60; let scancode = inb(KBD_DATA);

6.6 Raw Instruction Injection — asm

Use asm for instructions not expressible in BedRock's grammar. Always document the encoding.

MIPS R-type encodingReference
Bits [31:26] = opcode (000000 for R-type) Bits [25:21] = rs Bits [20:16] = rt Bits [15:11] = rd Bits [10:6] = shamt Bits [5:0] = function code
1234
// Logical shift left by 2: sll $t0, $t0, 2 = 0x00084080 asm("00084080"); // Exception return (ERET) asm("42000018");

7.1 The Address Library Pattern

Declare all hardware addresses as root constants in a dedicated include file. This creates a zero-cost hardware abstraction layer and eliminates magic numbers from your code.

❌ Without Address Library (Avoid)

poke(0xB8000, 0x0741); // What is this address? poke(0xB8002, 0x0720); // Unclear intent outb(0x3F8, 0x80); // Magic numbers

✅ With Address Library (Preferred)

hardware.brBedRock
123456789101112
root VGA_BASE = 0xB8000; root UART_DATA = 0x3F8; root UART_IER = 0x3F9; root UART_LCR = 0x3FB; root UART_LSR = 0x3FD; root PIC_MASTER = 0x20; root PIC_SLAVE = 0xA0; root PIT_CH0 = 0x40; root PIT_CMD = 0x43; root KBD_DATA = 0x60; root KBD_STATUS = 0x64;
main.brBedRock
1234
include "hardware.br"; poke(VGA_BASE, 0x0741); // Self-documenting outb(UART_LCR, 0x80); // Intent is clear

7.2 Write Pure Functions

Functions that only operate on their parameters and local variables are predictable, testable, and cacheable. Avoid functions that modify hardware state without naming what they target.

123456789
// ❌ Less clear — what address is modified? fn update(x, y, ch) { poke(0xB8000 + (y * 160) + (x * 2), ch); } // ✅ Self-documenting — explicit VGA purpose fn vga_put_char(col, row, char_attr) { let offset = row * 160 + col * 2; poke(VGA + offset, char_attr); }

7.3 Use Loops for Bulk Hardware Operations

12345678910
// ❌ Unrolled — does not scale poke(VGA, 0x0720); poke(VGA + 2, 0x0720); // ... 2000 times // ✅ Loop — correct fn cls() { let i = 0; while (i < 4000) { poke(VGA + i, 0x0720); i = i + 2; } }

7.4 Always Set BASE, DATA, STACK Explicitly

Never rely on compiler defaults in production code. Explicit memory layout declarations make your program's address space auditable and portable across MIPS simulator configurations.
root BASE = 0x80000000; // Code starts here root DATA = 0x80010000; // Static data here root STACK = 0x80020000; // Stack grows down from here

7.5 Use asm Sparingly and Document It

Inline assembly breaks the readability contract of BedRock. When you must use it, always document the instruction's human-readable name and effect.

// ❌ Undocumented — unacceptable asm("00084080"); // ✅ Documented — acceptable // Shift left by 2 (multiply by 4) — sll $t0, $t0, 2 asm("00084080");
Global state tip: let variables at global scope are static and persist for the program lifetime. Prefer passing values through function parameters and return values to keep program state explicit and auditable.
Critical Reading: Several of these edge cases cause silent data corruption or undefined behavior — not compile errors. Study this section carefully before writing any non-trivial BedRock program.

8.1 root Requires Literal Number Values

The right-hand side of a root declaration must be a literal integer. Expressions are not evaluated.

root A = 0x80000000; // ✅ Valid root B = A + 0x10000; // ❌ Not supported — expression not evaluated

8.2 Array Literals Allow Only Integer Literals

Array literal values must be compile-time integer constants. Variables and expressions are not permitted — the parser will panic.

let arr = [1, 2, 3]; // ✅ Valid let arr = [0xFF, 0x0720]; // ✅ Valid — hex literals permitted let arr = [x, y, z]; // ❌ Parser panics — expects Token::Number

8.3 Unresolved Variable Panics at Codegen

Referencing an undeclared variable causes a panic! in the codegen phase. There is no graceful error recovery — the compiler process terminates.

let x = y + 1; // ❌ Panics if 'y' was never declared // .expect("Variable not defined!") — source: codegen/mod.rs

8.4 Function Call Arguments Are Positional Only

BedRock has no named parameters at call sites. Arguments are matched by position. The number of arguments passed is not validated by the parser.

fn add(a, b) { return a + b; } add(1, 2); // ✅ Correct add(1); // ⚠ Compiles, but 'b' loads garbage from stack add(1, 2, 3); // ⚠ Compiles, third arg is silently ignored

8.5 Return Value Is Always $v0

There is only one return register ($v0 = register 2). Multiple return values are not supported. Calling two functions in a complex expression may overwrite $v0 before it is consumed.

// ⚠ Unsafe — second call may overwrite first $v0 before use let result = foo() + bar(); // ✅ Safe — capture each return value before next call let a = foo(); let b = bar(); let result = a + b;

8.6 No Operator Precedence for Bitwise / Comparison

All of +, -, ==, !=, >, <, &, |, ^ share the same precedence level. Only * and / bind more tightly.

let flag = (x & 0xFF) == 0x41; // ✅ Parentheses make intent clear let flag = x & 0xFF == 0x41; // ⚠ Evaluates left-to-right, not as intended

8.7 ! Without = Is a Lexer Error

The lexer only recognizes !=. A lone ! produces Token::EOF and terminates lexing silently — not an error message.

if (!flag) { ... } // ❌ Lexer error — '!' alone terminates lexing if (flag == 0) { ... } // ✅ Correct equivalent form

8.8 String Literals Do Not Support Escape Sequences

The lexer reads string content character-by-character until the closing ". Backslash sequences are stored literally as two characters.

let msg = "Hello\nWorld"; // Stored as: H e l l o \ n W o r l d \0 // '\n' is TWO characters (0x5C 0x6E), NOT a newline (0x0A)

8.9 Maximum 8 Concurrent Temporary Registers

The register pool contains 8 registers ($t0$t7). If the pool is exhausted, alloc_reg() silently returns register 8 ($t0), corrupting a live value without any warning.

123456789101112
// ⚠ Potentially unsafe — may exhaust register pool let v = ((((a + b) * c) - d) / e) + ((f & g) | h); // ✅ Safe — break into intermediate let bindings let step1 = a + b; let step2 = step1 * c; let step3 = step2 - d; let step4 = step3 / e; let step5 = f & g; let step6 = step5 | h; let v = step4 + step6;

8.10 No Shift Operators (<< / >>)

These operators are absent from the lexer and parser. Use asm for all bit-shift operations.

// Logical shift left by 3: sll $t0, $t0, 3 // Encoding: bits[31:26]=000000 rs=00000 rt=01000 rd=01000 shamt=00011 funct=000000 // = 0x000840C0 asm("000840C0");

8.11 Integer Overflow Is Silent

All arithmetic uses unsigned MIPS addu/subu. Overflow wraps silently at 2³² − 1. There are no overflow traps or checks at compile time or runtime.

Silent Wrap: 0xFFFFFFFF + 1 produces 0x00000000 with no warning, no exception, and no way to detect it after the fact.

8.12 Division by Zero Is Undefined

BedRock emits a raw MIPS div instruction. On MIPS, division by zero produces an unpredictable value in LO and does not raise an exception unless the hardware is explicitly configured to do so. There is no compile-time or runtime guard.

let result = x / 0; // Compiles. Runtime behavior is undefined.
Always validate divisors before dividing. Use an if guard or assert non-zero before any / expression where the denominator could be variable.