Files
yaze/src/app/emu/audio/spc700.h
2023-08-26 02:33:19 -04:00

482 lines
10 KiB
C++

#ifndef YAZE_APP_EMU_SPC700_H
#define YAZE_APP_EMU_SPC700_H
#include <cstdint>
#include <iostream>
#include <vector>
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<uint8_t> ram = std::vector<uint8_t>(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<int8_t>(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