From 8b3851175c8ec26cbefad9812648412a73848a01 Mon Sep 17 00:00:00 2001 From: Thomas Date: Sat, 14 Oct 2023 20:42:14 +0200 Subject: [PATCH] 65816: implement STZ --- src/snes/cpu_65816/cpu.rs | 80 +++++++++++++++++++++++++++++-- src/snes/cpu_65816/instruction.rs | 12 +++++ src/test/processortests_65816.rs | 8 ++-- 3 files changed, 92 insertions(+), 8 deletions(-) diff --git a/src/snes/cpu_65816/cpu.rs b/src/snes/cpu_65816/cpu.rs index 05eb6ab..845dbdd 100644 --- a/src/snes/cpu_65816/cpu.rs +++ b/src/snes/cpu_65816/cpu.rs @@ -1,9 +1,9 @@ use anyhow::Result; -use crate::snes::bus::{Address, Bus, BusIterator}; +use crate::snes::bus::{Address, Bus, BusIterator, ADDRESS_MASK}; use crate::tickable::Ticks; -use super::instruction::{Instruction, InstructionType}; +use super::instruction::{AddressingMode, Instruction, InstructionType}; use super::regs::{Flag, Register, RegisterFile}; /// Main SNES CPU (65816) @@ -70,8 +70,6 @@ where let instr = self.fetch_next_instr()?; let _cycles = self.execute_instruction(&instr)?; - - // TODO program bank? self.regs.pc = self.regs.pc.wrapping_add(instr.len as u16); Ok(()) @@ -95,6 +93,24 @@ where v } + /// Writes a memory location while ticking peripherals + /// for the access time. + fn write_tick(&mut self, addr: Address, val: u8) { + self.bus.write(addr, val); + self.tick_bus(1).unwrap(); + } + + /// Writes 16-bit to a memory location while ticking + /// peripherals for the access time. + /// 16-bit address wrap. + fn write16_tick_a16(&mut self, addr: Address, val: u16) { + self.bus.write(addr, (val & 0xFF) as u8); + self.tick_bus(1).unwrap(); + let hi_addr = addr & 0xFFFF0000 | Address::from((addr as u16).wrapping_add(1)); + self.bus.write(hi_addr, (val >> 8) as u8); + self.tick_bus(1).unwrap(); + } + fn execute_instruction(&mut self, instr: &Instruction) -> Result<()> { match instr.def.instr_type { InstructionType::NOP => self.tick_bus(1), @@ -120,6 +136,8 @@ where InstructionType::TXY => self.op_txx(Register::X, Register::Y, Flag::X), InstructionType::TYA => self.op_txx(Register::Y, Register::C, Flag::M), InstructionType::TYX => self.op_txx(Register::Y, Register::X, Flag::X), + InstructionType::STZ => self.op_stz(&instr), + _ => todo!(), } } @@ -192,4 +210,58 @@ where } self.tick_bus(1) } + + /// STZ - Store zero + fn op_stz(&mut self, instr: &Instruction) -> Result<()> { + let addr = self.resolve_address(instr)?; + + // Extra internal cycles + if (instr.def.mode == AddressingMode::Direct || instr.def.mode == AddressingMode::DirectX) + && self.regs.read(Register::DL) != 0 + { + self.tick_bus(1)?; + } + if instr.def.mode == AddressingMode::DirectX || instr.def.mode == AddressingMode::AbsoluteX + { + self.tick_bus(1)?; + } + + if self.regs.test_flag(Flag::M) { + self.write_tick(addr, 0); + } else { + self.write16_tick_a16(addr, 0); + } + + Ok(()) + } + + /// Resolves an address from instruction data, registers, etc. + /// based on the addressing mode. + fn resolve_address(&self, instr: &Instruction) -> Result
{ + Ok(match instr.def.mode { + AddressingMode::Direct => Address::from( + self.regs + .read(Register::D) + .wrapping_add(instr.imm::()?), + ), + AddressingMode::DirectX => Address::from( + self.regs + .read(Register::D) + .wrapping_add(instr.imm::()?) + .wrapping_add(self.regs.read(Register::X)), + ), + AddressingMode::Absolute => { + Address::from(self.regs.read(Register::DBR)) << 16 + | Address::from(instr.imm::()?) + } + AddressingMode::AbsoluteX => { + (Address::from(self.regs.read(Register::DBR)) << 16 + | Address::from(instr.imm::()?)) + .wrapping_add(Address::from(self.regs.read(Register::X))) + & ADDRESS_MASK + } + + _ => todo!(), + }) + } } diff --git a/src/snes/cpu_65816/instruction.rs b/src/snes/cpu_65816/instruction.rs index 4df709f..969326e 100644 --- a/src/snes/cpu_65816/instruction.rs +++ b/src/snes/cpu_65816/instruction.rs @@ -6,6 +6,7 @@ use thiserror::Error; use super::instruction_table::INSTRUCTION_TABLE; /// Instruction addressing mode +#[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum AddressingMode { /// Absolute /// 3 bytes, $OP $LL $HH @@ -294,6 +295,17 @@ impl Instruction { len, }) } + + /// Reads the immediate value for addressing modes that + /// have one immediate value. + pub fn imm>(&self) -> Result + where + anyhow::Error: From, + { + assert_ne!(self.def.mode, AddressingMode::SrcDest); + assert_ne!(self.def.mode, AddressingMode::Implied); + Ok(self.immediate[0].try_into()?) + } } impl fmt::Display for Instruction { diff --git a/src/test/processortests_65816.rs b/src/test/processortests_65816.rs index 652072d..f3f2b60 100644 --- a/src/test/processortests_65816.rs +++ b/src/test/processortests_65816.rs @@ -236,7 +236,7 @@ cpu_test!(instr_5b, 0x5b); //cpu_test!(instr_61, 0x61); //cpu_test!(instr_62, 0x62); //cpu_test!(instr_63, 0x63); -//cpu_test!(instr_64, 0x64); +cpu_test!(instr_64, 0x64); //cpu_test!(instr_65, 0x65); //cpu_test!(instr_66, 0x66); //cpu_test!(instr_67, 0x67); @@ -252,7 +252,7 @@ cpu_test!(instr_5b, 0x5b); //cpu_test!(instr_71, 0x71); //cpu_test!(instr_72, 0x72); //cpu_test!(instr_73, 0x73); -//cpu_test!(instr_74, 0x74); +cpu_test!(instr_74, 0x74); //cpu_test!(instr_75, 0x75); //cpu_test!(instr_76, 0x76); //cpu_test!(instr_77, 0x77); @@ -292,9 +292,9 @@ cpu_test!(instr_98, 0x98); //cpu_test!(instr_99, 0x99); cpu_test!(instr_9a, 0x9a); cpu_test!(instr_9b, 0x9b); -//cpu_test!(instr_9c, 0x9c); +cpu_test!(instr_9c, 0x9c); //cpu_test!(instr_9d, 0x9d); -//cpu_test!(instr_9e, 0x9e); +cpu_test!(instr_9e, 0x9e); //cpu_test!(instr_9f, 0x9f); //cpu_test!(instr_a0, 0xa0); //cpu_test!(instr_a1, 0xa1);