Update Spc700, separate addressing and opcodes

This commit is contained in:
scawful
2023-12-05 03:44:24 -05:00
parent dbedd83612
commit b7558371b3
4 changed files with 974 additions and 307 deletions

View File

@@ -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

View File

@@ -0,0 +1,263 @@
#pragma once
#include <cstdint>
#include <string>
#include <unordered_map>
const std::unordered_map<uint8_t, std::string> 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"}};

View File

@@ -1,206 +1,337 @@
#include "app/emu/audio/spc700.h"
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#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<int>(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<int>(A)
<< "\033[0m";
log_entry_stream << " \033[1;33mX: " << std::hex << std::setw(2)
<< std::setfill('0') << std::right << static_cast<int>(X)
<< "\033[0m";
log_entry_stream << " \033[1;33mY: " << std::hex << std::setw(2)
<< std::setfill('0') << std::right << static_cast<int>(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

View File

@@ -3,6 +3,7 @@
#include <cstdint>
#include <iostream>
#include <unordered_map>
#include <vector>
namespace yaze {
@@ -38,23 +39,32 @@ class AudioRamImpl : public AudioRam {
}
};
class SPC700 {
class Spc700 {
private:
AudioRam& aram_;
std::vector<std::string> 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