More complete execution trace support

This change adds more complete and more accurate execution
tracing support.
This commit is contained in:
Seth Morabito 2021-04-23 13:43:50 -07:00
parent e8f8584e64
commit 4706ec625f
4 changed files with 179 additions and 36 deletions

58
doc/notes.org Normal file
View File

@ -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

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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) {