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
|
|
|
|
//
|
2013-01-04 19:49:01 +00:00
|
|
|
// Copyright (c) 1995-2013 by Bradford W. Mott, Stephen Anthony
|
2010-04-10 21:37:23 +00:00
|
|
|
// 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"
|
The emulation core now tracks access to DATA areas (currently, any address
used as a peek operand). Still TODO is deal with poke areas, which would
be relevant in carts with extended RAM.
The interaction between the internal tracking and Distella is now much
tighter, in that knowledge gained by Distella is used in the core code,
and vice versa. This allows the best of both worlds, where the internal
tracking finds stuff at runtime (that couldn't be found in a static
analysis), and Distella tracks potential paths (that haven't occurred at
runtime yet).
Added 'type' debugger prompt command, which basically queries an address
for its disassembly type (CODE/GFX/DATA, etc).
Added debugger commands to query the last address used in an operation
for various registers, but they're only stubs at the moment.
Updated the bankswitch schemes to deal with accesses in and around the
hotspot areas. Previously, peek accesses in these areas weren't being
recorded as DATA areas.
git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2145 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2010-10-10 20:24:22 +00:00
|
|
|
#include "CartDebug.hxx"
|
2010-10-18 18:39:57 +00:00
|
|
|
#include "PackedBitArray.hxx"
|
The emulation core now tracks access to DATA areas (currently, any address
used as a peek operand). Still TODO is deal with poke areas, which would
be relevant in carts with extended RAM.
The interaction between the internal tracking and Distella is now much
tighter, in that knowledge gained by Distella is used in the core code,
and vice versa. This allows the best of both worlds, where the internal
tracking finds stuff at runtime (that couldn't be found in a static
analysis), and Distella tracks potential paths (that haven't occurred at
runtime yet).
Added 'type' debugger prompt command, which basically queries an address
for its disassembly type (CODE/GFX/DATA, etc).
Added debugger commands to query the last address used in an operation
for various registers, but they're only stubs at the moment.
Updated the bankswitch schemes to deal with accesses in and around the
hotspot areas. Previously, peek accesses in these areas weren't being
recorded as DATA areas.
git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2145 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2010-10-10 20:24:22 +00:00
|
|
|
|
|
|
|
// Flags for disassembly types
|
|
|
|
#define DISASM_CODE CartDebug::CODE
|
|
|
|
#define DISASM_GFX CartDebug::GFX
|
2010-10-21 17:46:23 +00:00
|
|
|
#define DISASM_PGFX CartDebug::PGFX
|
The emulation core now tracks access to DATA areas (currently, any address
used as a peek operand). Still TODO is deal with poke areas, which would
be relevant in carts with extended RAM.
The interaction between the internal tracking and Distella is now much
tighter, in that knowledge gained by Distella is used in the core code,
and vice versa. This allows the best of both worlds, where the internal
tracking finds stuff at runtime (that couldn't be found in a static
analysis), and Distella tracks potential paths (that haven't occurred at
runtime yet).
Added 'type' debugger prompt command, which basically queries an address
for its disassembly type (CODE/GFX/DATA, etc).
Added debugger commands to query the last address used in an operation
for various registers, but they're only stubs at the moment.
Updated the bankswitch schemes to deal with accesses in and around the
hotspot areas. Previously, peek accesses in these areas weren't being
recorded as DATA areas.
git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2145 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2010-10-10 20:24:22 +00:00
|
|
|
#define DISASM_DATA CartDebug::DATA
|
|
|
|
#define DISASM_ROW CartDebug::ROW
|
|
|
|
#define DISASM_NONE 0
|
|
|
|
#else
|
|
|
|
// Flags for disassembly types
|
|
|
|
#define DISASM_CODE 0
|
|
|
|
#define DISASM_GFX 0
|
2010-10-21 17:46:23 +00:00
|
|
|
#define DISASM_PGFX 0
|
The emulation core now tracks access to DATA areas (currently, any address
used as a peek operand). Still TODO is deal with poke areas, which would
be relevant in carts with extended RAM.
The interaction between the internal tracking and Distella is now much
tighter, in that knowledge gained by Distella is used in the core code,
and vice versa. This allows the best of both worlds, where the internal
tracking finds stuff at runtime (that couldn't be found in a static
analysis), and Distella tracks potential paths (that haven't occurred at
runtime yet).
Added 'type' debugger prompt command, which basically queries an address
for its disassembly type (CODE/GFX/DATA, etc).
Added debugger commands to query the last address used in an operation
for various registers, but they're only stubs at the moment.
Updated the bankswitch schemes to deal with accesses in and around the
hotspot areas. Previously, peek accesses in these areas weren't being
recorded as DATA areas.
git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2145 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2010-10-10 20:24:22 +00:00
|
|
|
#define DISASM_DATA 0
|
|
|
|
#define DISASM_ROW 0
|
|
|
|
#define DISASM_NONE 0
|
2005-08-24 22:54:30 +00:00
|
|
|
#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),
|
2010-10-11 00:44:25 +00:00
|
|
|
myLastPokeAddress(0),
|
2010-11-10 14:01:41 +00:00
|
|
|
myLastSrcAddressS(0),
|
2010-10-21 21:01:00 +00:00
|
|
|
myLastSrcAddressA(0),
|
|
|
|
myLastSrcAddressX(0),
|
|
|
|
myLastSrcAddressY(0),
|
|
|
|
myDataAddressForPoke(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;
|
2010-10-11 00:44:25 +00:00
|
|
|
|
|
|
|
myLastAddress = myLastPeekAddress = myLastPokeAddress = 0;
|
2010-11-10 14:01:41 +00:00
|
|
|
myLastSrcAddressS = myLastSrcAddressA =
|
|
|
|
myLastSrcAddressX = myLastSrcAddressY = 0;
|
2010-10-21 21:01:00 +00:00
|
|
|
myDataAddressForPoke = 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
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
The emulation core now tracks access to DATA areas (currently, any address
used as a peek operand). Still TODO is deal with poke areas, which would
be relevant in carts with extended RAM.
The interaction between the internal tracking and Distella is now much
tighter, in that knowledge gained by Distella is used in the core code,
and vice versa. This allows the best of both worlds, where the internal
tracking finds stuff at runtime (that couldn't be found in a static
analysis), and Distella tracks potential paths (that haven't occurred at
runtime yet).
Added 'type' debugger prompt command, which basically queries an address
for its disassembly type (CODE/GFX/DATA, etc).
Added debugger commands to query the last address used in an operation
for various registers, but they're only stubs at the moment.
Updated the bankswitch schemes to deal with accesses in and around the
hotspot areas. Previously, peek accesses in these areas weren't being
recorded as DATA areas.
git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2145 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2010-10-10 20:24:22 +00:00
|
|
|
inline uInt8 M6502::peek(uInt16 address, uInt8 flags)
|
2009-05-17 19:30:10 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
Added rule for recompiling the M6502.m4 script to the Makefile, because I'm
tired of doing it manually every time it changes.
Tweaked the handling of JSR, RTI and RTS commands, so they don't erroneously
mark associated addresses as CODE when in fact they're never actually
executed.
Several parts of the Distella code were marking areas as DATA, even though
it depending on knowing the values for the X and Y registers (which it
doesn't, as it's a static analysis). As such, these areas are now marked
as ROW instead, since that's as precise as a static analysis can do. The
processing blocks are left there, though, in case Distella is improved in
a future release.
All the above changes allow for better disassembly with less
'false positives' (ie, areas marked as CODE or DATA when they really aren't).
git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2172 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2010-11-07 22:52:42 +00:00
|
|
|
//cerr << "addr = " << HEX4 << address << ", flags = " << Debugger::to_bin_8(flags) << endl;
|
2009-05-17 19:30:10 +00:00
|
|
|
#endif
|
|
|
|
|
The emulation core now tracks access to DATA areas (currently, any address
used as a peek operand). Still TODO is deal with poke areas, which would
be relevant in carts with extended RAM.
The interaction between the internal tracking and Distella is now much
tighter, in that knowledge gained by Distella is used in the core code,
and vice versa. This allows the best of both worlds, where the internal
tracking finds stuff at runtime (that couldn't be found in a static
analysis), and Distella tracks potential paths (that haven't occurred at
runtime yet).
Added 'type' debugger prompt command, which basically queries an address
for its disassembly type (CODE/GFX/DATA, etc).
Added debugger commands to query the last address used in an operation
for various registers, but they're only stubs at the moment.
Updated the bankswitch schemes to deal with accesses in and around the
hotspot areas. Previously, peek accesses in these areas weren't being
recorded as DATA areas.
git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2145 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2010-10-10 20:24:22 +00:00
|
|
|
uInt8 result = mySystem->peek(address, flags);
|
2009-05-17 19:30:10 +00:00
|
|
|
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)
|
|
|
|
{
|
2012-01-17 22:20:20 +00:00
|
|
|
if(myDebugger && myDebugger->start(myHitTrapInfo.message, myHitTrapInfo.address))
|
2009-05-17 19:30:10 +00:00
|
|
|
{
|
|
|
|
myJustHitTrapFlag = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(myBreakPoints != NULL)
|
|
|
|
{
|
|
|
|
if(myBreakPoints->isSet(PC))
|
|
|
|
{
|
2012-01-17 22:20:20 +00:00
|
|
|
if(myDebugger && myDebugger->start("BP: ", PC))
|
2009-05-17 19:30:10 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int cond = evalCondBreaks();
|
|
|
|
if(cond > -1)
|
|
|
|
{
|
|
|
|
string buf = "CBP: " + myBreakCondNames[cond];
|
2012-01-17 22:20:20 +00:00
|
|
|
if(myDebugger && myDebugger->start(buf))
|
2009-05-17 19:30:10 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
#endif
|
2010-10-11 00:44:25 +00:00
|
|
|
uInt16 operandAddress = 0, intermediateAddress = 0;
|
2009-11-11 20:53:57 +00:00
|
|
|
uInt8 operand = 0;
|
|
|
|
|
|
|
|
// Reset the peek/poke address pointers
|
2010-10-21 21:01:00 +00:00
|
|
|
myLastPeekAddress = myLastPokeAddress = myDataAddressForPoke = 0;
|
2009-05-17 19:30:10 +00:00
|
|
|
|
|
|
|
// Fetch instruction at the program counter
|
The emulation core now tracks access to DATA areas (currently, any address
used as a peek operand). Still TODO is deal with poke areas, which would
be relevant in carts with extended RAM.
The interaction between the internal tracking and Distella is now much
tighter, in that knowledge gained by Distella is used in the core code,
and vice versa. This allows the best of both worlds, where the internal
tracking finds stuff at runtime (that couldn't be found in a static
analysis), and Distella tracks potential paths (that haven't occurred at
runtime yet).
Added 'type' debugger prompt command, which basically queries an address
for its disassembly type (CODE/GFX/DATA, etc).
Added debugger commands to query the last address used in an operation
for various registers, but they're only stubs at the moment.
Updated the bankswitch schemes to deal with accesses in and around the
hotspot areas. Previously, peek accesses in these areas weren't being
recorded as DATA areas.
git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2145 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
2010-10-10 20:24:22 +00:00
|
|
|
IR = peek(PC++, DISASM_CODE); // This address represents a code section
|
2009-05-17 19:30:10 +00:00
|
|
|
|
|
|
|
#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 << " ";
|
2010-10-03 00:23:13 +00:00
|
|
|
// << setw(3) << ourInstructionMnemonicTable[IR]
|
2009-05-17 19:30:10 +00:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
2012-05-20 14:23:48 +00:00
|
|
|
out.putByte(A); // Accumulator
|
|
|
|
out.putByte(X); // X index register
|
|
|
|
out.putByte(Y); // Y index register
|
|
|
|
out.putByte(SP); // Stack Pointer
|
|
|
|
out.putByte(IR); // Instruction register
|
|
|
|
out.putShort(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(myExecutionStatus);
|
2009-05-17 19:30:10 +00:00
|
|
|
|
|
|
|
// Indicates the number of distinct memory accesses
|
|
|
|
out.putInt(myNumberOfDistinctAccesses);
|
2010-10-21 21:01:00 +00:00
|
|
|
// Indicates the last address(es) which was accessed
|
2012-05-20 14:23:48 +00:00
|
|
|
out.putShort(myLastAddress);
|
|
|
|
out.putShort(myLastPeekAddress);
|
|
|
|
out.putShort(myLastPokeAddress);
|
|
|
|
out.putShort(myLastSrcAddressS);
|
|
|
|
out.putShort(myLastSrcAddressA);
|
|
|
|
out.putShort(myLastSrcAddressX);
|
|
|
|
out.putShort(myLastSrcAddressY);
|
|
|
|
out.putShort(myDataAddressForPoke);
|
2009-05-17 19:30:10 +00:00
|
|
|
}
|
2012-05-25 12:41:19 +00:00
|
|
|
catch(...)
|
2009-05-17 19:30:10 +00:00
|
|
|
{
|
2012-05-25 12:41:19 +00:00
|
|
|
cerr << "ERROR: M6502::save" << 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;
|
|
|
|
|
2012-05-20 14:23:48 +00:00
|
|
|
A = in.getByte(); // Accumulator
|
|
|
|
X = in.getByte(); // X index register
|
|
|
|
Y = in.getByte(); // Y index register
|
|
|
|
SP = in.getByte(); // Stack Pointer
|
|
|
|
IR = in.getByte(); // Instruction register
|
|
|
|
PC = in.getShort(); // Program Counter
|
2009-05-17 19:30:10 +00:00
|
|
|
|
2012-05-20 14:23:48 +00:00
|
|
|
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
|
2009-05-17 19:30:10 +00:00
|
|
|
|
2012-05-20 14:23:48 +00:00
|
|
|
myExecutionStatus = in.getByte();
|
2009-05-17 19:30:10 +00:00
|
|
|
|
|
|
|
// Indicates the number of distinct memory accesses
|
2012-05-20 14:23:48 +00:00
|
|
|
myNumberOfDistinctAccesses = in.getInt();
|
2010-10-21 21:01:00 +00:00
|
|
|
// Indicates the last address(es) which was accessed
|
2012-05-20 14:23:48 +00:00
|
|
|
myLastAddress = in.getShort();
|
|
|
|
myLastPeekAddress = in.getShort();
|
|
|
|
myLastPokeAddress = in.getShort();
|
|
|
|
myLastSrcAddressS = in.getShort();
|
|
|
|
myLastSrcAddressA = in.getShort();
|
|
|
|
myLastSrcAddressX = in.getShort();
|
|
|
|
myLastSrcAddressY = in.getShort();
|
|
|
|
myDataAddressForPoke = in.getShort();
|
2009-05-17 19:30:10 +00:00
|
|
|
}
|
2012-05-25 12:41:19 +00:00
|
|
|
catch(...)
|
2009-05-17 19:30:10 +00:00
|
|
|
{
|
2012-05-25 12:41:19 +00:00
|
|
|
cerr << "ERROR: M6502::laod" << endl;
|
2009-05-17 19:30:10 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2009-12-17 21:07:56 +00:00
|
|
|
#ifdef DEBUGGER_SUPPORT
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
|
|
void M6502::attach(Debugger& debugger)
|
|
|
|
{
|
|
|
|
// Remember the debugger for this microprocessor
|
|
|
|
myDebugger = &debugger;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2012-01-17 22:20:20 +00:00
|
|
|
uInt32 M6502::addCondBreak(Expression *e, const string& name)
|
2009-12-17 21:07:56 +00:00
|
|
|
{
|
|
|
|
myBreakConds.push_back(e);
|
|
|
|
myBreakCondNames.push_back(name);
|
|
|
|
return myBreakConds.size() - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2012-01-17 22:20:20 +00:00
|
|
|
void M6502::delCondBreak(uInt32 brk)
|
2009-12-17 21:07:56 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
2012-01-17 22:20:20 +00:00
|
|
|
Int32 M6502::evalCondBreaks()
|
2009-12-17 21:07:56 +00:00
|
|
|
{
|
|
|
|
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
|
|
|
};
|