summaryrefslogtreecommitdiff
path: root/cpu.ha
blob: 7c237146da373a19e860ecf330d9b0792692a53f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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);
};