Overhaul Spc700 implementation, add apu callbacks

This commit is contained in:
scawful
2024-04-22 15:55:37 -04:00
parent 3eb245f985
commit ca1d0e9131
4 changed files with 1618 additions and 960 deletions

View File

@@ -5,12 +5,90 @@ namespace app {
namespace emu {
namespace audio {
// Immediate
uint8_t Spc700::imm() {
PC++;
return read(PC);
// adressing modes
// uint16_t adrDp() {
// return ReadOpcode() | (PSW.P << 8);
// }
uint16_t Spc700::ind() {
read(PC);
return X | (PSW.P << 8);
}
uint16_t Spc700::idx() {
uint8_t pointer = ReadOpcode();
callbacks_.idle(false);
return read_word(((pointer + X) & 0xff) | (PSW.P << 8));
}
uint16_t Spc700::dpx() {
uint16_t res = ((ReadOpcode() + X) & 0xff) | (PSW.P << 8);
callbacks_.idle(false);
return res;
}
uint16_t Spc700::dp_y() {
uint16_t res = ((ReadOpcode() + Y) & 0xff) | (PSW.P << 8);
callbacks_.idle(false);
return res;
}
uint16_t Spc700::abs_x() {
uint16_t res = (ReadOpcodeWord() + X) & 0xffff;
callbacks_.idle(false);
return res;
}
uint16_t Spc700::abs_y() {
uint16_t res = (ReadOpcodeWord() + Y) & 0xffff;
callbacks_.idle(false);
return res;
}
uint16_t Spc700::idy() {
uint8_t pointer = ReadOpcode();
uint16_t adr = read_word(pointer | (PSW.P << 8));
callbacks_.idle(false);
return (adr + Y) & 0xffff;
}
// uint16_t adrDpDp(uint8_t* srcVal) {
// *srcVal = read(spc, ReadOpcode() | (PSW.P << 8));
// return ReadOpcode() | (PSW.P << 8);
// }
uint16_t Spc700::dp_imm(uint8_t* srcVal) {
*srcVal = ReadOpcode();
return ReadOpcode() | (PSW.P << 8);
}
uint16_t Spc700::ind_ind(uint8_t* srcVal) {
read(PC);
*srcVal = read(Y | (PSW.P << 8));
return X | (PSW.P << 8);
}
uint8_t Spc700::abs_bit(uint16_t* adr) {
uint16_t adrBit = ReadOpcodeWord();
*adr = adrBit & 0x1fff;
return adrBit >> 13;
}
uint16_t Spc700::dp_word(uint16_t* low) {
uint8_t adr = ReadOpcode();
*low = adr | (PSW.P << 8);
return ((adr + 1) & 0xff) | (PSW.P << 8);
}
uint16_t Spc700::ind_p() {
read(PC);
return X++ | (PSW.P << 8);
}
// Immediate
uint16_t Spc700::imm() { return PC++; }
// Direct page
uint8_t Spc700::dp() {
PC++;
@@ -18,12 +96,6 @@ uint8_t Spc700::dp() {
return read((PSW.P << 8) + offset);
}
uint8_t& Spc700::mutable_dp() {
PC++;
uint8_t offset = read(PC);
return mutable_read((PSW.P << 8) + offset);
}
uint8_t Spc700::get_dp_addr() {
PC++;
uint8_t offset = read(PC);
@@ -47,23 +119,24 @@ uint8_t Spc700::dp_plus_y() {
// Indexed indirect (add index before 16-bit lookup).
uint16_t Spc700::dp_plus_x_indirect() {
PC++;
uint16_t addr = read_16(PC + X);
uint16_t addr = read_word(PC + X);
return addr;
}
// Indirect indexed (add index after 16-bit lookup).
uint16_t Spc700::dp_indirect_plus_y() {
PC++;
uint16_t offset = read_16(PC);
uint16_t offset = read_word(PC);
return offset + Y;
}
uint16_t Spc700::abs() {
PC++;
uint16_t addr = read(PC) | (read(PC) << 8);
return addr;
uint16_t Spc700::dp_dp(uint8_t* src) {
*src = read(ReadOpcode() | (PSW.P << 8));
return ReadOpcode() | (PSW.P << 8);
}
uint16_t Spc700::abs() { return ReadOpcodeWord(); }
int8_t Spc700::rel() {
PC++;
return static_cast<int8_t>(read(PC));

View File

@@ -5,10 +5,39 @@ namespace app {
namespace emu {
namespace audio {
void Spc700::MOV(uint8_t& dest, uint8_t operand) {
dest = operand;
PSW.Z = (operand == 0);
PSW.N = (operand & 0x80);
// opcode functions
void Spc700::MOVX(uint16_t adr) {
X = read(adr);
PSW.Z = (X == 0);
PSW.N = (X & 0x80);
}
void Spc700::MOVY(uint16_t adr) {
Y = read(adr);
PSW.Z = (Y == 0);
PSW.N = (Y & 0x80);
}
void Spc700::MOVS(uint16_t adr) {
read(adr);
write(adr, A);
}
void Spc700::MOVSX(uint16_t adr) {
read(adr);
write(adr, X);
}
void Spc700::MOVSY(uint16_t adr) {
read(adr);
write(adr, Y);
}
void Spc700::MOV(uint16_t adr) {
A = read(adr);
PSW.Z = (A == 0);
PSW.N = (A & 0x80);
}
void Spc700::MOV_ADDR(uint16_t address, uint8_t operand) {
@@ -17,75 +46,198 @@ void Spc700::MOV_ADDR(uint16_t address, uint8_t operand) {
PSW.N = (operand & 0x80);
}
void Spc700::ADC(uint8_t& dest, uint8_t operand) {
uint16_t result = dest + operand + PSW.C;
PSW.V = ((A ^ result) & (operand ^ result) & 0x80);
void Spc700::ADC(uint16_t adr) {
uint8_t value = read(adr);
uint16_t result = A + value + PSW.C;
PSW.V = ((A ^ result) & (adr ^ result) & 0x80);
PSW.C = (result > 0xFF);
PSW.H = ((A ^ adr ^ result) & 0x10);
A = result & 0xFF;
PSW.Z = ((A & 0xFF) == 0);
PSW.N = (A & 0x80);
}
// void spc_adcm(uint16_t dst, uint8_t value) {
// uint8_t applyOn = read(dst);
// int result = applyOn + value + PSW.C;
// spc->v =
// (applyOn & 0x80) == (value & 0x80) && (value & 0x80) != (result &
// 0x80);
// PSW.H = ((applyOn & 0xf) + (value & 0xf) + PSW.C) > 0xf;
// PSW.C = result > 0xff;
// spc_write(dst, result);
// spc_setZN(result);
// }
void Spc700::ADCM(uint16_t& dest, uint8_t operand) {
uint8_t applyOn = read(dest);
int result = applyOn + operand + PSW.C;
PSW.V = (applyOn & 0x80) == (operand & 0x80) &&
(operand & 0x80) != (result & 0x80);
PSW.H = ((applyOn & 0xf) + (operand & 0xf) + PSW.C) > 0xf;
PSW.C = result > 0xff;
write(dest, result);
PSW.Z = ((result & 0xFF) == 0);
PSW.N = (result & 0x80);
PSW.H = ((A ^ operand ^ result) & 0x10);
dest = result & 0xFF;
// dest = result & 0xFF;
}
void Spc700::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;
// void spc_sbcm(uint16_t dst, uint8_t value) {
// value ^= 0xff;
// uint8_t applyOn = read(dst);
// int result = applyOn + value + PSW.C;
// spc->v = (applyOn & 0x80) == (value & 0x80) && (value & 0x80) != (result &
// 0x80); PSW.H = ((applyOn & 0xf) + (value & 0xf) + PSW.C) > 0xf; PSW.C =
// result > 0xff; spc_write(dst, result); spc_setZN(result);
// }
void Spc700::SBC(uint16_t adr) {
uint8_t value = read(adr) ^ 0xff;
int result = A + value + PSW.C;
PSW.V = (A & 0x80) == (value & 0x80) && (value & 0x80) != (result & 0x80);
PSW.H = ((A & 0xf) + (value & 0xf) + PSW.C) > 0xf;
PSW.C = result > 0xff;
A = result;
PSW.Z = ((A & 0xFF) == 0);
PSW.N = (A & 0x80);
}
void Spc700::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);
}
void Spc700::AND(uint8_t& dest, uint8_t operand) {
dest &= operand;
PSW.Z = (dest == 0);
PSW.N = (dest & 0x80);
}
void Spc700::OR(uint8_t& dest, uint8_t operand) {
dest |= operand;
PSW.Z = (dest == 0);
PSW.N = (dest & 0x80);
}
void Spc700::EOR(uint8_t& dest, uint8_t operand) {
dest ^= operand;
PSW.Z = (dest == 0);
PSW.N = (dest & 0x80);
}
void Spc700::ASL(uint8_t operand) {
PSW.C = (operand & 0x80);
operand <<= 1;
PSW.Z = (operand == 0);
PSW.N = (operand & 0x80);
// A = value;
}
void Spc700::LSR(uint8_t& operand) {
PSW.C = (operand & 0x01);
operand >>= 1;
PSW.Z = (operand == 0);
PSW.N = (operand & 0x80);
}
void Spc700::ROL(uint8_t operand, bool isImmediate) {
uint8_t value = isImmediate ? imm() : operand;
uint8_t carry = PSW.C;
PSW.C = (value & 0x80);
value <<= 1;
value |= carry;
PSW.Z = (value == 0);
void Spc700::SBCM(uint16_t& dest, uint8_t operand) {
uint8_t value = read(dest) - operand - (1 - PSW.C);
PSW.V = ((read(dest) ^ value) & (read(dest) ^ operand) & 0x80);
PSW.C = (value < 0x100);
PSW.Z = ((value & 0xFF) == 0);
PSW.N = (value & 0x80);
// operand = value;
PSW.H = ((read(dest) ^ operand ^ value) & 0x10);
write(dest, value);
}
void Spc700::CMPX(uint16_t adr) {
uint8_t value = read(adr) ^ 0xff;
int result = X + value + 1;
PSW.C = result > 0xff;
PSW.Z = (result == 0);
PSW.N = (result & 0x80);
}
void Spc700::CMPY(uint16_t adr) {
uint8_t value = read(adr) ^ 0xff;
int result = Y + value + 1;
PSW.C = result > 0xff;
PSW.Z = (result == 0);
PSW.N = (result & 0x80);
}
void Spc700::CMPM(uint16_t dst, uint8_t value) {
value ^= 0xff;
int result = read(dst) + value + 1;
PSW.C = result > 0xff;
callbacks_.idle(false);
PSW.Z = (result == 0);
PSW.N = (result & 0x80);
}
void Spc700::CMP(uint16_t adr) {
uint8_t value = read(adr) ^ 0xff;
int result = A + value + 1;
PSW.C = result > 0xff;
PSW.Z = ((result & 0xFF) == 0);
PSW.N = (result & 0x80);
}
// void spc_andm(uint16_t dst, uint8_t value) {
// uint8_t result = read(dst) & value;
// spc_write(dst, result);
// spc_setZN(result);
// }
void Spc700::AND(uint16_t adr) {
A &= read(adr);
PSW.Z = (A == 0);
PSW.N = (A & 0x80);
}
void Spc700::ANDM(uint16_t dest, uint8_t operand) {
uint8_t result = read(dest) & operand;
write(dest, result);
PSW.Z = (result == 0);
PSW.N = (result & 0x80);
}
// void spc_or(uint16_t adr) {
// A |= read(adr);
// spc_setZN(A);
// }
// void spc_orm(uint16_t dst, uint8_t value) {
// uint8_t result = read(dst) | value;
// spc_write(dst, result);
// spc_setZN(result);
// }
void Spc700::OR(uint16_t adr) {
A |= read(adr);
PSW.Z = (A == 0);
PSW.N = (A & 0x80);
}
void Spc700::ORM(uint16_t dst, uint8_t value) {
uint8_t result = read(dst) | value;
write(dst, result);
PSW.Z = (result == 0);
PSW.N = (result & 0x80);
}
void Spc700::EOR(uint16_t adr) {
A ^= read(adr);
PSW.Z = (A == 0);
PSW.N = (A & 0x80);
}
void Spc700::EORM(uint16_t dest, uint8_t operand) {
uint8_t result = read(dest) ^ operand;
write(dest, result);
PSW.Z = (result == 0);
PSW.N = (result & 0x80);
}
void Spc700::ASL(uint16_t operand) {
uint8_t val = read(operand);
write(operand, val);
PSW.C = (val & 0x80);
val <<= 1;
PSW.Z = (val == 0);
PSW.N = (val & 0x80);
}
void Spc700::LSR(uint16_t adr) {
uint8_t val = read(adr);
PSW.C = (val & 0x01);
val >>= 1;
write(adr, val);
PSW.Z = (val == 0);
PSW.N = (val & 0x80);
}
void Spc700::ROR(uint16_t adr) {
uint8_t val = read(adr);
bool newC = val & 1;
val = (val >> 1) | (PSW.C << 7);
PSW.C = newC;
write(adr, val);
PSW.Z = (val == 0);
PSW.N = (val & 0x80);
}
void Spc700::ROL(uint16_t adr) {
uint8_t val = read(adr);
bool newC = val & 0x80;
val = (val << 1) | PSW.C;
PSW.C = newC;
write(adr, val);
PSW.Z = (val == 0);
PSW.N = (val & 0x80);
}
void Spc700::XCN(uint8_t operand, bool isImmediate) {
@@ -96,14 +248,16 @@ void Spc700::XCN(uint8_t operand, bool isImmediate) {
// operand = value;
}
void Spc700::INC(uint8_t& operand) {
operand++;
PSW.Z = (operand == 0);
PSW.N = (operand & 0x80);
void Spc700::INC(uint16_t adr) {
uint8_t val = read(adr) + 1;
write(adr, val);
PSW.Z = (val == 0);
PSW.N = (val & 0x80);
}
void Spc700::DEC(uint8_t& operand) {
operand--;
void Spc700::DEC(uint16_t operand) {
uint8_t val = read(operand) - 1;
write(operand, val);
PSW.Z = (operand == 0);
PSW.N = (operand & 0x80);
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,7 @@
#define YAZE_APP_EMU_SPC700_H
#include <cstdint>
#include <functional>
#include <iostream>
#include <unordered_map>
#include <vector>
@@ -21,6 +22,7 @@ class AudioRam {
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); }
};
/**
@@ -45,8 +47,17 @@ class AudioRamImpl : public AudioRam {
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.
@@ -61,8 +72,12 @@ class AudioRamImpl : public AudioRam {
class Spc700 {
private:
AudioRam& aram_;
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,
@@ -72,7 +87,8 @@ class Spc700 {
0xDD, 0x5D, 0xD0, 0xDB, 0x1F, 0x00, 0x00, 0xC0, 0xFF};
public:
explicit Spc700(AudioRam& aram) : aram_(aram) {}
explicit Spc700(AudioRam& aram, ApuCallbacks& callbacks)
: aram_(aram), callbacks_(callbacks) {}
// Registers
uint8_t A = 0x00; // 8-bit accumulator
@@ -112,80 +128,99 @@ class Spc700 {
return flags;
}
void Reset();
void Reset(bool hard = false);
void BootIplRom();
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) {
if (address < 0xFFC0) {
return aram_.read(address);
} else {
// Check if register is set to unmap the IPL ROM
if (read(0xF1) & 0x80) {
return aram_.read(address);
}
return ipl_rom_[address - 0xFFC0];
}
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& mutable_read(uint16_t address) {
if (address < 0xFFC0) {
return aram_.mutable_read(address);
} else {
// NOTE: Mutable access to IPL ROM is not allowed
return aram_.mutable_read(address);
}
uint8_t ReadOpcode() {
uint8_t opcode = read(PC);
PC++;
return opcode;
}
uint16_t& mutable_read_16(uint16_t address) {
if (address < 0xFFC0) {
return *reinterpret_cast<uint16_t*>(&aram_.mutable_read(address));
} else {
// NOTE: Mutable access to IPL ROM is not allowed
return *reinterpret_cast<uint16_t*>(&aram_.mutable_read(address));
}
uint16_t ReadOpcodeWord() {
uint16_t opcode = read_word(PC);
return opcode;
}
uint16_t read_16(uint16_t address) {
if (address < 0xFFC0) {
return (aram_.read(address) | (aram_.read(address + 1) << 8));
} else {
// Check if register is set to unmap the IPL ROM
if (read(0xF1) & 0x80) {
return aram_.read(address);
}
return ipl_rom_[address - 0xFFC0];
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) {
if (address < 0xFFC0) {
aram_.write(address, value);
} else {
// Check if register is set to unmap the IPL ROM
if (read(0xF1) & 0x80) {
aram_.write(address, 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
uint8_t imm();
uint16_t imm();
// Direct page
uint8_t dp();
uint8_t& mutable_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();
@@ -197,11 +232,6 @@ class Spc700 {
// Indirect indexed (add index after 16-bit lookup).
uint16_t dp_indirect_plus_y();
uint16_t abs();
int8_t rel();
uint8_t i();
uint8_t i_postinc();
@@ -213,20 +243,43 @@ class Spc700 {
// ==========================================================================
// Instructions
void MOV(uint8_t& dest, uint8_t operand);
void MOV(uint16_t adr);
void MOV_ADDR(uint16_t address, uint8_t operand);
void ADC(uint8_t& dest, uint8_t operand);
void SBC(uint8_t& dest, uint8_t operand);
void CMP(uint8_t& dest, uint8_t operand);
void AND(uint8_t& dest, uint8_t operand);
void OR(uint8_t& dest, uint8_t operand);
void EOR(uint8_t& dest, uint8_t operand);
void ASL(uint8_t operand);
void LSR(uint8_t& operand);
void ROL(uint8_t operand, bool isImmediate = false);
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(uint8_t& operand);
void DEC(uint8_t& operand);
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);