summaryrefslogtreecommitdiff
path: root/cpu.ha
diff options
context:
space:
mode:
Diffstat (limited to 'cpu.ha')
-rw-r--r--cpu.ha130
1 files changed, 130 insertions, 0 deletions
diff --git a/cpu.ha b/cpu.ha
new file mode 100644
index 0000000..3ffea05
--- /dev/null
+++ b/cpu.ha
@@ -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);
+};
+
+
+
+