// 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, } pub mod commands; impl Core { pub fn new(mem: MemConfig, command_stream: mpsc::Receiver) -> 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) -> 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::>() .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, 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); } }