From 59508ac489ca8acc03c18ebdf830358f2054dc6f Mon Sep 17 00:00:00 2001 From: Daniel Bauer Date: Tue, 24 Dec 2019 11:40:30 +0100 Subject: [PATCH] disassembler --- src/main.rs | 37 ++++++++++++++------ src/nes.rs | 3 +- src/nes/bus.rs | 29 ++++++++++++++++ src/nes/cpu.rs | 49 +++++++++++--------------- src/nes/cpu/instructions.rs | 14 +++++++- src/nes/disasm.rs | 69 +++++++++++++++++++++++++++++++++++++ 6 files changed, 161 insertions(+), 40 deletions(-) diff --git a/src/main.rs b/src/main.rs index da370e4..0f3102d 100644 --- a/src/main.rs +++ b/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) { diff --git a/src/nes.rs b/src/nes.rs index 7407f4d..0eef856 100644 --- a/src/nes.rs +++ b/src/nes.rs @@ -1,4 +1,5 @@ #[allow(non_snake_case)] pub mod cpu; pub mod bus; -pub mod types; \ No newline at end of file +pub mod types; +pub mod disasm; \ No newline at end of file diff --git a/src/nes/bus.rs b/src/nes/bus.rs index 4e30e58..a3a3d37 100644 --- a/src/nes/bus.rs +++ b/src/nes/bus.rs @@ -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(&self, bus: &T, addr: Addr) -> Byte { + bus.readb(addr) + } + + fn readw(&self, bus: &T, addr: Addr) -> Word { + bus.readw(addr) + } + + fn writeb(&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(&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); + } } diff --git a/src/nes/cpu.rs b/src/nes/cpu.rs index 491c048..80421d1 100644 --- a/src/nes/cpu.rs +++ b/src/nes/cpu.rs @@ -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(&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(&self, bus: &T, addr: Addr) -> Byte { - bus.readb(addr) - } - - fn readw(&self, bus: &T, addr: Addr) -> Word { - bus.readw(addr) - } - - fn writeb(&mut self, bus: &mut T, addr: Addr, data: Byte) { - bus.writeb(addr, data) - } - // read the next opcode and increment pc fn readb_pc(&mut self, bus: &T) -> Byte { let val = self.readb(bus, self.regs.pc); @@ -135,19 +141,6 @@ impl CPU { } } - - pub fn clock(&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(&mut self, bus: &mut T, i: &Instruction) -> u8 { let (value, page_cross) = match &i.addr_mode { AddrMode::IMP => self.am_IMP(bus), diff --git a/src/nes/cpu/instructions.rs b/src/nes/cpu/instructions.rs index 4b2485b..da0c45a 100644 --- a/src/nes/cpu/instructions.rs +++ b/src/nes/cpu/instructions.rs @@ -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 = phf_map! { // 0x00 0x00u8 => Instruction { opcode: 0x00, addr_mode: AddrMode::IMM, operation: Operation::BRK, cycles: [7, 0] }, diff --git a/src/nes/disasm.rs b/src/nes/disasm.rs index e69de29..991c55c 100644 --- a/src/nes/disasm.rs +++ b/src/nes/disasm.rs @@ -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, +} + +impl BusDevice for Disasm {} +impl Clockable for Disasm { + fn clock(&mut self, bus: &mut T) { + // TODO find new pc location + } +} + +impl Disasm { + // Disassemble given code region + pub fn disassemble(mem: &[Byte], offset: Addr) -> Result { + 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 + } + } + +} \ No newline at end of file