]> Witch of Git - jade-mouse/blob - toolchain/src/cpu.rs
Implement instruction execution
[jade-mouse] / toolchain / src / cpu.rs
1 use crate::inst::{Addr, Cond, Half, Inst, LdSt, Op1, Op2, OpC, Reg, Size};
2 use std::ops::{Index, IndexMut};
3
4 #[cfg(test)]
5 mod test;
6
7 struct RegFile {
8 values: [u16; 4],
9 }
10
11 pub struct Cpu {
12 init: bool,
13 pc: u16,
14 reg: RegFile,
15 memory: Vec<u8>,
16 }
17
18 impl Cpu {
19 pub fn new() -> Self {
20 Cpu {
21 init: false,
22 pc: 0,
23 reg: RegFile { values: [0; 4] },
24 memory: vec![0; 1 << 16],
25 }
26 }
27
28 pub fn reg(&self, reg: Reg) -> u16 {
29 self.reg[reg]
30 }
31
32 pub fn mem(&self) -> &[u8] {
33 &self.memory[..]
34 }
35
36 pub fn mem_mut(&mut self) -> &mut [u8] {
37 &mut self.memory[..]
38 }
39
40 pub fn run_inst(&mut self, inst: &Inst) {
41 match *inst {
42 Inst::Halt | Inst::Nope => (),
43 Inst::Alu2(rd, op, r2) => {
44 self.reg[rd] = match op {
45 Op2::Add => self.reg[rd].wrapping_add(self.reg[r2]),
46 Op2::Sub => self.reg[rd].wrapping_sub(self.reg[r2]),
47 Op2::AddPc => self.pc.wrapping_add(self.reg[r2]),
48 Op2::And => self.reg[rd] & self.reg[r2],
49 Op2::Or => self.reg[rd] | self.reg[r2],
50 Op2::Xor => self.reg[rd] ^ self.reg[r2],
51 }
52 }
53 Inst::Jalr(r) => std::mem::swap(&mut self.pc, &mut self.reg[r]),
54 Inst::Move(rd, r2) => self.reg[rd] = self.reg[r2],
55 Inst::Alu1(rd, op) => {
56 self.reg[rd] = match op {
57 Op1::Inc => self.reg[rd].wrapping_add(1),
58 Op1::Dec => self.reg[rd].wrapping_sub(1),
59 Op1::Neg => 0u16.wrapping_sub(self.reg[rd]), // negate :)
60 Op1::Compl => !self.reg[rd],
61 }
62 }
63 Inst::Mem(LdSt::Ld, r, addr) => {
64 if let Addr::Extended {
65 base,
66 stack: true,
67 size,
68 ..
69 } = addr
70 {
71 self.reg[base] = self.reg[base].wrapping_sub(size.bytes());
72 }
73 let (addr0, size) = self.eval_addr(addr);
74 self.reg[r] = self.load(addr0, size);
75 }
76 Inst::Mem(LdSt::St, r, addr) => {
77 let (addr0, size) = self.eval_addr(addr);
78 self.store(addr0, size, self.reg[r]);
79 if let Addr::Extended {
80 base,
81 stack: true,
82 size,
83 ..
84 } = addr
85 {
86 self.reg[base] = self.reg[base].wrapping_add(size.bytes());
87 }
88 }
89 Inst::Branch(cond, offset) => {
90 if self.eval_cond(cond) {
91 self.pc = self.pc.offset(offset);
92 }
93 }
94 Inst::JumpI(offset) => self.pc = self.pc.offset(offset),
95 Inst::AddI(rd, i) => self.reg[rd] = self.reg[rd].offset(i),
96 Inst::AluCompact(rd, op, i) => {
97 self.reg[rd] = match op {
98 OpC::Lsl => self.reg[rd] << i.get(),
99 OpC::Lsr => self.reg[rd] >> i.get(),
100 OpC::Asr => (self.reg[rd] as i16 >> i.get()) as u16,
101 OpC::Rol => self.reg[rd].rotate_left(i.get() as u32),
102 OpC::Clr => self.reg[rd] & !(1 << i.get()),
103 OpC::Set => self.reg[rd] | (1 << i.get()),
104 OpC::Tog => self.reg[rd] ^ (1 << i.get()),
105 OpC::Ext => self.reg[rd] & (1 << i.get()),
106 }
107 }
108 Inst::LdImm(Half::Low, rd, i) => self.reg[rd] = self.reg[rd] & 0xFF00 | i as u16,
109 Inst::LdImm(Half::High, rd, i) => {
110 self.reg[rd] = self.reg[rd] & 0x00FF | (i as u16) << 8
111 }
112 }
113 }
114
115 fn eval_addr(&self, addr: Addr) -> (u16, Size) {
116 match addr {
117 Addr::Fixed(i) => (i.get() * 2, Size::Word),
118 Addr::Reg(r) => (self.reg[r], Size::Word),
119 Addr::Extended {
120 base, size, offset, ..
121 } => (self.reg[base].offset(offset), size),
122 }
123 }
124
125 fn load(&self, addr: u16, size: Size) -> u16 {
126 match size {
127 Size::Byte => self.memory[addr as usize] as u16,
128 Size::Word => u16::from_le_bytes([
129 self.memory[addr as usize],
130 self.memory[addr.wrapping_add(1) as usize],
131 ]),
132 }
133 }
134
135 fn store(&mut self, addr: u16, size: Size, value: u16) {
136 match size {
137 Size::Byte => self.memory[addr as usize] = value as u8,
138 Size::Word => {
139 let [b0, b1] = value.to_le_bytes();
140 self.memory[addr as usize] = b0;
141 self.memory[addr.wrapping_add(1) as usize] = b1;
142 }
143 }
144 }
145
146 fn eval_cond(&self, cond: Cond) -> bool {
147 match cond {
148 Cond::Eql(a, b) => self.reg[a] == self.reg[b],
149 Cond::Neq(a, b) => self.reg[a] != self.reg[b],
150 Cond::Test(a, b) => self.reg[a] & self.reg[b] != 0,
151 Cond::TestNot(a, b) => self.reg[a] & self.reg[b] == 0,
152 Cond::Lt(a, b) => (self.reg[a] as i16) < self.reg[b] as i16,
153 Cond::Ult(a, b) => self.reg[a] < self.reg[b],
154 }
155 }
156 }
157
158 impl Index<Reg> for RegFile {
159 type Output = u16;
160 fn index(&self, index: Reg) -> &Self::Output {
161 match index {
162 Reg::R0 => &self.values[0],
163 Reg::R1 => &self.values[1],
164 Reg::R2 => &self.values[2],
165 Reg::R3 => &self.values[3],
166 }
167 }
168 }
169
170 impl IndexMut<Reg> for RegFile {
171 fn index_mut(&mut self, index: Reg) -> &mut Self::Output {
172 match index {
173 Reg::R0 => &mut self.values[0],
174 Reg::R1 => &mut self.values[1],
175 Reg::R2 => &mut self.values[2],
176 Reg::R3 => &mut self.values[3],
177 }
178 }
179 }
180
181 impl std::fmt::Debug for RegFile {
182 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
183 fmt.debug_struct("RegFile")
184 .field("r0", &self[Reg::R0])
185 .field("r1", &self[Reg::R1])
186 .field("r2", &self[Reg::R2])
187 .field("r3", &self[Reg::R3])
188 .finish()
189 }
190 }
191
192 impl std::fmt::Debug for Cpu {
193 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
194 fmt.debug_struct("Cpu")
195 .field("init", &self.init)
196 .field("pc", &self.pc)
197 .field("reg", &self.reg)
198 .field("mem", &format_args!("[...]"))
199 .finish()
200 }
201 }
202
203 trait OffsetExt {
204 fn offset(&self, x: impl Into<i32>) -> Self;
205 }
206
207 impl OffsetExt for u16 {
208 fn offset(&self, x: impl Into<i32>) -> Self {
209 self.wrapping_add(x.into() as u16)
210 }
211 }