Files
trve/src/core.rs

199 lines
5.6 KiB
Rust

// Copyright (c) 2025 taitep
// SPDX-License-Identifier: BSD-2-Clause
//
// This file is part of TRVE (https://gitea.taitep.se/taitep/trve)
// See LICENSE file in the project root for full license text.
use std::{collections::HashSet, sync::mpsc};
use crate::{
core::commands::CoreCmd,
decode::Instruction,
exceptions::{Exception, ExceptionType, MemoryException},
gdb::{self, DebugCommand, DebugStream, StopReason},
instructions::find_and_exec,
mem::MemConfig,
};
pub struct Core {
pub(crate) x_regs: [u64; 32],
pub(crate) pc: u64,
pub(crate) mem: MemConfig,
command_stream: mpsc::Receiver<CoreCmd>,
}
pub mod commands;
impl Core {
pub fn new(mem: MemConfig, command_stream: mpsc::Receiver<CoreCmd>) -> Self {
Self {
x_regs: [0; 32],
pc: 0,
mem,
command_stream,
}
}
pub fn run(&mut self) {
loop {
if let Ok(cmd) = self.command_stream.try_recv() {
match cmd {
CoreCmd::EnterDbgMode(DebugStream(dbg_stream)) => {
let _ = self.debug_loop(dbg_stream);
}
};
}
if let Err(e) = self.step() {
self.throw_exception(e);
break;
}
}
}
pub fn run_waiting_for_cmd(&mut self) {
eprintln!("Waiting for any core command...");
if let Ok(cmd) = self.command_stream.recv() {
eprintln!("Recieved a command");
match cmd {
CoreCmd::EnterDbgMode(DebugStream(dbg_stream)) => {
let _ = self.debug_loop(dbg_stream);
}
};
} else {
eprintln!("Error recieving command, starting anyway");
}
eprintln!("Command processed");
self.run();
}
fn debug_loop(&mut self, dbg_stream: mpsc::Receiver<gdb::DebugCommand>) -> anyhow::Result<()> {
let mut breakpoints = HashSet::new();
loop {
match dbg_stream.recv()? {
DebugCommand::GetRegs(sender) => sender.send(gdb::RegsResponse {
x_regs: self.x_regs.clone(),
pc: self.pc,
})?,
DebugCommand::ReadMem {
addr,
len,
responder,
} => {
let data = (0..len)
.map(|offset| self.mem.read_byte(addr + offset))
.collect::<Result<_, MemoryException>>()
.map_err(Into::into);
responder.send(data)?;
}
DebugCommand::SetBreakpoint(addr) => {
breakpoints.insert(addr);
}
DebugCommand::RemoveBreakpoint(addr) => {
breakpoints.remove(&addr);
}
DebugCommand::Step(responder) => {
responder.send(match self.step() {
Ok(_) => gdb::StopReason::Step,
Err(e) => {
self.throw_exception(e);
gdb::StopReason::Exception(e.into())
}
})?;
}
DebugCommand::Continue(responder, stopper) => {
responder.send(self.continue_loop(&breakpoints, stopper))?;
}
DebugCommand::ExitDebugMode => {
eprintln!("exitdbgmode");
break Ok(());
}
};
}
}
fn continue_loop(
&mut self,
breakpoints: &HashSet<u64>,
stopper: oneshot::Receiver<()>,
) -> StopReason {
loop {
if breakpoints.contains(&self.pc) {
return StopReason::Exception(ExceptionType::Breakpoint);
}
if let Ok(_) = stopper.try_recv() {
return StopReason::Interrupted;
}
if let Err(e) = self.step() {
self.throw_exception(e);
return StopReason::Exception(e.into());
}
}
}
pub(crate) fn step(&mut self) -> Result<(), Exception> {
if !self.pc.is_multiple_of(4) {
self.throw_exception(Exception {
type_: ExceptionType::InstructionAddressMisaligned,
value: self.pc,
});
}
let instr = match self.mem.read_word(self.pc) {
Ok(i) => i,
Err(e) => {
return Err(e.to_exception_instr());
}
};
if instr & 3 != 3 {
// Compressed instruction - (currently) unsupported
// Could also be zero instruction, that also matches this
return Err(Exception {
type_: ExceptionType::IllegalInstruction,
value: instr as u64,
});
}
let instr = Instruction(instr);
if let Err(e) = find_and_exec(instr, self) {
dbg!(instr);
return Err(e);
}
Ok(())
}
fn throw_exception(&mut self, exception: Exception) {
eprintln!("Exception: {exception:?}");
dbg!(self.pc, self.x_regs);
dbg!(self.x_regs[10]);
}
pub fn reset(&mut self, pc: u64) {
self.pc = pc;
}
pub(crate) fn reg_read(&self, id: u8) -> u64 {
self.x_regs[id as usize]
}
pub(crate) fn reg_write(&mut self, id: u8, value: u64) {
if id == 0 {
return;
}
self.x_regs[id as usize] = value;
}
pub(crate) fn advance_pc(&mut self) {
self.pc = self.pc.wrapping_add(4);
}
}