gui to step through cpu instructions
This commit is contained in:
37
src/cpu.rs
37
src/cpu.rs
@@ -7,12 +7,12 @@ use crate::types::*;
|
||||
use log::{debug};
|
||||
|
||||
pub struct Registers {
|
||||
a: Byte,
|
||||
x: Byte,
|
||||
y: Byte,
|
||||
sp: Byte,
|
||||
pc: Addr,
|
||||
flags: Byte
|
||||
pub a: Byte,
|
||||
pub x: Byte,
|
||||
pub y: Byte,
|
||||
pub sp: Byte,
|
||||
pub pc: Addr,
|
||||
pub flags: Byte
|
||||
}
|
||||
|
||||
impl Registers {
|
||||
@@ -37,17 +37,17 @@ impl Debug for Registers {
|
||||
}
|
||||
|
||||
|
||||
const CARRY: Byte = 1 << 0;
|
||||
const ZERO: Byte = 1 << 1;
|
||||
const IRQ: Byte = 1 << 2;
|
||||
const DECIMAL: Byte = 1 << 3;
|
||||
const BREAK: Byte = 1 << 4;
|
||||
const UNUSED: Byte = 1 << 5;
|
||||
const OVERFLOW: Byte = 1 << 6;
|
||||
const NEGATIVE: Byte = 1 << 7;
|
||||
pub const CARRY: Byte = 1 << 0;
|
||||
pub const ZERO: Byte = 1 << 1;
|
||||
pub const IRQ: Byte = 1 << 2;
|
||||
pub const DECIMAL: Byte = 1 << 3;
|
||||
pub const BREAK: Byte = 1 << 4;
|
||||
pub const UNUSED: Byte = 1 << 5;
|
||||
pub const OVERFLOW: Byte = 1 << 6;
|
||||
pub const NEGATIVE: Byte = 1 << 7;
|
||||
|
||||
pub struct CPU {
|
||||
regs: Registers,
|
||||
pub regs: Registers,
|
||||
curr_op: Byte, // current operation
|
||||
cycles: u8, // number of clock clycles the CPU is ahead of global clock
|
||||
}
|
||||
@@ -86,6 +86,11 @@ impl CPU {
|
||||
self.curr_op = 0x00;
|
||||
}
|
||||
|
||||
// True if the operation is not finished yet
|
||||
pub fn is_ahead(&self) -> bool {
|
||||
return self.cycles > 0;
|
||||
}
|
||||
|
||||
fn readb<T: Bus>(&self, bus: &T, addr: Addr) -> Byte {
|
||||
bus.readb(addr)
|
||||
}
|
||||
@@ -122,7 +127,7 @@ impl CPU {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_flag(&mut self, flag: Byte) -> Byte {
|
||||
pub fn get_flag(&self, flag: Byte) -> Byte {
|
||||
if self.regs.flags & flag > 0 {
|
||||
1
|
||||
} else {
|
||||
|
||||
156
src/main.rs
156
src/main.rs
@@ -2,18 +2,27 @@
|
||||
#[macro_use] extern crate failure;
|
||||
extern crate simple_logger;
|
||||
extern crate phf;
|
||||
extern crate piston_window;
|
||||
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
mod cpu;
|
||||
mod bus;
|
||||
mod types;
|
||||
|
||||
use std::path::Path;
|
||||
use piston_window::*;
|
||||
use types::*;
|
||||
use cpu::CPU;
|
||||
use cpu::*;
|
||||
use bus::{MemoryBus, Bus};
|
||||
use opengl_graphics::OpenGL;
|
||||
use log::Level;
|
||||
use piston_window::Text;
|
||||
|
||||
fn main() {
|
||||
simple_logger::init().unwrap();
|
||||
simple_logger::init_with_level(Level::Debug).unwrap();
|
||||
let mut window: PistonWindow = WindowSettings::new("NESemu", [256*6, 240*4])
|
||||
.exit_on_esc(true).graphics_api(OpenGL::V3_2).build().unwrap();
|
||||
|
||||
let mut bus = MemoryBus::new();
|
||||
|
||||
@@ -30,12 +39,145 @@ fn main() {
|
||||
bus.writeb(0xfffc, offset as Byte);
|
||||
bus.writeb(0xfffc + 1, (offset >> 8) as Byte);
|
||||
|
||||
// Create and reset CPU
|
||||
let mut cpu: CPU = CPU::new();
|
||||
cpu.reset(&bus);
|
||||
for _ in 0..500+10 {
|
||||
cpu.clock(&mut bus);
|
||||
debug!("Mem: {} {} {}", bus.readb(0x00), bus.readb(0x01), bus.readb(0x02))
|
||||
|
||||
// Load font and start main loop
|
||||
let font_path = Path::new("resources/fonts/PressStart2P.ttf");
|
||||
let mut glyphs = window.load_font(font_path).unwrap();
|
||||
while let Some(event) = window.next() {
|
||||
if let Some(Button::Keyboard(key)) = event.press_args() {
|
||||
if key == Key::C { // advance one clock
|
||||
cpu.clock(&mut bus);
|
||||
} else if key == Key::Space { // advance to next instruction
|
||||
cpu.clock(&mut bus);
|
||||
while cpu.is_ahead() {
|
||||
cpu.clock(&mut bus);
|
||||
}
|
||||
}
|
||||
}
|
||||
render(&mut window, &event, &mut glyphs, &cpu, &bus);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
fn render(window: &mut PistonWindow, event: &Event, glyphs: &mut Glyphs, cpu: &CPU, bus: &dyn Bus) {
|
||||
window.draw_2d(event, |c, g, d| {
|
||||
clear([0.0, 0.0, 1.0, 1.0], g);
|
||||
render_cpu(&c, g, glyphs, cpu);
|
||||
render_pc(&c, g, glyphs, cpu, bus);
|
||||
glyphs.factory.encoder.flush(d);
|
||||
});
|
||||
}
|
||||
|
||||
fn render_cpu(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, cpu: &CPU) {
|
||||
let font_size_pt = 36;
|
||||
let color_white = [1.0; 4];
|
||||
let color_red = [1.0, 0.0, 0.0, 1.0];
|
||||
let color_green = [0.0, 1.0, 0.0, 1.0];
|
||||
|
||||
let font_scale = 0.25;
|
||||
let font_size_px = 12;
|
||||
|
||||
let line_distance = font_size_px as f64 * 0.5;
|
||||
let x_dist = font_size_px as f64;
|
||||
|
||||
let flags_y = line_distance + font_size_px as f64;
|
||||
let mut flags_x_offset = x_dist;
|
||||
let mut transform = c.transform.trans(x_dist, flags_y).scale(font_scale, font_scale);
|
||||
Text::new_color(color_white, font_size_pt).draw(
|
||||
&"Flags:",
|
||||
glyphs,
|
||||
&c.draw_state,
|
||||
transform,
|
||||
g
|
||||
);
|
||||
transform = transform.trans(300.0, 0.0);
|
||||
Text::new_color(if cpu.get_flag(NEGATIVE) == 1 { color_green} else { color_red } , font_size_pt).draw(
|
||||
&"N",
|
||||
glyphs,
|
||||
&c.draw_state,
|
||||
transform,
|
||||
g
|
||||
);
|
||||
transform = transform.trans(72.0, 0.0);
|
||||
Text::new_color(if cpu.get_flag(OVERFLOW) == 1 { color_green} else { color_red } , font_size_pt).draw(
|
||||
&"V",
|
||||
glyphs,
|
||||
&c.draw_state,
|
||||
transform,
|
||||
g
|
||||
);
|
||||
transform = transform.trans(72.0, 0.0);
|
||||
Text::new_color(color_white, font_size_pt).draw(
|
||||
&"-",
|
||||
glyphs,
|
||||
&c.draw_state,
|
||||
transform,
|
||||
g
|
||||
);
|
||||
transform = transform.trans(72.0, 0.0);
|
||||
Text::new_color(if cpu.get_flag(BREAK) == 1 { color_green} else { color_red } , font_size_pt).draw(
|
||||
&"B",
|
||||
glyphs,
|
||||
&c.draw_state,
|
||||
transform,
|
||||
g
|
||||
);
|
||||
transform = transform.trans(72.0, 0.0);
|
||||
Text::new_color(if cpu.get_flag(DECIMAL) == 1 { color_green} else { color_red } , font_size_pt).draw(
|
||||
&"D",
|
||||
glyphs,
|
||||
&c.draw_state,
|
||||
transform,
|
||||
g
|
||||
);
|
||||
transform = transform.trans(72.0, 0.0);
|
||||
Text::new_color(if cpu.get_flag(IRQ) == 1 { color_green} else { color_red } , font_size_pt).draw(
|
||||
&"I",
|
||||
glyphs,
|
||||
&c.draw_state,
|
||||
transform,
|
||||
g
|
||||
);
|
||||
transform = transform.trans(72.0, 0.0);
|
||||
Text::new_color(if cpu.get_flag(ZERO) == 1 { color_green} else { color_red } , font_size_pt).draw(
|
||||
&"Z",
|
||||
glyphs,
|
||||
&c.draw_state,
|
||||
transform,
|
||||
g
|
||||
);
|
||||
transform = transform.trans(72.0, 0.0);
|
||||
Text::new_color(if cpu.get_flag(CARRY) == 1 { color_green} else { color_red } , font_size_pt).draw(
|
||||
&"C",
|
||||
glyphs,
|
||||
&c.draw_state,
|
||||
transform,
|
||||
g
|
||||
);
|
||||
|
||||
let cpu_register_texts = [
|
||||
&format!("A: {:#x} ({})", cpu.regs.a, cpu.regs.a),
|
||||
&format!("X: {:#x} ({})", cpu.regs.x, cpu.regs.y),
|
||||
&format!("Y: {:#x} ({})", cpu.regs.y, cpu.regs.x),
|
||||
&format!("Stack P: {:#x} ({})", cpu.regs.sp, cpu.regs.sp),
|
||||
&format!("Program P: {:#x}", cpu.regs.pc),
|
||||
];
|
||||
|
||||
for (i, &text) in cpu_register_texts.iter().enumerate() {
|
||||
let y_dist: f64 = (i+2) as f64*(line_distance + font_size_px as f64);
|
||||
let transform = c.transform.trans(x_dist, y_dist).scale(font_scale, font_scale);
|
||||
Text::new_color(color_white, font_size_pt).draw(
|
||||
&text,
|
||||
glyphs,
|
||||
&c.draw_state,
|
||||
transform,
|
||||
g
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn render_pc(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, cpu: &CPU, bus: &dyn Bus) {
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user