void DiStella::disasm()

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;
}