Files
yaze/src/app/emu/audio/spc700.h

335 lines
8.4 KiB
C++

#ifndef YAZE_APP_EMU_SPC700_H
#define YAZE_APP_EMU_SPC700_H
#include <cstdint>
#include <functional>
#include <iostream>
#include <unordered_map>
#include <vector>
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<uint8_t> ram = std::vector<uint8_t>(ARAM_SIZE, 0);
public:
AudioRamImpl() = default;
void reset() override { ram = std::vector<uint8_t>(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<void(uint16_t, uint8_t)> write;
std::function<uint8_t(uint16_t)> read;
std::function<void(bool)> 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<std::string> 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