render memory

This commit is contained in:
Daniel Bauer
2019-12-24 14:06:10 +01:00
parent f2f4d49cc7
commit 0f73777413
4 changed files with 166 additions and 85 deletions

1
Cargo.lock generated
View File

@@ -5,6 +5,7 @@ name = "NESemu"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"gfx_core 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"phf 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "phf 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"piston2d-opengl_graphics 0.70.0 (registry+https://github.com/rust-lang/crates.io-index)", "piston2d-opengl_graphics 0.70.0 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@@ -14,3 +14,4 @@ phf = { version = "0.8", features = ['macros'] }
failure = "0.1.*" failure = "0.1.*"
piston_window = "0.105.*" piston_window = "0.105.*"
piston2d-opengl_graphics = "0.70.*" piston2d-opengl_graphics = "0.70.*"
gfx_core = "0.9.2"

View File

@@ -8,6 +8,7 @@ mod nes;
use std::path::Path; use std::path::Path;
use piston_window::*; use piston_window::*;
use gfx_core::Device;
use nes::types::*; use nes::types::*;
use nes::cpu::*; use nes::cpu::*;
use nes::bus::*; use nes::bus::*;
@@ -16,10 +17,22 @@ use opengl_graphics::OpenGL;
use log::Level; use log::Level;
use piston_window::Text; use piston_window::Text;
// font options
const FT_SIZE_PT: u32 = 6;
const FT_SIZE_PX: f64 = 8.0;
const FT_COLOR_WHITE: [f32; 4] = [1.0; 4];
const FT_COLOR_RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
const FT_COLOR_GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0];
const FT_LINE_DISTANCE: f64 = FT_SIZE_PX * 0.5;
// font for text drawing
fn get_glyphs(window: &mut PistonWindow) -> Glyphs {
let font_path = Path::new("resources/fonts/PressStart2P.ttf");
window.load_font(font_path).unwrap()
}
fn main() { fn main() {
simple_logger::init_with_level(Level::Debug).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(); let mut bus = MemoryBus::new();
@@ -32,129 +45,143 @@ fn main() {
bus.writeb(addr, test_prog[i]) bus.writeb(addr, test_prog[i])
} }
// hint program location to processor
bus.writew(0xfffc, offset);
// disassemble instructions // disassemble instructions
let disasm = Disasm::disassemble(&test_prog, offset).unwrap(); let disasm = Disasm::disassemble(&test_prog, offset).unwrap();
// hint program location to processor
bus.writew(0xfffc, offset);
// Create and reset CPU // Create and reset CPU
let mut cpu: CPU = CPU::new(); let mut cpu: CPU = CPU::new();
cpu.reset(&bus); cpu.reset(&bus);
// Load font and start main loop // Prepare window and drawing resources
let font_path = Path::new("resources/fonts/PressStart2P.ttf"); let mut window: PistonWindow = WindowSettings::new("NESemu", [256*3, 240*2])
let mut glyphs = window.load_font(font_path).unwrap(); .exit_on_esc(true).graphics_api(OpenGL::V3_2).build().unwrap();
while let Some(event) = window.next() { let mut event_settings = EventSettings::new();
event_settings.max_fps = 60;
let mut events = Events::new(event_settings);
let mut glyphs = get_glyphs(&mut window);
// Main loop
while let Some(event) = events.next(&mut window) {
if let Some(_) = event.render_args() {
render(&mut window, &event, &mut glyphs, &cpu, &bus, &disasm);
}
if let Some(Button::Keyboard(key)) = event.press_args() { if let Some(Button::Keyboard(key)) = event.press_args() {
if key == Key::C { // advance one clock match key {
cpu.clock(&mut bus); Key::C => cpu.clock(&mut bus), // advance one clock
} else if key == Key::Space { // advance to next instruction Key::Space => { // advance to next instruction
cpu.clock(&mut bus); cpu.clock(&mut bus);
while cpu.is_ahead() { while cpu.is_ahead() {
cpu.clock(&mut bus); cpu.clock(&mut bus);
} }
} }
_ => { }
}
} }
render(&mut window, &event, &mut glyphs, &cpu, &bus, &disasm);
} }
} }
fn render(window: &mut PistonWindow, event: &Event, glyphs: &mut Glyphs, cpu: &CPU, bus: &dyn Bus, disasm: &Disasm) { fn render(window: &mut PistonWindow, event: &Event, glyphs: &mut Glyphs, cpu: &CPU, bus: &dyn Bus, disasm: &Disasm) {
window.draw_2d(event, |c, g, d| { window.draw_2d(event, |c, g, d| {
clear([0.0, 0.0, 1.0, 1.0], g); clear([0.0, 0.0, 1.0, 1.0], g);
render_cpu(&c, g, glyphs, cpu, disasm);
render_pc(&c, g, glyphs, cpu, bus); let debug_offset = [10.0, 10.0];
render_cpu(&c, g, glyphs, cpu, debug_offset);
render_disasm(&c, g, glyphs, disasm, cpu.regs.pc,
[debug_offset[0], debug_offset[1] + (7.0 * (FT_LINE_DISTANCE+FT_SIZE_PX))]);
render_memory(&c, g, glyphs, bus,
[debug_offset[0] + 300.0, debug_offset[1]]);
glyphs.factory.encoder.flush(d); glyphs.factory.encoder.flush(d);
}); });
} }
fn render_cpu(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, cpu: &CPU, disasm: &Disasm) { fn render_cpu(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, cpu: &CPU, offset: [f64; 2]) {
let font_size_pt = 36; let mut transform = c.transform
let color_white = [1.0; 4]; .trans(offset[0], offset[1] + FT_SIZE_PX);
let color_red = [1.0, 0.0, 0.0, 1.0]; let mut color = FT_COLOR_WHITE;
let color_green = [0.0, 1.0, 0.0, 1.0]; Text::new_color(color, FT_SIZE_PT).draw(
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:", &"Flags:",
glyphs, glyphs,
&c.draw_state, &c.draw_state,
transform, transform,
g g
); ).unwrap();
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( transform = transform.trans(70.0, 0.0);
color = if cpu.get_flag(NEGATIVE) == 1 { FT_COLOR_GREEN} else { FT_COLOR_RED };
Text::new_color(color , FT_SIZE_PT).draw(
&"N", &"N",
glyphs, glyphs,
&c.draw_state, &c.draw_state,
transform, transform,
g g
); ).unwrap();
transform = transform.trans(72.0, 0.0); transform = transform.trans(16.0, 0.0);
Text::new_color(if cpu.get_flag(OVERFLOW) == 1 { color_green} else { color_red } , font_size_pt).draw( color = if cpu.get_flag(OVERFLOW) == 1 { FT_COLOR_GREEN} else { FT_COLOR_RED };
Text::new_color(color, FT_SIZE_PT).draw(
&"V", &"V",
glyphs, glyphs,
&c.draw_state, &c.draw_state,
transform, transform,
g g
); ).unwrap();
transform = transform.trans(72.0, 0.0); transform = transform.trans(16.0, 0.0);
Text::new_color(color_white, font_size_pt).draw( color = FT_COLOR_WHITE;
Text::new_color(color, FT_SIZE_PT).draw(
&"-", &"-",
glyphs, glyphs,
&c.draw_state, &c.draw_state,
transform, transform,
g g
); ).unwrap();
transform = transform.trans(72.0, 0.0); transform = transform.trans(16.0, 0.0);
Text::new_color(if cpu.get_flag(BREAK) == 1 { color_green} else { color_red } , font_size_pt).draw( color = if cpu.get_flag(BREAK) == 1 { FT_COLOR_GREEN} else { FT_COLOR_RED };
Text::new_color(color, FT_SIZE_PT).draw(
&"B", &"B",
glyphs, glyphs,
&c.draw_state, &c.draw_state,
transform, transform,
g g
); ).unwrap();
transform = transform.trans(72.0, 0.0); transform = transform.trans(16.0, 0.0);
Text::new_color(if cpu.get_flag(DECIMAL) == 1 { color_green} else { color_red } , font_size_pt).draw( color = if cpu.get_flag(DECIMAL) == 1 { FT_COLOR_GREEN} else { FT_COLOR_RED };
Text::new_color(color, FT_SIZE_PT).draw(
&"D", &"D",
glyphs, glyphs,
&c.draw_state, &c.draw_state,
transform, transform,
g g
); ).unwrap();
transform = transform.trans(72.0, 0.0); transform = transform.trans(16.0, 0.0);
Text::new_color(if cpu.get_flag(IRQ) == 1 { color_green} else { color_red } , font_size_pt).draw( color = if cpu.get_flag(IRQ) == 1 { FT_COLOR_GREEN} else { FT_COLOR_RED };
Text::new_color(color, FT_SIZE_PT).draw(
&"I", &"I",
glyphs, glyphs,
&c.draw_state, &c.draw_state,
transform, transform,
g g
); ).unwrap();
transform = transform.trans(72.0, 0.0); transform = transform.trans(16.0, 0.0);
Text::new_color(if cpu.get_flag(ZERO) == 1 { color_green} else { color_red } , font_size_pt).draw( color = if cpu.get_flag(ZERO) == 1 { FT_COLOR_GREEN} else { FT_COLOR_RED };
Text::new_color(color, FT_SIZE_PT).draw(
&"Z", &"Z",
glyphs, glyphs,
&c.draw_state, &c.draw_state,
transform, transform,
g g
); ).unwrap();
transform = transform.trans(72.0, 0.0); transform = transform.trans(16.0, 0.0);
Text::new_color(if cpu.get_flag(CARRY) == 1 { color_green} else { color_red } , font_size_pt).draw( color = if cpu.get_flag(CARRY) == 1 { FT_COLOR_GREEN} else { FT_COLOR_RED };
Text::new_color(color, FT_SIZE_PT).draw(
&"C", &"C",
glyphs, glyphs,
&c.draw_state, &c.draw_state,
transform, transform,
g g
); ).unwrap();
let cpu_register_texts = [ let cpu_register_texts = [
&format!("A: {:#x} ({})", cpu.regs.a, cpu.regs.a), &format!("A: {:#x} ({})", cpu.regs.a, cpu.regs.a),
@@ -165,32 +192,85 @@ fn render_cpu(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, cpu: &CPU, disasm:
]; ];
for (i, &text) in cpu_register_texts.iter().enumerate() { 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 y_offset = (i+2) as f64 * (FT_LINE_DISTANCE + FT_SIZE_PX);
let transform = c.transform.trans(x_dist, y_dist).scale(font_scale, font_scale); let transform = c.transform.trans(offset[0], offset[1] + y_offset);
Text::new_color(color_white, font_size_pt).draw( Text::new_color(FT_COLOR_WHITE, FT_SIZE_PT).draw(
&text, &text,
glyphs, glyphs,
&c.draw_state, &c.draw_state,
transform, transform,
g g
); ).unwrap();
} }
}
for (i, text) in disasm.instructions.iter().enumerate() { fn render_disasm(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, disasm: &Disasm, pc: Addr, offset: [f64; 2]) {
let y_dist: f64 = (i+8) as f64*(line_distance + font_size_px as f64); let mut transform = c.transform.trans(offset[0], offset[1]);
let transform = c.transform.trans(x_dist, y_dist).scale(font_scale, font_scale); for (text, addr) in disasm.instructions.iter().zip(disasm.addresses.iter()) {
Text::new_color(color_white, font_size_pt).draw( transform = transform.trans(0.0, FT_LINE_DISTANCE + FT_SIZE_PX);
let color = if *addr == pc {
FT_COLOR_RED
} else {
FT_COLOR_WHITE
};
Text::new_color(color, FT_SIZE_PT).draw(
&text, &text,
glyphs, glyphs,
&c.draw_state, &c.draw_state,
transform, transform,
g g
); ).unwrap();
} }
} }
fn render_pc(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, cpu: &CPU, bus: &dyn Bus) { fn render_memory(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, bus: &dyn Bus, offset: [f64; 2]) {
let mut transform_y = c.transform.trans(offset[0], offset[1]);
for page in (0x0000..0x00F0).step_by(16) {
transform_y = transform_y.trans(0.0, FT_LINE_DISTANCE+FT_SIZE_PX);
Text::new_color(FT_COLOR_WHITE, FT_SIZE_PT).draw(
&format!("{:#06x}:", page),
glyphs,
&c.draw_state,
transform_y,
g
).unwrap();
let mut transform_x = transform_y.trans(40.0, 0.0);
for offset in 0u16..16u16 {
transform_x = transform_x.trans(20.0, 0.0);
let addr = page + offset;
Text::new_color(FT_COLOR_WHITE, FT_SIZE_PT).draw(
&format!("{:02x}", bus.readb(addr)),
glyphs,
&c.draw_state,
transform_x,
g
).unwrap();
}
}
transform_y = transform_y.trans(0.0, (FT_LINE_DISTANCE+FT_SIZE_PX) * 1.5);
for page in (0x8000..0x80F0).step_by(16) {
transform_y = transform_y.trans(0.0, (FT_LINE_DISTANCE+FT_SIZE_PX));
Text::new_color(FT_COLOR_WHITE, FT_SIZE_PT).draw(
&format!("{:#06x}:", page),
glyphs,
&c.draw_state,
transform_y,
g
).unwrap();
let mut transform_x = transform_y.trans(40.0, 0.0);
for offset in 0u16..16u16 {
transform_x = transform_x.trans(20.0, 0.0);
let addr = page + offset;
Text::new_color(FT_COLOR_WHITE, FT_SIZE_PT).draw(
&format!("{:02x}", bus.readb(addr)),
glyphs,
&c.draw_state,
transform_x,
g
).unwrap();
}
}
} }

View File

@@ -2,31 +2,29 @@ use failure::Error;
use crate::nes::bus::*; use crate::nes::bus::*;
use crate::nes::types::*; use crate::nes::types::*;
use crate::nes::cpu::instructions::*; use crate::nes::cpu::instructions::*;
use std::collections::HashMap;
// Disassemble code around the pc of the cpu // Disassemble code around the pc of the cpu
pub struct Disasm { pub struct Disasm {
pub offset: Addr, pub offset: Addr,
pub instructions: Vec<String>, pub instructions: Vec<String>,
pub addresses: Vec<Addr>
} }
impl BusDevice for Disasm {} impl BusDevice for Disasm {}
impl Clockable for Disasm {
fn clock<T: Bus>(&mut self, bus: &mut T) {
// TODO find new pc location
}
}
impl Disasm { impl Disasm {
// Disassemble given code region // Disassemble given code region
pub fn disassemble(mem: &[Byte], offset: Addr) -> Result<Self, Error> { pub fn disassemble(mem: &[Byte], offset: Addr) -> Result<Self, Error> {
let mut instructions = Vec::new(); let mut instructions = Vec::new();
let mut addresses = Vec::new();
let mut mem_iter = mem.iter().enumerate(); let mut mem_iter = mem.iter().enumerate();
while let Some(b) = mem_iter.next() { while let Some(b) = mem_iter.next() {
let addr = offset + (b.0 as Addr); let addr = offset + (b.0 as Addr);
let i = Instruction::decode_op(*b.1)?; let i = Instruction::decode_op(*b.1)?;
let args = match i.addr_mode { let args = match i.addr_mode {
AddrMode::IMM => format!("#{0:#04x} ({0})", mem_iter.next().unwrap().1), AddrMode::IMM => format!("#{0:02x} ({0})", mem_iter.next().unwrap().1),
// AddrMode::ZP0 => self.am_ZP0(bus), // AddrMode::ZP0 => self.am_ZP0(bus),
// AddrMode::ZPX => self.am_ZPX(bus), // AddrMode::ZPX => self.am_ZPX(bus),
// AddrMode::ZPY => self.am_ZPY(bus), // AddrMode::ZPY => self.am_ZPY(bus),
@@ -40,7 +38,7 @@ impl Disasm {
AddrMode::REL => { AddrMode::REL => {
let rel_addr = (*mem_iter.next().unwrap().1) as Addr; let rel_addr = (*mem_iter.next().unwrap().1) as Addr;
let jmp_addr = Disasm::get_rel_addr(rel_addr, addr+2); let jmp_addr = Disasm::get_rel_addr(rel_addr, addr+2);
format!("{:#04x} => {:#06x}", rel_addr, jmp_addr) format!("#{:02x} => {:#06x}", rel_addr, jmp_addr)
}, },
// AddrMode::ABX => self.am_ABX(bus), // AddrMode::ABX => self.am_ABX(bus),
@@ -54,8 +52,9 @@ impl Disasm {
let s = format!("{:#06x}: {} {} ({})", addr, i.operation, args, i.addr_mode); let s = format!("{:#06x}: {} {} ({})", addr, i.operation, args, i.addr_mode);
instructions.push(s); instructions.push(s);
addresses.push(addr);
} }
Ok(Disasm { offset, instructions }) Ok(Disasm { offset, instructions, addresses })
} }
fn get_rel_addr(rel_addr: Addr, curr_addr: Addr) -> Addr { fn get_rel_addr(rel_addr: Addr, curr_addr: Addr) -> Addr {