#ifndef YAZE_APP_EMU_SPC700_H #define YAZE_APP_EMU_SPC700_H #include #include #include #include #include namespace yaze { namespace app { namespace emu { namespace audio { /** * @brief AudioRam is an interface for the Audio RAM used by the SPC700. */ class AudioRam { public: virtual ~AudioRam() = default; virtual void reset() = 0; virtual uint8_t read(uint16_t address) const = 0; virtual uint8_t& mutable_read(uint16_t address) = 0; virtual void write(uint16_t address, uint8_t value) = 0; uint8_t operator[](uint16_t address) { return mutable_read(address); } }; /** * @brief AudioRamImpl is an implementation of the AudioRam interface. */ class AudioRamImpl : public AudioRam { static const int ARAM_SIZE = 0x10000; std::vector ram = std::vector(ARAM_SIZE, 0); public: AudioRamImpl() = default; void reset() override { ram = std::vector(ARAM_SIZE, 0); } uint8_t read(uint16_t address) const override { return ram[address % ARAM_SIZE]; } uint8_t& mutable_read(uint16_t address) override { return ram.at(address % ARAM_SIZE); } void write(uint16_t address, uint8_t value) override { ram[address % ARAM_SIZE] = value; } // add [] operators uint8_t operator[](uint16_t address) const { return read(address); } }; typedef struct ApuCallbacks { std::function write; std::function read; std::function idle; } ApuCallbacks; /** * @class Spc700 * @brief The Spc700 class represents the SPC700 processor. * * The Spc700 class provides the functionality to execute instructions, read and * write memory, and handle various addressing modes. It also contains registers * and flags specific to the SPC700. * * @note This class assumes the existence of an `AudioRam` object for memory * access. */ class Spc700 { private: ApuCallbacks callbacks_; std::vector log_; bool stopped_; bool reset_wanted_; const uint8_t ipl_rom_[64]{ 0xCD, 0xEF, 0xBD, 0xE8, 0x00, 0xC6, 0x1D, 0xD0, 0xFC, 0x8F, 0xAA, 0xF4, 0x8F, 0xBB, 0xF5, 0x78, 0xCC, 0xF4, 0xD0, 0xFB, 0x2F, 0x19, 0xEB, 0xF4, 0xD0, 0xFC, 0x7E, 0xF4, 0xD0, 0x0B, 0xE4, 0xF5, 0xCB, 0xF4, 0xD7, 0x00, 0xFC, 0xD0, 0xF3, 0xAB, 0x01, 0x10, 0xEF, 0x7E, 0xF4, 0x10, 0xEB, 0xBA, 0xF6, 0xDA, 0x00, 0xBA, 0xF4, 0xC4, 0xF4, 0xDD, 0x5D, 0xD0, 0xDB, 0x1F, 0x00, 0x00, 0xC0, 0xFF}; public: explicit Spc700(ApuCallbacks& callbacks) : callbacks_(callbacks) {} // Registers uint8_t A = 0x00; // 8-bit accumulator uint8_t X = 0x00; // 8-bit index uint8_t Y = 0x00; // 8-bit index uint16_t YA = 0x00; // 16-bit pair of A (lsb) and Y (msb) uint16_t PC = 0xFFC0; // program counter uint8_t SP = 0x00; // 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 uint8_t FlagsToByte(Flags flags) { return (flags.N << 7) | (flags.V << 6) | (flags.P << 5) | (flags.B << 4) | (flags.H << 3) | (flags.I << 2) | (flags.Z << 1) | (flags.C); } Flags ByteToFlags(uint8_t byte) { Flags flags; flags.N = (byte & 0x80) >> 7; flags.V = (byte & 0x40) >> 6; flags.P = (byte & 0x20) >> 5; flags.B = (byte & 0x10) >> 4; flags.H = (byte & 0x08) >> 3; flags.I = (byte & 0x04) >> 2; flags.Z = (byte & 0x02) >> 1; flags.C = (byte & 0x01); return flags; } void Reset(bool hard = false); void RunOpcode(); void ExecuteInstructions(uint8_t opcode); void LogInstruction(uint16_t initial_pc, uint8_t opcode); // Read a byte from the memory-mapped registers uint8_t read(uint16_t address) { return callbacks_.read(address); } uint16_t read_word(uint16_t address) { uint8_t adrl = address; uint8_t adrh = address + 1; uint8_t value = read(adrl); return value | (read(adrh) << 8); } uint8_t ReadOpcode() { uint8_t opcode = read(PC++); return opcode; } uint16_t ReadOpcodeWord() { uint8_t low = ReadOpcode(); uint8_t high = ReadOpcode(); return low | (high << 8); } void DoBranch(uint8_t value, bool check) { if (check) { // taken branch: 2 extra cycles callbacks_.idle(false); callbacks_.idle(false); PC += (int8_t)value; } } // Write a byte to the memory-mapped registers void write(uint16_t address, uint8_t value) { callbacks_.write(address, value); } void push_byte(uint8_t value) { write(0x100 | SP, value); SP--; } void push_word(uint16_t value) { push_byte(value >> 8); push_byte(value & 0xFF); } uint8_t pull_byte() { SP++; return read(0x100 | SP); } uint16_t pull_word() { uint16_t value = pull_byte(); value |= pull_byte() << 8; return value; } // ====================================================== // Addressing modes // Immediate uint16_t imm(); // Direct page uint8_t dp(); uint16_t dpx(); uint8_t get_dp_addr(); uint8_t abs_bit(uint16_t* adr); uint16_t dp_dp(uint8_t* src); uint16_t ind(); uint16_t ind_ind(uint8_t* srcVal); uint16_t dp_word(uint16_t* low); uint16_t ind_p(); uint16_t abs_x(); uint16_t abs_y(); uint16_t idx(); uint16_t idy(); uint16_t dp_y(); uint16_t dp_imm(uint8_t* srcVal); uint16_t abs(); int8_t rel(); // Direct page indexed by X uint8_t dp_plus_x(); // Direct page indexed by Y uint8_t dp_plus_y(); // Indexed indirect (add index before 16-bit lookup). uint16_t dp_plus_x_indirect(); // Indirect indexed (add index after 16-bit lookup). uint16_t dp_indirect_plus_y(); uint8_t i(); uint8_t i_postinc(); uint16_t addr_plus_i(); uint16_t addr_plus_i_indexed(); // ========================================================================== // Instructions void MOV(uint16_t adr); void MOV_ADDR(uint16_t address, uint8_t operand); void MOVY(uint16_t adr); void MOVX(uint16_t adr); void MOVS(uint16_t adr); void MOVSX(uint16_t adr); void MOVSY(uint16_t adr); void ADC(uint16_t adr); void ADCM(uint16_t& dest, uint8_t operand); void SBC(uint16_t adr); void SBCM(uint16_t& dest, uint8_t operand); void CMP(uint16_t adr); void CMPX(uint16_t adr); void CMPM(uint16_t dst, uint8_t value); void CMPY(uint16_t adr); void AND(uint16_t adr); void ANDM(uint16_t dest, uint8_t operand); void OR(uint16_t adr); void ORM(uint16_t dest, uint8_t operand); void EOR(uint16_t adr); void EORM(uint16_t dest, uint8_t operand); void ASL(uint16_t operand); void LSR(uint16_t adr); void ROL(uint16_t operand); void ROR(uint16_t adr); void XCN(uint8_t operand, bool isImmediate = false); void INC(uint16_t adr); void DEC(uint16_t operand); void MOVW(uint16_t& dest, uint16_t operand); void INCW(uint16_t& operand); void DECW(uint16_t& operand); void ADDW(uint16_t& dest, uint16_t operand); void SUBW(uint16_t& dest, uint16_t operand); void CMPW(uint16_t operand); void MUL(uint8_t operand); void DIV(uint8_t operand); void BRA(int8_t offset); void BEQ(int8_t offset); void BNE(int8_t offset); void BCS(int8_t offset); void BCC(int8_t offset); void BVS(int8_t offset); void BVC(int8_t offset); void BMI(int8_t offset); void BPL(int8_t offset); void BBS(uint8_t bit, uint8_t operand); void BBC(uint8_t bit, uint8_t operand); void JMP(uint16_t address); void CALL(uint16_t address); void PCALL(uint8_t offset); void TCALL(uint8_t offset); void BRK(); void RET(); void RETI(); void PUSH(uint8_t operand); void POP(uint8_t& operand); void SET1(uint8_t bit, uint8_t& operand); void CLR1(uint8_t bit, uint8_t& operand); void TSET1(uint8_t bit, uint8_t& operand); void TCLR1(uint8_t bit, uint8_t& operand); void AND1(uint8_t bit, uint8_t& operand); void OR1(uint8_t bit, uint8_t& operand); void EOR1(uint8_t bit, uint8_t& operand); void NOT1(uint8_t bit, uint8_t& operand); void MOV1(uint8_t bit, uint8_t& operand); void CLRC(); void SETC(); void NOTC(); void CLRV(); void CLRP(); void SETP(); void EI(); void DI(); void NOP(); void SLEEP(); void STOP(); // CBNE DBNZ }; } // namespace audio } // namespace emu } // namespace app } // namespace yaze #endif // YAZE_APP_EMU_SPC700_H