More complete execution trace support
This change adds more complete and more accurate execution tracing support.
This commit is contained in:
parent
e8f8584e64
commit
4706ec625f
|
@ -0,0 +1,58 @@
|
|||
#+TITLE: DMD 5620 Core Notes
|
||||
#+AUTHOR: Seth Morabito
|
||||
#+EMAIL: web@loomcom.com
|
||||
#+DATE: <2021-04-23 Fri 12:33>
|
||||
#+STARTUP: showall inlineimages
|
||||
#+OPTIONS: toc:nil num:nil
|
||||
|
||||
* About
|
||||
|
||||
This document contains miscellaneous development notes taken while working
|
||||
on devleopment.
|
||||
|
||||
* Features
|
||||
|
||||
** CPU Idle Loop Detection
|
||||
|
||||
There are several interesting blocks that look like idle
|
||||
loops. Ideally, we want to detect these loops and just idle the CPU
|
||||
until the next interrupt is likely to occur.
|
||||
|
||||
*** Idle Loop Example 1
|
||||
|
||||
This tight loop occurs early on before startup is complete, and only exits
|
||||
via interrupt which sets some value in ~*$0x71a79c~.
|
||||
|
||||
#+BEGIN_EXAMPLE
|
||||
[0000678d] MOVH *$0x71a79c,%r0
|
||||
[00006795] CMPW %r1,%r0
|
||||
[00006798] BLB
|
||||
#+END_EXAMPLE
|
||||
|
||||
*** Idle Loop Example 2
|
||||
|
||||
This occurs later on after startup is complete. It continuously calls
|
||||
the function at ~0x715c~.
|
||||
|
||||
#+BEGIN_EXAMPLE
|
||||
[0000068c] BITW %r0,$0x16
|
||||
[0000068f] BEB
|
||||
[00000659] TSTB 0x6293186
|
||||
[0000065f] BNEB
|
||||
[00000661] TSTW 64(%r9)
|
||||
[00000664] BNEB
|
||||
[0000715c] CALL (%r12),0x29020
|
||||
[0000715c] SAVE %r9
|
||||
[0000715e] MOVW $0x71c590,%r1
|
||||
[00007161] ADDW3 $0x71c590,0x1096,%r0
|
||||
[00007169] TSTH (%r0)
|
||||
[0000716b] BLEB
|
||||
[00007170] MOVW *$0x719024,%r0
|
||||
[00007177] MOVW (%r0),%r0
|
||||
[0000717a] TSTH 52(%r0)
|
||||
[0000717d] BLEB
|
||||
[00007182] MOVW %r1,%r0
|
||||
[00007185] RESTORE %r9
|
||||
[0000068c] RET
|
||||
|
||||
#+END_EXAMPLE
|
13
src/bus.rs
13
src/bus.rs
|
@ -5,8 +5,8 @@ use crate::err::BusError;
|
|||
use crate::mem::Mem;
|
||||
use crate::mouse::Mouse;
|
||||
use std::io::Write;
|
||||
use std::{fmt, fs::File, ops::Range};
|
||||
use std::{fmt::Debug, fs::OpenOptions};
|
||||
use std::{fs::File, ops::Range};
|
||||
|
||||
const NVRAM_SIZE: usize = 8192;
|
||||
|
||||
|
@ -108,8 +108,8 @@ impl Bus {
|
|||
}
|
||||
|
||||
pub fn trace_on(&mut self, name: &str) -> std::io::Result<()> {
|
||||
let mut out = OpenOptions::new().write(true).open(name)?;
|
||||
write!(out, "TRACE START")?;
|
||||
let mut out = OpenOptions::new().create(true).write(true).open(name)?;
|
||||
writeln!(out, "TRACE START")?;
|
||||
self.trace_log = Some(out);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -122,12 +122,9 @@ impl Bus {
|
|||
self.trace_log = None;
|
||||
}
|
||||
|
||||
pub fn trace<T>(&mut self, step: u64, object: &T)
|
||||
where
|
||||
T: fmt::Display,
|
||||
{
|
||||
pub fn trace(&mut self, step: u64, line: &str) {
|
||||
if let Some(trace_log) = &mut self.trace_log {
|
||||
let _ = writeln!(trace_log, "{:08}: {}", step, object);
|
||||
let _ = writeln!(trace_log, "{:08}: {}", step, line);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
136
src/cpu.rs
136
src/cpu.rs
|
@ -48,6 +48,14 @@ const IPL_TABLE: [u32; 64] = [
|
|||
const WE32100_VERSION: u32 = 0x1a;
|
||||
const HALFWORD_MNEMONIC_COUNT: usize = 11;
|
||||
|
||||
macro_rules! trace {
|
||||
($bus:ident, $steps:expr, $msg:expr) => {
|
||||
if ($bus.trace_enabled()) {
|
||||
$bus.trace($steps, $msg)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub enum ExceptionType {
|
||||
ExternalMemory,
|
||||
}
|
||||
|
@ -152,6 +160,15 @@ impl Operand {
|
|||
None => self.data_type,
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.size = 0;
|
||||
self.mode = AddrMode::None;
|
||||
self.data_type = Data::None;
|
||||
self.expanded_type = None;
|
||||
self.register = None;
|
||||
self.embedded = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
|
@ -173,16 +190,59 @@ pub struct Instruction {
|
|||
|
||||
impl fmt::Display for Operand {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.data_type {
|
||||
Data::None => Ok(()),
|
||||
Data::Byte | Data::SByte => {
|
||||
write!(f, "0x{:02x}", self.data as u8)
|
||||
match self.mode {
|
||||
AddrMode::None => Ok(()),
|
||||
AddrMode::Absolute => {
|
||||
write!(f, "0x{}", self.data)
|
||||
}
|
||||
Data::Half | Data::UHalf => {
|
||||
write!(f, "0x{:04x}", self.data as u16)
|
||||
AddrMode::AbsoluteDeferred => write!(f, "*$0x{:x}", self.data),
|
||||
AddrMode::ByteDisplacement => {
|
||||
write!(f, "{}(%r{})", self.data as i8, self.register.unwrap())
|
||||
}
|
||||
Data::Word | Data::UWord => {
|
||||
write!(f, "0x{:08x}", self.data)
|
||||
AddrMode::ByteDisplacementDeferred => {
|
||||
write!(f, "*{}(%r{})", self.data as i8, self.register.unwrap())
|
||||
}
|
||||
AddrMode::HalfwordDisplacement => {
|
||||
write!(f, "0x{:x}(%r{})", self.data as i16, self.register.unwrap())
|
||||
}
|
||||
AddrMode::HalfwordDisplacementDeferred => {
|
||||
write!(f, "*0x{:x}(%r{})", self.data as i16, self.register.unwrap())
|
||||
}
|
||||
AddrMode::WordDisplacement => {
|
||||
write!(f, "0x{:x}(%r{})", self.data, self.register.unwrap())
|
||||
}
|
||||
AddrMode::WordDisplacementDeferred => {
|
||||
write!(f, "*0x{:x}(%r{})", self.data, self.register.unwrap())
|
||||
}
|
||||
AddrMode::ApShortOffset => {
|
||||
write!(f, "{}(%ap)", self.data as i8)
|
||||
}
|
||||
AddrMode::FpShortOffset => {
|
||||
write!(f, "{}(%fp)", self.data as i8)
|
||||
}
|
||||
AddrMode::ByteImmediate => {
|
||||
write!(f, "&{}", self.data as u8)
|
||||
}
|
||||
AddrMode::HalfwordImmediate => {
|
||||
write!(f, "&0x{:x}", self.data as u16)
|
||||
}
|
||||
AddrMode::WordImmediate => {
|
||||
write!(f, "&0x{:x}", self.data)
|
||||
}
|
||||
AddrMode::PositiveLiteral => {
|
||||
write!(f, "$0x{:x}", self.data)
|
||||
}
|
||||
AddrMode::NegativeLiteral => {
|
||||
write!(f, "-$0x{:x}", self.data)
|
||||
}
|
||||
AddrMode::Register => {
|
||||
write!(f, "%r{}", self.register.unwrap())
|
||||
}
|
||||
AddrMode::RegisterDeferred => {
|
||||
write!(f, "(%r{})", self.register.unwrap())
|
||||
}
|
||||
AddrMode::Expanded => {
|
||||
write!(f, "{{???}}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -190,11 +250,23 @@ impl fmt::Display for Operand {
|
|||
|
||||
impl fmt::Display for Instruction {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{:5} {}{}{}{}",
|
||||
self.name, self.operands[0], self.operands[1], self.operands[2], self.operands[3]
|
||||
)
|
||||
let _ = write!(f, "{:8} ", self.name);
|
||||
|
||||
for index in 0..=3 {
|
||||
let op = &self.operands[index];
|
||||
|
||||
if op.mode == AddrMode::None {
|
||||
break;
|
||||
}
|
||||
|
||||
if index == 0 {
|
||||
let _ = write!(f, "{}", op);
|
||||
} else {
|
||||
let _ = write!(f, ",{}", op);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -948,6 +1020,11 @@ impl Cpu {
|
|||
if let Some(val) = bus.get_interrupts() {
|
||||
let cpu_ipl = (self.r[R_PSW]) >> 13 & 0xf;
|
||||
if cpu_ipl < IPL_TABLE[(val & 0x3f) as usize] {
|
||||
trace!(
|
||||
bus,
|
||||
self.steps,
|
||||
&format!("[{:08x}] INTERRUPT 0x{:x}", &self.r[R_PC], (!val) & 0x3f)
|
||||
);
|
||||
self.on_interrupt(bus, (!val) & 0x3f);
|
||||
}
|
||||
}
|
||||
|
@ -955,8 +1032,6 @@ impl Cpu {
|
|||
self.decode_instruction(bus)?;
|
||||
let mut pc_increment: i32 = i32::from(self.ir.bytes);
|
||||
|
||||
bus.trace(self.steps, &self.ir);
|
||||
|
||||
match self.ir.opcode {
|
||||
NOP => {
|
||||
pc_increment = 1;
|
||||
|
@ -1905,18 +1980,24 @@ impl Cpu {
|
|||
pub fn step(&mut self, bus: &mut Bus) {
|
||||
// TODO: On CPU Exception or Bus Error, handle each error with the appropriate exception handler routine
|
||||
match self.dispatch(bus) {
|
||||
Ok(i) => self.r[R_PC] = (self.r[R_PC] as i32 + i) as u32,
|
||||
Err(CpuError::Bus(BusError::Alignment)) => {}
|
||||
Err(CpuError::Bus(BusError::Permission)) => {}
|
||||
Ok(i) => {
|
||||
// We should have the necessary information to trace after dispatch.
|
||||
trace!(bus, self.steps, &format!("[{:08x}] {}", &self.r[R_PC], &self.ir));
|
||||
self.r[R_PC] = (self.r[R_PC] as i32 + i) as u32
|
||||
}
|
||||
Err(CpuError::Bus(BusError::NoDevice(_)))
|
||||
| Err(CpuError::Bus(BusError::Read(_)))
|
||||
| Err(CpuError::Bus(BusError::Write(_))) => {
|
||||
self.on_exception(bus, &ExceptionType::ExternalMemory).unwrap();
|
||||
}
|
||||
Err(CpuError::Exception(CpuException::IllegalOpcode)) => {}
|
||||
Err(CpuError::Exception(CpuException::InvalidDescriptor)) => {}
|
||||
Err(CpuError::Exception(CpuException::PrivilegedOpcode)) => {}
|
||||
Err(_) => {}
|
||||
// Err(CpuError::Bus(BusError::Alignment)) => {}
|
||||
// Err(CpuError::Bus(BusError::Permission)) => {}
|
||||
// Err(CpuError::Exception(CpuException::IllegalOpcode)) => {}
|
||||
// Err(CpuError::Exception(CpuException::InvalidDescriptor)) => {}
|
||||
// Err(CpuError::Exception(CpuException::PrivilegedOpcode)) => {}
|
||||
Err(e) => {
|
||||
panic!("Unexpected CPU Error: {}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2393,12 +2474,13 @@ impl Cpu {
|
|||
|
||||
for (index, ot) in mn.ops.iter().enumerate() {
|
||||
if *ot == OpType::None {
|
||||
break;
|
||||
self.ir.operands[index].clear();
|
||||
} else {
|
||||
// Push a decoded operand
|
||||
self.decode_operand(bus, index, mn, *ot, etype, addr)?;
|
||||
etype = self.ir.operands[index].expanded_type;
|
||||
addr += self.ir.operands[index].size as usize;
|
||||
}
|
||||
// Push a decoded operand
|
||||
self.decode_operand(bus, index, mn, *ot, etype, addr)?;
|
||||
etype = self.ir.operands[index].expanded_type;
|
||||
addr += self.ir.operands[index].size as usize;
|
||||
}
|
||||
|
||||
let total_bytes = addr - initial_addr;
|
||||
|
|
|
@ -175,8 +175,14 @@ fn dmd_step() -> c_int {
|
|||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Uses a raw pointer.
|
||||
///
|
||||
#[no_mangle]
|
||||
fn dmd_trace_on(file_name: &CStr) -> c_int {
|
||||
pub unsafe fn dmd_trace_on(file_name: *const c_char) -> c_int {
|
||||
let file_name = CStr::from_ptr(file_name);
|
||||
|
||||
match DMD.lock() {
|
||||
Ok(mut dmd) => match file_name.to_str() {
|
||||
Ok(file_name) => match dmd.trace_on(file_name) {
|
||||
|
|
Loading…
Reference in New Issue