gui to step through cpu instructions

This commit is contained in:
Daniel Bauer
2019-12-23 22:36:40 +01:00
parent d54427c2b3
commit ec7998278e
6 changed files with 1479 additions and 26 deletions

View File

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

View File

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