diff --git a/6502m.cpp b/6502m.cpp index 3edd686..b33db75 100644 --- a/6502m.cpp +++ b/6502m.cpp @@ -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 = ®s[0]; - uint8_t *_rX = ®s[1]; - uint8_t *_rY = ®s[2]; - uint8_t *_rS = ®s[3]; - uint8_t *_rP = ®s[4]; - uint8_t *_rPC_lo = ®s[5]; - uint8_t *_rPC_hi = ®s[6]; - uint16_t *_rPC = (uint16_t*)®s[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 = ®s[7]; - uint8_t *_rT = ®s[8]; - uint8_t *_rI = ®s[9]; - uint8_t *_rAD_lo = ®s[10]; - uint8_t *_rAD_hi = ®s[11]; - uint16_t *_rAD = (uint16_t*)®s[10]; - uint8_t *_rTEMP_lo = ®s[12]; - uint8_t *_rTEMP_hi = ®s[13]; - uint16_t *_rTEMP = (uint16_t*)®s[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,