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

View File

@@ -1,4 +1,5 @@
#[allow(non_snake_case)]
pub mod cpu;
pub mod bus;
pub mod types;
pub mod types;
pub mod disasm;

View File

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

View File

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

View File

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

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