stella/src/debugger/DiStella.cxx

1269 lines
45 KiB
C++

//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2010 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id$
//============================================================================
#include "bspf.hxx"
#include "Debugger.hxx"
#include "DiStella.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DiStella::DiStella(const CartDebug& dbg, CartDebug::DisassemblyList& list,
CartDebug::BankInfo& info, bool resolvedata)
: myDbg(dbg),
myList(list)
{
CartDebug::AddressList& addresses = info.addressList;
if(addresses.size() == 0)
return;
while(!myAddressQueue.empty())
myAddressQueue.pop();
CartDebug::AddressList::iterator it = addresses.begin();
uInt16 start = *it++;
if(start & 0x1000)
{
if(info.size == 4096) // 4K ROM space
{
/*============================================
The offset is the address where the code segment
starts. For a 4K game, it is usually 0xf000.
Example:
Start address = $D973, so therefore
Offset to code = $D000
Code range = $D000-$DFFF
=============================================*/
myAppData.start = 0x0000;
myAppData.end = 0x0FFF;
myOffset = (start - (start % 0x1000));
}
else // 2K ROM space
{
/*============================================
The offset is the address where the code segment
starts. For a 2K game, it is usually 0xf800,
but can also be 0xf000.
=============================================*/
myAppData.start = 0x0000;
myAppData.end = 0x07FF;
myOffset = (start & 0xF800);
}
}
else // ZP RAM
{
// For now, we assume all accesses below $1000 are zero-page
myAppData.start = 0x0080;
myAppData.end = 0x00FF;
myOffset = 0;
}
myAppData.length = info.size;
info.start = myAppData.start;
info.end = myAppData.end;
info.offset = myOffset;
memset(labels, 0, 0x1000);
myAddressQueue.push(start);
// Process any directives first, as they override automatic code determination
processDirectives(info.directiveList);
if(resolvedata)
{
// After we've disassembled from all addresses in the address list,
// use all access points determined by Stella during emulation
int codeAccessPoint = 0;
while(!myAddressQueue.empty())
{
myPC = myAddressQueue.front();
myPCBeg = myPC;
myAddressQueue.pop();
disasm(myPC, 1);
if(myPCBeg <= myPCEnd)
for (uInt32 k = myPCBeg; k <= myPCEnd; k++)
mark(k, CartDebug::CODE);
// When we get to this point, all addresses have been processed
// starting from the initial one in the address list
// If so, process the next one in the list that hasn't already
// been marked as CODE
// If it *has* been marked, it can be removed from consideration
// in all subsequent passes
//
// Once the address list has been exhausted, we process all addresses
// determined during emulation to represent code, which *haven't* already
// been considered
//
// Note that we can't simply add all addresses right away, since
// the processing of a single address can cause others to be added in
// the ::disasm method
// All of these have to be exhausted before considering a new address
if(myAddressQueue.empty())
{
while(it != addresses.end())
{
uInt16 addr = *it;
if(!check_bit(addr-myOffset, CartDebug::CODE))
{
cerr << "(list) marking " << HEX4 << addr << " as CODE\n";
myAddressQueue.push(addr);
++it;
break;
}
else // remove this address, it is redundant
it = addresses.erase(it);
}
// Stella itself can provide hints on whether an address has ever
// been referenced as CODE
while(it == addresses.end() && codeAccessPoint <= myAppData.end)
{
if((Debugger::debugger().getAddressDisasmType(codeAccessPoint+myOffset) & CartDebug::CODE)
&& !(labels[codeAccessPoint & myAppData.end] & CartDebug::CODE))
{
cerr << "(emul) marking " << HEX4 << (codeAccessPoint+myOffset) << " as CODE\n";
myAddressQueue.push(codeAccessPoint+myOffset);
++codeAccessPoint;
break;
}
++codeAccessPoint;
}
}
}
for (int k = 0; k <= myAppData.end; k++)
{
if (!check_bit(k, CartDebug::SKIP|CartDebug::CODE|CartDebug::GFX|
CartDebug::PGFX|CartDebug::DATA))
mark(k+myOffset, CartDebug::ROW);
}
}
// Second pass
disasm(myOffset, 2);
// Third pass
disasm(myOffset, 3);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DiStella::~DiStella()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DiStella::disasm(uInt32 distart, int pass)
{
#define USER_OR_AUTO_LABEL(pre, address, post) \
const string& l = myDbg.getLabel(address, true); \
if(l != EmptyString) nextline << pre << l << post; \
else nextline << pre << "L" << HEX4 << address << post;
uInt8 op, d1;
uInt16 ad;
AddressingMode addr_mode;
int bytes=0, labfound=0, addbranch=0;
stringstream nextline, nextlinebytes;
myDisasmBuf.str("");
/* pc=myAppData.start; */
myPC = distart - myOffset;
while(myPC <= myAppData.end)
{
if(check_bit(myPC, CartDebug::GFX|CartDebug::PGFX) && !check_bit(myPC, CartDebug::CODE))
{
if (pass == 2)
mark(myPC+myOffset, CartDebug::VALID_ENTRY);
else if (pass == 3)
{
if (check_bit(myPC, CartDebug::REFERENCED))
myDisasmBuf << HEX4 << myPC+myOffset << "'L" << HEX4 << myPC+myOffset << "'";
else
myDisasmBuf << HEX4 << myPC+myOffset << "' '";
const string& bit_string = check_bit(myPC, CartDebug::GFX) ? "\x7f" : "\x80";
uInt8 byte = Debugger::debugger().peek(myPC+myOffset);
myDisasmBuf << ".byte $" << HEX2 << (int)byte << " |";
for(uInt8 i = 0, c = byte; i < 8; ++i, c <<= 1)
myDisasmBuf << ((c > 127) ? bit_string : " ");
myDisasmBuf << "| $" << HEX4 << myPC+myOffset << "'";
if(settings.gfx_format == kBASE_2)
myDisasmBuf << Debugger::to_bin_8(byte);
else
myDisasmBuf << HEX2 << (int)byte;
addEntry(CartDebug::GFX);
}
myPC++;
}
else if (check_bit(myPC, CartDebug::DATA) &&
!check_bit(myPC, CartDebug::CODE|CartDebug::GFX|CartDebug::PGFX))
{
if (pass == 2)
mark(myPC+myOffset, CartDebug::VALID_ENTRY);
else if (pass == 3)
{
if (check_bit(myPC, CartDebug::REFERENCED))
myDisasmBuf << HEX4 << myPC+myOffset << "'L" << HEX4 << myPC+myOffset << "'";
else
myDisasmBuf << HEX4 << myPC+myOffset << "' '";
uInt8 byte = Debugger::debugger().peek(myPC+myOffset);
myDisasmBuf << ".byte $" << HEX2 << (int)byte << " $"
<< HEX4 << myPC+myOffset << "'"
<< HEX2 << (int)byte;
addEntry(CartDebug::DATA);
}
myPC++;
}
else if (check_bit(myPC, CartDebug::ROW) &&
!check_bit(myPC, CartDebug::CODE|CartDebug::DATA|CartDebug::GFX|CartDebug::PGFX))
{
mark(myPC+myOffset, CartDebug::VALID_ENTRY);
if (pass == 3)
{
bytes = 1;
myDisasmBuf << HEX4 << myPC+myOffset << "'L" << HEX4 << myPC+myOffset << "'.byte "
<< "$" << HEX2 << (int)Debugger::debugger().peek(myPC+myOffset);
}
myPC++;
while (check_bit(myPC, CartDebug::ROW) &&
!check_bit(myPC, CartDebug::CODE|CartDebug::DATA|CartDebug::GFX|CartDebug::PGFX)
&& pass == 3 && myPC <= myAppData.end)
{
bytes++;
if (bytes == 17)
{
addEntry(CartDebug::ROW);
myDisasmBuf << " ' '.byte $" << HEX2 << (int)Debugger::debugger().peek(myPC+myOffset);
bytes = 1;
}
else
myDisasmBuf << ",$" << HEX2 << (int)Debugger::debugger().peek(myPC+myOffset);
myPC++;
}
if (pass == 3)
{
addEntry(CartDebug::ROW);
myDisasmBuf << " ' ' ";
addEntry(CartDebug::NONE);
}
}
else // The following sections must be SKIP or CODE
{
// Add label (if any)
//
op = Debugger::debugger().peek(myPC+myOffset);
/* version 2.1 bug fix */
if (pass == 2)
mark(myPC+myOffset, CartDebug::VALID_ENTRY);
else if (pass == 3)
{
if (check_bit(myPC, CartDebug::REFERENCED))
myDisasmBuf << HEX4 << myPC+myOffset << "'L" << HEX4 << myPC+myOffset << "'";
else
myDisasmBuf << HEX4 << myPC+myOffset << "' '";
}
// Add opcode mneumonic
//
addr_mode = ourLookup[op].addr_mode;
myPC++;
#if 0
// FIXME - the following condition is never true
if (ourLookup[op].mnemonic[0] == '.')
{
addr_mode = IMPLIED;
if (pass == 3)
nextline << ".byte $" << HEX2 << (int)op << " ;";
}
#endif
if (pass == 1)
{
/* M_REL covers BPL, BMI, BVC, BVS, BCC, BCS, BNE, BEQ
M_ADDR = JMP $NNNN, JSR $NNNN
M_AIND = JMP Abs, Indirect */
switch(ourLookup[op].source)
{
case M_REL:
case M_ADDR:
case M_AIND:
addbranch = 1;
break;
default:
addbranch = 0;
break;
}
}
else if (pass == 3)
{
nextline << ourLookup[op].mnemonic;
nextlinebytes << HEX2 << (int)op << " ";
}
// Add operand(s) for PC values outside the app data range
//
if (myPC >= myAppData.end)
{
switch(addr_mode)
{
case ABSOLUTE:
case ABSOLUTE_X:
case ABSOLUTE_Y:
case INDIRECT_X:
case INDIRECT_Y:
case ABS_INDIRECT:
{
if (pass == 3)
{
/* Line information is already printed; append .byte since last
instruction will put recompilable object larger that original
binary file */
myDisasmBuf << ".byte $" << HEX2 << (int)op << " $"
<< HEX4 << myPC+myOffset << "'"
<< HEX2 << (int)op;
addEntry(CartDebug::DATA);
if (myPC == myAppData.end)
{
if (check_bit(myPC, CartDebug::REFERENCED))
myDisasmBuf << HEX4 << myPC+myOffset << "'L" << HEX4 << myPC+myOffset << "'";
else
myDisasmBuf << HEX4 << myPC+myOffset << "' '";
op = Debugger::debugger().peek(myPC+myOffset); myPC++;
myDisasmBuf << ".byte $" << HEX2 << (int)op << " $"
<< HEX4 << myPC+myOffset << "'"
<< HEX2 << (int)op;
addEntry(CartDebug::DATA);
}
}
myPCEnd = myAppData.end + myOffset;
return;
}
case ZERO_PAGE:
case IMMEDIATE:
case ZERO_PAGE_X:
case ZERO_PAGE_Y:
case RELATIVE:
{
if (myPC > myAppData.end)
{
if (pass == 3)
{
/* Line information is already printed, but we can remove the
Instruction (i.e. BMI) by simply clearing the buffer to print */
myDisasmBuf << ".byte $" << HEX2 << (int)op;
addEntry(CartDebug::ROW);
nextline.str("");
nextlinebytes.str("");
}
myPC++;
myPCEnd = myAppData.end + myOffset;
return;
}
}
default:
break;
} // end switch(addr_mode)
}
// Add operand(s)
//
/* Version 2.1 added the extensions to mnemonics */
switch(addr_mode)
{
#if 0
case IMPLIED:
{
if (op == 0x40 || op == 0x60)
if (pass == 3)
nextline << "\n";
break;
}
#endif
case ACCUMULATOR:
{
if (pass == 3)
nextline << " A";
break;
}
case ABSOLUTE:
{
ad = Debugger::debugger().dpeek(myPC+myOffset); myPC+=2;
labfound = mark(ad, CartDebug::REFERENCED);
if (pass == 1)
{
if (addbranch)
{
if (!check_bit(ad & myAppData.end, CartDebug::CODE))
{
if (ad > 0xfff)
myAddressQueue.push((ad & myAppData.end) + myOffset);
mark(ad, CartDebug::CODE);
}
}
else if(ad > 0xfff)
mark(ad, CartDebug::DATA);
}
else if (pass == 3)
{
if (ad < 0x100)
nextline << ".w ";
else
nextline << " ";
if (labfound == 1)
{
USER_OR_AUTO_LABEL("", ad, "");
nextlinebytes << HEX2 << (int)(ad&0xff) << " " << HEX2 << (int)(ad>>8);
}
else if (labfound == 3)
{
nextline << CartDebug::ourIOMnemonic[ad-0x280];
nextlinebytes << HEX2 << (int)(ad&0xff) << " " << HEX2 << (int)(ad>>8);
}
else if (labfound == 4)
{
int tmp = (ad & myAppData.end)+myOffset;
USER_OR_AUTO_LABEL("", tmp, "");
nextlinebytes << HEX2 << (int)(tmp&0xff) << " " << HEX2 << (int)(tmp>>8);
}
else
{
nextline << "$" << HEX4 << ad;
nextlinebytes << HEX2 << (int)(ad&0xff) << " " << HEX2 << (int)(ad>>8);
}
}
break;
}
case ZERO_PAGE:
{
d1 = Debugger::debugger().peek(myPC+myOffset); myPC++;
labfound = mark(d1, CartDebug::REFERENCED);
if (pass == 3)
{
if (labfound == 2)
nextline << " " << (ourLookup[op].rw_mode == READ ?
CartDebug::ourTIAMnemonicR[d1&0x0f] : CartDebug::ourTIAMnemonicW[d1&0x3f]);
else
nextline << " $" << HEX2 << (int)d1;
nextlinebytes << HEX2 << (int)d1;
}
break;
}
case IMMEDIATE:
{
d1 = Debugger::debugger().peek(myPC+myOffset); myPC++;
if (pass == 3)
{
nextline << " #$" << HEX2 << (int)d1 << " ";
nextlinebytes << HEX2 << (int)d1;
}
break;
}
case ABSOLUTE_X:
{
ad = Debugger::debugger().dpeek(myPC+myOffset); myPC+=2;
labfound = mark(ad, CartDebug::REFERENCED);
if (pass == 2 && !check_bit(ad & myAppData.end, CartDebug::CODE))
{
mark(ad, CartDebug::DATA);
}
else if (pass == 3)
{
if (ad < 0x100)
nextline << ".wx ";
else
nextline << " ";
if (labfound == 1)
{
USER_OR_AUTO_LABEL("", ad, ",X");
nextlinebytes << HEX2 << (int)(ad&0xff) << " " << HEX2 << (int)(ad>>8);
}
else if (labfound == 3)
{
nextline << CartDebug::ourIOMnemonic[ad-0x280] << ",X";
nextlinebytes << HEX2 << (int)(ad&0xff) << " " << HEX2 << (int)(ad>>8);
}
else if (labfound == 4)
{
int tmp = (ad & myAppData.end)+myOffset;
USER_OR_AUTO_LABEL("", tmp, ",X");
nextlinebytes << HEX2 << (int)(tmp&0xff) << " " << HEX2 << (int)(tmp>>8);
}
else
{
nextline << "$" << HEX4 << ad << ",X";
nextlinebytes << HEX2 << (int)(ad&0xff) << " " << HEX2 << (int)(ad>>8);
}
}
break;
}
case ABSOLUTE_Y:
{
ad = Debugger::debugger().dpeek(myPC+myOffset); myPC+=2;
labfound = mark(ad, CartDebug::REFERENCED);
if (pass == 2 && !check_bit(ad & myAppData.end, CartDebug::CODE))
{
mark(ad, CartDebug::DATA);
}
else if (pass == 3)
{
if (ad < 0x100)
nextline << ".wy ";
else
nextline << " ";
if (labfound == 1)
{
USER_OR_AUTO_LABEL("", ad, ",Y");
nextlinebytes << HEX2 << (int)(ad&0xff) << " " << HEX2 << (int)(ad>>8);
}
else if (labfound == 3)
{
nextline << CartDebug::ourIOMnemonic[ad-0x280] << ",Y";
nextlinebytes << HEX2 << (int)(ad&0xff) << " " << HEX2 << (int)(ad>>8);
}
else if (labfound == 4)
{
int tmp = (ad & myAppData.end)+myOffset;
USER_OR_AUTO_LABEL("", tmp, ",Y");
nextlinebytes << HEX2 << (int)(tmp&0xff) << " " << HEX2 << (int)(tmp>>8);
}
else
{
nextline << "$" << HEX4 << ad << ",Y";
nextlinebytes << HEX2 << (int)(ad&0xff) << " " << HEX2 << (int)(ad>>8);
}
}
break;
}
case INDIRECT_X:
{
d1 = Debugger::debugger().peek(myPC+myOffset); myPC++;
if (pass == 3)
{
nextline << " ($" << HEX2 << (int)d1 << ",X)";
nextlinebytes << HEX2 << (int)d1;
}
break;
}
case INDIRECT_Y:
{
d1 = Debugger::debugger().peek(myPC+myOffset); myPC++;
if (pass == 3)
{
nextline << " ($" << HEX2 << (int)d1 << "),Y";
nextlinebytes << HEX2 << (int)d1;
}
break;
}
case ZERO_PAGE_X:
{
d1 = Debugger::debugger().peek(myPC+myOffset); myPC++;
labfound = mark(d1, CartDebug::REFERENCED);
if (pass == 3)
{
if (labfound == 2)
nextline << " " << (ourLookup[op].rw_mode == READ ?
CartDebug::ourTIAMnemonicR[d1&0x0f] :
CartDebug::ourTIAMnemonicW[d1&0x3f]) << ",X";
else
nextline << " $" << HEX2 << (int)d1 << ",X";
}
nextlinebytes << HEX2 << (int)d1;
break;
}
case ZERO_PAGE_Y:
{
d1 = Debugger::debugger().peek(myPC+myOffset); myPC++;
labfound = mark(d1, CartDebug::REFERENCED);
if (pass == 3)
{
if (labfound == 2)
nextline << " " << (ourLookup[op].rw_mode == READ ?
CartDebug::ourTIAMnemonicR[d1&0x0f] :
CartDebug::ourTIAMnemonicW[d1&0x3f]) << ",Y";
else
nextline << " $" << HEX2 << (int)d1 << ",Y";
}
nextlinebytes << HEX2 << (int)d1;
break;
}
case RELATIVE:
{
// SA - 04-06-2010: there seemed to be a bug in distella,
// where wraparound occurred on a 32-bit int, and subsequent
// indexing into the labels array caused a crash
d1 = Debugger::debugger().peek(myPC+myOffset); myPC++;
ad = ((myPC + (Int8)d1) & 0xfff) + myOffset;
labfound = mark(ad, CartDebug::REFERENCED);
if (pass == 1)
{
if ((addbranch) && !check_bit(ad-myOffset, CartDebug::CODE))
{
myAddressQueue.push(ad);
mark(ad, CartDebug::CODE);
}
}
else if (pass == 3)
{
if (labfound == 1)
{
USER_OR_AUTO_LABEL(" ", ad, "");
}
else
nextline << " $" << HEX4 << ad;
nextlinebytes << HEX2 << (int)d1;
}
break;
}
case ABS_INDIRECT:
{
ad = Debugger::debugger().dpeek(myPC+myOffset); myPC+=2;
labfound = mark(ad, CartDebug::REFERENCED);
if (pass == 2 && !check_bit(ad & myAppData.end, CartDebug::CODE))
{
mark(ad, CartDebug::DATA);
}
else if (pass == 3)
{
if (ad < 0x100)
nextline << ".ind ";
else
nextline << " ";
}
if (labfound == 1)
{
USER_OR_AUTO_LABEL("(", ad, ")");
}
else if (labfound == 3)
nextline << "(" << CartDebug::ourIOMnemonic[ad-0x280] << ")";
else
nextline << "($" << HEX4 << ad << ")";
nextlinebytes << HEX2 << (int)(ad&0xff) << " " << HEX2 << (int)(ad>>8);
break;
}
default:
break;
} // end switch
if (pass == 1)
{
if (!strcmp(ourLookup[op].mnemonic,"RTS") ||
!strcmp(ourLookup[op].mnemonic,"JMP") ||
/* !strcmp(ourLookup[op].mnemonic,"BRK") || */
!strcmp(ourLookup[op].mnemonic,"RTI"))
{
myPCEnd = (myPC-1) + myOffset;
return;
}
}
else if (pass == 3)
{
// A complete line of disassembly (text, cycle count, and bytes)
myDisasmBuf << nextline.str() << "'"
<< ";" << dec << (int)ourLookup[op].cycles << "'"
<< nextlinebytes.str();
addEntry(CartDebug::CODE);
if (op == 0x40 || op == 0x60)
{
myDisasmBuf << " ' ' ";
addEntry(CartDebug::NONE);
}
nextline.str("");
nextlinebytes.str("");
}
}
} /* while loop */
/* Just in case we are disassembling outside of the address range, force the myPCEnd to EOF */
myPCEnd = myAppData.end + myOffset;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int DiStella::mark(uInt32 address, uInt8 mask)
{
/*-----------------------------------------------------------------------
For any given offset and code range...
If we're between the offset and the end of the code range, we mark
the bit in the labels array for that data. The labels array is an
array of label info for each code address. If this is the case,
return "1", else...
We sweep for hardware/system equates, which are valid addresses,
outside the scope of the code/data range. For these, we mark its
corresponding hardware/system array element, and return "2" or "3"
(depending on which system/hardware element was accessed).
If this was not the case...
Next we check if it is a code "mirror". For the 2600, address ranges
are limited with 13 bits, so other addresses can exist outside of the
standard code/data range. For these, we mark the element in the "labels"
array that corresponds to the mirrored address, and return "4"
If all else fails, it's not a valid address, so return 0.
A quick example breakdown for a 2600 4K cart:
===========================================================
$00-$3d = system equates (WSYNC, etc...); mark the array's element
with the appropriate bit; return 2.
$0280-$0297 = system equates (INPT0, etc...); mark the array's element
with the appropriate bit; return 3.
$1000-$1FFF = mark the code/data array for the mirrored address
with the appropriate bit; return 4.
$3000-$3FFF = mark the code/data array for the mirrored address
with the appropriate bit; return 4.
$5000-$5FFF = mark the code/data array for the mirrored address
with the appropriate bit; return 4.
$7000-$7FFF = mark the code/data array for the mirrored address
with the appropriate bit; return 4.
$9000-$9FFF = mark the code/data array for the mirrored address
with the appropriate bit; return 4.
$B000-$BFFF = mark the code/data array for the mirrored address
with the appropriate bit; return 4.
$D000-$DFFF = mark the code/data array for the mirrored address
with the appropriate bit; return 4.
$F000-$FFFF = mark the code/data array for the address
with the appropriate bit; return 1.
Anything else = invalid, return 0.
===========================================================
-----------------------------------------------------------------------*/
if (address >= myOffset && address <= myAppData.end + myOffset)
{
Debugger::debugger().setAddressDisasmType(address | myOffset, mask);
labels[address-myOffset] = labels[address-myOffset] | mask;
return 1;
}
else if (address >= 0 && address <= 0x3f)
{
return 2;
}
else if (address >= 0x280 && address <= 0x297)
{
return 3;
}
else if (address > 0x1000)
{
/* 2K & 4K case */
Debugger::debugger().setAddressDisasmType(address | myOffset, mask);
labels[address & myAppData.end] = labels[address & myAppData.end] | mask;
return 4;
}
else
return 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool DiStella::check_bit(uInt16 address, uInt8 mask) const
{
if(Debugger::debugger().getAddressDisasmType(address | myOffset) & mask)
return true;
else
return labels[address & myAppData.end] & mask;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool DiStella::check_range(uInt16 beg, uInt16 end) const
{
if(beg > end)
{
cerr << "Beginning of range greater than end: start = " << hex << beg
<< ", end = " << hex << end << endl;
return false;
}
else if(beg > myAppData.end + myOffset)
{
cerr << "Beginning of range out of range: start = " << hex << beg
<< ", range = " << hex << (myAppData.end + myOffset) << endl;
return false;
}
else if(beg < myOffset)
{
cerr << "Beginning of range out of range: start = " << hex << beg
<< ", offset = " << hex << myOffset << endl;
return false;
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DiStella::addEntry(CartDebug::DisasmType type)
{
CartDebug::DisassemblyTag tag;
// Type
tag.type = type;
// Address
myDisasmBuf.seekg(0, ios::beg);
if(myDisasmBuf.peek() == ' ')
tag.address = 0;
else
myDisasmBuf >> setw(4) >> hex >> tag.address;
// Only include addresses within the requested range
if(tag.address < myAppData.start)
goto DONE_WITH_ADD;
// Label (a user-defined label always overrides any auto-generated one)
myDisasmBuf.seekg(5, ios::beg);
if(tag.address)
{
tag.label = myDbg.getLabel(tag.address, true);
if(tag.label == EmptyString)
{
if(myDisasmBuf.peek() != ' ')
getline(myDisasmBuf, tag.label, '\'');
else if(settings.show_addresses && tag.type == CartDebug::CODE)
{
// Have addresses indented, to differentiate from actual labels
char address[8];
sprintf(address, " $%X", tag.address);
tag.label = address;
}
}
}
// Disassembly
// Up to this point the field sizes are fixed, until we get to
// variable length labels, cycle counts, etc
myDisasmBuf.seekg(11, ios::beg);
switch(tag.type)
{
case CartDebug::SKIP: // TODO - handle this
tag.disasm = " ";
break;
case CartDebug::CODE:
getline(myDisasmBuf, tag.disasm, '\'');
getline(myDisasmBuf, tag.ccount, '\'');
getline(myDisasmBuf, tag.bytes);
break;
case CartDebug::GFX:
case CartDebug::PGFX:
getline(myDisasmBuf, tag.disasm, '\'');
getline(myDisasmBuf, tag.bytes);
break;
case CartDebug::DATA:
getline(myDisasmBuf, tag.disasm, '\'');
getline(myDisasmBuf, tag.bytes);
break;
case CartDebug::ROW:
getline(myDisasmBuf, tag.disasm);
break;
default: // should never happen
tag.disasm = " ";
break;
}
myList.push_back(tag);
DONE_WITH_ADD:
myDisasmBuf.clear();
myDisasmBuf.str("");
#if 0
// debugging output
cerr << (tag.type == CartDebug::CODE ? "CartDebug::CODE" : (tag.type == CartDebug::ROW ? "CartDebug::ROW" :
(tag.type == CartDebug::GFX ? "CartDebug::GFX " : "NONE"))) << "|"
<< hex << setw(4) << setfill('0') << tag.address << "|"
<< tag.label << "|" << tag.disasm << "|" << tag.ccount << "|" << "|"
<< tag.bytes << endl;
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void DiStella::processDirectives(const CartDebug::DirectiveList& directives)
{
for(CartDebug::DirectiveList::const_iterator i = directives.begin();
i != directives.end(); ++i)
{
const CartDebug::DirectiveTag tag = *i;
if(check_range(tag.start, tag.end))
for(uInt32 k = tag.start; k <= tag.end; ++k)
mark(k, tag.type);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DiStella::Settings DiStella::settings = {
kBASE_2, // gfx_format
true // show_addresses
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const DiStella::Instruction_tag DiStella::ourLookup[256] = {
/**** Positive ****/
/* 00 */ { "BRK", IMPLIED, M_NONE, NONE, 7 }, /* Pseudo Absolute */
/* 01 */ { "ORA", INDIRECT_X, M_INDX, READ, 6 }, /* (Indirect,X) */
/* 02 */ { "jam", IMPLIED, M_NONE, NONE, 0 }, /* TILT */
/* 03 */ { "slo", INDIRECT_X, M_INDX, WRITE, 8 },
/* 04 */ { "nop", ZERO_PAGE, M_NONE, NONE, 3 },
/* 05 */ { "ORA", ZERO_PAGE, M_ZERO, READ, 3 }, /* Zeropage */
/* 06 */ { "ASL", ZERO_PAGE, M_ZERO, WRITE, 5 }, /* Zeropage */
/* 07 */ { "slo", ZERO_PAGE, M_ZERO, WRITE, 5 },
/* 08 */ { "PHP", IMPLIED, M_SR, NONE, 3 },
/* 09 */ { "ORA", IMMEDIATE, M_IMM, READ, 2 }, /* Immediate */
/* 0a */ { "ASL", ACCUMULATOR, M_AC, WRITE, 2 }, /* Accumulator */
/* 0b */ { "anc", IMMEDIATE, M_ACIM, READ, 2 },
/* 0c */ { "nop", ABSOLUTE, M_NONE, NONE, 4 },
/* 0d */ { "ORA", ABSOLUTE, M_ABS, READ, 4 }, /* Absolute */
/* 0e */ { "ASL", ABSOLUTE, M_ABS, WRITE, 6 }, /* Absolute */
/* 0f */ { "slo", ABSOLUTE, M_ABS, WRITE, 6 },
/* 10 */ { "BPL", RELATIVE, M_REL, READ, 2 },
/* 11 */ { "ORA", INDIRECT_Y, M_INDY, READ, 5 }, /* (Indirect),Y */
/* 12 */ { "jam", IMPLIED, M_NONE, NONE, 0 }, /* TILT */
/* 13 */ { "slo", INDIRECT_Y, M_INDY, WRITE, 8 },
/* 14 */ { "nop", ZERO_PAGE_X, M_NONE, NONE, 4 },
/* 15 */ { "ORA", ZERO_PAGE_X, M_ZERX, READ, 4 }, /* Zeropage,X */
/* 16 */ { "ASL", ZERO_PAGE_X, M_ZERX, WRITE, 6 }, /* Zeropage,X */
/* 17 */ { "slo", ZERO_PAGE_X, M_ZERX, WRITE, 6 },
/* 18 */ { "CLC", IMPLIED, M_NONE, NONE, 2 },
/* 19 */ { "ORA", ABSOLUTE_Y, M_ABSY, READ, 4 }, /* Absolute,Y */
/* 1a */ { "nop", IMPLIED, M_NONE, NONE, 2 },
/* 1b */ { "slo", ABSOLUTE_Y, M_ABSY, WRITE, 7 },
/* 1c */ { "nop", ABSOLUTE_X, M_NONE, NONE, 4 },
/* 1d */ { "ORA", ABSOLUTE_X, M_ABSX, READ, 4 }, /* Absolute,X */
/* 1e */ { "ASL", ABSOLUTE_X, M_ABSX, WRITE, 7 }, /* Absolute,X */
/* 1f */ { "slo", ABSOLUTE_X, M_ABSX, WRITE, 7 },
/* 20 */ { "JSR", ABSOLUTE, M_ADDR, READ, 6 },
/* 21 */ { "AND", INDIRECT_X, M_INDX, READ, 6 }, /* (Indirect ,X) */
/* 22 */ { "jam", IMPLIED, M_NONE, NONE, 0 }, /* TILT */
/* 23 */ { "rla", INDIRECT_X, M_INDX, WRITE, 8 },
/* 24 */ { "BIT", ZERO_PAGE, M_ZERO, READ, 3 }, /* Zeropage */
/* 25 */ { "AND", ZERO_PAGE, M_ZERO, READ, 3 }, /* Zeropage */
/* 26 */ { "ROL", ZERO_PAGE, M_ZERO, WRITE, 5 }, /* Zeropage */
/* 27 */ { "rla", ZERO_PAGE, M_ZERO, WRITE, 5 },
/* 28 */ { "PLP", IMPLIED, M_NONE, NONE, 4 },
/* 29 */ { "AND", IMMEDIATE, M_IMM, READ, 2 }, /* Immediate */
/* 2a */ { "ROL", ACCUMULATOR, M_AC, WRITE, 2 }, /* Accumulator */
/* 2b */ { "anc", IMMEDIATE, M_ACIM, READ, 2 },
/* 2c */ { "BIT", ABSOLUTE, M_ABS, READ, 4 }, /* Absolute */
/* 2d */ { "AND", ABSOLUTE, M_ABS, READ, 4 }, /* Absolute */
/* 2e */ { "ROL", ABSOLUTE, M_ABS, WRITE, 6 }, /* Absolute */
/* 2f */ { "rla", ABSOLUTE, M_ABS, WRITE, 6 },
/* 30 */ { "BMI", RELATIVE, M_REL, READ, 2 },
/* 31 */ { "AND", INDIRECT_Y, M_INDY, READ, 5 }, /* (Indirect),Y */
/* 32 */ { "jam", IMPLIED, M_NONE, NONE, 0 }, /* TILT */
/* 33 */ { "rla", INDIRECT_Y, M_INDY, WRITE, 8 },
/* 34 */ { "nop", ZERO_PAGE_X, M_NONE, NONE, 4 },
/* 35 */ { "AND", ZERO_PAGE_X, M_ZERX, READ, 4 }, /* Zeropage,X */
/* 36 */ { "ROL", ZERO_PAGE_X, M_ZERX, WRITE, 6 }, /* Zeropage,X */
/* 37 */ { "rla", ZERO_PAGE_X, M_ZERX, WRITE, 6 },
/* 38 */ { "SEC", IMPLIED, M_NONE, NONE, 2 },
/* 39 */ { "AND", ABSOLUTE_Y, M_ABSY, READ, 4 }, /* Absolute,Y */
/* 3a */ { "nop", IMPLIED, M_NONE, NONE, 2 },
/* 3b */ { "rla", ABSOLUTE_Y, M_ABSY, WRITE, 7 },
/* 3c */ { "nop", ABSOLUTE_X, M_NONE, NONE, 4 },
/* 3d */ { "AND", ABSOLUTE_X, M_ABSX, READ, 4 }, /* Absolute,X */
/* 3e */ { "ROL", ABSOLUTE_X, M_ABSX, WRITE, 7 }, /* Absolute,X */
/* 3f */ { "rla", ABSOLUTE_X, M_ABSX, WRITE, 7 },
/* 40 */ { "RTI", IMPLIED, M_NONE, NONE, 6 },
/* 41 */ { "EOR", INDIRECT_X, M_INDX, READ, 6 }, /* (Indirect,X) */
/* 42 */ { "jam", IMPLIED, M_NONE, NONE, 0 }, /* TILT */
/* 43 */ { "sre", INDIRECT_X, M_INDX, WRITE, 8 },
/* 44 */ { "nop", ZERO_PAGE, M_NONE, NONE, 3 },
/* 45 */ { "EOR", ZERO_PAGE, M_ZERO, READ, 3 }, /* Zeropage */
/* 46 */ { "LSR", ZERO_PAGE, M_ZERO, WRITE, 5 }, /* Zeropage */
/* 47 */ { "sre", ZERO_PAGE, M_ZERO, WRITE, 5 },
/* 48 */ { "PHA", IMPLIED, M_AC, NONE, 3 },
/* 49 */ { "EOR", IMMEDIATE, M_IMM, READ, 2 }, /* Immediate */
/* 4a */ { "LSR", ACCUMULATOR, M_AC, WRITE, 2 }, /* Accumulator */
/* 4b */ { "asr", IMMEDIATE, M_ACIM, READ, 2 }, /* (AC & IMM) >>1 */
/* 4c */ { "JMP", ABSOLUTE, M_ADDR, READ, 3 }, /* Absolute */
/* 4d */ { "EOR", ABSOLUTE, M_ABS, READ, 4 }, /* Absolute */
/* 4e */ { "LSR", ABSOLUTE, M_ABS, WRITE, 6 }, /* Absolute */
/* 4f */ { "sre", ABSOLUTE, M_ABS, WRITE, 6 },
/* 50 */ { "BVC", RELATIVE, M_REL, READ, 2 },
/* 51 */ { "EOR", INDIRECT_Y, M_INDY, READ, 5 }, /* (Indirect),Y */
/* 52 */ { "jam", IMPLIED, M_NONE, NONE, 0 }, /* TILT */
/* 53 */ { "sre", INDIRECT_Y, M_INDY, WRITE, 8 },
/* 54 */ { "nop", ZERO_PAGE_X, M_NONE, NONE, 4 },
/* 55 */ { "EOR", ZERO_PAGE_X, M_ZERX, READ, 4 }, /* Zeropage,X */
/* 56 */ { "LSR", ZERO_PAGE_X, M_ZERX, WRITE, 6 }, /* Zeropage,X */
/* 57 */ { "sre", ZERO_PAGE_X, M_ZERX, WRITE, 6 },
/* 58 */ { "CLI", IMPLIED, M_NONE, NONE, 2 },
/* 59 */ { "EOR", ABSOLUTE_Y, M_ABSY, READ, 4 }, /* Absolute,Y */
/* 5a */ { "nop", IMPLIED, M_NONE, NONE, 2 },
/* 5b */ { "sre", ABSOLUTE_Y, M_ABSY, WRITE, 7 },
/* 5c */ { "nop", ABSOLUTE_X, M_NONE, NONE, 4 },
/* 5d */ { "EOR", ABSOLUTE_X, M_ABSX, READ, 4 }, /* Absolute,X */
/* 5e */ { "LSR", ABSOLUTE_X, M_ABSX, WRITE, 7 }, /* Absolute,X */
/* 5f */ { "sre", ABSOLUTE_X, M_ABSX, WRITE, 7 },
/* 60 */ { "RTS", IMPLIED, M_NONE, NONE, 6 },
/* 61 */ { "ADC", INDIRECT_X, M_INDX, READ, 6 }, /* (Indirect,X) */
/* 62 */ { "jam", IMPLIED, M_NONE, NONE, 0 }, /* TILT */
/* 63 */ { "rra", INDIRECT_X, M_INDX, WRITE, 8 },
/* 64 */ { "nop", ZERO_PAGE, M_NONE, NONE, 3 },
/* 65 */ { "ADC", ZERO_PAGE, M_ZERO, READ, 3 }, /* Zeropage */
/* 66 */ { "ROR", ZERO_PAGE, M_ZERO, WRITE, 5 }, /* Zeropage */
/* 67 */ { "rra", ZERO_PAGE, M_ZERO, WRITE, 5 },
/* 68 */ { "PLA", IMPLIED, M_NONE, NONE, 4 },
/* 69 */ { "ADC", IMMEDIATE, M_IMM, READ, 2 }, /* Immediate */
/* 6a */ { "ROR", ACCUMULATOR, M_AC, WRITE, 2 }, /* Accumulator */
/* 6b */ { "arr", IMMEDIATE, M_ACIM, READ, 2 }, /* ARR isn't typo */
/* 6c */ { "JMP", ABS_INDIRECT,M_AIND, READ, 5 }, /* Indirect */
/* 6d */ { "ADC", ABSOLUTE, M_ABS, READ, 4 }, /* Absolute */
/* 6e */ { "ROR", ABSOLUTE, M_ABS, WRITE, 6 }, /* Absolute */
/* 6f */ { "rra", ABSOLUTE, M_ABS, WRITE, 6 },
/* 70 */ { "BVS", RELATIVE, M_REL, READ, 2 },
/* 71 */ { "ADC", INDIRECT_Y, M_INDY, READ, 5 }, /* (Indirect),Y */
/* 72 */ { "jam", IMPLIED, M_NONE, NONE, 0 }, /* TILT relative? */
/* 73 */ { "rra", INDIRECT_Y, M_INDY, WRITE, 8 },
/* 74 */ { "nop", ZERO_PAGE_X, M_NONE, NONE, 4 },
/* 75 */ { "ADC", ZERO_PAGE_X, M_ZERX, READ, 4 }, /* Zeropage,X */
/* 76 */ { "ROR", ZERO_PAGE_X, M_ZERX, WRITE, 6 }, /* Zeropage,X */
/* 77 */ { "rra", ZERO_PAGE_X, M_ZERX, WRITE, 6 },
/* 78 */ { "SEI", IMPLIED, M_NONE, NONE, 2 },
/* 79 */ { "ADC", ABSOLUTE_Y, M_ABSY, READ, 4 }, /* Absolute,Y */
/* 7a */ { "nop", IMPLIED, M_NONE, NONE, 2 },
/* 7b */ { "rra", ABSOLUTE_Y, M_ABSY, WRITE, 7 },
/* 7c */ { "nop", ABSOLUTE_X, M_NONE, NONE, 4 },
/* 7d */ { "ADC", ABSOLUTE_X, M_ABSX, READ, 4 }, /* Absolute,X */
/* 7e */ { "ROR", ABSOLUTE_X, M_ABSX, WRITE, 7 }, /* Absolute,X */
/* 7f */ { "rra", ABSOLUTE_X, M_ABSX, WRITE, 7 },
/**** Negative ****/
/* 80 */ { "nop", IMMEDIATE, M_NONE, NONE, 2 },
/* 81 */ { "STA", INDIRECT_X, M_AC, WRITE, 6 }, /* (Indirect,X) */
/* 82 */ { "nop", IMMEDIATE, M_NONE, NONE, 2 },
/* 83 */ { "sax", INDIRECT_X, M_ANXR, WRITE, 6 },
/* 84 */ { "STY", ZERO_PAGE, M_YR, WRITE, 3 }, /* Zeropage */
/* 85 */ { "STA", ZERO_PAGE, M_AC, WRITE, 3 }, /* Zeropage */
/* 86 */ { "STX", ZERO_PAGE, M_XR, WRITE, 3 }, /* Zeropage */
/* 87 */ { "sax", ZERO_PAGE, M_ANXR, WRITE, 3 },
/* 88 */ { "DEY", IMPLIED, M_YR, NONE, 2 },
/* 89 */ { "nop", IMMEDIATE, M_NONE, NONE, 2 },
/* 8a */ { "TXA", IMPLIED, M_XR, NONE, 2 },
/**** very abnormal: usually AC = AC | #$EE & XR & #$oper ****/
/* 8b */ { "ane", IMMEDIATE, M_AXIM, READ, 2 },
/* 8c */ { "STY", ABSOLUTE, M_YR, WRITE, 4 }, /* Absolute */
/* 8d */ { "STA", ABSOLUTE, M_AC, WRITE, 4 }, /* Absolute */
/* 8e */ { "STX", ABSOLUTE, M_XR, WRITE, 4 }, /* Absolute */
/* 8f */ { "sax", ABSOLUTE, M_ANXR, WRITE, 4 },
/* 90 */ { "BCC", RELATIVE, M_REL, READ, 2 },
/* 91 */ { "STA", INDIRECT_Y, M_AC, WRITE, 6 }, /* (Indirect),Y */
/* 92 */ { "jam", IMPLIED, M_NONE, NONE, 0 }, /* TILT relative? */
/* 93 */ { "sha", INDIRECT_Y, M_ANXR, WRITE, 6 },
/* 94 */ { "STY", ZERO_PAGE_X, M_YR, WRITE, 4 }, /* Zeropage,X */
/* 95 */ { "STA", ZERO_PAGE_X, M_AC, WRITE, 4 }, /* Zeropage,X */
/* 96 */ { "STX", ZERO_PAGE_Y, M_XR, WRITE, 4 }, /* Zeropage,Y */
/* 97 */ { "sax", ZERO_PAGE_Y, M_ANXR, WRITE, 4 },
/* 98 */ { "TYA", IMPLIED, M_YR, NONE, 2 },
/* 99 */ { "STA", ABSOLUTE_Y, M_AC, WRITE, 5 }, /* Absolute,Y */
/* 9a */ { "TXS", IMPLIED, M_XR, NONE, 2 },
/*** This is very mysterious comm AND ... */
/* 9b */ { "shs", ABSOLUTE_Y, M_ANXR, WRITE, 5 },
/* 9c */ { "shy", ABSOLUTE_X, M_YR, WRITE, 5 },
/* 9d */ { "STA", ABSOLUTE_X, M_AC, WRITE, 5 }, /* Absolute,X */
/* 9e */ { "shx", ABSOLUTE_Y, M_XR , WRITE, 5 },
/* 9f */ { "sha", ABSOLUTE_Y, M_ANXR, WRITE, 5 },
/* a0 */ { "LDY", IMMEDIATE, M_IMM, READ, 2 }, /* Immediate */
/* a1 */ { "LDA", INDIRECT_X, M_INDX, READ, 6 }, /* (indirect,X) */
/* a2 */ { "LDX", IMMEDIATE, M_IMM, READ, 2 }, /* Immediate */
/* a3 */ { "lax", INDIRECT_X, M_INDX, READ, 6 }, /* (indirect,X) */
/* a4 */ { "LDY", ZERO_PAGE, M_ZERO, READ, 3 }, /* Zeropage */
/* a5 */ { "LDA", ZERO_PAGE, M_ZERO, READ, 3 }, /* Zeropage */
/* a6 */ { "LDX", ZERO_PAGE, M_ZERO, READ, 3 }, /* Zeropage */
/* a7 */ { "lax", ZERO_PAGE, M_ZERO, READ, 3 },
/* a8 */ { "TAY", IMPLIED, M_AC, NONE, 2 },
/* a9 */ { "LDA", IMMEDIATE, M_IMM, READ, 2 }, /* Immediate */
/* aa */ { "TAX", IMPLIED, M_AC, NONE, 2 },
/* ab */ { "lxa", IMMEDIATE, M_ACIM, READ, 2 }, /* LXA isn't a typo */
/* ac */ { "LDY", ABSOLUTE, M_ABS, READ, 4 }, /* Absolute */
/* ad */ { "LDA", ABSOLUTE, M_ABS, READ, 4 }, /* Absolute */
/* ae */ { "LDX", ABSOLUTE, M_ABS, READ, 4 }, /* Absolute */
/* af */ { "lax", ABSOLUTE, M_ABS, READ, 4 },
/* b0 */ { "BCS", RELATIVE, M_REL, READ, 2 },
/* b1 */ { "LDA", INDIRECT_Y, M_INDY, READ, 5 }, /* (indirect),Y */
/* b2 */ { "jam", IMPLIED, M_NONE, NONE, 0 }, /* TILT */
/* b3 */ { "lax", INDIRECT_Y, M_INDY, READ, 5 },
/* b4 */ { "LDY", ZERO_PAGE_X, M_ZERX, READ, 4 }, /* Zeropage,X */
/* b5 */ { "LDA", ZERO_PAGE_X, M_ZERX, READ, 4 }, /* Zeropage,X */
/* b6 */ { "LDX", ZERO_PAGE_Y, M_ZERY, READ, 4 }, /* Zeropage,Y */
/* b7 */ { "lax", ZERO_PAGE_Y, M_ZERY, READ, 4 },
/* b8 */ { "CLV", IMPLIED, M_NONE, NONE, 2 },
/* b9 */ { "LDA", ABSOLUTE_Y, M_ABSY, READ, 4 }, /* Absolute,Y */
/* ba */ { "TSX", IMPLIED, M_SP, NONE, 2 },
/* bb */ { "las", ABSOLUTE_Y, M_SABY, READ, 4 },
/* bc */ { "LDY", ABSOLUTE_X, M_ABSX, READ, 4 }, /* Absolute,X */
/* bd */ { "LDA", ABSOLUTE_X, M_ABSX, READ, 4 }, /* Absolute,X */
/* be */ { "LDX", ABSOLUTE_Y, M_ABSY, READ, 4 }, /* Absolute,Y */
/* bf */ { "lax", ABSOLUTE_Y, M_ABSY, READ, 4 },
/* c0 */ { "CPY", IMMEDIATE, M_IMM, READ, 2 }, /* Immediate */
/* c1 */ { "CMP", INDIRECT_X, M_INDX, READ, 6 }, /* (Indirect,X) */
/* c2 */ { "nop", IMMEDIATE, M_NONE, NONE, 2 }, /* occasional TILT */
/* c3 */ { "dcp", INDIRECT_X, M_INDX, WRITE, 8 },
/* c4 */ { "CPY", ZERO_PAGE, M_ZERO, READ, 3 }, /* Zeropage */
/* c5 */ { "CMP", ZERO_PAGE, M_ZERO, READ, 3 }, /* Zeropage */
/* c6 */ { "DEC", ZERO_PAGE, M_ZERO, WRITE, 5 }, /* Zeropage */
/* c7 */ { "dcp", ZERO_PAGE, M_ZERO, WRITE, 5 },
/* c8 */ { "INY", IMPLIED, M_YR, NONE, 2 },
/* c9 */ { "CMP", IMMEDIATE, M_IMM, READ, 2 }, /* Immediate */
/* ca */ { "DEX", IMPLIED, M_XR, NONE, 2 },
/* cb */ { "sbx", IMMEDIATE, M_IMM, READ, 2 },
/* cc */ { "CPY", ABSOLUTE, M_ABS, READ, 4 }, /* Absolute */
/* cd */ { "CMP", ABSOLUTE, M_ABS, READ, 4 }, /* Absolute */
/* ce */ { "DEC", ABSOLUTE, M_ABS, WRITE, 6 }, /* Absolute */
/* cf */ { "dcp", ABSOLUTE, M_ABS, WRITE, 6 },
/* d0 */ { "BNE", RELATIVE, M_REL, READ, 2 },
/* d1 */ { "CMP", INDIRECT_Y, M_INDY, READ, 5 }, /* (Indirect),Y */
/* d2 */ { "jam", IMPLIED, M_NONE, NONE, 0 }, /* TILT */
/* d3 */ { "dcp", INDIRECT_Y, M_INDY, WRITE, 8 },
/* d4 */ { "nop", ZERO_PAGE_X, M_NONE, NONE, 4 },
/* d5 */ { "CMP", ZERO_PAGE_X, M_ZERX, READ, 4 }, /* Zeropage,X */
/* d6 */ { "DEC", ZERO_PAGE_X, M_ZERX, WRITE, 6 }, /* Zeropage,X */
/* d7 */ { "dcp", ZERO_PAGE_X, M_ZERX, WRITE, 6 },
/* d8 */ { "CLD", IMPLIED, M_NONE, NONE, 2 },
/* d9 */ { "CMP", ABSOLUTE_Y, M_ABSY, READ, 4 }, /* Absolute,Y */
/* da */ { "nop", IMPLIED, M_NONE, NONE, 2 },
/* db */ { "dcp", ABSOLUTE_Y, M_ABSY, WRITE, 7 },
/* dc */ { "nop", ABSOLUTE_X, M_NONE, NONE, 4 },
/* dd */ { "CMP", ABSOLUTE_X, M_ABSX, READ, 4 }, /* Absolute,X */
/* de */ { "DEC", ABSOLUTE_X, M_ABSX, WRITE, 7 }, /* Absolute,X */
/* df */ { "dcp", ABSOLUTE_X, M_ABSX, WRITE, 7 },
/* e0 */ { "CPX", IMMEDIATE, M_IMM, READ, 2 }, /* Immediate */
/* e1 */ { "SBC", INDIRECT_X, M_INDX, READ, 6 }, /* (Indirect,X) */
/* e2 */ { "nop", IMMEDIATE, M_NONE, NONE, 2 },
/* e3 */ { "isb", INDIRECT_X, M_INDX, WRITE, 8 },
/* e4 */ { "CPX", ZERO_PAGE, M_ZERO, READ, 3 }, /* Zeropage */
/* e5 */ { "SBC", ZERO_PAGE, M_ZERO, READ, 3 }, /* Zeropage */
/* e6 */ { "INC", ZERO_PAGE, M_ZERO, WRITE, 5 }, /* Zeropage */
/* e7 */ { "isb", ZERO_PAGE, M_ZERO, WRITE, 5 },
/* e8 */ { "INX", IMPLIED, M_XR, NONE, 2 },
/* e9 */ { "SBC", IMMEDIATE, M_IMM, READ, 2 }, /* Immediate */
/* ea */ { "NOP", IMPLIED, M_NONE, NONE, 2 },
/* eb */ { "sbc", IMMEDIATE, M_IMM, READ, 2 }, /* same as e9 */
/* ec */ { "CPX", ABSOLUTE, M_ABS, READ, 4 }, /* Absolute */
/* ed */ { "SBC", ABSOLUTE, M_ABS, READ, 4 }, /* Absolute */
/* ee */ { "INC", ABSOLUTE, M_ABS, WRITE, 6 }, /* Absolute */
/* ef */ { "isb", ABSOLUTE, M_ABS, WRITE, 6 },
/* f0 */ { "BEQ", RELATIVE, M_REL, READ, 2 },
/* f1 */ { "SBC", INDIRECT_Y, M_INDY, READ, 5 }, /* (Indirect),Y */
/* f2 */ { "jam", IMPLIED, M_NONE, NONE, 0 }, /* TILT */
/* f3 */ { "isb", INDIRECT_Y, M_INDY, WRITE, 8 },
/* f4 */ { "nop", ZERO_PAGE_X, M_NONE, NONE, 4 },
/* f5 */ { "SBC", ZERO_PAGE_X, M_ZERX, READ, 4 }, /* Zeropage,X */
/* f6 */ { "INC", ZERO_PAGE_X, M_ZERX, WRITE, 6 }, /* Zeropage,X */
/* f7 */ { "isb", ZERO_PAGE_X, M_ZERX, WRITE, 6 },
/* f8 */ { "SED", IMPLIED, M_NONE, NONE, 2 },
/* f9 */ { "SBC", ABSOLUTE_Y, M_ABSY, READ, 4 }, /* Absolute,Y */
/* fa */ { "nop", IMPLIED, M_NONE, NONE, 2 },
/* fb */ { "isb", ABSOLUTE_Y, M_ABSY, WRITE, 7 },
/* fc */ { "nop", ABSOLUTE_X, M_NONE, NONE, 4 },
/* fd */ { "SBC", ABSOLUTE_X, M_ABSX, READ, 4 }, /* Absolute,X */
/* fe */ { "INC", ABSOLUTE_X, M_ABSX, WRITE, 7 }, /* Absolute,X */
/* ff */ { "isb", ABSOLUTE_X, M_ABSX, WRITE, 7 }
};