#ifndef YAZE_APP_EMU_SPC700_H #define YAZE_APP_EMU_SPC700_H #include #include #include namespace yaze { namespace app { namespace emu { class AudioRam { public: virtual ~AudioRam() = default; // Read a byte from ARAM at the given address virtual uint8_t read(uint16_t address) const = 0; // Write a byte to ARAM at the given address virtual void write(uint16_t address, uint8_t value) = 0; }; class AudioRamImpl : public AudioRam { static const size_t ARAM_SIZE = 64 * 1024; // 64 KB std::vector ram = std::vector(ARAM_SIZE, 0); public: AudioRamImpl() = default; // Read a byte from ARAM at the given address uint8_t read(uint16_t address) const override { return ram[address % ARAM_SIZE]; } // Write a byte to ARAM at the given address void write(uint16_t address, uint8_t value) override { ram[address % ARAM_SIZE] = value; } }; class SPC700 { private: AudioRam& aram_; public: explicit SPC700(AudioRam& aram) : aram_(aram) {} uint8_t test_register_; uint8_t control_register_; uint8_t dsp_address_register_; // 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 Reset(); void Notify(uint32_t address, uint8_t data); void ExecuteInstructions(uint8_t opcode); // Read a byte from the memory-mapped registers uint8_t read(uint16_t address) { switch (address) { case 0xF0: return test_register_; case 0xF1: return control_register_; case 0xF2: return dsp_address_register_; 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: test_register_ = value; break; case 0xF1: control_register_ = value; break; case 0xF2: dsp_address_register_ = value; break; default: if (address < 0xFFC0) { aram_.write(address, value); } else { // Handle IPL ROM or RAM writes here } } } // ========================================================================== // Addressing modes // Immediate uint8_t imm() { PC++; return read(PC); } // Direct page uint8_t dp() { PC++; uint8_t offset = read(PC); return read((PSW.P << 8) + offset); } uint8_t get_dp_addr() { PC++; uint8_t offset = read(PC); return (PSW.P << 8) + offset; } // Direct page indexed by X uint8_t dp_plus_x() { PC++; uint8_t offset = read(PC); return read((PSW.P << 8) + offset + X); } // Direct page indexed by Y uint8_t dp_plus_y() { PC++; uint8_t offset = read(PC); return read((PSW.P << 8) + offset + Y); } // Indexed indirect (add index before 16-bit lookup). uint16_t dp_plus_x_indirect() { PC++; uint8_t offset = read(PC); uint16_t addr = read((PSW.P << 8) + offset + X) | (read((PSW.P << 8) + offset + X + 1) << 8); return addr; } // Indirect indexed (add index after 16-bit lookup). uint16_t dp_indirect_plus_y() { PC++; uint8_t offset = read(PC); uint16_t baseAddr = read((PSW.P << 8) + offset) | (read((PSW.P << 8) + offset + 1) << 8); return baseAddr + Y; } 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& dest, uint8_t operand) { dest = operand; PSW.Z = (operand == 0); PSW.N = (operand & 0x80); } void MOV_ADDR(uint16_t address, uint8_t operand) { write(address, operand); PSW.Z = (operand == 0); PSW.N = (operand & 0x80); } // ADC void ADC(uint8_t& dest, uint8_t operand) { uint16_t result = dest + operand + PSW.C; PSW.V = ((A ^ result) & (operand ^ result) & 0x80); PSW.C = (result > 0xFF); PSW.Z = ((result & 0xFF) == 0); PSW.N = (result & 0x80); PSW.H = ((A ^ operand ^ result) & 0x10); dest = result & 0xFF; } // SBC void SBC(uint8_t& dest, uint8_t operand) { uint16_t result = dest - operand - (1 - PSW.C); PSW.V = ((dest ^ result) & (dest ^ operand) & 0x80); PSW.C = (result < 0x100); PSW.Z = ((result & 0xFF) == 0); PSW.N = (result & 0x80); PSW.H = ((dest ^ operand ^ result) & 0x10); dest = result & 0xFF; } // CMP void CMP(uint8_t& dest, uint8_t operand) { uint16_t result = dest - operand; PSW.C = (result < 0x100); PSW.Z = ((result & 0xFF) == 0); PSW.N = (result & 0x80); } // AND void AND(uint8_t& dest, uint8_t operand) { dest &= operand; PSW.Z = (dest == 0); PSW.N = (dest & 0x80); } // OR void OR(uint8_t& dest, uint8_t operand) { dest |= operand; PSW.Z = (dest == 0); PSW.N = (dest & 0x80); } // EOR void EOR(uint8_t& dest, uint8_t operand) { dest ^= operand; PSW.Z = (dest == 0); PSW.N = (dest & 0x80); } // ASL void ASL(uint8_t operand) { PSW.C = (operand & 0x80); operand <<= 1; PSW.Z = (operand == 0); PSW.N = (operand & 0x80); // A = value; } // LSR void LSR(uint8_t& operand) { PSW.C = (operand & 0x01); operand >>= 1; PSW.Z = (operand == 0); PSW.N = (operand & 0x80); } // ROL void ROL(uint8_t operand, bool isImmediate = false) { uint8_t value = isImmediate ? imm() : operand; uint8_t carry = PSW.C; PSW.C = (value & 0x80); value <<= 1; value |= carry; PSW.Z = (value == 0); PSW.N = (value & 0x80); // operand = value; } // XCN void XCN(uint8_t operand, bool isImmediate = false) { uint8_t value = isImmediate ? imm() : operand; value = ((value & 0xF0) >> 4) | ((value & 0x0F) << 4); PSW.Z = (value == 0); PSW.N = (value & 0x80); // operand = value; } // INC void INC(uint8_t& operand) { operand++; PSW.Z = (operand == 0); PSW.N = (operand & 0x80); } // DEC void DEC(uint8_t& operand) { operand--; PSW.Z = (operand == 0); PSW.N = (operand & 0x80); } // MOVW void MOVW(uint16_t& dest, uint16_t operand) { dest = operand; PSW.Z = (operand == 0); PSW.N = (operand & 0x8000); } // INCW void INCW(uint16_t& operand) { operand++; PSW.Z = (operand == 0); PSW.N = (operand & 0x8000); } // DECW void DECW(uint16_t& operand) { operand--; PSW.Z = (operand == 0); PSW.N = (operand & 0x8000); } // ADDW void ADDW(uint16_t& dest, uint16_t operand) { uint32_t result = dest + operand; PSW.C = (result > 0xFFFF); PSW.Z = ((result & 0xFFFF) == 0); PSW.N = (result & 0x8000); PSW.V = ((dest ^ result) & (operand ^ result) & 0x8000); dest = result & 0xFFFF; } // SUBW void SUBW(uint16_t& dest, uint16_t operand) { uint32_t result = dest - operand; PSW.C = (result < 0x10000); PSW.Z = ((result & 0xFFFF) == 0); PSW.N = (result & 0x8000); PSW.V = ((dest ^ result) & (dest ^ operand) & 0x8000); dest = result & 0xFFFF; } // CMPW void CMPW(uint16_t operand) { uint32_t result = YA - operand; PSW.C = (result < 0x10000); PSW.Z = ((result & 0xFFFF) == 0); PSW.N = (result & 0x8000); } // MUL void MUL(uint8_t operand) { uint16_t result = A * operand; YA = result; PSW.Z = (result == 0); PSW.N = (result & 0x8000); } // DIV void DIV(uint8_t operand) { if (operand == 0) { // Handle divide by zero error return; } uint8_t quotient = A / operand; uint8_t remainder = A % operand; A = quotient; Y = remainder; PSW.Z = (quotient == 0); PSW.N = (quotient & 0x80); } // DAA // BRA void BRA(int8_t offset) { PC += offset; } // BEQ void BEQ(int8_t offset) { if (PSW.Z) { PC += offset; } } // BNE void BNE(int8_t offset) { if (!PSW.Z) { PC += offset; } } // BCS void BCS(int8_t offset) { if (PSW.C) { PC += offset; } } // BCC void BCC(int8_t offset) { if (!PSW.C) { PC += offset; } } // BVS void BVS(int8_t offset) { if (PSW.V) { PC += offset; } } // BVC void BVC(int8_t offset) { if (!PSW.V) { PC += offset; } } // BMI void BMI(int8_t offset) { if (PSW.N) { PC += offset; } } // BPL void BPL(int8_t offset) { if (!PSW.N) { PC += offset; } } // BBS void BBS(uint8_t bit, uint8_t operand) { if (operand & (1 << bit)) { PC += rel(); } } // BBC void BBC(uint8_t bit, uint8_t operand) { if (!(operand & (1 << bit))) { PC += rel(); } } // CBNE DBNZ // JMP void JMP(uint16_t address) { PC = address; } // 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