Add addressing modes and opcodes for SPC700

This commit is contained in:
scawful
2023-08-25 17:44:04 -04:00
parent 25c43cbaaa
commit af73af4300
2 changed files with 356 additions and 59 deletions

View File

@@ -11,41 +11,59 @@ void SPC700::ExecuteInstructions(uint8_t opcode) {
switch (opcode) {
// 8-bit Move Memory to Register
case 0xE8: // MOV A, #imm
MOV(A, true);
MOV(A, imm());
break;
case 0xE6: // MOV A, (X)
MOV(A, X);
break;
case 0xBF: // MOV A, (X)+
MOV(A, X);
X++;
break;
case 0xE4: // MOV A, dp
MOV(A, dp());
break;
case 0xF4: // MOV A, dp+X
MOV(A, dp_plus_x());
break;
case 0xE5: // MOV A, !abs
MOV(A, abs());
break;
case 0xF5: // MOV A, !abs+X
MOV(A, abs() + X);
break;
case 0xF6: // MOV A, !abs+Y
MOV(A, abs() + Y);
break;
case 0xE7: // MOV A, [dp+X]
MOV(A, read(dp_plus_x_indirect()));
break;
case 0xF7: // MOV A, [dp]+Y
MOV(A, read(dp_indirect_plus_y()));
break;
case 0xCD: // MOV X, #imm
MOV(X, imm());
break;
case 0xF8: // MOV X, dp
MOV(X, dp());
break;
case 0xF9: // MOV X, dp+Y
MOV(X, dp_plus_y());
break;
case 0xE9: // MOV X, !abs
MOV(X, abs());
break;
case 0x8D: // MOV Y, #imm
MOV(Y, imm());
break;
case 0xEB: // MOV Y, dp
MOV(Y, dp());
break;
case 0xFB: // MOV Y, dp+X
MOV(Y, dp_plus_x());
break;
case 0xEC: // MOV Y, !abs
MOV(Y, abs());
break;
// 8-bit move register to memory
@@ -55,30 +73,43 @@ void SPC700::ExecuteInstructions(uint8_t opcode) {
case 0xAF: // MOV (X)+, A
break;
case 0xC4: // MOV dp, A
MOV_ADDR(get_dp_addr(), A);
break;
case 0xD4: // MOV dp+X, A
MOV_ADDR(get_dp_addr() + X, A);
break;
case 0xC5: // MOV !abs, A
MOV_ADDR(abs(), A);
break;
case 0xD5: // MOV !abs+X, A
MOV_ADDR(abs() + X, A);
break;
case 0xD6: // MOV !abs+Y, A
MOV_ADDR(abs() + Y, A);
break;
case 0xC7: // MOV [dp+X], A
MOV_ADDR(dp_plus_x_indirect(), A);
break;
case 0xD7: // MOV [dp]+Y, A
MOV_ADDR(dp_indirect_plus_y(), A);
break;
case 0xD8: // MOV dp, X
MOV_ADDR(get_dp_addr(), X);
break;
case 0xD9: // MOV dp+Y, X
MOV_ADDR(get_dp_addr() + Y, X);
break;
case 0xC9: // MOV !abs, X
MOV_ADDR(abs(), X);
break;
case 0xCB: // MOV dp, Y
MOV_ADDR(get_dp_addr(), Y);
break;
case 0xDB: // MOV dp+X, Y
MOV_ADDR(get_dp_addr() + X, Y);
break;
case 0xCC: // MOV !abs, Y
MOV_ADDR(abs(), Y);
break;
// . 8-bit move register to register / special direct page moves
@@ -103,22 +134,28 @@ void SPC700::ExecuteInstructions(uint8_t opcode) {
// . 8-bit arithmetic
case 0x88: // ADC A, #imm
ADC(A, imm());
break;
case 0x86: // ADC A, (X)
break;
case 0x84: // ADC A, dp
ADC(A, dp());
break;
case 0x94: // ADC A, dp+X
ADC(A, dp_plus_x());
break;
case 0x85: // ADC A, !abs
ADC(A, abs());
break;
case 0x95: // ADC A, !abs+X
break;
case 0x96: // ADC A, !abs+Y
break;
case 0x87: // ADC A, [dp+X]
ADC(A, dp_plus_x_indirect());
break;
case 0x97: // ADC A, [dp]+Y
ADC(A, dp_indirect_plus_y());
break;
case 0x99: // ADC (X), (Y)
break;
@@ -127,6 +164,7 @@ void SPC700::ExecuteInstructions(uint8_t opcode) {
case 0x98: // ADC dp, #imm
break;
case 0xA8: // SBC A, #imm
SBC(A, imm());
break;
case 0xA6: // SBC A, (X)
break;
@@ -214,14 +252,18 @@ void SPC700::ExecuteInstructions(uint8_t opcode) {
case 0x38: // AND dp, #imm
break;
case 0x08: // OR A, #imm
OR(A, imm());
break;
case 0x06: // OR A, (X)
break;
case 0x04: // OR A, dp
OR(A, dp());
break;
case 0x14: // OR A, dp+X
OR(A, dp_plus_x());
break;
case 0x05: // OR A, !abs
OR(A, abs());
break;
case 0x15: // OR A, !abs+X
break;
@@ -238,10 +280,12 @@ void SPC700::ExecuteInstructions(uint8_t opcode) {
case 0x18: // OR dp, #imm
break;
case 0x48: // EOR A, #imm
EOR(A, imm());
break;
case 0x46: // EOR A, (X)
break;
case 0x44: // EOR A, dp
EOR(A, dp());
break;
case 0x54: // EOR A, dp+X
break;
@@ -265,6 +309,7 @@ void SPC700::ExecuteInstructions(uint8_t opcode) {
// . 8-bit increment / decrement
case 0xBC: // INC A
INC(A);
break;
case 0xAB: // INC dp
break;
@@ -273,10 +318,13 @@ void SPC700::ExecuteInstructions(uint8_t opcode) {
case 0xAC: // INC !abs
break;
case 0x3D: // INC X
INC(X);
break;
case 0xFC: // INC Y
INC(Y);
break;
case 0x9C: // DEC A
DEC(A);
break;
case 0x8B: // DEC dp
break;
@@ -285,21 +333,28 @@ void SPC700::ExecuteInstructions(uint8_t opcode) {
case 0x8C: // DEC !abs
break;
case 0x1D: // DEC X
DEC(X);
break;
case 0xDC: // DEC Y
DEC(Y);
break;
// 8-bit shift / rotation
case 0x1C: // ASL A
ASL(A);
break;
case 0x0B: // ASL dp
ASL(dp());
break;
case 0x1B: // ASL dp+X
ASL(dp_plus_x());
break;
case 0x0C: // ASL !abs
ASL(abs());
break;
case 0x5C: // LSR A
LSR(A);
break;
case 0x4B: // LSR dp
break;
@@ -324,6 +379,7 @@ void SPC700::ExecuteInstructions(uint8_t opcode) {
case 0x6C: // ROR !abs
break;
case 0x9F: // XCN A Exchange nibbles of A
break;
// . 16-bit operations

View File

@@ -22,10 +22,10 @@ class VirtualAudioRAM {
class AudioRAM : public VirtualAudioRAM {
static const size_t ARAM_SIZE = 64 * 1024; // 64 KB
std::vector<uint8_t> ram;
std::vector<uint8_t> ram = std::vector<uint8_t>(ARAM_SIZE, 0);
public:
AudioRAM() : ram(ARAM_SIZE, 0) {}
AudioRAM() = default;
// Read a byte from ARAM at the given address
uint8_t read(uint16_t address) const override {
@@ -38,39 +38,40 @@ class AudioRAM : public VirtualAudioRAM {
}
};
class SDSP {
// Digital Signal Processor
class DigitalSignalProcessor {
private:
static const size_t NUM_VOICES = 8;
static const size_t NUM_VOICE_REGS = 10;
static const size_t NUM_GLOBAL_REGS = 15;
// Each voice has 10 registers
std::vector<std::vector<uint8_t>> voices;
std::vector<std::vector<uint8_t>> voices = std::vector<std::vector<uint8_t>>(
NUM_VOICES, std::vector<uint8_t>(NUM_VOICE_REGS, 0));
// Global registers
std::vector<uint8_t> globalRegs;
std::vector<uint8_t> globalRegs = std::vector<uint8_t>(NUM_GLOBAL_REGS, 0x00);
public:
SDSP()
: voices(NUM_VOICES, std::vector<uint8_t>(NUM_VOICE_REGS, 0)),
globalRegs(NUM_GLOBAL_REGS, 0) {}
DigitalSignalProcessor() = default;
// Read a byte from a voice register
uint8_t readVoiceReg(uint8_t voice, uint8_t reg) const {
uint8_t ReadVoiceReg(uint8_t voice, uint8_t reg) const {
return voices[voice % NUM_VOICES][reg % NUM_VOICE_REGS];
}
// Write a byte to a voice register
void writeVoiceReg(uint8_t voice, uint8_t reg, uint8_t value) {
void WriteVoiceReg(uint8_t voice, uint8_t reg, uint8_t value) {
voices[voice % NUM_VOICES][reg % NUM_VOICE_REGS] = value;
}
// Read a byte from a global register
uint8_t readGlobalReg(uint8_t reg) const {
uint8_t ReadGlobalReg(uint8_t reg) const {
return globalRegs[reg % NUM_GLOBAL_REGS];
}
// Write a byte to a global register
void writeGlobalReg(uint8_t reg, uint8_t value) {
void WriteGlobalReg(uint8_t reg, uint8_t value) {
globalRegs[reg % NUM_GLOBAL_REGS] = value;
}
};
@@ -81,7 +82,7 @@ class SPC700 {
public:
explicit SPC700(VirtualAudioRAM& aram) : aram_(aram) {}
SDSP sdsp;
DigitalSignalProcessor sdsp;
uint8_t test_register_;
uint8_t control_register_;
uint8_t dsp_address_register_;
@@ -118,7 +119,7 @@ class SPC700 {
case 0xF2:
return dsp_address_register_;
case 0xF3:
return sdsp.readGlobalReg(dsp_address_register_);
return sdsp.ReadGlobalReg(dsp_address_register_);
default:
if (address < 0xFFC0) {
return aram_.read(address);
@@ -142,7 +143,7 @@ class SPC700 {
dsp_address_register_ = value;
break;
case 0xF3:
sdsp.writeGlobalReg(dsp_address_register_, value);
sdsp.WriteGlobalReg(dsp_address_register_, value);
break;
default:
if (address < 0xFFC0) {
@@ -153,18 +154,60 @@ class SPC700 {
}
}
// ==========================================================================
// 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);
@@ -197,82 +240,280 @@ class SPC700 {
return read(addr) | (read(addr + 1) << 8);
}
// ==========================================================================
// Instructions
// MOV
void MOV(uint8_t operand, bool isImmediate = false) {
uint8_t value = isImmediate ? imm() : operand;
operand = value;
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 operand, bool isImmediate = false) {
uint8_t value = isImmediate ? imm() : operand;
uint16_t result = A + value + PSW.C;
PSW.V = ((A ^ result) & (value ^ result) & 0x80);
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 ^ value ^ result) & 0x10);
A = result & 0xFF;
PSW.H = ((A ^ operand ^ result) & 0x10);
dest = result & 0xFF;
}
// SBC
void SBC(uint8_t operand, bool isImmediate = false) {
uint8_t value = isImmediate ? imm() : operand;
uint16_t result = A - value - (1 - PSW.C);
PSW.V = ((A ^ result) & (A ^ value) & 0x80);
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 = ((A ^ value ^ result) & 0x10);
A = result & 0xFF;
PSW.H = ((dest ^ operand ^ result) & 0x10);
dest = result & 0xFF;
}
// CMP
void CMP(uint8_t operand, bool isImmediate = false) {
uint8_t value = isImmediate ? imm() : operand;
uint16_t result = A - value;
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 operand, bool isImmediate = false) {
uint8_t value = isImmediate ? imm() : operand;
A &= value;
PSW.Z = (A == 0);
PSW.N = (A & 0x80);
void AND(uint8_t& dest, uint8_t operand) {
dest &= operand;
PSW.Z = (dest == 0);
PSW.N = (dest & 0x80);
}
// OR
void OR(uint8_t operand, bool isImmediate = false) {
uint8_t value = isImmediate ? imm() : operand;
A |= value;
PSW.Z = (A == 0);
PSW.N = (A & 0x80);
void OR(uint8_t& dest, uint8_t operand) {
dest |= operand;
PSW.Z = (dest == 0);
PSW.N = (dest & 0x80);
}
// EOR
void EOR(uint8_t operand, bool isImmediate = false) {
uint8_t value = isImmediate ? imm() : operand;
A ^= value;
PSW.Z = (A == 0);
PSW.N = (A & 0x80);
void EOR(uint8_t& dest, uint8_t operand) {
dest ^= operand;
PSW.Z = (dest == 0);
PSW.N = (dest & 0x80);
}
// ASL LSR ROL XCN
// INC DEC
// MOVW INCW DECW ADDW SUBW CMPW MUL DIV
// DAA DAS
// BRA BEQ BNE BCS BCC BVS BVC BMI BPL BBS BBC CBNE DBNZ JMP
// 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
// 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