diff options
Diffstat (limited to 'cpu.ha')
-rw-r--r-- | cpu.ha | 130 |
1 files changed, 130 insertions, 0 deletions
@@ -0,0 +1,130 @@ +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 = { + const cpu = cpu_new(); + const fp = match (os::open("sys0.r1b")) { + case let i: io::file => yield i; + case let e: fs::error => fmt::fatalf("MISSING BOOT SECTOR: {}", + fs::strerror(e)); + }; + + match (io::readall(fp, cpu.mem[0x0200..0x0300])) { + case let e: io::error => fmt::fatalf("BOOT SECTOR READ ERROR: {}", io::strerror(e)); + case => yield; + }; + + mainloop(&cpu); +}; + + + + |