disassembler
This commit is contained in:
37
src/main.rs
37
src/main.rs
@@ -10,7 +10,8 @@ use std::path::Path;
|
|||||||
use piston_window::*;
|
use piston_window::*;
|
||||||
use nes::types::*;
|
use nes::types::*;
|
||||||
use nes::cpu::*;
|
use nes::cpu::*;
|
||||||
use nes::bus::{MemoryBus, Bus};
|
use nes::bus::*;
|
||||||
|
use nes::disasm::*;
|
||||||
use opengl_graphics::OpenGL;
|
use opengl_graphics::OpenGL;
|
||||||
use log::Level;
|
use log::Level;
|
||||||
use piston_window::Text;
|
use piston_window::Text;
|
||||||
@@ -25,15 +26,17 @@ fn main() {
|
|||||||
// Load little test program into ram
|
// Load little test program into ram
|
||||||
let test_prog: [Byte; 28] = [0xA2, 0x0A, 0x8E, 0x00, 0x00, 0xA2, 0x03, 0x8E, 0x01, 0x00, 0xAC, 0x00, 0x00, 0xA9,
|
let test_prog: [Byte; 28] = [0xA2, 0x0A, 0x8E, 0x00, 0x00, 0xA2, 0x03, 0x8E, 0x01, 0x00, 0xAC, 0x00, 0x00, 0xA9,
|
||||||
0x00, 0x18, 0x6D, 0x01, 0x00, 0x88, 0xD0, 0xFA, 0x8D, 0x02, 0x00, 0xEA, 0xEA, 0xEA];
|
0x00, 0x18, 0x6D, 0x01, 0x00, 0x88, 0xD0, 0xFA, 0x8D, 0x02, 0x00, 0xEA, 0xEA, 0xEA];
|
||||||
let offset = 0x8000;
|
let offset: Addr = 0x8000;
|
||||||
for i in 0..test_prog.len() {
|
for i in 0..test_prog.len() {
|
||||||
let addr = i + offset;
|
let addr = (i as Addr) + offset;
|
||||||
bus.writeb(addr as u16, test_prog[i])
|
bus.writeb(addr, test_prog[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
// hint program location to processor
|
// hint program location to processor
|
||||||
bus.writeb(0xfffc, offset as Byte);
|
bus.writew(0xfffc, offset);
|
||||||
bus.writeb(0xfffc + 1, (offset >> 8) as Byte);
|
|
||||||
|
// disassemble instructions
|
||||||
|
let disasm = Disasm::disassemble(&test_prog, offset).unwrap();
|
||||||
|
|
||||||
// Create and reset CPU
|
// Create and reset CPU
|
||||||
let mut cpu: CPU = CPU::new();
|
let mut cpu: CPU = CPU::new();
|
||||||
@@ -53,20 +56,20 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
render(&mut window, &event, &mut glyphs, &cpu, &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) {
|
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);
|
render_cpu(&c, g, glyphs, cpu, disasm);
|
||||||
render_pc(&c, g, glyphs, cpu, bus);
|
render_pc(&c, g, glyphs, cpu, bus);
|
||||||
glyphs.factory.encoder.flush(d);
|
glyphs.factory.encoder.flush(d);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_cpu(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, cpu: &CPU) {
|
fn render_cpu(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, cpu: &CPU, disasm: &Disasm) {
|
||||||
let font_size_pt = 36;
|
let font_size_pt = 36;
|
||||||
let color_white = [1.0; 4];
|
let color_white = [1.0; 4];
|
||||||
let color_red = [1.0, 0.0, 0.0, 1.0];
|
let color_red = [1.0, 0.0, 0.0, 1.0];
|
||||||
@@ -172,6 +175,20 @@ fn render_cpu(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, cpu: &CPU) {
|
|||||||
g
|
g
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
glyphs,
|
||||||
|
&c.draw_state,
|
||||||
|
transform,
|
||||||
|
g
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_pc(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, cpu: &CPU, bus: &dyn Bus) {
|
fn render_pc(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, cpu: &CPU, bus: &dyn Bus) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub mod cpu;
|
pub mod cpu;
|
||||||
pub mod bus;
|
pub mod bus;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
pub mod disasm;
|
||||||
@@ -1,11 +1,35 @@
|
|||||||
use crate::nes::types::*;
|
use crate::nes::types::*;
|
||||||
|
|
||||||
|
// Generic interface describing a Bus
|
||||||
pub trait Bus {
|
pub trait Bus {
|
||||||
fn readb(&self, addr: Addr) -> Byte;
|
fn readb(&self, addr: Addr) -> Byte;
|
||||||
fn readw(&self, addr: Addr) -> Word;
|
fn readw(&self, addr: Addr) -> Word;
|
||||||
fn writeb(&mut self, addr: Addr, data: Byte);
|
fn writeb(&mut self, addr: Addr, data: Byte);
|
||||||
|
fn writew(&mut self, addr: Addr, data: Word);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Impl by devices to access the Bus
|
||||||
|
pub trait BusDevice {
|
||||||
|
fn readb<T: Bus>(&self, bus: &T, addr: Addr) -> Byte {
|
||||||
|
bus.readb(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn readw<T: Bus>(&self, bus: &T, addr: Addr) -> Word {
|
||||||
|
bus.readw(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn writeb<T: Bus>(&mut self, bus: &mut T, addr: Addr, data: Byte) {
|
||||||
|
bus.writeb(addr, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Impl by devices to do stuff on bus clock
|
||||||
|
pub trait Clockable {
|
||||||
|
fn clock<T: Bus>(&mut self, bus: &mut T);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// A simple bus giving access to a chunk of memory
|
||||||
pub struct MemoryBus {
|
pub struct MemoryBus {
|
||||||
ram: [Byte; 0xFFFF]
|
ram: [Byte; 0xFFFF]
|
||||||
}
|
}
|
||||||
@@ -33,4 +57,9 @@ impl Bus for MemoryBus {
|
|||||||
fn writeb(&mut self, addr: Addr, data: Byte) {
|
fn writeb(&mut self, addr: Addr, data: Byte) {
|
||||||
self.ram[addr as usize] = data;
|
self.ram[addr as usize] = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn writew(&mut self, addr: Addr, data: Word) {
|
||||||
|
self.writeb(addr, data as Byte);
|
||||||
|
self.writeb(addr + 1, (data >> 8) as Byte);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
mod instructions;
|
pub mod instructions;
|
||||||
|
|
||||||
use instructions::{Instruction,Operation,AddrMode};
|
use instructions::{Instruction,Operation,AddrMode};
|
||||||
use core::fmt::{Debug,Formatter,Result};
|
use core::fmt::{Debug,Formatter,Result};
|
||||||
use crate::nes::bus::Bus;
|
use crate::nes::bus::*;
|
||||||
use crate::nes::types::*;
|
use crate::nes::types::*;
|
||||||
use log::{debug};
|
use log::{debug};
|
||||||
|
|
||||||
@@ -52,8 +52,26 @@ pub struct CPU {
|
|||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Default implementation to read/write from bus
|
||||||
|
impl BusDevice for CPU { }
|
||||||
|
|
||||||
|
// A cpu is clockable
|
||||||
|
impl Clockable for CPU {
|
||||||
|
fn clock<T: Bus>(&mut self, bus: &mut T) {
|
||||||
|
if self.cycles == 0 {
|
||||||
|
let opcode = self.readb_pc(bus);
|
||||||
|
self.curr_op = opcode;
|
||||||
|
let instruction = Instruction::decode_op(opcode).unwrap(); // TODO error handling
|
||||||
|
self.cycles = self.run_instruction(bus, instruction);
|
||||||
|
}
|
||||||
|
debug!("{:?}", self);
|
||||||
|
self.cycles -= 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl CPU {
|
impl CPU {
|
||||||
pub fn new() -> CPU {
|
pub fn new() -> Self {
|
||||||
CPU {
|
CPU {
|
||||||
regs: Registers::new(),
|
regs: Registers::new(),
|
||||||
cycles: 0,
|
cycles: 0,
|
||||||
@@ -91,18 +109,6 @@ impl CPU {
|
|||||||
return self.cycles > 0;
|
return self.cycles > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn readb<T: Bus>(&self, bus: &T, addr: Addr) -> Byte {
|
|
||||||
bus.readb(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn readw<T: Bus>(&self, bus: &T, addr: Addr) -> Word {
|
|
||||||
bus.readw(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn writeb<T: Bus>(&mut self, bus: &mut T, addr: Addr, data: Byte) {
|
|
||||||
bus.writeb(addr, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// read the next opcode and increment pc
|
// read the next opcode and increment pc
|
||||||
fn readb_pc<T: Bus>(&mut self, bus: &T) -> Byte {
|
fn readb_pc<T: Bus>(&mut self, bus: &T) -> Byte {
|
||||||
let val = self.readb(bus, self.regs.pc);
|
let val = self.readb(bus, self.regs.pc);
|
||||||
@@ -135,19 +141,6 @@ impl CPU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn clock<T: Bus>(&mut self, bus: &mut T) {
|
|
||||||
if self.cycles == 0 {
|
|
||||||
let opcode = self.readb_pc(bus);
|
|
||||||
self.curr_op = opcode;
|
|
||||||
let instruction = Instruction::decode_op(opcode).unwrap(); // TODO error handling
|
|
||||||
self.cycles = self.run_instruction(bus, instruction);
|
|
||||||
}
|
|
||||||
debug!("{:?}", self);
|
|
||||||
self.cycles -= 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_instruction<T: Bus>(&mut self, bus: &mut T, i: &Instruction) -> u8 {
|
fn run_instruction<T: Bus>(&mut self, bus: &mut T, i: &Instruction) -> u8 {
|
||||||
let (value, page_cross) = match &i.addr_mode {
|
let (value, page_cross) = match &i.addr_mode {
|
||||||
AddrMode::IMP => self.am_IMP(bus),
|
AddrMode::IMP => self.am_IMP(bus),
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use failure::err_msg;
|
|||||||
use phf::{Map,phf_map};
|
use phf::{Map,phf_map};
|
||||||
use failure::{Error};
|
use failure::{Error};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::Debug;
|
use std::fmt::{Debug,Display};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AddrMode {
|
pub enum AddrMode {
|
||||||
@@ -20,6 +20,12 @@ pub enum AddrMode {
|
|||||||
IZY,
|
IZY,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for AddrMode {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{:?}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Operation {
|
pub enum Operation {
|
||||||
@@ -81,6 +87,12 @@ pub enum Operation {
|
|||||||
TYA,
|
TYA,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Operation {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{:?}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static INSTRUCTION_SET: Map<u8, Instruction> = phf_map! {
|
static INSTRUCTION_SET: Map<u8, Instruction> = phf_map! {
|
||||||
// 0x00
|
// 0x00
|
||||||
0x00u8 => Instruction { opcode: 0x00, addr_mode: AddrMode::IMM, operation: Operation::BRK, cycles: [7, 0] },
|
0x00u8 => Instruction { opcode: 0x00, addr_mode: AddrMode::IMM, operation: Operation::BRK, cycles: [7, 0] },
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
use failure::Error;
|
||||||
|
use crate::nes::bus::*;
|
||||||
|
use crate::nes::types::*;
|
||||||
|
use crate::nes::cpu::instructions::*;
|
||||||
|
|
||||||
|
// Disassemble code around the pc of the cpu
|
||||||
|
pub struct Disasm {
|
||||||
|
pub offset: Addr,
|
||||||
|
pub instructions: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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::ZP0 => self.am_ZP0(bus),
|
||||||
|
// AddrMode::ZPX => self.am_ZPX(bus),
|
||||||
|
// AddrMode::ZPY => self.am_ZPY(bus),
|
||||||
|
AddrMode::ABS => {
|
||||||
|
let lo = *mem_iter.next().unwrap().1;
|
||||||
|
let hi = *mem_iter.next().unwrap().1;
|
||||||
|
let val = (hi as Addr) << 8 | lo as Addr;
|
||||||
|
format!("{:#06x}", val)
|
||||||
|
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
},
|
||||||
|
|
||||||
|
// AddrMode::ABX => self.am_ABX(bus),
|
||||||
|
// AddrMode::ABY => self.am_ABY(bus),
|
||||||
|
// AddrMode::IND => self.am_IND(bus),
|
||||||
|
// AddrMode::IZX => self.am_IZX(bus),
|
||||||
|
// AddrMode::IZY => self.am_IZY(bus),
|
||||||
|
AddrMode::IMP => String::from(""),
|
||||||
|
_ => String::from("TODO"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let s = format!("{:#06x}: {} {} ({})", addr, i.operation, args, i.addr_mode);
|
||||||
|
instructions.push(s);
|
||||||
|
}
|
||||||
|
Ok(Disasm { offset, instructions })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_rel_addr(rel_addr: Addr, curr_addr: Addr) -> Addr {
|
||||||
|
if rel_addr < 0x80 {
|
||||||
|
curr_addr + rel_addr
|
||||||
|
} else {
|
||||||
|
curr_addr + (rel_addr) - 256
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user