You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
341 lines
13 KiB
341 lines
13 KiB
use std::io; |
|
|
|
use tracing::{debug, info, trace}; |
|
|
|
use crate::{ |
|
isa::{CmpFlags, InstVariant, Instruction, InstructionData, RegisterFile, State}, |
|
memory::{Memory, MemoryMap}, |
|
}; |
|
|
|
#[derive(Default)] |
|
pub struct CPU { |
|
pub mmap: MemoryMap, |
|
pub reg: RegisterFile, |
|
|
|
pub step_wait: bool, |
|
} |
|
|
|
#[derive(Debug)] |
|
pub enum CPUError { |
|
InvalidInstruction, |
|
Unimplemented, |
|
Halt, |
|
} |
|
|
|
fn wait() { |
|
io::stdin() |
|
.read_line(&mut String::new()) |
|
.expect("couldn't read stdin"); |
|
} |
|
|
|
impl CPU { |
|
pub fn run_until_halt(&mut self) -> Result<(), CPUError> { |
|
loop { |
|
match self.step() { |
|
Ok(_) => continue, |
|
Err(CPUError::Halt) => { |
|
info!("halting"); |
|
return Ok(()); |
|
} |
|
Err(e) => return Err(e), |
|
} |
|
} |
|
} |
|
|
|
pub fn call_peripherals_pre(&mut self) { |
|
self.mmap.mems.iter_mut().for_each(|m| m.inner.pre_step()) |
|
} |
|
|
|
pub fn call_peripherals_post(&mut self) { |
|
self.mmap.mems.iter_mut().for_each(|m| m.inner.post_step()) |
|
} |
|
|
|
pub fn step(&mut self) -> Result<(), CPUError> { |
|
use InstVariant::*; |
|
use InstructionData::*; |
|
|
|
debug!("calling peripherals pre-step"); |
|
self.call_peripherals_pre(); |
|
|
|
let current_ia = self.reg.ia; |
|
let current_inst: Instruction = self.read_ia().try_into()?; |
|
|
|
debug!("running instruction at {:#010x}", current_ia); |
|
trace!("register dump:\n{}", self.reg); |
|
|
|
if self.step_wait { |
|
info!("will run {}", current_inst); |
|
info!("waiting..."); |
|
wait(); |
|
} |
|
|
|
if self.reg.st.contains(State::CMP_EN) |
|
&& (self.reg.st & current_inst.cmp.into()) == State(0) |
|
{ |
|
debug!("optional instruction skipped!"); |
|
if current_inst.has_upcoming_value() { |
|
self.read_ia(); |
|
} |
|
return Ok(()); |
|
} |
|
|
|
match current_inst.var { |
|
Nop => (), |
|
Halt => return Err(CPUError::Halt), |
|
Add(id) => { |
|
let (t, a, b) = match id { |
|
TwoRegisters(a, b) => (a, a, self.reg[b]), |
|
ThreeRegisters(t, a, b) => (t, a, self.reg[b]), |
|
RegisterAndU8(r, v) => (r, r, v as u32), |
|
RegisterAndU32(r, _) => (r, r, self.read_ia()), |
|
TwoRegistersAndU8(t, a, b) => (t, a, b as u32), |
|
TwoRegistersAndU32(t, a, _) => (t, a, self.read_ia()), |
|
_ => return Err(CPUError::InvalidInstruction), |
|
}; |
|
self.reg[t] = if current_inst.sign { |
|
self.reg[a] - b |
|
} else { |
|
self.reg[a] + b |
|
}; |
|
} |
|
And(id) => { |
|
let (t, a, b) = match id { |
|
TwoRegisters(a, b) => (a, a, self.reg[b]), |
|
ThreeRegisters(t, a, b) => (t, a, self.reg[b]), |
|
RegisterAndU8(r, v) => (r, r, v as u32), |
|
RegisterAndU32(r, _) => (r, r, self.read_ia()), |
|
TwoRegistersAndU8(t, a, b) => (t, a, b as u32), |
|
TwoRegistersAndU32(t, a, _) => (t, a, self.read_ia()), |
|
_ => return Err(CPUError::InvalidInstruction), |
|
}; |
|
self.reg[t] = self.reg[a] & b; |
|
} |
|
Or(id) => { |
|
let (t, a, b) = match id { |
|
TwoRegisters(a, b) => (a, a, self.reg[b]), |
|
ThreeRegisters(t, a, b) => (t, a, self.reg[b]), |
|
RegisterAndU8(r, v) => (r, r, v as u32), |
|
RegisterAndU32(r, _) => (r, r, self.read_ia()), |
|
TwoRegistersAndU8(t, a, b) => (t, a, b as u32), |
|
TwoRegistersAndU32(t, a, _) => (t, a, self.read_ia()), |
|
_ => return Err(CPUError::InvalidInstruction), |
|
}; |
|
self.reg[t] = self.reg[a] | b; |
|
} |
|
Xor(id) => { |
|
let (t, a, b) = match id { |
|
TwoRegisters(a, b) => (a, a, self.reg[b]), |
|
ThreeRegisters(t, a, b) => (t, a, self.reg[b]), |
|
RegisterAndU8(r, v) => (r, r, v as u32), |
|
RegisterAndU32(r, _) => (r, r, self.read_ia()), |
|
TwoRegistersAndU8(t, a, b) => (t, a, b as u32), |
|
TwoRegistersAndU32(t, a, _) => (t, a, self.read_ia()), |
|
_ => return Err(CPUError::InvalidInstruction), |
|
}; |
|
self.reg[t] = self.reg[a] ^ b; |
|
} |
|
Not(id) => { |
|
let (t, v) = match id { |
|
OneRegister(r) => (r, self.reg[r]), |
|
TwoRegisters(t, v) => (t, self.reg[v]), |
|
RegisterAndU8(t, v) => (t, v as u32), |
|
RegisterAndU32(t, _) => (t, self.read_ia()), |
|
_ => return Err(CPUError::InvalidInstruction), |
|
}; |
|
self.reg[t] = !v; |
|
} |
|
Mul(id) => { |
|
let (t, a, b) = match id { |
|
TwoRegisters(a, b) => (a, a, self.reg[b]), |
|
ThreeRegisters(t, a, b) => (t, a, self.reg[b]), |
|
RegisterAndU8(r, v) => (r, r, v as u32), |
|
RegisterAndU32(r, _) => (r, r, self.read_ia()), |
|
TwoRegistersAndU8(t, a, b) => (t, a, b as u32), |
|
TwoRegistersAndU32(t, a, _) => (t, a, self.read_ia()), |
|
_ => return Err(CPUError::InvalidInstruction), |
|
}; |
|
self.reg[t] = self.reg[a] * b; |
|
} |
|
Cmp(id) => { |
|
let (a, b) = match id { |
|
OneRegister(a) => (a, 0), |
|
TwoRegisters(a, b) => (a, self.reg[b]), |
|
RegisterAndU8(a, b) => (a, b as u32), |
|
RegisterAndU32(a, _) => (a, self.read_ia()), |
|
_ => return Err(CPUError::InvalidInstruction), |
|
}; |
|
self.reg.st.set_cmp(CmpFlags::new(self.reg[a], b)); |
|
} |
|
Set => { |
|
self.reg.st.set(State::CMP_EN, current_inst.sign); |
|
} |
|
Rst => self.reg.st.set_cmp(CmpFlags { |
|
gt: true, |
|
eq: true, |
|
lt: true, |
|
}), |
|
Lsl(id) => { |
|
let (t, a, b) = match id { |
|
TwoRegisters(a, b) => (a, a, self.reg[b]), |
|
ThreeRegisters(t, a, b) => (t, a, self.reg[b]), |
|
RegisterAndU8(r, v) => (r, r, v as u32), |
|
TwoRegistersAndU8(t, a, b) => (t, a, b as u32), |
|
_ => return Err(CPUError::InvalidInstruction), |
|
}; |
|
self.reg[t] = self.reg[a] << b; |
|
} |
|
Lsr(id) => { |
|
let (t, a, b) = match id { |
|
TwoRegisters(a, b) => (a, a, self.reg[b]), |
|
ThreeRegisters(t, a, b) => (t, a, self.reg[b]), |
|
RegisterAndU8(r, v) => (r, r, v as u32), |
|
TwoRegistersAndU8(t, a, b) => (t, a, b as u32), |
|
_ => return Err(CPUError::InvalidInstruction), |
|
}; |
|
self.reg[t] = self.reg[a] >> b; |
|
} |
|
Asr(id) => { |
|
let (t, a, b) = match id { |
|
TwoRegisters(a, b) => (a, a, self.reg[b]), |
|
ThreeRegisters(t, a, b) => (t, a, self.reg[b]), |
|
RegisterAndU8(r, v) => (r, r, v as u32), |
|
TwoRegistersAndU8(t, a, b) => (t, a, b as u32), |
|
_ => return Err(CPUError::InvalidInstruction), |
|
}; |
|
// rust does asr for signed ints, and lsr for unsigned |
|
self.reg[t] = ((self.reg[a] as i32) >> b) as u32; |
|
} |
|
Ror(id) => { |
|
let (t, a, b) = match id { |
|
TwoRegisters(a, b) => (a, a, self.reg[b]), |
|
ThreeRegisters(t, a, b) => (t, a, self.reg[b]), |
|
RegisterAndU8(r, v) => (r, r, v as u32), |
|
TwoRegistersAndU8(t, a, b) => (t, a, b as u32), |
|
_ => return Err(CPUError::InvalidInstruction), |
|
}; |
|
self.reg[t] = self.reg[a].rotate_right(b); |
|
} |
|
Ld(id) => { |
|
let (a, r) = match id { |
|
TwoRegisters(r, a) => (self.reg[a], r), |
|
RegisterAndU32(r, _) => (self.read_ia(), r), |
|
_ => return Err(CPUError::InvalidInstruction), |
|
}; |
|
self.reg[r] = self.mmap.read_u32(a); |
|
} |
|
Sto(id) => { |
|
let (v, a) = match id { |
|
TwoRegisters(v, a) => (self.reg[v], self.reg[a]), |
|
RegisterAndU32(v, _) => (self.reg[v], self.read_ia()), |
|
_ => return Err(CPUError::InvalidInstruction), |
|
}; |
|
self.mmap.write_u32(a, v); |
|
} |
|
Push(id) => { |
|
match id { |
|
OneRegister(a) => vec![self.reg[a]], |
|
TwoRegisters(a, b) => vec![self.reg[a], self.reg[b]], |
|
ThreeRegisters(a, b, c) => vec![self.reg[a], self.reg[b], self.reg[c]], |
|
RegisterAndU8(a, b) => vec![self.reg[a], b as u32], |
|
RegisterAndU32(a, _) => vec![self.reg[a], self.read_ia()], |
|
TwoRegistersAndU8(a, b, c) => vec![self.reg[a], self.reg[b], c as u32], |
|
TwoRegistersAndU32(a, b, _) => vec![self.reg[a], self.reg[b], self.read_ia()], |
|
ImmediateOnly(_) => vec![self.read_ia()], |
|
} |
|
.into_iter() |
|
.for_each(|val| self.push_stack(val)); |
|
} |
|
Pop(id) => { |
|
match id { |
|
OneRegister(a) => vec![a], |
|
TwoRegisters(a, b) => vec![a, b], |
|
ThreeRegisters(a, b, c) => vec![a, b, c], |
|
_ => return Err(CPUError::InvalidInstruction), |
|
} |
|
.into_iter() |
|
.for_each(|r| self.reg[r] = self.pop_stack()); |
|
} |
|
Mov(id) => { |
|
let (r, v) = match id { |
|
TwoRegisters(a, b) => (a, self.reg[b]), |
|
RegisterAndU32(a, _) => (a, self.read_ia()), |
|
RegisterAndU8(a, v) => (a, v as u32), |
|
_ => return Err(CPUError::InvalidInstruction), |
|
}; |
|
self.reg[r] = v; |
|
} |
|
Call(id) => { |
|
let call_addr = match id { |
|
OneRegister(r) => self.reg[r], |
|
ImmediateOnly(_) => self.read_ia(), |
|
_ => return Err(CPUError::InvalidInstruction), |
|
}; |
|
self.push_cs(); |
|
self.reg.ra = self.reg.ia; |
|
self.reg.ia = call_addr; |
|
} |
|
Ret => { |
|
self.reg.ia = self.reg.ra; |
|
self.pop_cs(); |
|
} |
|
Jmp(id) => { |
|
let jump_offset = match id { |
|
OneRegister(r) => self.reg[r], |
|
ImmediateOnly(_) => self.read_ia(), |
|
RegisterAndU8(_, v) | TwoRegistersAndU8(_, _, v) => (v as u32) * 4, |
|
_ => return Err(CPUError::InvalidInstruction), |
|
}; |
|
self.reg.ia = if current_inst.sign { |
|
current_ia - jump_offset |
|
} else { |
|
current_ia + jump_offset |
|
}; |
|
} |
|
JmpAbs(id) => { |
|
let jump_addr = match id { |
|
OneRegister(r) => self.reg[r], |
|
ImmediateOnly(_) => self.read_ia(), |
|
_ => return Err(CPUError::InvalidInstruction), |
|
}; |
|
self.reg.ia = jump_addr; |
|
} |
|
}; |
|
|
|
debug!("calling peripherals post-step"); |
|
self.call_peripherals_post(); |
|
|
|
Ok(()) |
|
} |
|
|
|
fn read_ia(&mut self) -> u32 { |
|
let v = self.mmap.read_u32(self.reg.ia); |
|
self.reg.ia += 4; |
|
trace!("read_ia: {:#010x}", v); |
|
v |
|
} |
|
|
|
fn push_stack(&mut self, v: u32) { |
|
self.reg.sa -= 4; |
|
debug!("pushed {:#010x} to {:#010x}", v, self.reg.sa); |
|
self.mmap.write_u32(self.reg.sa, v); |
|
} |
|
|
|
fn pop_stack(&mut self) -> u32 { |
|
let v = self.mmap.read_u32(self.reg.sa); |
|
debug!("popped {:#010x} from {:#010x}", v, self.reg.sa); |
|
self.reg.sa += 4; |
|
v |
|
} |
|
|
|
fn push_cs(&mut self) { |
|
self.reg.cs -= 4; |
|
self.mmap.write_u32(self.reg.cs, self.reg.ra); |
|
debug!("pushed {:#010x} onto callstack", self.reg.ra); |
|
} |
|
|
|
fn pop_cs(&mut self) { |
|
self.reg.ra = self.mmap.read_u32(self.reg.cs); |
|
self.reg.cs += 4; |
|
debug!("popped {:#010x} from callstack", self.reg.ra); |
|
} |
|
}
|
|
|