#undef DPCM_SYNCCLOCK using System; using VirtualNes.Core.Debug; namespace VirtualNes.Core { public class CPU { private static int nmicount; // 6502 status flags public const byte C_FLAG = 0x01; // 1: Carry public const byte Z_FLAG = 0x02; // 1: Zero public const byte I_FLAG = 0x04; // 1: Irq disabled public const byte D_FLAG = 0x08; // 1: Decimal mode flag (NES unused) public const byte B_FLAG = 0x10; // 1: Break public const byte R_FLAG = 0x20; // 1: Reserved (Always 1) public const byte V_FLAG = 0x40; // 1: Overflow public const byte N_FLAG = 0x80; // 1: Negative // Interrupt public const byte NMI_FLAG = 0x01; public const byte IRQ_FLAG = 0x02; public const byte IRQ_FRAMEIRQ = 0x04; public const byte IRQ_DPCM = 0x08; public const byte IRQ_MAPPER = 0x10; public const byte IRQ_MAPPER2 = 0x20; public const byte IRQ_TRIGGER = 0x40; // one shot(媽IRQ()) public const byte IRQ_TRIGGER2 = 0x80; // one shot(媽IRQ_NotPending()) public static readonly byte IRQ_MASK = unchecked((byte)(~(NMI_FLAG | IRQ_FLAG))); // Vector public const ushort NMI_VECTOR = 0xFFFA; public const ushort RES_VECTOR = 0xFFFC; public const ushort IRQ_VECTOR = 0xFFFE; private NES nes; private bool m_bClockProcess; private int TOTAL_cycles; private int DMA_cycles; private Mapper mapper; private APU apu; internal R6502 R = new R6502(); private byte[] ZN_Table = new byte[256]; private ArrayRef STACK; public CPU(NES parent) { nes = parent; m_bClockProcess = false; } public void Dispose() { } ushort EA = 0; ushort ET = 0; ushort WT = 0; byte DT = 0; int exec_cycles = 0; internal long EXEC(int request_cycles) { byte opcode = 0; int OLD_cycles = TOTAL_cycles; byte nmi_request = 0, irq_request = 0; bool bClockProcess = m_bClockProcess; exec_cycles = 0; EA = 0; ET = 0; WT = 0; DT = 0; while (request_cycles > 0) { exec_cycles = 0; if (DMA_cycles > 0) { if (request_cycles <= DMA_cycles) { DMA_cycles -= request_cycles; TOTAL_cycles += request_cycles; mapper.Clock(request_cycles); #if DPCM_SYNCCLOCK apu.SyncDPCM(request_cycles); #endif if (m_bClockProcess) { nes.Clock(request_cycles); } goto _execute_exit; } else { exec_cycles += DMA_cycles; DMA_cycles = 0; } } nmi_request = irq_request = 0; opcode = OP6502(R.PC++); if (R.INT_pending != 0) { if ((R.INT_pending & NMI_FLAG) != 0) { nmi_request = 0xFF; byte temp = unchecked((byte)(~NMI_FLAG)); R.INT_pending &= temp; } else if ((R.INT_pending & IRQ_MASK) != 0) { byte temp = unchecked((byte)(~IRQ_TRIGGER2)); R.INT_pending &= temp; if ( ((R.P & I_FLAG) == 0) && (opcode != 0x40) ) { irq_request = 0xFF; temp = unchecked((byte)(~IRQ_TRIGGER)); R.INT_pending &= temp; } } } switch (opcode) { case 0x69: // ADC #$?? MR_IM(); ADC(); ADD_CYCLE(2); break; case 0x65: // ADC $?? MR_ZP(); ADC(); ADD_CYCLE(3); break; case 0x75: // ADC $??,X MR_ZX(); ADC(); ADD_CYCLE(4); break; case 0x6D: // ADC $???? MR_AB(); ADC(); ADD_CYCLE(4); break; case 0x7D: // ADC $????,X MR_AX(); ADC(); CHECK_EA(); ADD_CYCLE(4); break; case 0x79: // ADC $????,Y MR_AY(); ADC(); CHECK_EA(); ADD_CYCLE(4); break; case 0x61: // ADC ($??,X) MR_IX(); ADC(); ADD_CYCLE(6); break; case 0x71: // ADC ($??),Y MR_IY(); ADC(); CHECK_EA(); ADD_CYCLE(4); break; case 0xE9: // SBC #$?? MR_IM(); SBC(); ADD_CYCLE(2); break; case 0xE5: // SBC $?? MR_ZP(); SBC(); ADD_CYCLE(3); break; case 0xF5: // SBC $??,X MR_ZX(); SBC(); ADD_CYCLE(4); break; case 0xED: // SBC $???? MR_AB(); SBC(); ADD_CYCLE(4); break; case 0xFD: // SBC $????,X MR_AX(); SBC(); CHECK_EA(); ADD_CYCLE(4); break; case 0xF9: // SBC $????,Y MR_AY(); SBC(); CHECK_EA(); ADD_CYCLE(4); break; case 0xE1: // SBC ($??,X) MR_IX(); SBC(); ADD_CYCLE(6); break; case 0xF1: // SBC ($??),Y MR_IY(); SBC(); CHECK_EA(); ADD_CYCLE(5); break; case 0xC6: // DEC $?? MR_ZP(); DEC(); MW_ZP(); ADD_CYCLE(5); break; case 0xD6: // DEC $??,X MR_ZX(); DEC(); MW_ZP(); ADD_CYCLE(6); break; case 0xCE: // DEC $???? MR_AB(); DEC(); MW_EA(); ADD_CYCLE(6); break; case 0xDE: // DEC $????,X MR_AX(); DEC(); MW_EA(); ADD_CYCLE(7); break; case 0xCA: // DEX DEX(); ADD_CYCLE(2); break; case 0x88: // DEY DEY(); ADD_CYCLE(2); break; case 0xE6: // INC $?? MR_ZP(); INC(); MW_ZP(); ADD_CYCLE(5); break; case 0xF6: // INC $??,X MR_ZX(); INC(); MW_ZP(); ADD_CYCLE(6); break; case 0xEE: // INC $???? MR_AB(); INC(); MW_EA(); ADD_CYCLE(6); break; case 0xFE: // INC $????,X MR_AX(); INC(); MW_EA(); ADD_CYCLE(7); break; case 0xE8: // INX INX(); ADD_CYCLE(2); break; case 0xC8: // INY INY(); ADD_CYCLE(2); break; case 0x29: // AND #$?? MR_IM(); AND(); ADD_CYCLE(2); break; case 0x25: // AND $?? MR_ZP(); AND(); ADD_CYCLE(3); break; case 0x35: // AND $??,X MR_ZX(); AND(); ADD_CYCLE(4); break; case 0x2D: // AND $???? MR_AB(); AND(); ADD_CYCLE(4); break; case 0x3D: // AND $????,X MR_AX(); AND(); CHECK_EA(); ADD_CYCLE(4); break; case 0x39: // AND $????,Y MR_AY(); AND(); CHECK_EA(); ADD_CYCLE(4); break; case 0x21: // AND ($??,X) MR_IX(); AND(); ADD_CYCLE(6); break; case 0x31: // AND ($??),Y MR_IY(); AND(); CHECK_EA(); ADD_CYCLE(5); break; case 0x0A: // ASL A ASL_A(); ADD_CYCLE(2); break; case 0x06: // ASL $?? MR_ZP(); ASL(); MW_ZP(); ADD_CYCLE(5); break; case 0x16: // ASL $??,X MR_ZX(); ASL(); MW_ZP(); ADD_CYCLE(6); break; case 0x0E: // ASL $???? MR_AB(); ASL(); MW_EA(); ADD_CYCLE(6); break; case 0x1E: // ASL $????,X MR_AX(); ASL(); MW_EA(); ADD_CYCLE(7); break; case 0x24: // BIT $?? MR_ZP(); BIT(); ADD_CYCLE(3); break; case 0x2C: // BIT $???? MR_AB(); BIT(); ADD_CYCLE(4); break; case 0x49: // EOR #$?? MR_IM(); EOR(); ADD_CYCLE(2); break; case 0x45: // EOR $?? MR_ZP(); EOR(); ADD_CYCLE(3); break; case 0x55: // EOR $??,X MR_ZX(); EOR(); ADD_CYCLE(4); break; case 0x4D: // EOR $???? MR_AB(); EOR(); ADD_CYCLE(4); break; case 0x5D: // EOR $????,X MR_AX(); EOR(); CHECK_EA(); ADD_CYCLE(4); break; case 0x59: // EOR $????,Y MR_AY(); EOR(); CHECK_EA(); ADD_CYCLE(4); break; case 0x41: // EOR ($??,X) MR_IX(); EOR(); ADD_CYCLE(6); break; case 0x51: // EOR ($??),Y MR_IY(); EOR(); CHECK_EA(); ADD_CYCLE(5); break; case 0x4A: // LSR A LSR_A(); ADD_CYCLE(2); break; case 0x46: // LSR $?? MR_ZP(); LSR(); MW_ZP(); ADD_CYCLE(5); break; case 0x56: // LSR $??,X MR_ZX(); LSR(); MW_ZP(); ADD_CYCLE(6); break; case 0x4E: // LSR $???? MR_AB(); LSR(); MW_EA(); ADD_CYCLE(6); break; case 0x5E: // LSR $????,X MR_AX(); LSR(); MW_EA(); ADD_CYCLE(7); break; case 0x09: // ORA #$?? MR_IM(); ORA(); ADD_CYCLE(2); break; case 0x05: // ORA $?? MR_ZP(); ORA(); ADD_CYCLE(3); break; case 0x15: // ORA $??,X MR_ZX(); ORA(); ADD_CYCLE(4); break; case 0x0D: // ORA $???? MR_AB(); ORA(); ADD_CYCLE(4); break; case 0x1D: // ORA $????,X MR_AX(); ORA(); CHECK_EA(); ADD_CYCLE(4); break; case 0x19: // ORA $????,Y MR_AY(); ORA(); CHECK_EA(); ADD_CYCLE(4); break; case 0x01: // ORA ($??,X) MR_IX(); ORA(); ADD_CYCLE(6); break; case 0x11: // ORA ($??),Y MR_IY(); ORA(); CHECK_EA(); ADD_CYCLE(5); break; case 0x2A: // ROL A ROL_A(); ADD_CYCLE(2); break; case 0x26: // ROL $?? MR_ZP(); ROL(); MW_ZP(); ADD_CYCLE(5); break; case 0x36: // ROL $??,X MR_ZX(); ROL(); MW_ZP(); ADD_CYCLE(6); break; case 0x2E: // ROL $???? MR_AB(); ROL(); MW_EA(); ADD_CYCLE(6); break; case 0x3E: // ROL $????,X MR_AX(); ROL(); MW_EA(); ADD_CYCLE(7); break; case 0x6A: // ROR A ROR_A(); ADD_CYCLE(2); break; case 0x66: // ROR $?? MR_ZP(); ROR(); MW_ZP(); ADD_CYCLE(5); break; case 0x76: // ROR $??,X MR_ZX(); ROR(); MW_ZP(); ADD_CYCLE(6); break; case 0x6E: // ROR $???? MR_AB(); ROR(); MW_EA(); ADD_CYCLE(6); break; case 0x7E: // ROR $????,X MR_AX(); ROR(); MW_EA(); ADD_CYCLE(7); break; case 0xA9: // LDA #$?? MR_IM(); LDA(); ADD_CYCLE(2); break; case 0xA5: // LDA $?? MR_ZP(); LDA(); ADD_CYCLE(3); break; case 0xB5: // LDA $??,X MR_ZX(); LDA(); ADD_CYCLE(4); break; case 0xAD: // LDA $???? MR_AB(); LDA(); ADD_CYCLE(4); break; case 0xBD: // LDA $????,X MR_AX(); LDA(); CHECK_EA(); ADD_CYCLE(4); break; case 0xB9: // LDA $????,Y MR_AY(); LDA(); CHECK_EA(); ADD_CYCLE(4); break; case 0xA1: // LDA ($??,X) MR_IX(); LDA(); ADD_CYCLE(6); break; case 0xB1: // LDA ($??),Y MR_IY(); LDA(); CHECK_EA(); ADD_CYCLE(5); break; case 0xA2: // LDX #$?? MR_IM(); LDX(); ADD_CYCLE(2); break; case 0xA6: // LDX $?? MR_ZP(); LDX(); ADD_CYCLE(3); break; case 0xB6: // LDX $??,Y MR_ZY(); LDX(); ADD_CYCLE(4); break; case 0xAE: // LDX $???? MR_AB(); LDX(); ADD_CYCLE(4); break; case 0xBE: // LDX $????,Y MR_AY(); LDX(); CHECK_EA(); ADD_CYCLE(4); break; case 0xA0: // LDY #$?? MR_IM(); LDY(); ADD_CYCLE(2); break; case 0xA4: // LDY $?? MR_ZP(); LDY(); ADD_CYCLE(3); break; case 0xB4: // LDY $??,X MR_ZX(); LDY(); ADD_CYCLE(4); break; case 0xAC: // LDY $???? MR_AB(); LDY(); ADD_CYCLE(4); break; case 0xBC: // LDY $????,X MR_AX(); LDY(); CHECK_EA(); ADD_CYCLE(4); break; case 0x85: // STA $?? EA_ZP(); STA(); MW_ZP(); ADD_CYCLE(3); break; case 0x95: // STA $??,X EA_ZX(); STA(); MW_ZP(); ADD_CYCLE(4); break; case 0x8D: // STA $???? EA_AB(); STA(); MW_EA(); ADD_CYCLE(4); break; case 0x9D: // STA $????,X EA_AX(); STA(); MW_EA(); ADD_CYCLE(5); break; case 0x99: // STA $????,Y EA_AY(); STA(); MW_EA(); ADD_CYCLE(5); break; case 0x81: // STA ($??,X) EA_IX(); STA(); MW_EA(); ADD_CYCLE(6); break; case 0x91: // STA ($??),Y EA_IY(); STA(); MW_EA(); ADD_CYCLE(6); break; case 0x86: // STX $?? EA_ZP(); STX(); MW_ZP(); ADD_CYCLE(3); break; case 0x96: // STX $??,Y EA_ZY(); STX(); MW_ZP(); ADD_CYCLE(4); break; case 0x8E: // STX $???? EA_AB(); STX(); MW_EA(); ADD_CYCLE(4); break; case 0x84: // STY $?? EA_ZP(); STY(); MW_ZP(); ADD_CYCLE(3); break; case 0x94: // STY $??,X EA_ZX(); STY(); MW_ZP(); ADD_CYCLE(4); break; case 0x8C: // STY $???? EA_AB(); STY(); MW_EA(); ADD_CYCLE(4); break; case 0xAA: // TAX TAX(); ADD_CYCLE(2); break; case 0x8A: // TXA TXA(); ADD_CYCLE(2); break; case 0xA8: // TAY TAY(); ADD_CYCLE(2); break; case 0x98: // TYA TYA(); ADD_CYCLE(2); break; case 0xBA: // TSX TSX(); ADD_CYCLE(2); break; case 0x9A: // TXS TXS(); ADD_CYCLE(2); break; case 0xC9: // CMP #$?? MR_IM(); CMP_(); ADD_CYCLE(2); break; case 0xC5: // CMP $?? MR_ZP(); CMP_(); ADD_CYCLE(3); break; case 0xD5: // CMP $??,X MR_ZX(); CMP_(); ADD_CYCLE(4); break; case 0xCD: // CMP $???? MR_AB(); CMP_(); ADD_CYCLE(4); break; case 0xDD: // CMP $????,X MR_AX(); CMP_(); CHECK_EA(); ADD_CYCLE(4); break; case 0xD9: // CMP $????,Y MR_AY(); CMP_(); CHECK_EA(); ADD_CYCLE(4); break; case 0xC1: // CMP ($??,X) MR_IX(); CMP_(); ADD_CYCLE(6); break; case 0xD1: // CMP ($??),Y MR_IY(); CMP_(); CHECK_EA(); ADD_CYCLE(5); break; case 0xE0: // CPX #$?? MR_IM(); CPX(); ADD_CYCLE(2); break; case 0xE4: // CPX $?? MR_ZP(); CPX(); ADD_CYCLE(3); break; case 0xEC: // CPX $???? MR_AB(); CPX(); ADD_CYCLE(4); break; case 0xC0: // CPY #$?? MR_IM(); CPY(); ADD_CYCLE(2); break; case 0xC4: // CPY $?? MR_ZP(); CPY(); ADD_CYCLE(3); break; case 0xCC: // CPY $???? MR_AB(); CPY(); ADD_CYCLE(4); break; case 0x90: // BCC MR_IM(); BCC(); ADD_CYCLE(2); break; case 0xB0: // BCS MR_IM(); BCS(); ADD_CYCLE(2); break; case 0xF0: // BEQ MR_IM(); BEQ(); ADD_CYCLE(2); break; case 0x30: // BMI MR_IM(); BMI(); ADD_CYCLE(2); break; case 0xD0: // BNE MR_IM(); BNE(); ADD_CYCLE(2); break; case 0x10: // BPL MR_IM(); BPL(); ADD_CYCLE(2); break; case 0x50: // BVC MR_IM(); BVC(); ADD_CYCLE(2); break; case 0x70: // BVS MR_IM(); BVS(); ADD_CYCLE(2); break; case 0x4C: // JMP $???? JMP(); ADD_CYCLE(3); break; case 0x6C: // JMP ($????) JMP_ID(); ADD_CYCLE(5); break; case 0x20: // JSR JSR(); ADD_CYCLE(6); break; case 0x40: // RTI RTI(); ADD_CYCLE(6); break; case 0x60: // RTS RTS(); ADD_CYCLE(6); break; // フラグ制御系 case 0x18: // CLC CLC(); ADD_CYCLE(2); break; case 0xD8: // CLD CLD(); ADD_CYCLE(2); break; case 0x58: // CLI CLI(); ADD_CYCLE(2); break; case 0xB8: // CLV CLV(); ADD_CYCLE(2); break; case 0x38: // SEC SEC(); ADD_CYCLE(2); break; case 0xF8: // SED SED(); ADD_CYCLE(2); break; case 0x78: // SEI SEI(); ADD_CYCLE(2); break; // スタック系 case 0x48: // PHA PUSH(R.A); ADD_CYCLE(3); break; case 0x08: // PHP PUSH((byte)(R.P | B_FLAG)); ADD_CYCLE(3); break; case 0x68: // PLA (N-----Z-) R.A = POP(); SET_ZN_FLAG(R.A); ADD_CYCLE(4); break; case 0x28: // PLP R.P = (byte)(POP() | R_FLAG); ADD_CYCLE(4); break; // その他 case 0x00: // BRK BRK(); ADD_CYCLE(7); break; case 0xEA: // NOP ADD_CYCLE(2); break; // 未公開命令群 case 0x0B: // ANC #$?? case 0x2B: // ANC #$?? MR_IM(); ANC(); ADD_CYCLE(2); break; case 0x8B: // ANE #$?? MR_IM(); ANE(); ADD_CYCLE(2); break; case 0x6B: // ARR #$?? MR_IM(); ARR(); ADD_CYCLE(2); break; case 0x4B: // ASR #$?? MR_IM(); ASR(); ADD_CYCLE(2); break; case 0xC7: // DCP $?? MR_ZP(); DCP(); MW_ZP(); ADD_CYCLE(5); break; case 0xD7: // DCP $??,X MR_ZX(); DCP(); MW_ZP(); ADD_CYCLE(6); break; case 0xCF: // DCP $???? MR_AB(); DCP(); MW_EA(); ADD_CYCLE(6); break; case 0xDF: // DCP $????,X MR_AX(); DCP(); MW_EA(); ADD_CYCLE(7); break; case 0xDB: // DCP $????,Y MR_AY(); DCP(); MW_EA(); ADD_CYCLE(7); break; case 0xC3: // DCP ($??,X) MR_IX(); DCP(); MW_EA(); ADD_CYCLE(8); break; case 0xD3: // DCP ($??),Y MR_IY(); DCP(); MW_EA(); ADD_CYCLE(8); break; case 0xE7: // ISB $?? MR_ZP(); ISB(); MW_ZP(); ADD_CYCLE(5); break; case 0xF7: // ISB $??,X MR_ZX(); ISB(); MW_ZP(); ADD_CYCLE(5); break; case 0xEF: // ISB $???? MR_AB(); ISB(); MW_EA(); ADD_CYCLE(5); break; case 0xFF: // ISB $????,X MR_AX(); ISB(); MW_EA(); ADD_CYCLE(5); break; case 0xFB: // ISB $????,Y MR_AY(); ISB(); MW_EA(); ADD_CYCLE(5); break; case 0xE3: // ISB ($??,X) MR_IX(); ISB(); MW_EA(); ADD_CYCLE(5); break; case 0xF3: // ISB ($??),Y MR_IY(); ISB(); MW_EA(); ADD_CYCLE(5); break; case 0xBB: // LAS $????,Y MR_AY(); LAS(); CHECK_EA(); ADD_CYCLE(4); break; case 0xA7: // LAX $?? MR_ZP(); LAX(); ADD_CYCLE(3); break; case 0xB7: // LAX $??,Y MR_ZY(); LAX(); ADD_CYCLE(4); break; case 0xAF: // LAX $???? MR_AB(); LAX(); ADD_CYCLE(4); break; case 0xBF: // LAX $????,Y MR_AY(); LAX(); CHECK_EA(); ADD_CYCLE(4); break; case 0xA3: // LAX ($??,X) MR_IX(); LAX(); ADD_CYCLE(6); break; case 0xB3: // LAX ($??),Y MR_IY(); LAX(); CHECK_EA(); ADD_CYCLE(5); break; case 0xAB: // LXA #$?? MR_IM(); LXA(); ADD_CYCLE(2); break; case 0x27: // RLA $?? MR_ZP(); RLA(); MW_ZP(); ADD_CYCLE(5); break; case 0x37: // RLA $??,X MR_ZX(); RLA(); MW_ZP(); ADD_CYCLE(6); break; case 0x2F: // RLA $???? MR_AB(); RLA(); MW_EA(); ADD_CYCLE(6); break; case 0x3F: // RLA $????,X MR_AX(); RLA(); MW_EA(); ADD_CYCLE(7); break; case 0x3B: // RLA $????,Y MR_AY(); RLA(); MW_EA(); ADD_CYCLE(7); break; case 0x23: // RLA ($??,X) MR_IX(); RLA(); MW_EA(); ADD_CYCLE(8); break; case 0x33: // RLA ($??),Y MR_IY(); RLA(); MW_EA(); ADD_CYCLE(8); break; case 0x67: // RRA $?? MR_ZP(); RRA(); MW_ZP(); ADD_CYCLE(5); break; case 0x77: // RRA $??,X MR_ZX(); RRA(); MW_ZP(); ADD_CYCLE(6); break; case 0x6F: // RRA $???? MR_AB(); RRA(); MW_EA(); ADD_CYCLE(6); break; case 0x7F: // RRA $????,X MR_AX(); RRA(); MW_EA(); ADD_CYCLE(7); break; case 0x7B: // RRA $????,Y MR_AY(); RRA(); MW_EA(); ADD_CYCLE(7); break; case 0x63: // RRA ($??,X) MR_IX(); RRA(); MW_EA(); ADD_CYCLE(8); break; case 0x73: // RRA ($??),Y MR_IY(); RRA(); MW_EA(); ADD_CYCLE(8); break; case 0x87: // SAX $?? MR_ZP(); SAX(); MW_ZP(); ADD_CYCLE(3); break; case 0x97: // SAX $??,Y MR_ZY(); SAX(); MW_ZP(); ADD_CYCLE(4); break; case 0x8F: // SAX $???? MR_AB(); SAX(); MW_EA(); ADD_CYCLE(4); break; case 0x83: // SAX ($??,X) MR_IX(); SAX(); MW_EA(); ADD_CYCLE(6); break; case 0xCB: // SBX #$?? MR_IM(); SBX(); ADD_CYCLE(2); break; case 0x9F: // SHA $????,Y MR_AY(); SHA(); MW_EA(); ADD_CYCLE(5); break; case 0x93: // SHA ($??),Y MR_IY(); SHA(); MW_EA(); ADD_CYCLE(6); break; case 0x9B: // SHS $????,Y MR_AY(); SHS(); MW_EA(); ADD_CYCLE(5); break; case 0x9E: // SHX $????,Y MR_AY(); SHX(); MW_EA(); ADD_CYCLE(5); break; case 0x9C: // SHY $????,X MR_AX(); SHY(); MW_EA(); ADD_CYCLE(5); break; case 0x07: // SLO $?? MR_ZP(); SLO(); MW_ZP(); ADD_CYCLE(5); break; case 0x17: // SLO $??,X MR_ZX(); SLO(); MW_ZP(); ADD_CYCLE(6); break; case 0x0F: // SLO $???? MR_AB(); SLO(); MW_EA(); ADD_CYCLE(6); break; case 0x1F: // SLO $????,X MR_AX(); SLO(); MW_EA(); ADD_CYCLE(7); break; case 0x1B: // SLO $????,Y MR_AY(); SLO(); MW_EA(); ADD_CYCLE(7); break; case 0x03: // SLO ($??,X) MR_IX(); SLO(); MW_EA(); ADD_CYCLE(8); break; case 0x13: // SLO ($??),Y MR_IY(); SLO(); MW_EA(); ADD_CYCLE(8); break; case 0x47: // SRE $?? MR_ZP(); SRE(); MW_ZP(); ADD_CYCLE(5); break; case 0x57: // SRE $??,X MR_ZX(); SRE(); MW_ZP(); ADD_CYCLE(6); break; case 0x4F: // SRE $???? MR_AB(); SRE(); MW_EA(); ADD_CYCLE(6); break; case 0x5F: // SRE $????,X MR_AX(); SRE(); MW_EA(); ADD_CYCLE(7); break; case 0x5B: // SRE $????,Y MR_AY(); SRE(); MW_EA(); ADD_CYCLE(7); break; case 0x43: // SRE ($??,X) MR_IX(); SRE(); MW_EA(); ADD_CYCLE(8); break; case 0x53: // SRE ($??),Y MR_IY(); SRE(); MW_EA(); ADD_CYCLE(8); break; case 0xEB: // SBC #$?? (Unofficial) MR_IM(); SBC(); ADD_CYCLE(2); break; case 0x1A: // NOP (Unofficial) case 0x3A: // NOP (Unofficial) case 0x5A: // NOP (Unofficial) case 0x7A: // NOP (Unofficial) case 0xDA: // NOP (Unofficial) case 0xFA: // NOP (Unofficial) ADD_CYCLE(2); break; case 0x80: // DOP (CYCLES 2) case 0x82: // DOP (CYCLES 2) case 0x89: // DOP (CYCLES 2) case 0xC2: // DOP (CYCLES 2) case 0xE2: // DOP (CYCLES 2) R.PC++; ADD_CYCLE(2); break; case 0x04: // DOP (CYCLES 3) case 0x44: // DOP (CYCLES 3) case 0x64: // DOP (CYCLES 3) R.PC++; ADD_CYCLE(3); break; case 0x14: // DOP (CYCLES 4) case 0x34: // DOP (CYCLES 4) case 0x54: // DOP (CYCLES 4) case 0x74: // DOP (CYCLES 4) case 0xD4: // DOP (CYCLES 4) case 0xF4: // DOP (CYCLES 4) R.PC++; ADD_CYCLE(4); break; case 0x0C: // TOP case 0x1C: // TOP case 0x3C: // TOP case 0x5C: // TOP case 0x7C: // TOP case 0xDC: // TOP case 0xFC: // TOP R.PC += 2; ADD_CYCLE(4); break; case 0x02: /* JAM */ case 0x12: /* JAM */ case 0x22: /* JAM */ case 0x32: /* JAM */ case 0x42: /* JAM */ case 0x52: /* JAM */ case 0x62: /* JAM */ case 0x72: /* JAM */ case 0x92: /* JAM */ case 0xB2: /* JAM */ case 0xD2: /* JAM */ case 0xF2: /* JAM */ default: if (!Supporter.Config.emulator.bIllegalOp) { throw new Exception("IllegalOp"); } else { R.PC--; ADD_CYCLE(4); } break; // default: // __assume(0); } if (nmi_request != 0) { _NMI(); } else if (irq_request != 0) { _IRQ(); } request_cycles -= exec_cycles; TOTAL_cycles += exec_cycles; mapper.Clock(exec_cycles); #if DPCM_SYNCCLOCK apu->SyncDPCM( exec_cycles ); #endif if (bClockProcess) { nes.Clock(exec_cycles); } } _execute_exit: #if !DPCM_SYNCCLOCK apu.SyncDPCM(TOTAL_cycles - OLD_cycles); #endif return TOTAL_cycles - OLD_cycles; } private void _IRQ() { PUSH((byte)(R.PC >> 8)); PUSH((byte)(R.PC & 0xFF)); CLR_FLAG(B_FLAG); PUSH(R.P); SET_FLAG(I_FLAG); R.PC = RD6502W(IRQ_VECTOR); exec_cycles += 7; } private ushort RD6502W(ushort addr) { if (addr < 0x2000) { // RAM (Mirror $0800, $1000, $1800) return BitConverter.ToUInt16(MMU.RAM, addr & 0x07FF); } else if (addr < 0x8000) { // Others return (ushort)(nes.Read(addr) + nes.Read((ushort)(addr + 1)) * 0x100); } var temp = MMU.CPU_MEM_BANK[addr >> 13]; shortTemp[0] = temp[addr & 0x1FFF]; shortTemp[1] = temp[(addr & 0x1FFF) + 1]; return BitConverter.ToUInt16(shortTemp, 0); } private void SET_FLAG(byte V) { R.P |= (V); } private void CLR_FLAG(byte V) { var temp = (byte)(~V); R.P &= temp; } private void _NMI() { PUSH((byte)(R.PC >> 8)); PUSH((byte)(R.PC & 0xFF)); CLR_FLAG(B_FLAG); PUSH(R.P); SET_FLAG(I_FLAG); R.PC = RD6502W(NMI_VECTOR); exec_cycles += 7; } private void SRE() { TST_FLAG((DT & 0x01) != 0, C_FLAG); DT >>= 1; R.A ^= DT; SET_ZN_FLAG(R.A); } private void SLO() { TST_FLAG((DT & 0x80) != 0, C_FLAG); DT <<= 1; R.A |= DT; SET_ZN_FLAG(R.A); } private void SHY() { DT = (byte)(R.Y & ((EA >> 8) + 1)); } private void SHX() { DT = (byte)(R.X & ((EA >> 8) + 1)); } private void SHS() { R.S = (byte)(R.A & R.X); DT = (byte)(R.S & ((EA >> 8) + 1)); } private void SHA() { DT = (byte)(R.A & R.X & ((EA >> 8) + 1)); } private void SBX() { WT = (ushort)((R.A & R.X) - DT); TST_FLAG(WT < 0x100, C_FLAG); R.X = (byte)(WT & 0xFF); SET_ZN_FLAG(R.X); } private void SAX() { DT = (byte)(R.A & R.X); } private void RRA() { if ((R.P & C_FLAG) != 0) { TST_FLAG((DT & 0x01) != 0, C_FLAG); DT = (byte)((DT >> 1) | 0x80); } else { TST_FLAG((DT & 0x01) != 0, C_FLAG); DT >>= 1; } ADC(); } private void RLA() { if ((R.P & C_FLAG) != 0) { TST_FLAG((DT & 0x80) != 0, C_FLAG); DT = (byte)((DT << 1) | 1); } else { TST_FLAG((DT & 0x80) != 0, C_FLAG); DT <<= 1; } R.A &= DT; SET_ZN_FLAG(R.A); } private void LXA() { R.A = R.X = (byte)((R.A | 0xEE) & DT); SET_ZN_FLAG(R.A); } private void LAX() { R.A = DT; R.X = R.A; SET_ZN_FLAG(R.A); } private void LAS() { R.A = R.X = R.S = (byte)(R.S & DT); SET_ZN_FLAG(R.A); } private void ISB() { DT++; SBC(); } private void DCP() { DT--; CMP_(); } private void ASR() { DT &= R.A; TST_FLAG((DT & 0x01) != 0, C_FLAG); R.A = (byte)(DT >> 1); SET_ZN_FLAG(R.A); } private void ARR() { DT &= R.A; R.A = (byte)((DT >> 1) | ((R.P & C_FLAG) << 7)); SET_ZN_FLAG(R.A); TST_FLAG((R.A & 0x40) != 0, C_FLAG); TST_FLAG(((R.A >> 6) ^ (R.A >> 5)) != 0, V_FLAG); } private void ANE() { R.A = (byte)((R.A | 0xEE) & R.X & DT); SET_ZN_FLAG(R.A); } private void ANC() { R.A &= DT; SET_ZN_FLAG(R.A); TST_FLAG((R.P & N_FLAG) != 0, C_FLAG); } private void BRK() { R.PC++; PUSH((byte)(R.PC >> 8)); PUSH((byte)(R.PC & 0xFF)); SET_FLAG(B_FLAG); PUSH(R.P); SET_FLAG(I_FLAG); R.PC = RD6502W(IRQ_VECTOR); } private byte POP() { return STACK[(++R.S) & 0xFF]; } private void PUSH(byte V) { STACK[(R.S--) & 0xFF] = V; } private void SEI() { R.P |= I_FLAG; } private void SED() { R.P |= D_FLAG; } private void SEC() { R.P |= C_FLAG; } private void CLV() { var temp = unchecked((byte)(~V_FLAG)); R.P &= temp; } private void CLI() { var temp = unchecked((byte)(~I_FLAG)); R.P &= temp; } private void CLD() { var temp = unchecked((byte)(~D_FLAG)); R.P &= temp; } private void CLC() { var temp = unchecked((byte)(~C_FLAG)); R.P &= temp; } private void RTS() { R.PC = POP(); R.PC |= (ushort)(POP() * 0x0100); R.PC++; } private void RTI() { R.P = (byte)(POP() | R_FLAG); R.PC = POP(); R.PC |= (ushort)(POP() * 0x0100); } private void JSR() { EA = OP6502W(R.PC); R.PC++; PUSH((byte)(R.PC >> 8)); PUSH((byte)(R.PC & 0xFF)); R.PC = EA; } private void JMP_ID() { WT = OP6502W(R.PC); EA = RD6502(WT); WT = (ushort)((WT & 0xFF00) | ((WT + 1) & 0x00FF)); R.PC = (ushort)(EA + RD6502(WT) * 0x100); } private void JMP() { R.PC = OP6502W(R.PC); } private void BVS() { if ((R.P & V_FLAG) != 0) REL_JUMP(); } private void REL_JUMP() { ET = R.PC; EA = (ushort)(R.PC + (sbyte)DT); R.PC = EA; ADD_CYCLE(1); CHECK_EA(); } private void BVC() { if ((R.P & V_FLAG) == 0) REL_JUMP(); } private void BPL() { if ((R.P & N_FLAG) == 0) REL_JUMP(); } private void BNE() { if ((R.P & Z_FLAG) == 0) REL_JUMP(); } private void BMI() { if ((R.P & N_FLAG) != 0) REL_JUMP(); } private void BEQ() { if ((R.P & Z_FLAG) != 0) REL_JUMP(); } private void BCS() { if ((R.P & C_FLAG) != 0) REL_JUMP(); } private void BCC() { if ((R.P & C_FLAG) == 0) REL_JUMP(); } private void CPY() { WT = (ushort)(R.Y - DT); TST_FLAG((WT & 0x8000) == 0, C_FLAG); SET_ZN_FLAG((byte)WT); } private void CPX() { WT = (ushort)(R.X - DT); TST_FLAG((WT & 0x8000) == 0, C_FLAG); SET_ZN_FLAG((byte)WT); } private void CMP_() { WT = (ushort)(R.A - DT); TST_FLAG((WT & 0x8000) == 0, C_FLAG); SET_ZN_FLAG((byte)WT); } private void TXS() { R.S = R.X; } private void TSX() { R.X = R.S; SET_ZN_FLAG(R.X); } private void TYA() { R.A = R.Y; SET_ZN_FLAG(R.A); } private void TAY() { R.Y = R.A; SET_ZN_FLAG(R.Y); } private void TXA() { R.A = R.X; SET_ZN_FLAG(R.A); } private void TAX() { R.X = R.A; SET_ZN_FLAG(R.X); } private void STY() { DT = R.Y; } private void EA_ZY() { DT = OP6502(R.PC++); EA = (byte)(DT + R.Y); } private void STX() { DT = R.X; } private void EA_IY() { DT = OP6502(R.PC++); ET = ZPRDW(DT); EA = (ushort)(ET + R.Y); } private void EA_IX() { DT = OP6502(R.PC++); EA = ZPRDW(DT + R.X); } private void EA_AY() { ET = OP6502W(R.PC); R.PC += 2; EA = (ushort)(ET + R.Y); } private void EA_AX() { ET = OP6502W(R.PC); R.PC += 2; EA = (ushort)(ET + R.X); } private void EA_AB() { EA = OP6502W(R.PC); R.PC += 2; } private void EA_ZX() { DT = OP6502(R.PC++); EA = (byte)(DT + R.X); } private void STA() { DT = R.A; } private void EA_ZP() { EA = OP6502(R.PC++); } private void LDY() { R.Y = DT; SET_ZN_FLAG(R.Y); } private void MR_ZY() { DT = OP6502(R.PC++); EA = (byte)(DT + R.Y); DT = ZPRD(EA); } private void LDX() { R.X = DT; SET_ZN_FLAG(R.X); } private void LDA() { R.A = DT; SET_ZN_FLAG(R.A); } private void ROR() { if ((R.P & C_FLAG) != 0) { TST_FLAG((DT & 0x01) != 0, C_FLAG); DT = (byte)((DT >> 1) | 0x80); } else { TST_FLAG((DT & 0x01) != 0, C_FLAG); DT >>= 1; } SET_ZN_FLAG(DT); } private void ROR_A() { if ((R.P & C_FLAG) != 0) { TST_FLAG((R.A & 0x01) != 0, C_FLAG); R.A = (byte)((R.A >> 1) | 0x80); } else { TST_FLAG((R.A & 0x01) != 0, C_FLAG); R.A >>= 1; } SET_ZN_FLAG(R.A); } private void ROL() { if ((R.P & C_FLAG) != 0) { TST_FLAG((DT & 0x80) != 0, C_FLAG); DT = (byte)((DT << 1) | 0x01); } else { TST_FLAG((DT & 0x80) != 0, C_FLAG); DT <<= 1; } SET_ZN_FLAG(DT); } private void ROL_A() { if ((R.P & C_FLAG) != 0) { TST_FLAG((R.A & 0x80) != 0, C_FLAG); R.A = (byte)((R.A << 1) | 0x01); } else { TST_FLAG((R.A & 0x80) != 0, C_FLAG); R.A <<= 1; } SET_ZN_FLAG(R.A); } private void ORA() { R.A |= DT; SET_ZN_FLAG(R.A); } private void LSR_A() { TST_FLAG((R.A & 0x01) != 0, C_FLAG); R.A >>= 1; SET_ZN_FLAG(R.A); } private void LSR() { TST_FLAG((DT & 0x01) != 0, C_FLAG); DT >>= 1; SET_ZN_FLAG(DT); } private void EOR() { R.A ^= DT; SET_ZN_FLAG(R.A); } internal void SetClockProcess(bool bEnable) { m_bClockProcess = bEnable; } internal byte OP6502(ushort addr) { return MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]; } private byte[] shortTemp = new byte[2]; internal ushort OP6502W(ushort addr) { var bytePage = MMU.CPU_MEM_BANK[addr >> 13]; var spanByte = bytePage; shortTemp[0] = spanByte[addr & 0x1FFF]; shortTemp[1] = spanByte[(addr & 0x1FFF) + 1]; return BitConverter.ToUInt16(shortTemp, 0); } internal byte RD6502(ushort addr) { if (addr < 0x2000) { // RAM (Mirror $0800, $1000, $1800) return MMU.RAM[addr & 0x07FF]; } else if (addr < 0x8000) { // Others return nes.Read(addr); } else { // Dummy access mapper.Read(addr, MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]); } // Quick bank read return MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]; } private void AND() { R.A &= DT; SET_ZN_FLAG(R.A); } private void MR_IM() { DT = OP6502(R.PC++); } private void BIT() { TST_FLAG((DT & R.A) == 0, Z_FLAG); TST_FLAG((DT & 0x80) != 0, N_FLAG); TST_FLAG((DT & 0x40) != 0, V_FLAG); } private void MR_ZP() { EA = OP6502(R.PC++); DT = ZPRD(EA); } private byte ZPRD(ushort A) { return MMU.RAM[A]; } private ushort ZPRDW(int A) { ushort ram1 = MMU.RAM[A]; ushort ram2 = MMU.RAM[A + 1]; ram2 <<= 8; return (ushort)(ram1 + ram2); } private void ADC() { WT = (ushort)(R.A + DT + (R.P & C_FLAG)); TST_FLAG(WT > 0xFF, C_FLAG); var temp = ((~(R.A ^ DT)) & (R.A ^ WT) & 0x80); TST_FLAG(temp != 0, V_FLAG); R.A = (byte)WT; SET_ZN_FLAG(R.A); } private void TST_FLAG(bool F, byte V) { byte temp = (byte)~V; R.P &= temp; if (F) R.P |= V; } private void SET_ZN_FLAG(byte A) { byte temp = unchecked((byte)(~(Z_FLAG | N_FLAG))); R.P &= temp; R.P |= ZN_Table[A]; } private void ADD_CYCLE(int V) { exec_cycles += V; } private void MR_ZX() { DT = OP6502(R.PC++); EA = (ushort)(DT + R.X); DT = ZPRD(EA); } private void MR_AB() { EA = OP6502W(R.PC); R.PC += 2; DT = RD6502(EA); } private void MR_AX() { ET = OP6502W(R.PC); R.PC += 2; EA = (ushort)(ET + R.X); DT = RD6502(EA); } private void CHECK_EA() { if ((ET & 0xFF00) != (EA & 0xFF00)) ADD_CYCLE(1); } private void MR_AY() { ET = OP6502W(R.PC); R.PC += 2; EA = (ushort)(ET + R.Y); DT = RD6502(EA); } private void MR_IX() { DT = OP6502(R.PC++); EA = ZPRDW(DT + R.X); DT = RD6502(EA); } private void MR_IY() { DT = OP6502(R.PC++); ET = ZPRDW(DT); EA = (ushort)(ET + R.Y); DT = RD6502(EA); } private void ASL_A() { TST_FLAG((R.A & 0x80) != 0, C_FLAG); R.A <<= 1; SET_ZN_FLAG(R.A); } private void ASL() { TST_FLAG((DT & 0x80) != 0, C_FLAG); DT <<= 1; SET_ZN_FLAG(DT); } private void SBC() { WT = (ushort)(R.A - DT - (~R.P & C_FLAG)); bool f = ((R.A ^ DT) & (R.A ^ WT) & (0x80)) != 0; TST_FLAG(f, V_FLAG); TST_FLAG(WT < 0x100, C_FLAG); R.A = (byte)WT; SET_ZN_FLAG(R.A); } private void DEC() { DT--; SET_ZN_FLAG(DT); } private void DEX() { R.X--; SET_ZN_FLAG(R.X); } private void DEY() { R.Y--; SET_ZN_FLAG(R.Y); } private void INC() { DT++; SET_ZN_FLAG(DT); } private void INX() { R.X++; SET_ZN_FLAG(R.X); } private void INY() { R.Y++; SET_ZN_FLAG(R.Y); } private void MW_ZP() { ZPWR(EA, DT); } private void ZPWR(ushort a, byte v) { MMU.RAM[a] = v; } private void MW_EA() { WR6502(EA, DT); } internal void ClrIRQ(byte mask) { byte temp = (byte)~mask; R.INT_pending &= temp; } internal void WR6502(ushort addr, byte data) { if (addr < 0x2000) { // RAM (Mirror $0800, $1000, $1800) MMU.RAM[addr & 0x07FF] = data; } else { // Others nes.Write(addr, data); } } internal void NMI() { R.INT_pending |= NMI_FLAG; nmicount = 0; } internal void SetIRQ(byte mask) { R.INT_pending |= mask; } internal int GetTotalCycles() { return TOTAL_cycles; } internal void DMA(int cycles) { DMA_cycles += cycles; } internal void Reset() { apu = nes.apu; mapper = nes.mapper; R.A = 0x00; R.X = 0x00; R.Y = 0x00; R.S = 0xFF; R.P = Z_FLAG | R_FLAG; R.PC = RD6502W(RES_VECTOR); R.INT_pending = 0; TOTAL_cycles = 0; DMA_cycles = 0; // STACK quick access STACK = new ArrayRef(MMU.RAM, 0x0100, MMU.RAM.Length - 0x100); // Zero/Negative FLAG ZN_Table[0] = Z_FLAG; for (int i = 1; i < 256; i++) ZN_Table[i] = (byte)((i & 0x80) != 0 ? N_FLAG : 0); } internal void GetContext(ref R6502 r) { r = R; } } public enum StatusFlag6502 : int { C_FLAG = 0x01, Z_FLAG = 0x02, I_FLAG = 0x04, D_FLAG = 0x08, B_FLAG = 0x10, R_FLAG = 0x20, V_FLAG = 0x40, N_FLAG = 0x80 } public enum Interrupt : int { NMI_FLAG = 0x01, IRQ_FLAG = 0x02, IRQ_FRAMEIRQ = 0x04, IRQ_DPCM = 0x08, IRQ_MAPPER = 0x10, IRQ_MAPPER2 = 0x20, IRQ_TRIGGER = 0x40, IRQ_TRIGGER2 = 0x80, IRQ_MASK = (~(NMI_FLAG | IRQ_FLAG)), } public enum Vector : int { NMI_VECTOR = 0xFFFA, RES_VECTOR = 0xFFFC, IRQ_VECTOR = 0xFFFE } public class R6502 { public ushort PC; public byte A; public byte P; public byte X; public byte Y; public byte S; public byte INT_pending; } }