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 nes::types::*;
|
||||
use nes::cpu::*;
|
||||
use nes::bus::{MemoryBus, Bus};
|
||||
use nes::bus::*;
|
||||
use nes::disasm::*;
|
||||
use opengl_graphics::OpenGL;
|
||||
use log::Level;
|
||||
use piston_window::Text;
|
||||
@@ -25,15 +26,17 @@ fn main() {
|
||||
// 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,
|
||||
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() {
|
||||
let addr = i + offset;
|
||||
bus.writeb(addr as u16, test_prog[i])
|
||||
let addr = (i as Addr) + offset;
|
||||
bus.writeb(addr, test_prog[i])
|
||||
}
|
||||
|
||||
// hint program location to processor
|
||||
bus.writeb(0xfffc, offset as Byte);
|
||||
bus.writeb(0xfffc + 1, (offset >> 8) as Byte);
|
||||
bus.writew(0xfffc, offset);
|
||||
|
||||
// disassemble instructions
|
||||
let disasm = Disasm::disassemble(&test_prog, offset).unwrap();
|
||||
|
||||
// Create and reset CPU
|
||||
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| {
|
||||
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);
|
||||
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 color_white = [1.0; 4];
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#[allow(non_snake_case)]
|
||||
pub mod cpu;
|
||||
pub mod bus;
|
||||
pub mod types;
|
||||
pub mod types;
|
||||
pub mod disasm;
|
||||
@@ -1,11 +1,35 @@
|
||||
use crate::nes::types::*;
|
||||
|
||||
// Generic interface describing a Bus
|
||||
pub trait Bus {
|
||||
fn readb(&self, addr: Addr) -> Byte;
|
||||
fn readw(&self, addr: Addr) -> Word;
|
||||
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 {
|
||||
ram: [Byte; 0xFFFF]
|
||||
}
|
||||
@@ -33,4 +57,9 @@ impl Bus for MemoryBus {
|
||||
fn writeb(&mut self, addr: Addr, data: Byte) {
|
||||
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 core::fmt::{Debug,Formatter,Result};
|
||||
use crate::nes::bus::Bus;
|
||||
use crate::nes::bus::*;
|
||||
use crate::nes::types::*;
|
||||
use log::{debug};
|
||||
|
||||
@@ -52,8 +52,26 @@ pub struct CPU {
|
||||
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 {
|
||||
pub fn new() -> CPU {
|
||||
pub fn new() -> Self {
|
||||
CPU {
|
||||
regs: Registers::new(),
|
||||
cycles: 0,
|
||||
@@ -91,18 +109,6 @@ impl CPU {
|
||||
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
|
||||
fn readb_pc<T: Bus>(&mut self, bus: &T) -> Byte {
|
||||
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 {
|
||||
let (value, page_cross) = match &i.addr_mode {
|
||||
AddrMode::IMP => self.am_IMP(bus),
|
||||
|
||||
@@ -2,7 +2,7 @@ use failure::err_msg;
|
||||
use phf::{Map,phf_map};
|
||||
use failure::{Error};
|
||||
use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
use std::fmt::{Debug,Display};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AddrMode {
|
||||
@@ -20,6 +20,12 @@ pub enum AddrMode {
|
||||
IZY,
|
||||
}
|
||||
|
||||
impl fmt::Display for AddrMode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Operation {
|
||||
@@ -81,6 +87,12 @@ pub enum Operation {
|
||||
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! {
|
||||
// 0x00
|
||||
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