more operations, better debugging
This commit is contained in:
36
src/main.rs
36
src/main.rs
@@ -34,16 +34,19 @@ fn get_glyphs(window: &mut PistonWindow) -> Glyphs {
|
||||
fn main() {
|
||||
simple_logger::init_with_level(Level::Debug).unwrap();
|
||||
|
||||
let mut bus = MemoryBus::new();
|
||||
let cartridge = Path::new("test_roms/registers.nes");
|
||||
let cartridge = Cartridge::new(cartridge).unwrap();
|
||||
|
||||
let mut bus = MemoryBus::new();
|
||||
bus.insert_cartrige(cartridge);
|
||||
|
||||
|
||||
// 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: Addr = 0x8000;
|
||||
// // craft test cartrige
|
||||
// let mut cart = Cartridge::dummy();
|
||||
// bus.insert_cartrige(cart);
|
||||
// for i in 0..test_prog.len() {
|
||||
// let addr = (i as Addr) + offset;
|
||||
// bus.writeb(addr, test_prog[i])
|
||||
@@ -52,8 +55,10 @@ fn main() {
|
||||
// bus.writew(0xfffc, offset);
|
||||
|
||||
// disassemble instructions
|
||||
// let disasm = Disasm::disassemble(&test_prog, offset).unwrap();
|
||||
let disasm = Disasm::disassemble(&[], 0x000).unwrap();
|
||||
let disasm = Disasm::disassemble(&bus, 0xe1ff, 0xffff-2).unwrap();
|
||||
// println!("{:?}", disasm.instructions);
|
||||
// return;
|
||||
// let disasm = Disasm::disassemble(&[], 0x000).unwrap();
|
||||
|
||||
// Create and reset CPU
|
||||
let mut cpu: CPU = CPU::new();
|
||||
@@ -81,6 +86,7 @@ fn main() {
|
||||
cpu.clock(&mut bus);
|
||||
}
|
||||
}
|
||||
Key::R => cpu.reset(&mut bus),
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
@@ -209,16 +215,32 @@ fn render_cpu(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, cpu: &CPU, offset:
|
||||
}
|
||||
|
||||
fn render_disasm(c: &Context, g: &mut G2d, glyphs: &mut Glyphs, disasm: &Disasm, pc: Addr, offset: [f64; 2]) {
|
||||
let pc_position = disasm.addresses.iter().position(|&pos| pos == pc);
|
||||
if let None = pc_position {
|
||||
return;
|
||||
}
|
||||
let pc_position = pc_position.unwrap();
|
||||
let lines_above_below: usize = 12;
|
||||
|
||||
let start = if (pc_position as i32 - lines_above_below as i32) < 0 {
|
||||
0
|
||||
} else {
|
||||
pc_position - lines_above_below
|
||||
};
|
||||
|
||||
let mut transform = c.transform.trans(offset[0], offset[1]);
|
||||
for (text, addr) in disasm.instructions.iter().zip(disasm.addresses.iter()) {
|
||||
for i in (start..disasm.addresses.len()).take(lines_above_below*2+1) {
|
||||
let addr = disasm.addresses[i];
|
||||
let txt = &disasm.instructions[i];
|
||||
|
||||
transform = transform.trans(0.0, FT_LINE_DISTANCE + FT_SIZE_PX);
|
||||
let color = if *addr == pc {
|
||||
let color = if addr == pc {
|
||||
FT_COLOR_RED
|
||||
} else {
|
||||
FT_COLOR_WHITE
|
||||
};
|
||||
Text::new_color(color, FT_SIZE_PT).draw(
|
||||
&text,
|
||||
txt,
|
||||
glyphs,
|
||||
&c.draw_state,
|
||||
transform,
|
||||
|
||||
162
src/nes/cpu.rs
162
src/nes/cpu.rs
@@ -237,16 +237,22 @@ impl CPU {
|
||||
(addr, false)
|
||||
}
|
||||
|
||||
// Absolute address on zero page
|
||||
fn am_ZP0<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
||||
unimplemented!()
|
||||
let addr = self.readb_pc(bus);
|
||||
(addr as Word & 0x0ff, false)
|
||||
}
|
||||
|
||||
// Absolute address on zero page with x offset
|
||||
fn am_ZPX<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
||||
unimplemented!()
|
||||
let addr = self.readb_pc(bus) + self.regs.x;
|
||||
(addr as Word & 0x0ff, false)
|
||||
}
|
||||
|
||||
// Absolute address on zero page with y offset
|
||||
fn am_ZPY<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
||||
unimplemented!()
|
||||
let addr = self.readb_pc(bus) + self.regs.y;
|
||||
(addr as Word & 0x0ff, false)
|
||||
}
|
||||
|
||||
fn am_REL<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
||||
@@ -264,26 +270,74 @@ impl CPU {
|
||||
}
|
||||
|
||||
fn am_ABX<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
||||
unimplemented!()
|
||||
let addr = self.readw_pc(bus) + self.regs.x as Word;
|
||||
(addr, false)
|
||||
}
|
||||
|
||||
fn am_ABY<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
||||
unimplemented!()
|
||||
let addr = self.readw_pc(bus) + self.regs.y as Word;
|
||||
(addr, false)
|
||||
}
|
||||
|
||||
// the next 16 bits are an address. This address stores the real address
|
||||
// that is used for the operation.
|
||||
// Hardware bug: Normally, if lo of the supplied address is 0xFF, high byte
|
||||
// must be read from the next page. Instead it wraps around and reads from
|
||||
// the same page!
|
||||
fn am_IND<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
||||
unimplemented!()
|
||||
let mut ind_addr = self.readw_pc(bus);
|
||||
|
||||
let lo = ind_addr & 0b00001111;
|
||||
if lo == 0x00FF {
|
||||
ind_addr -= 0x00FF;
|
||||
}
|
||||
|
||||
|
||||
let addr = bus.readw(ind_addr);
|
||||
(addr, false)
|
||||
}
|
||||
|
||||
// the next 16 bits + x are an address. This address stores the real address
|
||||
// that is used for the operation.
|
||||
// Hardware bug: Normally, if lo of the supplied address is 0xFF, high byte
|
||||
// must be read from the next page. Instead it wraps around and reads from
|
||||
// the same page!
|
||||
fn am_IZX<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
||||
unimplemented!()
|
||||
let mut ind_addr = self.readw_pc(bus);
|
||||
ind_addr += self.regs.x as Word;
|
||||
|
||||
let lo = ind_addr & 0b00001111;
|
||||
if lo == 0x00FF {
|
||||
ind_addr -= 0x00FF;
|
||||
}
|
||||
|
||||
|
||||
let addr = bus.readw(ind_addr);
|
||||
(addr, false)
|
||||
}
|
||||
|
||||
// the next 16 bits + y are an address. This address stores the real address
|
||||
// that is used for the operation.
|
||||
// Hardware bug: Normally, if lo of the supplied address is 0xFF, high byte
|
||||
// must be read from the next page. Instead it wraps around and reads from
|
||||
// the same page!
|
||||
fn am_IZY<T: Memory>(&mut self, bus: &T) -> (Word, bool) {
|
||||
unimplemented!()
|
||||
}
|
||||
let mut ind_addr = self.readw_pc(bus);
|
||||
ind_addr += self.regs.y as Word;
|
||||
|
||||
let lo = ind_addr & 0b00001111;
|
||||
if lo == 0x00FF {
|
||||
ind_addr -= 0x00FF;
|
||||
}
|
||||
|
||||
|
||||
let addr = bus.readw(ind_addr);
|
||||
(addr, false)
|
||||
}
|
||||
|
||||
// Operations
|
||||
|
||||
// Add to A
|
||||
fn op_ADC<T: Memory>(&mut self, bus: &T, addr: Word) {
|
||||
let val = self.readb(bus, addr) as Word;
|
||||
let tmp = (self.regs.a as Word) + val + (self.get_flag(CARRY) as Word);
|
||||
@@ -322,6 +376,7 @@ impl CPU {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// Jump if 0
|
||||
fn op_BNE<T: Memory>(&mut self, bus: &T, addr: Word) {
|
||||
if self.get_flag(ZERO) == 0 {
|
||||
let old_addr = self.regs.pc;
|
||||
@@ -346,20 +401,25 @@ impl CPU {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// Clear carry flag
|
||||
fn op_CLC(&mut self) {
|
||||
self.set_flag(CARRY, false);
|
||||
}
|
||||
|
||||
|
||||
// clear decimal flag
|
||||
fn op_CLD<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||
unimplemented!()
|
||||
self.set_flag(DECIMAL, false);
|
||||
}
|
||||
|
||||
|
||||
// clear IRQ
|
||||
fn op_CLI<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||
unimplemented!()
|
||||
self.set_flag(IRQ, false);
|
||||
}
|
||||
|
||||
// clear Overflow
|
||||
fn op_CLV<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||
unimplemented!()
|
||||
self.set_flag(OVERFLOW, false);
|
||||
}
|
||||
|
||||
fn op_CMP<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||
@@ -378,12 +438,14 @@ impl CPU {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// Decrement X
|
||||
fn op_DEX(&mut self) {
|
||||
self.regs.x -= 1;
|
||||
self.set_flag(ZERO, self.regs.x == 0);
|
||||
self.set_flag(NEGATIVE, (self.regs.x & 0x80) != 0)
|
||||
}
|
||||
|
||||
// Decrement Y
|
||||
fn op_DEY(&mut self) {
|
||||
self.regs.y -= 1;
|
||||
self.set_flag(ZERO, self.regs.y == 0);
|
||||
@@ -406,14 +468,22 @@ impl CPU {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn op_JMP<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||
unimplemented!()
|
||||
// Jump to address (set pc)
|
||||
fn op_JMP<T: Memory>(&mut self, bus: &T, addr: Word) {
|
||||
self.regs.pc = addr;
|
||||
}
|
||||
|
||||
fn op_JSR<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||
unimplemented!()
|
||||
// Jump to subroutine (leaves trace on the stack)
|
||||
fn op_JSR<T: Memory>(&mut self, bus: &mut T, addr: Word) {
|
||||
self.regs.pc -= 1;
|
||||
bus.writeb(0x0100 + self.regs.sp as Word, ((self.regs.pc >> 8) & 0x00ff) as Byte);
|
||||
self.regs.sp -= 1;
|
||||
bus.writeb(0x0100 + self.regs.sp as Word, (self.regs.pc & 0x00ff) as Byte);
|
||||
self.regs.sp -= 1;
|
||||
self.regs.pc = addr;
|
||||
}
|
||||
|
||||
// Read value from addr into A
|
||||
fn op_LDA<T: Memory>(&mut self, bus: &T, addr: Word) {
|
||||
let val = bus.readb(addr);
|
||||
self.regs.a = val;
|
||||
@@ -421,6 +491,7 @@ impl CPU {
|
||||
self.set_flag(NEGATIVE, (val & 0x80) != 0);
|
||||
}
|
||||
|
||||
// Read value from addr into X
|
||||
fn op_LDX<T: Memory>(&mut self, bus: &T, addr: Word) {
|
||||
let val = bus.readb(addr);
|
||||
self.regs.x = val;
|
||||
@@ -428,6 +499,7 @@ impl CPU {
|
||||
self.set_flag(NEGATIVE, (val & 0x80) != 0);
|
||||
}
|
||||
|
||||
// Read value from addr into Y
|
||||
fn op_LDY<T: Memory>(&mut self, bus: &T, addr: Word) {
|
||||
let val = bus.readb(addr);
|
||||
self.regs.y = val;
|
||||
@@ -451,12 +523,21 @@ impl CPU {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn op_PHP<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||
unimplemented!()
|
||||
// Write flags to stack
|
||||
fn op_PHP<T: Memory>(&mut self, bus: &mut T, val: Word) {
|
||||
let tmp = self.regs.flags | BREAK | UNUSED;
|
||||
bus.writeb(0x0100 + self.regs.sp as Word, tmp);
|
||||
self.set_flag(BREAK, false);
|
||||
self.set_flag(UNUSED, false);
|
||||
self.regs.sp -= 1;
|
||||
}
|
||||
|
||||
// Read from stack into A
|
||||
fn op_PLA<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||
unimplemented!()
|
||||
self.regs.sp += 1;
|
||||
self.regs.a = bus.readb(0x0100 + self.regs.sp as Word);
|
||||
self.set_flag(ZERO, self.regs.a == 0);
|
||||
self.set_flag(NEGATIVE, (self.regs.a & 0x80) == 1)
|
||||
}
|
||||
|
||||
fn op_PLP<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||
@@ -491,45 +572,64 @@ impl CPU {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// set irq flag
|
||||
fn op_SEI<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||
unimplemented!()
|
||||
self.set_flag(IRQ, true);
|
||||
}
|
||||
|
||||
// Push A reg to memory
|
||||
fn op_STA<T: Memory>(&mut self, bus: &mut T, addr: Word) {
|
||||
self.writeb(bus, addr, self.regs.a)
|
||||
}
|
||||
|
||||
// Push X reg to memory
|
||||
fn op_STX<T: Memory>(&mut self, bus: &mut T, addr: Word) {
|
||||
self.writeb(bus, addr, self.regs.x)
|
||||
}
|
||||
|
||||
// Push Y reg to memory
|
||||
fn op_STY<T: Memory>(&mut self, bus: &mut T, addr: Word) {
|
||||
self.writeb(bus, addr, self.regs.y)
|
||||
}
|
||||
|
||||
// a to x
|
||||
fn op_TAX<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||
unimplemented!()
|
||||
self.regs.x = self.regs.a;
|
||||
self.set_flag(ZERO, self.regs.x == 0);
|
||||
self.set_flag(NEGATIVE, (self.regs.x & 0x80) == 1)
|
||||
}
|
||||
|
||||
// a to y
|
||||
fn op_TAY<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||
unimplemented!()
|
||||
self.regs.y = self.regs.a;
|
||||
self.set_flag(ZERO, self.regs.y == 0);
|
||||
self.set_flag(NEGATIVE, (self.regs.y & 0x80) == 1)
|
||||
}
|
||||
|
||||
fn op_TSX<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||
unimplemented!()
|
||||
self.regs.x = bus.readb(0x0100 + self.regs.sp as Word);
|
||||
self.set_flag(ZERO, self.regs.x == 0);
|
||||
self.set_flag(NEGATIVE, (self.regs.x & 0x80) == 1)
|
||||
}
|
||||
|
||||
// transfer x to a
|
||||
fn op_TXA<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn op_TXS<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||
unimplemented!()
|
||||
self.regs.a = self.regs.x;
|
||||
self.set_flag(ZERO, self.regs.a == 0);
|
||||
self.set_flag(NEGATIVE, (self.regs.a & 0x80) == 1)
|
||||
}
|
||||
|
||||
// transfer y to a
|
||||
fn op_TYA<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||
unimplemented!()
|
||||
}
|
||||
self.regs.a = self.regs.y;
|
||||
self.set_flag(ZERO, self.regs.a == 0);
|
||||
self.set_flag(NEGATIVE, (self.regs.a & 0x80) == 1)
|
||||
}
|
||||
|
||||
// transfer x to stack
|
||||
fn op_TXS<T: Memory>(&mut self, bus: &T, val: Word) {
|
||||
self.regs.sp = self.regs.x;
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for CPU {
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
use crate::nes::cpu::instructions::*;
|
||||
use failure::Error;
|
||||
use std::fmt;
|
||||
use crate::nes::bus::*;
|
||||
use crate::nes::types::*;
|
||||
use crate::nes::cpu::instructions::*;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
|
||||
// Disassemble code around the pc of the cpu
|
||||
pub struct Disasm {
|
||||
pub offset: Addr,
|
||||
pub start: Addr,
|
||||
pub stop: Addr,
|
||||
pub instructions: Vec<String>,
|
||||
pub addresses: Vec<Addr>
|
||||
}
|
||||
@@ -15,46 +17,49 @@ impl BusDevice for Disasm {}
|
||||
|
||||
impl Disasm {
|
||||
// Disassemble given code region
|
||||
pub fn disassemble(mem: &[Byte], offset: Addr) -> Result<Self, Error> {
|
||||
pub fn disassemble(mem: &MemoryBus, start: Addr, stop: Addr) -> Result<Self, Error> {
|
||||
let mut instructions = Vec::new();
|
||||
let mut addresses = 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 mut mem_iter = ((start as usize) .. (stop as usize)+1).map({|a| a as Addr});
|
||||
while let Some(addr) = mem_iter.next() {
|
||||
let opcode = mem.readb(addr);
|
||||
|
||||
// Decode opcode, default to NOP/IMP to "skip" the byte
|
||||
let i = Instruction::decode_op(opcode)
|
||||
.unwrap_or(Instruction::decode_op(0xeau8).unwrap());
|
||||
// debug!("{:?}", i);
|
||||
let args = match i.addr_mode {
|
||||
AddrMode::IMM => format!("#{0:02x} ({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;
|
||||
AddrMode::IMM => format!("#{0:02x} ({0})", mem.readb(mem_iter.next().unwrap())),
|
||||
AddrMode::ZP0 | AddrMode::ZPX | AddrMode::ZPY => {
|
||||
let rel_addr = mem.readb(mem_iter.next().unwrap());
|
||||
format!("{:#04x}", rel_addr)
|
||||
},
|
||||
AddrMode::ABS | AddrMode::ABX | AddrMode::ABY => {
|
||||
let val = mem.readw(mem_iter.next().unwrap());
|
||||
mem_iter.next().unwrap();
|
||||
format!("{:#06x}", val)
|
||||
|
||||
}
|
||||
},
|
||||
AddrMode::REL => {
|
||||
let rel_addr = (*mem_iter.next().unwrap().1) as Addr;
|
||||
let rel_addr = mem.readb(mem_iter.next().unwrap()) as Word;
|
||||
let jmp_addr = Disasm::get_rel_addr(rel_addr, addr+2);
|
||||
format!("#{:02x} => {:#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::IND | AddrMode::IZX | AddrMode::IZY=> {
|
||||
let addr = mem.readw(mem_iter.next().unwrap());
|
||||
mem_iter.next().unwrap();
|
||||
format!("{:#06x}", addr)
|
||||
}
|
||||
AddrMode::IMP => String::from(""),
|
||||
_ => String::from("TODO"),
|
||||
_ => bail!("Disassembly of {} not implemented", i.addr_mode),
|
||||
};
|
||||
|
||||
let s = format!("{:#06x}: {} {} ({})", addr, i.operation, args, i.addr_mode);
|
||||
// debug!("{:#06x} {}", addr, &s);
|
||||
instructions.push(s);
|
||||
addresses.push(addr);
|
||||
}
|
||||
Ok(Disasm { offset, instructions, addresses })
|
||||
Ok(Disasm { start, stop, instructions, addresses })
|
||||
}
|
||||
|
||||
fn get_rel_addr(rel_addr: Addr, curr_addr: Addr) -> Addr {
|
||||
@@ -65,4 +70,11 @@ impl Disasm {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
impl Debug for Disasm {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user