5 changed files with 341 additions and 1 deletions
@ -0,0 +1,79 @@
@@ -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); |
||||
} |
||||
} |
||||
Binary file not shown.
@ -0,0 +1,64 @@
@@ -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(); |
||||
} |
||||
@ -0,0 +1,197 @@
@@ -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<u8, &'static opcodes::OpCode> = *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::<Vec<String>>() |
||||
.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<String> = 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<String> = 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] |
||||
); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue