disassembler

This commit is contained in:
Daniel Bauer
2019-12-24 11:40:30 +01:00
parent f0de1c20ca
commit 59508ac489
6 changed files with 161 additions and 40 deletions

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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);
}
} }

View File

@@ -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),

View File

@@ -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] },

View File

@@ -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
}
}
}