render memory
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -5,6 +5,7 @@ name = "NESemu"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"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)",
|
||||
"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)",
|
||||
|
||||
@@ -14,3 +14,4 @@ phf = { version = "0.8", features = ['macros'] }
|
||||
failure = "0.1.*"
|
||||
piston_window = "0.105.*"
|
||||
piston2d-opengl_graphics = "0.70.*"
|
||||
gfx_core = "0.9.2"
|
||||
228
src/main.rs
228
src/main.rs
@@ -8,6 +8,7 @@ mod nes;
|
||||
|
||||
use std::path::Path;
|
||||
use piston_window::*;
|
||||
use gfx_core::Device;
|
||||
use nes::types::*;
|
||||
use nes::cpu::*;
|
||||
use nes::bus::*;
|
||||
@@ -16,10 +17,22 @@ use opengl_graphics::OpenGL;
|
||||
use log::Level;
|
||||
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() {
|
||||
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();
|
||||
|
||||
@@ -32,129 +45,143 @@ fn main() {
|
||||
bus.writeb(addr, test_prog[i])
|
||||
}
|
||||
|
||||
// hint program location to processor
|
||||
bus.writew(0xfffc, offset);
|
||||
|
||||
// disassemble instructions
|
||||
let disasm = Disasm::disassemble(&test_prog, offset).unwrap();
|
||||
|
||||
// hint program location to processor
|
||||
bus.writew(0xfffc, offset);
|
||||
|
||||
// Create and reset CPU
|
||||
let mut cpu: CPU = CPU::new();
|
||||
cpu.reset(&bus);
|
||||
|
||||
// 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() {
|
||||
// Prepare window and drawing resources
|
||||
let mut window: PistonWindow = WindowSettings::new("NESemu", [256*3, 240*2])
|
||||
.exit_on_esc(true).graphics_api(OpenGL::V3_2).build().unwrap();
|
||||
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 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() {
|
||||
match key {
|
||||
Key::C => cpu.clock(&mut bus), // advance one clock
|
||||
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, &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| {
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
fn render_cpu(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, cpu: &CPU, disasm: &Disasm) {
|
||||
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(
|
||||
fn render_cpu(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, cpu: &CPU, offset: [f64; 2]) {
|
||||
let mut transform = c.transform
|
||||
.trans(offset[0], offset[1] + FT_SIZE_PX);
|
||||
let mut color = FT_COLOR_WHITE;
|
||||
Text::new_color(color, FT_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(
|
||||
).unwrap();
|
||||
|
||||
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",
|
||||
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(
|
||||
).unwrap();
|
||||
transform = transform.trans(16.0, 0.0);
|
||||
color = if cpu.get_flag(OVERFLOW) == 1 { FT_COLOR_GREEN} else { FT_COLOR_RED };
|
||||
Text::new_color(color, FT_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(
|
||||
).unwrap();
|
||||
transform = transform.trans(16.0, 0.0);
|
||||
color = FT_COLOR_WHITE;
|
||||
Text::new_color(color, FT_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(
|
||||
).unwrap();
|
||||
transform = transform.trans(16.0, 0.0);
|
||||
color = if cpu.get_flag(BREAK) == 1 { FT_COLOR_GREEN} else { FT_COLOR_RED };
|
||||
Text::new_color(color, FT_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(
|
||||
).unwrap();
|
||||
transform = transform.trans(16.0, 0.0);
|
||||
color = if cpu.get_flag(DECIMAL) == 1 { FT_COLOR_GREEN} else { FT_COLOR_RED };
|
||||
Text::new_color(color, FT_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(
|
||||
).unwrap();
|
||||
transform = transform.trans(16.0, 0.0);
|
||||
color = if cpu.get_flag(IRQ) == 1 { FT_COLOR_GREEN} else { FT_COLOR_RED };
|
||||
Text::new_color(color, FT_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(
|
||||
).unwrap();
|
||||
transform = transform.trans(16.0, 0.0);
|
||||
color = if cpu.get_flag(ZERO) == 1 { FT_COLOR_GREEN} else { FT_COLOR_RED };
|
||||
Text::new_color(color, FT_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(
|
||||
).unwrap();
|
||||
transform = transform.trans(16.0, 0.0);
|
||||
color = if cpu.get_flag(CARRY) == 1 { FT_COLOR_GREEN} else { FT_COLOR_RED };
|
||||
Text::new_color(color, FT_SIZE_PT).draw(
|
||||
&"C",
|
||||
glyphs,
|
||||
&c.draw_state,
|
||||
transform,
|
||||
g
|
||||
);
|
||||
).unwrap();
|
||||
|
||||
let cpu_register_texts = [
|
||||
&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() {
|
||||
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(
|
||||
let y_offset = (i+2) as f64 * (FT_LINE_DISTANCE + FT_SIZE_PX);
|
||||
let transform = c.transform.trans(offset[0], offset[1] + y_offset);
|
||||
Text::new_color(FT_COLOR_WHITE, FT_SIZE_PT).draw(
|
||||
&text,
|
||||
glyphs,
|
||||
&c.draw_state,
|
||||
transform,
|
||||
g
|
||||
);
|
||||
).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
for (i, text) in disasm.instructions.iter().enumerate() {
|
||||
let y_dist: f64 = (i+8) 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,
|
||||
fn render_disasm(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, disasm: &Disasm, pc: Addr, offset: [f64; 2]) {
|
||||
let mut transform = c.transform.trans(offset[0], offset[1]);
|
||||
for (text, addr) in disasm.instructions.iter().zip(disasm.addresses.iter()) {
|
||||
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,
|
||||
glyphs,
|
||||
&c.draw_state,
|
||||
transform,
|
||||
g
|
||||
).unwrap();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
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,
|
||||
transform_x,
|
||||
g
|
||||
);
|
||||
).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
fn render_pc(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, cpu: &CPU, bus: &dyn Bus) {
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,31 +2,29 @@ use failure::Error;
|
||||
use crate::nes::bus::*;
|
||||
use crate::nes::types::*;
|
||||
use crate::nes::cpu::instructions::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
// Disassemble code around the pc of the cpu
|
||||
pub struct Disasm {
|
||||
pub offset: Addr,
|
||||
pub instructions: Vec<String>,
|
||||
pub addresses: Vec<Addr>
|
||||
}
|
||||
|
||||
impl BusDevice for Disasm {}
|
||||
impl Clockable for Disasm {
|
||||
fn clock<T: Bus>(&mut self, bus: &mut T) {
|
||||
// TODO find new pc location
|
||||
}
|
||||
}
|
||||
|
||||
impl Disasm {
|
||||
// Disassemble given code region
|
||||
pub fn disassemble(mem: &[Byte], offset: Addr) -> Result<Self, Error> {
|
||||
let mut instructions = Vec::new();
|
||||
let mut addresses = Vec::new();
|
||||
let mut mem_iter = mem.iter().enumerate();
|
||||
while let Some(b) = mem_iter.next() {
|
||||
let addr = offset + (b.0 as Addr);
|
||||
let i = Instruction::decode_op(*b.1)?;
|
||||
|
||||
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::ZPX => self.am_ZPX(bus),
|
||||
// AddrMode::ZPY => self.am_ZPY(bus),
|
||||
@@ -40,7 +38,7 @@ impl Disasm {
|
||||
AddrMode::REL => {
|
||||
let rel_addr = (*mem_iter.next().unwrap().1) as Addr;
|
||||
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),
|
||||
@@ -54,8 +52,9 @@ impl Disasm {
|
||||
|
||||
let s = format!("{:#06x}: {} {} ({})", addr, i.operation, args, i.addr_mode);
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user