diff --git a/bus.rs b/bus.rs new file mode 100644 index 0000000..98b3974 --- /dev/null +++ b/bus.rs @@ -0,0 +1,79 @@ +use crate::{cartridge::Rom, cpu::Mem}; +const RAM: u16 = 0x0000; +const RAM_MIRRORS_END: u16 = 0x1FFF; +const PPU_REGISTERS: u16 = 0x2000; +const PPU_REGISTERS_MIRRORS_END: u16 = 0x3FFF; + +pub struct Bus { + cpu_vram: [u8; 2048], + rom: Rom, +} + +impl Bus { + pub fn new(rom: Rom) -> Self { + Bus { + cpu_vram: [0; 2048], + rom, + } + } + fn read_prg_rom(&self, mut addr: u16) -> u8 { + addr -= 0x8000; + if self.rom.prg_rom.len() == 0x4000 && addr >= 0x4000 { + addr = addr % 0x4000; + } + self.rom.prg_rom[addr as usize] + } +} + +impl Mem for Bus { + fn mem_read(&self, addr: u16) -> u8 { + match addr { + RAM..=RAM_MIRRORS_END => { + let mirror_down_addr = addr & 0b00000111_11111111; + self.cpu_vram[mirror_down_addr as usize] + } + PPU_REGISTERS..=PPU_REGISTERS_MIRRORS_END => { + let _mirror_down_addr = addr & 0b00100000_00000111; + // todo!("PPU is not supported yet") + 0 + } + 0x8000..=0xFFFF => self.read_prg_rom(addr), + + _ => { + println!("Ignoring mem access at {}", addr); + 0 + } + } + } + + fn mem_write(&mut self, addr: u16, data: u8) { + match addr { + RAM..=RAM_MIRRORS_END => { + let mirror_down_addr = addr & 0b11111111111; + self.cpu_vram[mirror_down_addr as usize] = data; + } + PPU_REGISTERS..=PPU_REGISTERS_MIRRORS_END => { + let _mirror_down_addr = addr & 0b00100000_00000111; + } + 0x8000..=0xFFFF => { + panic!("Attempt to write to ROM space") + } + _ => { + println!("Ignoring mem write-access at {}", addr); + } + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::cartridge::test; + + #[test] + fn test_mem_read_write_to_ram() { + let mut bus = Bus::new(test::test_rom(vec![])); + bus.mem_write(0x01, 0x55); + assert_eq!(bus.mem_read(0x01), 0x55); + } +} diff --git a/main.rs b/main.rs index b7fcaef..01fd858 100644 --- a/main.rs +++ b/main.rs @@ -8,7 +8,7 @@ use sdl2::keyboard::Keycode; use sdl2::pixels::Color; use sdl2::pixels::PixelFormatEnum; use sdl2::EventPump; -mod assembly; +mod cartridge; extern crate lazy_static; diff --git a/nestest.nes b/nestest.nes new file mode 100644 index 0000000..fc2a88c Binary files /dev/null and b/nestest.nes differ diff --git a/snake.rs b/snake.rs new file mode 100644 index 0000000..ac12d7d --- /dev/null +++ b/snake.rs @@ -0,0 +1,64 @@ +use std::fs::File; +use std::io::prelude::*; + +fn create_fake_rom(file_name: String) { + let mut buffer = File::create(file_name).unwrap(); + let header = vec![ + 0x4E, 0x45, 0x53, 0x1A, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + ]; + + let pre = [0; 0x600]; + let code = vec![ + 0x20, 0x06, 0x86, 0x20, 0x38, 0x86, 0x20, 0x0d, 0x86, 0x20, 0x2a, 0x86, 0x60, 0xa9, 0x02, + 0x85, 0x02, 0xa9, 0x06, 0x85, 0x03, 0xa9, 0x11, 0x85, 0x10, 0xa9, 0x10, 0x85, 0x12, 0xa9, + 0x0f, 0x85, 0x14, 0xa9, 0x04, 0x85, 0x11, 0x85, 0x13, 0x85, 0x15, 0x60, 0xa5, 0xfe, 0x85, + 0x00, 0xa5, 0xfe, 0x29, 0x03, 0x18, 0x69, 0x02, 0x85, 0x01, 0x60, 0x20, 0x4d, 0x86, 0x20, + 0x8d, 0x86, 0x20, 0xc3, 0x86, 0x20, 0x19, 0x87, 0x20, 0x20, 0x87, 0x20, 0x2d, 0x87, 0x4c, + 0x38, 0x86, 0xa5, 0xff, 0xc9, 0x77, 0xf0, 0x0d, 0xc9, 0x64, 0xf0, 0x14, 0xc9, 0x73, 0xf0, + 0x1b, 0xc9, 0x61, 0xf0, 0x22, 0x60, 0xa9, 0x04, 0x24, 0x02, 0xd0, 0x26, 0xa9, 0x01, 0x85, + 0x02, 0x60, 0xa9, 0x08, 0x24, 0x02, 0xd0, 0x1b, 0xa9, 0x02, 0x85, 0x02, 0x60, 0xa9, 0x01, + 0x24, 0x02, 0xd0, 0x10, 0xa9, 0x04, 0x85, 0x02, 0x60, 0xa9, 0x02, 0x24, 0x02, 0xd0, 0x05, + 0xa9, 0x08, 0x85, 0x02, 0x60, 0x60, 0x20, 0x94, 0x86, 0x20, 0xa8, 0x86, 0x60, 0xa5, 0x00, + 0xc5, 0x10, 0xd0, 0x0d, 0xa5, 0x01, 0xc5, 0x11, 0xd0, 0x07, 0xe6, 0x03, 0xe6, 0x03, 0x20, + 0x2a, 0x86, 0x60, 0xa2, 0x02, 0xb5, 0x10, 0xc5, 0x10, 0xd0, 0x06, 0xb5, 0x11, 0xc5, 0x11, + 0xf0, 0x09, 0xe8, 0xe8, 0xe4, 0x03, 0xf0, 0x06, 0x4c, 0xaa, 0x86, 0x4c, 0x35, 0x87, 0x60, + 0xa6, 0x03, 0xca, 0x8a, 0xb5, 0x10, 0x95, 0x12, 0xca, 0x10, 0xf9, 0xa5, 0x02, 0x4a, 0xb0, + 0x09, 0x4a, 0xb0, 0x19, 0x4a, 0xb0, 0x1f, 0x4a, 0xb0, 0x2f, 0xa5, 0x10, 0x38, 0xe9, 0x20, + 0x85, 0x10, 0x90, 0x01, 0x60, 0xc6, 0x11, 0xa9, 0x01, 0xc5, 0x11, 0xf0, 0x28, 0x60, 0xe6, + 0x10, 0xa9, 0x1f, 0x24, 0x10, 0xf0, 0x1f, 0x60, 0xa5, 0x10, 0x18, 0x69, 0x20, 0x85, 0x10, + 0xb0, 0x01, 0x60, 0xe6, 0x11, 0xa9, 0x06, 0xc5, 0x11, 0xf0, 0x0c, 0x60, 0xc6, 0x10, 0xa5, + 0x10, 0x29, 0x1f, 0xc9, 0x1f, 0xf0, 0x01, 0x60, 0x4c, 0x35, 0x87, 0xa0, 0x00, 0xa5, 0xfe, + 0x91, 0x00, 0x60, 0xa6, 0x03, 0xa9, 0x00, 0x81, 0x10, 0xa2, 0x00, 0xa9, 0x01, 0x81, 0x10, + 0x60, 0xa2, 0x00, 0xea, 0xea, 0xca, 0xd0, 0xfb, 0x60, + ]; + + let mut pos = 0; + while pos < header.len() { + let bytes_written = buffer.write(&header[pos..]).unwrap(); + pos += bytes_written; + } + + pos = 0; + + while pos < header.len() { + let bytes_written = buffer.write(&pre[pos..]).unwrap(); + pos += bytes_written; + } + + pos = 0; + while pos < header.len() { + let bytes_written = buffer.write(&code[pos..]).unwrap(); + pos += bytes_written; + } + + pos = 0x600 + code.len(); + + while pos < (0xFFFC - 0x8000) { + buffer.write(&[0]).unwrap(); + pos += 1; + } + buffer.write(&[0x0, 0x86, 0, 0]).unwrap(); + + buffer.flush().unwrap(); +} diff --git a/trace.rs b/trace.rs new file mode 100644 index 0000000..205de37 --- /dev/null +++ b/trace.rs @@ -0,0 +1,197 @@ +use crate::cpu::AddressingMode; +use crate::cpu::Mem; +use crate::cpu::CPU; +use crate::opcodes; +use std::collections::HashMap; + +pub fn trace(cpu: &CPU) -> String { + let ref opscodes: HashMap = *opcodes::OPCODES_MAP; + + let code = cpu.mem_read(cpu.program_counter); + let ops = opscodes.get(&code).unwrap(); + + let begin = cpu.program_counter; + let mut hex_dump = vec![]; + hex_dump.push(code); + + let (mem_addr, stored_value) = match ops.mode { + AddressingMode::Immediate | AddressingMode::NoneAddressing => (0, 0), + _ => { + let addr = cpu.get_absolute_address(&ops.mode, begin + 1); + (addr, cpu.mem_read(addr)) + } + }; + + let tmp = match ops.len { + 1 => match ops.code { + 0x0a | 0x4a | 0x2a | 0x6a => format!("A "), + _ => String::from(""), + }, + 2 => { + let address: u8 = cpu.mem_read(begin + 1); + // let value = cpu.mem_read(address)); + hex_dump.push(address); + + match ops.mode { + AddressingMode::Immediate => format!("#${:02x}", address), + AddressingMode::ZeroPage => format!("${:02x} = {:02x}", mem_addr, stored_value), + AddressingMode::ZeroPage_X => format!( + "${:02x},X @ {:02x} = {:02x}", + address, mem_addr, stored_value + ), + AddressingMode::ZeroPage_Y => format!( + "${:02x},Y @ {:02x} = {:02x}", + address, mem_addr, stored_value + ), + AddressingMode::Indirect_X => format!( + "(${:02x},X) @ {:02x} = {:04x} = {:02x}", + address, + (address.wrapping_add(cpu.register_x)), + mem_addr, + stored_value + ), + AddressingMode::Indirect_Y => format!( + "(${:02x}),Y = {:04x} @ {:04x} = {:02x}", + address, + (mem_addr.wrapping_sub(cpu.register_y as u16)), + mem_addr, + stored_value + ), + AddressingMode::NoneAddressing => { + // assuming local jumps: BNE, BVS, etc.... + let address: usize = + (begin as usize + 2).wrapping_add((address as i8) as usize); + format!("${:04x}", address) + } + + _ => panic!( + "unexpected addressing mode {:?} has ops-len 2. code {:02x}", + ops.mode, ops.code + ), + } + } + 3 => { + let address_lo = cpu.mem_read(begin + 1); + let address_hi = cpu.mem_read(begin + 2); + hex_dump.push(address_lo); + hex_dump.push(address_hi); + + let address = cpu.mem_read_u16(begin + 1); + + match ops.mode { + AddressingMode::NoneAddressing => { + if ops.code == 0x6c { + //jmp indirect + let jmp_addr = if address & 0x00FF == 0x00FF { + let lo = cpu.mem_read(address); + let hi = cpu.mem_read(address & 0xFF00); + (hi as u16) << 8 | (lo as u16) + } else { + cpu.mem_read_u16(address) + }; + + // let jmp_addr = cpu.mem_read_u16(address); + format!("(${:04x}) = {:04x}", address, jmp_addr) + } else { + format!("${:04x}", address) + } + } + AddressingMode::Absolute => format!("${:04x} = {:02x}", mem_addr, stored_value), + AddressingMode::Absolute_X => format!( + "${:04x},X @ {:04x} = {:02x}", + address, mem_addr, stored_value + ), + AddressingMode::Absolute_Y => format!( + "${:04x},Y @ {:04x} = {:02x}", + address, mem_addr, stored_value + ), + _ => panic!( + "unexpected addressing mode {:?} has ops-len 3. code {:02x}", + ops.mode, ops.code + ), + } + } + _ => String::from(""), + }; + + let hex_str = hex_dump + .iter() + .map(|z| format!("{:02x}", z)) + .collect::>() + .join(" "); + let asm_str = format!("{:04x} {:8} {: >4} {}", begin, hex_str, ops.mnemonic, tmp) + .trim() + .to_string(); + + format!( + "{:47} A:{:02x} X:{:02x} Y:{:02x} P:{:02x} SP:{:02x}", + asm_str, cpu.register_a, cpu.register_x, cpu.register_y, cpu.status, cpu.stack_pointer, + ) + .to_ascii_uppercase() +} + +#[cfg(test)] +mod test { + use super::*; + use crate::bus::Bus; + use crate::cartridge::test::test_rom; + + #[test] + fn test_format_trace() { + let mut bus = Bus::new(test_rom(vec![])); + bus.mem_write(100, 0xa2); + bus.mem_write(101, 0x01); + bus.mem_write(102, 0xca); + bus.mem_write(103, 0x88); + bus.mem_write(104, 0x00); + + let mut cpu = CPU::new(bus); + cpu.program_counter = 0x64; + cpu.register_a = 1; + cpu.register_x = 2; + cpu.register_y = 3; + let mut result: Vec = vec![]; + cpu.run_with_callback(|cpu| { + result.push(trace(cpu)); + }); + assert_eq!( + "0064 A2 01 LDX #$01 A:01 X:02 Y:03 P:24 SP:FD", + result[0] + ); + assert_eq!( + "0066 CA DEX A:01 X:01 Y:03 P:24 SP:FD", + result[1] + ); + assert_eq!( + "0067 88 DEY A:01 X:00 Y:03 P:26 SP:FD", + result[2] + ); + } + + #[test] + fn test_format_mem_access() { + let mut bus = Bus::new(test_rom(vec![])); + // ORA ($33), Y + bus.mem_write(100, 0x11); + bus.mem_write(101, 0x33); + + //data + bus.mem_write(0x33, 00); + bus.mem_write(0x34, 04); + + //target cell + bus.mem_write(0x400, 0xAA); + + let mut cpu = CPU::new(bus); + cpu.program_counter = 0x64; + cpu.register_y = 0; + let mut result: Vec = vec![]; + cpu.run_with_callback(|cpu| { + result.push(trace(cpu)); + }); + assert_eq!( + "0064 11 33 ORA ($33),Y = 0400 @ 0400 = AA A:00 X:00 Y:00 P:24 SP:FD", + result[0] + ); + } +}