diff --git a/src/app/emu/audio/internal/addressing.cc b/src/app/emu/audio/internal/addressing.cc new file mode 100644 index 00000000..f8f973b1 --- /dev/null +++ b/src/app/emu/audio/internal/addressing.cc @@ -0,0 +1,361 @@ +#include "app/emu/audio/spc700.h" + +namespace yaze { +namespace app { +namespace emu { + +void Spc700::MOV(uint8_t& dest, uint8_t operand) { + dest = operand; + PSW.Z = (operand == 0); + PSW.N = (operand & 0x80); +} + +void Spc700::MOV_ADDR(uint16_t address, uint8_t operand) { + write(address, operand); + PSW.Z = (operand == 0); + 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); + PSW.C = (result > 0xFF); + PSW.Z = ((result & 0xFF) == 0); + PSW.N = (result & 0x80); + PSW.H = ((A ^ operand ^ result) & 0x10); + 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 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); + PSW.N = (value & 0x80); + // operand = value; +} + +void Spc700::XCN(uint8_t operand, bool isImmediate) { + uint8_t value = isImmediate ? imm() : operand; + value = ((value & 0xF0) >> 4) | ((value & 0x0F) << 4); + PSW.Z = (value == 0); + PSW.N = (value & 0x80); + // operand = value; +} + +void Spc700::INC(uint8_t& operand) { + operand++; + PSW.Z = (operand == 0); + PSW.N = (operand & 0x80); +} + +void Spc700::DEC(uint8_t& operand) { + operand--; + PSW.Z = (operand == 0); + PSW.N = (operand & 0x80); +} + +void Spc700::MOVW(uint16_t& dest, uint16_t operand) { + dest = operand; + PSW.Z = (operand == 0); + PSW.N = (operand & 0x8000); +} + +void Spc700::INCW(uint16_t& operand) { + operand++; + PSW.Z = (operand == 0); + PSW.N = (operand & 0x8000); +} + +void Spc700::DECW(uint16_t& operand) { + operand--; + PSW.Z = (operand == 0); + PSW.N = (operand & 0x8000); +} + +void Spc700::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; +} + +void Spc700::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; +} + +void Spc700::CMPW(uint16_t operand) { + uint32_t result = YA - operand; + PSW.C = (result < 0x10000); + PSW.Z = ((result & 0xFFFF) == 0); + PSW.N = (result & 0x8000); +} + +void Spc700::MUL(uint8_t operand) { + uint16_t result = A * operand; + YA = result; + PSW.Z = (result == 0); + PSW.N = (result & 0x8000); +} + +void Spc700::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); +} + +void Spc700::BRA(int8_t offset) { PC += offset; } + +void Spc700::BEQ(int8_t offset) { + if (PSW.Z) { + PC += offset; + } +} + +void Spc700::BNE(int8_t offset) { + if (!PSW.Z) { + PC += offset; + } +} + +void Spc700::BCS(int8_t offset) { + if (PSW.C) { + PC += offset; + } +} + +void Spc700::BCC(int8_t offset) { + if (!PSW.C) { + PC += offset; + } +} + +void Spc700::BVS(int8_t offset) { + if (PSW.V) { + PC += offset; + } +} + +void Spc700::BVC(int8_t offset) { + if (!PSW.V) { + PC += offset; + } +} + +void Spc700::BMI(int8_t offset) { + if (PSW.N) { + PC += offset; + } +} + +void Spc700::BPL(int8_t offset) { + if (!PSW.N) { + PC += offset; + } +} + +void Spc700::BBS(uint8_t bit, uint8_t operand) { + if (operand & (1 << bit)) { + PC += rel(); + } +} + +void Spc700::BBC(uint8_t bit, uint8_t operand) { + if (!(operand & (1 << bit))) { + PC += rel(); + } +} + +// CBNE DBNZ +// JMP +void Spc700::JMP(uint16_t address) { PC = address; } + +void Spc700::CALL(uint16_t address) { + uint16_t return_address = PC + 2; + write(SP, return_address & 0xFF); + write(SP - 1, (return_address >> 8) & 0xFF); + SP -= 2; + PC = address; +} + +void Spc700::PCALL(uint8_t offset) { + uint16_t return_address = PC + 2; + write(SP, return_address & 0xFF); + write(SP - 1, (return_address >> 8) & 0xFF); + SP -= 2; + PC += offset; +} + +void Spc700::TCALL(uint8_t offset) { + uint16_t return_address = PC + 2; + write(SP, return_address & 0xFF); + write(SP - 1, (return_address >> 8) & 0xFF); + SP -= 2; + PC = 0xFFDE + offset; +} + +void Spc700::BRK() { + uint16_t return_address = PC + 2; + write(SP, return_address & 0xFF); + write(SP - 1, (return_address >> 8) & 0xFF); + SP -= 2; + PC = 0xFFDE; +} + +void Spc700::RET() { + uint16_t return_address = read(SP) | (read(SP + 1) << 8); + SP += 2; + PC = return_address; +} + +void Spc700::RETI() { + uint16_t return_address = read(SP) | (read(SP + 1) << 8); + SP += 2; + PC = return_address; + PSW.I = 1; +} + +void Spc700::PUSH(uint8_t operand) { + write(SP, operand); + SP--; +} + +void Spc700::POP(uint8_t& operand) { + SP++; + operand = read(SP); +} + +void Spc700::SET1(uint8_t bit, uint8_t& operand) { operand |= (1 << bit); } + +void Spc700::CLR1(uint8_t bit, uint8_t& operand) { operand &= ~(1 << bit); } + +void Spc700::TSET1(uint8_t bit, uint8_t& operand) { + PSW.C = (operand & (1 << bit)); + operand |= (1 << bit); +} + +void Spc700::TCLR1(uint8_t bit, uint8_t& operand) { + PSW.C = (operand & (1 << bit)); + operand &= ~(1 << bit); +} + +void Spc700::AND1(uint8_t bit, uint8_t& operand) { + operand &= (1 << bit); + PSW.Z = (operand == 0); + PSW.N = (operand & 0x80); +} + +void Spc700::OR1(uint8_t bit, uint8_t& operand) { + operand |= (1 << bit); + PSW.Z = (operand == 0); + PSW.N = (operand & 0x80); +} + +void Spc700::EOR1(uint8_t bit, uint8_t& operand) { + operand ^= (1 << bit); + PSW.Z = (operand == 0); + PSW.N = (operand & 0x80); +} + +void Spc700::NOT1(uint8_t bit, uint8_t& operand) { + operand ^= (1 << bit); + PSW.Z = (operand == 0); + PSW.N = (operand & 0x80); +} + +void Spc700::MOV1(uint8_t bit, uint8_t& operand) { + PSW.C = (operand & (1 << bit)); + operand |= (1 << bit); +} + +void Spc700::CLRC() { PSW.C = 0; } + +void Spc700::SETC() { PSW.C = 1; } + +void Spc700::NOTC() { PSW.C = !PSW.C; } + +void Spc700::CLRV() { PSW.V = 0; } + +void Spc700::CLRP() { PSW.P = 0; } + +void Spc700::SETP() { PSW.P = 1; } + +void Spc700::EI() { PSW.I = 1; } + +void Spc700::DI() { PSW.I = 0; } + +void Spc700::NOP() { PC++; } + +void Spc700::SLEEP() {} + +void Spc700::STOP() {} + +} // namespace emu +} // namespace app +} // namespace yaze \ No newline at end of file diff --git a/src/app/emu/audio/internal/opcodes.h b/src/app/emu/audio/internal/opcodes.h new file mode 100644 index 00000000..15a48cc7 --- /dev/null +++ b/src/app/emu/audio/internal/opcodes.h @@ -0,0 +1,263 @@ +#pragma once + +#include +#include +#include + +const std::unordered_map spc_opcode_map = { + {0x00, "NOP"}, + {0x01, "TCALL0"}, + {0x02, "SET1 direct.0"}, + {0x03, "BBS direct.0,rel"}, + {0x04, "OR A,direct"}, + {0x05, "OR A,abs"}, + {0x06, "OR A,(X)"}, + {0x07, "OR A,(direct+X)"}, + {0x08, "OR A,#imm"}, + {0x09, "OR direct,imm"}, + {0x0A, "OR1 C,membit"}, + {0x0B, "ASL direct"}, + {0x0C, "ASL abs"}, + {0x0D, "PUSH PSW"}, + {0x0E, "TSET1 abs"}, + {0x0F, "BRK"}, + {0x10, "BPL rel"}, + {0x11, "TCALL1"}, + {0x12, "CLR1 direct.0"}, + {0x13, "BBC direct.0,rel"}, + {0x14, "OR A,direct+X"}, + {0x15, "OR A,abs+X"}, + {0x16, "OR A,abs+Y"}, + {0x17, "OR A,(direct)+Y"}, + {0x18, "OR direct,direct"}, + {0x19, "OR (X),(Y)"}, + {0x1A, "DECW direct"}, + {0x1B, "ASL direct+X"}, + {0x1C, "ASL A"}, + {0x1D, "DEC X"}, + {0x1E, "CMP X,abs"}, + {0x1F, "JMP (abs+X)"}, + {0x20, "CLRP"}, + {0x21, "TCALL2"}, + {0x22, "SET1 direct.1"}, + {0x23, "BBS direct.1,rel"}, + {0x24, "AND A,direct"}, + {0x25, "AND A,abs"}, + {0x26, "AND A,(X)"}, + {0x27, "AND A,(direct+X)"}, + {0x28, "AND A,#imm"}, + {0x29, "AND direct,imm"}, + {0x2A, "OR1 C,/membit"}, + {0x2B, "ROL direct"}, + {0x2C, "ROL abs"}, + {0x2D, "PUSH A"}, + {0x2E, "CBNE direct,rel"}, + {0x2F, "BRA rel"}, + {0x30, "BMI rel"}, + {0x31, "TCALL3"}, + {0x32, "CLR1 direct.1"}, + {0x33, "BBC direct.1,rel"}, + {0x34, "AND A,direct+X"}, + {0x35, "AND A,abs+X"}, + {0x36, "AND A,abs+Y"}, + {0x37, "AND A,(direct)+Y"}, + {0x38, "AND direct,direct"}, + {0x39, "AND (X),(Y)"}, + {0x3A, "INCW direct"}, + {0x3B, "ROL direct+X"}, + {0x3C, "ROL A"}, + {0x3D, "INC X"}, + {0x3E, "CMP X,direct"}, + {0x3F, "CALL abs"}, + {0x40, "SETP"}, + {0x41, "TCALL4"}, + {0x42, "SET1 direct.2"}, + {0x43, "BBS direct.2,rel"}, + {0x44, "EOR A,direct"}, + {0x45, "EOR A,abs"}, + {0x46, "EOR A,(X)"}, + {0x47, "EOR A,(direct+X)"}, + {0x48, "EOR A,#imm"}, + {0x49, "EOR direct,imm"}, + {0x4A, "AND1 C,membit"}, + {0x4B, "LSR direct"}, + {0x4C, "LSR abs"}, + {0x4D, "PUSH X"}, + {0x4E, "TCLR1 abs"}, + {0x4F, "PCALL addr"}, + {0x50, "BVC rel"}, + {0x51, "TCALL5"}, + {0x52, "CLR1 direct.2"}, + {0x53, "BBC direct.2,rel"}, + {0x54, "EOR A,direct+X"}, + {0x55, "EOR A,abs+X"}, + {0x56, "EOR A,abs+Y"}, + {0x57, "EOR A,(direct)+Y"}, + {0x58, "EOR direct,direct"}, + {0x59, "EOR (X),(Y)"}, + {0x5A, "CMPW YA,direct"}, + {0x5B, "LSR direct+X"}, + {0x5C, "LSR A"}, + {0x5D, "MOV X,A"}, + {0x5E, "CMP Y,abs"}, + {0x5F, "JMP abs"}, + {0x60, "CLRC"}, + {0x61, "TCALL6"}, + {0x62, "SET1 direct.3"}, + {0x63, "BBS direct.3,rel"}, + {0x64, "CMP A,direct"}, + {0x65, "CMP A,abs"}, + {0x66, "CMP A,(X)"}, + {0x67, "CMP A,(direct+X)"}, + {0x68, "CMP A,#imm"}, + {0x69, "CMP direct,imm"}, + {0x6A, "AND1 C,/membit"}, + {0x6B, "ROR direct"}, + {0x6C, "ROR abs"}, + {0x6D, "PUSH Y"}, + {0x6E, "DBNZ direct,rel"}, + {0x6F, "RET"}, + {0x70, "BVS rel"}, + {0x71, "TCALL7"}, + {0x72, "CLR1 direct.3"}, + {0x73, "BBC direct.3,rel"}, + {0x74, "CMP A,direct+X"}, + {0x75, "CMP A,abs+X"}, + {0x76, "CMP A,abs+Y"}, + {0x77, "CMP A,(direct)+Y"}, + {0x78, "CMP direct,direct"}, + {0x79, "CMP (X),(Y)"}, + {0x7A, "ADDW YA,direct"}, + {0x7B, "ROR direct+X"}, + {0x7C, "ROR A"}, + {0x7D, "MOV A,X"}, + {0x7E, "CMP Y,direct"}, + {0x7F, "RETI"}, + {0x80, "SETC"}, + {0x81, "TCALL8"}, + {0x82, "SET1 direct.4"}, + {0x83, "BBS direct.4,rel"}, + {0x84, "ADC A,direct"}, + {0x85, "ADC A,abs"}, + {0x86, "ADC A,(X)"}, + {0x87, "ADC A,(direct+X)"}, + {0x88, "ADC A,#imm"}, + {0x89, "ADC direct,imm"}, + {0x8A, "EOR1 C,membit"}, + {0x8B, "DEC direct"}, + {0x8C, "DEC abs"}, + {0x8D, "MOV Y,#imm"}, + {0x8E, "POP PSW"}, + {0x8F, "MOV direct,#imm"}, + {0x90, "BCC rel"}, + {0x91, "TCALL9"}, + {0x92, "CLR1 direct.4"}, + {0x93, "BBC direct.4,rel"}, + {0x94, "ADC A,direct+X"}, + {0x95, "ADC A,abs+X"}, + {0x96, "ADC A,abs+Y"}, + {0x97, "ADC A,(direct)+Y"}, + {0x98, "ADC direct,direct"}, + {0x99, "ADC (X),(Y)"}, + {0x9A, "SUBW YA,direct"}, + {0x9B, "DEC direct+X"}, + {0x9C, "DEC A"}, + {0x9D, "MOV X,SP"}, + {0x9E, "DIV YA,X"}, + {0x9F, "XCN A"}, + {0xA0, "EI"}, + {0xA1, "TCALL10"}, + {0xA2, "SET1 direct.5"}, + {0xA3, "BBS direct.5,rel"}, + {0xA4, "SBC A,direct"}, + {0xA5, "SBC A,abs"}, + {0xA6, "SBC A,(X)"}, + {0xA7, "SBC A,(direct+X)"}, + {0xA8, "SBC A,#imm"}, + {0xA9, "SBC direct,imm"}, + {0xAA, "MOV1 C,membit"}, + {0xAB, "INC direct"}, + {0xAC, "INC abs"}, + {0xAD, "CMP Y,#imm"}, + {0xAE, "POP A"}, + {0xAF, "MOV (X)+,A"}, + {0xB0, "BCS rel"}, + {0xB1, "TCALL11"}, + {0xB2, "CLR1 direct.5"}, + {0xB3, "BBC direct.5,rel"}, + {0xB4, "SBC A,direct+X"}, + {0xB5, "SBC A,abs+X"}, + {0xB6, "SBC A,abs+Y"}, + {0xB7, "SBC A,(direct)+Y"}, + {0xB8, "SBC direct,direct"}, + {0xB9, "SBC (X),(Y)"}, + {0xBA, "MOVW YA,direct"}, + {0xBB, "INC direct+X"}, + {0xBC, "INC A"}, + {0xBD, "MOV SP,X"}, + {0xBE, "DAS"}, + {0xBF, "MOV A,(X)+"}, + {0xC0, "DI"}, + {0xC1, "TCALL12"}, + {0xC2, "SET1 direct.6"}, + {0xC3, "BBS direct.6,rel"}, + {0xC4, "MOV direct,A"}, + {0xC5, "MOV abs,A"}, + {0xC6, "MOV (X),A"}, + {0xC7, "MOV (direct+X),A"}, + {0xC8, "CMP X,#imm"}, + {0xC9, "MOV abs,X"}, + {0xCA, "MOV1 membit,C"}, + {0xCB, "MOV direct,Y"}, + {0xCC, "MOV abs,Y"}, + {0xCD, "MOV X,#imm"}, + {0xCE, "POP X"}, + {0xCF, "MUL YA"}, + {0xD0, "BNE rel"}, + {0xD1, "TCALL13"}, + {0xD2, "CLR1 direct.6"}, + {0xD3, "BBC direct.6,rel"}, + {0xD4, "MOV direct+X,A"}, + {0xD5, "MOV abs+X,A"}, + {0xD6, "MOV abs+Y,A"}, + {0xD7, "MOV (direct)+Y,A"}, + {0xD8, "MOV direct,X"}, + {0xD9, "MOV direct+Y,X"}, + {0xDA, "MOVW direct,YA"}, + {0xDB, "MOV direct+X,Y"}, + {0xDC, "DEC Y"}, + {0xDD, "MOV A,Y"}, + {0xDE, "CBNE direct+X,rel"}, + {0xDF, "DAA"}, + {0xE0, "CLRV"}, + {0xE1, "TCALL14"}, + {0xE2, "SET1 direct.7"}, + {0xE3, "BBS direct.7,rel"}, + {0xE4, "MOV A,direct"}, + {0xE5, "MOV A,abs"}, + {0xE6, "MOV A,(X)"}, + {0xE7, "MOV A,(direct+X)"}, + {0xE8, "MOV A,#imm"}, + {0xE9, "MOV X,abs"}, + {0xEA, "NOT1 membit"}, + {0xEB, "MOV Y,direct"}, + {0xEC, "MOV Y,abs"}, + {0xED, "NOTC"}, + {0xEE, "POP Y"}, + {0xEF, "SLEEP"}, + {0xF0, "BEQ rel"}, + {0xF1, "TCALL15"}, + {0xF2, "CLR1 direct.7"}, + {0xF3, "BBC direct.7,rel"}, + {0xF4, "MOV A,direct+X"}, + {0xF5, "MOV A,abs+X"}, + {0xF6, "MOV A,abs+Y"}, + {0xF7, "MOV A,(direct)+Y"}, + {0xF8, "MOV X,direct"}, + {0xF9, "MOV X,direct+Y"}, + {0xFA, "MOV direct,S"}, + {0xFB, "MOV Y,direct+X"}, + {0xFC, "INC Y"}, + {0xFD, "MOV Y,A"}, + {0xFE, "DBNZ Y,rel"}, + {0xFF, "STOP"}}; diff --git a/src/app/emu/audio/spc700.cc b/src/app/emu/audio/spc700.cc index 76318851..3bb7cf1b 100644 --- a/src/app/emu/audio/spc700.cc +++ b/src/app/emu/audio/spc700.cc @@ -1,206 +1,337 @@ #include "app/emu/audio/spc700.h" +#include #include +#include +#include #include +#include "app/emu/audio/internal/opcodes.h" + namespace yaze { namespace app { namespace emu { -void SPC700::Reset() {} +void Spc700::Reset() {} -void SPC700::Notify(uint32_t address, uint8_t data) { - // Check if the address corresponds to the APU's I/O ports - if (address >= 0x2140 && address <= 0x2143) { - // Handle the IPL process based on the address and data - if (address == 0x2140) { - // ... Handle data sent to APUIO0 ... - // For instance, checking for the $CC signal to start the APU program - } else if (address == 0x2141) { - // ... Handle data sent to APUIO1 ... - // This might involve storing data for the APU or signaling the DSP, etc. - } else if (address == 0x2142) { - // ... Handle data sent to APUIO2 ... - } else if (address == 0x2143) { - // ... Handle data sent to APUIO3 ... +void Spc700::BootIplRom() { + PC = 0xFFC0; + A = 0; + X = 0; + Y = 0; + + // for (int i = 0; i < 0x40; ++i) { + // uint8_t opcode = read(PC); + // ExecuteInstructions(opcode); + // PC++; + // } + + int i = 0; + while (PC != 0xFFC0 + 0x3F) { + uint8_t opcode = read(PC); + ExecuteInstructions(opcode); + PC++; + i++; + + if (i > 1000) { + break; } } } -void SPC700::ExecuteInstructions(uint8_t opcode) { +void Spc700::ExecuteInstructions(uint8_t opcode) { + uint16_t initialPC = PC; switch (opcode) { - // 8-bit Move Memory to Register + // 8-bit Move Memory to Register case 0xE8: // MOV A, #imm + { 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 - + // 8-bit move register to memory case 0xC6: // MOV (X), A + { + MOV_ADDR(X, A); break; + } case 0xAF: // MOV (X)+, A + { + MOV_ADDR(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 - + // . 8-bit move register to register / special direct page moves case 0x7D: // MOV A, X + { + MOV(A, X); break; + } case 0xDD: // MOV A, Y + { + MOV(A, Y); break; + } case 0x5D: // MOV X, A + { + MOV(X, A); break; + } case 0xFD: // MOV Y, A + { + MOV(Y, A); break; + } case 0x9D: // MOV X, SP + { + MOV(X, SP); break; + } case 0xBD: // MOV SP, X + { + MOV(SP, X); break; + } case 0xFA: // MOV dp, dp + { + MOV_ADDR(get_dp_addr(), dp()); break; + } case 0x8F: // MOV dp, #imm + { + MOV_ADDR(get_dp_addr(), imm()); break; + } - // . 8-bit arithmetic - + // . 8-bit arithmetic case 0x88: // ADC A, #imm + { ADC(A, imm()); break; + } case 0x86: // ADC A, (X) + { + 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 + { + ADC(A, abs() + X); break; + } case 0x96: // ADC A, !abs+Y + { + 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; case 0x89: // ADC dp, dp break; case 0x98: // ADC dp, #imm break; + case 0xA8: // SBC A, #imm SBC(A, imm()); break; case 0xA6: // SBC A, (X) break; case 0xA4: // SBC A, dp + SBC(A, dp()); break; case 0xB4: // SBC A, dp+X + SBC(A, dp_plus_x()); break; case 0xA5: // SBC A, !abs + SBC(A, abs()); break; case 0xB5: // SBC A, !abs+X + SBC(A, abs() + X); break; case 0xB6: // SBC A, !abs+Y + SBC(A, abs() + Y); break; case 0xA7: // SBC A, [dp+X] + SBC(A, dp_plus_x_indirect()); break; case 0xB7: // SBC A, [dp]+Y + SBC(A, dp_indirect_plus_y()); break; case 0xB9: // SBC (X), (Y) break; @@ -208,19 +339,26 @@ void SPC700::ExecuteInstructions(uint8_t opcode) { break; case 0xB8: // SBC dp, #imm break; + case 0x68: // CMP A, #imm + CMP(A, imm()); break; case 0x66: // CMP A, (X) break; case 0x64: // CMP A, dp + CMP(A, dp()); break; case 0x74: // CMP A, dp+X + CMP(A, dp_plus_x()); break; case 0x65: // CMP A, !abs + CMP(A, abs()); break; case 0x75: // CMP A, !abs+X + CMP(A, abs() + X); break; case 0x76: // CMP A, !abs+Y + CMP(A, abs() + Y); break; case 0x67: // CMP A, [dp+X] break; @@ -245,8 +383,7 @@ void SPC700::ExecuteInstructions(uint8_t opcode) { case 0x5E: // CMP Y, !abs break; - // 8-bit boolean logic - + // 8-bit boolean logic case 0x28: // AND A, #imm AND(A, imm()); break; @@ -287,12 +424,16 @@ void SPC700::ExecuteInstructions(uint8_t opcode) { OR(A, abs()); break; case 0x15: // OR A, !abs+X + OR(A, abs() + X); break; case 0x16: // OR A, !abs+Y + OR(A, abs() + Y); break; case 0x07: // OR A, [dp+X] + OR(A, dp_plus_x_indirect()); break; case 0x17: // OR A, [dp]+Y + OR(A, dp_indirect_plus_y()); break; case 0x19: // OR (X), (Y) break; @@ -309,16 +450,22 @@ void SPC700::ExecuteInstructions(uint8_t opcode) { EOR(A, dp()); break; case 0x54: // EOR A, dp+X + EOR(A, dp_plus_x()); break; case 0x45: // EOR A, !abs + EOR(A, abs()); break; case 0x55: // EOR A, !abs+X + EOR(A, abs() + X); break; case 0x56: // EOR A, !abs+Y + EOR(A, abs() + Y); break; case 0x47: // EOR A, [dp+X] + EOR(A, dp_plus_x_indirect()); break; case 0x57: // EOR A, [dp]+Y + EOR(A, dp_indirect_plus_y()); break; case 0x59: // EOR (X), (Y) break; @@ -406,6 +553,7 @@ void SPC700::ExecuteInstructions(uint8_t opcode) { // . 16-bit operations case 0xBA: // MOVW YA, dp + MOVW(YA, dp()); break; case 0xDA: // MOVW dp, YA break; @@ -478,24 +626,50 @@ void SPC700::ExecuteInstructions(uint8_t opcode) { case 0x7F: // RETI break; - // . stack - + // . stack case 0x2D: // PUSH A + { + PUSH(A); break; + } case 0x4D: // PUSH X + { + PUSH(X); break; + } case 0x6D: // PUSH Y + { + PUSH(Y); break; + } case 0x0D: // PUSH PSW + { + PUSH(FlagsToByte(PSW)); break; + } + case 0xAE: // POP A + { + POP(A); break; + } case 0xCE: // POP X + { + POP(X); break; + } case 0xEE: // POP Y + { + POP(Y); break; + } case 0x8E: // POP PSW + { + uint8_t flags_byte; + POP(flags_byte); + PSW = ByteToFlags(flags_byte); break; + } // . memory bit operations @@ -519,35 +693,81 @@ void SPC700::ExecuteInstructions(uint8_t opcode) { // . status flags case 0x60: // CLRC + CLRC(); break; case 0x80: // SETC + SETC(); break; case 0xED: // NOTC + NOTC(); break; case 0xE0: // CLRV + CLRV(); break; case 0x20: // CLRP + CLRP(); break; case 0x40: // SETP + SETP(); break; case 0xA0: // EI + EI(); break; case 0xC0: // DI + DI(); break; - // .no-operation and halt - + // .no-operation and haltF case 0x00: // NOP + { + NOP(); break; + } case 0xEF: // SLEEP + { + SLEEP(); break; + } case 0x0F: // STOP + { + STOP(); break; + } default: std::cout << "Unknown opcode: " << std::hex << opcode << std::endl; break; } + + LogInstruction(initialPC, opcode); +} + +void Spc700::LogInstruction(uint16_t initial_pc, uint8_t opcode) { + std::string mnemonic = spc_opcode_map.at(opcode); + + std::stringstream log_entry_stream; + log_entry_stream << "\033[1;36m$" << std::hex << std::setw(4) + << std::setfill('0') << initial_pc << "\033[0m"; + log_entry_stream << " \033[1;32m" << std::hex << std::setw(2) + << std::setfill('0') << static_cast(opcode) << "\033[0m" + << " \033[1;35m" << std::setw(18) << std::left + << std::setfill(' ') << mnemonic << "\033[0m"; + + log_entry_stream << " \033[1;33mA: " << std::hex << std::setw(2) + << std::setfill('0') << std::right << static_cast(A) + << "\033[0m"; + log_entry_stream << " \033[1;33mX: " << std::hex << std::setw(2) + << std::setfill('0') << std::right << static_cast(X) + << "\033[0m"; + log_entry_stream << " \033[1;33mY: " << std::hex << std::setw(2) + << std::setfill('0') << std::right << static_cast(Y) + << "\033[0m"; + std::string log_entry = log_entry_stream.str(); + + std::cerr << log_entry << std::endl; + + // Append the log entry to the log + log_.push_back(log_entry); } } // namespace emu diff --git a/src/app/emu/audio/spc700.h b/src/app/emu/audio/spc700.h index 75d9f7b9..f8a2ceb2 100644 --- a/src/app/emu/audio/spc700.h +++ b/src/app/emu/audio/spc700.h @@ -3,6 +3,7 @@ #include #include +#include #include namespace yaze { @@ -38,23 +39,32 @@ class AudioRamImpl : public AudioRam { } }; -class SPC700 { +class Spc700 { private: AudioRam& aram_; + std::vector log_; + + 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(AudioRam& aram) : aram_(aram) {} + 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 + uint8_t A = 0; // 8-bit accumulator + uint8_t X = 0; // 8-bit index + uint8_t Y = 0; // 8-bit index + uint16_t YA = 0; // 16-bit pair of A (lsb) and Y (msb) + uint16_t PC = 0; // program counter + uint8_t SP = 0; // stack pointer struct Flags { uint8_t N : 1; // Negative flag @@ -68,11 +78,30 @@ class SPC700 { }; 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(); - void Notify(uint32_t address, uint8_t data); + void BootIplRom(); 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) { @@ -88,6 +117,7 @@ class SPC700 { return aram_.read(address); } else { // Handle IPL ROM or RAM reads here + return ipl_rom_[address - 0xFFC0]; } } return 0; @@ -203,277 +233,70 @@ class SPC700 { // ========================================================================== // 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(); - } - } + void MOV(uint8_t& dest, uint8_t operand); + 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 XCN(uint8_t operand, bool isImmediate = false); + void INC(uint8_t& operand); + void DEC(uint8_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 - // 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