- [FIX] Opcode 0x28 (PLP) usava la microinstrucció miPushP en compte de la correcta miPullP.

- [FIX Opcode 0x38 estaba etiquetat com a ESC, en compte del correcte SEC.
- [FIX] Relative branching es feia incorrectament.
- [FIX] Afegida emulació del hardware bug en opcode 0x6c (indirect jump).
- [FIX] registres de 16 bits implementats amb funcions explicites per a evitar errors de aliasing (puto C++).
- [FIX] Implementats els modes BCD en ADC i SBC
- [FIX] Arreglat el càlcul del signe al creuar una pàgina de memòria.
This commit is contained in:
Raimon @ quifisraimon
2026-09-08 14:01:19 +02:00
parent 111fe32bb9
commit 34877060c0
+138 -61
View File
@@ -15,45 +15,37 @@ namespace m6502
{
uint8_t interrupt_vector = 0xfe;
uint8_t regs[13];
uint8_t rA;
uint8_t rX;
uint8_t rY;
uint8_t rS;
uint8_t rP;
uint8_t rPC_lo;
uint8_t rPC_hi;
//#define rPC (*_rPC)
uint8_t *_rA = &regs[0];
uint8_t *_rX = &regs[1];
uint8_t *_rY = &regs[2];
uint8_t *_rS = &regs[3];
uint8_t *_rP = &regs[4];
uint8_t *_rPC_lo = &regs[5];
uint8_t *_rPC_hi = &regs[6];
uint16_t *_rPC = (uint16_t*)&regs[5];
uint8_t rB;
uint8_t rT;
uint8_t rI;
uint8_t rAD_lo;
uint8_t rAD_hi;
//#define rAD (*_rAD)
uint8_t rTEMP_lo;
uint8_t rTEMP_hi;
//#define rTEMP (*_rTEMP)
uint8_t *_rB = &regs[7];
uint8_t *_rT = &regs[8];
uint8_t *_rI = &regs[9];
uint8_t *_rAD_lo = &regs[10];
uint8_t *_rAD_hi = &regs[11];
uint16_t *_rAD = (uint16_t*)&regs[10];
uint8_t *_rTEMP_lo = &regs[12];
uint8_t *_rTEMP_hi = &regs[13];
uint16_t *_rTEMP = (uint16_t*)&regs[12];
// Explicit 16-bit accessors prevent UB and stay host-endian safe
inline uint16_t get_PC() { return (uint16_t)rPC_lo | ((uint16_t)rPC_hi << 8); }
inline void set_PC(uint16_t val) { rPC_lo = val & 0xFF; rPC_hi = val >> 8; }
inline uint16_t inc_PC() { if (++rPC_lo == 0) rPC_hi++; return get_PC(); } // Natural 16-bit bump
#define rA (*_rA)
#define rX (*_rX)
#define rY (*_rY)
#define rS (*_rS)
#define rP (*_rP)
#define rPC_lo (*_rPC_lo)
#define rPC_hi (*_rPC_hi)
#define rPC (*_rPC)
inline uint16_t get_AD() { return (uint16_t)rAD_lo | ((uint16_t)rAD_hi << 8); }
inline void set_AD(uint16_t val) { rAD_lo = val & 0xFF; rAD_hi = val >> 8; }
inline void inc_AD() { if (++rAD_lo == 0) rAD_hi++; } // Natural 16-bit bump
#define rB (*_rB)
#define rT (*_rT)
#define rI (*_rI)
#define rAD_lo (*_rAD_lo)
#define rAD_hi (*_rAD_hi)
#define rAD (*_rAD)
#define rTEMP_lo (*_rTEMP_lo)
#define rTEMP_hi (*_rTEMP_hi)
#define rTEMP (*_rTEMP)
inline uint16_t get_TEMP() { return (uint16_t)rTEMP_lo | ((uint16_t)rTEMP_hi << 8); }
inline void set_TEMP(uint16_t val) { rTEMP_lo = val & 0xFF; rTEMP_hi = val >> 8; }
inline void inc_TEMP() { if (++rTEMP_lo == 0) rTEMP_hi++; } // Natural 16-bit bump
uint8_t microcode[256];
uint8_t microcode_pos = 0;
@@ -136,7 +128,7 @@ namespace m6502
/* 0x25 AND zpg */ { miFetchOperandLo, miReadAddress, miFetchOpcode },
/* 0x26 ROL zpg */ { miFetchOperandLo, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode },
/* 0x27 --- */ { miFetchOpcode },
/* 0x28 PLP */ { miFetchOperandLo, miPushP, miFetchOpcode },
/* 0x28 PLP */ { miFetchOperandLo, miPullP, miFetchOpcode },
/* 0x29 AND imm */ { miFetchOperandLo, miFetchOpcode },
/* 0x2A ROL A */ { miFakeFetchOperand, miFetchOpcode },
/* 0x2B --- */ { miFetchOpcode },
@@ -153,7 +145,7 @@ namespace m6502
/* 0x35 AND zpg,X */ { miFetchOperandLo, miIndexX, miReadAddress, miFetchOpcode },
/* 0x36 ROL zpg,X */ { miFetchOperandLo, miIndexX, miReadAddress, miWriteAddress, miWriteAddress, miFetchOpcode },
/* 0x37 --- */ { miFetchOpcode },
/* 0x38 ESC */ { miFetchOperandLo, miFetchOpcode },
/* 0x38 SEC */ { miFetchOperandLo, miFetchOpcode },
/* 0x39 AND abs,Y */ { miFetchOperandLo, miFetchOperandHiAndIndexY, miReadAddressAndSkip, miReadAddress, miFetchOpcode },
/* 0x3A --- */ { miFetchOpcode },
/* 0x3B --- */ { miFetchOpcode },
@@ -375,6 +367,61 @@ namespace m6502
if (flags&fV) rP = ( (rTEMP_lo ^ rA) & (rTEMP_lo ^ rB) & 0x80 ) ? rP | fV : rP & ~fV;
}
void DoDecimalADC()
{
uint8_t c_in = (rP & fC) ? 1 : 0;
uint8_t low = (rA & 0x0F) + (rB & 0x0F) + c_in;
if (low > 9) low += 6;
uint8_t high = (rA >> 4) + (rB >> 4) + (low > 0x0F ? 1 : 0);
// Set Zero and Negative flags based on BINARY addition result (NMOS 6502 accurate)
uint16_t bin_sum = (uint16_t)rA + rB + c_in;
rTEMP_lo = bin_sum & 0xFF;
if (~(rA ^ rB) & (rA ^ rTEMP_lo) & 0x80) rP |= fV; else rP &= ~fV;
SetFlags(fN | fZ);
if (high > 9) high += 6;
rP = (high > 15) ? (rP | fC) : (rP & ~fC);
rA = ((high << 4) | (low & 0x0F)) & 0xFF;
}
void DoDecimalSBC()
{
uint8_t c_in = (rP & fC) ? 1 : 0;
uint16_t bin_diff = (uint16_t)rA - rB - (1 - c_in);
int low = (rA & 0x0F) - (rB & 0x0F) - (1 - c_in);
int high = (rA >> 4) - (rB >> 4);
if (low < 0) { low -= 6; high--; }
if (high < 0) high -= 6;
rTEMP_lo = bin_diff & 0xFF;
if ((rA ^ rB) & (rA ^ rTEMP_lo) & 0x80) rP |= fV; else rP &= ~fV;
if (!(bin_diff & 0x100)) rP |= fC; else rP &= ~fC;
SetFlags(fN | fZ);
rA = ((high << 4) | (low & 0x0F)) & 0xFF;
}
void DoBinaryADC()
{
uint8_t c_in = (rP & fC) ? 1 : 0;
uint16_t sum = (uint16_t)rA + rB + c_in;
rTEMP_lo = sum & 0xFF;
if (sum > 0xFF) rP |= fC; else rP &= ~fC;
if (~(rA ^ rB) & (rA ^ rTEMP_lo) & 0x80) rP |= fV; else rP &= ~fV;
SetFlags(fN | fZ);
rA = rTEMP_lo;
}
void DoBinarySBC()
{
uint8_t tmp = rB;
rB ^= 0xFF;
DoBinaryADC();
rB = tmp;
}
void DoOpcodeWork()
{
if (rI==0xCA) { rX--; rB = rX; SetFlags(fN|fZ); return; } /* DEX */
@@ -405,21 +452,27 @@ namespace m6502
/* LDY */ rTEMP_lo = rB; SetFlags(fN|fZ); rY = rTEMP_lo; break;
} else if ( ((rI&0xD3)==0xC0) && (((rI>>2)&3)!=2) ) {
if ( (rI&0xf0)==0xC0 ) {
/* CPY */ rTEMP = rY + ~rB; SetFlags(fN|fZ|fC); break;
/* CPY */ set_TEMP(rY + ~rB); SetFlags(fN|fZ|fC); break;
} else {
/* CPX */ rTEMP = rX + ~rB; SetFlags(fN|fZ|fC); break;
/* CPX */ set_TEMP(rX + ~rB); SetFlags(fN|fZ|fC); break;
}
}
break;
case 1: /* ALU */
switch ((rI>>5)&0x07) {
case 0: /* ORA */ rTEMP_lo = rA | rB; SetFlags(fN|fZ); rA = rTEMP_lo; break;
case 1: /* AND */ rTEMP_lo = rA & rB; SetFlags(fN|fZ); rA = rTEMP_lo; break;
case 2: /* EOR */ rTEMP_lo = rA ^ rB; SetFlags(fN|fZ); rA = rTEMP_lo; break;
case 3: /* ADC */ rTEMP = rA + rB + fC; SetFlags(fN|fV|fZ|fC); rA = rTEMP_lo; break;
case 7: /* SBC */ if (rP & fD) DoDecimalSBC(); else DoBinarySBC(); break;
case 3: /* ADC */ if (rP & fD) DoDecimalADC(); else DoBinaryADC(); break;
case 5: /* LDA */ rTEMP_lo = rB; SetFlags(fN|fZ); rA = rTEMP_lo; break;
case 6: /* CMP */ rTEMP = rA + ~rB; SetFlags(fN|fZ|fC); break;
case 7: /* SBC */ rTEMP = rA + ~rB + fC; SetFlags(fN|fV|fZ|fC); rA = rTEMP_lo; break;
case 6: /* CMP */
uint16_t diff = (uint16_t)rA - rB;
rP = (diff < 0x100) ? (rP | fC) : (rP & ~fC);
rTEMP_lo = diff & 0xFF;
SetFlags(fN|fZ);
break;
};
if ( (rI==0x24) || (rI==0x2C) ) /* BIT */ { rTEMP_lo = rA & rB; rP = (rP&~(fN|fV)) | (rB&(fN|fV)); SetFlags(fZ); }
break;
@@ -467,15 +520,15 @@ namespace m6502
void FetchOpcode()
{
DoOpcodeWork();
rI = mem::read(rPC++);
rI = mem::read(get_PC()); inc_PC();
int i=0; do { microcode[microcode_last++] = instructions[rI][i++]; } while (instructions[rI][i-1] != miFetchOpcode);
}
void FakeFetchOperand() { rAD_lo = mem::read(rPC); }
void FetchOperandLo() { rTEMP = rAD_lo = rB = mem::read(rPC++); }
void FetchOperandHi() { rAD_hi = mem::read(rPC++); }
void FetchOperandHiAndIndexX() { rAD_hi = mem::read(rPC++); rTEMP = rAD_lo + rX; rAD_lo = rTEMP_lo; }
void FetchOperandHiAndIndexY() { rAD_hi = mem::read(rPC++); rTEMP = rAD_lo + rY; rAD_lo = rTEMP_lo; }
void FakeFetchOperand() { rAD_lo = mem::read(get_PC()); }
void FetchOperandLo() { rAD_lo = rB = mem::read(get_PC()); inc_PC(); set_TEMP(rB); }
void FetchOperandHi() { rAD_hi = mem::read(get_PC()); inc_PC(); }
void FetchOperandHiAndIndexX() { rAD_hi = mem::read(get_PC()); inc_PC(); set_TEMP(rAD_lo + rX); rAD_lo = rTEMP_lo; }
void FetchOperandHiAndIndexY() { rAD_hi = mem::read(get_PC()); inc_PC(); set_TEMP(rAD_lo + rY); rAD_lo = rTEMP_lo; }
void PushPCHi() { mem::write(0x0100+(rS--), rPC_hi); }
void PushPCLo() { mem::write(0x0100+(rS--), rPC_lo); }
@@ -483,27 +536,51 @@ namespace m6502
void PCLoInt() { rPC_lo = mem::read(0xff00+interrupt_vector); }
void PCHiInt() { rPC_hi = mem::read(0xff00+interrupt_vector+1); }
void IndexX() { rB = mem::read(rAD); rAD_lo += rX; }
void FetchAddressLo() { rT = mem::read(rAD++); }
void FetchAddressHi() { rAD_hi = mem::read(rAD); rAD_lo = rT; }
void FetchAddressHiAndIndex() { rAD_hi = mem::read(rAD); rTEMP = rAD_lo + rY; rAD_lo = rTEMP_lo; }
void ReadAddress() { rB = mem::read(rAD); rAD_hi += rTEMP_hi; }
void IndexX() { rB = mem::read(get_AD()); rAD_lo += rX; }
void FetchAddressLo() { rT = mem::read(get_AD()); inc_AD(); }
void FetchAddressHi() { rAD_hi = mem::read(get_AD()); rAD_lo = rT; }
void FetchAddressHiAndIndex() { rAD_hi = mem::read(get_AD()); set_TEMP(rAD_lo + rY); rAD_lo = rTEMP_lo; }
void ReadAddress() { rB = mem::read(get_AD()); rAD_hi += rTEMP_hi; }
void ReadAddressAndSkip() { rB = mem::read(rAD); rAD_hi += rTEMP_hi; if (rTEMP_hi) InsertFetchOpcode(); }
void WriteRegister() { mem::write(rAD, (rI&3)==1 ? rA : (rI&3)==2 ? rX : rY ); }
void WriteAddress() { mem::write(rAD, rB); DoRMW(); }
void ReadAddressAndSkip() { rB = mem::read(get_AD()); rAD_hi += rTEMP_hi; if (rTEMP_hi) InsertFetchOpcode(); }
void WriteRegister() { mem::write(get_AD(), (rI&3)==1 ? rA : (rI&3)==2 ? rX : rY ); }
void WriteAddress() { mem::write(get_AD(), rB); DoRMW(); }
void PushA() { mem::write(0x0100+(rS--), rA); }
void FetchOperandAndCheckBranch() { FetchOperandLo(); if (!BranchCondition()) InsertFetchOpcode(); else { rT = rPC_hi; rPC = rPC + (int8_t)rB; } }
void FetchOperandAndCheckBranch() {
int8_t offset = (int8_t)mem::read(get_PC()); inc_PC();
rB = (uint8_t)offset;
if (!BranchCondition()) {
InsertFetchOpcode();
return;
}
set_TEMP(rPC_lo + offset);
rT = rPC_hi;
rPC_lo = rTEMP_lo;
}
void CheckIfPageCrossed() {
FakeFetchOperand();
int8_t offset = (int8_t)rB;
if (offset >= 0 && rTEMP_hi != 0) {
rPC_hi++;
} else if (offset < 0 && rTEMP_hi == 0) {
rPC_hi--;
} else {
InsertFetchOpcode();
}
}
void CheckIfPageCrossed() { FakeFetchOperand(); if (rPC_hi == rT) InsertFetchOpcode(); }
void PullPCHi() { rPC_hi = mem::read(0x0100+(++rS)); }
void PullPCLo() { rPC_lo = mem::read(0x0100+(++rS)); }
void PullP() { rP = mem::read(0x0100+(++rS)); }
void PullA() { rA = mem::read(0x0100+(++rS)); }
void IncrementPC() { rPC++; }
void FetchAddressHiToPC() { rPC_hi = mem::read(rAD); rPC_lo = rT; }
void IndexY() { rB = mem::read(rAD); rAD_lo += rY; }
void IncrementPC() { inc_PC(); }
void FetchAddressHiToPC() { uint16_t high_addr = (get_AD() & 0xFF00) | ((get_AD() + 1) & 0x00FF); rPC_hi = mem::read(high_addr); rPC_lo = rT; }
void IndexY() { rB = mem::read(get_AD()); rAD_lo += rY; }
void (*microinstructions[miNumMicroinstructions])(void) {
FetchOpcode, FakeFetchOperand, FetchOperandLo, FetchOperandHi, FetchOperandHiAndIndexX, FetchOperandHiAndIndexY,