gui to step through cpu instructions
This commit is contained in:
1210
Cargo.lock
generated
1210
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,9 @@ edition = "2018"
|
|||||||
test = []
|
test = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "*"
|
log = "0.4.*"
|
||||||
simple_logger = "*"
|
simple_logger = "1.3.*"
|
||||||
phf = { version = "0.8", features = ['macros'] }
|
phf = { version = "0.8", features = ['macros'] }
|
||||||
failure = "*"
|
failure = "0.1.*"
|
||||||
|
piston_window = "0.105.*"
|
||||||
|
piston2d-opengl_graphics = "0.70.*"
|
||||||
94
resources/fonts/LICENSE.txt
Normal file
94
resources/fonts/LICENSE.txt
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
Copyright (c) 2011, Cody "CodeMan38" Boisclair (cody@zone38.net),
|
||||||
|
with Reserved Font Name "Press Start".
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
BIN
resources/fonts/PressStart2P.ttf
Normal file
BIN
resources/fonts/PressStart2P.ttf
Normal file
Binary file not shown.
37
src/cpu.rs
37
src/cpu.rs
@@ -7,12 +7,12 @@ use crate::types::*;
|
|||||||
use log::{debug};
|
use log::{debug};
|
||||||
|
|
||||||
pub struct Registers {
|
pub struct Registers {
|
||||||
a: Byte,
|
pub a: Byte,
|
||||||
x: Byte,
|
pub x: Byte,
|
||||||
y: Byte,
|
pub y: Byte,
|
||||||
sp: Byte,
|
pub sp: Byte,
|
||||||
pc: Addr,
|
pub pc: Addr,
|
||||||
flags: Byte
|
pub flags: Byte
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Registers {
|
impl Registers {
|
||||||
@@ -37,17 +37,17 @@ impl Debug for Registers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const CARRY: Byte = 1 << 0;
|
pub const CARRY: Byte = 1 << 0;
|
||||||
const ZERO: Byte = 1 << 1;
|
pub const ZERO: Byte = 1 << 1;
|
||||||
const IRQ: Byte = 1 << 2;
|
pub const IRQ: Byte = 1 << 2;
|
||||||
const DECIMAL: Byte = 1 << 3;
|
pub const DECIMAL: Byte = 1 << 3;
|
||||||
const BREAK: Byte = 1 << 4;
|
pub const BREAK: Byte = 1 << 4;
|
||||||
const UNUSED: Byte = 1 << 5;
|
pub const UNUSED: Byte = 1 << 5;
|
||||||
const OVERFLOW: Byte = 1 << 6;
|
pub const OVERFLOW: Byte = 1 << 6;
|
||||||
const NEGATIVE: Byte = 1 << 7;
|
pub const NEGATIVE: Byte = 1 << 7;
|
||||||
|
|
||||||
pub struct CPU {
|
pub struct CPU {
|
||||||
regs: Registers,
|
pub regs: Registers,
|
||||||
curr_op: Byte, // current operation
|
curr_op: Byte, // current operation
|
||||||
cycles: u8, // number of clock clycles the CPU is ahead of global clock
|
cycles: u8, // number of clock clycles the CPU is ahead of global clock
|
||||||
}
|
}
|
||||||
@@ -86,6 +86,11 @@ impl CPU {
|
|||||||
self.curr_op = 0x00;
|
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 {
|
fn readb<T: Bus>(&self, bus: &T, addr: Addr) -> Byte {
|
||||||
bus.readb(addr)
|
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 {
|
if self.regs.flags & flag > 0 {
|
||||||
1
|
1
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
150
src/main.rs
150
src/main.rs
@@ -2,18 +2,27 @@
|
|||||||
#[macro_use] extern crate failure;
|
#[macro_use] extern crate failure;
|
||||||
extern crate simple_logger;
|
extern crate simple_logger;
|
||||||
extern crate phf;
|
extern crate phf;
|
||||||
|
extern crate piston_window;
|
||||||
|
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
mod cpu;
|
mod cpu;
|
||||||
mod bus;
|
mod bus;
|
||||||
mod types;
|
mod types;
|
||||||
|
|
||||||
|
use std::path::Path;
|
||||||
|
use piston_window::*;
|
||||||
use types::*;
|
use types::*;
|
||||||
use cpu::CPU;
|
use cpu::*;
|
||||||
use bus::{MemoryBus, Bus};
|
use bus::{MemoryBus, Bus};
|
||||||
|
use opengl_graphics::OpenGL;
|
||||||
|
use log::Level;
|
||||||
|
use piston_window::Text;
|
||||||
|
|
||||||
fn main() {
|
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();
|
let mut bus = MemoryBus::new();
|
||||||
|
|
||||||
@@ -30,12 +39,145 @@ fn main() {
|
|||||||
bus.writeb(0xfffc, offset as Byte);
|
bus.writeb(0xfffc, offset as Byte);
|
||||||
bus.writeb(0xfffc + 1, (offset >> 8) as Byte);
|
bus.writeb(0xfffc + 1, (offset >> 8) as Byte);
|
||||||
|
|
||||||
|
// Create and reset CPU
|
||||||
let mut cpu: CPU = CPU::new();
|
let mut cpu: CPU = CPU::new();
|
||||||
cpu.reset(&bus);
|
cpu.reset(&bus);
|
||||||
for _ in 0..500+10 {
|
|
||||||
|
// 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);
|
cpu.clock(&mut bus);
|
||||||
debug!("Mem: {} {} {}", bus.readb(0x00), bus.readb(0x01), bus.readb(0x02))
|
} 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