2001-12-27 19:54:36 +00:00
|
|
|
//============================================================================
|
|
|
|
//
|
|
|
|
// MM MM 6666 555555 0000 2222
|
|
|
|
// MMMM MMMM 66 66 55 00 00 22 22
|
|
|
|
// MM MMM MM 66 55 00 00 22
|
|
|
|
// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator"
|
|
|
|
// MM MM 66 66 55 00 00 22
|
|
|
|
// MM MM 66 66 55 55 00 00 22
|
|
|
|
// MM MM 6666 5555 0000 222222
|
|
|
|
//
|
2010-04-10 21:37:23 +00:00
|
|
|
// Copyright (c) 1995-2010 by Bradford W. Mott, Stephen Anthony
|
|
|
|
// and the Stella Team
|
2001-12-27 19:54:36 +00:00
|
|
|
//
|
2010-01-10 03:23:32 +00:00
|
|
|
// See the file "License.txt" for information on usage and redistribution of
|
2001-12-27 19:54:36 +00:00
|
|
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
|
|
|
//
|
2009-05-13 13:55:40 +00:00
|
|
|
// $Id$
|
2001-12-27 19:54:36 +00:00
|
|
|
//============================================================================
|
|
|
|
|
2009-05-17 19:30:10 +00:00
|
|
|
//#define DEBUG_OUTPUT
|
|
|
|
#define debugStream cout
|
|
|
|
|
2006-12-15 16:43:12 +00:00
|
|
|
#ifdef DEBUGGER_SUPPORT
|
2009-05-17 19:30:10 +00:00
|
|
|
#include "Debugger.hxx"
|
2005-08-24 22:54:30 +00:00
|
|
|
#include "Expression.hxx"
|
|
|
|
#endif
|
2001-12-27 19:54:36 +00:00
|
|
|
|
2009-05-17 19:30:10 +00:00
|
|
|
#include "M6502.hxx"
|
|
|
|
|
2001-12-27 19:54:36 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
M6502::M6502(uInt32 systemCyclesPerProcessorCycle)
|
2008-05-04 17:16:39 +00:00
|
|
|
: myExecutionStatus(0),
|
|
|
|
mySystem(0),
|
|
|
|
mySystemCyclesPerProcessorCycle(systemCyclesPerProcessorCycle),
|
|
|
|
myLastAccessWasRead(true),
|
2009-05-17 19:30:10 +00:00
|
|
|
myTotalInstructionCount(0),
|
|
|
|
myNumberOfDistinctAccesses(0),
|
2009-11-11 20:53:57 +00:00
|
|
|
myLastAddress(0),
|
|
|
|
myLastPeekAddress(0),
|
|
|
|
myLastPokeAddress(0)
|
2001-12-27 19:54:36 +00:00
|
|
|
{
|
2006-12-15 16:43:12 +00:00
|
|
|
#ifdef DEBUGGER_SUPPORT
|
2005-09-20 19:09:10 +00:00
|
|
|
myDebugger = NULL;
|
|
|
|
myBreakPoints = NULL;
|
|
|
|
myReadTraps = NULL;
|
|
|
|
myWriteTraps = NULL;
|
2009-05-17 19:30:10 +00:00
|
|
|
|
|
|
|
myJustHitTrapFlag = false;
|
2005-08-24 22:54:30 +00:00
|
|
|
#endif
|
2001-12-27 19:54:36 +00:00
|
|
|
|
|
|
|
// Compute the System Cycle table
|
2009-04-20 15:03:13 +00:00
|
|
|
for(uInt32 t = 0; t < 256; ++t)
|
2001-12-27 19:54:36 +00:00
|
|
|
{
|
OK, this is the first pass at a huge reorganization of the debugger
classes. First off, the distella code has been integrated into a
DiStella class. This code isn't yet tied to the debugger, but it does
at least compile and generate valid output.
The RamDebug class has been replaced by a CartDebug class, which
takes responsibility for the previous RamDebug stuff as well as
things related to Cart address space (read from write ports,
disassembly, etc).
Fixed E7 bankswitching when reading from the write port in the upper
256byte area.
Fixed 'read from write port functionality' in general for all carts
that supported it previously. Basically, if _rwport is enabled, the
address is checked to be an actual read (vs. one that's part of a
normal write cycle), *and* it's actually an illegal access (each
cart/bankswitch type now provides a hint to indicate this condition).
Still TODO is clean up the rework, properly integrate DiStella, and
fix labels and defines (which seem to be completely broken).
git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1922 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2010-01-17 16:48:45 +00:00
|
|
|
myInstructionSystemCycleTable[t] = ourInstructionCycleTable[t] *
|
2001-12-27 19:54:36 +00:00
|
|
|
mySystemCyclesPerProcessorCycle;
|
|
|
|
}
|
2009-05-17 19:30:10 +00:00
|
|
|
|
2009-05-25 17:51:52 +00:00
|
|
|
#ifdef DEBUG_OUTPUT
|
2009-05-17 19:30:10 +00:00
|
|
|
debugStream << "( Fm Ln Cyc Clk) ( P0 P1 M0 M1 BL) "
|
|
|
|
<< "flags A X Y SP Code Disasm" << endl
|
|
|
|
<< endl;
|
2009-05-25 17:51:52 +00:00
|
|
|
#endif
|
2001-12-27 19:54:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
M6502::~M6502()
|
|
|
|
{
|
2006-12-15 16:43:12 +00:00
|
|
|
#ifdef DEBUGGER_SUPPORT
|
We've done it!
Ladies and gentlemen, we have working conditional breakpoints! And they're
*fast*!
The command to use is "breakif". It takes one argument, the condition.
You can use curly braces if you want to include spaces in your arguments
(they don't do anything except help readability).
On the Athlon 2100, I was able to run the emulator at full speed, using
100% of the CPU, with *20* expressions of the form:
breakif a==101||a==102||a==103||a==104||a==105
Around 25 expressions, the game started getting noticeably jerky, but was
still playable. With 1 to 5 expressions, there was *no* noticeable increase
in CPU usage!
Actually, this can probably be improved on: I used gcc 3.2.2 for
this. With 3.3.x or 4.0.x, it should be even faster.
git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@665 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2005-07-17 02:26:50 +00:00
|
|
|
myBreakConds.clear();
|
|
|
|
myBreakCondNames.clear();
|
2005-08-24 22:54:30 +00:00
|
|
|
#endif
|
2001-12-27 19:54:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void M6502::install(System& system)
|
|
|
|
{
|
|
|
|
// Remember which system I'm installed in
|
|
|
|
mySystem = &system;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void M6502::reset()
|
|
|
|
{
|
|
|
|
// Clear the execution status flags
|
|
|
|
myExecutionStatus = 0;
|
|
|
|
|
|
|
|
// Set registers to default values
|
|
|
|
A = X = Y = 0;
|
|
|
|
SP = 0xff;
|
|
|
|
PS(0x20);
|
|
|
|
|
2006-08-31 02:31:29 +00:00
|
|
|
// Reset access flag
|
|
|
|
myLastAccessWasRead = true;
|
|
|
|
|
2001-12-27 19:54:36 +00:00
|
|
|
// Load PC from the reset vector
|
|
|
|
PC = (uInt16)mySystem->peek(0xfffc) | ((uInt16)mySystem->peek(0xfffd) << 8);
|
2008-05-04 17:16:39 +00:00
|
|
|
|
|
|
|
myTotalInstructionCount = 0;
|
2001-12-27 19:54:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void M6502::irq()
|
|
|
|
{
|
|
|
|
myExecutionStatus |= MaskableInterruptBit;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void M6502::nmi()
|
|
|
|
{
|
|
|
|
myExecutionStatus |= NonmaskableInterruptBit;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void M6502::stop()
|
|
|
|
{
|
|
|
|
myExecutionStatus |= StopExecutionBit;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
uInt8 M6502::PS() const
|
|
|
|
{
|
|
|
|
uInt8 ps = 0x20;
|
|
|
|
|
|
|
|
if(N)
|
|
|
|
ps |= 0x80;
|
|
|
|
if(V)
|
|
|
|
ps |= 0x40;
|
|
|
|
if(B)
|
|
|
|
ps |= 0x10;
|
|
|
|
if(D)
|
|
|
|
ps |= 0x08;
|
|
|
|
if(I)
|
|
|
|
ps |= 0x04;
|
|
|
|
if(!notZ)
|
|
|
|
ps |= 0x02;
|
|
|
|
if(C)
|
|
|
|
ps |= 0x01;
|
|
|
|
|
|
|
|
return ps;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void M6502::PS(uInt8 ps)
|
|
|
|
{
|
|
|
|
N = ps & 0x80;
|
|
|
|
V = ps & 0x40;
|
2006-09-04 00:11:38 +00:00
|
|
|
B = true; // B = ps & 0x10; The 6507's B flag always true
|
2001-12-27 19:54:36 +00:00
|
|
|
D = ps & 0x08;
|
|
|
|
I = ps & 0x04;
|
|
|
|
notZ = !(ps & 0x02);
|
|
|
|
C = ps & 0x01;
|
|
|
|
}
|
|
|
|
|
2009-05-17 19:30:10 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
inline uInt8 M6502::peek(uInt16 address)
|
|
|
|
{
|
|
|
|
if(address != myLastAddress)
|
|
|
|
{
|
|
|
|
myNumberOfDistinctAccesses++;
|
|
|
|
myLastAddress = address;
|
|
|
|
}
|
|
|
|
mySystem->incrementCycles(mySystemCyclesPerProcessorCycle);
|
|
|
|
|
|
|
|
#ifdef DEBUGGER_SUPPORT
|
|
|
|
if(myReadTraps != NULL && myReadTraps->isSet(address))
|
|
|
|
{
|
|
|
|
myJustHitTrapFlag = true;
|
|
|
|
myHitTrapInfo.message = "RTrap: ";
|
|
|
|
myHitTrapInfo.address = address;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
uInt8 result = mySystem->peek(address);
|
|
|
|
myLastAccessWasRead = true;
|
2009-11-11 20:53:57 +00:00
|
|
|
myLastPeekAddress = address;
|
2009-05-17 19:30:10 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
inline void M6502::poke(uInt16 address, uInt8 value)
|
|
|
|
{
|
|
|
|
if(address != myLastAddress)
|
|
|
|
{
|
|
|
|
myNumberOfDistinctAccesses++;
|
|
|
|
myLastAddress = address;
|
|
|
|
}
|
|
|
|
mySystem->incrementCycles(mySystemCyclesPerProcessorCycle);
|
|
|
|
|
|
|
|
#ifdef DEBUGGER_SUPPORT
|
|
|
|
if(myWriteTraps != NULL && myWriteTraps->isSet(address))
|
|
|
|
{
|
|
|
|
myJustHitTrapFlag = true;
|
|
|
|
myHitTrapInfo.message = "WTrap: ";
|
|
|
|
myHitTrapInfo.address = address;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
mySystem->poke(address, value);
|
|
|
|
myLastAccessWasRead = false;
|
2009-11-11 20:53:57 +00:00
|
|
|
myLastPokeAddress = address;
|
2009-05-17 19:30:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
bool M6502::execute(uInt32 number)
|
|
|
|
{
|
|
|
|
// Clear all of the execution status bits except for the fatal error bit
|
|
|
|
myExecutionStatus &= FatalErrorBit;
|
|
|
|
|
|
|
|
// Loop until execution is stopped or a fatal error occurs
|
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
for(; !myExecutionStatus && (number != 0); --number)
|
|
|
|
{
|
|
|
|
#ifdef DEBUGGER_SUPPORT
|
|
|
|
if(myJustHitTrapFlag)
|
|
|
|
{
|
|
|
|
if(myDebugger->start(myHitTrapInfo.message, myHitTrapInfo.address))
|
|
|
|
{
|
|
|
|
myJustHitTrapFlag = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(myBreakPoints != NULL)
|
|
|
|
{
|
|
|
|
if(myBreakPoints->isSet(PC))
|
|
|
|
{
|
|
|
|
if(myDebugger->start("BP: ", PC))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int cond = evalCondBreaks();
|
|
|
|
if(cond > -1)
|
|
|
|
{
|
|
|
|
string buf = "CBP: " + myBreakCondNames[cond];
|
|
|
|
if(myDebugger->start(buf))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
#endif
|
2009-11-11 20:53:57 +00:00
|
|
|
uInt16 operandAddress = 0;
|
|
|
|
uInt8 operand = 0;
|
|
|
|
|
|
|
|
// Reset the peek/poke address pointers
|
|
|
|
myLastPeekAddress = myLastPokeAddress = 0;
|
2009-05-17 19:30:10 +00:00
|
|
|
|
|
|
|
// Fetch instruction at the program counter
|
|
|
|
IR = peek(PC++);
|
|
|
|
|
|
|
|
#ifdef DEBUG_OUTPUT
|
|
|
|
debugStream << ::hex << setw(2) << (int)A << " "
|
|
|
|
<< ::hex << setw(2) << (int)X << " "
|
|
|
|
<< ::hex << setw(2) << (int)Y << " "
|
|
|
|
<< ::hex << setw(2) << (int)SP << " "
|
|
|
|
<< setw(4) << (PC-1) << ": "
|
|
|
|
<< setw(2) << (int)IR << " "
|
|
|
|
// << "<" << ourAddressingModeTable[IR] << " ";
|
|
|
|
// debugStream << hex << setw(4) << operandAddress << " ";
|
|
|
|
<< setw(3) << ourInstructionMnemonicTable[IR]
|
|
|
|
|
|
|
|
// debugStream << "PS=" << ::hex << setw(2) << (int)PS() << " ";
|
|
|
|
|
|
|
|
// debugStream << "Cyc=" << dec << mySystem->cycles();
|
|
|
|
<< endl;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Call code to execute the instruction
|
|
|
|
switch(IR)
|
|
|
|
{
|
|
|
|
// 6502 instruction emulation is generated by an M4 macro file
|
|
|
|
#include "M6502.ins"
|
|
|
|
|
|
|
|
default:
|
|
|
|
// Oops, illegal instruction executed so set fatal error flag
|
|
|
|
myExecutionStatus |= FatalErrorBit;
|
|
|
|
}
|
|
|
|
|
|
|
|
myTotalInstructionCount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// See if we need to handle an interrupt
|
|
|
|
if((myExecutionStatus & MaskableInterruptBit) ||
|
|
|
|
(myExecutionStatus & NonmaskableInterruptBit))
|
|
|
|
{
|
|
|
|
// Yes, so handle the interrupt
|
|
|
|
interruptHandler();
|
|
|
|
}
|
|
|
|
|
|
|
|
// See if execution has been stopped
|
|
|
|
if(myExecutionStatus & StopExecutionBit)
|
|
|
|
{
|
|
|
|
// Yes, so answer that everything finished fine
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// See if a fatal error has occured
|
|
|
|
if(myExecutionStatus & FatalErrorBit)
|
|
|
|
{
|
|
|
|
// Yes, so answer that something when wrong
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// See if we've executed the specified number of instructions
|
|
|
|
if(number == 0)
|
|
|
|
{
|
|
|
|
// Yes, so answer that everything finished fine
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void M6502::interruptHandler()
|
|
|
|
{
|
|
|
|
// Handle the interrupt
|
|
|
|
if((myExecutionStatus & MaskableInterruptBit) && !I)
|
|
|
|
{
|
|
|
|
mySystem->incrementCycles(7 * mySystemCyclesPerProcessorCycle);
|
|
|
|
mySystem->poke(0x0100 + SP--, (PC - 1) >> 8);
|
|
|
|
mySystem->poke(0x0100 + SP--, (PC - 1) & 0x00ff);
|
|
|
|
mySystem->poke(0x0100 + SP--, PS() & (~0x10));
|
|
|
|
D = false;
|
|
|
|
I = true;
|
|
|
|
PC = (uInt16)mySystem->peek(0xFFFE) | ((uInt16)mySystem->peek(0xFFFF) << 8);
|
|
|
|
}
|
|
|
|
else if(myExecutionStatus & NonmaskableInterruptBit)
|
|
|
|
{
|
|
|
|
mySystem->incrementCycles(7 * mySystemCyclesPerProcessorCycle);
|
|
|
|
mySystem->poke(0x0100 + SP--, (PC - 1) >> 8);
|
|
|
|
mySystem->poke(0x0100 + SP--, (PC - 1) & 0x00ff);
|
|
|
|
mySystem->poke(0x0100 + SP--, PS() & (~0x10));
|
|
|
|
D = false;
|
|
|
|
PC = (uInt16)mySystem->peek(0xFFFA) | ((uInt16)mySystem->peek(0xFFFB) << 8);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clear the interrupt bits in myExecutionStatus
|
|
|
|
myExecutionStatus &= ~(MaskableInterruptBit | NonmaskableInterruptBit);
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
OK, this looks like a huge update, but it's only because of some Serializer
class reworking. Serializer class now handles read/write of state from
files as well as in-memory streams. As a result, Deserializer class has
been removed.
Added state rewinding to the debugger. For now, this is limited to 100
levels of undo, with a new state generated each time a step/trace/frame/
scanline advance is performed. The undo level is 'rolling', in that it
remembers the last 100 levels (so you lose the oldest states when you
start adding more than 100). For now, this is tied to the 'Alt-r' key
in the debugger. Still TODO is add a button for it, and clean up some
TIA output issues when rewinding.
Added support for 6K version of Supercharger ROMs (this fixes issues
with the 6K version of Cubis).
Cleaned up the Serializable infrastructure, making sure that all
classes that need to implement it actually do so now.
Fixed issue with editable widgets in the UI, where pressing Enter
on the keypad wasn't actually being registered.
git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1849 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2009-08-05 16:05:34 +00:00
|
|
|
bool M6502::save(Serializer& out) const
|
2009-05-17 19:30:10 +00:00
|
|
|
{
|
OK, this looks like a huge update, but it's only because of some Serializer
class reworking. Serializer class now handles read/write of state from
files as well as in-memory streams. As a result, Deserializer class has
been removed.
Added state rewinding to the debugger. For now, this is limited to 100
levels of undo, with a new state generated each time a step/trace/frame/
scanline advance is performed. The undo level is 'rolling', in that it
remembers the last 100 levels (so you lose the oldest states when you
start adding more than 100). For now, this is tied to the 'Alt-r' key
in the debugger. Still TODO is add a button for it, and clean up some
TIA output issues when rewinding.
Added support for 6K version of Supercharger ROMs (this fixes issues
with the 6K version of Cubis).
Cleaned up the Serializable infrastructure, making sure that all
classes that need to implement it actually do so now.
Fixed issue with editable widgets in the UI, where pressing Enter
on the keypad wasn't actually being registered.
git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1849 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2009-08-05 16:05:34 +00:00
|
|
|
const string& CPU = name();
|
2009-05-17 19:30:10 +00:00
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
out.putString(CPU);
|
|
|
|
|
|
|
|
out.putByte((char)A); // Accumulator
|
|
|
|
out.putByte((char)X); // X index register
|
|
|
|
out.putByte((char)Y); // Y index register
|
|
|
|
out.putByte((char)SP); // Stack Pointer
|
|
|
|
out.putByte((char)IR); // Instruction register
|
|
|
|
out.putInt(PC); // Program Counter
|
|
|
|
|
|
|
|
out.putBool(N); // N flag for processor status register
|
|
|
|
out.putBool(V); // V flag for processor status register
|
|
|
|
out.putBool(B); // B flag for processor status register
|
|
|
|
out.putBool(D); // D flag for processor status register
|
|
|
|
out.putBool(I); // I flag for processor status register
|
|
|
|
out.putBool(notZ); // Z flag complement for processor status register
|
|
|
|
out.putBool(C); // C flag for processor status register
|
|
|
|
|
|
|
|
out.putByte((char)myExecutionStatus);
|
|
|
|
|
|
|
|
// Indicates the number of distinct memory accesses
|
|
|
|
out.putInt(myNumberOfDistinctAccesses);
|
|
|
|
// Indicates the last address which was accessed
|
|
|
|
out.putInt(myLastAddress);
|
|
|
|
|
|
|
|
}
|
2009-08-27 22:59:14 +00:00
|
|
|
catch(const char* msg)
|
2009-05-17 19:30:10 +00:00
|
|
|
{
|
2009-08-27 22:59:14 +00:00
|
|
|
cerr << "ERROR: M6502::save" << endl << " " << msg << endl;
|
2009-05-17 19:30:10 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
OK, this looks like a huge update, but it's only because of some Serializer
class reworking. Serializer class now handles read/write of state from
files as well as in-memory streams. As a result, Deserializer class has
been removed.
Added state rewinding to the debugger. For now, this is limited to 100
levels of undo, with a new state generated each time a step/trace/frame/
scanline advance is performed. The undo level is 'rolling', in that it
remembers the last 100 levels (so you lose the oldest states when you
start adding more than 100). For now, this is tied to the 'Alt-r' key
in the debugger. Still TODO is add a button for it, and clean up some
TIA output issues when rewinding.
Added support for 6K version of Supercharger ROMs (this fixes issues
with the 6K version of Cubis).
Cleaned up the Serializable infrastructure, making sure that all
classes that need to implement it actually do so now.
Fixed issue with editable widgets in the UI, where pressing Enter
on the keypad wasn't actually being registered.
git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1849 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2009-08-05 16:05:34 +00:00
|
|
|
bool M6502::load(Serializer& in)
|
2009-05-17 19:30:10 +00:00
|
|
|
{
|
OK, this looks like a huge update, but it's only because of some Serializer
class reworking. Serializer class now handles read/write of state from
files as well as in-memory streams. As a result, Deserializer class has
been removed.
Added state rewinding to the debugger. For now, this is limited to 100
levels of undo, with a new state generated each time a step/trace/frame/
scanline advance is performed. The undo level is 'rolling', in that it
remembers the last 100 levels (so you lose the oldest states when you
start adding more than 100). For now, this is tied to the 'Alt-r' key
in the debugger. Still TODO is add a button for it, and clean up some
TIA output issues when rewinding.
Added support for 6K version of Supercharger ROMs (this fixes issues
with the 6K version of Cubis).
Cleaned up the Serializable infrastructure, making sure that all
classes that need to implement it actually do so now.
Fixed issue with editable widgets in the UI, where pressing Enter
on the keypad wasn't actually being registered.
git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1849 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2009-08-05 16:05:34 +00:00
|
|
|
const string& CPU = name();
|
2009-05-17 19:30:10 +00:00
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if(in.getString() != CPU)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
A = (uInt8) in.getByte(); // Accumulator
|
|
|
|
X = (uInt8) in.getByte(); // X index register
|
|
|
|
Y = (uInt8) in.getByte(); // Y index register
|
|
|
|
SP = (uInt8) in.getByte(); // Stack Pointer
|
|
|
|
IR = (uInt8) in.getByte(); // Instruction register
|
|
|
|
PC = (uInt16) in.getInt(); // Program Counter
|
|
|
|
|
|
|
|
N = in.getBool(); // N flag for processor status register
|
|
|
|
V = in.getBool(); // V flag for processor status register
|
|
|
|
B = in.getBool(); // B flag for processor status register
|
|
|
|
D = in.getBool(); // D flag for processor status register
|
|
|
|
I = in.getBool(); // I flag for processor status register
|
|
|
|
notZ = in.getBool(); // Z flag complement for processor status register
|
|
|
|
C = in.getBool(); // C flag for processor status register
|
|
|
|
|
|
|
|
myExecutionStatus = (uInt8) in.getByte();
|
|
|
|
|
|
|
|
// Indicates the number of distinct memory accesses
|
|
|
|
myNumberOfDistinctAccesses = (uInt32) in.getInt();
|
|
|
|
// Indicates the last address which was accessed
|
|
|
|
myLastAddress = (uInt16) in.getInt();
|
|
|
|
}
|
2009-08-27 22:59:14 +00:00
|
|
|
catch(const char* msg)
|
2009-05-17 19:30:10 +00:00
|
|
|
{
|
2009-08-27 22:59:14 +00:00
|
|
|
cerr << "ERROR: M6502::laod" << endl << " " << msg << endl;
|
2009-05-17 19:30:10 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2009-12-17 21:07:56 +00:00
|
|
|
#if 0
|
2001-12-27 19:54:36 +00:00
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
ostream& operator<<(ostream& out, const M6502::AddressingMode& mode)
|
|
|
|
{
|
|
|
|
switch(mode)
|
|
|
|
{
|
|
|
|
case M6502::Absolute:
|
|
|
|
out << "$nnnn ";
|
|
|
|
break;
|
|
|
|
case M6502::AbsoluteX:
|
|
|
|
out << "$nnnn,X";
|
|
|
|
break;
|
|
|
|
case M6502::AbsoluteY:
|
|
|
|
out << "$nnnn,Y";
|
|
|
|
break;
|
|
|
|
case M6502::Implied:
|
|
|
|
out << "implied";
|
|
|
|
break;
|
|
|
|
case M6502::Immediate:
|
|
|
|
out << "#$nn ";
|
|
|
|
break;
|
|
|
|
case M6502::Indirect:
|
|
|
|
out << "($nnnn)";
|
|
|
|
break;
|
|
|
|
case M6502::IndirectX:
|
|
|
|
out << "($nn,X)";
|
|
|
|
break;
|
|
|
|
case M6502::IndirectY:
|
|
|
|
out << "($nn),Y";
|
|
|
|
break;
|
|
|
|
case M6502::Invalid:
|
|
|
|
out << "invalid";
|
|
|
|
break;
|
|
|
|
case M6502::Relative:
|
|
|
|
out << "$nn ";
|
|
|
|
break;
|
|
|
|
case M6502::Zero:
|
|
|
|
out << "$nn ";
|
|
|
|
break;
|
|
|
|
case M6502::ZeroX:
|
|
|
|
out << "$nn,X ";
|
|
|
|
break;
|
|
|
|
case M6502::ZeroY:
|
|
|
|
out << "$nn,Y ";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return out;
|
|
|
|
}
|
2009-12-17 21:07:56 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef DEBUGGER_SUPPORT
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void M6502::attach(Debugger& debugger)
|
|
|
|
{
|
|
|
|
// Remember the debugger for this microprocessor
|
|
|
|
myDebugger = &debugger;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
unsigned int M6502::addCondBreak(Expression *e, const string& name)
|
|
|
|
{
|
|
|
|
myBreakConds.push_back(e);
|
|
|
|
myBreakCondNames.push_back(name);
|
|
|
|
return myBreakConds.size() - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void M6502::delCondBreak(unsigned int brk)
|
|
|
|
{
|
|
|
|
if(brk < myBreakConds.size())
|
|
|
|
{
|
|
|
|
delete myBreakConds[brk];
|
|
|
|
myBreakConds.remove_at(brk);
|
|
|
|
myBreakCondNames.remove_at(brk);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void M6502::clearCondBreaks()
|
|
|
|
{
|
|
|
|
for(uInt32 i = 0; i < myBreakConds.size(); i++)
|
|
|
|
delete myBreakConds[i];
|
|
|
|
|
|
|
|
myBreakConds.clear();
|
|
|
|
myBreakCondNames.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
const StringList& M6502::getCondBreakNames() const
|
|
|
|
{
|
|
|
|
return myBreakCondNames;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
int M6502::evalCondBreaks()
|
|
|
|
{
|
|
|
|
for(uInt32 i = 0; i < myBreakConds.size(); i++)
|
|
|
|
if(myBreakConds[i]->evaluate())
|
|
|
|
return i;
|
|
|
|
|
|
|
|
return -1; // no break hit
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void M6502::setBreakPoints(PackedBitArray *bp)
|
|
|
|
{
|
|
|
|
myBreakPoints = bp;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void M6502::setTraps(PackedBitArray *read, PackedBitArray *write)
|
|
|
|
{
|
|
|
|
myReadTraps = read;
|
|
|
|
myWriteTraps = write;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
2001-12-27 19:54:36 +00:00
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
OK, this is the first pass at a huge reorganization of the debugger
classes. First off, the distella code has been integrated into a
DiStella class. This code isn't yet tied to the debugger, but it does
at least compile and generate valid output.
The RamDebug class has been replaced by a CartDebug class, which
takes responsibility for the previous RamDebug stuff as well as
things related to Cart address space (read from write ports,
disassembly, etc).
Fixed E7 bankswitching when reading from the write port in the upper
256byte area.
Fixed 'read from write port functionality' in general for all carts
that supported it previously. Basically, if _rwport is enabled, the
address is checked to be an actual read (vs. one that's part of a
normal write cycle), *and* it's actually an illegal access (each
cart/bankswitch type now provides a hint to indicate this condition).
Still TODO is clean up the rework, properly integrate DiStella, and
fix labels and defines (which seem to be completely broken).
git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1922 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2010-01-17 16:48:45 +00:00
|
|
|
uInt32 M6502::ourInstructionCycleTable[256] = {
|
2001-12-27 19:54:36 +00:00
|
|
|
// 0 1 2 3 4 5 6 7 8 9 a b c d e f
|
|
|
|
7, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6, // 0
|
|
|
|
2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, // 1
|
|
|
|
6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 6, // 2
|
|
|
|
2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, // 3
|
2001-12-29 17:55:59 +00:00
|
|
|
6, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 6, // 4
|
|
|
|
2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, // 5
|
|
|
|
6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 5, 4, 6, 6, // 6
|
|
|
|
2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, // 7
|
2001-12-27 19:54:36 +00:00
|
|
|
2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, // 8
|
2001-12-29 17:55:59 +00:00
|
|
|
2, 6, 2, 6, 4, 4, 4, 4, 2, 5, 2, 5, 5, 5, 5, 5, // 9
|
|
|
|
2, 6, 2, 6, 3, 3, 3, 4, 2, 2, 2, 2, 4, 4, 4, 4, // a
|
|
|
|
2, 5, 2, 5, 4, 4, 4, 4, 2, 4, 2, 4, 4, 4, 4, 4, // b
|
|
|
|
2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, // c
|
|
|
|
2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, // d
|
|
|
|
2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, // e
|
|
|
|
2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7 // f
|
2001-12-27 19:54:36 +00:00
|
|
|
};
|