use endian; use io; use os; use fs; use fmt; type stack_overflow = !void; type stack_underflow = !void; type cpu = struct { sp: u8, rp: u8, pc: u16, mem: []u8, halted: bool, }; fn cpu_new() cpu = cpu { sp = 0, rp = 0, pc = 0x0200, mem = alloc([0...], 65536): []u8, halted = false, }; fn rmem(mem: []u8, addr: u16) u16 = endian::begetu16(mem[addr..addr+2]); fn wmem(mem: []u8, addr: u16, w: u16) void = endian::beputu16(mem[addr..addr+2], w); // stacks grow up, for a change fn push(cpu: *cpu, val: u16) void = { wmem(cpu.mem, 0 + cpu.sp, val); cpu.sp += 2; }; fn pop(cpu: *cpu) u16 = { cpu.sp -= 2; return rmem(cpu.mem, 0 + cpu.sp); }; fn peek(cpu: *cpu, n: u16 = 0) u16 = rmem(cpu.mem, 0 + cpu.sp - 2 + n); fn rpush(cpu: *cpu, val: u16) void = { wmem(cpu.mem, 0x100 + cpu.rp:u16, val); cpu.rp += 2; }; fn rpop(cpu: *cpu) u16 = { cpu.rp -= 2; return rmem(cpu.mem, 0x100 + cpu.rp:u16); }; def MASK_CALL: u16 = 0b1000000000000000; def MASK_LIT : u16 = 0b0110000000000000; def MASK_JMP : u16 = 0b0100000000000000; fn maskeq(x: u16, m: u16) bool = // still must be called in the right order (x&m) == m; fn do_word(cpu: *cpu, word: u16) void = { if (maskeq(word, MASK_CALL)) { const addr = word & 0b0111111111111111; const addr = addr << 1; do_call(cpu, addr); } else if (maskeq(word, MASK_LIT)) { const lval = word & 0b0001111111111111; push(cpu, lval); } else if (maskeq(word, MASK_JMP)) { const is_cond = (word & 0b0001000000000000) != 0; const rel: u16 = word & 0b0000111111111111; const rel: i16 = rel:i16 - 2048i16; const target: i16 = rel + cpu.pc:i16 - 2i16; const target: u16 = target:u16; if (is_cond) do_0branch(cpu, target) else do_jmp(cpu, target); } else { const opc1: u8 = ((word & 0b0011111110000000) >> 7):u8; const opc2: u8 = (word & 0b0000000001111111):u8; do_opc(cpu, opc1); do_opc(cpu, opc2); }; }; fn do_call(cpu: *cpu, addr: u16) void = { rpush(cpu, cpu.pc); cpu.pc = addr; }; fn do_jmp(cpu: *cpu, addr: u16) void = { cpu.pc = addr; }; fn do_0branch(cpu: *cpu, addr: u16) void = { const val = pop(cpu); if (val == 0) { cpu.pc = addr; }; }; fn mainloop(cpu: *cpu) void = { for (! cpu.halted) { const word = rmem(cpu.mem, cpu.pc); cpu.pc += 2; do_word(cpu, word); }; }; export fn main() void = { if (len(os::args) != 2) fmt::fatalf("usage: {} FILE.r1b", os::args[0]); const fname = os::args[1]; const cpu = cpu_new(); const fp = match (os::open(fname)) { case let i: io::file => yield i; case let e: fs::error => fmt::fatalf("couldn't open boot sector {}", fs::strerror(e)); }; match (io::readall(fp, cpu.mem[0x0200..0x0300])) { case let e: io::error => match (e) { case io::underread => yield; case let e: io::error => fmt::fatalf("couldn't read boot sector {}", io::strerror(e)); }; case => yield; }; mainloop(&cpu); };