cog/Frameworks/libsidplay/sidplay-residfp-code/.svn/pristine/b3/b3298071e359ba474f6ef71cf2e...

2197 lines
62 KiB
Plaintext

/*
* This file is part of libsidplayfp, a SID player engine.
*
* Copyright 2011-2014 Leandro Nini <drfiemost@users.sourceforge.net>
* Copyright 2007-2010 Antti Lankila
* Copyright 2000 Simon White
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "mos6510.h"
#include "sidplayfp/event.h"
#include "sidendian.h"
#include "opcodes.h"
#ifdef DEBUG
# include <cstdio>
# include "mos6510debug.h"
#endif
namespace libsidplayfp
{
#ifdef PC64_TESTSUITE
# include <stdlib.h>
/**
* CHR$ conversion table (0x01 = no output)
*/
static const char CHRtab[256] =
{
0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x0d,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x20,0x21,0x01,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
0x40,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x5b,0x24,0x5d,0x20,0x20,
// alternative: CHR$(92=0x5c) => ISO Latin-1(0xa3)
0x2d,0x23,0x7c,0x2d,0x2d,0x2d,0x2d,0x7c,0x7c,0x5c,0x5c,0x2f,0x5c,0x5c,0x2f,0x2f,
0x5c,0x23,0x5f,0x23,0x7c,0x2f,0x58,0x4f,0x23,0x7c,0x23,0x2b,0x7c,0x7c,0x26,0x5c,
// 0x80-0xFF
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,
0x20,0x7c,0x23,0x2d,0x2d,0x7c,0x23,0x7c,0x23,0x2f,0x7c,0x7c,0x2f,0x5c,0x5c,0x2d,
0x2f,0x2d,0x2d,0x7c,0x7c,0x7c,0x7c,0x2d,0x2d,0x2d,0x2f,0x5c,0x5c,0x2f,0x2f,0x23,
0x20,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x2b,0x7c,0x7c,0x26,0x5c,
0x20,0x7c,0x23,0x2d,0x2d,0x7c,0x23,0x7c,0x23,0x2f,0x7c,0x7c,0x2f,0x5c,0x5c,0x2d,
0x2f,0x2d,0x2d,0x7c,0x7c,0x7c,0x7c,0x2d,0x2d,0x2d,0x2f,0x5c,0x5c,0x2f,0x2f,0x23
};
static char filetmp[0x100];
static int filepos = 0;
#endif // PC64_TESTSUITE
/**
* Magic value for lxa and ane undocumented instructions.
* Magic may be EE, EF, FE or FF, but most emulators seem to use EE.
* Based on tests on a couple of chips at
* http://visual6502.org/wiki/index.php?title=6502_Opcode_8B_(XAA,_ANE)
* the value of magic for the MOS 6510 is FF.
* However the Lorentz test suite assumes this to be EE.
*/
const uint8_t magic =
#ifdef PC64_TESTSUITE
0xee
#else
0xff
#endif
;
//-------------------------------------------------------------------------//
/**
* When AEC signal is high, no stealing is possible.
*/
void MOS6510::eventWithoutSteals()
{
const ProcessorCycle &instr = instrTable[cycleCount++];
(this->*(instr.func)) ();
eventContext.schedule(m_nosteal, 1);
}
/**
* When AEC signal is low, steals permitted.
*/
void MOS6510::eventWithSteals()
{
if (instrTable[cycleCount].nosteal)
{
const ProcessorCycle &instr = instrTable[cycleCount++];
(this->*(instr.func)) ();
eventContext.schedule(m_steal, 1);
}
else
{
/* Even while stalled, the CPU can still process first clock of
* interrupt delay, but only the first one. */
if (interruptCycle == cycleCount)
{
interruptCycle --;
}
}
}
/**
* Handle bus access signals. When RDY line is asserted, the CPU
* will pause when executing the next read operation.
*
* @param rdy new state for RDY signal
*/
void MOS6510::setRDY(bool newRDY)
{
rdy = newRDY;
if (rdy)
{
eventContext.cancel(m_steal);
eventContext.schedule(m_nosteal, 0, EVENT_CLOCK_PHI2);
}
else
{
eventContext.cancel(m_nosteal);
eventContext.schedule(m_steal, 0, EVENT_CLOCK_PHI2);
}
}
/**
* Push P on stack, decrement S.
*/
void MOS6510::PushSR()
{
const uint_least16_t addr = endian_16(SP_PAGE, Register_StackPointer);
cpuWrite(addr, flags.get());
Register_StackPointer--;
}
/**
* increment S, Pop P off stack.
*/
void MOS6510::PopSR()
{
// Get status register off stack
Register_StackPointer++;
const uint_least16_t addr = endian_16(SP_PAGE, Register_StackPointer);
flags.set(cpuRead(addr));
flags.B = true;
calculateInterruptTriggerCycle();
}
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
// Interrupt Routines //
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
/**
* This forces the CPU to abort whatever it is doing and immediately
* enter the RST interrupt handling sequence. The implementation is
* not compatible: instructions actually get aborted mid-execution.
* However, there is no possible way to trigger this signal from
* programs, so it's OK.
*/
void MOS6510::triggerRST()
{
Initialise();
cycleCount = BRKn << 3;
rstFlag = true;
calculateInterruptTriggerCycle();
}
/**
* Trigger NMI interrupt on the CPU. Calling this method
* flags that CPU must enter the NMI routine at earliest
* opportunity. There is no way to cancel NMI request once
* given.
*/
void MOS6510::triggerNMI()
{
nmiFlag = true;
calculateInterruptTriggerCycle();
/* maybe process 1 clock of interrupt delay. */
if (!rdy)
{
eventContext.cancel(m_steal);
eventContext.schedule(m_steal, 0, EVENT_CLOCK_PHI2);
}
}
/**
* Pull IRQ line low on CPU.
*/
void MOS6510::triggerIRQ()
{
irqAssertedOnPin = true;
calculateInterruptTriggerCycle();
/* maybe process 1 clock of interrupt delay. */
if (!rdy && interruptCycle == cycleCount)
{
eventContext.cancel(m_steal);
eventContext.schedule(m_steal, 0, EVENT_CLOCK_PHI2);
}
}
/**
* Inform CPU that IRQ is no longer pulled low.
*/
void MOS6510::clearIRQ()
{
irqAssertedOnPin = false;
calculateInterruptTriggerCycle();
}
void MOS6510::interruptsAndNextOpcode()
{
if (cycleCount > interruptCycle + 2)
{
#ifdef DEBUG
if (dodump)
{
const event_clock_t cycles = eventContext.getTime(EVENT_CLOCK_PHI2);
fprintf(m_fdbg, "****************************************************\n");
fprintf(m_fdbg, " interrupt (%d)\n", static_cast<int>cycles);
fprintf(m_fdbg, "****************************************************\n");
MOS6510Debug::DumpState(cycles, *this);
}
#endif
cpuRead(Register_ProgramCounter);
cycleCount = BRKn << 3;
flags.B = false;
interruptCycle = MAX;
} else {
fetchNextOpcode();
}
}
void MOS6510::fetchNextOpcode()
{
#ifdef DEBUG
if (dodump)
{
MOS6510Debug::DumpState(eventContext.getTime(EVENT_CLOCK_PHI2), *this);
}
instrStartPC = Register_ProgramCounter;
#endif
cycleCount = cpuRead(Register_ProgramCounter) << 3;
Register_ProgramCounter++;
if (!rstFlag && !nmiFlag && !(!flags.I && irqAssertedOnPin))
{
interruptCycle = MAX;
}
if (interruptCycle != MAX)
{
interruptCycle = -MAX;
}
}
/**
* Evaluate when to execute an interrupt. Calling this method can also
* result in the decision that no interrupt at all needs to be scheduled.
*/
void MOS6510::calculateInterruptTriggerCycle()
{
/* Interrupt cycle not going to trigger? */
if (interruptCycle == MAX)
{
if (rstFlag || nmiFlag || (!flags.I && irqAssertedOnPin))
{
interruptCycle = cycleCount;
}
}
}
void MOS6510::IRQLoRequest()
{
endian_16lo8(Register_ProgramCounter, cpuRead(Cycle_EffectiveAddress));
}
void MOS6510::IRQHiRequest()
{
endian_16hi8(Register_ProgramCounter, cpuRead(Cycle_EffectiveAddress + 1));
}
/**
* Read the next opcode byte from memory (and throw it away)
*/
void MOS6510::throwAwayFetch()
{
cpuRead(Register_ProgramCounter);
}
/**
* Issue throw-away read. Some people use these to ACK CIA IRQs.
*/
void MOS6510::throwAwayRead()
{
cpuRead(Cycle_HighByteWrongEffectiveAddress);
}
/**
* Fetch value, increment PC.
*
* Addressing Modes:
* - Immediate
* - Relative
*/
void MOS6510::FetchDataByte()
{
Cycle_Data = cpuRead(Register_ProgramCounter);
if (flags.B)
{
Register_ProgramCounter++;
}
#ifdef DEBUG
instrOperand = Cycle_Data;
#endif
}
/**
* Fetch low address byte, increment PC.
*
* Addressing Modes:
* - Stack Manipulation
* - Absolute
* - Zero Page
* - Zero Page Indexed
* - Absolute Indexed
* - Absolute Indirect
*/
void MOS6510::FetchLowAddr()
{
Cycle_EffectiveAddress = cpuRead(Register_ProgramCounter);
Register_ProgramCounter++;
#ifdef DEBUG
instrOperand = Cycle_EffectiveAddress;
#endif
}
/**
* Read from address, add index register X to it.
*
* Addressing Modes:
* - Zero Page Indexed
*/
void MOS6510::FetchLowAddrX()
{
FetchLowAddr();
Cycle_EffectiveAddress = (Cycle_EffectiveAddress + Register_X) & 0xFF;
}
/**
* Read from address, add index register Y to it.
*
* Addressing Modes:
* - Zero Page Indexed
*/
void MOS6510::FetchLowAddrY()
{
FetchLowAddr();
Cycle_EffectiveAddress = (Cycle_EffectiveAddress + Register_Y) & 0xFF;
}
/**
* Fetch high address byte, increment PC (Absolute Addressing).
*
* Low byte must have been obtained first!
*
* Addressing Modes:
* - Absolute
*/
void MOS6510::FetchHighAddr()
{ // Get the high byte of an address from memory
endian_16hi8(Cycle_EffectiveAddress, cpuRead(Register_ProgramCounter));
Register_ProgramCounter++;
#ifdef DEBUG
endian_16hi8(instrOperand, endian_16hi8(Cycle_EffectiveAddress));
#endif
}
/**
* Fetch high byte of address, add index register X to low address byte,
* increment PC.
*
* Addressing Modes:
* - Absolute Indexed
*/
void MOS6510::FetchHighAddrX()
{
FetchHighAddr();
Cycle_HighByteWrongEffectiveAddress = (Cycle_EffectiveAddress & 0xff00) | ((Cycle_EffectiveAddress + Register_X) & 0xff);
Cycle_EffectiveAddress += Register_X;
}
/**
* Same as #FetchHighAddrX except dosen't worry about page crossing.
*/
void MOS6510::FetchHighAddrX2()
{
FetchHighAddrX();
if (Cycle_EffectiveAddress == Cycle_HighByteWrongEffectiveAddress)
cycleCount++;
}
/**
* Fetch high byte of address, add index register Y to low address byte,
* increment PC.
*
* Addressing Modes:
* - Absolute Indexed
*/
void MOS6510::FetchHighAddrY()
{
FetchHighAddr();
Cycle_HighByteWrongEffectiveAddress = (Cycle_EffectiveAddress & 0xff00) | ((Cycle_EffectiveAddress + Register_Y) & 0xff);
Cycle_EffectiveAddress += Register_Y;
}
/**
* Same as #FetchHighAddrY except dosen't worry about page crossing.
*/
void MOS6510::FetchHighAddrY2()
{
FetchHighAddrY();
if (Cycle_EffectiveAddress == Cycle_HighByteWrongEffectiveAddress)
cycleCount++;
}
/**
* Fetch pointer address low, increment PC.
*
* Addressing Modes:
* - Absolute Indirect
* - Indirect indexed (post Y)
*/
void MOS6510::FetchLowPointer()
{
Cycle_Pointer = cpuRead(Register_ProgramCounter);
Register_ProgramCounter++;
#ifdef DEBUG
instrOperand = Cycle_Pointer;
#endif
}
/**
* Add X to it.
*
* Addressing Modes:
* - Indexed Indirect (pre X)
*/
void MOS6510::FetchLowPointerX()
{
endian_16lo8(Cycle_Pointer, (Cycle_Pointer + Register_X) & 0xFF);
}
/**
* Fetch pointer address high, increment PC.
*
* Addressing Modes:
* - Absolute Indirect
*/
void MOS6510::FetchHighPointer()
{
endian_16hi8(Cycle_Pointer, cpuRead(Register_ProgramCounter));
Register_ProgramCounter++;
#ifdef DEBUG
endian_16hi8(instrOperand, endian_16hi8(Cycle_Pointer));
#endif
}
/**
* Fetch effective address low.
*
* Addressing Modes:
* - Indirect
* - Indexed Indirect (pre X)
* - Indirect indexed (post Y)
*/
void MOS6510::FetchLowEffAddr()
{
Cycle_EffectiveAddress = cpuRead(Cycle_Pointer);
}
/**
* Fetch effective address high.
*
* Addressing Modes:
* - Indirect
* - Indexed Indirect (pre X)
*/
void MOS6510::FetchHighEffAddr()
{
endian_16lo8(Cycle_Pointer, (Cycle_Pointer + 1) & 0xff);
endian_16hi8(Cycle_EffectiveAddress, cpuRead(Cycle_Pointer));
}
/**
* Fetch effective address high, add Y to low byte of effective address.
*
* Addressing Modes:
* - Indirect indexed (post Y)
*/
void MOS6510::FetchHighEffAddrY()
{
FetchHighEffAddr();
Cycle_HighByteWrongEffectiveAddress = (Cycle_EffectiveAddress & 0xff00) | ((Cycle_EffectiveAddress + Register_Y) & 0xff);
Cycle_EffectiveAddress += Register_Y;
}
/** Same as #FetchHighEffAddrY except dosen't worry about page crossing. */
void MOS6510::FetchHighEffAddrY2()
{
FetchHighEffAddrY();
if (Cycle_EffectiveAddress == Cycle_HighByteWrongEffectiveAddress)
cycleCount++;
}
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
// Common Data Accessing Routines //
// Data Accessing operations as described in 64doc by John West and //
// Marko Makela //
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
void MOS6510::FetchEffAddrDataByte()
{
Cycle_Data = cpuRead(Cycle_EffectiveAddress);
}
/**
* Write Cycle_Data to effective address.
*/
void MOS6510::PutEffAddrDataByte()
{
cpuWrite(Cycle_EffectiveAddress, Cycle_Data);
}
/**
* Push Program Counter Low Byte on stack, decrement S.
*/
void MOS6510::PushLowPC()
{
const uint_least16_t addr = endian_16(SP_PAGE, Register_StackPointer);
cpuWrite(addr, endian_16lo8(Register_ProgramCounter));
Register_StackPointer--;
}
/**
* Push Program Counter High Byte on stack, decrement S.
*/
void MOS6510::PushHighPC()
{
const uint_least16_t addr = endian_16(SP_PAGE, Register_StackPointer);
cpuWrite(addr, endian_16hi8(Register_ProgramCounter));
Register_StackPointer--;
}
/**
* Increment stack and pull program counter low byte from stack.
*/
void MOS6510::PopLowPC()
{
Register_StackPointer++;
const uint_least16_t addr = endian_16(SP_PAGE, Register_StackPointer);
endian_16lo8(Cycle_EffectiveAddress, cpuRead(addr));
}
/**
* Increment stack and pull program counter high byte from stack.
*/
void MOS6510::PopHighPC()
{
Register_StackPointer++;
const uint_least16_t addr = endian_16(SP_PAGE, Register_StackPointer);
endian_16hi8(Cycle_EffectiveAddress, cpuRead(addr));
}
void MOS6510::WasteCycle() {}
void MOS6510::brkPushLowPC()
{
PushLowPC();
if (rstFlag)
{
/* rst = %10x */
Cycle_EffectiveAddress = 0xfffc;
}
else if (nmiFlag)
{
/* nmi = %01x */
Cycle_EffectiveAddress = 0xfffa;
}
else
{
/* irq = %11x */
Cycle_EffectiveAddress = 0xfffe;
}
rstFlag = false;
nmiFlag = false;
calculateInterruptTriggerCycle();
}
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
// Common Instruction Opcodes //
// See and 6510 Assembly Book for more information on these instructions //
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
void MOS6510::brk_instr()
{
PushSR();
flags.B = true;
flags.I = true;
}
void MOS6510::cld_instr()
{
flags.D = false;
interruptsAndNextOpcode();
}
void MOS6510::cli_instr()
{
flags.I = false;
calculateInterruptTriggerCycle();
interruptsAndNextOpcode();
}
void MOS6510::jmp_instr()
{
doJSR();
interruptsAndNextOpcode();
}
void MOS6510::doJSR()
{
Register_ProgramCounter = Cycle_EffectiveAddress;
#ifdef PC64_TESTSUITE
// trap handlers
// Print character
if (Register_ProgramCounter == 0xffd2)
{
const char ch = CHRtab[Register_Accumulator];
switch (ch)
{
case 0:
break;
case 1:
fprintf(stderr, " ");
break;
case 0xd:
fprintf(stderr, "\n");
filepos = 0;
break;
default:
filetmp[filepos++] = ch;// - 'A' + 'a';
fprintf(stderr, "%c", ch);
}
}
// Load
else if (Register_ProgramCounter == 0xe16f)
{
filetmp[filepos] = '\0';
loadFile(filetmp);
}
// Stop
else if (Register_ProgramCounter == 0x8000
|| Register_ProgramCounter == 0xa474)
{
exit(0);
}
#endif // PC64_TESTSUITE
}
void MOS6510::pha_instr()
{
const uint_least16_t addr = endian_16(SP_PAGE, Register_StackPointer);
cpuWrite(addr, Register_Accumulator);
Register_StackPointer--;
}
/**
* RTI does not delay the IRQ I flag change as it is set 3 cycles before
* the end of the opcode, and thus the 6510 has enough time to call the
* interrupt routine as soon as the opcode ends, if necessary.
*/
void MOS6510::rti_instr()
{
#ifdef DEBUG
if (dodump)
fprintf (m_fdbg, "****************************************************\n\n");
#endif
Register_ProgramCounter = Cycle_EffectiveAddress;
interruptsAndNextOpcode();
}
void MOS6510::rts_instr()
{
cpuRead(Cycle_EffectiveAddress);
Register_ProgramCounter = Cycle_EffectiveAddress;
Register_ProgramCounter++;
}
void MOS6510::sed_instr()
{
flags.D = true;
interruptsAndNextOpcode();
}
void MOS6510::sei_instr()
{
flags.I = true;
interruptsAndNextOpcode();
if (!rstFlag && !nmiFlag && interruptCycle != MAX)
interruptCycle = MAX;
}
void MOS6510::sta_instr()
{
Cycle_Data = Register_Accumulator;
PutEffAddrDataByte();
}
void MOS6510::stx_instr()
{
Cycle_Data = Register_X;
PutEffAddrDataByte();
}
void MOS6510::sty_instr()
{
Cycle_Data = Register_Y;
PutEffAddrDataByte();
}
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
// Common Instruction Undocumented Opcodes //
// See documented 6502-nmo.opc by Adam Vardy for more details //
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
/**
* Undocumented - This opcode stores the result of A AND X AND the high
* byte of the target address of the operand +1 in memory.
*/
void MOS6510::axa_instr()
{
Cycle_Data = Register_X & Register_Accumulator & (endian_16hi8(Cycle_EffectiveAddress) + 1);
if (Cycle_HighByteWrongEffectiveAddress != Cycle_EffectiveAddress)
Cycle_EffectiveAddress = endian_16(Cycle_Data, endian_16lo8(Cycle_EffectiveAddress));
PutEffAddrDataByte();
}
/**
* Undocumented - AXS ANDs the contents of the A and X registers (without changing the
* contents of either register) and stores the result in memory.
* AXS does not affect any flags in the processor status register.
*/
void MOS6510::axs_instr()
{
Cycle_Data = Register_Accumulator & Register_X;
PutEffAddrDataByte();
}
#if 0 // Not required - Operation performed By another method
// Undocumented - HLT crashes the microprocessor. When this opcode is executed, program
// execution ceases. No hardware interrupts will execute either. The author
// has characterized this instruction as a halt instruction since this is the
// most straightforward explanation for this opcode's behaviour. Only a reset
// will restart execution. This opcode leaves no trace of any operation
// performed! No registers affected.
void MOS6510::hlt_instr() {}
#endif
/**
* Undocumented - This opcode ANDs the contents of the Y register with <ab+1> and stores the
* result in memory.
*/
void MOS6510::say_instr()
{
Cycle_Data = Register_Y & (endian_16hi8(Cycle_EffectiveAddress) + 1);
if (Cycle_HighByteWrongEffectiveAddress != Cycle_EffectiveAddress)
Cycle_EffectiveAddress = endian_16(Cycle_Data, endian_16lo8(Cycle_EffectiveAddress));
PutEffAddrDataByte();
}
/**
* Undocumented - This opcode ANDs the contents of the X register with <ab+1> and stores the
* result in memory.
*/
void MOS6510::xas_instr()
{
Cycle_Data = Register_X & (endian_16hi8(Cycle_EffectiveAddress) + 1);
if (Cycle_HighByteWrongEffectiveAddress != Cycle_EffectiveAddress)
Cycle_EffectiveAddress = endian_16(Cycle_Data, endian_16lo8(Cycle_EffectiveAddress));
PutEffAddrDataByte();
}
/**
* BCD adding.
*/
void MOS6510::doADC()
{
const unsigned int C = flags.C ? 1 : 0;
const unsigned int A = Register_Accumulator;
const unsigned int s = Cycle_Data;
const unsigned int regAC2 = A + s + C;
if (flags.D)
{ // BCD mode
unsigned int lo = (A & 0x0f) + (s & 0x0f) + C;
unsigned int hi = (A & 0xf0) + (s & 0xf0);
if (lo > 0x09)
lo += 0x06;
if (lo > 0x0f)
hi += 0x10;
flags.Z = !(regAC2 & 0xff);
flags.N = hi & 0x80;
flags.V = ((hi ^ A) & 0x80) && !((A ^ s) & 0x80);
if (hi > 0x90)
hi += 0x60;
flags.C = hi > 0xff;
Register_Accumulator = (hi | (lo & 0x0f));
}
else
{ // Binary mode
flags.C = regAC2 > 0xff;
flags.V = ((regAC2 ^ A) & 0x80) && !((A ^ s) & 0x80);
flags.setNZ(Register_Accumulator = regAC2 & 0xff);
}
}
/**
* BCD subtracting.
*/
void MOS6510::doSBC()
{
const unsigned int C = flags.C? 0 : 1;
const unsigned int A = Register_Accumulator;
const unsigned int s = Cycle_Data;
const unsigned int regAC2 = A - s - C;
flags.C = regAC2 < 0x100;
flags.V = ((regAC2 ^ A) & 0x80) && ((A ^ s) & 0x80);
flags.setNZ(regAC2);
if (flags.D)
{ // BCD mode
unsigned int lo = (A & 0x0f) - (s & 0x0f) - C;
unsigned int hi = (A & 0xf0) - (s & 0xf0);
if (lo & 0x10)
{
lo -= 0x06;
hi -= 0x10;
}
if (hi & 0x100)
hi -= 0x60;
Register_Accumulator = (hi | (lo & 0x0f));
}
else
{ // Binary mode
Register_Accumulator = regAC2 & 0xff;
}
}
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
// Generic Instruction Addressing Routines //
//-------------------------------------------------------------------------/
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
// Generic Instruction Opcodes //
// See and 6510 Assembly Book for more information on these instructions //
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
void MOS6510::adc_instr()
{
doADC();
interruptsAndNextOpcode();
}
void MOS6510::and_instr()
{
flags.setNZ(Register_Accumulator &= Cycle_Data);
interruptsAndNextOpcode();
}
/**
* Undocumented - For a detailed explanation of this opcode look at:
* http://visual6502.org/wiki/index.php?title=6502_Opcode_8B_(XAA,_ANE)
*/
void MOS6510::ane_instr()
{
flags.setNZ(Register_Accumulator = (Register_Accumulator | magic) & Register_X & Cycle_Data);
interruptsAndNextOpcode();
}
void MOS6510::asl_instr()
{
PutEffAddrDataByte();
flags.C = Cycle_Data & 0x80;
flags.setNZ(Cycle_Data <<= 1);
}
void MOS6510::asla_instr()
{
flags.C = Register_Accumulator & 0x80;
flags.setNZ(Register_Accumulator <<= 1);
interruptsAndNextOpcode();
}
void MOS6510::branch_instr(bool condition)
{
/*
* 2 cycles spent before arriving here. spend 0 - 2 cycles here;
* - condition false: Continue immediately to FetchNextInstr.
*
* Otherwise read the byte following the opcode (which is already scheduled to occur on this cycle).
* This effort is wasted. Then calculate address of the branch target. If branch is on same page,
* then continue at that insn on next cycle (this delays IRQs by 1 clock for some reason, allegedly).
*
* If the branch is on different memory page, issue a spurious read with wrong high byte before
* continuing at the correct address.
*/
if (condition)
{
// issue the spurious read for next insn here.
cpuRead(Register_ProgramCounter);
Cycle_HighByteWrongEffectiveAddress = (Register_ProgramCounter & 0xff00) | ((Register_ProgramCounter + static_cast<int8_t>(Cycle_Data)) & 0xff);
Cycle_EffectiveAddress = Register_ProgramCounter + static_cast<int8_t>(Cycle_Data);
// Check for page boundary crossing
if (Cycle_EffectiveAddress == Cycle_HighByteWrongEffectiveAddress)
{
cycleCount ++;
// Hack: delay the interrupt past this instruction.
if (interruptCycle >> 3 == cycleCount >> 3)
interruptCycle += 2;
}
Register_ProgramCounter = Cycle_EffectiveAddress;
}
else
{
// branch not taken: skip the following spurious read insn and go to FetchNextInstr immediately.
interruptsAndNextOpcode();
}
}
void MOS6510::bcc_instr()
{
branch_instr(!flags.C);
}
void MOS6510::bcs_instr()
{
branch_instr(flags.C);
}
void MOS6510::beq_instr()
{
branch_instr(flags.Z);
}
void MOS6510::bit_instr()
{
flags.Z = (Register_Accumulator & Cycle_Data) == 0;
flags.N = Cycle_Data & 0x80;
flags.V = Cycle_Data & 0x40;
interruptsAndNextOpcode();
}
void MOS6510::bmi_instr()
{
branch_instr(flags.N);
}
void MOS6510::bne_instr()
{
branch_instr(!flags.Z);
}
void MOS6510::bpl_instr()
{
branch_instr(!flags.N);
}
void MOS6510::bvc_instr()
{
branch_instr(!flags.V);
}
void MOS6510::bvs_instr()
{
branch_instr(flags.V);
}
void MOS6510::clc_instr()
{
flags.C = false;
interruptsAndNextOpcode();
}
void MOS6510::clv_instr()
{
flags.V = false;
interruptsAndNextOpcode();
}
void MOS6510::cmp_instr()
{
const uint_least16_t tmp = static_cast<uint_least16_t>(Register_Accumulator) - Cycle_Data;
flags.setNZ(tmp);
flags.C = tmp < 0x100;
interruptsAndNextOpcode();
}
void MOS6510::cpx_instr()
{
const uint_least16_t tmp = static_cast<uint_least16_t>(Register_X) - Cycle_Data;
flags.setNZ(tmp);
flags.C = tmp < 0x100;
interruptsAndNextOpcode();
}
void MOS6510::cpy_instr()
{
const uint_least16_t tmp = static_cast<uint_least16_t>(Register_Y) - Cycle_Data;
flags.setNZ(tmp);
flags.C = tmp < 0x100;
interruptsAndNextOpcode();
}
void MOS6510::dec_instr()
{
PutEffAddrDataByte();
flags.setNZ(--Cycle_Data);
}
void MOS6510::dex_instr()
{
flags.setNZ(--Register_X);
interruptsAndNextOpcode();
}
void MOS6510::dey_instr()
{
flags.setNZ(--Register_Y);
interruptsAndNextOpcode();
}
void MOS6510::eor_instr()
{
flags.setNZ(Register_Accumulator ^= Cycle_Data);
interruptsAndNextOpcode();
}
void MOS6510::inc_instr()
{
PutEffAddrDataByte();
flags.setNZ(++Cycle_Data);
}
void MOS6510::inx_instr()
{
flags.setNZ(++Register_X);
interruptsAndNextOpcode();
}
void MOS6510::iny_instr()
{
flags.setNZ(++Register_Y);
interruptsAndNextOpcode();
}
void MOS6510::lda_instr()
{
flags.setNZ(Register_Accumulator = Cycle_Data);
interruptsAndNextOpcode();
}
void MOS6510::ldx_instr()
{
flags.setNZ(Register_X = Cycle_Data);
interruptsAndNextOpcode();
}
void MOS6510::ldy_instr()
{
flags.setNZ(Register_Y = Cycle_Data);
interruptsAndNextOpcode();
}
void MOS6510::lsr_instr()
{
PutEffAddrDataByte();
flags.C = Cycle_Data & 0x01;
flags.setNZ(Cycle_Data >>= 1);
}
void MOS6510::lsra_instr()
{
flags.C = Register_Accumulator & 0x01;
flags.setNZ(Register_Accumulator >>= 1);
interruptsAndNextOpcode();
}
void MOS6510::ora_instr()
{
flags.setNZ(Register_Accumulator |= Cycle_Data);
interruptsAndNextOpcode();
}
void MOS6510::pla_instr()
{
Register_StackPointer++;
const uint_least16_t addr = endian_16(SP_PAGE, Register_StackPointer);
flags.setNZ(Register_Accumulator = cpuRead (addr));
}
void MOS6510::plp_instr()
{
interruptsAndNextOpcode();
}
void MOS6510::rol_instr()
{
const uint8_t newC = Cycle_Data & 0x80;
PutEffAddrDataByte();
Cycle_Data <<= 1;
if (flags.C)
Cycle_Data |= 0x01;
flags.setNZ(Cycle_Data);
flags.C = newC;
}
void MOS6510::rola_instr()
{
const uint8_t newC = Register_Accumulator & 0x80;
Register_Accumulator <<= 1;
if (flags.C)
Register_Accumulator |= 0x01;
flags.setNZ(Register_Accumulator);
flags.C = newC;
interruptsAndNextOpcode();
}
void MOS6510::ror_instr()
{
const uint8_t newC = Cycle_Data & 0x01;
PutEffAddrDataByte();
Cycle_Data >>= 1;
if (flags.C)
Cycle_Data |= 0x80;
flags.setNZ(Cycle_Data);
flags.C = newC;
}
void MOS6510::rora_instr()
{
const uint8_t newC = Register_Accumulator & 0x01;
Register_Accumulator >>= 1;
if (flags.C)
Register_Accumulator |= 0x80;
flags.setNZ(Register_Accumulator);
flags.C = newC;
interruptsAndNextOpcode();
}
void MOS6510::sbx_instr()
{
const unsigned int tmp = (Register_X & Register_Accumulator) - Cycle_Data;
flags.setNZ(Register_X = tmp & 0xff);
flags.C = tmp < 0x100;
interruptsAndNextOpcode();
}
void MOS6510::sbc_instr()
{
doSBC();
interruptsAndNextOpcode();
}
void MOS6510::sec_instr()
{
flags.C = true;
interruptsAndNextOpcode();
}
void MOS6510::shs_instr()
{
Register_StackPointer = Register_Accumulator & Register_X;
Cycle_Data = (endian_16hi8(Cycle_EffectiveAddress) + 1) & Register_StackPointer;
if (Cycle_HighByteWrongEffectiveAddress != Cycle_EffectiveAddress)
Cycle_EffectiveAddress = endian_16(Cycle_Data, endian_16lo8(Cycle_EffectiveAddress));
PutEffAddrDataByte();
}
void MOS6510::tax_instr()
{
flags.setNZ(Register_X = Register_Accumulator);
interruptsAndNextOpcode();
}
void MOS6510::tay_instr()
{
flags.setNZ(Register_Y = Register_Accumulator);
interruptsAndNextOpcode();
}
void MOS6510::tsx_instr()
{
flags.setNZ(Register_X = Register_StackPointer);
interruptsAndNextOpcode();
}
void MOS6510::txa_instr()
{
flags.setNZ(Register_Accumulator = Register_X);
interruptsAndNextOpcode();
}
void MOS6510::txs_instr()
{
Register_StackPointer = Register_X;
interruptsAndNextOpcode();
}
void MOS6510::tya_instr()
{
flags.setNZ(Register_Accumulator = Register_Y);
interruptsAndNextOpcode();
}
void MOS6510::illegal_instr()
{
cycleCount --;
}
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
// Generic Instruction Undocumented Opcodes //
// See documented 6502-nmo.opc by Adam Vardy for more details //
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
/**
* Undocumented - This opcode ANDs the contents of the A register with an immediate value and
* then LSRs the result.
*/
void MOS6510::alr_instr()
{
Register_Accumulator &= Cycle_Data;
flags.C = Register_Accumulator & 0x01;
flags.setNZ(Register_Accumulator >>= 1);
interruptsAndNextOpcode();
}
/**
* Undocumented - ANC ANDs the contents of the A register with an immediate value and then
* moves bit 7 of A into the Carry flag. This opcode works basically
* identically to AND #immed. except that the Carry flag is set to the same
* state that the Negative flag is set to.
*/
void MOS6510::anc_instr()
{
flags.setNZ(Register_Accumulator &= Cycle_Data);
flags.C = flags.N;
interruptsAndNextOpcode();
}
/**
* Undocumented - This opcode ANDs the contents of the A register with an immediate value and
* then RORs the result. (Implementation based on that of Frodo C64 Emulator)
*/
void MOS6510::arr_instr()
{
const uint8_t data = Cycle_Data & Register_Accumulator;
Register_Accumulator = data >> 1;
if (flags.C)
Register_Accumulator |= 0x80;
if (flags.D)
{
flags.N = flags.C;
flags.Z = Register_Accumulator == 0;
flags.V = (data ^ Register_Accumulator) & 0x40;
if ((data & 0x0f) + (data & 0x01) > 5)
Register_Accumulator = (Register_Accumulator & 0xf0) | ((Register_Accumulator + 6) & 0x0f);
flags.C = ((data + (data & 0x10)) & 0x1f0) > 0x50;
if (flags.C)
Register_Accumulator += 0x60;
}
else
{
flags.setNZ(Register_Accumulator);
flags.C = Register_Accumulator & 0x40;
flags.V = (Register_Accumulator & 0x40) ^ ((Register_Accumulator & 0x20) << 1);
}
interruptsAndNextOpcode();
}
/**
* Undocumented - This opcode ASLs the contents of a memory location and then ORs the result
* with the accumulator.
*/
void MOS6510::aso_instr()
{
PutEffAddrDataByte();
flags.C = Cycle_Data & 0x80;
Cycle_Data <<= 1;
flags.setNZ(Register_Accumulator |= Cycle_Data);
}
/**
* Undocumented - This opcode DECs the contents of a memory location and then CMPs the result
* with the A register.
*/
void MOS6510::dcm_instr()
{
PutEffAddrDataByte();
Cycle_Data--;
const uint_least16_t tmp = static_cast<uint_least16_t>(Register_Accumulator) - Cycle_Data;
flags.setNZ(tmp);
flags.C = tmp < 0x100;
}
/**
* Undocumented - This opcode INCs the contents of a memory location and then SBCs the result
* from the A register.
*/
void MOS6510::ins_instr()
{
PutEffAddrDataByte();
Cycle_Data++;
doSBC();
}
/**
* Undocumented - This opcode ANDs the contents of a memory location with the contents of the
* stack pointer register and stores the result in the accumulator, the X
* register, and the stack pointer. Affected flags: N Z.
*/
void MOS6510::las_instr()
{
flags.setNZ(Cycle_Data &= Register_StackPointer);
Register_Accumulator = Cycle_Data;
Register_X = Cycle_Data;
Register_StackPointer = Cycle_Data;
interruptsAndNextOpcode();
}
/**
* Undocumented - This opcode loads both the accumulator and the X register with the contents
* of a memory location.
*/
void MOS6510::lax_instr()
{
flags.setNZ(Register_Accumulator = Register_X = Cycle_Data);
interruptsAndNextOpcode();
}
/**
* Undocumented - LSE LSRs the contents of a memory location and then EORs the result with
* the accumulator.
*/
void MOS6510::lse_instr()
{
PutEffAddrDataByte();
flags.C = Cycle_Data & 0x01;
Cycle_Data >>= 1;
flags.setNZ(Register_Accumulator ^= Cycle_Data);
}
/**
* Undocumented - This opcode ORs the A register with #xx (the "magic" value),
* ANDs the result with an immediate value, and then stores the result in both A and X.
*/
void MOS6510::oal_instr()
{
flags.setNZ(Register_X = (Register_Accumulator = (Cycle_Data & (Register_Accumulator | magic))));
interruptsAndNextOpcode();
}
/**
* Undocumented - RLA ROLs the contents of a memory location and then ANDs the result with
* the accumulator.
*/
void MOS6510::rla_instr()
{
const uint8_t newC = Cycle_Data & 0x80;
PutEffAddrDataByte();
Cycle_Data = Cycle_Data << 1;
if (flags.C) Cycle_Data |= 0x01;
flags.C = newC;
flags.setNZ(Register_Accumulator &= Cycle_Data);
}
/**
* Undocumented - RRA RORs the contents of a memory location and then ADCs the result with
* the accumulator.
*/
void MOS6510::rra_instr()
{
const uint8_t newC = Cycle_Data & 0x01;
PutEffAddrDataByte();
Cycle_Data >>= 1;
if (flags.C) Cycle_Data |= 0x80;
flags.C = newC;
doADC();
}
//-------------------------------------------------------------------------//
/**
* Create new CPU emu.
*
* @param context
* The Event Context
*/
MOS6510::MOS6510(EventContext *context) :
eventContext(*context),
#ifdef DEBUG
m_fdbg(stdout),
#endif
m_nosteal("CPU-nosteal", *this, &MOS6510::eventWithoutSteals),
m_steal("CPU-steal", *this, &MOS6510::eventWithSteals)
{
//----------------------------------------------------------------------
// Build up the processor instruction table
for (int i = 0; i < 0x100; i++)
{
#if DEBUG > 1
printf("Building Command %d[%02x]... ", i, i);
#endif
/*
* So: what cycles are marked as stealable? Rules are:
*
* - CPU performs either read or write at every cycle. Reads are
* always stealable. Writes are rare.
*
* - Every instruction begins with a sequence of reads. Writes,
* if any, are at the end for most instructions.
*/
int buildCycle = i << 3;
typedef enum { WRITE, READ } AccessMode;
AccessMode access = WRITE;
bool legalMode = true;
bool legalInstr = true;
switch (i)
{
// Accumulator or Implied addressing
case ASLn: case CLCn: case CLDn: case CLIn: case CLVn: case DEXn:
case DEYn: case INXn: case INYn: case LSRn: case NOPn_: case PHAn:
case PHPn: case PLAn: case PLPn: case ROLn: case RORn:
case SECn: case SEDn: case SEIn: case TAXn: case TAYn:
case TSXn: case TXAn: case TXSn: case TYAn:
instrTable[buildCycle++].func = &MOS6510::throwAwayFetch;
break;
// Immediate and Relative Addressing Mode Handler
case ADCb: case ANDb: case ANCb_: case ANEb: case ASRb: case ARRb:
case BCCr: case BCSr: case BEQr: case BMIr: case BNEr: case BPLr:
case BRKn: case BVCr: case BVSr: case CMPb: case CPXb: case CPYb:
case EORb: case LDAb: case LDXb: case LDYb: case LXAb: case NOPb_:
case ORAb: case SBCb_: case SBXb: case RTIn: case RTSn:
instrTable[buildCycle++].func = &MOS6510::FetchDataByte;
break;
// Zero Page Addressing Mode Handler - Read & RMW
case ADCz: case ANDz: case BITz: case CMPz: case CPXz: case CPYz:
case EORz: case LAXz: case LDAz: case LDXz: case LDYz: case ORAz:
case NOPz_: case SBCz:
case ASLz: case DCPz: case DECz: case INCz: case ISBz: case LSRz:
case ROLz: case RORz: case SREz: case SLOz: case RLAz: case RRAz:
access = READ;
case SAXz: case STAz: case STXz: case STYz:
instrTable[buildCycle++].func = &MOS6510::FetchLowAddr;
break;
// Zero Page with X Offset Addressing Mode Handler
// these issue extra reads on the 0 page, but we don't care about it
// because there are no detectable effects from them. These reads
// occur during the "wasted" cycle.
case ADCzx: case ANDzx: case CMPzx: case EORzx: case LDAzx: case LDYzx:
case NOPzx_: case ORAzx: case SBCzx:
case ASLzx: case DCPzx: case DECzx: case INCzx: case ISBzx: case LSRzx:
case RLAzx: case ROLzx: case RORzx: case RRAzx: case SLOzx: case SREzx:
access = READ;
case STAzx: case STYzx:
instrTable[buildCycle++].func = &MOS6510::FetchLowAddrX;
// operates on 0 page in read mode. Truly side-effect free.
instrTable[buildCycle++].func = &MOS6510::WasteCycle;
break;
// Zero Page with Y Offset Addressing Mode Handler
case LDXzy: case LAXzy:
access = READ;
case STXzy: case SAXzy:
instrTable[buildCycle++].func = &MOS6510::FetchLowAddrY;
// operates on 0 page in read mode. Truly side-effect free.
instrTable[buildCycle++].func = &MOS6510::WasteCycle;
break;
// Absolute Addressing Mode Handler
case ADCa: case ANDa: case BITa: case CMPa: case CPXa: case CPYa:
case EORa: case LAXa: case LDAa: case LDXa: case LDYa: case NOPa:
case ORAa: case SBCa:
case ASLa: case DCPa: case DECa: case INCa: case ISBa: case LSRa:
case ROLa: case RORa: case SLOa: case SREa: case RLAa: case RRAa:
access = READ;
case JMPw: case SAXa: case STAa: case STXa: case STYa:
instrTable[buildCycle++].func = &MOS6510::FetchLowAddr;
instrTable[buildCycle++].func = &MOS6510::FetchHighAddr;
break;
case JSRw:
instrTable[buildCycle++].func = &MOS6510::FetchLowAddr;
break;
// Absolute With X Offset Addressing Mode Handler (Read)
case ADCax: case ANDax: case CMPax: case EORax: case LDAax:
case LDYax: case NOPax_: case ORAax: case SBCax:
access = READ;
instrTable[buildCycle++].func = &MOS6510::FetchLowAddr;
instrTable[buildCycle++].func = &MOS6510::FetchHighAddrX2;
// this cycle is skipped if the address is already correct.
// otherwise, it will be read and ignored.
instrTable[buildCycle++].func = &MOS6510::throwAwayRead;
break;
// Absolute X (RMW; no page crossing handled, always reads before writing)
case ASLax: case DCPax: case DECax: case INCax: case ISBax:
case LSRax: case RLAax: case ROLax: case RORax: case RRAax:
case SLOax: case SREax:
access = READ;
case SHYax: case STAax:
instrTable[buildCycle++].func = &MOS6510::FetchLowAddr;
instrTable[buildCycle++].func = &MOS6510::FetchHighAddrX;
instrTable[buildCycle++].func = &MOS6510::throwAwayRead;
break;
// Absolute With Y Offset Addresing Mode Handler (Read)
case ADCay: case ANDay: case CMPay: case EORay: case LASay:
case LAXay: case LDAay: case LDXay: case ORAay: case SBCay:
access = READ;
instrTable[buildCycle++].func = &MOS6510::FetchLowAddr;
instrTable[buildCycle++].func = &MOS6510::FetchHighAddrY2;
instrTable[buildCycle++].func = &MOS6510::throwAwayRead;
break;
// Absolute Y (No page crossing handled)
case DCPay: case ISBay: case RLAay: case RRAay: case SLOay:
case SREay:
access = READ;
case SHAay: case SHSay: case SHXay: case STAay:
instrTable[buildCycle++].func = &MOS6510::FetchLowAddr;
instrTable[buildCycle++].func = &MOS6510::FetchHighAddrY;
instrTable[buildCycle++].func = &MOS6510::throwAwayRead;
break;
// Absolute Indirect Addressing Mode Handler
case JMPi:
instrTable[buildCycle++].func = &MOS6510::FetchLowPointer;
instrTable[buildCycle++].func = &MOS6510::FetchHighPointer;
instrTable[buildCycle++].func = &MOS6510::FetchLowEffAddr;
instrTable[buildCycle++].func = &MOS6510::FetchHighEffAddr;
break;
// Indexed with X Preinc Addressing Mode Handler
case ADCix: case ANDix: case CMPix: case EORix: case LAXix: case LDAix:
case ORAix: case SBCix:
case DCPix: case ISBix: case SLOix: case SREix: case RLAix: case RRAix:
access = READ;
case SAXix: case STAix:
instrTable[buildCycle++].func = &MOS6510::FetchLowPointer;
instrTable[buildCycle++].func = &MOS6510::FetchLowPointerX;
instrTable[buildCycle++].func = &MOS6510::FetchLowEffAddr;
instrTable[buildCycle++].func = &MOS6510::FetchHighEffAddr;
break;
// Indexed with Y Postinc Addressing Mode Handler (Read)
case ADCiy: case ANDiy: case CMPiy: case EORiy: case LAXiy:
case LDAiy: case ORAiy: case SBCiy:
access = READ;
instrTable[buildCycle++].func = &MOS6510::FetchLowPointer;
instrTable[buildCycle++].func = &MOS6510::FetchLowEffAddr;
instrTable[buildCycle++].func = &MOS6510::FetchHighEffAddrY2;
instrTable[buildCycle++].func = &MOS6510::throwAwayRead;
break;
// Indexed Y (No page crossing handled)
case DCPiy: case ISBiy: case RLAiy: case RRAiy: case SLOiy:
case SREiy:
access = READ;
case SHAiy: case STAiy:
instrTable[buildCycle++].func = &MOS6510::FetchLowPointer;
instrTable[buildCycle++].func = &MOS6510::FetchLowEffAddr;
instrTable[buildCycle++].func = &MOS6510::FetchHighEffAddrY;
instrTable[buildCycle++].func = &MOS6510::throwAwayRead;
break;
default:
legalMode = false;
break;
}
if (access == READ)
{
instrTable[buildCycle++].func = &MOS6510::FetchEffAddrDataByte;
}
//---------------------------------------------------------------------------------------
// Addressing Modes Finished, other cycles are instruction dependent
switch(i)
{
case ADCz: case ADCzx: case ADCa: case ADCax: case ADCay: case ADCix:
case ADCiy: case ADCb:
instrTable[buildCycle++].func = &MOS6510::adc_instr;
break;
case ANCb_:
instrTable[buildCycle++].func = &MOS6510::anc_instr;
break;
case ANDz: case ANDzx: case ANDa: case ANDax: case ANDay: case ANDix:
case ANDiy: case ANDb:
instrTable[buildCycle++].func = &MOS6510::and_instr;
break;
case ANEb: // Also known as XAA
instrTable[buildCycle++].func = &MOS6510::ane_instr;
break;
case ARRb:
instrTable[buildCycle++].func = &MOS6510::arr_instr;
break;
case ASLn:
instrTable[buildCycle++].func = &MOS6510::asla_instr;
break;
case ASLz: case ASLzx: case ASLa: case ASLax:
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::asl_instr;
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::PutEffAddrDataByte;
break;
case ASRb: // Also known as ALR
instrTable[buildCycle++].func = &MOS6510::alr_instr;
break;
case BCCr:
instrTable[buildCycle++].func = &MOS6510::bcc_instr;
instrTable[buildCycle++].func = &MOS6510::throwAwayRead;
break;
case BCSr:
instrTable[buildCycle++].func = &MOS6510::bcs_instr;
instrTable[buildCycle++].func = &MOS6510::throwAwayRead;
break;
case BEQr:
instrTable[buildCycle++].func = &MOS6510::beq_instr;
instrTable[buildCycle++].func = &MOS6510::throwAwayRead;
break;
case BITz: case BITa:
instrTable[buildCycle++].func = &MOS6510::bit_instr;
break;
case BMIr:
instrTable[buildCycle++].func = &MOS6510::bmi_instr;
instrTable[buildCycle++].func = &MOS6510::throwAwayRead;
break;
case BNEr:
instrTable[buildCycle++].func = &MOS6510::bne_instr;
instrTable[buildCycle++].func = &MOS6510::throwAwayRead;
break;
case BPLr:
instrTable[buildCycle++].func = &MOS6510::bpl_instr;
instrTable[buildCycle++].func = &MOS6510::throwAwayRead;
break;
case BRKn:
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::PushHighPC;
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::brkPushLowPC;
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::brk_instr;
instrTable[buildCycle++].func = &MOS6510::IRQLoRequest;
instrTable[buildCycle++].func = &MOS6510::IRQHiRequest;
instrTable[buildCycle++].func = &MOS6510::fetchNextOpcode;
break;
case BVCr:
instrTable[buildCycle++].func = &MOS6510::bvc_instr;
instrTable[buildCycle++].func = &MOS6510::throwAwayRead;
break;
case BVSr:
instrTable[buildCycle++].func = &MOS6510::bvs_instr;
instrTable[buildCycle++].func = &MOS6510::throwAwayRead;
break;
case CLCn:
instrTable[buildCycle++].func = &MOS6510::clc_instr;
break;
case CLDn:
instrTable[buildCycle++].func = &MOS6510::cld_instr;
break;
case CLIn:
instrTable[buildCycle++].func = &MOS6510::cli_instr;
break;
case CLVn:
instrTable[buildCycle++].func = &MOS6510::clv_instr;
break;
case CMPz: case CMPzx: case CMPa: case CMPax: case CMPay: case CMPix:
case CMPiy: case CMPb:
instrTable[buildCycle++].func = &MOS6510::cmp_instr;
break;
case CPXz: case CPXa: case CPXb:
instrTable[buildCycle++].func = &MOS6510::cpx_instr;
break;
case CPYz: case CPYa: case CPYb:
instrTable[buildCycle++].func = &MOS6510::cpy_instr;
break;
case DCPz: case DCPzx: case DCPa: case DCPax: case DCPay: case DCPix:
case DCPiy: // Also known as DCM
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::dcm_instr;
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::PutEffAddrDataByte;
break;
case DECz: case DECzx: case DECa: case DECax:
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::dec_instr;
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::PutEffAddrDataByte;
break;
case DEXn:
instrTable[buildCycle++].func = &MOS6510::dex_instr;
break;
case DEYn:
instrTable[buildCycle++].func = &MOS6510::dey_instr;
break;
case EORz: case EORzx: case EORa: case EORax: case EORay: case EORix:
case EORiy: case EORb:
instrTable[buildCycle++].func = &MOS6510::eor_instr;
break;
#if 0
// HLT, also known as JAM
case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52:
case 0x62: case 0x72: case 0x92: case 0xb2: case 0xd2: case 0xf2:
case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52:
case 0x62: case 0x72: case 0x92: case 0xb2: case 0xd2: case 0xf2:
instrTable[buildCycle++].func = hlt_instr;
break;
#endif
case INCz: case INCzx: case INCa: case INCax:
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::inc_instr;
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::PutEffAddrDataByte;
break;
case INXn:
instrTable[buildCycle++].func = &MOS6510::inx_instr;
break;
case INYn:
instrTable[buildCycle++].func = &MOS6510::iny_instr;
break;
case ISBz: case ISBzx: case ISBa: case ISBax: case ISBay: case ISBix:
case ISBiy: // Also known as INS
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::ins_instr;
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::PutEffAddrDataByte;
break;
case JSRw:
instrTable[buildCycle++].func = &MOS6510::WasteCycle;
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::PushHighPC;
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::PushLowPC;
instrTable[buildCycle++].func = &MOS6510::FetchHighAddr;
case JMPw: case JMPi:
instrTable[buildCycle++].func = &MOS6510::jmp_instr;
break;
case LASay:
instrTable[buildCycle++].func = &MOS6510::las_instr;
break;
case LAXz: case LAXzy: case LAXa: case LAXay: case LAXix: case LAXiy:
instrTable[buildCycle++].func = &MOS6510::lax_instr;
break;
case LDAz: case LDAzx: case LDAa: case LDAax: case LDAay: case LDAix:
case LDAiy: case LDAb:
instrTable[buildCycle++].func = &MOS6510::lda_instr;
break;
case LDXz: case LDXzy: case LDXa: case LDXay: case LDXb:
instrTable[buildCycle++].func = &MOS6510::ldx_instr;
break;
case LDYz: case LDYzx: case LDYa: case LDYax: case LDYb:
instrTable[buildCycle++].func = &MOS6510::ldy_instr;
break;
case LSRn:
instrTable[buildCycle++].func = &MOS6510::lsra_instr;
break;
case LSRz: case LSRzx: case LSRa: case LSRax:
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::lsr_instr;
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::PutEffAddrDataByte;
break;
case NOPn_: case NOPb_:
case NOPz_: case NOPzx_: case NOPa: case NOPax_:
// NOPb NOPz NOPzx - Also known as SKBn
// NOPa NOPax - Also known as SKWn
break;
case LXAb: // Also known as OAL
instrTable[buildCycle++].func = &MOS6510::oal_instr;
break;
case ORAz: case ORAzx: case ORAa: case ORAax: case ORAay: case ORAix:
case ORAiy: case ORAb:
instrTable[buildCycle++].func = &MOS6510::ora_instr;
break;
case PHAn:
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::pha_instr;
break;
case PHPn:
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::PushSR;
break;
case PLAn:
// should read the value at current stack register.
// Truly side-effect free.
instrTable[buildCycle++].func = &MOS6510::WasteCycle;
instrTable[buildCycle++].func = &MOS6510::pla_instr;
break;
case PLPn:
// should read the value at current stack register.
// Truly side-effect free.
instrTable[buildCycle++].func = &MOS6510::WasteCycle;
instrTable[buildCycle++].func = &MOS6510::PopSR;
instrTable[buildCycle++].func = &MOS6510::plp_instr;
break;
case RLAz: case RLAzx: case RLAix: case RLAa: case RLAax: case RLAay:
case RLAiy:
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::rla_instr;
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::PutEffAddrDataByte;
break;
case ROLn:
instrTable[buildCycle++].func = &MOS6510::rola_instr;
break;
case ROLz: case ROLzx: case ROLa: case ROLax:
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::rol_instr;
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::PutEffAddrDataByte;
break;
case RORn:
instrTable[buildCycle++].func = &MOS6510::rora_instr;
break;
case RORz: case RORzx: case RORa: case RORax:
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::ror_instr;
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::PutEffAddrDataByte;
break;
case RRAa: case RRAax: case RRAay: case RRAz: case RRAzx: case RRAix:
case RRAiy:
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::rra_instr;
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::PutEffAddrDataByte;
break;
case RTIn:
// should read the value at current stack register.
// Truly side-effect free.
instrTable[buildCycle++].func = &MOS6510::WasteCycle;
instrTable[buildCycle++].func = &MOS6510::PopSR;
instrTable[buildCycle++].func = &MOS6510::PopLowPC;
instrTable[buildCycle++].func = &MOS6510::PopHighPC;
instrTable[buildCycle++].func = &MOS6510::rti_instr;
break;
case RTSn:
// should read the value at current stack register.
// Truly side-effect free.
instrTable[buildCycle++].func = &MOS6510::WasteCycle;
instrTable[buildCycle++].func = &MOS6510::PopLowPC;
instrTable[buildCycle++].func = &MOS6510::PopHighPC;
instrTable[buildCycle++].func = &MOS6510::rts_instr;
break;
case SAXz: case SAXzy: case SAXa: case SAXix: // Also known as AXS
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::axs_instr;
break;
case SBCz: case SBCzx: case SBCa: case SBCax: case SBCay: case SBCix:
case SBCiy: case SBCb_:
instrTable[buildCycle++].func = &MOS6510::sbc_instr;
break;
case SBXb:
instrTable[buildCycle++].func = &MOS6510::sbx_instr;
break;
case SECn:
instrTable[buildCycle++].func = &MOS6510::sec_instr;
break;
case SEDn:
instrTable[buildCycle++].func = &MOS6510::sed_instr;
break;
case SEIn:
instrTable[buildCycle++].func = &MOS6510::sei_instr;
break;
case SHAay: case SHAiy: // Also known as AXA
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::axa_instr;
break;
case SHSay: // Also known as TAS
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::shs_instr;
break;
case SHXay: // Also known as XAS
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::xas_instr;
break;
case SHYax: // Also known as SAY
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::say_instr;
break;
case SLOz: case SLOzx: case SLOa: case SLOax: case SLOay: case SLOix:
case SLOiy: // Also known as ASO
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::aso_instr;
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::PutEffAddrDataByte;
break;
case SREz: case SREzx: case SREa: case SREax: case SREay: case SREix:
case SREiy: // Also known as LSE
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::lse_instr;
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::PutEffAddrDataByte;
break;
case STAz: case STAzx: case STAa: case STAax: case STAay: case STAix:
case STAiy:
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::sta_instr;
break;
case STXz: case STXzy: case STXa:
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::stx_instr;
break;
case STYz: case STYzx: case STYa:
instrTable[buildCycle].nosteal = true;
instrTable[buildCycle++].func = &MOS6510::sty_instr;
break;
case TAXn:
instrTable[buildCycle++].func = &MOS6510::tax_instr;
break;
case TAYn:
instrTable[buildCycle++].func = &MOS6510::tay_instr;
break;
case TSXn:
instrTable[buildCycle++].func = &MOS6510::tsx_instr;
break;
case TXAn:
instrTable[buildCycle++].func = &MOS6510::txa_instr;
break;
case TXSn:
instrTable[buildCycle++].func = &MOS6510::txs_instr;
break;
case TYAn:
instrTable[buildCycle++].func = &MOS6510::tya_instr;
break;
default:
legalInstr = false;
break;
}
/* Missing an addressing mode or implementation makes opcode invalid.
* These are normally called HLT instructions. In the hardware, the
* CPU state machine locks up and will never recover. */
if (!(legalMode && legalInstr))
{
instrTable[buildCycle++].func = &MOS6510::illegal_instr;
}
// check for IRQ triggers or fetch next opcode...
instrTable[buildCycle].func = &MOS6510::interruptsAndNextOpcode;
#if DEBUG > 1
printf("Done [%d Cycles]\n", buildCycle - (i << 3));
#endif
}
// Intialise Processor Registers
Register_Accumulator = 0;
Register_X = 0;
Register_Y = 0;
Cycle_EffectiveAddress = 0;
Cycle_Data = 0;
#ifdef DEBUG
dodump = false;
#endif
Initialise();
}
/**
* Initialise CPU Emulation (Registers).
*/
void MOS6510::Initialise()
{
// Reset stack
Register_StackPointer = 0xFF;
// Reset Cycle Count
cycleCount = (BRKn << 3) + 6; // fetchNextOpcode
// Reset Status Register
flags.reset();
// Set PC to some value
Register_ProgramCounter = 0;
// IRQs pending check
irqAssertedOnPin = false;
nmiFlag = false;
rstFlag = false;
interruptCycle = MAX;
// Signals
rdy = true;
eventContext.schedule(m_nosteal, 0, EVENT_CLOCK_PHI2);
}
/**
* Reset CPU Emulation.
*/
void MOS6510::reset()
{ // Internal Stuff
Initialise();
// Set processor port to the default values
cpuWrite(0, 0x2F);
cpuWrite(1, 0x37);
// Requires External Bits
// Read from reset vector for program entry point
endian_16lo8(Cycle_EffectiveAddress, cpuRead(0xFFFC));
endian_16hi8(Cycle_EffectiveAddress, cpuRead(0xFFFD));
Register_ProgramCounter = Cycle_EffectiveAddress;
}
/**
* Module Credits.
*/
const char *MOS6510::credit =
{
"MOS6510 Cycle Exact Emulation\n"
"\t(C) 2000 Simon A. White\n"
"\t(C) 2008-2010 Antti S. Lankila\n"
"\t(C) 2011-2012 Leandro Nini\n"
};
void MOS6510::debug(bool enable, FILE *out)
{
#ifdef DEBUG
dodump = enable;
if (!(out && enable))
m_fdbg = stdout;
else
m_fdbg = out;
#endif
}
}