377 lines
17 KiB
C++
377 lines
17 KiB
C++
|
// Pi1541 - A Commodore 1541 disk drive emulator
|
||
|
// Copyright(C) 2018 Stephen White
|
||
|
//
|
||
|
// This file is part of Pi1541.
|
||
|
//
|
||
|
// Pi1541 is free software : you can redistribute it and/or modify
|
||
|
// it under the terms of the GNU General Public License as published by
|
||
|
// the Free Software Foundation, either version 3 of the License, or
|
||
|
// (at your option) any later version.
|
||
|
//
|
||
|
// Pi1541 is distributed in the hope that it will be useful,
|
||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
// GNU General Public License for more details.
|
||
|
//
|
||
|
// You should have received a copy of the GNU General Public License
|
||
|
// along with Pi1541. If not, see <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
#include "m6502.h"
|
||
|
|
||
|
M6502::OpcodeCycleFunction M6502::opcodeFunctions[256] =
|
||
|
{
|
||
|
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||
|
&M6502::BRK,&M6502::ORA,&M6502::JAM,&M6502::SLO,&M6502::NOP,&M6502::ORA,&M6502::ASL,&M6502::SLO,&M6502::PHP,&M6502::ORA,&M6502::ASL,&M6502::ANC,&M6502::NOP,&M6502::ORA,&M6502::ASL,&M6502::SLO,// 0
|
||
|
&M6502::BPL,&M6502::ORA,&M6502::JAM,&M6502::SLO,&M6502::NOP,&M6502::ORA,&M6502::ASL,&M6502::SLO,&M6502::CLC,&M6502::ORA,&M6502::NOP,&M6502::SLO,&M6502::NOP,&M6502::ORA,&M6502::ASL,&M6502::SLO,// 1
|
||
|
&M6502::JSR,&M6502::AND,&M6502::JAM,&M6502::RLA,&M6502::BIT,&M6502::AND,&M6502::ROL,&M6502::RLA,&M6502::PLP,&M6502::AND,&M6502::ROL,&M6502::ANC,&M6502::BIT,&M6502::AND,&M6502::ROL,&M6502::RLA,// 2
|
||
|
&M6502::BMI,&M6502::AND,&M6502::JAM,&M6502::RLA,&M6502::NOP,&M6502::AND,&M6502::ROL,&M6502::RLA,&M6502::SEC,&M6502::AND,&M6502::NOP,&M6502::RLA,&M6502::NOP,&M6502::AND,&M6502::ROL,&M6502::RLA,// 3
|
||
|
&M6502::RTI,&M6502::EOR,&M6502::JAM,&M6502::SRE,&M6502::NOP,&M6502::EOR,&M6502::LSR,&M6502::SRE,&M6502::PHA,&M6502::EOR,&M6502::LSR,&M6502::ASR,&M6502::JMP,&M6502::EOR,&M6502::LSR,&M6502::SRE,// 4
|
||
|
&M6502::BVC,&M6502::EOR,&M6502::JAM,&M6502::SRE,&M6502::NOP,&M6502::EOR,&M6502::LSR,&M6502::SRE,&M6502::CLI,&M6502::EOR,&M6502::NOP,&M6502::SRE,&M6502::NOP,&M6502::EOR,&M6502::LSR,&M6502::SRE,// 5
|
||
|
&M6502::RTS,&M6502::ADC,&M6502::JAM,&M6502::RRA,&M6502::NOP,&M6502::ADC,&M6502::ROR,&M6502::RRA,&M6502::PLA,&M6502::ADC,&M6502::ROR,&M6502::ARR,&M6502::JMP,&M6502::ADC,&M6502::ROR,&M6502::RRA,// 6
|
||
|
&M6502::BVS,&M6502::ADC,&M6502::JAM,&M6502::RRA,&M6502::NOP,&M6502::ADC,&M6502::ROR,&M6502::RRA,&M6502::SEI,&M6502::ADC,&M6502::NOP,&M6502::RRA,&M6502::NOP,&M6502::ADC,&M6502::ROR,&M6502::RRA,// 7
|
||
|
&M6502::NOP,&M6502::STA,&M6502::NOP,&M6502::SAX,&M6502::STY,&M6502::STA,&M6502::STX,&M6502::SAX,&M6502::DEY,&M6502::NOP,&M6502::TXA,&M6502::XAA,&M6502::STY,&M6502::STA,&M6502::STX,&M6502::SAX,// 8
|
||
|
&M6502::BCC,&M6502::STA,&M6502::JAM,&M6502::SHA,&M6502::STY,&M6502::STA,&M6502::STX,&M6502::SAX,&M6502::TYA,&M6502::STA,&M6502::TXS,&M6502::SHS,&M6502::SHY,&M6502::STA,&M6502::SHX,&M6502::SHA,// 9
|
||
|
&M6502::LDY,&M6502::LDA,&M6502::LDX,&M6502::LAX,&M6502::LDY,&M6502::LDA,&M6502::LDX,&M6502::LAX,&M6502::TAY,&M6502::LDA,&M6502::TAX,&M6502::LXA,&M6502::LDY,&M6502::LDA,&M6502::LDX,&M6502::LAX,// A
|
||
|
&M6502::BCS,&M6502::LDA,&M6502::JAM,&M6502::LAX,&M6502::LDY,&M6502::LDA,&M6502::LDX,&M6502::LAX,&M6502::CLV,&M6502::LDA,&M6502::TSX,&M6502::LAS,&M6502::LDY,&M6502::LDA,&M6502::LDX,&M6502::LAX,// B
|
||
|
&M6502::CPY,&M6502::CMP,&M6502::NOP,&M6502::DCP,&M6502::CPY,&M6502::CMP,&M6502::DEC,&M6502::DCP,&M6502::INY,&M6502::CMP,&M6502::DEX,&M6502::SBX,&M6502::CPY,&M6502::CMP,&M6502::DEC,&M6502::DCP,// C
|
||
|
&M6502::BNE,&M6502::CMP,&M6502::JAM,&M6502::DCP,&M6502::NOP,&M6502::CMP,&M6502::DEC,&M6502::DCP,&M6502::CLD,&M6502::CMP,&M6502::NOP,&M6502::DCP,&M6502::NOP,&M6502::CMP,&M6502::DEC,&M6502::DCP,// D
|
||
|
&M6502::CPX,&M6502::SBC,&M6502::NOP,&M6502::ISB,&M6502::CPX,&M6502::SBC,&M6502::INC,&M6502::ISB,&M6502::INX,&M6502::SBC,&M6502::NOP,&M6502::SBC,&M6502::CPX,&M6502::SBC,&M6502::INC,&M6502::ISB,// E
|
||
|
&M6502::BEQ,&M6502::SBC,&M6502::JAM,&M6502::ISB,&M6502::NOP,&M6502::SBC,&M6502::INC,&M6502::ISB,&M6502::SED,&M6502::SBC,&M6502::NOP,&M6502::ISB,&M6502::NOP,&M6502::SBC,&M6502::INC,&M6502::ISB // F
|
||
|
};
|
||
|
|
||
|
M6502::AddressModeCycleFunction M6502::T1AddressModeFunctions[256] =
|
||
|
{
|
||
|
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||
|
&M6502::brk_5_4_T1,&M6502::idx_2_4_T1,&M6502::sb_jam_T1, &M6502::idx_Undoc_T1,&M6502::zp_2_1_T1, &M6502::zp_2_1_T1, &M6502::zp_4_1_T1, &M6502::zp_4_1_T1, &M6502::ph_5_1_T1,&M6502::imm_2_1_T1, &M6502::sb_1_T1,&M6502::imm_2_1_T1, &M6502::abs_2_3_T1, &M6502::abs_2_3_T1, &M6502::abs_4_2_T1, &M6502::abs_4_2_T1, //0
|
||
|
&M6502::rel_5_8_T1,&M6502::idy_2_7_T1,&M6502::sb_jam_T1, &M6502::idy_Undoc_T1,&M6502::zpx_2_6_T1,&M6502::zpx_2_6_T1,&M6502::zpx_4_3_T1,&M6502::zpx_4_3_T1,&M6502::sb_1_T1, &M6502::absy_2_5_T1,&M6502::sb_1_T1,&M6502::absy_4_4_T1,&M6502::absx_2_5_T1,&M6502::absx_2_5_T1,&M6502::absx_4_4_T1,&M6502::absx_4_4_T1,//1
|
||
|
&M6502::jsr_5_3_T1,&M6502::idx_2_4_T1,&M6502::sb_jam_T1, &M6502::idx_Undoc_T1,&M6502::zp_2_1_T1, &M6502::zp_2_1_T1, &M6502::zp_4_1_T1, &M6502::zp_4_1_T1, &M6502::pl_5_2_T1,&M6502::imm_2_1_T1, &M6502::sb_1_T1,&M6502::imm_2_1_T1, &M6502::abs_2_3_T1, &M6502::abs_2_3_T1, &M6502::abs_4_2_T1, &M6502::abs_4_2_T1, //2
|
||
|
&M6502::rel_5_8_T1,&M6502::idy_2_7_T1,&M6502::sb_jam_T1, &M6502::idy_Undoc_T1,&M6502::zpx_2_6_T1,&M6502::zpx_2_6_T1,&M6502::zpx_4_3_T1,&M6502::zpx_4_3_T1,&M6502::sb_1_T1, &M6502::absy_2_5_T1,&M6502::sb_1_T1,&M6502::absy_4_4_T1,&M6502::absx_2_5_T1,&M6502::absx_2_5_T1,&M6502::absx_4_4_T1,&M6502::absx_4_4_T1,//3
|
||
|
&M6502::rti_5_5_T1,&M6502::idx_2_4_T1,&M6502::sb_jam_T1, &M6502::idx_Undoc_T1,&M6502::zp_2_1_T1, &M6502::zp_2_1_T1, &M6502::zp_4_1_T1, &M6502::zp_4_1_T1, &M6502::ph_5_1_T1,&M6502::imm_2_1_T1, &M6502::sb_1_T1,&M6502::imm_2_1_T1, &M6502::abs5_6_1_T1,&M6502::abs_2_3_T1, &M6502::abs_4_2_T1, &M6502::abs_4_2_T1, //4
|
||
|
&M6502::rel_5_8_T1,&M6502::idy_2_7_T1,&M6502::sb_jam_T1, &M6502::idy_Undoc_T1,&M6502::zpx_2_6_T1,&M6502::zpx_2_6_T1,&M6502::zpx_4_3_T1,&M6502::zpx_4_3_T1,&M6502::sb_1_T1, &M6502::absy_2_5_T1,&M6502::sb_1_T1,&M6502::absy_4_4_T1,&M6502::absx_2_5_T1,&M6502::absx_2_5_T1,&M6502::absx_4_4_T1,&M6502::absx_4_4_T1,//5
|
||
|
&M6502::rts_5_7_T1,&M6502::idx_2_4_T1,&M6502::sb_jam_T1, &M6502::idx_Undoc_T1,&M6502::zp_2_1_T1, &M6502::zp_2_1_T1, &M6502::zp_4_1_T1, &M6502::zp_4_1_T1, &M6502::pl_5_2_T1,&M6502::imm_2_1_T1, &M6502::sb_1_T1,&M6502::imm_2_1_T1, &M6502::abs5_6_2_T1,&M6502::abs_2_3_T1, &M6502::abs_4_2_T1, &M6502::abs_4_2_T1, //6
|
||
|
&M6502::rel_5_8_T1,&M6502::idy_2_7_T1,&M6502::sb_jam_T1, &M6502::idy_Undoc_T1,&M6502::zpx_2_6_T1,&M6502::zpx_2_6_T1,&M6502::zpx_4_3_T1,&M6502::zpx_4_3_T1,&M6502::sb_1_T1, &M6502::absy_2_5_T1,&M6502::sb_1_T1,&M6502::absy_4_4_T1,&M6502::absx_2_5_T1,&M6502::absx_2_5_T1,&M6502::absx_4_4_T1,&M6502::absx_4_4_T1,//7
|
||
|
&M6502::imm_2_1_T1,&M6502::idx_3_3_T1,&M6502::imm_2_1_T1,&M6502::idx_3_3_T1, &M6502::zp_3_1_T1, &M6502::zp_3_1_T1, &M6502::zp_2_1_T1, &M6502::zp_3_1_T1, &M6502::sb_1_T1, &M6502::imm_2_1_T1, &M6502::sb_1_T1,&M6502::imm_2_1_T1, &M6502::abs_3_2_T1, &M6502::abs_3_2_T1, &M6502::abs_3_2_T1, &M6502::abs_3_2_T1, //8
|
||
|
&M6502::rel_5_8_T1,&M6502::idy_3_6_T1,&M6502::sb_jam_T1, &M6502::idy_3_6_T1, &M6502::zpx_3_5_T1,&M6502::zpx_3_5_T1,&M6502::zpy_3_5_T1,&M6502::zpy_3_5_T1,&M6502::sb_1_T1, &M6502::absy_3_4_T1,&M6502::sb_1_T1,&M6502::absy_3_4_T1,&M6502::absx_3_4_T1,&M6502::absx_3_4_T1,&M6502::absy_3_4_T1,&M6502::absy_3_4_T1,//9
|
||
|
&M6502::imm_2_1_T1,&M6502::idx_2_4_T1,&M6502::imm_2_1_T1,&M6502::idx_2_4_T1, &M6502::zp_2_1_T1, &M6502::zp_2_1_T1, &M6502::zp_2_1_T1, &M6502::zp_2_1_T1, &M6502::sb_1_T1, &M6502::imm_2_1_T1, &M6502::sb_1_T1,&M6502::imm_2_1_T1, &M6502::abs_2_3_T1, &M6502::abs_2_3_T1, &M6502::abs_2_3_T1, &M6502::abs_2_3_T1, //A
|
||
|
&M6502::rel_5_8_T1,&M6502::idy_2_7_T1,&M6502::sb_jam_T1, &M6502::idy_2_7_T1, &M6502::zpx_2_6_T1,&M6502::zpx_2_6_T1,&M6502::zpy_2_6_T1,&M6502::zpy_2_6_T1,&M6502::sb_1_T1, &M6502::absy_2_5_T1,&M6502::sb_1_T1,&M6502::absy_4_4_T1,&M6502::absx_2_5_T1,&M6502::absx_2_5_T1,&M6502::absy_2_5_T1,&M6502::absy_2_5_T1,//B
|
||
|
&M6502::imm_2_1_T1,&M6502::idx_2_4_T1,&M6502::imm_2_1_T1,&M6502::idx_Undoc_T1,&M6502::zp_2_1_T1, &M6502::zp_2_1_T1, &M6502::zp_4_1_T1, &M6502::zp_4_1_T1, &M6502::sb_1_T1, &M6502::imm_2_1_T1, &M6502::sb_1_T1,&M6502::imm_2_1_T1, &M6502::abs_2_3_T1, &M6502::abs_2_3_T1, &M6502::abs_4_2_T1, &M6502::abs_4_2_T1, //C
|
||
|
&M6502::rel_5_8_T1,&M6502::idy_2_7_T1,&M6502::sb_jam_T1, &M6502::idy_Undoc_T1,&M6502::zpx_2_6_T1,&M6502::zpx_2_6_T1,&M6502::zpx_4_3_T1,&M6502::zpx_4_3_T1,&M6502::sb_1_T1, &M6502::absy_2_5_T1,&M6502::sb_1_T1,&M6502::absy_4_4_T1,&M6502::absx_2_5_T1,&M6502::absx_2_5_T1,&M6502::absx_4_4_T1,&M6502::absx_4_4_T1,//D
|
||
|
&M6502::imm_2_1_T1,&M6502::idx_2_4_T1,&M6502::imm_2_1_T1,&M6502::idx_Undoc_T1,&M6502::zp_2_1_T1, &M6502::zp_2_1_T1, &M6502::zp_4_1_T1, &M6502::zp_4_1_T1, &M6502::sb_1_T1, &M6502::imm_2_1_T1, &M6502::sb_1_T1,&M6502::imm_2_1_T1, &M6502::abs_2_3_T1, &M6502::abs_2_3_T1, &M6502::abs_4_2_T1, &M6502::abs_4_2_T1, //E
|
||
|
&M6502::rel_5_8_T1,&M6502::idy_2_7_T1,&M6502::sb_jam_T1, &M6502::idy_Undoc_T1,&M6502::zpx_2_6_T1,&M6502::zpx_2_6_T1,&M6502::zpx_4_3_T1,&M6502::zpx_4_3_T1,&M6502::sb_1_T1, &M6502::absy_2_5_T1,&M6502::sb_1_T1,&M6502::absy_4_4_T1,&M6502::absx_2_5_T1,&M6502::absx_2_5_T1,&M6502::absx_4_4_T1,&M6502::absx_4_4_T1 //F
|
||
|
};
|
||
|
|
||
|
void M6502::ADC(void)
|
||
|
{
|
||
|
u16 result;
|
||
|
|
||
|
result = a + value + (status & FLAG_CARRY);
|
||
|
EstablishZ(result);
|
||
|
|
||
|
if (status & FLAG_DECIMAL)
|
||
|
{
|
||
|
result = (a & 0xf) + (value & 0xf) + (status & FLAG_CARRY);
|
||
|
if (result > 0x9) result += 0x6;
|
||
|
if (result <= 0x0f) result = (result & 0xf) + (a & 0xf0) + (value & 0xf0);
|
||
|
else result = (result & 0xf) + (a & 0xf0) + (value & 0xf0) + 0x10;
|
||
|
EstablishV(result, value);
|
||
|
EstablishN(result);
|
||
|
if ((result & 0x1f0) > 0x90) result += 0x60;
|
||
|
EstablishC(result);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
EstablishC(result);
|
||
|
EstablishV(result, value);
|
||
|
EstablishN(result);
|
||
|
}
|
||
|
a = (u8)result;
|
||
|
}
|
||
|
|
||
|
void M6502::ARR(void)
|
||
|
{
|
||
|
u16 result = a & value;
|
||
|
u16 carry = status & FLAG_CARRY;
|
||
|
|
||
|
if (status & FLAG_DECIMAL)
|
||
|
{
|
||
|
u16 resultDEC = result;
|
||
|
resultDEC |= carry << 8;
|
||
|
resultDEC >>= 1;
|
||
|
SetN(carry);
|
||
|
SetZ(resultDEC == 0);
|
||
|
SetV((resultDEC ^ result) & 0x40);
|
||
|
if (((result & 0xf) + (result & 0x1)) > 0x5) resultDEC = (resultDEC & 0xf0) | ((resultDEC + 0x6) & 0xf);
|
||
|
if (((result & 0xf0) + (result & 0x10)) > 0x50)
|
||
|
{
|
||
|
resultDEC = (resultDEC & 0x0f) | ((resultDEC + 0x60) & 0xf0);
|
||
|
SetC();
|
||
|
}
|
||
|
else ClearC();
|
||
|
a = (u8)resultDEC;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
result |= carry << 8;
|
||
|
result >>= 1;
|
||
|
EstablishNZ(result);
|
||
|
|
||
|
u16 and40 = result & 0x40;
|
||
|
SetC(and40 != 0);
|
||
|
SetV(and40 ^ ((result & 0x20) << 1));
|
||
|
a = (u8)result;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void M6502::SBC(void)
|
||
|
{
|
||
|
u16 result = a - value - ((status & FLAG_CARRY) ? 0 : 1);
|
||
|
if (status & FLAG_DECIMAL)
|
||
|
{
|
||
|
u16 tmp_a;
|
||
|
tmp_a = (a & 0xf) - (value & 0xf) - ((status & FLAG_CARRY) ? 0 : 1);
|
||
|
if (tmp_a & 0x10) tmp_a = ((tmp_a - 6) & 0xf) | ((a & 0xf0) - (value & 0xf0) - 0x10);
|
||
|
else tmp_a = (tmp_a & 0xf) | ((a & 0xf0) - (value & 0xf0));
|
||
|
if (tmp_a & 0x100) tmp_a -= 0x60;
|
||
|
SetC(result < 0x100);
|
||
|
EstablishV(result, value ^ 0xff);
|
||
|
EstablishNZ(result);
|
||
|
a = (u8)tmp_a;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
EstablishNZ(result);
|
||
|
SetC(result < 0x100);
|
||
|
EstablishV(result, value ^ 0xff);
|
||
|
a = (u8)result;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void M6502::absx_2_5_T3(void)
|
||
|
{
|
||
|
u16 startpage = ea & 0xFF00;
|
||
|
ea += x;
|
||
|
if (startpage != (ea & 0xFF00))
|
||
|
{
|
||
|
BUS_READ(startpage | (ea & 0xff));
|
||
|
addressModeCycleFn = &M6502::absx_2_5_T4;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
value = BUS_READ(ea);
|
||
|
ExecuteOpcode();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void M6502::absy_2_5_T3(void)
|
||
|
{
|
||
|
u16 startpage = ea & 0xFF00;
|
||
|
ea += y;
|
||
|
if (startpage != (ea & 0xFF00))
|
||
|
{
|
||
|
BUS_READ(startpage | (ea & 0xff));
|
||
|
addressModeCycleFn = &M6502::absy_2_5_T4;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
value = BUS_READ(ea);
|
||
|
ExecuteOpcode();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void M6502::idy_2_7_T4(void)
|
||
|
{
|
||
|
u16 startpage = ea & 0xFF00;
|
||
|
ea += y;
|
||
|
if (startpage != (ea & 0xFF00))
|
||
|
{
|
||
|
BUS_READ(startpage | (ea & 0xff));
|
||
|
addressModeCycleFn = &M6502::idy_2_7_T5;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
value = BUS_READ(ea);
|
||
|
ExecuteOpcode();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void M6502::rel_5_8_T2(void)
|
||
|
{
|
||
|
BUS_READ(oldpc);
|
||
|
pc = oldpc + ra;
|
||
|
if ((oldpc & 0xFF00) == (pc & 0xFF00))
|
||
|
{
|
||
|
BranchTakenMaskingInterrupt = true;
|
||
|
addressModeCycleFn = &M6502::InstructionFetch; // Opcode has already been executed in T1 so just move on to the next instruction.
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
addressModeCycleFn = &M6502::rel_5_8_T3;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// When executing a BRK and an interrupt condition is triggered between T0 and T4 the BRK morphs into the interrupt instruction.
|
||
|
// We check here if we continue on executing the BRK or morph and take the interrupt.
|
||
|
void M6502::brk_5_4_T4(void)
|
||
|
{
|
||
|
#ifdef SUPPORT_NMI
|
||
|
if (NMIPending)
|
||
|
{
|
||
|
NMIPending = 0;
|
||
|
NMI_T4();
|
||
|
return;
|
||
|
}
|
||
|
#endif
|
||
|
#ifdef SUPPORT_IRQ
|
||
|
if (IRQPending && !IRQDisabled())
|
||
|
{
|
||
|
IRQPending = 0;
|
||
|
IRQ_T4();
|
||
|
return;
|
||
|
}
|
||
|
#endif
|
||
|
Push(status | FLAG_CONSTANT | FLAG_BREAK);
|
||
|
addressModeCycleFn = &M6502::brk_5_4_T5;
|
||
|
}
|
||
|
|
||
|
// It is possible for a BRK/IRQ to mask a NMI for short burts of NMI assertions.
|
||
|
// If the NMI asserts and un-asserts between IRQ_T4 and IRQ_T6 this will occur.
|
||
|
// This occurs on real hardware and it will be emulated correctly.
|
||
|
// The processor has already commited to fetching the interrupt vectors and will now complete this process.
|
||
|
// If the NMI now un-asserts between now and the end of IRQ_T6 it will be missed/masked.
|
||
|
// The ability for a BRK/IRQ to turn into a NMI shows how the designers of the 6502 anticipated this masking and kept it to a minimum of only four 1/2 cycles!
|
||
|
// But then again, perhpas not, as a NMI that asserts after IRQ_T4 and remains asserted will not be processed until the first instruction of the IRQ routine has completed (see InstructionFetchIRQ).
|
||
|
void M6502::IRQ_T4(void)
|
||
|
{
|
||
|
#ifdef SUPPORT_NMI
|
||
|
if (NMIPending)
|
||
|
{
|
||
|
NMIPending = 0;
|
||
|
NMI_T4();
|
||
|
return;
|
||
|
}
|
||
|
#endif
|
||
|
ClearB();
|
||
|
Push(status);
|
||
|
addressModeCycleFn = &M6502::IRQ_T5;
|
||
|
}
|
||
|
|
||
|
// Interrupts are polled before starting a new instruction
|
||
|
// T0 of every address mode (except reset).
|
||
|
void M6502::InstructionFetch()
|
||
|
{
|
||
|
opcode = BUS_READ(pc); // Technically the InstructionFetch cycle T0 is part of the previous instruction's execution and the check for interrupts occurs after this fetch.
|
||
|
|
||
|
#ifdef SUPPORT_NMI
|
||
|
if (NMIPending)
|
||
|
addressModeCycleFn = &M6502::NMI_T1;
|
||
|
else
|
||
|
#endif // SUPPORT_NMI
|
||
|
#ifdef SUPPORT_IRQ
|
||
|
if (IRQPending && !IRQDisabled())
|
||
|
{
|
||
|
IRQPending = 0;
|
||
|
addressModeCycleFn = &M6502::IRQ_T1;
|
||
|
}
|
||
|
else
|
||
|
#endif // SUPPORT_IRQ
|
||
|
{
|
||
|
pc++;
|
||
|
addressModeCycleFn = T1AddressModeFunctions[opcode];
|
||
|
opcodeCycleFn = opcodeFunctions[opcode];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef SUPPORT_IRQ
|
||
|
// If a NMI asserts too late during the IRQ execution ie after IRQ_T4 then it must wait one more instruction. So no polling is performed during this fetch.
|
||
|
// This is an idiosyncrasy of the real hardware and will be emulated using this fuction.
|
||
|
void M6502::InstructionFetchIRQ()
|
||
|
{
|
||
|
opcode = BUS_READ(pc++); // T0
|
||
|
addressModeCycleFn = T1AddressModeFunctions[opcode];
|
||
|
opcodeCycleFn = opcodeFunctions[opcode];
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// A single step emulates both real 6502 1/2 cycles.
|
||
|
// On a real 6502, interrupts can be asserted between 1/2 cycles. When this occurs the hardware effectively ignores it for a further 1/2 cycle anyway.
|
||
|
// Here, interrupts are polled at the start of a cycle (in an instruction fetch cycle) emulating this behaviour.
|
||
|
// High frequency (1/2 cycle) bursts of interrupts assertions and un-assertions occuring mid full cycle will be missed by the hardware anyway.
|
||
|
void M6502::Step(void)
|
||
|
{
|
||
|
bool irq;
|
||
|
|
||
|
// If an IRQ occurs during a CLI then it will not take effect until the instruction after the CLI has been executed.
|
||
|
// To emulate this, CLI simply sets CLIMaskingInterrupt flag.
|
||
|
// Similar behaviour can be witnessed with the 3 cycle branch taken instruction.
|
||
|
#ifdef SUPPORT_IRQ
|
||
|
irq = IRQ.IsAsserted();
|
||
|
if (irq && ((status & FLAG_INTERRUPT) == 0) && !CLIMaskingInterrupt && !BranchTakenMaskingInterrupt)
|
||
|
IRQPending = 1;
|
||
|
if (!irq)
|
||
|
IRQPending = 0;
|
||
|
#endif // SUPPORT_IRQ
|
||
|
|
||
|
#ifdef SUPPORT_NMI
|
||
|
NMIPending = NMI.IsAsserted() && !CLIMaskingInterrupt && !BranchTakenMaskingInterrupt;
|
||
|
#endif // SUPPORT_NMI
|
||
|
|
||
|
if (CLIMaskingInterrupt) // If so we have delayed the IRQ long enough for the next instruction to now start. The CLI will then take effect after that instruction completes executing.
|
||
|
CLIMaskingInterrupt = false;
|
||
|
if (BranchTakenMaskingInterrupt) // If so we have delayed the IRQ long enough for the next instruction to now start.
|
||
|
BranchTakenMaskingInterrupt = false;
|
||
|
|
||
|
#ifdef SUPPORT_RDY_HALTING
|
||
|
if (!Halted())
|
||
|
{
|
||
|
CheckForHalt();
|
||
|
(this->*M6502::addressModeCycleFn)();
|
||
|
}
|
||
|
#else
|
||
|
(this->*M6502::addressModeCycleFn)();
|
||
|
#endif // SUPPORT_RDY_HALTING
|
||
|
}
|
||
|
|
||
|
void M6502::Reset(void)
|
||
|
{
|
||
|
CLIMaskingInterrupt = false;
|
||
|
BranchTakenMaskingInterrupt = false;
|
||
|
#ifdef SUPPORT_IRQ
|
||
|
IRQ.Reset();
|
||
|
IRQPending = 0;
|
||
|
#endif // SUPPORT_IRQ
|
||
|
#ifdef SUPPORT_NMI
|
||
|
NMI.Reset();
|
||
|
NMIPending = 0;
|
||
|
#endif // SUPPORT_NMI
|
||
|
#ifdef SUPPORT_RDY_HALTING
|
||
|
RDYCounter = 0; // Don't know if the real hardware does this.
|
||
|
RDYAsserted = 0;
|
||
|
RDYHalted = 0;
|
||
|
#endif // SUPPORT_RDY_HALTING
|
||
|
Reset_T0();
|
||
|
}
|
||
|
|
||
|
#ifdef SUPPORT_RDY_HALTING
|
||
|
void M6502::RDY(bool asserted)
|
||
|
{
|
||
|
if (asserted && (RDYHalted == 0)) RDYCounter = 3;
|
||
|
RDYAsserted = asserted;
|
||
|
if (RDYHalted && !asserted) RDYHalted = 0;
|
||
|
}
|
||
|
|
||
|
void M6502::CheckForHalt()
|
||
|
{
|
||
|
if ((RDYHalted == 0) && RDYAsserted && RDYCounter)
|
||
|
{
|
||
|
RDYCounter--;
|
||
|
if (RDYCounter == 0) RDYHalted = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
u8 M6502::BusRead(u16 address)
|
||
|
{
|
||
|
if ((RDYHalted == 0) && RDYAsserted) RDYHalted = 1;
|
||
|
return dataBusReadFn(address);
|
||
|
}
|
||
|
#endif // SUPPORT_RDY_HALTING
|