Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an option to produce a textfile trace of instructions executed #39

Merged
merged 3 commits into from
Dec 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/cpu/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
use crate::trace::TraceHandler;

pub mod mos6502;

pub trait Cpu {
/// Reset this CPU, clearing internal state.
fn reset(&mut self);

/// Attach the given handler to receive trace events from this CPU.
fn attach_trace_handler(&mut self, trace: Box<dyn TraceHandler>);

/// Return the number of cycles elapsed since the system last reset.
fn get_cycle_count(&self) -> u64;

/// Execute a single instruction. Return the number of cycles elapsed.
fn tick(&mut self) -> u8;

/// Clean up any resources used by this CPU.
fn cleanup(&mut self) -> Result<(), &str>;
}
23 changes: 23 additions & 0 deletions src/cpu/mos6502/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
mod fetch;
mod registers;
use crate::memory::{ActiveInterrupt, Memory};
use crate::trace::{CpuTrace, TraceHandler};
use execute::Execute;
use fetch::Fetch;
use registers::{flags, Registers};
Expand All @@ -25,6 +26,7 @@
cycle_count: u64,
cycles_since_poll: u64,
variant: Mos6502Variant,
trace: Option<Box<dyn TraceHandler>>,
}

/// Read and write from the system's memory.
Expand Down Expand Up @@ -144,6 +146,7 @@
cycle_count: 0,
cycles_since_poll: 0,
variant,
trace: None,
}
}
}
Expand All @@ -161,9 +164,21 @@
self.cycle_count
}

fn attach_trace_handler(&mut self, trace: Box<dyn TraceHandler>) {
self.trace = Some(trace);
}

Check warning on line 169 in src/cpu/mos6502/mod.rs

View check run for this annotation

Codecov / codecov/patch

src/cpu/mos6502/mod.rs#L167-L169

Added lines #L167 - L169 were not covered by tests

/// Execute a single instruction.
fn tick(&mut self) -> u8 {
let opcode = self.fetch();

if let Some(tracer) = &mut self.trace {
tracer.handle(&CpuTrace {
address: self.registers.pc.address(),
opcode,
});

Check warning on line 179 in src/cpu/mos6502/mod.rs

View check run for this annotation

Codecov / codecov/patch

src/cpu/mos6502/mod.rs#L176-L179

Added lines #L176 - L179 were not covered by tests
}

match self.execute(opcode) {
Ok(cycles) => {
self.cycle_count += cycles as u64;
Expand Down Expand Up @@ -192,4 +207,12 @@
}
}
}

fn cleanup(&mut self) -> Result<(), &str> {
if let Some(tracer) = &mut self.trace {
tracer.flush()

Check warning on line 213 in src/cpu/mos6502/mod.rs

View check run for this annotation

Codecov / codecov/patch

src/cpu/mos6502/mod.rs#L211-L213

Added lines #L211 - L213 were not covered by tests
} else {
Ok(())

Check warning on line 215 in src/cpu/mos6502/mod.rs

View check run for this annotation

Codecov / codecov/patch

src/cpu/mos6502/mod.rs#L215

Added line #L215 was not covered by tests
}
}

Check warning on line 217 in src/cpu/mos6502/mod.rs

View check run for this annotation

Codecov / codecov/patch

src/cpu/mos6502/mod.rs#L217

Added line #L217 was not covered by tests
}
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#![doc = include_str!("../README.md")]
#![allow(clippy::new_without_default)]

/// The [`cpu::Mos6502`] represents a 6502 processor and associated memory.

Check warning on line 4 in src/lib.rs

View workflow job for this annotation

GitHub Actions / WASM Build, Docs Build, and Web Deploy

unresolved link to `cpu::Mos6502`
pub mod cpu;

/// A [`memory::Memory`] implementation can be read from and written to, but it can also be polled for interrupts. This is used for the PIA, VIA, and other chips that interface over memory but also trigger interrupts. The [`memory`] module provides implementations for various types of memory and other memory-mapped devices. Mappings are handled using [`memory::BranchMemory`].
Expand All @@ -28,9 +28,12 @@
/// ROM file loading and unloading is different on different platforms: desktop platforms typically load ROMs from a file, while WebAssembly platforms need to load ROMs from a `Uint8Array`. ROM file definition and loading is handled in the [`roms`] module, with specific [`roms::DiskLoadable`] and `roms::JsValueLoadable` traits for these two cases. Loaded ROMs are represented with a [`roms::RomFile`] object, which can be passed to [`memory::BlockMemory::from_file`].
pub mod roms;

/// Systems are created by a [`systems::SystemBuilder`]. A system is created with some roms, configuration, and platform. For instance, the `build` implementation on [`systems::pet::PetSystemBuilder`] takes in [`systems::pet::PetSystemRoms`], [`systems::pet::PetSystemConfig`], and an `Arc<dyn PlatformProvider>`.

Check warning on line 31 in src/lib.rs

View workflow job for this annotation

GitHub Actions / WASM Build, Docs Build, and Web Deploy

unresolved link to `systems::SystemBuilder`

Check warning on line 31 in src/lib.rs

View workflow job for this annotation

GitHub Actions / WASM Build, Docs Build, and Web Deploy

unresolved link to `systems::pet::PetSystemBuilder`
pub mod systems;

/// Traces log the state of the system as it runs (e.g., to a file). This is useful for debugging.
pub mod trace;

mod time;

#[cfg(target_arch = "wasm32")]
Expand Down
13 changes: 11 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,16 @@

#[clap(short, long, value_parser, default_value = "symbolic")]
key_mapping: KeyMappingArg,

#[clap(short, long, value_parser, default_value = "false")]
trace: bool,

Check warning on line 56 in src/main.rs

View check run for this annotation

Codecov / codecov/patch

src/main.rs#L56

Added line #L56 was not covered by tests
}

#[cfg(not(target_arch = "wasm32"))]
fn main() {
use libnoentiendo::{cpu::mos6502::Mos6502Variant, systems::klaus::KlausSystemConfig};
use libnoentiendo::{
cpu::mos6502::Mos6502Variant, systems::klaus::KlausSystemConfig, trace::file::FileTraceHandler,
};

Check warning on line 63 in src/main.rs

View check run for this annotation

Codecov / codecov/patch

src/main.rs#L61-L63

Added lines #L61 - L63 were not covered by tests

let args = Args::parse();

Expand All @@ -74,7 +79,7 @@
KeyMappingArg::Physical => KeyMappingStrategy::Physical,
};

let system = match args.system {
let mut system = match args.system {

Check warning on line 82 in src/main.rs

View check run for this annotation

Codecov / codecov/patch

src/main.rs#L82

Added line #L82 was not covered by tests
SystemArg::Basic => BasicSystem::build(romfile.unwrap(), (), platform.provider()),
SystemArg::Easy => Easy6502System::build(romfile.unwrap(), (), platform.provider()),
SystemArg::Klaus => KlausSystem::build(
Expand Down Expand Up @@ -105,5 +110,9 @@
),
};

if args.trace {
system.attach_trace_handler(Box::new(FileTraceHandler::new("./cpu.trace".to_owned())));
}

Check warning on line 115 in src/main.rs

View check run for this annotation

Codecov / codecov/patch

src/main.rs#L113-L115

Added lines #L113 - L115 were not covered by tests

platform.run(system);
}
7 changes: 6 additions & 1 deletion src/systems/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::{
cpu::Cpu,
platform::{PlatformProvider, WindowConfig},
trace::TraceHandler,
};
use instant::Duration;
use std::sync::Arc;
Expand All @@ -27,6 +28,10 @@
/// Return a mutable reference to the CPU used in this system.
fn get_cpu_mut(&mut self) -> Box<&mut dyn Cpu>;

fn attach_trace_handler(&mut self, handler: Box<dyn TraceHandler>) {
self.get_cpu_mut().attach_trace_handler(handler);
}

Check warning on line 33 in src/systems/mod.rs

View check run for this annotation

Codecov / codecov/patch

src/systems/mod.rs#L31-L33

Added lines #L31 - L33 were not covered by tests

/// Advance the system by one tick.
fn tick(&mut self) -> Duration;

Expand All @@ -38,6 +43,6 @@

/// Clean up any resources used by this system.
fn cleanup(&mut self) -> Result<(), &str> {
Ok(())
self.get_cpu_mut().cleanup()

Check warning on line 46 in src/systems/mod.rs

View check run for this annotation

Codecov / codecov/patch

src/systems/mod.rs#L46

Added line #L46 was not covered by tests
}
}
30 changes: 30 additions & 0 deletions src/trace/file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use crate::trace::{CpuTrace, TraceHandler};
use std::{
fs::File,
io::{BufWriter, Write},
};

pub struct FileTraceHandler {
file: BufWriter<File>,
}

impl FileTraceHandler {
pub fn new(filename: String) -> Self {
Self {
file: BufWriter::new(File::create(filename).expect("Invalid filename")),
}
}

Check warning on line 16 in src/trace/file.rs

View check run for this annotation

Codecov / codecov/patch

src/trace/file.rs#L12-L16

Added lines #L12 - L16 were not covered by tests
}

impl TraceHandler for FileTraceHandler {
fn handle(&mut self, trace: &CpuTrace) {
self
.file
.write_all(format!("{:04X}: {:02X}\n", trace.address, trace.opcode).as_bytes())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could be good to make this buffered with BufWriter, i'm happy to implement that

.unwrap();
}

Check warning on line 25 in src/trace/file.rs

View check run for this annotation

Codecov / codecov/patch

src/trace/file.rs#L20-L25

Added lines #L20 - L25 were not covered by tests

fn flush(&mut self) -> Result<(), &str> {
self.file.flush().map_err(|_| "failed to flush file")
}

Check warning on line 29 in src/trace/file.rs

View check run for this annotation

Codecov / codecov/patch

src/trace/file.rs#L27-L29

Added lines #L27 - L29 were not covered by tests
}
19 changes: 19 additions & 0 deletions src/trace/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#[cfg(not(target_arch = "wasm32"))]
pub mod file;

/// Trace information provided after each instruction by the CPU.
pub struct CpuTrace {
pub address: u16,
pub opcode: u8,
}

/// An item which can handle a CPU trace (e.g. logging to a file)
pub trait TraceHandler {
/// Handle a trace event.
fn handle(&mut self, trace: &CpuTrace);

/// Flush any existing resource buffers.
fn flush(&mut self) -> Result<(), &str> {
Ok(())
}

Check warning on line 18 in src/trace/mod.rs

View check run for this annotation

Codecov / codecov/patch

src/trace/mod.rs#L16-L18

Added lines #L16 - L18 were not covered by tests
}
Loading