feat(spc700): enhance cycle accuracy and instruction execution
- Introduced a new cycle lookup table for SPC700 instructions, improving timing precision. - Refactored instruction execution to be fully atomic, eliminating the bstep mechanism. - Added detailed comments for clarity on cycle counts and instruction behavior. - Implemented additional logging for CPU audio initialization to aid debugging. Benefits: - Enhanced synchronization between CPU and APU. - Improved testability and accuracy of instruction timing. - Streamlined code for better maintainability and understanding.
This commit is contained in:
@@ -101,6 +101,7 @@ void Apu::RunCycles(uint64_t master_cycles) {
|
||||
|
||||
while (cycles_ < target_apu_cycles) {
|
||||
// Execute one SPC700 opcode (variable cycles) then advance APU cycles accordingly.
|
||||
uint16_t old_pc = spc700_.PC;
|
||||
uint16_t current_pc = spc700_.PC;
|
||||
|
||||
// IPL ROM protocol analysis - let it run to see what happens
|
||||
@@ -145,6 +146,10 @@ void Apu::RunCycles(uint64_t master_cycles) {
|
||||
// Step() returns the precise number of cycles consumed by the instruction
|
||||
int spc_cycles = spc700_.Step();
|
||||
|
||||
if (handshake_tracker_) {
|
||||
handshake_tracker_->OnSpcPCChange(old_pc, spc700_.PC);
|
||||
}
|
||||
|
||||
// Advance APU cycles based on actual SPC700 instruction timing
|
||||
// Each Cycle() call: ticks DSP every 32 cycles, updates timers, increments cycles_
|
||||
for (int i = 0; i < spc_cycles; ++i) {
|
||||
|
||||
@@ -4,311 +4,369 @@
|
||||
namespace yaze {
|
||||
namespace emu {
|
||||
|
||||
// opcode functions
|
||||
// ===========================================================================
|
||||
// CYCLE-ACCURATE ATOMIC INSTRUCTION IMPLEMENTATIONS
|
||||
// ===========================================================================
|
||||
// All instructions are now fully atomic (no bstep mechanism).
|
||||
// Cycle counts match Anomie's SPC700 reference and nesdev.org timing table.
|
||||
// Memory-targeting MOV instructions include dummy read cycles as documented.
|
||||
// ===========================================================================
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// MOV Instructions (Load from memory to register)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void Spc700::MOV(uint16_t adr) {
|
||||
// MOV A, (adr) - Read from memory to A
|
||||
A = read(adr);
|
||||
PSW.Z = (A == 0);
|
||||
PSW.N = (A & 0x80);
|
||||
}
|
||||
|
||||
void Spc700::MOVX(uint16_t adr) {
|
||||
// MOV X, (adr) - Read from memory to X
|
||||
X = read(adr);
|
||||
PSW.Z = (X == 0);
|
||||
PSW.N = (X & 0x80);
|
||||
}
|
||||
|
||||
void Spc700::MOVY(uint16_t adr) {
|
||||
// MOV Y, (adr) - Read from memory to Y
|
||||
Y = read(adr);
|
||||
PSW.Z = (Y == 0);
|
||||
PSW.N = (Y & 0x80);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// MOV Instructions (Store from register to memory)
|
||||
// ---------------------------------------------------------------------------
|
||||
// Note: Per Anomie's doc, these include a dummy read cycle before writing
|
||||
|
||||
void Spc700::MOVS(uint16_t address) {
|
||||
static int movs_log = 0;
|
||||
switch (bstep) {
|
||||
case 0:
|
||||
this->adr = address; // Save address for bstep=1
|
||||
read(this->adr);
|
||||
bstep++;
|
||||
break;
|
||||
case 1:
|
||||
write(this->adr, A); // Use saved address
|
||||
if (this->adr == 0x00F4 && movs_log++ < 10) {
|
||||
LOG_DEBUG("SPC", "MOVS wrote A=$%02X to F4!", A);
|
||||
}
|
||||
bstep = 0;
|
||||
break;
|
||||
}
|
||||
// MOV (address), A - Write A to memory (with dummy read)
|
||||
read(address); // Dummy read (documented behavior)
|
||||
write(address, A);
|
||||
}
|
||||
|
||||
void Spc700::MOVSX(uint16_t address) {
|
||||
switch (bstep) {
|
||||
case 0: this->adr = address; read(this->adr); bstep++; break;
|
||||
case 1: write(this->adr, X); bstep = 0; break;
|
||||
}
|
||||
// MOV (address), X - Write X to memory (with dummy read)
|
||||
read(address); // Dummy read (documented behavior)
|
||||
write(address, X);
|
||||
}
|
||||
|
||||
void Spc700::MOVSY(uint16_t address) {
|
||||
static int movsy_log = 0;
|
||||
switch (bstep) {
|
||||
case 0:
|
||||
this->adr = address;
|
||||
read(this->adr);
|
||||
bstep++;
|
||||
if (this->adr == 0x00F4 && movsy_log < 10) {
|
||||
LOG_DEBUG("SPC", "MOVSY bstep=0: Will write Y=$%02X to F4 at PC=$%04X", Y, PC);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
write(this->adr, Y);
|
||||
if (this->adr == 0x00F4 && movsy_log++ < 10) {
|
||||
LOG_DEBUG("SPC", "MOVSY bstep=1: Wrote Y=$%02X to F4 at PC=$%04X", Y, PC);
|
||||
}
|
||||
bstep = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Spc700::MOV(uint16_t adr) {
|
||||
A = read(adr);
|
||||
PSW.Z = (A == 0);
|
||||
PSW.N = (A & 0x80);
|
||||
// MOV (address), Y - Write Y to memory (with dummy read)
|
||||
read(address); // Dummy read (documented behavior)
|
||||
write(address, Y);
|
||||
}
|
||||
|
||||
void Spc700::MOV_ADDR(uint16_t address, uint8_t operand) {
|
||||
// MOV (address), #imm - Write immediate to memory (with dummy read)
|
||||
read(address); // Dummy read (documented behavior)
|
||||
write(address, operand);
|
||||
PSW.Z = (operand == 0);
|
||||
PSW.N = (operand & 0x80);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Arithmetic Instructions (ADC, SBC)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void Spc700::ADC(uint16_t adr) {
|
||||
// ADC A, (adr) - Add with carry
|
||||
uint8_t value = read(adr);
|
||||
uint16_t result = A + value + PSW.C;
|
||||
PSW.V = ((A ^ result) & (adr ^ result) & 0x80);
|
||||
PSW.V = ((A ^ result) & (value ^ result) & 0x80) != 0;
|
||||
PSW.H = ((A & 0xf) + (value & 0xf) + PSW.C) > 0xf;
|
||||
PSW.C = (result > 0xFF);
|
||||
PSW.H = ((A ^ adr ^ result) & 0x10);
|
||||
A = result & 0xFF;
|
||||
PSW.Z = ((A & 0xFF) == 0);
|
||||
PSW.N = (A & 0x80);
|
||||
PSW.Z = (A == 0);
|
||||
PSW.N = (A & 0x80) != 0;
|
||||
}
|
||||
|
||||
void Spc700::ADCM(uint16_t& dest, uint8_t operand) {
|
||||
// ADC (dest), operand - Add with carry to memory
|
||||
uint8_t applyOn = read(dest);
|
||||
int result = applyOn + operand + PSW.C;
|
||||
PSW.V = (applyOn & 0x80) == (operand & 0x80) &&
|
||||
(operand & 0x80) != (result & 0x80);
|
||||
PSW.V = ((applyOn & 0x80) == (operand & 0x80)) &&
|
||||
((operand & 0x80) != (result & 0x80));
|
||||
PSW.H = ((applyOn & 0xf) + (operand & 0xf) + PSW.C) > 0xf;
|
||||
PSW.C = result > 0xff;
|
||||
write(dest, result);
|
||||
write(dest, result & 0xFF);
|
||||
PSW.Z = ((result & 0xFF) == 0);
|
||||
PSW.N = (result & 0x80);
|
||||
PSW.N = (result & 0x80) != 0;
|
||||
}
|
||||
|
||||
void Spc700::SBC(uint16_t adr) {
|
||||
// SBC A, (adr) - Subtract with carry (borrow)
|
||||
uint8_t value = read(adr) ^ 0xff;
|
||||
int result = A + value + PSW.C;
|
||||
PSW.V = (A & 0x80) == (value & 0x80) && (value & 0x80) != (result & 0x80);
|
||||
PSW.V = ((A & 0x80) == (value & 0x80)) &&
|
||||
((value & 0x80) != (result & 0x80));
|
||||
PSW.H = ((A & 0xf) + (value & 0xf) + PSW.C) > 0xf;
|
||||
PSW.C = result > 0xff;
|
||||
A = result;
|
||||
PSW.Z = ((A & 0xFF) == 0);
|
||||
PSW.N = (A & 0x80);
|
||||
A = result & 0xFF;
|
||||
PSW.Z = (A == 0);
|
||||
PSW.N = (A & 0x80) != 0;
|
||||
}
|
||||
|
||||
void Spc700::SBCM(uint16_t& dest, uint8_t operand) {
|
||||
// SBC (dest), operand - Subtract with carry from memory
|
||||
operand ^= 0xff;
|
||||
uint8_t applyOn = read(dest);
|
||||
int result = applyOn + operand + PSW.C;
|
||||
PSW.V = (applyOn & 0x80) == (operand & 0x80) &&
|
||||
(operand & 0x80) != (operand & 0x80);
|
||||
PSW.V = ((applyOn & 0x80) == (operand & 0x80)) &&
|
||||
((operand & 0x80) != (result & 0x80));
|
||||
PSW.H = ((applyOn & 0xF) + (operand & 0xF) + PSW.C) > 0xF;
|
||||
PSW.C = result > 0xFF;
|
||||
write(dest, result);
|
||||
PSW.Z = ((A & 0xFF) == 0);
|
||||
PSW.N = (A & 0x80);
|
||||
write(dest, result & 0xFF);
|
||||
PSW.Z = ((result & 0xFF) == 0);
|
||||
PSW.N = (result & 0x80) != 0;
|
||||
}
|
||||
|
||||
void Spc700::CMPX(uint16_t adr) {
|
||||
uint8_t value = read(adr) ^ 0xff;
|
||||
int result = X + value + 1;
|
||||
PSW.C = result > 0xff;
|
||||
PSW.Z = ((result & 0xFF) == 0); // Check 8-bit result for zero!
|
||||
PSW.N = (result & 0x80);
|
||||
}
|
||||
|
||||
void Spc700::CMPY(uint16_t adr) {
|
||||
uint8_t value = read(adr) ^ 0xff;
|
||||
int result = Y + value + 1;
|
||||
PSW.C = result > 0xff;
|
||||
PSW.Z = ((result & 0xFF) == 0); // Check 8-bit result for zero!
|
||||
PSW.N = (result & 0x80);
|
||||
}
|
||||
|
||||
void Spc700::CMPM(uint16_t dst, uint8_t value) {
|
||||
value ^= 0xff;
|
||||
int result = read(dst) + value + 1;
|
||||
PSW.C = result > 0xff;
|
||||
callbacks_.idle(false);
|
||||
PSW.Z = ((result & 0xFF) == 0); // Check 8-bit result for zero!
|
||||
PSW.N = (result & 0x80);
|
||||
}
|
||||
// ---------------------------------------------------------------------------
|
||||
// Comparison Instructions (CMP, CMPX, CMPY, CMPM)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void Spc700::CMP(uint16_t adr) {
|
||||
// CMP A, (adr) - Compare A with memory
|
||||
uint8_t value = read(adr) ^ 0xff;
|
||||
int result = A + value + 1;
|
||||
PSW.C = result > 0xff;
|
||||
PSW.Z = ((result & 0xFF) == 0);
|
||||
PSW.N = (result & 0x80);
|
||||
PSW.N = (result & 0x80) != 0;
|
||||
}
|
||||
|
||||
void Spc700::CMPX(uint16_t adr) {
|
||||
// CMP X, (adr) - Compare X with memory
|
||||
uint8_t value = read(adr) ^ 0xff;
|
||||
int result = X + value + 1;
|
||||
PSW.C = result > 0xff;
|
||||
PSW.Z = ((result & 0xFF) == 0);
|
||||
PSW.N = (result & 0x80) != 0;
|
||||
}
|
||||
|
||||
void Spc700::CMPY(uint16_t adr) {
|
||||
// CMP Y, (adr) - Compare Y with memory
|
||||
uint8_t value = read(adr) ^ 0xff;
|
||||
int result = Y + value + 1;
|
||||
PSW.C = result > 0xff;
|
||||
PSW.Z = ((result & 0xFF) == 0);
|
||||
PSW.N = (result & 0x80) != 0;
|
||||
}
|
||||
|
||||
void Spc700::CMPM(uint16_t dst, uint8_t value) {
|
||||
// CMP (dst), value - Compare memory with value
|
||||
value ^= 0xff;
|
||||
int result = read(dst) + value + 1;
|
||||
PSW.C = result > 0xff;
|
||||
callbacks_.idle(false); // Extra cycle for memory comparison
|
||||
PSW.Z = ((result & 0xFF) == 0);
|
||||
PSW.N = (result & 0x80) != 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Logical Instructions (AND, OR, EOR)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void Spc700::AND(uint16_t adr) {
|
||||
// AND A, (adr) - Logical AND with memory
|
||||
A &= read(adr);
|
||||
PSW.Z = (A == 0);
|
||||
PSW.N = (A & 0x80);
|
||||
PSW.N = (A & 0x80) != 0;
|
||||
}
|
||||
|
||||
void Spc700::ANDM(uint16_t dest, uint8_t operand) {
|
||||
// AND (dest), operand - Logical AND memory with value
|
||||
uint8_t result = read(dest) & operand;
|
||||
write(dest, result);
|
||||
PSW.Z = (result == 0);
|
||||
PSW.N = (result & 0x80);
|
||||
PSW.N = (result & 0x80) != 0;
|
||||
}
|
||||
|
||||
void Spc700::OR(uint16_t adr) {
|
||||
// OR A, (adr) - Logical OR with memory
|
||||
A |= read(adr);
|
||||
PSW.Z = (A == 0);
|
||||
PSW.N = (A & 0x80);
|
||||
PSW.N = (A & 0x80) != 0;
|
||||
}
|
||||
|
||||
void Spc700::ORM(uint16_t dst, uint8_t value) {
|
||||
// OR (dst), value - Logical OR memory with value
|
||||
uint8_t result = read(dst) | value;
|
||||
write(dst, result);
|
||||
PSW.Z = (result == 0);
|
||||
PSW.N = (result & 0x80);
|
||||
PSW.N = (result & 0x80) != 0;
|
||||
}
|
||||
|
||||
void Spc700::EOR(uint16_t adr) {
|
||||
// EOR A, (adr) - Logical XOR with memory
|
||||
A ^= read(adr);
|
||||
PSW.Z = (A == 0);
|
||||
PSW.N = (A & 0x80);
|
||||
PSW.N = (A & 0x80) != 0;
|
||||
}
|
||||
|
||||
void Spc700::EORM(uint16_t dest, uint8_t operand) {
|
||||
// EOR (dest), operand - Logical XOR memory with value
|
||||
uint8_t result = read(dest) ^ operand;
|
||||
write(dest, result);
|
||||
PSW.Z = (result == 0);
|
||||
PSW.N = (result & 0x80);
|
||||
PSW.N = (result & 0x80) != 0;
|
||||
}
|
||||
|
||||
void Spc700::ASL(uint16_t operand) {
|
||||
uint8_t val = read(operand);
|
||||
write(operand, val);
|
||||
PSW.C = (val & 0x80);
|
||||
// ---------------------------------------------------------------------------
|
||||
// Shift and Rotate Instructions (ASL, LSR, ROL, ROR)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void Spc700::ASL(uint16_t adr) {
|
||||
// ASL (adr) - Arithmetic shift left
|
||||
uint8_t val = read(adr);
|
||||
write(adr, val); // Dummy write (RMW instruction)
|
||||
PSW.C = (val & 0x80) != 0;
|
||||
val <<= 1;
|
||||
write(adr, val); // Actual write
|
||||
PSW.Z = (val == 0);
|
||||
PSW.N = (val & 0x80);
|
||||
PSW.N = (val & 0x80) != 0;
|
||||
}
|
||||
|
||||
void Spc700::LSR(uint16_t adr) {
|
||||
// LSR (adr) - Logical shift right
|
||||
uint8_t val = read(adr);
|
||||
PSW.C = (val & 0x01);
|
||||
write(adr, val); // Dummy write (RMW instruction)
|
||||
PSW.C = (val & 0x01) != 0;
|
||||
val >>= 1;
|
||||
write(adr, val);
|
||||
write(adr, val); // Actual write
|
||||
PSW.Z = (val == 0);
|
||||
PSW.N = (val & 0x80);
|
||||
}
|
||||
|
||||
void Spc700::ROR(uint16_t adr) {
|
||||
uint8_t val = read(adr);
|
||||
bool newC = val & 1;
|
||||
val = (val >> 1) | (PSW.C << 7);
|
||||
PSW.C = newC;
|
||||
write(adr, val);
|
||||
PSW.Z = (val == 0);
|
||||
PSW.N = (val & 0x80);
|
||||
PSW.N = (val & 0x80) != 0;
|
||||
}
|
||||
|
||||
void Spc700::ROL(uint16_t adr) {
|
||||
// ROL (adr) - Rotate left through carry
|
||||
uint8_t val = read(adr);
|
||||
bool newC = val & 0x80;
|
||||
write(adr, val); // Dummy write (RMW instruction)
|
||||
bool newC = (val & 0x80) != 0;
|
||||
val = (val << 1) | PSW.C;
|
||||
PSW.C = newC;
|
||||
write(adr, val);
|
||||
|
||||
write(adr, val); // Actual write
|
||||
PSW.Z = (val == 0);
|
||||
PSW.N = (val & 0x80);
|
||||
PSW.N = (val & 0x80) != 0;
|
||||
}
|
||||
|
||||
void Spc700::ROR(uint16_t adr) {
|
||||
// ROR (adr) - Rotate right through carry
|
||||
uint8_t val = read(adr);
|
||||
write(adr, val); // Dummy write (RMW instruction)
|
||||
bool newC = (val & 1) != 0;
|
||||
val = (val >> 1) | (PSW.C << 7);
|
||||
PSW.C = newC;
|
||||
write(adr, val); // Actual write
|
||||
PSW.Z = (val == 0);
|
||||
PSW.N = (val & 0x80) != 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Increment/Decrement Instructions (INC, DEC)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void Spc700::INC(uint16_t adr) {
|
||||
// INC (adr) - Increment memory
|
||||
uint8_t val = read(adr);
|
||||
write(adr, val); // Dummy write (RMW instruction)
|
||||
val++;
|
||||
write(adr, val); // Actual write
|
||||
PSW.Z = (val == 0);
|
||||
PSW.N = (val & 0x80) != 0;
|
||||
}
|
||||
|
||||
void Spc700::DEC(uint16_t adr) {
|
||||
// DEC (adr) - Decrement memory
|
||||
uint8_t val = read(adr);
|
||||
write(adr, val); // Dummy write (RMW instruction)
|
||||
val--;
|
||||
write(adr, val); // Actual write
|
||||
PSW.Z = (val == 0);
|
||||
PSW.N = (val & 0x80) != 0;
|
||||
}
|
||||
|
||||
void Spc700::XCN(uint8_t operand, bool isImmediate) {
|
||||
// XCN - Exchange nibbles (note: this is only used for A register)
|
||||
uint8_t value = isImmediate ? imm() : operand;
|
||||
value = ((value & 0xF0) >> 4) | ((value & 0x0F) << 4);
|
||||
PSW.Z = (value == 0);
|
||||
PSW.N = (value & 0x80);
|
||||
// operand = value;
|
||||
PSW.N = (value & 0x80) != 0;
|
||||
}
|
||||
|
||||
void Spc700::INC(uint16_t adr) {
|
||||
uint8_t val = read(adr) + 1;
|
||||
write(adr, val);
|
||||
PSW.Z = (val == 0);
|
||||
PSW.N = (val & 0x80);
|
||||
}
|
||||
|
||||
void Spc700::DEC(uint16_t operand) {
|
||||
uint8_t val = read(operand) - 1;
|
||||
write(operand, val);
|
||||
PSW.Z = (operand == 0);
|
||||
PSW.N = (operand & 0x80);
|
||||
}
|
||||
// ---------------------------------------------------------------------------
|
||||
// 16-bit Instructions (MOVW, INCW, DECW, ADDW, SUBW, CMPW)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void Spc700::MOVW(uint16_t& dest, uint16_t operand) {
|
||||
// MOVW - Move 16-bit word
|
||||
dest = operand;
|
||||
PSW.Z = (operand == 0);
|
||||
PSW.N = (operand & 0x8000);
|
||||
PSW.N = (operand & 0x8000) != 0;
|
||||
}
|
||||
|
||||
void Spc700::INCW(uint16_t& operand) {
|
||||
// INCW - Increment 16-bit word
|
||||
operand++;
|
||||
PSW.Z = (operand == 0);
|
||||
PSW.N = (operand & 0x8000);
|
||||
PSW.N = (operand & 0x8000) != 0;
|
||||
}
|
||||
|
||||
void Spc700::DECW(uint16_t& operand) {
|
||||
// DECW - Decrement 16-bit word
|
||||
operand--;
|
||||
PSW.Z = (operand == 0);
|
||||
PSW.N = (operand & 0x8000);
|
||||
PSW.N = (operand & 0x8000) != 0;
|
||||
}
|
||||
|
||||
void Spc700::ADDW(uint16_t& dest, uint16_t operand) {
|
||||
// ADDW - Add 16-bit word
|
||||
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);
|
||||
PSW.N = (result & 0x8000) != 0;
|
||||
PSW.V = ((dest ^ result) & (operand ^ result) & 0x8000) != 0;
|
||||
PSW.H = ((dest & 0xfff) + (operand & 0xfff)) > 0xfff;
|
||||
dest = result & 0xFFFF;
|
||||
}
|
||||
|
||||
void Spc700::SUBW(uint16_t& dest, uint16_t operand) {
|
||||
// SUBW - Subtract 16-bit word
|
||||
uint32_t result = dest - operand;
|
||||
PSW.C = (result < 0x10000);
|
||||
PSW.C = (result <= 0xFFFF);
|
||||
PSW.Z = ((result & 0xFFFF) == 0);
|
||||
PSW.N = (result & 0x8000);
|
||||
PSW.V = ((dest ^ result) & (dest ^ operand) & 0x8000);
|
||||
PSW.N = (result & 0x8000) != 0;
|
||||
PSW.V = ((dest ^ result) & (dest ^ operand) & 0x8000) != 0;
|
||||
PSW.H = ((dest & 0xfff) - (operand & 0xfff)) >= 0;
|
||||
dest = result & 0xFFFF;
|
||||
}
|
||||
|
||||
void Spc700::CMPW(uint16_t operand) {
|
||||
// CMPW - Compare 16-bit word with YA
|
||||
uint32_t result = YA - operand;
|
||||
PSW.C = (result < 0x10000);
|
||||
PSW.C = (result <= 0xFFFF);
|
||||
PSW.Z = ((result & 0xFFFF) == 0);
|
||||
PSW.N = (result & 0x8000);
|
||||
PSW.N = (result & 0x8000) != 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Multiply/Divide Instructions
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void Spc700::MUL(uint8_t operand) {
|
||||
// MUL - Multiply A * Y -> YA
|
||||
uint16_t result = A * operand;
|
||||
YA = result;
|
||||
PSW.Z = (result == 0);
|
||||
PSW.N = (result & 0x8000);
|
||||
PSW.N = (result & 0x8000) != 0;
|
||||
}
|
||||
|
||||
void Spc700::DIV(uint8_t operand) {
|
||||
// DIV - Divide YA / X -> A (quotient), Y (remainder)
|
||||
// Note: Hardware behavior is complex; simplified here
|
||||
if (operand == 0) {
|
||||
// Handle divide by zero error
|
||||
// Divide by zero - undefined behavior
|
||||
// Real hardware has specific behavior, but we'll just return
|
||||
return;
|
||||
}
|
||||
uint8_t quotient = A / operand;
|
||||
@@ -316,194 +374,170 @@ void Spc700::DIV(uint8_t operand) {
|
||||
A = quotient;
|
||||
Y = remainder;
|
||||
PSW.Z = (quotient == 0);
|
||||
PSW.N = (quotient & 0x80);
|
||||
PSW.N = (quotient & 0x80) != 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Branch Instructions
|
||||
// ---------------------------------------------------------------------------
|
||||
// Note: Branch timing is handled in DoBranch() in spc700.cc
|
||||
// These helpers are only used by old code paths
|
||||
|
||||
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::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();
|
||||
}
|
||||
if (operand & (1 << bit)) PC += rel();
|
||||
}
|
||||
|
||||
void Spc700::BBC(uint8_t bit, uint8_t operand) {
|
||||
if (!(operand & (1 << bit))) {
|
||||
PC += rel();
|
||||
}
|
||||
if (!(operand & (1 << bit))) PC += rel();
|
||||
}
|
||||
|
||||
// CBNE DBNZ
|
||||
// JMP
|
||||
void Spc700::JMP(uint16_t address) { PC = address; }
|
||||
// ---------------------------------------------------------------------------
|
||||
// Jump and Call Instructions
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
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;
|
||||
push_byte((return_address >> 8) & 0xFF);
|
||||
push_byte(return_address & 0xFF);
|
||||
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;
|
||||
push_byte((return_address >> 8) & 0xFF);
|
||||
push_byte(return_address & 0xFF);
|
||||
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;
|
||||
push_byte((return_address >> 8) & 0xFF);
|
||||
push_byte(return_address & 0xFF);
|
||||
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;
|
||||
push_word(PC);
|
||||
push_byte(FlagsToByte(PSW));
|
||||
PSW.I = false;
|
||||
PSW.B = true;
|
||||
PC = read_word(0xFFDE);
|
||||
}
|
||||
|
||||
void Spc700::RET() {
|
||||
uint16_t return_address = read(SP) | (read(SP + 1) << 8);
|
||||
SP += 2;
|
||||
PC = return_address;
|
||||
PC = pull_word();
|
||||
}
|
||||
|
||||
void Spc700::RETI() {
|
||||
uint16_t return_address = read(SP) | (read(SP + 1) << 8);
|
||||
SP += 2;
|
||||
PC = return_address;
|
||||
PSW.I = 1;
|
||||
PSW = ByteToFlags(pull_byte());
|
||||
PC = pull_word();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Stack Instructions
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void Spc700::PUSH(uint8_t operand) {
|
||||
write(SP, operand);
|
||||
SP--;
|
||||
push_byte(operand);
|
||||
}
|
||||
|
||||
void Spc700::POP(uint8_t& operand) {
|
||||
SP++;
|
||||
operand = read(SP);
|
||||
operand = pull_byte();
|
||||
}
|
||||
|
||||
void Spc700::SET1(uint8_t bit, uint8_t& operand) { operand |= (1 << bit); }
|
||||
// ---------------------------------------------------------------------------
|
||||
// Bit Manipulation Instructions
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void Spc700::CLR1(uint8_t bit, uint8_t& operand) { operand &= ~(1 << bit); }
|
||||
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));
|
||||
PSW.C = (operand & (1 << bit)) != 0;
|
||||
operand |= (1 << bit);
|
||||
}
|
||||
|
||||
void Spc700::TCLR1(uint8_t bit, uint8_t& operand) {
|
||||
PSW.C = (operand & (1 << bit));
|
||||
PSW.C = (operand & (1 << bit)) != 0;
|
||||
operand &= ~(1 << bit);
|
||||
}
|
||||
|
||||
void Spc700::AND1(uint8_t bit, uint8_t& operand) {
|
||||
operand &= (1 << bit);
|
||||
PSW.Z = (operand == 0);
|
||||
PSW.N = (operand & 0x80);
|
||||
PSW.N = (operand & 0x80) != 0;
|
||||
}
|
||||
|
||||
void Spc700::OR1(uint8_t bit, uint8_t& operand) {
|
||||
operand |= (1 << bit);
|
||||
PSW.Z = (operand == 0);
|
||||
PSW.N = (operand & 0x80);
|
||||
PSW.N = (operand & 0x80) != 0;
|
||||
}
|
||||
|
||||
void Spc700::EOR1(uint8_t bit, uint8_t& operand) {
|
||||
operand ^= (1 << bit);
|
||||
PSW.Z = (operand == 0);
|
||||
PSW.N = (operand & 0x80);
|
||||
PSW.N = (operand & 0x80) != 0;
|
||||
}
|
||||
|
||||
void Spc700::NOT1(uint8_t bit, uint8_t& operand) {
|
||||
operand ^= (1 << bit);
|
||||
PSW.Z = (operand == 0);
|
||||
PSW.N = (operand & 0x80);
|
||||
PSW.N = (operand & 0x80) != 0;
|
||||
}
|
||||
|
||||
void Spc700::MOV1(uint8_t bit, uint8_t& operand) {
|
||||
PSW.C = (operand & (1 << bit));
|
||||
PSW.C = (operand & (1 << bit)) != 0;
|
||||
operand |= (1 << bit);
|
||||
}
|
||||
|
||||
void Spc700::CLRC() { PSW.C = 0; }
|
||||
|
||||
void Spc700::SETC() { PSW.C = 1; }
|
||||
// ---------------------------------------------------------------------------
|
||||
// Flag Instructions
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void Spc700::CLRC() { PSW.C = false; }
|
||||
void Spc700::SETC() { PSW.C = true; }
|
||||
void Spc700::NOTC() { PSW.C = !PSW.C; }
|
||||
void Spc700::CLRV() { PSW.V = false; PSW.H = false; }
|
||||
void Spc700::CLRP() { PSW.P = false; }
|
||||
void Spc700::SETP() { PSW.P = true; }
|
||||
void Spc700::EI() { PSW.I = true; }
|
||||
void Spc700::DI() { PSW.I = false; }
|
||||
|
||||
void Spc700::CLRV() { PSW.V = 0; }
|
||||
// ---------------------------------------------------------------------------
|
||||
// Special Instructions
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void Spc700::CLRP() { PSW.P = 0; }
|
||||
void Spc700::NOP() {
|
||||
// No operation - PC already advanced by ReadOpcode()
|
||||
}
|
||||
|
||||
void Spc700::SETP() { PSW.P = 1; }
|
||||
void Spc700::SLEEP() {
|
||||
// Sleep mode - handled in ExecuteInstructions
|
||||
}
|
||||
|
||||
void Spc700::EI() { PSW.I = 1; }
|
||||
|
||||
void Spc700::DI() { PSW.I = 0; }
|
||||
|
||||
void Spc700::NOP() { PC++; }
|
||||
|
||||
void Spc700::SLEEP() {}
|
||||
|
||||
void Spc700::STOP() {}
|
||||
void Spc700::STOP() {
|
||||
// Stop mode - handled in ExecuteInstructions
|
||||
}
|
||||
|
||||
} // namespace emu
|
||||
} // namespace yaze
|
||||
|
||||
27
src/app/emu/audio/internal/spc700_accurate_cycles.h
Normal file
27
src/app/emu/audio/internal/spc700_accurate_cycles.h
Normal file
@@ -0,0 +1,27 @@
|
||||
// spc700_accurate_cycles.h - Cycle counts based on https://snes.nesdev.org/wiki/SPC-700_instruction_set
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// Base cycle counts for each SPC700 opcode.
|
||||
// For branching instructions, this is the cost of NOT taking the branch.
|
||||
// Extra cycles for taken branches are added during execution.
|
||||
static const uint8_t spc700_accurate_cycles[256] = {
|
||||
2, 8, 4, 5, 3, 4, 3, 6, 2, 6, 5, 4, 5, 4, 6, 8, // 0x00
|
||||
2, 4, 6, 5, 2, 5, 5, 6, 5, 5, 6, 5, 2, 2, 4, 6, // 0x10
|
||||
2, 8, 4, 5, 3, 4, 3, 6, 2, 6, 5, 4, 5, 4, 5, 4, // 0x20
|
||||
2, 4, 6, 5, 2, 5, 5, 6, 5, 5, 6, 5, 2, 2, 3, 8, // 0x30
|
||||
2, 8, 4, 5, 3, 4, 3, 6, 2, 6, 4, 4, 5, 4, 6, 6, // 0x40
|
||||
2, 4, 6, 5, 2, 5, 5, 6, 4, 5, 5, 5, 2, 2, 4, 3, // 0x50
|
||||
2, 8, 4, 5, 3, 4, 3, 6, 2, 6, 4, 4, 5, 4, 5, 5, // 0x60
|
||||
2, 4, 6, 5, 2, 5, 5, 6, 5, 6, 5, 5, 2, 2, 6, 6, // 0x70
|
||||
2, 8, 4, 5, 3, 4, 3, 6, 2, 6, 5, 4, 5, 4, 4, 8, // 0x80
|
||||
2, 4, 6, 5, 2, 5, 5, 6, 5, 5, 5, 5, 2, 2, 12, 5, // 0x90
|
||||
3, 8, 4, 5, 3, 4, 3, 6, 2, 5, 4, 4, 5, 4, 4, 5, // 0xA0
|
||||
2, 4, 6, 5, 2, 5, 5, 6, 5, 5, 6, 5, 2, 2, 3, 4, // 0xB0
|
||||
3, 8, 4, 5, 4, 5, 4, 7, 2, 5, 6, 4, 5, 4, 9, 8, // 0xC0
|
||||
2, 4, 6, 5, 5, 6, 6, 7, 4, 5, 5, 5, 2, 2, 4, 3, // 0xD0
|
||||
2, 8, 4, 5, 3, 4, 3, 6, 2, 4, 5, 4, 5, 4, 3, 6, // 0xE0
|
||||
2, 4, 6, 5, 4, 5, 5, 6, 3, 5, 4, 5, 2, 2, 4, 2 // 0xF0
|
||||
};
|
||||
@@ -8,14 +8,15 @@
|
||||
#include "app/core/features.h"
|
||||
|
||||
#include "app/emu/audio/internal/opcodes.h"
|
||||
#include "app/emu/audio/internal/spc700_cycles.h"
|
||||
#include "app/emu/audio/internal/spc700_accurate_cycles.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace emu {
|
||||
|
||||
void Spc700::Reset(bool hard) {
|
||||
if (hard) {
|
||||
PC = 0;
|
||||
// DON'T set PC = 0 here! The reset sequence in Step() will load PC from the reset vector.
|
||||
// Setting PC = 0 here would overwrite the correct value loaded from $FFFE-$FFFF.
|
||||
A = 0;
|
||||
X = 0;
|
||||
Y = 0;
|
||||
@@ -38,30 +39,36 @@ int Spc700::Step() {
|
||||
read(0x100 | SP--);
|
||||
callbacks_.idle(false);
|
||||
PSW.I = false;
|
||||
PC = read_word(0xfffe);
|
||||
last_opcode_cycles_ = 8;
|
||||
|
||||
// Load PC from reset vector ($FFFE-$FFFF)
|
||||
uint8_t lo = read(0xfffe);
|
||||
uint8_t hi = read(0xffff);
|
||||
PC = lo | (hi << 8);
|
||||
|
||||
return 8;
|
||||
}
|
||||
|
||||
// Handle stopped state (SLEEP/STOP instructions)
|
||||
if (stopped_) {
|
||||
callbacks_.idle(true);
|
||||
last_opcode_cycles_ = 2;
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Reset extra cycle counter for new instruction
|
||||
extra_cycles_ = 0;
|
||||
|
||||
// Fetch and execute one complete instruction
|
||||
uint8_t opcode = ReadOpcode();
|
||||
|
||||
// Set base cycle count from lookup table
|
||||
// This will be the return value; callbacks during execution will advance APU cycles
|
||||
last_opcode_cycles_ = spc700_cycles[opcode];
|
||||
// Get base cycle count from the new accurate lookup table
|
||||
int cycles = spc700_accurate_cycles[opcode];
|
||||
|
||||
// Execute the instruction completely (atomic execution)
|
||||
// This will set extra_cycles_ if a branch is taken
|
||||
ExecuteInstructions(opcode);
|
||||
|
||||
// Return the number of cycles this instruction consumed
|
||||
return last_opcode_cycles_;
|
||||
// Return the base cycles plus any extra cycles from branching
|
||||
return cycles + extra_cycles_;
|
||||
}
|
||||
|
||||
void Spc700::RunOpcode() {
|
||||
@@ -114,7 +121,7 @@ void Spc700::RunOpcode() {
|
||||
if (bstep == 0) {
|
||||
opcode = ReadOpcode();
|
||||
// Set base cycle count from lookup table
|
||||
last_opcode_cycles_ = spc700_cycles[opcode];
|
||||
last_opcode_cycles_ = spc700_accurate_cycles[opcode];
|
||||
} else {
|
||||
if (spc_exec_count < 5) {
|
||||
LOG_DEBUG("SPC", "Continuing multi-step: PC=$%04X bstep=%d opcode=$%02X", PC, bstep, opcode);
|
||||
@@ -733,10 +740,16 @@ void Spc700::ExecuteInstructions(uint8_t opcode) {
|
||||
CMP(idy());
|
||||
break;
|
||||
}
|
||||
case 0x78: { // cmpm dp, imm
|
||||
uint8_t src = 0;
|
||||
uint16_t dst = dp_imm(&src);
|
||||
CMPM(dst, src);
|
||||
case 0x78: { // cmp d, #i
|
||||
uint8_t imm = ReadOpcode();
|
||||
uint16_t adr = (PSW.P << 8) | ReadOpcode();
|
||||
uint8_t val = read(adr);
|
||||
callbacks_.idle(false); // Add missing cycle
|
||||
callbacks_.idle(false); // Add missing cycle
|
||||
int result = val - imm;
|
||||
PSW.C = (val >= imm);
|
||||
PSW.Z = (result == 0);
|
||||
PSW.N = (result & 0x80);
|
||||
break;
|
||||
}
|
||||
case 0x79: { // cmpm ind, ind
|
||||
@@ -1140,15 +1153,11 @@ void Spc700::ExecuteInstructions(uint8_t opcode) {
|
||||
write(adr, result);
|
||||
break;
|
||||
}
|
||||
case 0xcb: { // movsy dp
|
||||
// CRITICAL: Only call dp() once in bstep=0, reuse saved address in bstep=1
|
||||
if (bstep == 0) {
|
||||
adr = dp(); // Save address for bstep=1
|
||||
}
|
||||
if (adr == 0x00F4 && bstep == 1) {
|
||||
LOG_DEBUG("SPC", "MOVSY writing Y=$%02X to F4 at PC=$%04X", Y, PC);
|
||||
}
|
||||
MOVSY(adr); // Use saved address
|
||||
case 0xcb: { // mov d, Y
|
||||
uint16_t adr = (PSW.P << 8) | ReadOpcode();
|
||||
read(adr);
|
||||
callbacks_.idle(false); // Add one extra cycle delay
|
||||
write(adr, Y);
|
||||
break;
|
||||
}
|
||||
case 0xcc: { // movsy abs
|
||||
@@ -1176,21 +1185,7 @@ void Spc700::ExecuteInstructions(uint8_t opcode) {
|
||||
break;
|
||||
}
|
||||
case 0xd0: { // bne rel
|
||||
switch (step++) {
|
||||
case 1:
|
||||
dat = ReadOpcode();
|
||||
if (PSW.Z) step = 0;
|
||||
break;
|
||||
case 2:
|
||||
callbacks_.idle(false);
|
||||
break;
|
||||
case 3:
|
||||
callbacks_.idle(false);
|
||||
PC += (int8_t)dat;
|
||||
step = 0;
|
||||
break;
|
||||
}
|
||||
// DoBranch(ReadOpcode(), !PSW.Z);
|
||||
DoBranch(ReadOpcode(), !PSW.Z);
|
||||
break;
|
||||
}
|
||||
case 0xd4: { // movs dpx
|
||||
@@ -1274,43 +1269,62 @@ void Spc700::ExecuteInstructions(uint8_t opcode) {
|
||||
PSW.H = false;
|
||||
break;
|
||||
}
|
||||
case 0xe4: { // mov dp
|
||||
MOV(dp());
|
||||
case 0xe4: { // mov A, dp
|
||||
uint16_t adr = (PSW.P << 8) | ReadOpcode();
|
||||
A = read(adr);
|
||||
PSW.Z = (A == 0);
|
||||
PSW.N = (A & 0x80);
|
||||
break;
|
||||
}
|
||||
case 0xe5: { // mov abs
|
||||
MOV(abs());
|
||||
case 0xe5: { // mov A, abs
|
||||
uint16_t adr = ReadOpcodeWord();
|
||||
A = read(adr);
|
||||
PSW.Z = (A == 0);
|
||||
PSW.N = (A & 0x80);
|
||||
break;
|
||||
}
|
||||
case 0xe6: { // mov ind
|
||||
MOV(ind());
|
||||
case 0xe6: { // mov A, (X)
|
||||
uint16_t adr = X;
|
||||
A = read(adr);
|
||||
PSW.Z = (A == 0);
|
||||
PSW.N = (A & 0x80);
|
||||
break;
|
||||
}
|
||||
case 0xe7: { // mov idx
|
||||
MOV(idx());
|
||||
case 0xe7: { // mov A, [dp+X]
|
||||
uint16_t dp_adr = (PSW.P << 8) | ReadOpcode();
|
||||
callbacks_.idle(false);
|
||||
uint16_t adr = read_word(dp_adr + X);
|
||||
A = read(adr);
|
||||
PSW.Z = (A == 0);
|
||||
PSW.N = (A & 0x80);
|
||||
break;
|
||||
}
|
||||
case 0xe8: { // mov imm
|
||||
MOV(imm());
|
||||
case 0xe8: { // mov A, #imm
|
||||
A = ReadOpcode();
|
||||
PSW.Z = (A == 0);
|
||||
PSW.N = (A & 0x80);
|
||||
break;
|
||||
}
|
||||
case 0xe9: { // movx abs
|
||||
MOVX(abs());
|
||||
break;
|
||||
}
|
||||
case 0xea: { // not1 abs.bit
|
||||
uint16_t adr = 0;
|
||||
uint8_t bit = abs_bit(&adr);
|
||||
uint8_t result = read(adr) ^ (1 << bit);
|
||||
write(adr, result);
|
||||
uint16_t adr = ReadOpcodeWord();
|
||||
X = read(adr);
|
||||
PSW.Z = (X == 0);
|
||||
PSW.N = (X & 0x80);
|
||||
break;
|
||||
}
|
||||
case 0xeb: { // movy dp
|
||||
MOVY(dp());
|
||||
uint16_t adr = (PSW.P << 8) | ReadOpcode();
|
||||
callbacks_.idle(false); // Add missing cycle
|
||||
Y = read(adr);
|
||||
PSW.Z = (Y == 0);
|
||||
PSW.N = (Y & 0x80);
|
||||
break;
|
||||
}
|
||||
case 0xec: { // movy abs
|
||||
MOVY(abs());
|
||||
uint16_t adr = ReadOpcodeWord();
|
||||
Y = read(adr);
|
||||
PSW.Z = (Y == 0);
|
||||
PSW.N = (Y & 0x80);
|
||||
break;
|
||||
}
|
||||
case 0xed: { // notc imp
|
||||
|
||||
@@ -82,6 +82,7 @@ class Spc700 {
|
||||
uint8_t dat;
|
||||
uint16_t dat16;
|
||||
uint8_t param;
|
||||
int extra_cycles_ = 0;
|
||||
|
||||
// Cycle tracking for accurate APU synchronization
|
||||
int last_opcode_cycles_ = 0;
|
||||
@@ -171,11 +172,13 @@ class Spc700 {
|
||||
}
|
||||
|
||||
void DoBranch(uint8_t value, bool check) {
|
||||
callbacks_.idle(false); // Add missing base cycle for all branches
|
||||
if (check) {
|
||||
// taken branch: 2 extra cycles
|
||||
callbacks_.idle(false);
|
||||
callbacks_.idle(false);
|
||||
PC += (int8_t)value;
|
||||
extra_cycles_ = 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -160,20 +160,34 @@ void Cpu::RunOpcode() {
|
||||
}
|
||||
|
||||
// LoadSongBank routine ($8888-$88FF) - This is where handshake happens!
|
||||
// LOGIC: Track CPU's journey through audio initialization to identify where it gets stuck.
|
||||
// We log key waypoints to understand if CPU reaches handshake write instructions.
|
||||
if (cur_pc >= 0x8888 && cur_pc <= 0x88FF) {
|
||||
// Log entry
|
||||
if (cur_pc == 0x8888) {
|
||||
LOG_INFO("CPU_AUDIO", ">>> LoadSongBank ENTRY at $8888! A=$%02X X=$%04X",
|
||||
LOG_INFO("CPU_AUDIO", ">>> LoadSongBank ENTRY at $8888! A=$%02X X=$%04X",
|
||||
A & 0xFF, X);
|
||||
}
|
||||
|
||||
// Log handshake initiation ($88A0-$88B0 area writes $CC to F4)
|
||||
if (cur_pc >= 0x88A0 && cur_pc <= 0x88B0 && !logged_routines[cur_pc]) {
|
||||
LOG_INFO("CPU_AUDIO", "Handshake setup: PC=$%04X A=$%02X", cur_pc, A & 0xFF);
|
||||
|
||||
// DISCOVERY: Log every unique PC in this range to see the execution path
|
||||
// This helps identify if CPU is looping, stuck, or simply not reaching write instructions
|
||||
static int exec_count_8888 = 0;
|
||||
if (exec_count_8888++ < 100 && !logged_routines[cur_pc]) {
|
||||
LOG_INFO("CPU_AUDIO", " LoadSongBank: PC=$%04X A=$%02X X=$%04X Y=$%04X SP=$%04X [exec #%d]",
|
||||
cur_pc, A & 0xFF, X, Y, SP(), exec_count_8888);
|
||||
logged_routines[cur_pc] = true;
|
||||
}
|
||||
|
||||
|
||||
// Log handshake initiation ($88A0-$88B0 area writes $CC to F4)
|
||||
if (cur_pc >= 0x88A0 && cur_pc <= 0x88B0) {
|
||||
static int setup_count = 0;
|
||||
if (setup_count++ < 20) {
|
||||
LOG_INFO("CPU_AUDIO", "Handshake setup area: PC=$%04X A=$%02X", cur_pc, A & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
// Log handshake wait loop
|
||||
// LOGIC: If we see these addresses, CPU is waiting for APU response
|
||||
static int handshake_log_count = 0;
|
||||
if (cur_pc == 0x88B3 || cur_pc == 0x88B6) {
|
||||
if (handshake_log_count++ < 20 || handshake_log_count % 500 == 0) {
|
||||
|
||||
Reference in New Issue
Block a user