Add all 65816 instruction tests

Separate CPU class into instructions and addressing files
This commit is contained in:
scawful
2023-12-03 05:13:25 -05:00
parent b4820d1d32
commit a0019ab7fb
14 changed files with 4538 additions and 1277 deletions

View File

@@ -51,6 +51,21 @@ set(
app/gui/pipeline.cc
)
set(
YAZE_APP_EMU_SRC
app/emu/emulator.cc
app/emu/audio/apu.cc
app/emu/audio/spc700.cc
app/emu/audio/dsp.cc
app/emu/cpu/instructions.cc
app/emu/cpu/addressing.cc
app/emu/video/ppu.cc
app/emu/memory/dma.cc
app/emu/memory/memory.cc
app/emu/cpu.cc
app/emu/snes.cc
)
set(SDL_TARGETS SDL2::SDL2)
if(WIN32 OR MINGW)

View File

@@ -2,15 +2,7 @@ add_executable(
yaze
app/yaze.cc
app/rom.cc
app/emu/emulator.cc
app/emu/audio/apu.cc
app/emu/audio/spc700.cc
app/emu/audio/dsp.cc
app/emu/video/ppu.cc
app/emu/memory/dma.cc
app/emu/memory/memory.cc
app/emu/cpu.cc
app/emu/snes.cc
${YAZE_APP_EMU_SRC}
${YAZE_APP_CORE_SRC}
${YAZE_APP_EDITOR_SRC}
${YAZE_APP_GFX_SRC}
@@ -23,10 +15,10 @@ target_include_directories(
yaze PUBLIC
lib/
app/
lib/SDL_mixer/include/
${CMAKE_SOURCE_DIR}/src/
${PNG_INCLUDE_DIRS}
${SDL2_INCLUDE_DIR}
lib/SDL_mixer/include/
)
target_link_libraries(

File diff suppressed because it is too large Load Diff

View File

@@ -50,31 +50,46 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
void ExecuteInstruction(uint8_t opcode);
void LogInstructions(uint16_t PC, uint8_t opcode, uint16_t operand,
bool immediate, bool accumulator_mode);
void HandleInterrupts();
void UpdatePC(uint8_t instruction_length) { PC += instruction_length; }
uint8_t GetInstructionLength(uint8_t opcode);
uint16_t SP() const override { return memory.SP(); }
void SetSP(uint16_t value) override { memory.SetSP(value); }
void set_next_pc(uint16_t value) { next_pc_ = value; }
void UpdateClock(int delta_time) { clock.UpdateClock(delta_time); }
bool IsBreakpoint(uint32_t address) {
return std::find(breakpoints_.begin(), breakpoints_.end(), address) !=
breakpoints_.end();
}
void SetBreakpoint(uint32_t address) { breakpoints_.push_back(address); }
void ClearBreakpoint(uint32_t address) {
breakpoints_.erase(
std::remove(breakpoints_.begin(), breakpoints_.end(), address),
breakpoints_.end());
}
void ClearBreakpoints() {
breakpoints_.clear();
breakpoints_.shrink_to_fit();
}
auto GetBreakpoints() { return breakpoints_; }
std::vector<uint32_t> breakpoints_;
std::vector<InstructionEntry> instruction_log_;
// ======================================================
// Interrupt Vectors
// Emulation mode, e = 1 Native mode, e = 0
//
// 0xFFFE,FF - IRQ/BRK 0xFFEE,EF - IRQ
// 0xFFFC,FD - RESET
// 0xFFFA,FB - NMI 0xFFEA,EB - NMI
// 0xFFF8,F9 - ABORT 0xFFE8,E9 - ABORT
// 0xFFE6,E7 - BRK
// 0xFFF4,F5 - COP 0xFFE4,E5 - COP
void HandleInterrupts();
// ======================================================
// Registers
@@ -101,8 +116,17 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
// B #$10 00010000 Break (emulation mode only)
// Setting flags in the status register
bool m() { return GetAccumulatorSize() ? 1 : 0; }
int GetAccumulatorSize() const { return status & 0x20; }
int GetIndexSize() const { return status & 0x10; }
void set_16_bit_mode() {
SetAccumulatorSize(true);
SetIndexSize(true);
}
void set_8_bit_mode() {
SetAccumulatorSize(false);
SetIndexSize(false);
}
void SetAccumulatorSize(bool set) { SetFlag(0x20, set); }
void SetIndexSize(bool set) { SetFlag(0x10, set); }
@@ -124,6 +148,8 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
bool GetZeroFlag() const { return GetFlag(0x02); }
bool GetCarryFlag() const { return GetFlag(0x01); }
enum class AccessType { Control, Data };
// ==========================================================================
// Addressing Modes
@@ -134,7 +160,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
// Low: First operand byte
//
// LDA addr
uint16_t Absolute() { return ReadWord((PB << 16) | PC + 1); }
uint16_t Absolute(AccessType access_type = AccessType::Data);
// Effective Address:
// The Data Bank Register is concatened with the 16-bit operand
@@ -142,7 +168,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
// based on the emulation mode (16:X=0, 8:X=1)
//
// LDA addr, X
uint16_t AbsoluteIndexedX() { return FetchWord() + X; }
uint32_t AbsoluteIndexedX();
// Effective Address:
// The Data Bank Register is concatened with the 16-bit operand
@@ -150,7 +176,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
// based on the emulation mode (16:Y=0, 8:Y=1)
//
// LDA addr, Y
uint16_t AbsoluteIndexedY() { return FetchWord() + Y; }
uint32_t AbsoluteIndexedY();
// Test Me :)
// Effective Address:
@@ -160,10 +186,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
// the operand double byte and X based on the
// emulation mode
// JMP (addr, X)
uint16_t AbsoluteIndexedIndirect() {
uint16_t address = FetchWord() + X;
return memory.ReadWord((PB << 16) | address & 0xFFFF);
}
uint16_t AbsoluteIndexedIndirect();
// Effective Address:
// Bank: Program Bank Register (PBR)
@@ -171,20 +194,14 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
// Indirect Address: Located in Bank Zero, at the operand double byte
//
// JMP (addr)
uint16_t AbsoluteIndirect() {
uint16_t address = FetchWord();
return memory.ReadWord((PB << 16) | address);
}
uint16_t AbsoluteIndirect();
// Effective Address:
// Bank/High/Low: The 24-bit Indirect Address
// Indirect Address: Located in Bank Zero, at the operand double byte
//
// JMP [addr]
uint32_t AbsoluteIndirectLong() {
uint16_t address = FetchWord();
return memory.ReadWordLong((PB << 16) | address);
}
uint32_t AbsoluteIndirectLong();
// Effective Address:
// Bank: Third operand byte
@@ -192,13 +209,13 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
// Low: First operand byte
//
// LDA long
uint32_t AbsoluteLong() { return FetchLong(); }
uint32_t AbsoluteLong();
// Effective Address:
// The 24-bit operand is added to X based on the emulation mode
//
// LDA long, X
uint16_t AbsoluteLongIndexedX() { return FetchLong() + X; }
uint32_t AbsoluteLongIndexedX();
// Source Effective Address:
// Bank: Second operand byte
@@ -212,21 +229,14 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
// The number of bytes to be moved: 16-bit value in Acculumator C plus 1.
//
// MVN src, dst
void BlockMove(uint16_t source, uint16_t dest, uint16_t length) {
for (int i = 0; i < length; i++) {
memory.WriteByte(dest + i, memory.ReadByte(source + i));
}
}
void BlockMove(uint16_t source, uint16_t dest, uint16_t length);
// Effective Address:
// Bank: Zero
// High/low: Direct Page Register plus operand byte
//
// LDA dp
uint16_t DirectPage() {
uint8_t dp = memory.ReadByte((PB << 16) | PC + 1);
return D + dp;
}
uint16_t DirectPage();
// Effective Address:
// Bank: Zero
@@ -234,20 +244,14 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
// based on the emulation mode
//
// LDA dp, X
uint16_t DirectPageIndexedX() {
uint8_t operand = FetchByte();
return D + operand + X;
}
uint16_t DirectPageIndexedX();
// Effective Address:
// Bank: Zero
// High/low: Direct Page Register plus operand byte plus Y
// based on the emulation mode
// LDA dp, Y
uint16_t DirectPageIndexedY() {
uint8_t dp = FetchByte();
return (dp + Y) & 0xFF;
}
uint16_t DirectPageIndexedY();
// Effective Address:
// Bank: Data bank register
@@ -256,12 +260,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
// register, the operand byte, and X based on the emulation mode in bank zero.
//
// LDA (dp, X)
uint16_t DirectPageIndexedIndirectX() {
uint8_t dp = FetchByte();
uint16_t effective_address = D + dp + X;
uint16_t indirect_address = memory.ReadWord(effective_address & 0xFFFF);
return indirect_address;
}
uint16_t DirectPageIndexedIndirectX();
// Effective Address:
// Bank: Data bank register
@@ -270,12 +269,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
// zero.
//
// LDA (dp)
uint16_t DirectPageIndirect() {
uint8_t dp = FetchByte();
// Add the Direct Page register to the fetched operand
uint16_t effective_address = D + dp;
return memory.ReadWord(effective_address);
}
uint16_t DirectPageIndirect();
// Effective Address:
// Bank/High/Low: The 24-bit indirect address
@@ -283,11 +277,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
// register in bank zero.
//
// LDA [dp]
uint32_t DirectPageIndirectLong() {
uint8_t dp = FetchByte();
uint16_t effective_address = D + dp;
return memory.ReadWordLong(effective_address);
}
uint32_t DirectPageIndirectLong();
// Effective Address:
// Found by concatenating the data bank to the double-byte
@@ -297,11 +287,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
// register and the operand byte, in bank zero.
//
// LDA (dp), Y
uint16_t DirectPageIndirectIndexedY() {
uint8_t dp = FetchByte();
uint16_t effective_address = D + dp;
return memory.ReadWord(effective_address) + Y;
}
uint16_t DirectPageIndirectIndexedY();
// Effective Address:
// Found by adding to the triple-byte indirect address Y based on the
@@ -312,11 +298,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
// the operand byte in bank zero.
//
// LDA (dp), Y
uint16_t DirectPageIndirectLongIndexedY() {
uint8_t dp = FetchByte();
uint16_t effective_address = D + dp + Y;
return memory.ReadWordLong(effective_address);
}
uint32_t DirectPageIndirectLongIndexedY();
// 8-bit data: Data Operand Byte
// 16-bit data 65816 native mode m or x = 0
@@ -324,23 +306,19 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
// Data Low: First Operand Byte
//
// LDA #const
uint16_t Immediate() {
if (GetAccumulatorSize()) {
return memory.ReadByte((PB << 16) | PC + 1);
} else {
return memory.ReadWord((PB << 16) | PC + 1);
}
}
uint16_t Immediate();
uint16_t StackRelative() {
uint8_t sr = FetchByte();
return SP() + sr;
}
uint16_t StackRelative();
uint16_t StackRelativeIndirectIndexedY() {
uint8_t sr = FetchByte();
return memory.ReadWord(SP() + sr + Y);
}
// Effective Address:
// The Data Bank Register is concatenated to the Indirect Address;
// the 24-bit result is added to Y (16 bits if x = 0; else 8 bits)
// Indirect Address:
// Located at the 16-bit sum of the 8-bit operand and the 16-bit stack
// pointer
//
// LDA (sr, S), Y
uint32_t StackRelativeIndirectIndexedY();
// Memory access routines
uint8_t ReadByte(uint32_t address) const override {
@@ -400,12 +378,11 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
uint8_t fetchedByte = memory.ReadByte(effectiveAddress);
next_pc_ = PC + 1;
// PC++; // Increment the Program Counter
return fetchedByte;
}
uint16_t ReadByBitMode(uint32_t address) {
uint16_t ReadByteOrWord(uint32_t address) {
if (GetAccumulatorSize()) {
// 8-bit mode
return memory.ReadByte(address) & 0xFF;
@@ -415,27 +392,15 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
}
}
void UpdatePC(uint8_t instruction_length) { PC += instruction_length; }
uint8_t GetInstructionLength(uint8_t opcode);
void SetMemory(const std::vector<uint8_t>& data) override {
memory.SetMemory(data);
}
int16_t SP() const override { return memory.SP(); }
void SetSP(int16_t value) override { memory.SetSP(value); }
void set_next_pc(uint16_t value) { next_pc_ = value; }
void UpdateClock(int delta_time) { clock.UpdateClock(delta_time); }
// ======================================================
// Instructions
// ADC: Add with carry
void ADC(uint8_t operand);
void ANDAbsoluteLong(uint32_t address);
void ADC(uint16_t operand);
// AND: Logical AND
void AND(uint16_t address, bool isImmediate = false);
void AND(uint32_t address, bool immediate = false);
void ANDAbsoluteLong(uint32_t address);
// ASL: Arithmetic shift left
void ASL(uint16_t address);
@@ -489,19 +454,19 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
void CLV();
// CMP: Compare
void CMP(uint16_t address, bool isImmediate = false);
void CMP(uint32_t address, bool immediate = false);
// COP: Coprocessor enable
void COP();
// CPX: Compare X register
void CPX(uint16_t address, bool isImmediate = false);
void CPX(uint16_t address, bool immediate = false);
// CPY: Compare Y register
void CPY(uint16_t address, bool isImmediate = false);
void CPY(uint16_t address, bool immediate = false);
// DEC: Decrement memory
void DEC(uint16_t address);
void DEC(uint32_t address, bool accumulator = false);
// DEX: Decrement X register
void DEX();
@@ -510,10 +475,10 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
void DEY();
// EOR: Exclusive OR
void EOR(uint16_t address, bool isImmediate = false);
void EOR(uint32_t address, bool immediate = false);
// INC: Increment memory
void INC(uint16_t address);
void INC(uint32_t address, bool accumulator = false);
// INX: Increment X register
void INX();
@@ -534,17 +499,16 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
void JSL(uint32_t address);
// LDA: Load accumulator
void LDA(uint16_t address, bool isImmediate = false,
bool direct_page = false);
void LDA(uint16_t address, bool immediate = false, bool direct_page = false);
// LDX: Load X register
void LDX(uint16_t address, bool isImmediate = false);
void LDX(uint16_t address, bool immediate = false);
// LDY: Load Y register
void LDY(uint16_t address, bool isImmediate = false);
void LDY(uint16_t address, bool immediate = false);
// LSR: Logical shift right
void LSR(uint16_t address);
void LSR(uint16_t address, bool accumulator = false);
// MVN: Block move next
void MVN(uint16_t source, uint16_t dest, uint16_t length);
@@ -556,7 +520,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
void NOP();
// ORA: Logical inclusive OR
void ORA(uint16_t address, bool isImmediate = false);
void ORA(uint16_t address, bool immediate = false);
// PEA: Push effective absolute address
void PEA();
@@ -610,10 +574,10 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
void REP();
// ROL: Rotate left
void ROL(uint16_t address);
void ROL(uint32_t address, bool accumulator = false);
// ROR: Rotate right
void ROR(uint16_t address);
void ROR(uint32_t address, bool accumulator = false);
// RTI: Return from interrupt
void RTI();
@@ -625,7 +589,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
void RTS();
// SBC: Subtract with carry
void SBC(uint16_t operand, bool isImmediate = false);
void SBC(uint32_t operand, bool immediate = false);
// SEC: Set carry flag
void SEC();
@@ -640,7 +604,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
void SEP();
// STA: Store accumulator
void STA(uint16_t address);
void STA(uint32_t address);
// STP: Stop the processor
void STP();
@@ -726,7 +690,6 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
SetCarryFlag(register_value >= memory_value); // Carry flag
}
// Helper function to set or clear a specific flag bit
void SetFlag(uint8_t mask, bool set) {
if (set) {
status |= mask; // Set the bit
@@ -735,10 +698,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
}
}
// Helper function to get the value of a specific flag bit
bool GetFlag(uint8_t mask) const { return (status & mask) != 0; }
// Appease the C++ Gods...
void PushByte(uint8_t value) override { memory.PushByte(value); }
void PushWord(uint16_t value) override { memory.PushWord(value); }
uint8_t PopByte() override { return memory.PopByte(); }
@@ -746,9 +706,6 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
void PushLong(uint32_t value) override { memory.PushLong(value); }
uint32_t PopLong() override { return memory.PopLong(); }
void ClearMemory() override { memory.ClearMemory(); }
void LoadData(const std::vector<uint8_t>& data) override {
memory.LoadData(data);
}
uint8_t operator[](int i) const override { return 0; }
uint8_t at(int i) const override { return 0; }

View File

@@ -0,0 +1,122 @@
#include "app/emu/cpu.h"
namespace yaze {
namespace app {
namespace emu {
uint16_t CPU::Absolute(CPU::AccessType access_type) {
auto operand = FetchWord();
uint32_t bank =
(access_type == CPU::AccessType::Data) ? (DB << 16) : (PB << 16);
return bank | (operand & 0xFFFF);
}
uint32_t CPU::AbsoluteIndexedX() {
uint16_t address = memory.ReadWord((PB << 16) | (PC + 1));
uint32_t effective_address = (DB << 16) | ((address + X) & 0xFFFF);
return effective_address;
}
uint32_t CPU::AbsoluteIndexedY() {
uint16_t address = memory.ReadWord((PB << 16) | (PC + 1));
uint32_t effective_address = (DB << 16) | address + Y;
return effective_address;
}
uint16_t CPU::AbsoluteIndexedIndirect() {
uint16_t address = FetchWord() + X;
return memory.ReadWord((DB << 16) | address & 0xFFFF);
}
uint16_t CPU::AbsoluteIndirect() {
uint16_t address = FetchWord();
return memory.ReadWord((PB << 16) | address);
}
uint32_t CPU::AbsoluteIndirectLong() {
uint16_t address = FetchWord();
return memory.ReadWordLong((PB << 16) | address);
}
uint32_t CPU::AbsoluteLong() { return FetchLong(); }
uint32_t CPU::AbsoluteLongIndexedX() { return FetchLong() + X; }
void CPU::BlockMove(uint16_t source, uint16_t dest, uint16_t length) {
for (int i = 0; i < length; i++) {
memory.WriteByte(dest + i, memory.ReadByte(source + i));
}
}
uint16_t CPU::DirectPage() {
uint8_t dp = FetchByte();
return D + dp;
}
uint16_t CPU::DirectPageIndexedX() {
uint8_t operand = FetchByte();
uint16_t x_by_mode = GetAccumulatorSize() ? X : X & 0xFF;
return D + operand + x_by_mode;
}
uint16_t CPU::DirectPageIndexedY() {
uint8_t operand = FetchByte();
return (operand + Y) & 0xFF;
}
uint16_t CPU::DirectPageIndexedIndirectX() {
uint8_t operand = FetchByte();
uint16_t indirect_address = D + operand + X;
uint16_t effective_address = memory.ReadWord(indirect_address & 0xFFFF);
return effective_address;
}
uint16_t CPU::DirectPageIndirect() {
uint8_t dp = FetchByte();
uint16_t effective_address = D + dp;
return memory.ReadWord(effective_address);
}
uint32_t CPU::DirectPageIndirectLong() {
uint8_t dp = FetchByte();
uint16_t effective_address = D + dp;
return memory.ReadWordLong((0x00 << 0x10) | effective_address);
}
uint16_t CPU::DirectPageIndirectIndexedY() {
uint8_t operand = FetchByte();
uint16_t indirect_address = D + operand;
return memory.ReadWord(indirect_address) + Y;
}
uint32_t CPU::DirectPageIndirectLongIndexedY() {
uint8_t operand = FetchByte();
uint16_t indirect_address = D + operand;
uint16_t y_by_mode = GetAccumulatorSize() ? Y : Y & 0xFF;
uint32_t effective_address =
memory.ReadWordLong(indirect_address) + y_by_mode;
return effective_address;
}
uint16_t CPU::Immediate() {
if (GetAccumulatorSize()) {
return memory.ReadByte((PB << 16) | PC + 1);
} else {
return memory.ReadWord((PB << 16) | PC + 1);
}
}
uint16_t CPU::StackRelative() {
uint8_t sr = FetchByte();
uint16_t effective_address = SP() + sr;
return effective_address;
}
uint32_t CPU::StackRelativeIndirectIndexedY() {
uint8_t sr = FetchByte();
return (DB << 0x10) | (memory.ReadWord(SP() + sr) + Y);
}
} // namespace emu
} // namespace app
} // namespace yaze

View File

@@ -0,0 +1,818 @@
#include <iostream>
#include <string>
#include <vector>
#include "app/emu/cpu.h"
namespace yaze {
namespace app {
namespace emu {
/**
* 65816 Instruction Set
*
* TODO: STP, WDM
*/
void CPU::ADC(uint16_t operand) {
bool C = GetCarryFlag();
if (GetAccumulatorSize()) { // 8-bit mode
uint16_t result = static_cast<uint16_t>(A & 0xFF) +
static_cast<uint16_t>(operand) + (C ? 1 : 0);
SetCarryFlag(result > 0xFF); // Update the carry flag
// Update the overflow flag
bool overflow = (~(A ^ operand) & (A ^ result) & 0x80) != 0;
SetOverflowFlag(overflow);
// Update the accumulator with proper wrap-around
A = (A & 0xFF00) | (result & 0xFF);
SetZeroFlag((A & 0xFF) == 0);
SetNegativeFlag(A & 0x80);
} else {
uint32_t result =
static_cast<uint32_t>(A) + static_cast<uint32_t>(operand) + (C ? 1 : 0);
SetCarryFlag(result > 0xFFFF); // Update the carry flag
// Update the overflow flag
bool overflow = (~(A ^ operand) & (A ^ result) & 0x8000) != 0;
SetOverflowFlag(overflow);
// Update the accumulator
A = result & 0xFFFF;
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x8000);
}
}
void CPU::AND(uint32_t value, bool isImmediate) {
uint16_t operand;
if (GetAccumulatorSize()) { // 8-bit mode
operand = isImmediate ? value : memory.ReadByte(value);
A &= operand;
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x80);
} else { // 16-bit mode
operand = isImmediate ? value : memory.ReadWord(value);
A &= operand;
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x8000);
}
}
// New function for absolute long addressing mode
void CPU::ANDAbsoluteLong(uint32_t address) {
uint32_t operand32 = memory.ReadWordLong(address);
A &= operand32;
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x8000);
}
void CPU::ASL(uint16_t address) {
uint8_t value = memory.ReadByte(address);
SetCarryFlag(!(value & 0x80)); // Set carry flag if bit 7 is set
value <<= 1; // Shift left
value &= 0xFE; // Clear bit 0
memory.WriteByte(address, value);
SetNegativeFlag(!value);
SetZeroFlag(value);
}
void CPU::BCC(int8_t offset) {
if (!GetCarryFlag()) { // If the carry flag is clear
next_pc_ = offset;
}
}
void CPU::BCS(int8_t offset) {
if (GetCarryFlag()) { // If the carry flag is set
next_pc_ = offset;
}
}
void CPU::BEQ(int8_t offset) {
if (GetZeroFlag()) { // If the zero flag is set
next_pc_ = offset;
}
}
void CPU::BIT(uint16_t address) {
uint8_t value = memory.ReadByte(address);
SetNegativeFlag(value & 0x80);
SetOverflowFlag(value & 0x40);
SetZeroFlag((A & value) == 0);
}
void CPU::BMI(int8_t offset) {
if (GetNegativeFlag()) { // If the negative flag is set
next_pc_ = offset;
}
}
void CPU::BNE(int8_t offset) {
if (!GetZeroFlag()) { // If the zero flag is clear
// PC += offset;
next_pc_ = offset;
}
}
void CPU::BPL(int8_t offset) {
if (!GetNegativeFlag()) { // If the negative flag is clear
next_pc_ = offset;
}
}
void CPU::BRA(int8_t offset) { next_pc_ = offset; }
void CPU::BRK() {
next_pc_ = PC + 2; // Increment the program counter by 2
memory.PushWord(next_pc_);
memory.PushByte(status);
SetInterruptFlag(true);
try {
next_pc_ = memory.ReadWord(0xFFFE);
} catch (const std::exception& e) {
std::cout << "BRK: " << e.what() << std::endl;
}
}
void CPU::BRL(int16_t offset) { next_pc_ = offset; }
void CPU::BVC(int8_t offset) {
if (!GetOverflowFlag()) { // If the overflow flag is clear
next_pc_ = offset;
}
}
void CPU::BVS(int8_t offset) {
if (GetOverflowFlag()) { // If the overflow flag is set
next_pc_ = offset;
}
}
void CPU::CLC() { status &= ~0x01; }
void CPU::CLD() { status &= ~0x08; }
void CPU::CLI() { status &= ~0x04; }
void CPU::CLV() { status &= ~0x40; }
// n Set if MSB of result is set; else cleared
// z Set if result is zero; else cleared
// c Set if no borrow; else cleared
void CPU::CMP(uint32_t value, bool isImmediate) {
if (GetAccumulatorSize()) { // 8-bit
uint8_t result;
if (isImmediate) {
result = A - (value & 0xFF);
} else {
uint8_t memory_value = memory.ReadByte(value);
result = A - memory_value;
}
SetZeroFlag(result == 0);
SetNegativeFlag(result & 0x80);
SetCarryFlag(A >= (value & 0xFF));
} else { // 16-bit
uint16_t result;
if (isImmediate) {
result = A - (value & 0xFFFF);
} else {
uint16_t memory_value = memory.ReadWord(value);
result = A - memory_value;
}
SetZeroFlag(result == 0);
SetNegativeFlag(result & 0x8000);
SetCarryFlag(A >= (value & 0xFFFF));
}
}
void CPU::COP() {
next_pc_ += 2; // Increment the program counter by 2
memory.PushWord(next_pc_);
memory.PushByte(status);
SetInterruptFlag(true);
if (E) {
next_pc_ = memory.ReadWord(0xFFF4);
} else {
next_pc_ = memory.ReadWord(0xFFE4);
}
SetDecimalFlag(false);
}
void CPU::CPX(uint16_t value, bool isImmediate) {
if (GetIndexSize()) { // 8-bit
uint8_t memory_value = isImmediate ? value : memory.ReadByte(value);
compare(X, memory_value);
} else { // 16-bit
uint16_t memory_value = isImmediate ? value : memory.ReadWord(value);
compare(X, memory_value);
}
}
void CPU::CPY(uint16_t value, bool isImmediate) {
if (GetIndexSize()) { // 8-bit
uint8_t memory_value = isImmediate ? value : memory.ReadByte(value);
compare(Y, memory_value);
} else { // 16-bit
uint16_t memory_value = isImmediate ? value : memory.ReadWord(value);
compare(Y, memory_value);
}
}
void CPU::DEC(uint32_t address, bool accumulator) {
if (accumulator) {
if (GetAccumulatorSize()) { // 8-bit
A = (A - 1) & 0xFF;
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x80);
} else { // 16-bit
A = (A - 1) & 0xFFFF;
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x8000);
}
return;
}
if (GetAccumulatorSize()) {
uint8_t value = memory.ReadByte(address);
value--;
memory.WriteByte(address, value);
SetZeroFlag(value == 0);
SetNegativeFlag(value & 0x80);
} else {
uint16_t value = memory.ReadWord(address);
value--;
memory.WriteWord(address, value);
SetZeroFlag(value == 0);
SetNegativeFlag(value & 0x8000);
}
}
void CPU::DEX() {
if (GetIndexSize()) { // 8-bit
X = static_cast<uint8_t>(X - 1);
SetZeroFlag(X == 0);
SetNegativeFlag(X & 0x80);
} else { // 16-bit
X = static_cast<uint16_t>(X - 1);
SetZeroFlag(X == 0);
SetNegativeFlag(X & 0x8000);
}
}
void CPU::DEY() {
if (GetIndexSize()) { // 8-bit
Y = static_cast<uint8_t>(Y - 1);
SetZeroFlag(Y == 0);
SetNegativeFlag(Y & 0x80);
} else { // 16-bit
Y = static_cast<uint16_t>(Y - 1);
SetZeroFlag(Y == 0);
SetNegativeFlag(Y & 0x8000);
}
}
void CPU::EOR(uint32_t address, bool isImmediate) {
if (GetAccumulatorSize()) {
A ^= isImmediate ? address : memory.ReadByte(address);
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x80);
} else {
A ^= isImmediate ? address : memory.ReadWord(address);
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x8000);
}
}
void CPU::INC(uint32_t address, bool accumulator) {
if (accumulator) {
if (GetAccumulatorSize()) { // 8-bit
A = (A + 1) & 0xFF;
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x80);
} else { // 16-bit
A = (A + 1) & 0xFFFF;
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x8000);
}
return;
}
if (GetAccumulatorSize()) {
uint8_t value = memory.ReadByte(address);
value++;
memory.WriteByte(address, value);
SetNegativeFlag(value & 0x80);
SetZeroFlag(value == 0);
} else {
uint16_t value = memory.ReadWord(address);
value++;
memory.WriteWord(address, value);
SetNegativeFlag(value & 0x8000);
SetZeroFlag(value == 0);
}
}
void CPU::INX() {
if (GetIndexSize()) { // 8-bit
X = static_cast<uint8_t>(X + 1);
SetZeroFlag(X == 0);
SetNegativeFlag(X & 0x80);
} else { // 16-bit
X = static_cast<uint16_t>(X + 1);
SetZeroFlag(X == 0);
SetNegativeFlag(X & 0x8000);
}
}
void CPU::INY() {
if (GetIndexSize()) { // 8-bit
Y = static_cast<uint8_t>(Y + 1);
SetZeroFlag(Y == 0);
SetNegativeFlag(Y & 0x80);
} else { // 16-bit
Y = static_cast<uint16_t>(Y + 1);
SetZeroFlag(Y == 0);
SetNegativeFlag(Y & 0x8000);
}
}
void CPU::JMP(uint16_t address) {
next_pc_ = address; // Set program counter to the new address
}
void CPU::JML(uint32_t address) {
next_pc_ = static_cast<uint16_t>(address & 0xFFFF);
// Set the PBR to the upper 8 bits of the address
PB = static_cast<uint8_t>((address >> 16) & 0xFF);
}
void CPU::JSR(uint16_t address) {
memory.PushWord(PC); // Push the program counter onto the stack
next_pc_ = address; // Set program counter to the new address
}
void CPU::JSL(uint32_t address) {
memory.PushLong(PC); // Push the program counter onto the stack as a long
// value (24 bits)
next_pc_ = address; // Set program counter to the new address
}
void CPU::LDA(uint16_t address, bool isImmediate, bool direct_page) {
uint8_t bank = PB;
if (direct_page) {
bank = 0;
}
if (GetAccumulatorSize()) {
A = isImmediate ? address : memory.ReadByte((bank << 16) | address);
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x80);
} else {
A = isImmediate ? address : memory.ReadWord((bank << 16) | address);
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x8000);
}
}
void CPU::LDX(uint16_t address, bool isImmediate) {
if (GetIndexSize()) {
X = isImmediate ? address : memory.ReadByte(address);
SetZeroFlag(X == 0);
SetNegativeFlag(X & 0x80);
} else {
X = isImmediate ? address : memory.ReadWord(address);
SetZeroFlag(X == 0);
SetNegativeFlag(X & 0x8000);
}
}
void CPU::LDY(uint16_t address, bool isImmediate) {
if (GetIndexSize()) {
Y = isImmediate ? address : memory.ReadByte(address);
SetZeroFlag(Y == 0);
SetNegativeFlag(Y & 0x80);
} else {
Y = isImmediate ? address : memory.ReadWord(address);
SetZeroFlag(Y == 0);
SetNegativeFlag(Y & 0x8000);
}
}
void CPU::LSR(uint16_t address, bool accumulator) {
if (accumulator) {
if (GetAccumulatorSize()) { // 8-bit
SetCarryFlag(A & 0x01);
A >>= 1;
SetZeroFlag(A == 0);
SetNegativeFlag(false);
} else { // 16-bit
SetCarryFlag(A & 0x0001);
A >>= 1;
SetZeroFlag(A == 0);
SetNegativeFlag(false);
}
return;
}
uint8_t value = memory.ReadByte(address);
SetCarryFlag(value & 0x01);
value >>= 1;
memory.WriteByte(address, value);
SetNegativeFlag(false);
SetZeroFlag(value == 0);
}
void CPU::MVN(uint16_t source, uint16_t dest, uint16_t length) {
for (uint16_t i = 0; i < length; i++) {
memory.WriteByte(dest, memory.ReadByte(source));
source++;
dest++;
}
}
void CPU::MVP(uint16_t source, uint16_t dest, uint16_t length) {
for (uint16_t i = 0; i < length; i++) {
memory.WriteByte(dest, memory.ReadByte(source));
source--;
dest--;
}
}
void CPU::NOP() {
// Do nothing
}
void CPU::ORA(uint16_t address, bool isImmediate) {
if (GetAccumulatorSize()) {
A |= isImmediate ? address : memory.ReadByte(address);
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x80);
} else {
A |= isImmediate ? address : memory.ReadWord(address);
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x8000);
}
}
void CPU::PEA() {
uint16_t address = FetchWord();
memory.PushWord(address);
}
void CPU::PEI() {
uint16_t address = FetchWord();
memory.PushWord(memory.ReadWord(address));
}
void CPU::PER() {
uint16_t address = FetchWord();
memory.PushWord(PC + address);
}
void CPU::PHA() {
if (GetAccumulatorSize()) {
memory.PushByte(static_cast<uint8_t>(A));
} else {
memory.PushWord(A);
}
}
void CPU::PHB() { memory.PushByte(DB); }
void CPU::PHD() { memory.PushWord(D); }
void CPU::PHK() { memory.PushByte(PB); }
void CPU::PHP() { memory.PushByte(status); }
void CPU::PHX() {
if (GetIndexSize()) {
memory.PushByte(static_cast<uint8_t>(X));
} else {
memory.PushWord(X);
}
}
void CPU::PHY() {
if (GetIndexSize()) {
memory.PushByte(static_cast<uint8_t>(Y));
} else {
memory.PushWord(Y);
}
}
void CPU::PLA() {
if (GetAccumulatorSize()) {
A = memory.PopByte();
SetNegativeFlag((A & 0x80) != 0);
} else {
A = memory.PopWord();
SetNegativeFlag((A & 0x8000) != 0);
}
SetZeroFlag(A == 0);
}
void CPU::PLB() {
DB = memory.PopByte();
SetNegativeFlag((DB & 0x80) != 0);
SetZeroFlag(DB == 0);
}
// Pull Direct Page Register from Stack
void CPU::PLD() {
D = memory.PopWord();
SetNegativeFlag((D & 0x8000) != 0);
SetZeroFlag(D == 0);
}
// Pull Processor Status Register from Stack
void CPU::PLP() { status = memory.PopByte(); }
void CPU::PLX() {
if (GetIndexSize()) {
X = memory.PopByte();
SetNegativeFlag((A & 0x80) != 0);
} else {
X = memory.PopWord();
SetNegativeFlag((A & 0x8000) != 0);
}
SetZeroFlag(X == 0);
}
void CPU::PLY() {
if (GetIndexSize()) {
Y = memory.PopByte();
SetNegativeFlag((A & 0x80) != 0);
} else {
Y = memory.PopWord();
SetNegativeFlag((A & 0x8000) != 0);
}
SetZeroFlag(Y == 0);
}
void CPU::REP() {
auto byte = FetchByte();
status &= ~byte;
}
void CPU::ROL(uint32_t address, bool accumulator) {
if (accumulator) {
if (GetAccumulatorSize()) { // 8-bit
uint8_t carry = GetCarryFlag() ? 0x01 : 0x00;
SetCarryFlag(A & 0x80);
A <<= 1;
A |= carry;
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x80);
} else { // 16-bit
uint8_t carry = GetCarryFlag() ? 0x01 : 0x00;
SetCarryFlag(A & 0x8000);
A <<= 1;
A |= carry;
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x8000);
}
return;
}
uint8_t value = memory.ReadByte(address);
uint8_t carry = GetCarryFlag() ? 0x01 : 0x00;
SetCarryFlag(value & 0x80);
value <<= 1;
value |= carry;
memory.WriteByte(address, value);
SetNegativeFlag(value & 0x80);
SetZeroFlag(value == 0);
}
void CPU::ROR(uint32_t address, bool accumulator) {
if (accumulator) {
if (GetAccumulatorSize()) { // 8-bit
uint8_t carry = GetCarryFlag() ? 0x80 : 0x00;
SetCarryFlag(A & 0x01);
A >>= 1;
A |= carry;
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x80);
} else { // 16-bit
uint8_t carry = GetCarryFlag() ? 0x8000 : 0x00;
SetCarryFlag(A & 0x0001);
A >>= 1;
A |= carry;
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x8000);
}
return;
}
uint8_t value = memory.ReadByte(address);
uint8_t carry = GetCarryFlag() ? 0x80 : 0x00;
SetCarryFlag(value & 0x01);
value >>= 1;
value |= carry;
memory.WriteByte(address, value);
SetNegativeFlag(value & 0x80);
SetZeroFlag(value == 0);
}
void CPU::RTI() {
status = memory.PopByte();
PC = memory.PopWord();
}
void CPU::RTL() {
next_pc_ = memory.PopWord();
PB = memory.PopByte();
}
void CPU::RTS() { last_call_frame_ = memory.PopWord(); }
void CPU::SBC(uint32_t value, bool isImmediate) {
uint16_t operand;
if (!GetAccumulatorSize()) { // 16-bit mode
operand = isImmediate ? value : memory.ReadWord(value);
uint16_t result = A - operand - (GetCarryFlag() ? 0 : 1);
SetCarryFlag(!(result > 0xFFFF)); // Update the carry flag
// Update the overflow flag
bool overflow = ((A ^ operand) & (A ^ result) & 0x8000) != 0;
SetOverflowFlag(overflow);
// Update the accumulator
A = result & 0xFFFF;
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x8000);
} else { // 8-bit mode
operand = isImmediate ? value : memory.ReadByte(value);
uint8_t result = A - operand - (GetCarryFlag() ? 0 : 1);
SetCarryFlag(!(result > 0xFF)); // Update the carry flag
// Update the overflow flag
bool overflow = ((A ^ operand) & (A ^ result) & 0x80) != 0;
SetOverflowFlag(overflow);
// Update the accumulator
A = result & 0xFF;
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x80);
}
}
void CPU::SEC() { status |= 0x01; }
void CPU::SED() { status |= 0x08; }
void CPU::SEI() { status |= 0x04; }
void CPU::SEP() {
auto byte = FetchByte();
status |= byte;
}
void CPU::STA(uint32_t address) {
if (GetAccumulatorSize()) {
memory.WriteByte(address, static_cast<uint8_t>(A));
} else {
memory.WriteWord(address, A);
}
}
// TODO: Make this work with the Clock class of the CPU
void CPU::STP() {
// During the next phase 2 clock cycle, stop the processors oscillator input
// The processor is effectively shut down until a reset occurs (RES` pin).
}
void CPU::STX(uint16_t address) {
if (GetIndexSize()) {
memory.WriteByte(address, static_cast<uint8_t>(X));
} else {
memory.WriteWord(address, X);
}
}
void CPU::STY(uint16_t address) {
if (GetIndexSize()) {
memory.WriteByte(address, static_cast<uint8_t>(Y));
} else {
memory.WriteWord(address, Y);
}
}
void CPU::STZ(uint16_t address) {
if (GetAccumulatorSize()) {
memory.WriteByte(address, 0x00);
} else {
memory.WriteWord(address, 0x0000);
}
}
void CPU::TAX() {
X = A;
SetZeroFlag(X == 0);
SetNegativeFlag(X & 0x80);
}
void CPU::TAY() {
Y = A;
SetZeroFlag(Y == 0);
SetNegativeFlag(Y & 0x80);
}
void CPU::TCD() {
D = A;
SetZeroFlag(D == 0);
SetNegativeFlag(D & 0x80);
}
void CPU::TCS() { memory.SetSP(A); }
void CPU::TDC() {
A = D;
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x80);
}
void CPU::TRB(uint16_t address) {
uint8_t value = memory.ReadByte(address);
SetZeroFlag((A & value) == 0);
value &= ~A;
memory.WriteByte(address, value);
}
void CPU::TSB(uint16_t address) {
uint8_t value = memory.ReadByte(address);
SetZeroFlag((A & value) == 0);
value |= A;
memory.WriteByte(address, value);
}
void CPU::TSC() {
A = SP();
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x80);
}
void CPU::TSX() {
X = SP();
SetZeroFlag(X == 0);
SetNegativeFlag(X & 0x80);
}
void CPU::TXA() {
A = X;
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x80);
}
void CPU::TXS() { memory.SetSP(X); }
void CPU::TXY() {
Y = X;
SetZeroFlag(X == 0);
SetNegativeFlag(X & 0x80);
}
void CPU::TYA() {
A = Y;
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x80);
}
void CPU::TYX() {
X = Y;
SetZeroFlag(Y == 0);
SetNegativeFlag(Y & 0x80);
}
// TODO: Make this communicate with the SNES class
void CPU::WAI() {
// Pull the RDY pin low
// Power consumption is reduced(?)
// RDY remains low until an external hardware interupt
// (NMI, IRQ, ABORT, or RESET) is received from the SNES class
}
void CPU::XBA() {
uint8_t lowByte = A & 0xFF;
uint8_t highByte = (A >> 8) & 0xFF;
A = (lowByte << 8) | highByte;
}
void CPU::XCE() {
uint8_t carry = status & 0x01;
status &= ~0x01;
status |= E;
E = carry;
}
} // namespace emu
} // namespace app
} // namespace yaze

View File

@@ -125,12 +125,10 @@ class Memory {
virtual void PushLong(uint32_t value) = 0;
virtual uint32_t PopLong() = 0;
virtual int16_t SP() const = 0;
virtual void SetSP(int16_t value) = 0;
virtual uint16_t SP() const = 0;
virtual void SetSP(uint16_t value) = 0;
virtual void SetMemory(const std::vector<uint8_t>& data) = 0;
virtual void ClearMemory() = 0;
virtual void LoadData(const std::vector<uint8_t>& data) = 0;
virtual uint8_t operator[](int i) const = 0;
virtual uint8_t at(int i) const = 0;
@@ -313,15 +311,9 @@ class MemoryImpl : public Memory, public Loggable {
void AddObserver(Observer* observer) { observers_.push_back(observer); }
// Stack Pointer access.
int16_t SP() const override { return SP_; }
void SetSP(int16_t value) override { SP_ = value; }
uint16_t SP() const override { return SP_; }
void SetSP(uint16_t value) override { SP_ = value; }
void ClearMemory() override { std::fill(memory_.begin(), memory_.end(), 0); }
void SetMemory(const std::vector<uint8_t>& data) override {
std::copy(data.begin(), data.end(), memory_.begin());
}
void LoadData(const std::vector<uint8_t>& data) override {
std::copy(data.begin(), data.end(), memory_.begin());
}
uint8_t at(int i) const override { return memory_[i]; }
uint8_t operator[](int i) const override {

View File

@@ -21,6 +21,9 @@ class MockClock : public Clock {
MOCK_METHOD(float, GetFrequency, (), (const, override));
};
// 0x1000000 is 16 MB, simplifying the memory layout for testing
// 2 MB is = 0x200000
class MockMemory : public Memory {
public:
MOCK_CONST_METHOD1(ReadByte, uint8_t(uint32_t address));
@@ -39,8 +42,8 @@ class MockMemory : public Memory {
MOCK_METHOD1(PushLong, void(uint32_t value));
MOCK_METHOD0(PopLong, uint32_t());
MOCK_CONST_METHOD0(SP, int16_t());
MOCK_METHOD1(SetSP, void(int16_t value));
MOCK_CONST_METHOD0(SP, uint16_t());
MOCK_METHOD1(SetSP, void(uint16_t value));
MOCK_METHOD1(SetMemory, void(const std::vector<uint8_t>& data));
MOCK_METHOD1(LoadData, void(const std::vector<uint8_t>& data));
@@ -51,12 +54,16 @@ class MockMemory : public Memory {
uint8_t operator[](int i) const override { return memory_[i]; }
void SetMemoryContents(const std::vector<uint8_t>& data) {
memory_.resize(64000);
if (data.size() > memory_.size()) {
memory_.resize(data.size());
}
std::copy(data.begin(), data.end(), memory_.begin());
}
void SetMemoryContents(const std::vector<uint16_t>& data) {
memory_.resize(64000);
if (data.size() > memory_.size()) {
memory_.resize(data.size());
}
int i = 0;
for (const auto& each : data) {
memory_[i] = each & 0xFF;
@@ -66,6 +73,10 @@ class MockMemory : public Memory {
}
void InsertMemory(const uint64_t address, const std::vector<uint8_t>& data) {
if (address > memory_.size()) {
memory_.resize(address + data.size());
}
int i = 0;
for (const auto& each : data) {
memory_[address + i] = each;
@@ -160,6 +171,9 @@ class MockMemory : public Memory {
this->SetSP(SP_ + 3);
return value;
});
ON_CALL(*this, SP()).WillByDefault([this]() { return SP_; });
ON_CALL(*this, SetSP(::testing::_))
.WillByDefault([this](uint16_t value) { SP_ = value; });
ON_CALL(*this, ClearMemory()).WillByDefault([this]() {
memory_.resize(64000, 0x00);
});

View File

@@ -29,7 +29,6 @@ uint16_t GetHeaderOffset(const Memory& memory) {
switch (mapMode & 0x07) {
case 0: // LoROM
offset = 0x7FC0;
// offset = 0xFFC0;
break;
case 1: // HiROM
offset = 0xFFC0;
@@ -113,12 +112,13 @@ void SNES::Init(ROM& rom) {
cpu_.E = 0;
// Initialize CPU
cpu_.Init();
cpu_.Init();
// Read the ROM header
auto header_offset = GetHeaderOffset(memory_);
rom_info_ = ReadRomHeader((0x00 << 16) + header_offset);
cpu_.PC = rom_info_.resetVector;
cpu_.PB = 0x00;
cpu_.PC = 0x8000;
// Initialize PPU
ppu_.Init();
@@ -300,16 +300,28 @@ void SNES::NmiIsr() {
// VBlank routine
void SNES::VBlankRoutine() {
// Execute code that needs to run during VBlank, such as transferring data to
// the PPU
// Read the joypad state
// ...
// Update the PPU
// ...
// Update the APU
// ...
}
void SNES::BootAPUWithIPL() {
// 1. Waiting for the SPC700 to be ready
while (!apu_.IsReadySignalReceived()) {
// Active waiting (this can be optimized)
void SNES::BootApuWithIPL() {
// 1. Check if the SPC700 is ready, else set a callback for when it becomes
// ready
if (!apu_.IsReadySignalReceived()) {
apu_.SetReadyCallback([this]() { this->StartApuDataTransfer(); });
return; // Exit and wait for callback to be called
}
StartApuDataTransfer();
}
void SNES::StartApuDataTransfer() {
// 2. Setting the starting address
const uint16_t startAddress = 0x0200;
memory_.WriteByte(0x2142, startAddress & 0xFF); // Lower byte

View File

@@ -1,8 +1,6 @@
#ifndef YAZE_APP_EMU_SNES_H
#define YAZE_APP_EMU_SNES_H
#include <SDL_mixer.h>
#include <cstdint>
#include <memory>
#include <string>
@@ -48,7 +46,8 @@ class SNES : public DMA {
void VBlankRoutine();
// Boot the APU with the IPL ROM
void BootAPUWithIPL();
void BootApuWithIPL();
void StartApuDataTransfer();
// Controller input handling
void HandleInput();

View File

@@ -6,6 +6,7 @@ add_executable(
app/rom.cc
app/core/common.cc
app/gui/pipeline.cc
${YAZE_APP_EMU_SRC}
${YAZE_APP_GFX_SRC}
${YAZE_APP_ZELDA3_SRC}
${YAZE_GUI_SRC}
@@ -16,11 +17,11 @@ target_include_directories(
z3ed PUBLIC
lib/
app/
lib/SDL_mixer/include/
${CMAKE_SOURCE_DIR}/src/
${PNG_INCLUDE_DIRS}
${SDL2_INCLUDE_DIR}
${GLEW_INCLUDE_DIRS}
lib/SDL_mixer/include/
)
target_link_libraries(

View File

@@ -14,12 +14,13 @@
#include "absl/strings/str_cat.h"
#include "app/core/common.h" // for PcToSnes, SnesToPc
#include "app/core/constants.h" // for RETURN_IF_ERROR
#include "app/gui/pipeline.h"
#include "app/emu/snes.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/compression.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
#include "app/gui/canvas.h"
#include "app/gui/pipeline.h"
#include "app/rom.h" // for ROM
#include "app/zelda3/overworld.h"
#include "cli/patch.h" // for ApplyBpsPatch, CreateBpsPatch
@@ -273,8 +274,32 @@ class Expand : public CommandHandler {
}
};
// Start Emulator on a SNES rom file
// -emu <rom_file> <optional:num_cpu_cycles>
class Emulator : public CommandHandler {
public:
absl::Status handle(const std::vector<std::string>& arg_vec) override {
std::string filename = arg_vec[0];
RETURN_IF_ERROR(rom_.LoadFromFile(filename))
snes.SetupMemory(rom_);
snes.Init(rom_);
int i = 0;
while (i < 80000) {
snes.Run();
i++;
}
return absl::OkStatus();
}
app::emu::SNES snes;
};
struct Commands {
std::unordered_map<std::string, std::shared_ptr<CommandHandler>> handlers = {
{"-emu", std::make_shared<Emulator>()},
{"-a", std::make_shared<ApplyPatch>()},
{"-c", std::make_shared<CreatePatch>()},
{"-o", std::make_shared<Open>()},

View File

@@ -23,6 +23,8 @@ add_executable(
../src/cli/command_handler.cc
../src/app/rom.cc
../src/app/emu/cpu.cc
../src/app/emu/cpu/instructions.cc
../src/app/emu/cpu/addressing.cc
../src/app/emu/audio/apu.cc
../src/app/emu/video/ppu.cc
../src/app/emu/audio/dsp.cc

File diff suppressed because it is too large Load Diff