From f0de24dfc5b4821aecab425028b50a21b6cadfcd Mon Sep 17 00:00:00 2001 From: Stephanie Gredell Date: Tue, 28 Jan 2025 20:51:31 -0800 Subject: [PATCH] stuff --- bus.rs | 79 +++++++++++++++++++++ main.rs | 2 +- nestest.nes | Bin 0 -> 24592 bytes snake.rs | 64 +++++++++++++++++ trace.rs | 197 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 341 insertions(+), 1 deletion(-) create mode 100644 bus.rs create mode 100644 nestest.nes create mode 100644 snake.rs create mode 100644 trace.rs 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 0000000000000000000000000000000000000000..fc2a88c36db9c5c2de9cca33be0e446e12007f17 GIT binary patch literal 24592 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 O2L?Sb=z;&AJn-*m;2p96 literal 0 HcmV?d00001 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] + ); + } +}