diff --git a/aliusnes/build.rs b/aliusnes/build.rs index a7e6488..82155a1 100644 --- a/aliusnes/build.rs +++ b/aliusnes/build.rs @@ -1,45 +1,24 @@ use heck::ToSnakeCase; -use std::{ - env, - fs::File, - io::Write, - path::{Path, PathBuf}, -}; - -enum Proc { - W65816, - Spc700, -} +use std::{env, fs::File, io::Write}; fn main() { println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=tests"); - generate_tomharte_opcode_test(Proc::W65816); - generate_tomharte_opcode_test(Proc::Spc700); + generate_tomharte_opcode_test("tomharte_65816.rs"); + generate_tomharte_opcode_test("tomharte_spc700.rs"); - let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let base_path = root_dir.join("tests/krom/"); - let mut file = File::create(Path::new(&env::var_os("OUT_DIR").unwrap()).join("krom_test.rs")) - .expect("File creation failed"); + let base_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/krom/"); + let mut file = create_file_in_out_dir("krom_test.rs"); visit_dirs(&base_path, &mut file).unwrap(); } -fn generate_tomharte_opcode_test(proc: Proc) { - let (file_name, interface) = match proc { - Proc::W65816 => ("tomharte_65816.rs", "CpuState"), - Proc::Spc700 => ("tomharte_spc700.rs", "Spc700State"), - }; - - let mut file = File::create(Path::new(&env::var_os("OUT_DIR").unwrap()).join(file_name)) - .expect("File creation failed"); +fn generate_tomharte_opcode_test(file_name: &'static str) { + let mut file = create_file_in_out_dir(file_name); for i in 0..256 { let test_name = format!("test_{i:02x}"); - let test_body = match proc { - Proc::W65816 => format!("run_test::<{interface}>(\"{i:02x}.n\");"), - Proc::Spc700 => format!("run_test::<{interface}>(\"{i:02x}\");"), - }; + let test_body = format!("run_test::(\"{i:02x}\");"); let test = format!("#[test]\nfn {test_name}() {{\n\t{test_body}\n}}\n\n"); file.write_all(test.as_bytes()) @@ -47,6 +26,11 @@ fn generate_tomharte_opcode_test(proc: Proc) { } } +fn create_file_in_out_dir(file_name: &'static str) -> File { + let file_path = std::path::Path::new(&env::var_os("OUT_DIR").unwrap()).join(file_name); + File::create(file_path).expect("File creation failed") +} + fn generate_krom_test(file: &mut File, path: &str, name: &str) { let rom_path = format!("{path}/{name}.sfc"); let png_path = format!("{path}/{name}.png"); @@ -59,7 +43,7 @@ fn generate_krom_test(file: &mut File, path: &str, name: &str) { .expect("Write to file failed"); } -fn visit_dirs(dir: &Path, file: &mut File) -> std::io::Result<()> { +fn visit_dirs(dir: &std::path::Path, file: &mut File) -> std::io::Result<()> { if dir.is_dir() { let mut has_subdir = false; for entry in dir.read_dir()? { diff --git a/aliusnes/src/apu/spc700/mod.rs b/aliusnes/src/apu/spc700/mod.rs index d94af84..23da9f5 100644 --- a/aliusnes/src/apu/spc700/mod.rs +++ b/aliusnes/src/apu/spc700/mod.rs @@ -1,330 +1,143 @@ use cpu::Cpu; +use opcode::{opcode_table, OpCode}; use crate::bus::Bus; mod addressing; -pub mod cpu; +mod cpu; mod instructions; +mod opcode; -#[derive(Clone, Copy)] -pub struct Meta { - pub code: u8, - pub mnemonic: &'static str, - pub mode: addressing::AddressingMode, -} - -impl Meta { - const fn new(code: u8, mnemonic: &'static str, mode: addressing::AddressingMode) -> Self { - Self { - code, - mnemonic, - mode, - } - } -} - -pub struct OpCode { - pub meta: Meta, - pub function: fn(&mut cpu::Cpu, &mut B, addressing::AddressingMode), -} - -impl OpCode { - const fn new( - meta: Meta, - function: fn(&mut cpu::Cpu, &mut B, addressing::AddressingMode), - ) -> Self { - OpCode { meta, function } - } -} - -pub struct Spc700 { - pub cpu: cpu::Cpu, +pub(crate) struct Spc700 { + cpu: Cpu, instruction_set: [OpCode; 256], } impl Spc700 { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self { cpu: Cpu::new(), instruction_set: opcode_table(), } } - pub fn step(&mut self, bus: &mut B) { + pub(crate) fn step(&mut self, bus: &mut B) { let op = self.cpu.get_imm::(bus); let opcode = &self.instruction_set[op as usize]; let instr = opcode.function; instr(&mut self.cpu, bus, opcode.meta.mode); } +} - pub fn peek_opcode(&self, bus: &B) -> Meta { - let op = bus - .peek_at(self.cpu.program_counter.into()) - .unwrap_or_default(); - self.instruction_set[op as usize].meta +#[cfg(test)] +mod tests { + use super::*; + use crate::apu::spc700::cpu::Status; + use crate::apu::spc700::opcode::Meta; + use crate::utils::testbus::{deserialize_as_map, Cycle, TomHarteBus}; + use crate::utils::testrun::{run_test, OpcodeTest}; + use serde::{Deserialize, Deserializer}; + use std::{collections::HashMap, path::PathBuf}; + + impl Spc700 { + fn peek_opcode(&self, bus: &B) -> Meta { + let op = bus + .peek_at(self.cpu.program_counter.into()) + .unwrap_or_default(); + self.instruction_set[op as usize].meta + } + } + + #[derive(Debug, PartialEq, Deserialize)] + pub struct CpuState { + pc: u16, + a: u8, + x: u8, + y: u8, + sp: u8, + psw: u8, + #[serde(deserialize_with = "deserialize_as_map")] + ram: HashMap, + } + + impl CpuState { + pub fn convert_state(&self) -> (Spc700, TomHarteBus) { + let mut spc700 = Spc700::new(); + spc700.cpu.accumulator = self.a; + spc700.cpu.index_x = self.x; + spc700.cpu.index_y = self.y; + spc700.cpu.program_counter = self.pc; + spc700.cpu.stack_pointer = self.sp; + spc700.cpu.status = Status(self.psw); + + let bus = TomHarteBus { + memory: self.ram.clone(), + ..Default::default() + }; + + (spc700, bus) + } + } + + impl From<(Cpu, TomHarteBus)> for CpuState { + fn from(value: (Cpu, TomHarteBus)) -> Self { + Self { + pc: value.0.program_counter, + a: value.0.accumulator, + x: value.0.index_x, + y: value.0.index_y, + sp: value.0.stack_pointer, + psw: value.0.status.0, + ram: value.1.memory, + } + } + } + + impl std::fmt::Display for CpuState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "pc:{:04X} sp:{:04X} psw:{:02X} a:{:04X} x:{:04X} y:{:04X} \n\t ram:{:02X?}", + self.pc, self.sp, self.psw, self.a, self.x, self.y, self.ram + ) + } + } + + impl OpcodeTest for CpuState { + fn test_path(name: &str) -> std::path::PathBuf { + let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + root_dir.join(format!("tests/spc700/{name}.json.xz")) + } + + fn step(&self, _other: &Self, _cycles_len: usize) -> (Self, Vec, bool) { + let (mut spc700, mut bus) = self.convert_state(); + let opcode = spc700.peek_opcode(&bus); + let skip_cycles = opcode.code == 0xFE; + + spc700.step(&mut bus); + + let cycles = bus.cycles.clone(); + (Self::from((spc700.cpu, bus)), cycles, skip_cycles) + } + + fn deserialize_cycles<'de, D: Deserializer<'de>>( + deserializer: D, + ) -> Result, D::Error> { + let v: Vec<(Option, Option, String)> = Deserialize::deserialize(deserializer)?; + let mut cycles: Vec = v + .iter() + .map(|(addr, value, state)| match state.as_str() { + "wait" => Cycle::Internal, + "read" => Cycle::Read(addr.unwrap_or_default(), *value), + "write" => Cycle::Write(addr.unwrap_or_default(), value.unwrap_or_default()), + _ => panic!("Unknown state: {state}"), + }) + .collect(); + cycles.sort(); + Ok(cycles) + } } -} -#[rustfmt::skip] -const fn opcode_table() -> [OpCode; 256] { - use addressing::AddressingMode::*; - [ - OpCode::new(Meta::new(0x00, "NOP", Implied), Spc700::nop), - OpCode::new(Meta::new(0x01, "TCALL", Implied), Spc700::tcall::<0>), - OpCode::new(Meta::new(0x02, "SET1", DirectPage), Spc700::set1::<0>), - OpCode::new(Meta::new(0x03, "BBS", DirectPage), Spc700::bbs::<0>), - OpCode::new(Meta::new(0x04, "OR", DirectPage), Spc700::or::<{ Accumulator }>), - OpCode::new(Meta::new(0x05, "OR", Absolute), Spc700::or::<{ Accumulator }>), - OpCode::new(Meta::new(0x06, "OR", IndirectX), Spc700::or::<{ Accumulator }>), - OpCode::new(Meta::new(0x07, "OR", XIndirect), Spc700::or::<{ Accumulator }>), - OpCode::new(Meta::new(0x08, "OR", Immediate), Spc700::or::<{ Accumulator }>), - OpCode::new(Meta::new(0x09, "OR", DirectPage), Spc700::or::<{ DirectPage }>), - OpCode::new(Meta::new(0x0A, "OR1", AbsoluteBooleanBit), Spc700::or1::), - OpCode::new(Meta::new(0x0B, "ASL", DirectPage), Spc700::asl), - OpCode::new(Meta::new(0x0C, "ASL", Absolute), Spc700::asl), - OpCode::new(Meta::new(0x0D, "PUSH", Psw), Spc700::push), - OpCode::new(Meta::new(0x0E, "TSET1", Absolute), Spc700::tset1), - OpCode::new(Meta::new(0x0F, "BRK", Implied), Spc700::brk), - OpCode::new(Meta::new(0x10, "BPL", Implied), Spc700::bpl), - OpCode::new(Meta::new(0x11, "TCALL", Implied), Spc700::tcall::<1>), - OpCode::new(Meta::new(0x12, "CLR1", DirectPage), Spc700::clr1::<0>), - OpCode::new(Meta::new(0x13, "BBC", DirectPage), Spc700::bbc::<0>), - OpCode::new(Meta::new(0x14, "OR", DirectX), Spc700::or::<{ Accumulator }>), - OpCode::new(Meta::new(0x15, "OR", AbsoluteX), Spc700::or::<{ Accumulator }>), - OpCode::new(Meta::new(0x16, "OR", AbsoluteY), Spc700::or::<{ Accumulator }>), - OpCode::new(Meta::new(0x17, "OR", DirectPageIndirectY), Spc700::or::<{ Accumulator }>), - OpCode::new(Meta::new(0x18, "OR", Immediate), Spc700::or::<{ DirectPage }>), - OpCode::new(Meta::new(0x19, "OR", IndirectY), Spc700::or::<{ IndirectX }>), - OpCode::new(Meta::new(0x1A, "DECW", DirectPage), Spc700::decw), - OpCode::new(Meta::new(0x1B, "ASL", DirectX), Spc700::asl), - OpCode::new(Meta::new(0x1C, "ASL", Accumulator), Spc700::asl), - OpCode::new(Meta::new(0x1D, "DEC", X), Spc700::dec), - OpCode::new(Meta::new(0x1E, "CMP", Absolute), Spc700::cmp::<{ X }>), - OpCode::new(Meta::new(0x1F, "JMP", AbsoluteX), Spc700::jmp), - OpCode::new(Meta::new(0x20, "CLRP", Implied), Spc700::clrp), - OpCode::new(Meta::new(0x21, "TCALL", Implied), Spc700::tcall::<2>), - OpCode::new(Meta::new(0x22, "SET1", DirectPage), Spc700::set1::<1>), - OpCode::new(Meta::new(0x23, "BBS", DirectPage), Spc700::bbs::<1>), - OpCode::new(Meta::new(0x24, "AND", DirectPage), Spc700::and::<{ Accumulator }>), - OpCode::new(Meta::new(0x25, "AND", Absolute), Spc700::and::<{ Accumulator }>), - OpCode::new(Meta::new(0x26, "AND", IndirectX), Spc700::and::<{ Accumulator }>), - OpCode::new(Meta::new(0x27, "AND", XIndirect), Spc700::and::<{ Accumulator }>), - OpCode::new(Meta::new(0x28, "AND", Immediate), Spc700::and::<{ Accumulator }>), - OpCode::new(Meta::new(0x29, "AND", DirectPage), Spc700::and::<{ DirectPage }>), - OpCode::new(Meta::new(0x2A, "OR1", AbsoluteBooleanBit), Spc700::or1::), - OpCode::new(Meta::new(0x2B, "ROL", DirectPage), Spc700::rol), - OpCode::new(Meta::new(0x2C, "ROL", Absolute), Spc700::rol), - OpCode::new(Meta::new(0x2D, "PUSH", Accumulator), Spc700::push), - OpCode::new(Meta::new(0x2E, "CBNE", DirectPage), Spc700::cbne), - OpCode::new(Meta::new(0x2F, "BRA", Implied), Spc700::bra), - OpCode::new(Meta::new(0x30, "BMI", Implied), Spc700::bmi), - OpCode::new(Meta::new(0x31, "TCALL", Implied), Spc700::tcall::<3>), - OpCode::new(Meta::new(0x32, "CLR1", DirectPage), Spc700::clr1::<1>), - OpCode::new(Meta::new(0x33, "BBC", DirectPage), Spc700::bbc::<1>), - OpCode::new(Meta::new(0x34, "AND", DirectX), Spc700::and::<{ Accumulator}>), - OpCode::new(Meta::new(0x35, "AND", AbsoluteX), Spc700::and::<{ Accumulator}>), - OpCode::new(Meta::new(0x36, "AND", AbsoluteY), Spc700::and::<{ Accumulator}>), - OpCode::new(Meta::new(0x37, "AND", DirectPageIndirectY), Spc700::and::<{ Accumulator}>), - OpCode::new(Meta::new(0x38, "AND", Immediate), Spc700::and::<{ DirectPage }>), - OpCode::new(Meta::new(0x39, "AND", IndirectY), Spc700::and::<{ IndirectX }>), - OpCode::new(Meta::new(0x3A, "INCW", DirectPage), Spc700::incw), - OpCode::new(Meta::new(0x3B, "ROL", DirectX), Spc700::rol), - OpCode::new(Meta::new(0x3C, "ROL", Accumulator), Spc700::rol), - OpCode::new(Meta::new(0x3D, "INC", X), Spc700::inc), - OpCode::new(Meta::new(0x3E, "CMP", DirectPage), Spc700::cmp::<{ X }>), - OpCode::new(Meta::new(0x3F, "CALL", Absolute), Spc700::call), - OpCode::new(Meta::new(0x40, "SETP", Implied), Spc700::setp), - OpCode::new(Meta::new(0x41, "TCALL", Implied), Spc700::tcall::<4>), - OpCode::new(Meta::new(0x42, "SET1", DirectPage), Spc700::set1::<2>), - OpCode::new(Meta::new(0x43, "BBS", DirectPage), Spc700::bbs::<2>), - OpCode::new(Meta::new(0x44, "EOR", DirectPage), Spc700::eor::<{ Accumulator }>), - OpCode::new(Meta::new(0x45, "EOR", Absolute), Spc700::eor::<{ Accumulator }>), - OpCode::new(Meta::new(0x46, "EOR", IndirectX), Spc700::eor::<{ Accumulator }>), - OpCode::new(Meta::new(0x47, "EOR", XIndirect), Spc700::eor::<{ Accumulator }>), - OpCode::new(Meta::new(0x48, "EOR", Immediate), Spc700::eor::<{ Accumulator }>), - OpCode::new(Meta::new(0x49, "EOR", DirectPage), Spc700::eor::<{ DirectPage }>), - OpCode::new(Meta::new(0x4A, "AND1", AbsoluteBooleanBit), Spc700::and1::), - OpCode::new(Meta::new(0x4B, "LSR", DirectPage), Spc700::lsr), - OpCode::new(Meta::new(0x4C, "LSR", Absolute), Spc700::lsr), - OpCode::new(Meta::new(0x4D, "PUSH", X), Spc700::push), - OpCode::new(Meta::new(0x4E, "TCLR1", Absolute), Spc700::tclr1), - OpCode::new(Meta::new(0x4F, "PCALL", Implied), Spc700::pcall), - OpCode::new(Meta::new(0x50, "BVC", Implied), Spc700::bvc), - OpCode::new(Meta::new(0x51, "TCALL", Implied), Spc700::tcall::<5>), - OpCode::new(Meta::new(0x52, "CLR1", DirectPage), Spc700::clr1::<2>), - OpCode::new(Meta::new(0x53, "BBC", DirectPage), Spc700::bbc::<2>), - OpCode::new(Meta::new(0x54, "EOR", DirectX), Spc700::eor::<{ Accumulator }>), - OpCode::new(Meta::new(0x55, "EOR", AbsoluteX), Spc700::eor::<{ Accumulator }>), - OpCode::new(Meta::new(0x56, "EOR", AbsoluteY), Spc700::eor::<{ Accumulator }>), - OpCode::new(Meta::new(0x57, "EOR", DirectPageIndirectY), Spc700::eor::<{ Accumulator }>), - OpCode::new(Meta::new(0x58, "EOR", Immediate), Spc700::eor::<{ DirectPage }>), - OpCode::new(Meta::new(0x59, "EOR", IndirectY), Spc700::eor::<{ IndirectX }>), - OpCode::new(Meta::new(0x5A, "CMPW", DirectPage), Spc700::cmpw), - OpCode::new(Meta::new(0x5B, "LSR", DirectX), Spc700::lsr), - OpCode::new(Meta::new(0x5C, "LSR", Accumulator), Spc700::lsr), - OpCode::new(Meta::new(0x5D, "MOV", Accumulator), Spc700::mov::<{ X }>), - OpCode::new(Meta::new(0x5E, "CMP", Absolute), Spc700::cmp::<{ Y }>), - OpCode::new(Meta::new(0x5F, "JMP", Absolute), Spc700::jmp), - OpCode::new(Meta::new(0x60, "CLRC", Implied), Spc700::clrc), - OpCode::new(Meta::new(0x61, "TCALL", Implied), Spc700::tcall::<6>), - OpCode::new(Meta::new(0x62, "SET1", DirectPage), Spc700::set1::<3>), - OpCode::new(Meta::new(0x63, "BBS", DirectPage), Spc700::bbs::<3>), - OpCode::new(Meta::new(0x64, "CMP", DirectPage), Spc700::cmp::<{ Accumulator }>), - OpCode::new(Meta::new(0x65, "CMP", Absolute), Spc700::cmp::<{ Accumulator }>), - OpCode::new(Meta::new(0x66, "CMP", IndirectX), Spc700::cmp::<{ Accumulator }>), - OpCode::new(Meta::new(0x67, "CMP", XIndirect), Spc700::cmp::<{ Accumulator }>), - OpCode::new(Meta::new(0x68, "CMP", Immediate), Spc700::cmp::<{ Accumulator }>), - OpCode::new(Meta::new(0x69, "CMP", DirectPage), Spc700::cmp::<{ DirectPage }>), - OpCode::new(Meta::new(0x6A, "AND1", AbsoluteBooleanBit), Spc700::and1::), - OpCode::new(Meta::new(0x6B, "ROR", DirectPage), Spc700::ror), - OpCode::new(Meta::new(0x6C, "ROR", Absolute), Spc700::ror), - OpCode::new(Meta::new(0x6D, "PUSH", Y), Spc700::push), - OpCode::new(Meta::new(0x6E, "DBNZ", DirectPage), Spc700::dbnz), - OpCode::new(Meta::new(0x6F, "RET", Implied), Spc700::ret), - OpCode::new(Meta::new(0x70, "BVS", Implied), Spc700::bvs), - OpCode::new(Meta::new(0x71, "TCALL", Implied), Spc700::tcall::<7>), - OpCode::new(Meta::new(0x72, "CLR1", DirectPage), Spc700::clr1::<3>), - OpCode::new(Meta::new(0x73, "BBC", DirectPage), Spc700::bbc::<3>), - OpCode::new(Meta::new(0x74, "CMP", DirectX), Spc700::cmp::<{ Accumulator }>), - OpCode::new(Meta::new(0x75, "CMP", AbsoluteX), Spc700::cmp::<{ Accumulator }>), - OpCode::new(Meta::new(0x76, "CMP", AbsoluteY), Spc700::cmp::<{ Accumulator }>), - OpCode::new(Meta::new(0x77, "CMP", DirectPageIndirectY), Spc700::cmp::<{ Accumulator }>), - OpCode::new(Meta::new(0x78, "CMP", Immediate), Spc700::cmp::<{ DirectPage }>), - OpCode::new(Meta::new(0x79, "CMP", IndirectY), Spc700::cmp::<{ IndirectX }>), - OpCode::new(Meta::new(0x7A, "ADDW", DirectPage), Spc700::addw), - OpCode::new(Meta::new(0x7B, "ROR", DirectX), Spc700::ror), - OpCode::new(Meta::new(0x7C, "ROR", Accumulator), Spc700::ror), - OpCode::new(Meta::new(0x7D, "MOV", X), Spc700::mov::<{ Accumulator }>), - OpCode::new(Meta::new(0x7E, "CMP", DirectPage), Spc700::cmp::<{ Y }>), - OpCode::new(Meta::new(0x7F, "RETI", Implied), Spc700::reti), - OpCode::new(Meta::new(0x80, "SETC", Implied), Spc700::setc), - OpCode::new(Meta::new(0x81, "TCALL", Implied), Spc700::tcall::<8>), - OpCode::new(Meta::new(0x82, "SET1", DirectPage), Spc700::set1::<4>), - OpCode::new(Meta::new(0x83, "BBS", DirectPage), Spc700::bbs::<4>), - OpCode::new(Meta::new(0x84, "ADC", DirectPage), Spc700::adc::<{ Accumulator }>), - OpCode::new(Meta::new(0x85, "ADC", Absolute), Spc700::adc::<{ Accumulator }>), - OpCode::new(Meta::new(0x86, "ADC", IndirectX), Spc700::adc::<{ Accumulator }>), - OpCode::new(Meta::new(0x87, "ADC", XIndirect), Spc700::adc::<{ Accumulator }>), - OpCode::new(Meta::new(0x88, "ADC", Immediate), Spc700::adc::<{ Accumulator }>), - OpCode::new(Meta::new(0x89, "ADC", DirectPage), Spc700::adc::<{ DirectPage }>), - OpCode::new(Meta::new(0x8A, "EOR1", AbsoluteBooleanBit), Spc700::eor1), - OpCode::new(Meta::new(0x8B, "DEC", DirectPage), Spc700::dec), - OpCode::new(Meta::new(0x8C, "DEC", Absolute), Spc700::dec), - OpCode::new(Meta::new(0x8D, "MOV", Immediate), Spc700::mov::<{ Y }>), - OpCode::new(Meta::new(0x8E, "POP", Psw), Spc700::pop), - OpCode::new(Meta::new(0x8F, "MOV", Immediate), Spc700::mov::<{ DirectPage }>), - OpCode::new(Meta::new(0x90, "BCC", Implied), Spc700::bcc), - OpCode::new(Meta::new(0x91, "TCALL", Implied), Spc700::tcall::<9>), - OpCode::new(Meta::new(0x92, "CLR1", DirectPage), Spc700::clr1::<4>), - OpCode::new(Meta::new(0x93, "BBC", DirectPage), Spc700::bbc::<4>), - OpCode::new(Meta::new(0x94, "ADC", DirectX), Spc700::adc::<{ Accumulator }>), - OpCode::new(Meta::new(0x95, "ADC", AbsoluteX), Spc700::adc::<{ Accumulator }>), - OpCode::new(Meta::new(0x96, "ADC", AbsoluteY), Spc700::adc::<{ Accumulator }>), - OpCode::new(Meta::new(0x97, "ADC", DirectPageIndirectY), Spc700::adc::<{ Accumulator }>), - OpCode::new(Meta::new(0x98, "ADC", Immediate), Spc700::adc::<{ DirectPage }>), - OpCode::new(Meta::new(0x99, "ADC", IndirectY), Spc700::adc::<{ IndirectX }>), - OpCode::new(Meta::new(0x9A, "SUBW", DirectPage), Spc700::subw), - OpCode::new(Meta::new(0x9B, "DEC", DirectX), Spc700::dec), - OpCode::new(Meta::new(0x9C, "DEC", Accumulator), Spc700::dec), - OpCode::new(Meta::new(0x9D, "MOV", Sp), Spc700::mov::<{ X }>), - OpCode::new(Meta::new(0x9E, "DIV", Implied), Spc700::div), - OpCode::new(Meta::new(0x9F, "XCN", Implied), Spc700::xcn), - OpCode::new(Meta::new(0xA0, "EI", Implied), Spc700::ei), - OpCode::new(Meta::new(0xA1, "TCALL", Implied), Spc700::tcall::<10>), - OpCode::new(Meta::new(0xA2, "SET1", DirectPage), Spc700::set1::<5>), - OpCode::new(Meta::new(0xA3, "BBS", DirectPage), Spc700::bbs::<5>), - OpCode::new(Meta::new(0xA4, "SBC", DirectPage), Spc700::sbc::<{ Accumulator }>), - OpCode::new(Meta::new(0xA5, "SBC", Absolute), Spc700::sbc::<{ Accumulator }>), - OpCode::new(Meta::new(0xA6, "SBC", IndirectX), Spc700::sbc::<{ Accumulator }>), - OpCode::new(Meta::new(0xA7, "SBC", XIndirect), Spc700::sbc::<{ Accumulator }>), - OpCode::new(Meta::new(0xA8, "SBC", Immediate), Spc700::sbc::<{ Accumulator }>), - OpCode::new(Meta::new(0xA9, "SBC", DirectPage), Spc700::sbc::<{ DirectPage }>), - OpCode::new(Meta::new(0xAA, "MOV1", AbsoluteBooleanBit), Spc700::mov1::), - OpCode::new(Meta::new(0xAB, "INC", DirectPage), Spc700::inc), - OpCode::new(Meta::new(0xAC, "INC", Absolute), Spc700::inc), - OpCode::new(Meta::new(0xAD, "CMP", Immediate), Spc700::cmp::<{ Y }>), - OpCode::new(Meta::new(0xAE, "POP", Accumulator), Spc700::pop), - OpCode::new(Meta::new(0xAF, "MOV", Accumulator), Spc700::mov::<{ DirectXPostIncrement }>), - OpCode::new(Meta::new(0xB0, "BCS", Implied), Spc700::bcs), - OpCode::new(Meta::new(0xB1, "TCALL", Implied), Spc700::tcall::<11>), - OpCode::new(Meta::new(0xB2, "CLR1", DirectPage), Spc700::clr1::<5>), - OpCode::new(Meta::new(0xB3, "BBC", DirectPage), Spc700::bbc::<5>), - OpCode::new(Meta::new(0xB4, "SBC", DirectX), Spc700::sbc::<{ Accumulator }>), - OpCode::new(Meta::new(0xB5, "SBC", AbsoluteX), Spc700::sbc::<{ Accumulator }>), - OpCode::new(Meta::new(0xB6, "SBC", AbsoluteY), Spc700::sbc::<{ Accumulator }>), - OpCode::new(Meta::new(0xB7, "SBC", DirectPageIndirectY), Spc700::sbc::<{ Accumulator }>), - OpCode::new(Meta::new(0xB8, "SBC", Immediate), Spc700::sbc::<{ DirectPage }>), - OpCode::new(Meta::new(0xB9, "SBC", IndirectY), Spc700::sbc::<{ IndirectX }>), - OpCode::new(Meta::new(0xBA, "MOVW", DirectPage), Spc700::movw::), - OpCode::new(Meta::new(0xBB, "INC", DirectX), Spc700::inc), - OpCode::new(Meta::new(0xBC, "INC", Accumulator), Spc700::inc), - OpCode::new(Meta::new(0xBD, "MOV", X), Spc700::mov::<{ Sp }>), - OpCode::new(Meta::new(0xBE, "DAS", Implied), Spc700::das), - OpCode::new(Meta::new(0xBF, "MOV", DirectXPostIncrement), Spc700::mov::<{ Accumulator }>), - OpCode::new(Meta::new(0xC0, "DI", Implied), Spc700::di), - OpCode::new(Meta::new(0xC1, "TCALL", Implied), Spc700::tcall::<12>), - OpCode::new(Meta::new(0xC2, "SET1", DirectPage), Spc700::set1::<6>), - OpCode::new(Meta::new(0xC3, "BBS", DirectPage), Spc700::bbs::<6>), - OpCode::new(Meta::new(0xC4, "MOV", Accumulator), Spc700::mov::<{ DirectPage }>), - OpCode::new(Meta::new(0xC5, "MOV", Accumulator), Spc700::mov::<{ Absolute }>), - OpCode::new(Meta::new(0xC6, "MOV", Accumulator), Spc700::mov::<{ IndirectX }>), - OpCode::new(Meta::new(0xC7, "MOV", Accumulator), Spc700::mov::<{ XIndirect }>), - OpCode::new(Meta::new(0xC8, "CMP", Immediate), Spc700::cmp::<{ X }>), - OpCode::new(Meta::new(0xC9, "MOV", X), Spc700::mov::<{ Absolute }>), - OpCode::new(Meta::new(0xCA, "MOV1", AbsoluteBooleanBit), Spc700::mov1::), - OpCode::new(Meta::new(0xCB, "MOV", Y), Spc700::mov::<{ DirectPage }>), - OpCode::new(Meta::new(0xCC, "MOV", Y), Spc700::mov::<{ Absolute }>), - OpCode::new(Meta::new(0xCD, "MOV", Immediate), Spc700::mov::<{ X }>), - OpCode::new(Meta::new(0xCE, "POP", X), Spc700::pop), - OpCode::new(Meta::new(0xCF, "MUL", Implied), Spc700::mul), - OpCode::new(Meta::new(0xD0, "BNE", Implied), Spc700::bne), - OpCode::new(Meta::new(0xD1, "TCALL", Implied), Spc700::tcall::<13>), - OpCode::new(Meta::new(0xD2, "CLR1", DirectPage), Spc700::clr1::<6>), - OpCode::new(Meta::new(0xD3, "BBC", DirectPage), Spc700::bbc::<6>), - OpCode::new(Meta::new(0xD4, "MOV", Accumulator), Spc700::mov::<{ DirectX }>), - OpCode::new(Meta::new(0xD5, "MOV", Accumulator), Spc700::mov::<{ AbsoluteX }>), - OpCode::new(Meta::new(0xD6, "MOV", Accumulator), Spc700::mov::<{ AbsoluteY }>), - OpCode::new(Meta::new(0xD7, "MOV", Accumulator), Spc700::mov::<{ DirectPageIndirectY }>), - OpCode::new(Meta::new(0xD8, "MOV", X), Spc700::mov::<{ DirectPage }>), - OpCode::new(Meta::new(0xD9, "MOV", X), Spc700::mov::<{ DirectY }>), - OpCode::new(Meta::new(0xDA, "MOVW", DirectPage), Spc700::movw::), - OpCode::new(Meta::new(0xDB, "MOV", Y), Spc700::mov::<{ DirectX }>), - OpCode::new(Meta::new(0xDC, "DEC", Y), Spc700::dec), - OpCode::new(Meta::new(0xDD, "MOV", Y), Spc700::mov::<{ Accumulator }>), - OpCode::new(Meta::new(0xDE, "CBNE", DirectX), Spc700::cbne), - OpCode::new(Meta::new(0xDF, "DAA", Implied), Spc700::daa), - OpCode::new(Meta::new(0xE0, "CLRV", Implied), Spc700::clrv), - OpCode::new(Meta::new(0xE1, "TCALL", Implied), Spc700::tcall::<14>), - OpCode::new(Meta::new(0xE2, "SET1", DirectPage), Spc700::set1::<7>), - OpCode::new(Meta::new(0xE3, "BBS", DirectPage), Spc700::bbs::<7>), - OpCode::new(Meta::new(0xE4, "MOV", DirectPage), Spc700::mov::<{ Accumulator }>), - OpCode::new(Meta::new(0xE5, "MOV", Absolute), Spc700::mov::<{ Accumulator }>), - OpCode::new(Meta::new(0xE6, "MOV", IndirectX), Spc700::mov::<{ Accumulator }>), - OpCode::new(Meta::new(0xE7, "MOV", XIndirect), Spc700::mov::<{ Accumulator }>), - OpCode::new(Meta::new(0xE8, "MOV", Immediate), Spc700::mov::<{ Accumulator }>), - OpCode::new(Meta::new(0xE9, "MOV", Absolute), Spc700::mov::<{ X }>), - OpCode::new(Meta::new(0xEA, "NOT1", AbsoluteBooleanBit), Spc700::not1), - OpCode::new(Meta::new(0xEB, "MOV", DirectPage), Spc700::mov::<{ Y }>), - OpCode::new(Meta::new(0xEC, "MOV", Absolute), Spc700::mov::<{ Y }>), - OpCode::new(Meta::new(0xED, "NOTC", Implied), Spc700::notc), - OpCode::new(Meta::new(0xEE, "POP", Y), Spc700::pop), - OpCode::new(Meta::new(0xEF, "SLEEP", Implied), Spc700::sleep), - OpCode::new(Meta::new(0xF0, "BEQ", Implied), Spc700::beq), - OpCode::new(Meta::new(0xF1, "TCALL", Implied), Spc700::tcall::<15>), - OpCode::new(Meta::new(0xF2, "CLR1", DirectPage), Spc700::clr1::<7>), - OpCode::new(Meta::new(0xF3, "BBC", DirectPage), Spc700::bbc::<7>), - OpCode::new(Meta::new(0xF4, "MOV", DirectX), Spc700::mov::<{ Accumulator }>), - OpCode::new(Meta::new(0xF5, "MOV", AbsoluteX), Spc700::mov::<{ Accumulator }>), - OpCode::new(Meta::new(0xF6, "MOV", AbsoluteY), Spc700::mov::<{ Accumulator }>), - OpCode::new(Meta::new(0xF7, "MOV", DirectPageIndirectY), Spc700::mov::<{ Accumulator }>), - OpCode::new(Meta::new(0xF8, "MOV", DirectPage), Spc700::mov::<{ X }>), - OpCode::new(Meta::new(0xF9, "MOV", DirectY), Spc700::mov::<{ X }>), - OpCode::new(Meta::new(0xFA, "MOV", DirectPage), Spc700::mov::<{ DirectPage }>), - OpCode::new(Meta::new(0xFB, "MOV", DirectX), Spc700::mov::<{ Y }>), - OpCode::new(Meta::new(0xFC, "INC", Y), Spc700::inc), - OpCode::new(Meta::new(0xFD, "MOV", Accumulator), Spc700::mov::<{ Y }>), - OpCode::new(Meta::new(0xFE, "DBNZ", Y), Spc700::dbnz), - OpCode::new(Meta::new(0xFF, "STOP", Implied), Spc700::stop), - ] + include!(concat!(env!("OUT_DIR"), "/tomharte_spc700.rs")); } diff --git a/aliusnes/src/apu/spc700/opcode.rs b/aliusnes/src/apu/spc700/opcode.rs new file mode 100644 index 0000000..40620ce --- /dev/null +++ b/aliusnes/src/apu/spc700/opcode.rs @@ -0,0 +1,295 @@ +use crate::{ + apu::spc700::{addressing::AddressingMode, Cpu, Spc700}, + bus::Bus, +}; + +#[derive(Clone, Copy)] +pub(crate) struct Meta { + pub code: u8, + pub mnemonic: &'static str, + pub mode: AddressingMode, +} + +impl Meta { + const fn new(code: u8, mnemonic: &'static str, mode: AddressingMode) -> Self { + Self { + code, + mnemonic, + mode, + } + } +} + +pub(crate) struct OpCode { + pub meta: Meta, + pub function: fn(&mut Cpu, &mut B, AddressingMode), +} + +impl OpCode { + const fn new(meta: Meta, function: fn(&mut Cpu, &mut B, AddressingMode)) -> Self { + OpCode { meta, function } + } +} + +#[rustfmt::skip] +pub(crate) const fn opcode_table() -> [OpCode; 256] { + use super::addressing::AddressingMode::*; + [ + OpCode::new(Meta::new(0x00, "NOP", Implied), Spc700::nop), + OpCode::new(Meta::new(0x01, "TCALL", Implied), Spc700::tcall::<0>), + OpCode::new(Meta::new(0x02, "SET1", DirectPage), Spc700::set1::<0>), + OpCode::new(Meta::new(0x03, "BBS", DirectPage), Spc700::bbs::<0>), + OpCode::new(Meta::new(0x04, "OR", DirectPage), Spc700::or::<{ Accumulator }>), + OpCode::new(Meta::new(0x05, "OR", Absolute), Spc700::or::<{ Accumulator }>), + OpCode::new(Meta::new(0x06, "OR", IndirectX), Spc700::or::<{ Accumulator }>), + OpCode::new(Meta::new(0x07, "OR", XIndirect), Spc700::or::<{ Accumulator }>), + OpCode::new(Meta::new(0x08, "OR", Immediate), Spc700::or::<{ Accumulator }>), + OpCode::new(Meta::new(0x09, "OR", DirectPage), Spc700::or::<{ DirectPage }>), + OpCode::new(Meta::new(0x0A, "OR1", AbsoluteBooleanBit), Spc700::or1::), + OpCode::new(Meta::new(0x0B, "ASL", DirectPage), Spc700::asl), + OpCode::new(Meta::new(0x0C, "ASL", Absolute), Spc700::asl), + OpCode::new(Meta::new(0x0D, "PUSH", Psw), Spc700::push), + OpCode::new(Meta::new(0x0E, "TSET1", Absolute), Spc700::tset1), + OpCode::new(Meta::new(0x0F, "BRK", Implied), Spc700::brk), + OpCode::new(Meta::new(0x10, "BPL", Implied), Spc700::bpl), + OpCode::new(Meta::new(0x11, "TCALL", Implied), Spc700::tcall::<1>), + OpCode::new(Meta::new(0x12, "CLR1", DirectPage), Spc700::clr1::<0>), + OpCode::new(Meta::new(0x13, "BBC", DirectPage), Spc700::bbc::<0>), + OpCode::new(Meta::new(0x14, "OR", DirectX), Spc700::or::<{ Accumulator }>), + OpCode::new(Meta::new(0x15, "OR", AbsoluteX), Spc700::or::<{ Accumulator }>), + OpCode::new(Meta::new(0x16, "OR", AbsoluteY), Spc700::or::<{ Accumulator }>), + OpCode::new(Meta::new(0x17, "OR", DirectPageIndirectY), Spc700::or::<{ Accumulator }>), + OpCode::new(Meta::new(0x18, "OR", Immediate), Spc700::or::<{ DirectPage }>), + OpCode::new(Meta::new(0x19, "OR", IndirectY), Spc700::or::<{ IndirectX }>), + OpCode::new(Meta::new(0x1A, "DECW", DirectPage), Spc700::decw), + OpCode::new(Meta::new(0x1B, "ASL", DirectX), Spc700::asl), + OpCode::new(Meta::new(0x1C, "ASL", Accumulator), Spc700::asl), + OpCode::new(Meta::new(0x1D, "DEC", X), Spc700::dec), + OpCode::new(Meta::new(0x1E, "CMP", Absolute), Spc700::cmp::<{ X }>), + OpCode::new(Meta::new(0x1F, "JMP", AbsoluteX), Spc700::jmp), + OpCode::new(Meta::new(0x20, "CLRP", Implied), Spc700::clrp), + OpCode::new(Meta::new(0x21, "TCALL", Implied), Spc700::tcall::<2>), + OpCode::new(Meta::new(0x22, "SET1", DirectPage), Spc700::set1::<1>), + OpCode::new(Meta::new(0x23, "BBS", DirectPage), Spc700::bbs::<1>), + OpCode::new(Meta::new(0x24, "AND", DirectPage), Spc700::and::<{ Accumulator }>), + OpCode::new(Meta::new(0x25, "AND", Absolute), Spc700::and::<{ Accumulator }>), + OpCode::new(Meta::new(0x26, "AND", IndirectX), Spc700::and::<{ Accumulator }>), + OpCode::new(Meta::new(0x27, "AND", XIndirect), Spc700::and::<{ Accumulator }>), + OpCode::new(Meta::new(0x28, "AND", Immediate), Spc700::and::<{ Accumulator }>), + OpCode::new(Meta::new(0x29, "AND", DirectPage), Spc700::and::<{ DirectPage }>), + OpCode::new(Meta::new(0x2A, "OR1", AbsoluteBooleanBit), Spc700::or1::), + OpCode::new(Meta::new(0x2B, "ROL", DirectPage), Spc700::rol), + OpCode::new(Meta::new(0x2C, "ROL", Absolute), Spc700::rol), + OpCode::new(Meta::new(0x2D, "PUSH", Accumulator), Spc700::push), + OpCode::new(Meta::new(0x2E, "CBNE", DirectPage), Spc700::cbne), + OpCode::new(Meta::new(0x2F, "BRA", Implied), Spc700::bra), + OpCode::new(Meta::new(0x30, "BMI", Implied), Spc700::bmi), + OpCode::new(Meta::new(0x31, "TCALL", Implied), Spc700::tcall::<3>), + OpCode::new(Meta::new(0x32, "CLR1", DirectPage), Spc700::clr1::<1>), + OpCode::new(Meta::new(0x33, "BBC", DirectPage), Spc700::bbc::<1>), + OpCode::new(Meta::new(0x34, "AND", DirectX), Spc700::and::<{ Accumulator}>), + OpCode::new(Meta::new(0x35, "AND", AbsoluteX), Spc700::and::<{ Accumulator}>), + OpCode::new(Meta::new(0x36, "AND", AbsoluteY), Spc700::and::<{ Accumulator}>), + OpCode::new(Meta::new(0x37, "AND", DirectPageIndirectY), Spc700::and::<{ Accumulator}>), + OpCode::new(Meta::new(0x38, "AND", Immediate), Spc700::and::<{ DirectPage }>), + OpCode::new(Meta::new(0x39, "AND", IndirectY), Spc700::and::<{ IndirectX }>), + OpCode::new(Meta::new(0x3A, "INCW", DirectPage), Spc700::incw), + OpCode::new(Meta::new(0x3B, "ROL", DirectX), Spc700::rol), + OpCode::new(Meta::new(0x3C, "ROL", Accumulator), Spc700::rol), + OpCode::new(Meta::new(0x3D, "INC", X), Spc700::inc), + OpCode::new(Meta::new(0x3E, "CMP", DirectPage), Spc700::cmp::<{ X }>), + OpCode::new(Meta::new(0x3F, "CALL", Absolute), Spc700::call), + OpCode::new(Meta::new(0x40, "SETP", Implied), Spc700::setp), + OpCode::new(Meta::new(0x41, "TCALL", Implied), Spc700::tcall::<4>), + OpCode::new(Meta::new(0x42, "SET1", DirectPage), Spc700::set1::<2>), + OpCode::new(Meta::new(0x43, "BBS", DirectPage), Spc700::bbs::<2>), + OpCode::new(Meta::new(0x44, "EOR", DirectPage), Spc700::eor::<{ Accumulator }>), + OpCode::new(Meta::new(0x45, "EOR", Absolute), Spc700::eor::<{ Accumulator }>), + OpCode::new(Meta::new(0x46, "EOR", IndirectX), Spc700::eor::<{ Accumulator }>), + OpCode::new(Meta::new(0x47, "EOR", XIndirect), Spc700::eor::<{ Accumulator }>), + OpCode::new(Meta::new(0x48, "EOR", Immediate), Spc700::eor::<{ Accumulator }>), + OpCode::new(Meta::new(0x49, "EOR", DirectPage), Spc700::eor::<{ DirectPage }>), + OpCode::new(Meta::new(0x4A, "AND1", AbsoluteBooleanBit), Spc700::and1::), + OpCode::new(Meta::new(0x4B, "LSR", DirectPage), Spc700::lsr), + OpCode::new(Meta::new(0x4C, "LSR", Absolute), Spc700::lsr), + OpCode::new(Meta::new(0x4D, "PUSH", X), Spc700::push), + OpCode::new(Meta::new(0x4E, "TCLR1", Absolute), Spc700::tclr1), + OpCode::new(Meta::new(0x4F, "PCALL", Implied), Spc700::pcall), + OpCode::new(Meta::new(0x50, "BVC", Implied), Spc700::bvc), + OpCode::new(Meta::new(0x51, "TCALL", Implied), Spc700::tcall::<5>), + OpCode::new(Meta::new(0x52, "CLR1", DirectPage), Spc700::clr1::<2>), + OpCode::new(Meta::new(0x53, "BBC", DirectPage), Spc700::bbc::<2>), + OpCode::new(Meta::new(0x54, "EOR", DirectX), Spc700::eor::<{ Accumulator }>), + OpCode::new(Meta::new(0x55, "EOR", AbsoluteX), Spc700::eor::<{ Accumulator }>), + OpCode::new(Meta::new(0x56, "EOR", AbsoluteY), Spc700::eor::<{ Accumulator }>), + OpCode::new(Meta::new(0x57, "EOR", DirectPageIndirectY), Spc700::eor::<{ Accumulator }>), + OpCode::new(Meta::new(0x58, "EOR", Immediate), Spc700::eor::<{ DirectPage }>), + OpCode::new(Meta::new(0x59, "EOR", IndirectY), Spc700::eor::<{ IndirectX }>), + OpCode::new(Meta::new(0x5A, "CMPW", DirectPage), Spc700::cmpw), + OpCode::new(Meta::new(0x5B, "LSR", DirectX), Spc700::lsr), + OpCode::new(Meta::new(0x5C, "LSR", Accumulator), Spc700::lsr), + OpCode::new(Meta::new(0x5D, "MOV", Accumulator), Spc700::mov::<{ X }>), + OpCode::new(Meta::new(0x5E, "CMP", Absolute), Spc700::cmp::<{ Y }>), + OpCode::new(Meta::new(0x5F, "JMP", Absolute), Spc700::jmp), + OpCode::new(Meta::new(0x60, "CLRC", Implied), Spc700::clrc), + OpCode::new(Meta::new(0x61, "TCALL", Implied), Spc700::tcall::<6>), + OpCode::new(Meta::new(0x62, "SET1", DirectPage), Spc700::set1::<3>), + OpCode::new(Meta::new(0x63, "BBS", DirectPage), Spc700::bbs::<3>), + OpCode::new(Meta::new(0x64, "CMP", DirectPage), Spc700::cmp::<{ Accumulator }>), + OpCode::new(Meta::new(0x65, "CMP", Absolute), Spc700::cmp::<{ Accumulator }>), + OpCode::new(Meta::new(0x66, "CMP", IndirectX), Spc700::cmp::<{ Accumulator }>), + OpCode::new(Meta::new(0x67, "CMP", XIndirect), Spc700::cmp::<{ Accumulator }>), + OpCode::new(Meta::new(0x68, "CMP", Immediate), Spc700::cmp::<{ Accumulator }>), + OpCode::new(Meta::new(0x69, "CMP", DirectPage), Spc700::cmp::<{ DirectPage }>), + OpCode::new(Meta::new(0x6A, "AND1", AbsoluteBooleanBit), Spc700::and1::), + OpCode::new(Meta::new(0x6B, "ROR", DirectPage), Spc700::ror), + OpCode::new(Meta::new(0x6C, "ROR", Absolute), Spc700::ror), + OpCode::new(Meta::new(0x6D, "PUSH", Y), Spc700::push), + OpCode::new(Meta::new(0x6E, "DBNZ", DirectPage), Spc700::dbnz), + OpCode::new(Meta::new(0x6F, "RET", Implied), Spc700::ret), + OpCode::new(Meta::new(0x70, "BVS", Implied), Spc700::bvs), + OpCode::new(Meta::new(0x71, "TCALL", Implied), Spc700::tcall::<7>), + OpCode::new(Meta::new(0x72, "CLR1", DirectPage), Spc700::clr1::<3>), + OpCode::new(Meta::new(0x73, "BBC", DirectPage), Spc700::bbc::<3>), + OpCode::new(Meta::new(0x74, "CMP", DirectX), Spc700::cmp::<{ Accumulator }>), + OpCode::new(Meta::new(0x75, "CMP", AbsoluteX), Spc700::cmp::<{ Accumulator }>), + OpCode::new(Meta::new(0x76, "CMP", AbsoluteY), Spc700::cmp::<{ Accumulator }>), + OpCode::new(Meta::new(0x77, "CMP", DirectPageIndirectY), Spc700::cmp::<{ Accumulator }>), + OpCode::new(Meta::new(0x78, "CMP", Immediate), Spc700::cmp::<{ DirectPage }>), + OpCode::new(Meta::new(0x79, "CMP", IndirectY), Spc700::cmp::<{ IndirectX }>), + OpCode::new(Meta::new(0x7A, "ADDW", DirectPage), Spc700::addw), + OpCode::new(Meta::new(0x7B, "ROR", DirectX), Spc700::ror), + OpCode::new(Meta::new(0x7C, "ROR", Accumulator), Spc700::ror), + OpCode::new(Meta::new(0x7D, "MOV", X), Spc700::mov::<{ Accumulator }>), + OpCode::new(Meta::new(0x7E, "CMP", DirectPage), Spc700::cmp::<{ Y }>), + OpCode::new(Meta::new(0x7F, "RETI", Implied), Spc700::reti), + OpCode::new(Meta::new(0x80, "SETC", Implied), Spc700::setc), + OpCode::new(Meta::new(0x81, "TCALL", Implied), Spc700::tcall::<8>), + OpCode::new(Meta::new(0x82, "SET1", DirectPage), Spc700::set1::<4>), + OpCode::new(Meta::new(0x83, "BBS", DirectPage), Spc700::bbs::<4>), + OpCode::new(Meta::new(0x84, "ADC", DirectPage), Spc700::adc::<{ Accumulator }>), + OpCode::new(Meta::new(0x85, "ADC", Absolute), Spc700::adc::<{ Accumulator }>), + OpCode::new(Meta::new(0x86, "ADC", IndirectX), Spc700::adc::<{ Accumulator }>), + OpCode::new(Meta::new(0x87, "ADC", XIndirect), Spc700::adc::<{ Accumulator }>), + OpCode::new(Meta::new(0x88, "ADC", Immediate), Spc700::adc::<{ Accumulator }>), + OpCode::new(Meta::new(0x89, "ADC", DirectPage), Spc700::adc::<{ DirectPage }>), + OpCode::new(Meta::new(0x8A, "EOR1", AbsoluteBooleanBit), Spc700::eor1), + OpCode::new(Meta::new(0x8B, "DEC", DirectPage), Spc700::dec), + OpCode::new(Meta::new(0x8C, "DEC", Absolute), Spc700::dec), + OpCode::new(Meta::new(0x8D, "MOV", Immediate), Spc700::mov::<{ Y }>), + OpCode::new(Meta::new(0x8E, "POP", Psw), Spc700::pop), + OpCode::new(Meta::new(0x8F, "MOV", Immediate), Spc700::mov::<{ DirectPage }>), + OpCode::new(Meta::new(0x90, "BCC", Implied), Spc700::bcc), + OpCode::new(Meta::new(0x91, "TCALL", Implied), Spc700::tcall::<9>), + OpCode::new(Meta::new(0x92, "CLR1", DirectPage), Spc700::clr1::<4>), + OpCode::new(Meta::new(0x93, "BBC", DirectPage), Spc700::bbc::<4>), + OpCode::new(Meta::new(0x94, "ADC", DirectX), Spc700::adc::<{ Accumulator }>), + OpCode::new(Meta::new(0x95, "ADC", AbsoluteX), Spc700::adc::<{ Accumulator }>), + OpCode::new(Meta::new(0x96, "ADC", AbsoluteY), Spc700::adc::<{ Accumulator }>), + OpCode::new(Meta::new(0x97, "ADC", DirectPageIndirectY), Spc700::adc::<{ Accumulator }>), + OpCode::new(Meta::new(0x98, "ADC", Immediate), Spc700::adc::<{ DirectPage }>), + OpCode::new(Meta::new(0x99, "ADC", IndirectY), Spc700::adc::<{ IndirectX }>), + OpCode::new(Meta::new(0x9A, "SUBW", DirectPage), Spc700::subw), + OpCode::new(Meta::new(0x9B, "DEC", DirectX), Spc700::dec), + OpCode::new(Meta::new(0x9C, "DEC", Accumulator), Spc700::dec), + OpCode::new(Meta::new(0x9D, "MOV", Sp), Spc700::mov::<{ X }>), + OpCode::new(Meta::new(0x9E, "DIV", Implied), Spc700::div), + OpCode::new(Meta::new(0x9F, "XCN", Implied), Spc700::xcn), + OpCode::new(Meta::new(0xA0, "EI", Implied), Spc700::ei), + OpCode::new(Meta::new(0xA1, "TCALL", Implied), Spc700::tcall::<10>), + OpCode::new(Meta::new(0xA2, "SET1", DirectPage), Spc700::set1::<5>), + OpCode::new(Meta::new(0xA3, "BBS", DirectPage), Spc700::bbs::<5>), + OpCode::new(Meta::new(0xA4, "SBC", DirectPage), Spc700::sbc::<{ Accumulator }>), + OpCode::new(Meta::new(0xA5, "SBC", Absolute), Spc700::sbc::<{ Accumulator }>), + OpCode::new(Meta::new(0xA6, "SBC", IndirectX), Spc700::sbc::<{ Accumulator }>), + OpCode::new(Meta::new(0xA7, "SBC", XIndirect), Spc700::sbc::<{ Accumulator }>), + OpCode::new(Meta::new(0xA8, "SBC", Immediate), Spc700::sbc::<{ Accumulator }>), + OpCode::new(Meta::new(0xA9, "SBC", DirectPage), Spc700::sbc::<{ DirectPage }>), + OpCode::new(Meta::new(0xAA, "MOV1", AbsoluteBooleanBit), Spc700::mov1::), + OpCode::new(Meta::new(0xAB, "INC", DirectPage), Spc700::inc), + OpCode::new(Meta::new(0xAC, "INC", Absolute), Spc700::inc), + OpCode::new(Meta::new(0xAD, "CMP", Immediate), Spc700::cmp::<{ Y }>), + OpCode::new(Meta::new(0xAE, "POP", Accumulator), Spc700::pop), + OpCode::new(Meta::new(0xAF, "MOV", Accumulator), Spc700::mov::<{ DirectXPostIncrement }>), + OpCode::new(Meta::new(0xB0, "BCS", Implied), Spc700::bcs), + OpCode::new(Meta::new(0xB1, "TCALL", Implied), Spc700::tcall::<11>), + OpCode::new(Meta::new(0xB2, "CLR1", DirectPage), Spc700::clr1::<5>), + OpCode::new(Meta::new(0xB3, "BBC", DirectPage), Spc700::bbc::<5>), + OpCode::new(Meta::new(0xB4, "SBC", DirectX), Spc700::sbc::<{ Accumulator }>), + OpCode::new(Meta::new(0xB5, "SBC", AbsoluteX), Spc700::sbc::<{ Accumulator }>), + OpCode::new(Meta::new(0xB6, "SBC", AbsoluteY), Spc700::sbc::<{ Accumulator }>), + OpCode::new(Meta::new(0xB7, "SBC", DirectPageIndirectY), Spc700::sbc::<{ Accumulator }>), + OpCode::new(Meta::new(0xB8, "SBC", Immediate), Spc700::sbc::<{ DirectPage }>), + OpCode::new(Meta::new(0xB9, "SBC", IndirectY), Spc700::sbc::<{ IndirectX }>), + OpCode::new(Meta::new(0xBA, "MOVW", DirectPage), Spc700::movw::), + OpCode::new(Meta::new(0xBB, "INC", DirectX), Spc700::inc), + OpCode::new(Meta::new(0xBC, "INC", Accumulator), Spc700::inc), + OpCode::new(Meta::new(0xBD, "MOV", X), Spc700::mov::<{ Sp }>), + OpCode::new(Meta::new(0xBE, "DAS", Implied), Spc700::das), + OpCode::new(Meta::new(0xBF, "MOV", DirectXPostIncrement), Spc700::mov::<{ Accumulator }>), + OpCode::new(Meta::new(0xC0, "DI", Implied), Spc700::di), + OpCode::new(Meta::new(0xC1, "TCALL", Implied), Spc700::tcall::<12>), + OpCode::new(Meta::new(0xC2, "SET1", DirectPage), Spc700::set1::<6>), + OpCode::new(Meta::new(0xC3, "BBS", DirectPage), Spc700::bbs::<6>), + OpCode::new(Meta::new(0xC4, "MOV", Accumulator), Spc700::mov::<{ DirectPage }>), + OpCode::new(Meta::new(0xC5, "MOV", Accumulator), Spc700::mov::<{ Absolute }>), + OpCode::new(Meta::new(0xC6, "MOV", Accumulator), Spc700::mov::<{ IndirectX }>), + OpCode::new(Meta::new(0xC7, "MOV", Accumulator), Spc700::mov::<{ XIndirect }>), + OpCode::new(Meta::new(0xC8, "CMP", Immediate), Spc700::cmp::<{ X }>), + OpCode::new(Meta::new(0xC9, "MOV", X), Spc700::mov::<{ Absolute }>), + OpCode::new(Meta::new(0xCA, "MOV1", AbsoluteBooleanBit), Spc700::mov1::), + OpCode::new(Meta::new(0xCB, "MOV", Y), Spc700::mov::<{ DirectPage }>), + OpCode::new(Meta::new(0xCC, "MOV", Y), Spc700::mov::<{ Absolute }>), + OpCode::new(Meta::new(0xCD, "MOV", Immediate), Spc700::mov::<{ X }>), + OpCode::new(Meta::new(0xCE, "POP", X), Spc700::pop), + OpCode::new(Meta::new(0xCF, "MUL", Implied), Spc700::mul), + OpCode::new(Meta::new(0xD0, "BNE", Implied), Spc700::bne), + OpCode::new(Meta::new(0xD1, "TCALL", Implied), Spc700::tcall::<13>), + OpCode::new(Meta::new(0xD2, "CLR1", DirectPage), Spc700::clr1::<6>), + OpCode::new(Meta::new(0xD3, "BBC", DirectPage), Spc700::bbc::<6>), + OpCode::new(Meta::new(0xD4, "MOV", Accumulator), Spc700::mov::<{ DirectX }>), + OpCode::new(Meta::new(0xD5, "MOV", Accumulator), Spc700::mov::<{ AbsoluteX }>), + OpCode::new(Meta::new(0xD6, "MOV", Accumulator), Spc700::mov::<{ AbsoluteY }>), + OpCode::new(Meta::new(0xD7, "MOV", Accumulator), Spc700::mov::<{ DirectPageIndirectY }>), + OpCode::new(Meta::new(0xD8, "MOV", X), Spc700::mov::<{ DirectPage }>), + OpCode::new(Meta::new(0xD9, "MOV", X), Spc700::mov::<{ DirectY }>), + OpCode::new(Meta::new(0xDA, "MOVW", DirectPage), Spc700::movw::), + OpCode::new(Meta::new(0xDB, "MOV", Y), Spc700::mov::<{ DirectX }>), + OpCode::new(Meta::new(0xDC, "DEC", Y), Spc700::dec), + OpCode::new(Meta::new(0xDD, "MOV", Y), Spc700::mov::<{ Accumulator }>), + OpCode::new(Meta::new(0xDE, "CBNE", DirectX), Spc700::cbne), + OpCode::new(Meta::new(0xDF, "DAA", Implied), Spc700::daa), + OpCode::new(Meta::new(0xE0, "CLRV", Implied), Spc700::clrv), + OpCode::new(Meta::new(0xE1, "TCALL", Implied), Spc700::tcall::<14>), + OpCode::new(Meta::new(0xE2, "SET1", DirectPage), Spc700::set1::<7>), + OpCode::new(Meta::new(0xE3, "BBS", DirectPage), Spc700::bbs::<7>), + OpCode::new(Meta::new(0xE4, "MOV", DirectPage), Spc700::mov::<{ Accumulator }>), + OpCode::new(Meta::new(0xE5, "MOV", Absolute), Spc700::mov::<{ Accumulator }>), + OpCode::new(Meta::new(0xE6, "MOV", IndirectX), Spc700::mov::<{ Accumulator }>), + OpCode::new(Meta::new(0xE7, "MOV", XIndirect), Spc700::mov::<{ Accumulator }>), + OpCode::new(Meta::new(0xE8, "MOV", Immediate), Spc700::mov::<{ Accumulator }>), + OpCode::new(Meta::new(0xE9, "MOV", Absolute), Spc700::mov::<{ X }>), + OpCode::new(Meta::new(0xEA, "NOT1", AbsoluteBooleanBit), Spc700::not1), + OpCode::new(Meta::new(0xEB, "MOV", DirectPage), Spc700::mov::<{ Y }>), + OpCode::new(Meta::new(0xEC, "MOV", Absolute), Spc700::mov::<{ Y }>), + OpCode::new(Meta::new(0xED, "NOTC", Implied), Spc700::notc), + OpCode::new(Meta::new(0xEE, "POP", Y), Spc700::pop), + OpCode::new(Meta::new(0xEF, "SLEEP", Implied), Spc700::sleep), + OpCode::new(Meta::new(0xF0, "BEQ", Implied), Spc700::beq), + OpCode::new(Meta::new(0xF1, "TCALL", Implied), Spc700::tcall::<15>), + OpCode::new(Meta::new(0xF2, "CLR1", DirectPage), Spc700::clr1::<7>), + OpCode::new(Meta::new(0xF3, "BBC", DirectPage), Spc700::bbc::<7>), + OpCode::new(Meta::new(0xF4, "MOV", DirectX), Spc700::mov::<{ Accumulator }>), + OpCode::new(Meta::new(0xF5, "MOV", AbsoluteX), Spc700::mov::<{ Accumulator }>), + OpCode::new(Meta::new(0xF6, "MOV", AbsoluteY), Spc700::mov::<{ Accumulator }>), + OpCode::new(Meta::new(0xF7, "MOV", DirectPageIndirectY), Spc700::mov::<{ Accumulator }>), + OpCode::new(Meta::new(0xF8, "MOV", DirectPage), Spc700::mov::<{ X }>), + OpCode::new(Meta::new(0xF9, "MOV", DirectY), Spc700::mov::<{ X }>), + OpCode::new(Meta::new(0xFA, "MOV", DirectPage), Spc700::mov::<{ DirectPage }>), + OpCode::new(Meta::new(0xFB, "MOV", DirectX), Spc700::mov::<{ Y }>), + OpCode::new(Meta::new(0xFC, "INC", Y), Spc700::inc), + OpCode::new(Meta::new(0xFD, "MOV", Accumulator), Spc700::mov::<{ Y }>), + OpCode::new(Meta::new(0xFE, "DBNZ", Y), Spc700::dbnz), + OpCode::new(Meta::new(0xFF, "STOP", Implied), Spc700::stop), + ] +} diff --git a/aliusnes/src/bus/dma.rs b/aliusnes/src/bus/dma.rs index 0d4d610..515196c 100644 --- a/aliusnes/src/bus/dma.rs +++ b/aliusnes/src/bus/dma.rs @@ -1,6 +1,6 @@ -use crate::{utils::int_traits::ManipulateU16, w65c816::addressing::Address}; +use crate::utils::int_traits::ManipulateU16; -use super::{system_bus::SystemBus, Access}; +use super::{system_bus::SystemBus, Access, Address}; bitfield! { #[derive(Copy, Clone)] diff --git a/aliusnes/src/bus/mod.rs b/aliusnes/src/bus/mod.rs index 5b3ba3a..beef2de 100644 --- a/aliusnes/src/bus/mod.rs +++ b/aliusnes/src/bus/mod.rs @@ -1,11 +1,62 @@ -use crate::w65c816::addressing::Address; - pub(super) mod dma; mod math; pub mod system_bus; mod wram; -pub trait Bus { +#[derive(Clone, Copy)] +pub(crate) struct Address { + pub bank: u8, + pub offset: u16, +} + +impl Address { + pub fn new(offset: u16, bank: u8) -> Self { + Self { bank, offset } + } + + pub fn wrapping_offset_add(&self, rhs: u16) -> Self { + Self { + bank: self.bank, + offset: self.offset.wrapping_add(rhs), + } + } + + pub fn wrapping_add(&self, rhs: u32) -> Self { + (u32::from(*self).wrapping_add(rhs)).into() + } +} + +impl From for Address { + fn from(value: u16) -> Self { + Self { + bank: 0, + offset: value, + } + } +} + +impl From for Address { + fn from(value: u32) -> Self { + Self { + bank: (value >> 16) as u8, + offset: value as u16, + } + } +} + +impl From
for u32 { + fn from(value: Address) -> Self { + (u32::from(value.bank) << 16) | u32::from(value.offset) + } +} + +impl From
for usize { + fn from(value: Address) -> Self { + ((value.bank as usize) << 16) | value.offset as usize + } +} + +pub(crate) trait Bus { fn peek_at(&self, addr: Address) -> Option; fn read_and_tick(&mut self, addr: Address) -> u8; fn write_and_tick(&mut self, addr: Address, data: u8); @@ -14,7 +65,7 @@ pub trait Bus { fn fired_irq(&mut self) -> bool; } -pub trait Access { +pub(crate) trait Access { fn read(&mut self, addr: u16, time: u64) -> Option; fn write(&mut self, addr: u16, data: u8); } diff --git a/aliusnes/src/bus/system_bus.rs b/aliusnes/src/bus/system_bus.rs index 336d0ea..3adab6a 100644 --- a/aliusnes/src/bus/system_bus.rs +++ b/aliusnes/src/bus/system_bus.rs @@ -1,9 +1,6 @@ -use crate::{ - cart::Cart, ppu::Ppu, scheduler::Scheduler, utils::int_traits::ManipulateU16, - w65c816::addressing::Address, -}; +use crate::{cart::Cart, ppu::Ppu, scheduler::Scheduler, utils::int_traits::ManipulateU16}; -use super::{dma::Dma, math::Math, wram::Wram, Access, Bus}; +use super::{dma::Dma, math::Math, wram::Wram, Access, Address, Bus}; pub struct SystemBus { mdr: u8, @@ -60,24 +57,7 @@ impl SystemBus { } } - fn peek(&self, addr: Address) -> Option { - let bank = addr.bank; - let page = addr.offset; - - match bank { - 0x00..=0x3F | 0x80..=0xBF => { - if let 0x00..=0x1F = page.high_byte() { - return Some(self.wram.ram[page as usize & 0x1FFF]); - } - } - 0x7E..=0x7F => return Some(self.wram.ram[u32::from(addr) as usize & 0x1_FFFF]), - _ => {} - } - - self.cart.read(bank.into(), page.into()) - } - - pub fn read(&mut self, addr: Address) -> u8 { + pub(crate) fn read(&mut self, addr: Address) -> u8 { let bank = addr.bank; let page = addr.offset; @@ -140,7 +120,7 @@ impl SystemBus { } } - pub fn write(&mut self, addr: Address, data: u8) { + pub(crate) fn write(&mut self, addr: Address, data: u8) { self.mdr = data; let bank = addr.bank; let page = addr.offset; @@ -170,7 +150,7 @@ impl SystemBus { self.cart.write(bank.into(), page.into(), data); } - pub fn memory_access_cycles(&self, addr: &Address) -> u32 { + pub(crate) fn memory_access_cycles(&self, addr: &Address) -> u32 { const FAST: u32 = 6; const SLOW: u32 = 8; const XSLOW: u32 = 12; @@ -202,7 +182,19 @@ impl SystemBus { impl Bus for SystemBus { fn peek_at(&self, addr: Address) -> Option { - self.peek(addr) + let bank = addr.bank; + let page = addr.offset; + + match bank { + 0x00..=0x3F | 0x80..=0xBF => { + if let 0x00..=0x1F = page.high_byte() { + return Some(self.wram.ram[page as usize & 0x1FFF]); + } + } + 0x7E..=0x7F => return Some(self.wram.ram[u32::from(addr) as usize & 0x1_FFFF]), + _ => {} + } + self.cart.read(bank.into(), page.into()) } fn read_and_tick(&mut self, addr: Address) -> u8 { diff --git a/aliusnes/src/bus/wram.rs b/aliusnes/src/bus/wram.rs index d991c29..3405577 100644 --- a/aliusnes/src/bus/wram.rs +++ b/aliusnes/src/bus/wram.rs @@ -1,5 +1,5 @@ -use super::Access; -use crate::{utils::int_traits::ManipulateU16, w65c816::addressing::Address}; +use super::{Access, Address}; +use crate::utils::int_traits::ManipulateU16; pub struct Wram { pub ram: [u8; 0x20000], diff --git a/aliusnes/src/emu.rs b/aliusnes/src/emu.rs index 5fabe91..0d10a8a 100644 --- a/aliusnes/src/emu.rs +++ b/aliusnes/src/emu.rs @@ -27,7 +27,7 @@ impl Emu { } pub fn reset(&mut self) { - self.w65c816.cpu.reset(&mut self.bus); + self.w65c816.reset(&mut self.bus); } pub fn step(&mut self) { diff --git a/aliusnes/src/utils/mod.rs b/aliusnes/src/utils/mod.rs index 460aa63..7d03e4e 100644 --- a/aliusnes/src/utils/mod.rs +++ b/aliusnes/src/utils/mod.rs @@ -1 +1,5 @@ pub mod int_traits; +#[cfg(test)] +pub(crate) mod testbus; +#[cfg(test)] +pub(crate) mod testrun; diff --git a/aliusnes/tests/utils/test_bus.rs b/aliusnes/src/utils/testbus.rs similarity index 80% rename from aliusnes/tests/utils/test_bus.rs rename to aliusnes/src/utils/testbus.rs index aedea18..973f4cc 100644 --- a/aliusnes/tests/utils/test_bus.rs +++ b/aliusnes/src/utils/testbus.rs @@ -1,4 +1,12 @@ -use aliusnes::{bus::Bus, w65c816::addressing::Address}; +use crate::bus::{Address, Bus}; +use serde::{Deserialize, Deserializer}; +use std::collections::HashMap; + +pub(crate) fn deserialize_as_map<'de, D: Deserializer<'de>>( + deserializer: D, +) -> Result, D::Error> { + Vec::deserialize(deserializer).map(|vec| vec.into_iter().collect()) +} #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum Cycle { diff --git a/aliusnes/tests/tomharte_test.rs b/aliusnes/src/utils/testrun.rs similarity index 59% rename from aliusnes/tests/tomharte_test.rs rename to aliusnes/src/utils/testrun.rs index a4eefbd..8b1f36c 100644 --- a/aliusnes/tests/tomharte_test.rs +++ b/aliusnes/src/utils/testrun.rs @@ -1,27 +1,15 @@ -mod utils; +use crate::utils::testbus::Cycle; +use serde::{Deserialize, Deserializer}; +use std::{fs::File, io::BufReader}; +use xz2::read::XzDecoder; + +use pretty_assertions::Comparison; use std::{ fmt::{Debug, Display}, - fs::File, - io::BufReader, path::PathBuf, }; -use pretty_assertions::Comparison; -use serde::{Deserialize, Deserializer}; -use utils::test_bus::{Cycle, TomHarteBus}; -use xz2::read::XzDecoder; - -mod w65c816 { - use crate::{run_test, utils::cpu_state::CpuState}; - include!(concat!(env!("OUT_DIR"), "/tomharte_65816.rs")); -} - -mod spc700 { - use crate::{run_test, utils::cpu_state::Spc700State}; - include!(concat!(env!("OUT_DIR"), "/tomharte_spc700.rs")); -} - #[derive(Deserialize)] struct TestCase { name: String, @@ -47,30 +35,26 @@ impl TestCase { } pub(crate) fn run_test(name: &str) { - for mut test_case in TestCase::::iter_json(T::test_path(name)) { - let (proc, bus, skip_cycles) = test_case + for test_case in TestCase::::iter_json(T::test_path(name)) { + let (result, mut cycles, skip_cycles) = test_case .initial - .do_step(&test_case.final_state, test_case.cycles.len()); + .step(&test_case.final_state, test_case.cycles.len()); - let mut cycles = bus.cycles.clone(); cycles.sort(); - let state = T::from((proc, bus)); - - let state_match = state == test_case.final_state; + let state_match = result == test_case.final_state; let cycles_match = cycles == test_case.cycles || skip_cycles; if state_match && cycles_match { continue; } - println!("Test {} failed", test_case.name,); - + println!("Test {} failed", test_case.name); if !state_match { println!("Initial: {}", &test_case.initial); println!( "Result: {}", - Comparison::new(&state, &test_case.final_state) + Comparison::new(&result, &test_case.final_state) ); } if !cycles_match { @@ -80,13 +64,11 @@ pub(crate) fn run_test(name: &str) { } } -pub trait OpcodeTest: - Debug + Display + PartialEq + for<'de> Deserialize<'de> + From<(Self::Proc, TomHarteBus)> +pub(crate) trait OpcodeTest: + Debug + Display + PartialEq + for<'de> Deserialize<'de> { - type Proc; - fn test_path(name: &str) -> PathBuf; - fn do_step(&mut self, other: &Self, cycles_len: usize) -> (Self::Proc, TomHarteBus, bool); + fn step(&self, other: &Self, cycles_len: usize) -> (Self, Vec, bool); fn deserialize_cycles<'de, D: Deserializer<'de>>( deserializer: D, ) -> Result, D::Error>; diff --git a/aliusnes/src/w65c816/addressing.rs b/aliusnes/src/w65c816/addressing.rs index 44cf727..c5c6eee 100644 --- a/aliusnes/src/w65c816/addressing.rs +++ b/aliusnes/src/w65c816/addressing.rs @@ -1,5 +1,8 @@ use super::{cpu::Cpu, regsize::RegSize}; -use crate::{bus::Bus, utils::int_traits::ManipulateU16}; +use crate::{ + bus::{Address, Bus}, + utils::int_traits::ManipulateU16, +}; #[derive(Clone, Copy, Debug)] pub enum AddressingMode { @@ -29,59 +32,6 @@ pub enum AddressingMode { BlockMove, } -#[derive(Clone, Copy)] -pub struct Address { - pub bank: u8, - pub offset: u16, -} - -impl Address { - pub fn new(offset: u16, bank: u8) -> Self { - Self { bank, offset } - } - - pub fn wrapping_offset_add(&self, rhs: u16) -> Self { - Self { - bank: self.bank, - offset: self.offset.wrapping_add(rhs), - } - } - - pub fn wrapping_add(&self, rhs: u32) -> Self { - (u32::from(*self).wrapping_add(rhs)).into() - } -} - -impl From for Address { - fn from(value: u16) -> Self { - Self { - bank: 0, - offset: value, - } - } -} - -impl From for Address { - fn from(value: u32) -> Self { - Self { - bank: (value >> 16) as u8, - offset: value as u16, - } - } -} - -impl From
for u32 { - fn from(value: Address) -> Self { - (u32::from(value.bank) << 16) | u32::from(value.offset) - } -} - -impl From
for usize { - fn from(value: Address) -> Self { - ((value.bank as usize) << 16) | value.offset as usize - } -} - impl Cpu { pub fn read_bank0(&mut self, bus: &mut B, page: u16) -> u16 { u16::from_le_bytes([ diff --git a/aliusnes/src/w65c816/cpu.rs b/aliusnes/src/w65c816/cpu.rs index 98cd33d..56a8107 100644 --- a/aliusnes/src/w65c816/cpu.rs +++ b/aliusnes/src/w65c816/cpu.rs @@ -1,9 +1,8 @@ -use super::{ - addressing::{Address, AddressingMode}, - functions::do_push, - regsize::RegSize, +use super::{addressing::AddressingMode, functions::do_push, regsize::RegSize}; +use crate::{ + bus::{Address, Bus}, + utils::int_traits::ManipulateU16, }; -use crate::{bus::Bus, utils::int_traits::ManipulateU16}; pub enum Vector { Cop, @@ -32,7 +31,7 @@ impl Vector { } bitfield!( - pub struct Status(pub u8) { + pub(crate) struct Status(pub u8) { pub carry: bool @ 0, pub zero: bool @ 1, pub irq_disable: bool @ 2, @@ -44,23 +43,7 @@ bitfield!( } ); -#[cfg(feature = "log")] -pub fn format_status(status: &Status) -> String { - [ - if status.negative() { 'N' } else { 'n' }, - if status.overflow() { 'O' } else { 'o' }, - if status.a_reg_size() { 'A' } else { 'a' }, - if status.index_regs_size() { 'X' } else { 'x' }, - if status.decimal() { 'D' } else { 'd' }, - if status.irq_disable() { 'I' } else { 'i' }, - if status.zero() { 'Z' } else { 'z' }, - if status.carry() { 'C' } else { 'c' }, - ] - .iter() - .collect() -} - -pub struct Cpu { +pub(crate) struct Cpu { pub accumulator: u16, pub index_x: u16, pub index_y: u16, diff --git a/aliusnes/src/w65c816/functions.rs b/aliusnes/src/w65c816/functions.rs index 9843bb1..e677d0a 100644 --- a/aliusnes/src/w65c816/functions.rs +++ b/aliusnes/src/w65c816/functions.rs @@ -1,9 +1,8 @@ -use super::{ - addressing::{Address, AddressingMode}, - cpu::Cpu, - regsize::RegSize, +use super::{addressing::AddressingMode, cpu::Cpu, regsize::RegSize}; +use crate::{ + bus::{Address, Bus}, + utils::int_traits::ManipulateU16, }; -use crate::{bus::Bus, utils::int_traits::ManipulateU16}; pub(super) fn do_bin_adc(cpu: &mut Cpu, operand: T) { if T::IS_U16 { diff --git a/aliusnes/src/w65c816/mod.rs b/aliusnes/src/w65c816/mod.rs index 43665a0..bb92e60 100644 --- a/aliusnes/src/w65c816/mod.rs +++ b/aliusnes/src/w65c816/mod.rs @@ -1,64 +1,32 @@ use crate::bus::Bus; -use cpu::Vector; +use cpu::{Cpu, Vector}; +use opcode::{opcode_table, OpCode}; -pub mod addressing; -pub mod cpu; +mod addressing; +mod cpu; mod functions; mod instructions; mod opcode; mod regsize; -#[derive(Clone, Copy)] -pub struct Meta { - pub code: u8, - pub mnemonic: &'static str, - pub mode: addressing::AddressingMode, -} - -impl Meta { - const fn new(code: u8, mnemonic: &'static str, mode: addressing::AddressingMode) -> Self { - Self { - code, - mnemonic, - mode, - } - } -} - -pub struct OpCode { - pub meta: Meta, - pub function: fn(&mut cpu::Cpu, &mut B, addressing::AddressingMode), -} - -impl OpCode { - const fn new( - meta: Meta, - function: fn(&mut cpu::Cpu, &mut B, addressing::AddressingMode), - ) -> Self { - OpCode { meta, function } - } -} - -pub struct W65C816 { - pub cpu: cpu::Cpu, - pub instruction_set: [OpCode; 256], -} - -impl Default for W65C816 { - fn default() -> Self { - Self::new() - } +pub(crate) struct W65C816 { + cpu: Cpu, + instruction_set: [OpCode; 256], } impl W65C816 { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Self { - cpu: cpu::Cpu::new(), + cpu: Cpu::new(), instruction_set: opcode_table(), } } - pub fn step(&mut self, bus: &mut B) { + pub(crate) fn reset(&mut self, bus: &mut B) { + self.cpu.reset(bus); + } + + pub(crate) fn step(&mut self, bus: &mut B) { if self.cpu.stopped { return; } @@ -85,14 +53,13 @@ impl W65C816 { #[cfg(feature = "trace")] { - use crate::w65c816::addressing::Address; - let disasm = address_mode - .disasm_operand(bus, Address::new(self.cpu.program_counter, self.cpu.pbr)); + use crate::bus::Address; + let address = Address::new(self.cpu.program_counter, self.cpu.pbr); + let opcode = opcode.meta.disasm_opcode(bus, address); log::trace!( - "{} {:<10} {:02x}:{:04x} A:{:04x} X:{:04x} Y:{:04x}, S:{:04x}, D:{:04x}, DB:{:02x}, P:{:08b}", - opcode.meta.mnemonic, - disasm, + "{:<13} {:02x}:{:04x} A:{:04x} X:{:04x} Y:{:04x}, S:{:04x}, D:{:04x}, DB:{:02x}, P:{:08b}", + opcode, self.cpu.dbr, (self.cpu.program_counter - 1), self.cpu.accumulator, @@ -106,272 +73,156 @@ impl W65C816 { } instr(&mut self.cpu, bus, address_mode); } +} - pub fn peek_opcode(&self, bus: &B) -> Meta { - let addr = addressing::Address::new(self.cpu.program_counter, self.cpu.pbr); - let op = bus.peek_at(addr).unwrap_or_default(); - self.instruction_set[op as usize].meta +#[cfg(test)] +mod tests { + use super::*; + use crate::bus::Address; + use crate::utils::testbus::{deserialize_as_map, Cycle, TomHarteBus}; + use crate::utils::testrun::{run_test, OpcodeTest}; + use crate::w65c816::cpu::{Cpu, Status}; + use crate::w65c816::opcode::Meta; + use serde::{Deserialize, Deserializer}; + use std::{collections::HashMap, path::PathBuf}; + + impl W65C816 { + fn peek_opcode(&self, bus: &B) -> Meta { + let addr = Address::new(self.cpu.program_counter, self.cpu.pbr); + let op = bus.peek_at(addr).unwrap_or_default(); + self.instruction_set[op as usize].meta + } + } + + #[derive(Debug, PartialEq, Deserialize)] + pub struct CpuState { + pc: u16, + s: u16, + p: u8, + a: u16, + x: u16, + y: u16, + dbr: u8, + d: u16, + pbr: u8, + e: u8, + #[serde(deserialize_with = "deserialize_as_map")] + ram: HashMap, + } + + impl CpuState { + pub fn convert_state(&self) -> (W65C816, TomHarteBus) { + let mut w65c816 = W65C816::new(); + w65c816.cpu.accumulator = self.a; + w65c816.cpu.index_x = self.x; + w65c816.cpu.index_y = self.y; + w65c816.cpu.stack_pointer = self.s; + w65c816.cpu.program_counter = self.pc; + w65c816.cpu.status = Status(self.p); + w65c816.cpu.dpr = self.d; + w65c816.cpu.pbr = self.pbr; + w65c816.cpu.dbr = self.dbr; + w65c816.cpu.emulation_mode = self.e == 1; + w65c816.cpu.stopped = false; + w65c816.cpu.waiting_interrupt = false; + + let bus = TomHarteBus { + memory: self.ram.clone(), + ..Default::default() + }; + + (w65c816, bus) + } + } + + impl From<(Cpu, TomHarteBus)> for CpuState { + fn from(value: (Cpu, TomHarteBus)) -> Self { + Self { + pc: value.0.program_counter, + s: value.0.stack_pointer, + p: value.0.status.0, + a: value.0.accumulator, + x: value.0.index_x, + y: value.0.index_y, + dbr: value.0.dbr, + d: value.0.dpr, + pbr: value.0.pbr, + e: value.0.emulation_mode as u8, + ram: value.1.memory, + } + } + } + + impl std::fmt::Display for CpuState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "pc:{:04X} s:{:04X} p:{:02X} a:{:04X} x:{:04X} y:{:04X} dbr:{:02X} d:{:04X} pbr:{:02X} e:{:01X} \n\t ram:{:02X?}", + self.pc, + self.s, + self.p, + self.a, + self.x, + self.y, + self.dbr, + self.d, + self.pbr, + self.e, + self.ram + ) + } + } + + impl OpcodeTest for CpuState { + fn test_path(name: &str) -> PathBuf { + let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + root_dir.join(format!("tests/65816/{name}.n.json.xz")) + } + + fn step(&self, other: &Self, cycles_len: usize) -> (Self, Vec, bool) { + let (mut w65c816, mut bus) = self.convert_state(); + + let opcode = w65c816.peek_opcode(&bus); + let skip_cycles = if opcode.code == 0x44 || opcode.code == 0x54 { + loop { + if bus.cycles.len() >= (cycles_len - 2) { + break; + } + w65c816.step(&mut bus); + } + w65c816.cpu.program_counter = other.pc; + true + } else { + w65c816.step(&mut bus); + false + }; + + let cycles = bus.cycles.clone(); + (Self::from((w65c816.cpu, bus)), cycles, skip_cycles) + } + + fn deserialize_cycles<'de, D: Deserializer<'de>>( + deserializer: D, + ) -> Result, D::Error> { + let v: Vec<(Option, Option, String)> = Deserialize::deserialize(deserializer)?; + let mut cycles: Vec = v + .iter() + .map(|(addr, value, state)| { + if !(state.contains('p') || state.contains('d')) { + Cycle::Internal + } else if state.contains('r') { + Cycle::Read(addr.unwrap_or_default(), *value) + } else if state.contains('w') { + Cycle::Write(addr.unwrap_or_default(), value.unwrap_or_default()) + } else { + Cycle::Internal + } + }) + .collect(); + cycles.sort(); + Ok(cycles) + } } -} -pub const fn opcode_table() -> [OpCode; 256] { - use addressing::AddressingMode::*; - [ - OpCode::new(Meta::new(0x00, "BRK", Immediate), W65C816::brk), - OpCode::new(Meta::new(0x01, "ORA", IndirectX), W65C816::ora), - OpCode::new(Meta::new(0x02, "COP", Immediate), W65C816::cop), - OpCode::new(Meta::new(0x03, "ORA", StackRelative), W65C816::ora), - OpCode::new(Meta::new(0x04, "TSB", Direct), W65C816::tsb), - OpCode::new(Meta::new(0x05, "ORA", Direct), W65C816::ora), - OpCode::new(Meta::new(0x06, "ASL", Direct), W65C816::asl), - OpCode::new(Meta::new(0x07, "ORA", IndirectLong), W65C816::ora), - OpCode::new(Meta::new(0x08, "PHP", Implied), W65C816::php), - OpCode::new(Meta::new(0x09, "ORA", Immediate), W65C816::ora), - OpCode::new(Meta::new(0x0a, "ASL", Accumulator), W65C816::asl), - OpCode::new(Meta::new(0x0b, "PHD", Implied), W65C816::phd), - OpCode::new(Meta::new(0x0c, "TSB", Absolute), W65C816::tsb), - OpCode::new(Meta::new(0x0d, "ORA", Absolute), W65C816::ora), - OpCode::new(Meta::new(0x0e, "ASL", Absolute), W65C816::asl), - OpCode::new(Meta::new(0x0f, "ORA", AbsoluteLong), W65C816::ora), - OpCode::new(Meta::new(0x10, "BPL", Relative), W65C816::bpl), - OpCode::new(Meta::new(0x11, "ORA", IndirectY), W65C816::ora), - OpCode::new(Meta::new(0x12, "ORA", Indirect), W65C816::ora), - OpCode::new(Meta::new(0x13, "ORA", StackRelIndirectY), W65C816::ora), - OpCode::new(Meta::new(0x14, "TRB", Direct), W65C816::trb), - OpCode::new(Meta::new(0x15, "ORA", DirectX), W65C816::ora), - OpCode::new(Meta::new(0x16, "ASL", DirectX), W65C816::asl), - OpCode::new(Meta::new(0x17, "ORA", IndirectLongY), W65C816::ora), - OpCode::new(Meta::new(0x18, "CLC", Implied), W65C816::clc), - OpCode::new(Meta::new(0x19, "ORA", AbsoluteY), W65C816::ora), - OpCode::new(Meta::new(0x1a, "INC", Accumulator), W65C816::inc), - OpCode::new(Meta::new(0x1b, "TCS", Implied), W65C816::tcs), - OpCode::new(Meta::new(0x1c, "TRB", Absolute), W65C816::trb), - OpCode::new(Meta::new(0x1d, "ORA", AbsoluteX), W65C816::ora), - OpCode::new(Meta::new(0x1e, "ASL", AbsoluteX), W65C816::asl), - OpCode::new(Meta::new(0x1f, "ORA", AbsoluteLongX), W65C816::ora), - OpCode::new(Meta::new(0x20, "JSR", Absolute), W65C816::jsr), - OpCode::new(Meta::new(0x21, "AND", IndirectX), W65C816::and), - OpCode::new(Meta::new(0x22, "JSL", AbsoluteLong), W65C816::jsl), - OpCode::new(Meta::new(0x23, "AND", StackRelative), W65C816::and), - OpCode::new(Meta::new(0x24, "BIT", Direct), W65C816::bit), - OpCode::new(Meta::new(0x25, "AND", Direct), W65C816::and), - OpCode::new(Meta::new(0x26, "ROL", Direct), W65C816::rol), - OpCode::new(Meta::new(0x27, "AND", IndirectLong), W65C816::and), - OpCode::new(Meta::new(0x28, "PLP", Implied), W65C816::plp), - OpCode::new(Meta::new(0x29, "AND", Immediate), W65C816::and), - OpCode::new(Meta::new(0x2a, "ROL", Accumulator), W65C816::rol), - OpCode::new(Meta::new(0x2b, "PLD", Implied), W65C816::pld), - OpCode::new(Meta::new(0x2c, "BIT", Absolute), W65C816::bit), - OpCode::new(Meta::new(0x2d, "AND", Absolute), W65C816::and), - OpCode::new(Meta::new(0x2e, "ROL", Absolute), W65C816::rol), - OpCode::new(Meta::new(0x2f, "AND", AbsoluteLong), W65C816::and), - OpCode::new(Meta::new(0x30, "BMI", Relative), W65C816::bmi), - OpCode::new(Meta::new(0x31, "AND", IndirectY), W65C816::and), - OpCode::new(Meta::new(0x32, "AND", Indirect), W65C816::and), - OpCode::new(Meta::new(0x33, "AND", StackRelIndirectY), W65C816::and), - OpCode::new(Meta::new(0x34, "BIT", DirectX), W65C816::bit), - OpCode::new(Meta::new(0x35, "AND", DirectX), W65C816::and), - OpCode::new(Meta::new(0x36, "ROL", DirectX), W65C816::rol), - OpCode::new(Meta::new(0x37, "AND", IndirectLongY), W65C816::and), - OpCode::new(Meta::new(0x38, "SEC", Implied), W65C816::sec), - OpCode::new(Meta::new(0x39, "AND", AbsoluteY), W65C816::and), - OpCode::new(Meta::new(0x3a, "DEC", Accumulator), W65C816::dec), - OpCode::new(Meta::new(0x3b, "TSC", Implied), W65C816::tsc), - OpCode::new(Meta::new(0x3c, "BIT", AbsoluteX), W65C816::bit), - OpCode::new(Meta::new(0x3d, "AND", AbsoluteX), W65C816::and), - OpCode::new(Meta::new(0x3e, "ROL", AbsoluteX), W65C816::rol), - OpCode::new(Meta::new(0x3f, "AND", AbsoluteLongX), W65C816::and), - OpCode::new(Meta::new(0x40, "RTI", Implied), W65C816::rti), - OpCode::new(Meta::new(0x41, "EOR", IndirectX), W65C816::eor), - OpCode::new(Meta::new(0x42, "WDM", Immediate), W65C816::wdm), - OpCode::new(Meta::new(0x43, "EOR", StackRelative), W65C816::eor), - OpCode::new(Meta::new(0x44, "MVP", BlockMove), W65C816::mvp), - OpCode::new(Meta::new(0x45, "EOR", Direct), W65C816::eor), - OpCode::new(Meta::new(0x46, "LSR", Direct), W65C816::lsr), - OpCode::new(Meta::new(0x47, "EOR", IndirectLong), W65C816::eor), - OpCode::new(Meta::new(0x48, "PHA", Implied), W65C816::pha), - OpCode::new(Meta::new(0x49, "EOR", Immediate), W65C816::eor), - OpCode::new(Meta::new(0x4a, "LSR", Accumulator), W65C816::lsr), - OpCode::new(Meta::new(0x4b, "PHK", Implied), W65C816::phk), - OpCode::new(Meta::new(0x4c, "JMP", Absolute), W65C816::jmp), - OpCode::new(Meta::new(0x4d, "EOR", Absolute), W65C816::eor), - OpCode::new(Meta::new(0x4e, "LSR", Absolute), W65C816::lsr), - OpCode::new(Meta::new(0x4f, "EOR", AbsoluteLong), W65C816::eor), - OpCode::new(Meta::new(0x50, "BVC", Relative), W65C816::bvc), - OpCode::new(Meta::new(0x51, "EOR", IndirectY), W65C816::eor), - OpCode::new(Meta::new(0x52, "EOR", Indirect), W65C816::eor), - OpCode::new(Meta::new(0x53, "EOR", StackRelIndirectY), W65C816::eor), - OpCode::new(Meta::new(0x54, "MVN", BlockMove), W65C816::mvn), - OpCode::new(Meta::new(0x55, "EOR", DirectX), W65C816::eor), - OpCode::new(Meta::new(0x56, "LSR", DirectX), W65C816::lsr), - OpCode::new(Meta::new(0x57, "EOR", IndirectLongY), W65C816::eor), - OpCode::new(Meta::new(0x58, "CLI", Implied), W65C816::cli), - OpCode::new(Meta::new(0x59, "EOR", AbsoluteY), W65C816::eor), - OpCode::new(Meta::new(0x5a, "PHY", Implied), W65C816::phy), - OpCode::new(Meta::new(0x5b, "TCD", Implied), W65C816::tcd), - OpCode::new(Meta::new(0x5c, "JMP", AbsoluteLong), W65C816::jmp), - OpCode::new(Meta::new(0x5d, "EOR", AbsoluteX), W65C816::eor), - OpCode::new(Meta::new(0x5e, "LSR", AbsoluteX), W65C816::lsr), - OpCode::new(Meta::new(0x5f, "EOR", AbsoluteLongX), W65C816::eor), - OpCode::new(Meta::new(0x60, "RTS", Implied), W65C816::rts), - OpCode::new(Meta::new(0x61, "ADC", IndirectX), W65C816::adc), - OpCode::new(Meta::new(0x62, "PER", RelativeLong), W65C816::per), - OpCode::new(Meta::new(0x63, "ADC", StackRelative), W65C816::adc), - OpCode::new(Meta::new(0x64, "STZ", Direct), W65C816::stz), - OpCode::new(Meta::new(0x65, "ADC", Direct), W65C816::adc), - OpCode::new(Meta::new(0x66, "ROR", Direct), W65C816::ror), - OpCode::new(Meta::new(0x67, "ADC", IndirectLong), W65C816::adc), - OpCode::new(Meta::new(0x68, "PLA", Implied), W65C816::pla), - OpCode::new(Meta::new(0x69, "ADC", Immediate), W65C816::adc), - OpCode::new(Meta::new(0x6a, "ROR", Accumulator), W65C816::ror), - OpCode::new(Meta::new(0x6b, "RTL", Implied), W65C816::rtl), - OpCode::new(Meta::new(0x6c, "JMP", AbsoluteIndirect), W65C816::jmp), - OpCode::new(Meta::new(0x6d, "ADC", Absolute), W65C816::adc), - OpCode::new(Meta::new(0x6e, "ROR", Absolute), W65C816::ror), - OpCode::new(Meta::new(0x6f, "ADC", AbsoluteLong), W65C816::adc), - OpCode::new(Meta::new(0x70, "BVS", Relative), W65C816::bvs), - OpCode::new(Meta::new(0x71, "ADC", IndirectY), W65C816::adc), - OpCode::new(Meta::new(0x72, "ADC", Indirect), W65C816::adc), - OpCode::new(Meta::new(0x73, "ADC", StackRelIndirectY), W65C816::adc), - OpCode::new(Meta::new(0x74, "STZ", DirectX), W65C816::stz), - OpCode::new(Meta::new(0x75, "ADC", DirectX), W65C816::adc), - OpCode::new(Meta::new(0x76, "ROR", DirectX), W65C816::ror), - OpCode::new(Meta::new(0x77, "ADC", IndirectLongY), W65C816::adc), - OpCode::new(Meta::new(0x78, "SEI", Implied), W65C816::sei), - OpCode::new(Meta::new(0x79, "ADC", AbsoluteY), W65C816::adc), - OpCode::new(Meta::new(0x7a, "PLY", Implied), W65C816::ply), - OpCode::new(Meta::new(0x7b, "TDC", Implied), W65C816::tdc), - OpCode::new(Meta::new(0x7c, "JMP", AbsoluteIndirectX), W65C816::jmp), - OpCode::new(Meta::new(0x7d, "ADC", AbsoluteX), W65C816::adc), - OpCode::new(Meta::new(0x7e, "ROR", AbsoluteX), W65C816::ror), - OpCode::new(Meta::new(0x7f, "ADC", AbsoluteLongX), W65C816::adc), - OpCode::new(Meta::new(0x80, "BRA", Relative), W65C816::bra), - OpCode::new(Meta::new(0x81, "STA", IndirectX), W65C816::sta), - OpCode::new(Meta::new(0x82, "BRL", RelativeLong), W65C816::brl), - OpCode::new(Meta::new(0x83, "STA", StackRelative), W65C816::sta), - OpCode::new(Meta::new(0x84, "STY", Direct), W65C816::sty), - OpCode::new(Meta::new(0x85, "STA", Direct), W65C816::sta), - OpCode::new(Meta::new(0x86, "STX", Direct), W65C816::stx), - OpCode::new(Meta::new(0x87, "STA", IndirectLong), W65C816::sta), - OpCode::new(Meta::new(0x88, "DEY", Implied), W65C816::dey), - OpCode::new(Meta::new(0x89, "BIT", Immediate), W65C816::bit), - OpCode::new(Meta::new(0x8a, "TXA", Implied), W65C816::txa), - OpCode::new(Meta::new(0x8b, "PHB", Implied), W65C816::phb), - OpCode::new(Meta::new(0x8c, "STY", Absolute), W65C816::sty), - OpCode::new(Meta::new(0x8d, "STA", Absolute), W65C816::sta), - OpCode::new(Meta::new(0x8e, "STX", Absolute), W65C816::stx), - OpCode::new(Meta::new(0x8f, "STA", AbsoluteLong), W65C816::sta), - OpCode::new(Meta::new(0x90, "BCC", Relative), W65C816::bcc), - OpCode::new(Meta::new(0x91, "STA", IndirectY), W65C816::sta), - OpCode::new(Meta::new(0x92, "STA", Indirect), W65C816::sta), - OpCode::new(Meta::new(0x93, "STA", StackRelIndirectY), W65C816::sta), - OpCode::new(Meta::new(0x94, "STY", DirectX), W65C816::sty), - OpCode::new(Meta::new(0x95, "STA", DirectX), W65C816::sta), - OpCode::new(Meta::new(0x96, "STX", DirectY), W65C816::stx), - OpCode::new(Meta::new(0x97, "STA", IndirectLongY), W65C816::sta), - OpCode::new(Meta::new(0x98, "TYA", Implied), W65C816::tya), - OpCode::new(Meta::new(0x99, "STA", AbsoluteY), W65C816::sta), - OpCode::new(Meta::new(0x9a, "TXS", Implied), W65C816::txs), - OpCode::new(Meta::new(0x9b, "TXY", Implied), W65C816::txy), - OpCode::new(Meta::new(0x9c, "STZ", Absolute), W65C816::stz), - OpCode::new(Meta::new(0x9d, "STA", AbsoluteX), W65C816::sta), - OpCode::new(Meta::new(0x9e, "STZ", AbsoluteX), W65C816::stz), - OpCode::new(Meta::new(0x9f, "STA", AbsoluteLongX), W65C816::sta), - OpCode::new(Meta::new(0xa0, "LDY", Immediate), W65C816::ldy), - OpCode::new(Meta::new(0xa1, "LDA", IndirectX), W65C816::lda), - OpCode::new(Meta::new(0xa2, "LDX", Immediate), W65C816::ldx), - OpCode::new(Meta::new(0xa3, "LDA", StackRelative), W65C816::lda), - OpCode::new(Meta::new(0xa4, "LDY", Direct), W65C816::ldy), - OpCode::new(Meta::new(0xa5, "LDA", Direct), W65C816::lda), - OpCode::new(Meta::new(0xa6, "LDX", Direct), W65C816::ldx), - OpCode::new(Meta::new(0xa7, "LDA", IndirectLong), W65C816::lda), - OpCode::new(Meta::new(0xa8, "TAY", Implied), W65C816::tay), - OpCode::new(Meta::new(0xa9, "LDA", Immediate), W65C816::lda), - OpCode::new(Meta::new(0xaa, "TAX", Implied), W65C816::tax), - OpCode::new(Meta::new(0xab, "PLB", Implied), W65C816::plb), - OpCode::new(Meta::new(0xac, "LDY", Absolute), W65C816::ldy), - OpCode::new(Meta::new(0xad, "LDA", Absolute), W65C816::lda), - OpCode::new(Meta::new(0xae, "LDX", Absolute), W65C816::ldx), - OpCode::new(Meta::new(0xaf, "LDA", AbsoluteLong), W65C816::lda), - OpCode::new(Meta::new(0xb0, "BCS", Relative), W65C816::bcs), - OpCode::new(Meta::new(0xb1, "LDA", IndirectY), W65C816::lda), - OpCode::new(Meta::new(0xb2, "LDA", Indirect), W65C816::lda), - OpCode::new(Meta::new(0xb3, "LDA", StackRelIndirectY), W65C816::lda), - OpCode::new(Meta::new(0xb4, "LDY", DirectX), W65C816::ldy), - OpCode::new(Meta::new(0xb5, "LDA", DirectX), W65C816::lda), - OpCode::new(Meta::new(0xb6, "LDX", DirectY), W65C816::ldx), - OpCode::new(Meta::new(0xb7, "LDA", IndirectLongY), W65C816::lda), - OpCode::new(Meta::new(0xb8, "CLV", Implied), W65C816::clv), - OpCode::new(Meta::new(0xb9, "LDA", AbsoluteY), W65C816::lda), - OpCode::new(Meta::new(0xba, "TSX", Implied), W65C816::tsx), - OpCode::new(Meta::new(0xbb, "TYX", Implied), W65C816::tyx), - OpCode::new(Meta::new(0xbc, "LDY", AbsoluteX), W65C816::ldy), - OpCode::new(Meta::new(0xbd, "LDA", AbsoluteX), W65C816::lda), - OpCode::new(Meta::new(0xbe, "LDX", AbsoluteY), W65C816::ldx), - OpCode::new(Meta::new(0xbf, "LDA", AbsoluteLongX), W65C816::lda), - OpCode::new(Meta::new(0xc0, "CPY", Immediate), W65C816::cpy), - OpCode::new(Meta::new(0xc1, "CMP", IndirectX), W65C816::cmp), - OpCode::new(Meta::new(0xc2, "REP", Immediate), W65C816::rep), - OpCode::new(Meta::new(0xc3, "CMP", StackRelative), W65C816::cmp), - OpCode::new(Meta::new(0xc4, "CPY", Direct), W65C816::cpy), - OpCode::new(Meta::new(0xc5, "CMP", Direct), W65C816::cmp), - OpCode::new(Meta::new(0xc6, "DEC", Direct), W65C816::dec), - OpCode::new(Meta::new(0xc7, "CMP", IndirectLong), W65C816::cmp), - OpCode::new(Meta::new(0xc8, "INY", Implied), W65C816::iny), - OpCode::new(Meta::new(0xc9, "CMP", Immediate), W65C816::cmp), - OpCode::new(Meta::new(0xca, "DEX", Implied), W65C816::dex), - OpCode::new(Meta::new(0xcb, "WAI", Implied), W65C816::wai), - OpCode::new(Meta::new(0xcc, "CPY", Absolute), W65C816::cpy), - OpCode::new(Meta::new(0xcd, "CMP", Absolute), W65C816::cmp), - OpCode::new(Meta::new(0xce, "DEC", Absolute), W65C816::dec), - OpCode::new(Meta::new(0xcf, "CMP", AbsoluteLong), W65C816::cmp), - OpCode::new(Meta::new(0xd0, "BNE", Relative), W65C816::bne), - OpCode::new(Meta::new(0xd1, "CMP", IndirectY), W65C816::cmp), - OpCode::new(Meta::new(0xd2, "CMP", Indirect), W65C816::cmp), - OpCode::new(Meta::new(0xd3, "CMP", StackRelIndirectY), W65C816::cmp), - OpCode::new(Meta::new(0xd4, "PEI", Direct), W65C816::pei), - OpCode::new(Meta::new(0xd5, "CMP", DirectX), W65C816::cmp), - OpCode::new(Meta::new(0xd6, "DEC", DirectX), W65C816::dec), - OpCode::new(Meta::new(0xd7, "CMP", IndirectLongY), W65C816::cmp), - OpCode::new(Meta::new(0xd8, "CLD", Implied), W65C816::cld), - OpCode::new(Meta::new(0xd9, "CMP", AbsoluteY), W65C816::cmp), - OpCode::new(Meta::new(0xda, "PHX", Implied), W65C816::phx), - OpCode::new(Meta::new(0xdb, "STP", Implied), W65C816::stp), - OpCode::new(Meta::new(0xdc, "JML", AbsoluteIndirectLong), W65C816::jml), - OpCode::new(Meta::new(0xdd, "CMP", AbsoluteX), W65C816::cmp), - OpCode::new(Meta::new(0xde, "DEC", AbsoluteX), W65C816::dec), - OpCode::new(Meta::new(0xdf, "CMP", AbsoluteLongX), W65C816::cmp), - OpCode::new(Meta::new(0xe0, "CPX", Immediate), W65C816::cpx), - OpCode::new(Meta::new(0xe1, "SBC", IndirectX), W65C816::sbc), - OpCode::new(Meta::new(0xe2, "SEP", Immediate), W65C816::sep), - OpCode::new(Meta::new(0xe3, "SBC", StackRelative), W65C816::sbc), - OpCode::new(Meta::new(0xe4, "CPX", Direct), W65C816::cpx), - OpCode::new(Meta::new(0xe5, "SBC", Direct), W65C816::sbc), - OpCode::new(Meta::new(0xe6, "INC", Direct), W65C816::inc), - OpCode::new(Meta::new(0xe7, "SBC", IndirectLong), W65C816::sbc), - OpCode::new(Meta::new(0xe8, "INX", Implied), W65C816::inx), - OpCode::new(Meta::new(0xe9, "SBC", Immediate), W65C816::sbc), - OpCode::new(Meta::new(0xea, "NOP", Implied), W65C816::nop), - OpCode::new(Meta::new(0xeb, "XBA", Implied), W65C816::xba), - OpCode::new(Meta::new(0xec, "CPX", Absolute), W65C816::cpx), - OpCode::new(Meta::new(0xed, "SBC", Absolute), W65C816::sbc), - OpCode::new(Meta::new(0xee, "INC", Absolute), W65C816::inc), - OpCode::new(Meta::new(0xef, "SBC", AbsoluteLong), W65C816::sbc), - OpCode::new(Meta::new(0xf0, "BEQ", Relative), W65C816::beq), - OpCode::new(Meta::new(0xf1, "SBC", IndirectY), W65C816::sbc), - OpCode::new(Meta::new(0xf2, "SBC", Indirect), W65C816::sbc), - OpCode::new(Meta::new(0xf3, "SBC", StackRelIndirectY), W65C816::sbc), - OpCode::new(Meta::new(0xf4, "PEA", Immediate), W65C816::pea), - OpCode::new(Meta::new(0xf5, "SBC", DirectX), W65C816::sbc), - OpCode::new(Meta::new(0xf6, "INC", DirectX), W65C816::inc), - OpCode::new(Meta::new(0xf7, "SBC", IndirectLongY), W65C816::sbc), - OpCode::new(Meta::new(0xf8, "SED", Implied), W65C816::sed), - OpCode::new(Meta::new(0xf9, "SBC", AbsoluteY), W65C816::sbc), - OpCode::new(Meta::new(0xfa, "PLX", Implied), W65C816::plx), - OpCode::new(Meta::new(0xfb, "XCE", Implied), W65C816::xce), - OpCode::new(Meta::new(0xfc, "JSR", AbsoluteIndirectX), W65C816::jsr), - OpCode::new(Meta::new(0xfd, "SBC", AbsoluteX), W65C816::sbc), - OpCode::new(Meta::new(0xfe, "INC", AbsoluteX), W65C816::inc), - OpCode::new(Meta::new(0xff, "SBC", AbsoluteLongX), W65C816::sbc), - ] + include!(concat!(env!("OUT_DIR"), "/tomharte_65816.rs")); } diff --git a/aliusnes/src/w65c816/opcode.rs b/aliusnes/src/w65c816/opcode.rs index 30248bc..dbcdece 100644 --- a/aliusnes/src/w65c816/opcode.rs +++ b/aliusnes/src/w65c816/opcode.rs @@ -1,8 +1,96 @@ +#[cfg(feature = "trace")] +use crate::bus::Address; use crate::{ bus::Bus, - w65c816::addressing::{Address, AddressingMode}, + w65c816::{addressing::AddressingMode, Cpu, W65C816}, }; +#[derive(Clone, Copy)] +pub(crate) struct Meta { + pub code: u8, + mnemonic: &'static str, + pub mode: AddressingMode, +} + +impl Meta { + const fn new(code: u8, mnemonic: &'static str, mode: AddressingMode) -> Self { + Self { + code, + mnemonic, + mode, + } + } +} + +#[cfg(feature = "trace")] +impl Meta { + pub(crate) fn disasm_opcode(&self, bus: &B, addr: Address) -> String { + let operand = match self.mode.operand_size() { + 0 => 0, + 1 => bus.peek_at(addr).unwrap_or_default() as u32, + 2 => u16::from_le_bytes([ + bus.peek_at(addr).unwrap_or_default(), + bus.peek_at(addr.wrapping_offset_add(1)).unwrap_or_default(), + ]) as u32, + 3 => u32::from_le_bytes([ + bus.peek_at(addr).unwrap_or_default(), + bus.peek_at(addr.wrapping_offset_add(1)).unwrap_or_default(), + bus.peek_at(addr.wrapping_offset_add(2)).unwrap_or_default(), + 0, + ]), + _ => unreachable!(), + }; + + match self.mode { + AddressingMode::Accumulator => "".to_string(), + AddressingMode::Implied => "".to_string(), + AddressingMode::Immediate => format!("{} #${:02X}", self.mnemonic, operand), + AddressingMode::Relative => format!("{} ${:04X}", self.mnemonic, operand), + AddressingMode::RelativeLong => format!("{} ${:06X}", self.mnemonic, operand), + AddressingMode::Direct => format!("{} ${:02X}", self.mnemonic, operand), + AddressingMode::DirectX => format!("{} ${:02X},X", self.mnemonic, operand), + AddressingMode::DirectY => format!("{} ${:02X},Y", self.mnemonic, operand), + AddressingMode::Indirect => format!("{} (${:02X})", self.mnemonic, operand), + AddressingMode::IndirectX => format!("{} (${:02X},X)", self.mnemonic, operand), + AddressingMode::IndirectY => format!("{} (${:02X}),Y", self.mnemonic, operand), + AddressingMode::IndirectLong => format!("{} [${:02X}]", self.mnemonic, operand), + AddressingMode::IndirectLongY => format!("{} [${:02X}],Y", self.mnemonic, operand), + AddressingMode::Absolute => format!("{} ${:04X}", self.mnemonic, operand), + AddressingMode::AbsoluteX => format!("{} ${:04X},X", self.mnemonic, operand), + AddressingMode::AbsoluteY => format!("{} ${:04X},Y", self.mnemonic, operand), + AddressingMode::AbsoluteLong => format!("{} ${:06X}", self.mnemonic, operand), + AddressingMode::AbsoluteLongX => format!("{} ${:06X},X", self.mnemonic, operand), + AddressingMode::AbsoluteIndirect => format!("{} (${:04X})", self.mnemonic, operand), + AddressingMode::AbsoluteIndirectLong => format!("{} [${:04X}]", self.mnemonic, operand), + AddressingMode::AbsoluteIndirectX => format!("{} (${:04X},X)", self.mnemonic, operand), + AddressingMode::StackRelative => format!("{} ${:02X},S", self.mnemonic, operand), + AddressingMode::StackRelIndirectY => { + format!("{} (${:02X},S),Y", self.mnemonic, operand) + } + AddressingMode::BlockMove => { + format!( + "{} ${:02X} ${:02X}", + self.mnemonic, + operand as u8, + (operand >> 8) & 0xFF + ) + } + } + } +} + +pub(crate) struct OpCode { + pub meta: Meta, + pub function: fn(&mut Cpu, &mut B, AddressingMode), +} + +impl OpCode { + const fn new(meta: Meta, function: fn(&mut Cpu, &mut B, AddressingMode)) -> Self { + OpCode { meta, function } + } +} + +#[cfg(feature = "trace")] impl AddressingMode { const fn operand_size(&self) -> u8 { match self { @@ -32,51 +120,266 @@ impl AddressingMode { AddressingMode::BlockMove => 1, } } +} - pub fn disasm_operand(&self, bus: &B, addr: Address) -> String { - let operand = match self.operand_size() { - 0 => 0, - 1 => bus.peek_at(addr).unwrap_or_default() as u32, - 2 => u16::from_le_bytes([ - bus.peek_at(addr).unwrap_or_default(), - bus.peek_at(addr.wrapping_offset_add(1)).unwrap_or_default(), - ]) as u32, - 3 => u32::from_le_bytes([ - bus.peek_at(addr).unwrap_or_default(), - bus.peek_at(addr.wrapping_offset_add(1)).unwrap_or_default(), - bus.peek_at(addr.wrapping_offset_add(2)).unwrap_or_default(), - 0, - ]), - _ => unreachable!(), - }; - - match self { - AddressingMode::Accumulator => "".to_string(), - AddressingMode::Implied => "".to_string(), - AddressingMode::Immediate => format!("#${:02X}", operand), - AddressingMode::Relative => format!("${:04X}", operand), - AddressingMode::RelativeLong => format!("${:06X}", operand), - AddressingMode::Direct => format!("${:02X}", operand), - AddressingMode::DirectX => format!("${:02X},X", operand), - AddressingMode::DirectY => format!("${:02X},Y", operand), - AddressingMode::Indirect => format!("(${:02X})", operand), - AddressingMode::IndirectX => format!("(${:02X},X)", operand), - AddressingMode::IndirectY => format!("(${:02X}),Y", operand), - AddressingMode::IndirectLong => format!("[${:02X}]", operand), - AddressingMode::IndirectLongY => format!("[${:02X}],Y", operand), - AddressingMode::Absolute => format!("${:04X}", operand), - AddressingMode::AbsoluteX => format!("${:04X},X", operand), - AddressingMode::AbsoluteY => format!("${:04X},Y", operand), - AddressingMode::AbsoluteLong => format!("${:06X}", operand), - AddressingMode::AbsoluteLongX => format!("${:06X},X", operand), - AddressingMode::AbsoluteIndirect => format!("(${:04X})", operand), - AddressingMode::AbsoluteIndirectLong => format!("[${:04X}]", operand), - AddressingMode::AbsoluteIndirectX => format!("(${:04X},X)", operand), - AddressingMode::StackRelative => format!("${:02X},S", operand), - AddressingMode::StackRelIndirectY => format!("(${:02X},S),Y", operand), - AddressingMode::BlockMove => { - format!("${:02X} ${:02X}", operand as u8, (operand >> 8) & 0xFF) - } - } - } +pub(crate) const fn opcode_table() -> [OpCode; 256] { + use super::addressing::AddressingMode::*; + [ + OpCode::new(Meta::new(0x00, "BRK", Immediate), W65C816::brk), + OpCode::new(Meta::new(0x01, "ORA", IndirectX), W65C816::ora), + OpCode::new(Meta::new(0x02, "COP", Immediate), W65C816::cop), + OpCode::new(Meta::new(0x03, "ORA", StackRelative), W65C816::ora), + OpCode::new(Meta::new(0x04, "TSB", Direct), W65C816::tsb), + OpCode::new(Meta::new(0x05, "ORA", Direct), W65C816::ora), + OpCode::new(Meta::new(0x06, "ASL", Direct), W65C816::asl), + OpCode::new(Meta::new(0x07, "ORA", IndirectLong), W65C816::ora), + OpCode::new(Meta::new(0x08, "PHP", Implied), W65C816::php), + OpCode::new(Meta::new(0x09, "ORA", Immediate), W65C816::ora), + OpCode::new(Meta::new(0x0a, "ASL", Accumulator), W65C816::asl), + OpCode::new(Meta::new(0x0b, "PHD", Implied), W65C816::phd), + OpCode::new(Meta::new(0x0c, "TSB", Absolute), W65C816::tsb), + OpCode::new(Meta::new(0x0d, "ORA", Absolute), W65C816::ora), + OpCode::new(Meta::new(0x0e, "ASL", Absolute), W65C816::asl), + OpCode::new(Meta::new(0x0f, "ORA", AbsoluteLong), W65C816::ora), + OpCode::new(Meta::new(0x10, "BPL", Relative), W65C816::bpl), + OpCode::new(Meta::new(0x11, "ORA", IndirectY), W65C816::ora), + OpCode::new(Meta::new(0x12, "ORA", Indirect), W65C816::ora), + OpCode::new(Meta::new(0x13, "ORA", StackRelIndirectY), W65C816::ora), + OpCode::new(Meta::new(0x14, "TRB", Direct), W65C816::trb), + OpCode::new(Meta::new(0x15, "ORA", DirectX), W65C816::ora), + OpCode::new(Meta::new(0x16, "ASL", DirectX), W65C816::asl), + OpCode::new(Meta::new(0x17, "ORA", IndirectLongY), W65C816::ora), + OpCode::new(Meta::new(0x18, "CLC", Implied), W65C816::clc), + OpCode::new(Meta::new(0x19, "ORA", AbsoluteY), W65C816::ora), + OpCode::new(Meta::new(0x1a, "INC", Accumulator), W65C816::inc), + OpCode::new(Meta::new(0x1b, "TCS", Implied), W65C816::tcs), + OpCode::new(Meta::new(0x1c, "TRB", Absolute), W65C816::trb), + OpCode::new(Meta::new(0x1d, "ORA", AbsoluteX), W65C816::ora), + OpCode::new(Meta::new(0x1e, "ASL", AbsoluteX), W65C816::asl), + OpCode::new(Meta::new(0x1f, "ORA", AbsoluteLongX), W65C816::ora), + OpCode::new(Meta::new(0x20, "JSR", Absolute), W65C816::jsr), + OpCode::new(Meta::new(0x21, "AND", IndirectX), W65C816::and), + OpCode::new(Meta::new(0x22, "JSL", AbsoluteLong), W65C816::jsl), + OpCode::new(Meta::new(0x23, "AND", StackRelative), W65C816::and), + OpCode::new(Meta::new(0x24, "BIT", Direct), W65C816::bit), + OpCode::new(Meta::new(0x25, "AND", Direct), W65C816::and), + OpCode::new(Meta::new(0x26, "ROL", Direct), W65C816::rol), + OpCode::new(Meta::new(0x27, "AND", IndirectLong), W65C816::and), + OpCode::new(Meta::new(0x28, "PLP", Implied), W65C816::plp), + OpCode::new(Meta::new(0x29, "AND", Immediate), W65C816::and), + OpCode::new(Meta::new(0x2a, "ROL", Accumulator), W65C816::rol), + OpCode::new(Meta::new(0x2b, "PLD", Implied), W65C816::pld), + OpCode::new(Meta::new(0x2c, "BIT", Absolute), W65C816::bit), + OpCode::new(Meta::new(0x2d, "AND", Absolute), W65C816::and), + OpCode::new(Meta::new(0x2e, "ROL", Absolute), W65C816::rol), + OpCode::new(Meta::new(0x2f, "AND", AbsoluteLong), W65C816::and), + OpCode::new(Meta::new(0x30, "BMI", Relative), W65C816::bmi), + OpCode::new(Meta::new(0x31, "AND", IndirectY), W65C816::and), + OpCode::new(Meta::new(0x32, "AND", Indirect), W65C816::and), + OpCode::new(Meta::new(0x33, "AND", StackRelIndirectY), W65C816::and), + OpCode::new(Meta::new(0x34, "BIT", DirectX), W65C816::bit), + OpCode::new(Meta::new(0x35, "AND", DirectX), W65C816::and), + OpCode::new(Meta::new(0x36, "ROL", DirectX), W65C816::rol), + OpCode::new(Meta::new(0x37, "AND", IndirectLongY), W65C816::and), + OpCode::new(Meta::new(0x38, "SEC", Implied), W65C816::sec), + OpCode::new(Meta::new(0x39, "AND", AbsoluteY), W65C816::and), + OpCode::new(Meta::new(0x3a, "DEC", Accumulator), W65C816::dec), + OpCode::new(Meta::new(0x3b, "TSC", Implied), W65C816::tsc), + OpCode::new(Meta::new(0x3c, "BIT", AbsoluteX), W65C816::bit), + OpCode::new(Meta::new(0x3d, "AND", AbsoluteX), W65C816::and), + OpCode::new(Meta::new(0x3e, "ROL", AbsoluteX), W65C816::rol), + OpCode::new(Meta::new(0x3f, "AND", AbsoluteLongX), W65C816::and), + OpCode::new(Meta::new(0x40, "RTI", Implied), W65C816::rti), + OpCode::new(Meta::new(0x41, "EOR", IndirectX), W65C816::eor), + OpCode::new(Meta::new(0x42, "WDM", Immediate), W65C816::wdm), + OpCode::new(Meta::new(0x43, "EOR", StackRelative), W65C816::eor), + OpCode::new(Meta::new(0x44, "MVP", BlockMove), W65C816::mvp), + OpCode::new(Meta::new(0x45, "EOR", Direct), W65C816::eor), + OpCode::new(Meta::new(0x46, "LSR", Direct), W65C816::lsr), + OpCode::new(Meta::new(0x47, "EOR", IndirectLong), W65C816::eor), + OpCode::new(Meta::new(0x48, "PHA", Implied), W65C816::pha), + OpCode::new(Meta::new(0x49, "EOR", Immediate), W65C816::eor), + OpCode::new(Meta::new(0x4a, "LSR", Accumulator), W65C816::lsr), + OpCode::new(Meta::new(0x4b, "PHK", Implied), W65C816::phk), + OpCode::new(Meta::new(0x4c, "JMP", Absolute), W65C816::jmp), + OpCode::new(Meta::new(0x4d, "EOR", Absolute), W65C816::eor), + OpCode::new(Meta::new(0x4e, "LSR", Absolute), W65C816::lsr), + OpCode::new(Meta::new(0x4f, "EOR", AbsoluteLong), W65C816::eor), + OpCode::new(Meta::new(0x50, "BVC", Relative), W65C816::bvc), + OpCode::new(Meta::new(0x51, "EOR", IndirectY), W65C816::eor), + OpCode::new(Meta::new(0x52, "EOR", Indirect), W65C816::eor), + OpCode::new(Meta::new(0x53, "EOR", StackRelIndirectY), W65C816::eor), + OpCode::new(Meta::new(0x54, "MVN", BlockMove), W65C816::mvn), + OpCode::new(Meta::new(0x55, "EOR", DirectX), W65C816::eor), + OpCode::new(Meta::new(0x56, "LSR", DirectX), W65C816::lsr), + OpCode::new(Meta::new(0x57, "EOR", IndirectLongY), W65C816::eor), + OpCode::new(Meta::new(0x58, "CLI", Implied), W65C816::cli), + OpCode::new(Meta::new(0x59, "EOR", AbsoluteY), W65C816::eor), + OpCode::new(Meta::new(0x5a, "PHY", Implied), W65C816::phy), + OpCode::new(Meta::new(0x5b, "TCD", Implied), W65C816::tcd), + OpCode::new(Meta::new(0x5c, "JMP", AbsoluteLong), W65C816::jmp), + OpCode::new(Meta::new(0x5d, "EOR", AbsoluteX), W65C816::eor), + OpCode::new(Meta::new(0x5e, "LSR", AbsoluteX), W65C816::lsr), + OpCode::new(Meta::new(0x5f, "EOR", AbsoluteLongX), W65C816::eor), + OpCode::new(Meta::new(0x60, "RTS", Implied), W65C816::rts), + OpCode::new(Meta::new(0x61, "ADC", IndirectX), W65C816::adc), + OpCode::new(Meta::new(0x62, "PER", RelativeLong), W65C816::per), + OpCode::new(Meta::new(0x63, "ADC", StackRelative), W65C816::adc), + OpCode::new(Meta::new(0x64, "STZ", Direct), W65C816::stz), + OpCode::new(Meta::new(0x65, "ADC", Direct), W65C816::adc), + OpCode::new(Meta::new(0x66, "ROR", Direct), W65C816::ror), + OpCode::new(Meta::new(0x67, "ADC", IndirectLong), W65C816::adc), + OpCode::new(Meta::new(0x68, "PLA", Implied), W65C816::pla), + OpCode::new(Meta::new(0x69, "ADC", Immediate), W65C816::adc), + OpCode::new(Meta::new(0x6a, "ROR", Accumulator), W65C816::ror), + OpCode::new(Meta::new(0x6b, "RTL", Implied), W65C816::rtl), + OpCode::new(Meta::new(0x6c, "JMP", AbsoluteIndirect), W65C816::jmp), + OpCode::new(Meta::new(0x6d, "ADC", Absolute), W65C816::adc), + OpCode::new(Meta::new(0x6e, "ROR", Absolute), W65C816::ror), + OpCode::new(Meta::new(0x6f, "ADC", AbsoluteLong), W65C816::adc), + OpCode::new(Meta::new(0x70, "BVS", Relative), W65C816::bvs), + OpCode::new(Meta::new(0x71, "ADC", IndirectY), W65C816::adc), + OpCode::new(Meta::new(0x72, "ADC", Indirect), W65C816::adc), + OpCode::new(Meta::new(0x73, "ADC", StackRelIndirectY), W65C816::adc), + OpCode::new(Meta::new(0x74, "STZ", DirectX), W65C816::stz), + OpCode::new(Meta::new(0x75, "ADC", DirectX), W65C816::adc), + OpCode::new(Meta::new(0x76, "ROR", DirectX), W65C816::ror), + OpCode::new(Meta::new(0x77, "ADC", IndirectLongY), W65C816::adc), + OpCode::new(Meta::new(0x78, "SEI", Implied), W65C816::sei), + OpCode::new(Meta::new(0x79, "ADC", AbsoluteY), W65C816::adc), + OpCode::new(Meta::new(0x7a, "PLY", Implied), W65C816::ply), + OpCode::new(Meta::new(0x7b, "TDC", Implied), W65C816::tdc), + OpCode::new(Meta::new(0x7c, "JMP", AbsoluteIndirectX), W65C816::jmp), + OpCode::new(Meta::new(0x7d, "ADC", AbsoluteX), W65C816::adc), + OpCode::new(Meta::new(0x7e, "ROR", AbsoluteX), W65C816::ror), + OpCode::new(Meta::new(0x7f, "ADC", AbsoluteLongX), W65C816::adc), + OpCode::new(Meta::new(0x80, "BRA", Relative), W65C816::bra), + OpCode::new(Meta::new(0x81, "STA", IndirectX), W65C816::sta), + OpCode::new(Meta::new(0x82, "BRL", RelativeLong), W65C816::brl), + OpCode::new(Meta::new(0x83, "STA", StackRelative), W65C816::sta), + OpCode::new(Meta::new(0x84, "STY", Direct), W65C816::sty), + OpCode::new(Meta::new(0x85, "STA", Direct), W65C816::sta), + OpCode::new(Meta::new(0x86, "STX", Direct), W65C816::stx), + OpCode::new(Meta::new(0x87, "STA", IndirectLong), W65C816::sta), + OpCode::new(Meta::new(0x88, "DEY", Implied), W65C816::dey), + OpCode::new(Meta::new(0x89, "BIT", Immediate), W65C816::bit), + OpCode::new(Meta::new(0x8a, "TXA", Implied), W65C816::txa), + OpCode::new(Meta::new(0x8b, "PHB", Implied), W65C816::phb), + OpCode::new(Meta::new(0x8c, "STY", Absolute), W65C816::sty), + OpCode::new(Meta::new(0x8d, "STA", Absolute), W65C816::sta), + OpCode::new(Meta::new(0x8e, "STX", Absolute), W65C816::stx), + OpCode::new(Meta::new(0x8f, "STA", AbsoluteLong), W65C816::sta), + OpCode::new(Meta::new(0x90, "BCC", Relative), W65C816::bcc), + OpCode::new(Meta::new(0x91, "STA", IndirectY), W65C816::sta), + OpCode::new(Meta::new(0x92, "STA", Indirect), W65C816::sta), + OpCode::new(Meta::new(0x93, "STA", StackRelIndirectY), W65C816::sta), + OpCode::new(Meta::new(0x94, "STY", DirectX), W65C816::sty), + OpCode::new(Meta::new(0x95, "STA", DirectX), W65C816::sta), + OpCode::new(Meta::new(0x96, "STX", DirectY), W65C816::stx), + OpCode::new(Meta::new(0x97, "STA", IndirectLongY), W65C816::sta), + OpCode::new(Meta::new(0x98, "TYA", Implied), W65C816::tya), + OpCode::new(Meta::new(0x99, "STA", AbsoluteY), W65C816::sta), + OpCode::new(Meta::new(0x9a, "TXS", Implied), W65C816::txs), + OpCode::new(Meta::new(0x9b, "TXY", Implied), W65C816::txy), + OpCode::new(Meta::new(0x9c, "STZ", Absolute), W65C816::stz), + OpCode::new(Meta::new(0x9d, "STA", AbsoluteX), W65C816::sta), + OpCode::new(Meta::new(0x9e, "STZ", AbsoluteX), W65C816::stz), + OpCode::new(Meta::new(0x9f, "STA", AbsoluteLongX), W65C816::sta), + OpCode::new(Meta::new(0xa0, "LDY", Immediate), W65C816::ldy), + OpCode::new(Meta::new(0xa1, "LDA", IndirectX), W65C816::lda), + OpCode::new(Meta::new(0xa2, "LDX", Immediate), W65C816::ldx), + OpCode::new(Meta::new(0xa3, "LDA", StackRelative), W65C816::lda), + OpCode::new(Meta::new(0xa4, "LDY", Direct), W65C816::ldy), + OpCode::new(Meta::new(0xa5, "LDA", Direct), W65C816::lda), + OpCode::new(Meta::new(0xa6, "LDX", Direct), W65C816::ldx), + OpCode::new(Meta::new(0xa7, "LDA", IndirectLong), W65C816::lda), + OpCode::new(Meta::new(0xa8, "TAY", Implied), W65C816::tay), + OpCode::new(Meta::new(0xa9, "LDA", Immediate), W65C816::lda), + OpCode::new(Meta::new(0xaa, "TAX", Implied), W65C816::tax), + OpCode::new(Meta::new(0xab, "PLB", Implied), W65C816::plb), + OpCode::new(Meta::new(0xac, "LDY", Absolute), W65C816::ldy), + OpCode::new(Meta::new(0xad, "LDA", Absolute), W65C816::lda), + OpCode::new(Meta::new(0xae, "LDX", Absolute), W65C816::ldx), + OpCode::new(Meta::new(0xaf, "LDA", AbsoluteLong), W65C816::lda), + OpCode::new(Meta::new(0xb0, "BCS", Relative), W65C816::bcs), + OpCode::new(Meta::new(0xb1, "LDA", IndirectY), W65C816::lda), + OpCode::new(Meta::new(0xb2, "LDA", Indirect), W65C816::lda), + OpCode::new(Meta::new(0xb3, "LDA", StackRelIndirectY), W65C816::lda), + OpCode::new(Meta::new(0xb4, "LDY", DirectX), W65C816::ldy), + OpCode::new(Meta::new(0xb5, "LDA", DirectX), W65C816::lda), + OpCode::new(Meta::new(0xb6, "LDX", DirectY), W65C816::ldx), + OpCode::new(Meta::new(0xb7, "LDA", IndirectLongY), W65C816::lda), + OpCode::new(Meta::new(0xb8, "CLV", Implied), W65C816::clv), + OpCode::new(Meta::new(0xb9, "LDA", AbsoluteY), W65C816::lda), + OpCode::new(Meta::new(0xba, "TSX", Implied), W65C816::tsx), + OpCode::new(Meta::new(0xbb, "TYX", Implied), W65C816::tyx), + OpCode::new(Meta::new(0xbc, "LDY", AbsoluteX), W65C816::ldy), + OpCode::new(Meta::new(0xbd, "LDA", AbsoluteX), W65C816::lda), + OpCode::new(Meta::new(0xbe, "LDX", AbsoluteY), W65C816::ldx), + OpCode::new(Meta::new(0xbf, "LDA", AbsoluteLongX), W65C816::lda), + OpCode::new(Meta::new(0xc0, "CPY", Immediate), W65C816::cpy), + OpCode::new(Meta::new(0xc1, "CMP", IndirectX), W65C816::cmp), + OpCode::new(Meta::new(0xc2, "REP", Immediate), W65C816::rep), + OpCode::new(Meta::new(0xc3, "CMP", StackRelative), W65C816::cmp), + OpCode::new(Meta::new(0xc4, "CPY", Direct), W65C816::cpy), + OpCode::new(Meta::new(0xc5, "CMP", Direct), W65C816::cmp), + OpCode::new(Meta::new(0xc6, "DEC", Direct), W65C816::dec), + OpCode::new(Meta::new(0xc7, "CMP", IndirectLong), W65C816::cmp), + OpCode::new(Meta::new(0xc8, "INY", Implied), W65C816::iny), + OpCode::new(Meta::new(0xc9, "CMP", Immediate), W65C816::cmp), + OpCode::new(Meta::new(0xca, "DEX", Implied), W65C816::dex), + OpCode::new(Meta::new(0xcb, "WAI", Implied), W65C816::wai), + OpCode::new(Meta::new(0xcc, "CPY", Absolute), W65C816::cpy), + OpCode::new(Meta::new(0xcd, "CMP", Absolute), W65C816::cmp), + OpCode::new(Meta::new(0xce, "DEC", Absolute), W65C816::dec), + OpCode::new(Meta::new(0xcf, "CMP", AbsoluteLong), W65C816::cmp), + OpCode::new(Meta::new(0xd0, "BNE", Relative), W65C816::bne), + OpCode::new(Meta::new(0xd1, "CMP", IndirectY), W65C816::cmp), + OpCode::new(Meta::new(0xd2, "CMP", Indirect), W65C816::cmp), + OpCode::new(Meta::new(0xd3, "CMP", StackRelIndirectY), W65C816::cmp), + OpCode::new(Meta::new(0xd4, "PEI", Direct), W65C816::pei), + OpCode::new(Meta::new(0xd5, "CMP", DirectX), W65C816::cmp), + OpCode::new(Meta::new(0xd6, "DEC", DirectX), W65C816::dec), + OpCode::new(Meta::new(0xd7, "CMP", IndirectLongY), W65C816::cmp), + OpCode::new(Meta::new(0xd8, "CLD", Implied), W65C816::cld), + OpCode::new(Meta::new(0xd9, "CMP", AbsoluteY), W65C816::cmp), + OpCode::new(Meta::new(0xda, "PHX", Implied), W65C816::phx), + OpCode::new(Meta::new(0xdb, "STP", Implied), W65C816::stp), + OpCode::new(Meta::new(0xdc, "JML", AbsoluteIndirectLong), W65C816::jml), + OpCode::new(Meta::new(0xdd, "CMP", AbsoluteX), W65C816::cmp), + OpCode::new(Meta::new(0xde, "DEC", AbsoluteX), W65C816::dec), + OpCode::new(Meta::new(0xdf, "CMP", AbsoluteLongX), W65C816::cmp), + OpCode::new(Meta::new(0xe0, "CPX", Immediate), W65C816::cpx), + OpCode::new(Meta::new(0xe1, "SBC", IndirectX), W65C816::sbc), + OpCode::new(Meta::new(0xe2, "SEP", Immediate), W65C816::sep), + OpCode::new(Meta::new(0xe3, "SBC", StackRelative), W65C816::sbc), + OpCode::new(Meta::new(0xe4, "CPX", Direct), W65C816::cpx), + OpCode::new(Meta::new(0xe5, "SBC", Direct), W65C816::sbc), + OpCode::new(Meta::new(0xe6, "INC", Direct), W65C816::inc), + OpCode::new(Meta::new(0xe7, "SBC", IndirectLong), W65C816::sbc), + OpCode::new(Meta::new(0xe8, "INX", Implied), W65C816::inx), + OpCode::new(Meta::new(0xe9, "SBC", Immediate), W65C816::sbc), + OpCode::new(Meta::new(0xea, "NOP", Implied), W65C816::nop), + OpCode::new(Meta::new(0xeb, "XBA", Implied), W65C816::xba), + OpCode::new(Meta::new(0xec, "CPX", Absolute), W65C816::cpx), + OpCode::new(Meta::new(0xed, "SBC", Absolute), W65C816::sbc), + OpCode::new(Meta::new(0xee, "INC", Absolute), W65C816::inc), + OpCode::new(Meta::new(0xef, "SBC", AbsoluteLong), W65C816::sbc), + OpCode::new(Meta::new(0xf0, "BEQ", Relative), W65C816::beq), + OpCode::new(Meta::new(0xf1, "SBC", IndirectY), W65C816::sbc), + OpCode::new(Meta::new(0xf2, "SBC", Indirect), W65C816::sbc), + OpCode::new(Meta::new(0xf3, "SBC", StackRelIndirectY), W65C816::sbc), + OpCode::new(Meta::new(0xf4, "PEA", Immediate), W65C816::pea), + OpCode::new(Meta::new(0xf5, "SBC", DirectX), W65C816::sbc), + OpCode::new(Meta::new(0xf6, "INC", DirectX), W65C816::inc), + OpCode::new(Meta::new(0xf7, "SBC", IndirectLongY), W65C816::sbc), + OpCode::new(Meta::new(0xf8, "SED", Implied), W65C816::sed), + OpCode::new(Meta::new(0xf9, "SBC", AbsoluteY), W65C816::sbc), + OpCode::new(Meta::new(0xfa, "PLX", Implied), W65C816::plx), + OpCode::new(Meta::new(0xfb, "XCE", Implied), W65C816::xce), + OpCode::new(Meta::new(0xfc, "JSR", AbsoluteIndirectX), W65C816::jsr), + OpCode::new(Meta::new(0xfd, "SBC", AbsoluteX), W65C816::sbc), + OpCode::new(Meta::new(0xfe, "INC", AbsoluteX), W65C816::inc), + OpCode::new(Meta::new(0xff, "SBC", AbsoluteLongX), W65C816::sbc), + ] } diff --git a/aliusnes/tests/utils/cpu_state.rs b/aliusnes/tests/utils/cpu_state.rs deleted file mode 100644 index b856c81..0000000 --- a/aliusnes/tests/utils/cpu_state.rs +++ /dev/null @@ -1,247 +0,0 @@ -use std::{collections::HashMap, path::PathBuf}; - -use crate::{Cycle, OpcodeTest, TomHarteBus}; -use aliusnes::{ - apu::spc700::{ - cpu::{Cpu as Spc, Status as Psw}, - Spc700, - }, - w65c816::{ - cpu::{Cpu, Status}, - W65C816, - }, -}; -use serde::{Deserialize, Deserializer}; - -pub(crate) fn deserialize_as_map<'de, D: Deserializer<'de>>( - deserializer: D, -) -> Result, D::Error> { - Vec::deserialize(deserializer).map(|vec| vec.into_iter().collect()) -} - -#[derive(Debug, PartialEq, Deserialize)] -pub struct CpuState { - pc: u16, - s: u16, - p: u8, - a: u16, - x: u16, - y: u16, - dbr: u8, - d: u16, - pbr: u8, - e: u8, - #[serde(deserialize_with = "deserialize_as_map")] - ram: HashMap, -} - -impl CpuState { - pub fn convert_state(&self) -> (W65C816, TomHarteBus) { - let mut w65c816 = W65C816::new(); - w65c816.cpu.accumulator = self.a; - w65c816.cpu.index_x = self.x; - w65c816.cpu.index_y = self.y; - w65c816.cpu.stack_pointer = self.s; - w65c816.cpu.program_counter = self.pc; - w65c816.cpu.status = Status(self.p); - w65c816.cpu.dpr = self.d; - w65c816.cpu.pbr = self.pbr; - w65c816.cpu.dbr = self.dbr; - w65c816.cpu.emulation_mode = self.e == 1; - w65c816.cpu.stopped = false; - w65c816.cpu.waiting_interrupt = false; - - let bus = TomHarteBus { - memory: self.ram.clone(), - ..Default::default() - }; - - (w65c816, bus) - } -} - -impl From<(Cpu, TomHarteBus)> for CpuState { - fn from(value: (Cpu, TomHarteBus)) -> Self { - Self { - pc: value.0.program_counter, - s: value.0.stack_pointer, - p: value.0.status.0, - a: value.0.accumulator, - x: value.0.index_x, - y: value.0.index_y, - dbr: value.0.dbr, - d: value.0.dpr, - pbr: value.0.pbr, - e: value.0.emulation_mode as u8, - ram: value.1.memory, - } - } -} - -impl std::fmt::Display for CpuState { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "pc:{:04X} s:{:04X} p:{:02X} a:{:04X} x:{:04X} y:{:04X} dbr:{:02X} d:{:04X} pbr:{:02X} e:{:01X} \n\t ram:{:02X?}", - self.pc, - self.s, - self.p, - self.a, - self.x, - self.y, - self.dbr, - self.d, - self.pbr, - self.e, - self.ram - ) - } -} - -impl OpcodeTest for CpuState { - type Proc = Cpu; - - fn test_path(name: &str) -> PathBuf { - let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - root_dir.join(format!("tests/65816/{name}.json.xz")) - } - - fn do_step(&mut self, other: &Self, cycles_len: usize) -> (Self::Proc, TomHarteBus, bool) { - let (mut w65c816, mut bus) = self.convert_state(); - - let opcode = w65c816.peek_opcode(&bus); - let skip_cycles = if opcode.code == 0x44 || opcode.code == 0x54 { - loop { - if bus.cycles.len() >= (cycles_len - 2) { - break; - } - w65c816.step(&mut bus); - } - w65c816.cpu.program_counter = other.pc; - true - } else { - w65c816.step(&mut bus); - false - }; - - (w65c816.cpu, bus, skip_cycles) - } - - fn deserialize_cycles<'de, D: Deserializer<'de>>( - deserializer: D, - ) -> Result, D::Error> { - let v: Vec<(Option, Option, String)> = Deserialize::deserialize(deserializer)?; - let mut cycles: Vec = v - .iter() - .map(|(addr, value, state)| { - if !(state.contains('p') || state.contains('d')) { - Cycle::Internal - } else if state.contains('r') { - Cycle::Read(addr.unwrap_or_default(), *value) - } else if state.contains('w') { - Cycle::Write(addr.unwrap_or_default(), value.unwrap_or_default()) - } else { - Cycle::Internal - } - }) - .collect(); - cycles.sort(); - Ok(cycles) - } -} - -#[derive(Debug, PartialEq, Deserialize)] -pub struct Spc700State { - pc: u16, - a: u8, - x: u8, - y: u8, - sp: u8, - psw: u8, - #[serde(deserialize_with = "deserialize_as_map")] - ram: HashMap, -} - -impl Spc700State { - pub fn convert_state(&self) -> (Spc700, TomHarteBus) { - let mut spc700 = Spc700::new(); - spc700.cpu.accumulator = self.a; - spc700.cpu.index_x = self.x; - spc700.cpu.index_y = self.y; - spc700.cpu.program_counter = self.pc; - spc700.cpu.stack_pointer = self.sp; - spc700.cpu.status = Psw(self.psw); - - let bus = TomHarteBus { - memory: self.ram.clone(), - ..Default::default() - }; - - (spc700, bus) - } -} - -impl From<(Spc, TomHarteBus)> for Spc700State { - fn from(value: (Spc, TomHarteBus)) -> Self { - Self { - pc: value.0.program_counter, - a: value.0.accumulator, - x: value.0.index_x, - y: value.0.index_y, - sp: value.0.stack_pointer, - psw: value.0.status.0, - ram: value.1.memory, - } - } -} - -impl std::fmt::Display for Spc700State { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "pc:{:04X} sp:{:04X} psw:{:02X} a:{:04X} x:{:04X} y:{:04X} \n\t ram:{:02X?}", - self.pc, self.sp, self.psw, self.a, self.x, self.y, self.ram - ) - } -} - -impl OpcodeTest for Spc700State { - type Proc = Spc; - - fn test_path(name: &str) -> std::path::PathBuf { - let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - root_dir.join(format!("tests/spc700/{name}.json.xz")) - } - - fn do_step(&mut self, _other: &Self, _cycles_len: usize) -> (Self::Proc, TomHarteBus, bool) { - let (mut spc700, mut bus) = self.convert_state(); - let opcode = spc700.peek_opcode(&bus); - let skip_cycles = opcode.code == 0xFE; - - spc700.step(&mut bus); - - (spc700.cpu, bus, skip_cycles) - } - - fn deserialize_cycles<'de, D: Deserializer<'de>>( - deserializer: D, - ) -> Result, D::Error> { - let v: Vec<(Option, Option, String)> = Deserialize::deserialize(deserializer)?; - let mut cycles: Vec = v - .iter() - .map(|(addr, value, state)| { - if state == "wait" { - Cycle::Internal - } else if state == "read" { - Cycle::Read(addr.unwrap_or_default(), *value) - } else if state == "write" { - Cycle::Write(addr.unwrap_or_default(), value.unwrap_or_default()) - } else { - panic!("Unknown state: {state}"); - } - }) - .collect(); - cycles.sort(); - Ok(cycles) - } -} diff --git a/aliusnes/tests/utils/mod.rs b/aliusnes/tests/utils/mod.rs deleted file mode 100644 index 8f7cb5e..0000000 --- a/aliusnes/tests/utils/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod cpu_state; -pub mod test_bus;