From af73af430061f6e3b05edbe91c374daa98490435 Mon Sep 17 00:00:00 2001 From: scawful Date: Fri, 25 Aug 2023 17:44:04 -0400 Subject: [PATCH] Add addressing modes and opcodes for SPC700 --- src/app/emu/spc700.cc | 58 ++++++- src/app/emu/spc700.h | 357 +++++++++++++++++++++++++++++++++++------- 2 files changed, 356 insertions(+), 59 deletions(-) diff --git a/src/app/emu/spc700.cc b/src/app/emu/spc700.cc index 3d3f34f6..bd550a5f 100644 --- a/src/app/emu/spc700.cc +++ b/src/app/emu/spc700.cc @@ -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 diff --git a/src/app/emu/spc700.h b/src/app/emu/spc700.h index 1a51605d..a1df080c 100644 --- a/src/app/emu/spc700.h +++ b/src/app/emu/spc700.h @@ -22,10 +22,10 @@ class VirtualAudioRAM { class AudioRAM : public VirtualAudioRAM { static const size_t ARAM_SIZE = 64 * 1024; // 64 KB - std::vector ram; + std::vector ram = std::vector(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> voices; + std::vector> voices = std::vector>( + NUM_VOICES, std::vector(NUM_VOICE_REGS, 0)); // Global registers - std::vector globalRegs; + std::vector globalRegs = std::vector(NUM_GLOBAL_REGS, 0x00); public: - SDSP() - : voices(NUM_VOICES, std::vector(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