diff --git a/src/main.rs b/src/main.rs index 363a988..ab23599 100644 --- a/src/main.rs +++ b/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, diff --git a/src/nes/cpu.rs b/src/nes/cpu.rs index 6d384bd..8b1e407 100644 --- a/src/nes/cpu.rs +++ b/src/nes/cpu.rs @@ -237,16 +237,22 @@ impl CPU { (addr, false) } + // Absolute address on zero page fn am_ZP0(&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(&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(&mut self, bus: &T) -> (Word, bool) { - unimplemented!() + let addr = self.readb_pc(bus) + self.regs.y; + (addr as Word & 0x0ff, false) } fn am_REL(&mut self, bus: &T) -> (Word, bool) { @@ -264,26 +270,74 @@ impl CPU { } fn am_ABX(&mut self, bus: &T) -> (Word, bool) { - unimplemented!() + let addr = self.readw_pc(bus) + self.regs.x as Word; + (addr, false) } fn am_ABY(&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(&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(&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(&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(&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(&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(&mut self, bus: &T, val: Word) { - unimplemented!() + self.set_flag(DECIMAL, false); } + + // clear IRQ fn op_CLI(&mut self, bus: &T, val: Word) { - unimplemented!() + self.set_flag(IRQ, false); } + // clear Overflow fn op_CLV(&mut self, bus: &T, val: Word) { - unimplemented!() + self.set_flag(OVERFLOW, false); } fn op_CMP(&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(&mut self, bus: &T, val: Word) { - unimplemented!() + // Jump to address (set pc) + fn op_JMP(&mut self, bus: &T, addr: Word) { + self.regs.pc = addr; } - fn op_JSR(&mut self, bus: &T, val: Word) { - unimplemented!() + // Jump to subroutine (leaves trace on the stack) + fn op_JSR(&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(&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(&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(&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(&mut self, bus: &T, val: Word) { - unimplemented!() + // Write flags to stack + fn op_PHP(&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(&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(&mut self, bus: &T, val: Word) { @@ -491,45 +572,64 @@ impl CPU { unimplemented!() } + // set irq flag fn op_SEI(&mut self, bus: &T, val: Word) { - unimplemented!() + self.set_flag(IRQ, true); } + // Push A reg to memory fn op_STA(&mut self, bus: &mut T, addr: Word) { self.writeb(bus, addr, self.regs.a) } + // Push X reg to memory fn op_STX(&mut self, bus: &mut T, addr: Word) { self.writeb(bus, addr, self.regs.x) } + // Push Y reg to memory fn op_STY(&mut self, bus: &mut T, addr: Word) { self.writeb(bus, addr, self.regs.y) } + // a to x fn op_TAX(&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(&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(&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(&mut self, bus: &T, val: Word) { - unimplemented!() - } - - fn op_TXS(&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(&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(&mut self, bus: &T, val: Word) { + self.regs.sp = self.regs.x; + } } impl Debug for CPU { diff --git a/src/nes/disasm.rs b/src/nes/disasm.rs index a2b3777..d4f58f9 100644 --- a/src/nes/disasm.rs +++ b/src/nes/disasm.rs @@ -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, pub addresses: Vec } @@ -15,46 +17,49 @@ impl BusDevice for Disasm {} impl Disasm { // Disassemble given code region - pub fn disassemble(mem: &[Byte], offset: Addr) -> Result { + pub fn disassemble(mem: &MemoryBus, start: Addr, stop: Addr) -> Result { 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) + } } \ No newline at end of file