diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index a4092a98..f7b78193 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -6,6 +6,7 @@ add_executable( app/emu/cpu.cc app/emu/ppu.cc app/emu/snes.cc + app/emu/spc700.cc ${YAZE_APP_CORE_SRC} ${YAZE_APP_EDITOR_SRC} ${YAZE_APP_GFX_SRC} diff --git a/src/app/emu/apu.h b/src/app/emu/apu.h index 1cedabdd..5cf6df46 100644 --- a/src/app/emu/apu.h +++ b/src/app/emu/apu.h @@ -4,12 +4,13 @@ #include #include "app/emu/mem.h" +#include "app/emu/spc700.h" namespace yaze { namespace app { namespace emu { -class APU { +class APU : public SPC700 { public: // Initializes the APU with the necessary resources and dependencies APU(Memory &memory); diff --git a/src/app/emu/spc700.cc b/src/app/emu/spc700.cc new file mode 100644 index 00000000..f1b23c90 --- /dev/null +++ b/src/app/emu/spc700.cc @@ -0,0 +1,474 @@ +#include "app/emu/spc700.h" + +#include +#include + +namespace yaze { +namespace app { +namespace emu { + +void SPC700::ExecuteInstructions(uint8_t opcode) { + switch (opcode) { + // 8-bit Move Memory to Register + case 0xE8: // MOV A, #imm + break; + case 0xE6: // MOV A, (X) + break; + case 0xBF: // MOV A, (X)+ + break; + case 0xE4: // MOV A, dp + break; + case 0xF4: // MOV A, dp+X + break; + case 0xE5: // MOV A, !abs + break; + case 0xF5: // MOV A, !abs+X + break; + case 0xF6: // MOV A, !abs+Y + break; + case 0xE7: // MOV A, [dp+X] + break; + case 0xF7: // MOV A, [dp]+Y + break; + case 0xCD: // MOV X, #imm + break; + case 0xF8: // MOV X, dp + break; + case 0xF9: // MOV X, dp+Y + break; + case 0xE9: // MOV X, !abs + break; + case 0x8D: // MOV Y, #imm + break; + case 0xEB: // MOV Y, dp + break; + case 0xFB: // MOV Y, dp+X + break; + case 0xEC: // MOV Y, !abs + break; + + // 8-bit move register to memory + + case 0xC6: // MOV (X), A + break; + case 0xAF: // MOV (X)+, A + break; + case 0xC4: // MOV dp, A + break; + case 0xD4: // MOV dp+X, A + break; + case 0xC5: // MOV !abs, A + break; + case 0xD5: // MOV !abs+X, A + break; + case 0xD6: // MOV !abs+Y, A + break; + case 0xC7: // MOV [dp+X], A + break; + case 0xD7: // MOV [dp]+Y, A + break; + case 0xD8: // MOV dp, X + break; + case 0xD9: // MOV dp+Y, X + break; + case 0xC9: // MOV !abs, X + break; + case 0xCB: // MOV dp, Y + break; + case 0xDB: // MOV dp+X, Y + break; + case 0xCC: // MOV !abs, Y + break; + + // . 8-bit move register to register / special direct page moves + + case 0x7D: // MOV A, X + break; + case 0xDD: // MOV A, Y + break; + case 0x5D: // MOV X, A + break; + case 0xFD: // MOV Y, A + break; + case 0x9D: // MOV X, SP + break; + case 0xBD: // MOV SP, X + break; + case 0xFA: // MOV dp, dp + break; + case 0x8F: // MOV dp, #imm + break; + + // . 8-bit arithmetic + + case 0x88: // ADC A, #imm + break; + case 0x86: // ADC A, (X) + break; + case 0x84: // ADC A, dp + break; + case 0x94: // ADC A, dp+X + break; + case 0x85: // ADC A, !abs + break; + case 0x95: // ADC A, !abs+X + break; + case 0x96: // ADC A, !abs+Y + break; + case 0x87: // ADC A, [dp+X] + break; + case 0x97: // ADC A, [dp]+Y + break; + case 0x99: // ADC (X), (Y) + break; + case 0x89: // ADC dp, dp + break; + case 0x98: // ADC dp, #imm + break; + case 0xA8: // SBC A, #imm + break; + case 0xA6: // SBC A, (X) + break; + case 0xA4: // SBC A, dp + break; + case 0xB4: // SBC A, dp+X + break; + case 0xA5: // SBC A, !abs + break; + case 0xB5: // SBC A, !abs+X + break; + case 0xB6: // SBC A, !abs+Y + break; + case 0xA7: // SBC A, [dp+X] + break; + case 0xB7: // SBC A, [dp]+Y + break; + case 0xB9: // SBC (X), (Y) + break; + case 0xA9: // SBC dp, dp + break; + case 0xB8: // SBC dp, #imm + break; + case 0x68: // CMP A, #imm + break; + case 0x66: // CMP A, (X) + break; + case 0x64: // CMP A, dp + break; + case 0x74: // CMP A, dp+X + break; + case 0x65: // CMP A, !abs + break; + case 0x75: // CMP A, !abs+X + break; + case 0x76: // CMP A, !abs+Y + break; + case 0x67: // CMP A, [dp+X] + break; + case 0x77: // CMP A, [dp]+Y + break; + case 0x79: // CMP (X), (Y) + break; + case 0x69: // CMP dp, dp + break; + case 0x78: // CMP dp, #imm + break; + case 0xC8: // CMP X, #imm + break; + case 0x3E: // CMP X, dp + break; + case 0x1E: // CMP X, !abs + break; + case 0xAD: // CMP Y, #imm + break; + case 0x7E: // CMP Y, dp + break; + case 0x5E: // CMP Y, !abs + break; + + // 8-bit boolean logic + + case 0x28: // AND A, #imm + break; + case 0x26: // AND A, (X) + break; + case 0x24: // AND A, dp + break; + case 0x34: // AND A, dp+X + break; + case 0x25: // AND A, !abs + break; + case 0x35: // AND A, !abs+X + break; + case 0x36: // AND A, !abs+Y + break; + case 0x27: // AND A, [dp+X] + break; + case 0x37: // AND A, [dp]+Y + break; + case 0x39: // AND (X), (Y) + break; + case 0x29: // AND dp, dp + break; + case 0x38: // AND dp, #imm + break; + case 0x08: // OR A, #imm + break; + case 0x06: // OR A, (X) + break; + case 0x04: // OR A, dp + break; + case 0x14: // OR A, dp+X + break; + case 0x05: // OR A, !abs + break; + case 0x15: // OR A, !abs+X + break; + case 0x16: // OR A, !abs+Y + break; + case 0x07: // OR A, [dp+X] + break; + case 0x17: // OR A, [dp]+Y + break; + case 0x19: // OR (X), (Y) + break; + case 0x09: // OR dp, dp + break; + case 0x18: // OR dp, #imm + break; + case 0x48: // EOR A, #imm + break; + case 0x46: // EOR A, (X) + break; + case 0x44: // EOR A, dp + break; + case 0x54: // EOR A, dp+X + break; + case 0x45: // EOR A, !abs + break; + case 0x55: // EOR A, !abs+X + break; + case 0x56: // EOR A, !abs+Y + break; + case 0x47: // EOR A, [dp+X] + break; + case 0x57: // EOR A, [dp]+Y + break; + case 0x59: // EOR (X), (Y) + break; + case 0x49: // EOR dp, dp + break; + case 0x58: // EOR dp, #imm + break; + + // . 8-bit increment / decrement + + case 0xBC: // INC A + break; + case 0xAB: // INC dp + break; + case 0xBB: // INC dp+X + break; + case 0xAC: // INC !abs + break; + case 0x3D: // INC X + break; + case 0xFC: // INC Y + break; + case 0x9C: // DEC A + break; + case 0x8B: // DEC dp + break; + case 0x9B: // DEC dp+X + break; + case 0x8C: // DEC !abs + break; + case 0x1D: // DEC X + break; + case 0xDC: // DEC Y + break; + + // 8-bit shift / rotation + + case 0x1C: // ASL A + break; + case 0x0B: // ASL dp + break; + case 0x1B: // ASL dp+X + break; + case 0x0C: // ASL !abs + break; + case 0x5C: // LSR A + break; + case 0x4B: // LSR dp + break; + case 0x5B: // LSR dp+X + break; + case 0x4C: // LSR !abs + break; + case 0x3C: // ROL A + break; + case 0x2B: // ROL dp + break; + case 0x3B: // ROL dp+X + break; + case 0x2C: // ROL !abs + break; + case 0x7C: // ROR A + break; + case 0x6B: // ROR dp + break; + case 0x7B: // ROR dp+X + break; + case 0x6C: // ROR !abs + break; + case 0x9F: // XCN A Exchange nibbles of A + break; + + // . 16-bit operations + + case 0xBA: // MOVW YA, dp + break; + case 0xDA: // MOVW dp, YA + break; + case 0x3A: // INCW dp + break; + case 0x1A: // DECW dp + break; + case 0x7A: // ADDW YA, dp + break; + case 0x9A: // SUBW YA, dp + break; + case 0x5A: // CMPW YA, dp + break; + case 0xCF: // MUL YA + break; + case 0x9E: // DIV YA, X + break; + + // . decimal adjust + + case 0xDF: // DAA A + break; + case 0xBE: // DAS A + break; + + // . branching + + case 0x2F: // BRA rel + break; + case 0xF0: // BEQ rel + break; + case 0xD0: // BNE rel + break; + case 0xB0: // BCS rel + break; + case 0x90: // BCC rel + break; + case 0x70: // BVS rel + break; + case 0x50: // BVC rel + break; + case 0x30: // BMI rel + break; + case 0x10: // BPL rel + break; + case 0x2E: // CBNE dp, rel + break; + case 0xDE: // CBNE dp+X, rel + break; + case 0x6E: // DBNZ dp, rel + break; + case 0xFE: // DBNZ Y, rel + break; + case 0x5F: // JMP !abs + break; + case 0x1F: // JMP [!abs+X] + break; + + // . subroutines + + case 0x3F: // CALL !abs + break; + case 0x4F: // PCALL up + break; + case 0x6F: // RET + break; + case 0x7F: // RETI + break; + + // . stack + + case 0x2D: // PUSH A + break; + case 0x4D: // PUSH X + break; + case 0x6D: // PUSH Y + break; + case 0x0D: // PUSH PSW + break; + case 0xAE: // POP A + break; + case 0xCE: // POP X + break; + case 0xEE: // POP Y + break; + case 0x8E: // POP PSW + break; + + // . memory bit operations + + case 0xEA: // NOT1 abs, bit + break; + case 0xAA: // MOV1 C, abs, bit + break; + case 0xCA: // MOV1 abs, bit, C + break; + case 0x4A: // AND1 C, abs, bit + break; + case 0x6A: // AND1 C, /abs, bit + break; + case 0x0A: // OR1 C, abs, bit + break; + case 0x2A: // OR1 C, /abs, bit + break; + case 0x8A: // EOR1 C, abs, bit + break; + + // . status flags + + case 0x60: // CLRC + break; + case 0x80: // SETC + break; + case 0xED: // NOTC + break; + case 0xE0: // CLRV + break; + case 0x20: // CLRP + break; + case 0x40: // SETP + break; + case 0xA0: // EI + break; + case 0xC0: // DI + break; + + // .no-operation and halt + + case 0x00: // NOP + break; + case 0xEF: // SLEEP + break; + case 0x0F: // STOP + break; + + default: + std::cout << "Unknown opcode: " << std::hex << opcode << std::endl; + break; + } +} + +} // namespace emu +} // namespace app +} // namespace yaze diff --git a/src/app/emu/spc700.h b/src/app/emu/spc700.h new file mode 100644 index 00000000..86c96e97 --- /dev/null +++ b/src/app/emu/spc700.h @@ -0,0 +1,208 @@ +#ifndef YAZE_APP_EMU_SPC700_H +#define YAZE_APP_EMU_SPC700_H + +#include +#include +#include + +namespace yaze { +namespace app { +namespace emu { + +class AudioRAM { + static const size_t ARAM_SIZE = 64 * 1024; // 64 KB + std::vector ram; + + public: + AudioRAM() : ram(ARAM_SIZE, 0) {} + + // Read a byte from ARAM at the given address + uint8_t read(uint16_t address) const { return ram[address % ARAM_SIZE]; } + + // Write a byte to ARAM at the given address + void write(uint16_t address, uint8_t value) { + ram[address % ARAM_SIZE] = value; + } +}; + +class SDSP { + static const size_t NUM_VOICES = 8; + static const size_t NUM_VOICE_REGS = 10; + static const size_t NUM_GLOBAL_REGS = 15; + + // Each voice has 10 registers + std::vector> voices; + + // Global registers + std::vector globalRegs; + + public: + SDSP() + : voices(NUM_VOICES, std::vector(NUM_VOICE_REGS, 0)), + globalRegs(NUM_GLOBAL_REGS, 0) {} + + // Read a byte from a voice register + uint8_t readVoiceReg(uint8_t voice, uint8_t reg) const { + return voices[voice % NUM_VOICES][reg % NUM_VOICE_REGS]; + } + + // Write a byte to a voice register + void writeVoiceReg(uint8_t voice, uint8_t reg, uint8_t value) { + voices[voice % NUM_VOICES][reg % NUM_VOICE_REGS] = value; + } + + // Read a byte from a global register + uint8_t readGlobalReg(uint8_t reg) const { + return globalRegs[reg % NUM_GLOBAL_REGS]; + } + + // Write a byte to a global register + void writeGlobalReg(uint8_t reg, uint8_t value) { + globalRegs[reg % NUM_GLOBAL_REGS] = value; + } +}; + +class SPC700 { + AudioRAM aram; + SDSP sdsp; + uint8_t testReg; + uint8_t controlReg; + uint8_t dspAddrReg; + + // Registers + uint8_t A; // 8-bit accumulator + uint8_t X; // 8-bit index + uint8_t Y; // 8-bit index + uint16_t YA; // 16-bit pair of A (lsb) and Y (msb) + uint16_t PC; // program counter + uint8_t SP; // stack pointer + + struct Flags { + uint8_t N : 1; // Negative flag + uint8_t V : 1; // Overflow flag + uint8_t P : 1; // Direct page flag + uint8_t B : 1; // Break flag + uint8_t H : 1; // Half-carry flag + uint8_t I : 1; // Interrupt enable + uint8_t Z : 1; // Zero flag + uint8_t C : 1; // Carry flag + }; + Flags PSW; // Processor status word + + void ExecuteInstructions(uint8_t opcode); + + // Read a byte from the memory-mapped registers + uint8_t read(uint16_t address) { + switch (address) { + case 0xF0: + return testReg; + case 0xF1: + return controlReg; + case 0xF2: + return dspAddrReg; + case 0xF3: + return sdsp.readGlobalReg(dspAddrReg); + default: + if (address < 0xFFC0) { + return aram.read(address); + } else { + // Handle IPL ROM or RAM reads here + } + } + return 0; + } + + // Write a byte to the memory-mapped registers + void write(uint16_t address, uint8_t value) { + switch (address) { + case 0xF0: + testReg = value; + break; + case 0xF1: + controlReg = value; + break; + case 0xF2: + dspAddrReg = value; + break; + case 0xF3: + sdsp.writeGlobalReg(dspAddrReg, value); + break; + default: + if (address < 0xFFC0) { + aram.write(address, value); + } else { + // Handle IPL ROM or RAM writes here + } + } + } + + // Addressing modes + uint8_t imm() { + PC++; + return read(PC); + } + + uint8_t dp() { + PC++; + uint8_t offset = read(PC); + return read((PSW.P << 8) + offset); + } + + uint16_t abs() { + PC++; + uint16_t addr = read(PC) | (read(PC) << 8); + return addr; + } + + int8_t rel() { + PC++; + return static_cast(read(PC)); + } + + uint8_t i() { return read((PSW.P << 8) + X); } + + uint8_t i_postinc() { + uint8_t value = read((PSW.P << 8) + X); + X++; + return value; + } + + uint16_t addr_plus_i() { + PC++; + uint16_t addr = read(PC) | (read(PC) << 8); + return read(addr) + X; + } + + uint16_t addr_plus_i_indexed() { + PC++; + uint16_t addr = read(PC) | (read(PC) << 8); + addr += X; + return read(addr) | (read(addr + 1) << 8); + } + + // Instructions + // MOV + void MOV(uint8_t operand, bool isImmediate) { + uint8_t value = isImmediate ? imm() : operand; + operand = value; + PSW.Z = (operand == 0); + PSW.N = (operand & 0x80); + } + + // ADC SBC CMP + // AND OR EOR ASL LSR ROL XCN + // INC DEC + // MOVW INCW DECW ADDW SUBW CMPW MUL DIV + // DAA DAS + // BRA BEQ BNE BCS BCC BVS BVC BMI BPL BBS BBC CBNE DBNZ JMP + // CALL PCALL TCALL BRK RET RETI + // PUSH POP + // SET1 CLR1 TSET1 TCLR1 AND1 OR1 EOR1 NOT1 MOV1 + // CLRC SETC NOTC CLRV CLRP SETP EI DI + // NOP SLEEP STOP +}; + +} // namespace emu +} // namespace app +} // namespace yaze +#endif // YAZE_APP_EMU_SPC700_H \ No newline at end of file