in cores/atari2600/stella/src/debugger/DiStella.cxx [230:838]
void DiStella::disasm(uInt32 distart, int pass)
{
#define LABEL_A12_HIGH(address) labelA12High(nextline, op, address, labfound)
#define LABEL_A12_LOW(address) labelA12Low(nextline, op, address, labfound)
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))
{
mark(myPC+myOffset, CartDebug::VALID_ENTRY);
if (pass == 3)
{
if (check_bit(myPC, CartDebug::REFERENCED))
myDisasmBuf << Base::HEX4 << myPC+myOffset << "'L" << Base::HEX4 << myPC+myOffset << "'";
else
myDisasmBuf << Base::HEX4 << myPC+myOffset << "' '";
bool isPGfx = check_bit(myPC, CartDebug::PGFX);
const string& bit_string = isPGfx ? "\x1f" : "\x1e";
uInt8 byte = Debugger::debugger().peek(myPC+myOffset);
myDisasmBuf << ".byte $" << Base::HEX2 << (int)byte << " |";
for(uInt8 i = 0, c = byte; i < 8; ++i, c <<= 1)
myDisasmBuf << ((c > 127) ? bit_string : " ");
myDisasmBuf << "| $" << Base::HEX4 << myPC+myOffset << "'";
if(mySettings.gfx_format == Base::F_2)
myDisasmBuf << Base::toString(byte, Base::F_2_8);
else
myDisasmBuf << Base::HEX2 << (int)byte;
addEntry(isPGfx ? CartDebug::PGFX : 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 << Base::HEX4 << myPC+myOffset << "'L" << Base::HEX4 << myPC+myOffset << "'";
else
myDisasmBuf << Base::HEX4 << myPC+myOffset << "' '";
uInt8 byte = Debugger::debugger().peek(myPC+myOffset);
myDisasmBuf << ".byte $" << Base::HEX2 << (int)byte << " $"
<< Base::HEX4 << myPC+myOffset << "'"
<< Base::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))
{
if (pass == 2)
mark(myPC+myOffset, CartDebug::VALID_ENTRY);
if (pass == 3)
{
bool row = check_bit(myPC, CartDebug::ROW) &&
!check_bit(myPC, CartDebug::CODE | CartDebug::DATA |
CartDebug::GFX | CartDebug::PGFX);
bool referenced = check_bit(myPC, CartDebug::REFERENCED);
bool line_empty = true;
while (row && myPC <= myAppData.end)
{
if(referenced) // start a new line with a label
{
if(!line_empty)
{
addEntry(CartDebug::ROW);
line_empty = true;
}
myDisasmBuf << Base::HEX4 << myPC+myOffset << "'L" << Base::HEX4
<< myPC+myOffset << "'.byte " << "$" << Base::HEX2
<< (int)Debugger::debugger().peek(myPC+myOffset);
myPC++;
bytes = 1;
line_empty = false;
}
else if(line_empty) // start a new line without a label
{
myDisasmBuf << " ' '.byte $" << Base::HEX2 << (int)Debugger::debugger().peek(myPC+myOffset);
myPC++;
bytes = 1;
line_empty = false;
}
// Otherwise, append bytes to the current line, up until the maximum
else if(++bytes == mySettings.bwidth)
{
addEntry(CartDebug::ROW);
line_empty = true;
}
else
{
myDisasmBuf << ",$" << Base::HEX2 << (int)Debugger::debugger().peek(myPC+myOffset);
myPC++;
}
row = check_bit(myPC, CartDebug::ROW) &&
!check_bit(myPC, CartDebug::CODE | CartDebug::DATA |
CartDebug::GFX | CartDebug::PGFX);
referenced = check_bit(myPC, CartDebug::REFERENCED);
}
if(!line_empty)
addEntry(CartDebug::ROW);
myDisasmBuf << " ' ' ";
addEntry(CartDebug::NONE);
}
else
myPC++;
}
else // The following sections must be 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 << Base::HEX4 << myPC+myOffset << "'L" << Base::HEX4 << myPC+myOffset << "'";
else
myDisasmBuf << Base::HEX4 << myPC+myOffset << "' '";
}
// Add opcode mneumonic
//
addr_mode = ourLookup[op].addr_mode;
myPC++;
// Undefined opcodes start with a '.'
// These are undefined wrt DASM
if (ourLookup[op].mnemonic[0] == '.')
{
addr_mode = IMPLIED;
if (pass == 3)
nextline << ".byte $" << Base::HEX2 << (int)op << " ;";
}
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 << Base::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 $" << Base::HEX2 << (int)op << " $"
<< Base::HEX4 << myPC+myOffset << "'"
<< Base::HEX2 << (int)op;
addEntry(CartDebug::DATA);
if (myPC == myAppData.end)
{
if (check_bit(myPC, CartDebug::REFERENCED))
myDisasmBuf << Base::HEX4 << myPC+myOffset << "'L" << Base::HEX4 << myPC+myOffset << "'";
else
myDisasmBuf << Base::HEX4 << myPC+myOffset << "' '";
op = Debugger::debugger().peek(myPC+myOffset); myPC++;
myDisasmBuf << ".byte $" << Base::HEX2 << (int)op << " $"
<< Base::HEX4 << myPC+myOffset << "'"
<< Base::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 $" << Base::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 && mySettings.aflag)
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 && mySettings.fflag)
nextline << ".w ";
else
nextline << " ";
if (labfound == 1)
{
LABEL_A12_HIGH(ad);
nextlinebytes << Base::HEX2 << (int)(ad&0xff) << " " << Base::HEX2 << (int)(ad>>8);
}
else if (labfound == 4)
{
if(mySettings.rflag)
{
int tmp = (ad & myAppData.end)+myOffset;
LABEL_A12_HIGH(tmp);
nextlinebytes << Base::HEX2 << (int)(tmp&0xff) << " " << Base::HEX2 << (int)(tmp>>8);
}
else
{
nextline << "$" << Base::HEX4 << ad;
nextlinebytes << Base::HEX2 << (int)(ad&0xff) << " " << Base::HEX2 << (int)(ad>>8);
}
}
else
{
LABEL_A12_LOW(ad);
nextlinebytes << Base::HEX2 << (int)(ad&0xff) << " " << Base::HEX2 << (int)(ad>>8);
}
}
break;
}
case ZERO_PAGE:
{
d1 = Debugger::debugger().peek(myPC+myOffset); myPC++;
labfound = mark(d1, CartDebug::REFERENCED);
if (pass == 3)
{
nextline << " ";
LABEL_A12_LOW((int)d1);
nextlinebytes << Base::HEX2 << (int)d1;
}
break;
}
case IMMEDIATE:
{
d1 = Debugger::debugger().peek(myPC+myOffset); myPC++;
if (pass == 3)
{
nextline << " #$" << Base::HEX2 << (int)d1 << " ";
nextlinebytes << Base::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))
{
// Since we can't know what address is being accessed unless we also
// know the current X value, this is marked as ROW instead of DATA
// The processing is left here, however, in case future versions of
// the code can somehow track access to CPU registers
mark(ad, CartDebug::ROW);
}
else if (pass == 3)
{
if (ad < 0x100 && mySettings.fflag)
nextline << ".wx ";
else
nextline << " ";
if (labfound == 1)
{
LABEL_A12_HIGH(ad);
nextline << ",X";
nextlinebytes << Base::HEX2 << (int)(ad&0xff) << " " << Base::HEX2 << (int)(ad>>8);
}
else if (labfound == 4)
{
if(mySettings.rflag)
{
int tmp = (ad & myAppData.end)+myOffset;
LABEL_A12_HIGH(tmp);
nextline << ",X";
nextlinebytes << Base::HEX2 << (int)(tmp&0xff) << " " << Base::HEX2 << (int)(tmp>>8);
}
else
{
nextline << "$" << Base::HEX4 << ad << ",X";
nextlinebytes << Base::HEX2 << (int)(ad&0xff) << " " << Base::HEX2 << (int)(ad>>8);
}
}
else
{
LABEL_A12_LOW(ad);
nextline << ",X";
nextlinebytes << Base::HEX2 << (int)(ad&0xff) << " " << Base::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))
{
// Since we can't know what address is being accessed unless we also
// know the current Y value, this is marked as ROW instead of DATA
// The processing is left here, however, in case future versions of
// the code can somehow track access to CPU registers
mark(ad, CartDebug::ROW);
}
else if (pass == 3)
{
if (ad < 0x100 && mySettings.fflag)
nextline << ".wy ";
else
nextline << " ";
if (labfound == 1)
{
LABEL_A12_HIGH(ad);
nextline << ",Y";
nextlinebytes << Base::HEX2 << (int)(ad&0xff) << " " << Base::HEX2 << (int)(ad>>8);
}
else if (labfound == 4)
{
if(mySettings.rflag)
{
int tmp = (ad & myAppData.end)+myOffset;
LABEL_A12_HIGH(tmp);
nextline << ",Y";
nextlinebytes << Base::HEX2 << (int)(tmp&0xff) << " " << Base::HEX2 << (int)(tmp>>8);
}
else
{
nextline << "$" << Base::HEX4 << ad << ",Y";
nextlinebytes << Base::HEX2 << (int)(ad&0xff) << " " << Base::HEX2 << (int)(ad>>8);
}
}
else
{
LABEL_A12_LOW(ad);
nextline << ",Y";
nextlinebytes << Base::HEX2 << (int)(ad&0xff) << " " << Base::HEX2 << (int)(ad>>8);
}
}
break;
}
case INDIRECT_X:
{
d1 = Debugger::debugger().peek(myPC+myOffset); myPC++;
if (pass == 3)
{
labfound = mark(d1, 0); // dummy call to get address type
nextline << " (";
LABEL_A12_LOW(d1);
nextline << ",X)";
nextlinebytes << Base::HEX2 << (int)d1;
}
break;
}
case INDIRECT_Y:
{
d1 = Debugger::debugger().peek(myPC+myOffset); myPC++;
if (pass == 3)
{
labfound = mark(d1, 0); // dummy call to get address type
nextline << " (";
LABEL_A12_LOW(d1);
nextline << "),Y";
nextlinebytes << Base::HEX2 << (int)d1;
}
break;
}
case ZERO_PAGE_X:
{
d1 = Debugger::debugger().peek(myPC+myOffset); myPC++;
labfound = mark(d1, CartDebug::REFERENCED);
if (pass == 3)
{
nextline << " ";
LABEL_A12_LOW(d1);
nextline << ",X";
}
nextlinebytes << Base::HEX2 << (int)d1;
break;
}
case ZERO_PAGE_Y:
{
d1 = Debugger::debugger().peek(myPC+myOffset); myPC++;
labfound = mark(d1, CartDebug::REFERENCED);
if (pass == 3)
{
nextline << " ";
LABEL_A12_LOW(d1);
nextline << ",Y";
}
nextlinebytes << Base::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)
{
nextline << " ";
LABEL_A12_HIGH(ad);
}
else
nextline << " $" << Base::HEX4 << ad;
nextlinebytes << Base::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))
{
// Since we can't know what address is being accessed unless we also
// know the current X value, this is marked as ROW instead of DATA
// The processing is left here, however, in case future versions of
// the code can somehow track access to CPU registers
mark(ad, CartDebug::ROW);
}
else if (pass == 3)
{
if (ad < 0x100 && mySettings.fflag)
nextline << ".ind ";
else
nextline << " ";
}
if (labfound == 1)
{
nextline << "(";
LABEL_A12_HIGH(ad);
nextline << ")";
}
// TODO - should we consider case 4??
else
{
nextline << "(";
LABEL_A12_LOW(ad);
nextline << ")";
}
nextlinebytes << Base::HEX2 << (int)(ad&0xff) << " " << Base::HEX2 << (int)(ad>>8);
break;
}
default:
break;
} // end switch
if (pass == 1)
{
// RTS/JMP/RTI always indicate the end of a block of CODE
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;
}