"Fossies" - the Fresh Open Source Software Archive

Member "stella-6.0.2/src/emucore/M6502.cxx" (11 Oct 2019, 20143 Bytes) of package /linux/privat/stella-6.0.2-src.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "M6502.cxx" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 6.0.1_vs_6.0.2.

    1 //============================================================================
    2 //
    3 // MM     MM  6666  555555  0000   2222
    4 // MMMM MMMM 66  66 55     00  00 22  22
    5 // MM MMM MM 66     55     00  00     22
    6 // MM  M  MM 66666  55555  00  00  22222  --  "A 6502 Microprocessor Emulator"
    7 // MM     MM 66  66     55 00  00 22
    8 // MM     MM 66  66 55  55 00  00 22
    9 // MM     MM  6666   5555   0000  222222
   10 //
   11 // Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
   12 // and the Stella Team
   13 //
   14 // See the file "License.txt" for information on usage and redistribution of
   15 // this file, and for a DISCLAIMER OF ALL WARRANTIES.
   16 //============================================================================
   17 
   18 #ifdef DEBUGGER_SUPPORT
   19   #include "Debugger.hxx"
   20   #include "Expression.hxx"
   21   #include "CartDebug.hxx"
   22   #include "PackedBitArray.hxx"
   23   #include "Base.hxx"
   24 
   25   // Flags for disassembly types
   26   #define DISASM_CODE  CartDebug::CODE
   27 //   #define DISASM_GFX   CartDebug::GFX
   28 //   #define DISASM_PGFX  CartDebug::PGFX
   29   #define DISASM_DATA  CartDebug::DATA
   30 //   #define DISASM_ROW   CartDebug::ROW
   31   #define DISASM_WRITE CartDebug::WRITE
   32   #define DISASM_NONE  0
   33 #else
   34   // Flags for disassembly types
   35   #define DISASM_CODE  0
   36 //   #define DISASM_GFX   0
   37 //   #define DISASM_PGFX  0
   38   #define DISASM_DATA  0
   39 //   #define DISASM_ROW   0
   40   #define DISASM_NONE  0
   41   #define DISASM_WRITE 0
   42 #endif
   43 #include "Settings.hxx"
   44 #include "Vec.hxx"
   45 
   46 #include "Cart.hxx"
   47 #include "TIA.hxx"
   48 #include "M6532.hxx"
   49 #include "System.hxx"
   50 #include "M6502.hxx"
   51 #include "DispatchResult.hxx"
   52 #include "exception/EmulationWarning.hxx"
   53 #include "exception/FatalEmulationError.hxx"
   54 
   55 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   56 M6502::M6502(const Settings& settings)
   57   : myExecutionStatus(0),
   58     mySystem(nullptr),
   59     mySettings(settings),
   60     A(0), X(0), Y(0), SP(0), IR(0), PC(0),
   61     N(false), V(false), B(false), D(false), I(false), notZ(false), C(false),
   62     icycles(0),
   63     myNumberOfDistinctAccesses(0),
   64     myLastAddress(0),
   65     myLastBreakCycle(ULLONG_MAX),
   66     myLastPeekAddress(0),
   67     myLastPokeAddress(0),
   68     myLastPeekBaseAddress(0),
   69     myLastPokeBaseAddress(0),
   70     myFlags(DISASM_NONE),
   71     myLastSrcAddressS(-1),
   72     myLastSrcAddressA(-1),
   73     myLastSrcAddressX(-1),
   74     myLastSrcAddressY(-1),
   75     myDataAddressForPoke(0),
   76     myOnHaltCallback(nullptr),
   77     myHaltRequested(false),
   78     myGhostReadsTrap(false),
   79     myReadFromWritePortBreak(false),
   80     myStepStateByInstruction(false)
   81 {
   82 #ifdef DEBUGGER_SUPPORT
   83   myDebugger = nullptr;
   84   myJustHitReadTrapFlag = myJustHitWriteTrapFlag = false;
   85 #endif
   86 }
   87 
   88 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   89 void M6502::install(System& system)
   90 {
   91   // Remember which system I'm installed in
   92   mySystem = &system;
   93 }
   94 
   95 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   96 void M6502::reset()
   97 {
   98   // Clear the execution status flags
   99   myExecutionStatus = 0;
  100 
  101   // Set registers to random or default values
  102   bool devSettings = mySettings.getBool("dev.settings");
  103   const string& cpurandom = mySettings.getString(devSettings ? "dev.cpurandom" : "plr.cpurandom");
  104   SP = BSPF::containsIgnoreCase(cpurandom, "S") ?
  105           mySystem->randGenerator().next() : 0xfd;
  106   A  = BSPF::containsIgnoreCase(cpurandom, "A") ?
  107           mySystem->randGenerator().next() : 0x00;
  108   X  = BSPF::containsIgnoreCase(cpurandom, "X") ?
  109           mySystem->randGenerator().next() : 0x00;
  110   Y  = BSPF::containsIgnoreCase(cpurandom, "Y") ?
  111           mySystem->randGenerator().next() : 0x00;
  112   PS(BSPF::containsIgnoreCase(cpurandom, "P") ?
  113           mySystem->randGenerator().next() : 0x20);
  114 
  115   icycles = 0;
  116 
  117   // Load PC from the reset vector
  118   PC = uInt16(mySystem->peek(0xfffc)) | (uInt16(mySystem->peek(0xfffd)) << 8);
  119 
  120   myLastAddress = myLastPeekAddress = myLastPokeAddress = myLastPeekBaseAddress = myLastPokeBaseAddress;
  121   myLastSrcAddressS = myLastSrcAddressA =
  122     myLastSrcAddressX = myLastSrcAddressY = -1;
  123   myDataAddressForPoke = 0;
  124   myFlags = DISASM_NONE;
  125 
  126   myHaltRequested = false;
  127   myGhostReadsTrap = mySettings.getBool("dbg.ghostreadstrap");
  128   myReadFromWritePortBreak = mySettings.getBool(devSettings ? "dev.rwportbreak" : "plr.rwportbreak");
  129 
  130   myLastBreakCycle = ULLONG_MAX;
  131 }
  132 
  133 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  134 inline uInt8 M6502::peek(uInt16 address, uInt8 flags)
  135 {
  136   handleHalt();
  137 
  138   ////////////////////////////////////////////////
  139   // TODO - move this logic directly into CartAR
  140   if(address != myLastAddress)
  141   {
  142     ++myNumberOfDistinctAccesses;
  143     myLastAddress = address;
  144   }
  145   ////////////////////////////////////////////////
  146   mySystem->incrementCycles(SYSTEM_CYCLES_PER_CPU);
  147   icycles += SYSTEM_CYCLES_PER_CPU;
  148   myFlags = flags;
  149   uInt8 result = mySystem->peek(address, flags);
  150   myLastPeekAddress = address;
  151 
  152 #ifdef DEBUGGER_SUPPORT
  153   if(myReadTraps.isInitialized() && myReadTraps.isSet(address)
  154      && (myGhostReadsTrap || flags != DISASM_NONE))
  155   {
  156     myLastPeekBaseAddress = myDebugger->getBaseAddress(myLastPeekAddress, true); // mirror handling
  157     int cond = evalCondTraps();
  158     if(cond > -1)
  159     {
  160       myJustHitReadTrapFlag = true;
  161       stringstream msg;
  162       msg << "RTrap" << (flags == DISASM_NONE ? "G[" : "[") << Common::Base::HEX2 << cond << "]"
  163         << (myTrapCondNames[cond].empty() ? ": " : "If: {" + myTrapCondNames[cond] + "} ");
  164       myHitTrapInfo.message = msg.str();
  165       myHitTrapInfo.address = address;
  166     }
  167   }
  168 #endif  // DEBUGGER_SUPPORT
  169 
  170   return result;
  171 }
  172 
  173 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  174 inline void M6502::poke(uInt16 address, uInt8 value, uInt8 flags)
  175 {
  176   ////////////////////////////////////////////////
  177   // TODO - move this logic directly into CartAR
  178   if(address != myLastAddress)
  179   {
  180     ++myNumberOfDistinctAccesses;
  181     myLastAddress = address;
  182   }
  183   ////////////////////////////////////////////////
  184   mySystem->incrementCycles(SYSTEM_CYCLES_PER_CPU);
  185   icycles += SYSTEM_CYCLES_PER_CPU;
  186   mySystem->poke(address, value, flags);
  187   myLastPokeAddress = address;
  188 
  189 #ifdef DEBUGGER_SUPPORT
  190   if(myWriteTraps.isInitialized() && myWriteTraps.isSet(address))
  191   {
  192     myLastPokeBaseAddress = myDebugger->getBaseAddress(myLastPokeAddress, false); // mirror handling
  193     int cond = evalCondTraps();
  194     if(cond > -1)
  195     {
  196       myJustHitWriteTrapFlag = true;
  197       stringstream msg;
  198       msg << "WTrap[" << Common::Base::HEX2 << cond << "]" << (myTrapCondNames[cond].empty() ? ": " : "If: {" + myTrapCondNames[cond] + "} ");
  199       myHitTrapInfo.message = msg.str();
  200       myHitTrapInfo.address = address;
  201     }
  202   }
  203 #endif  // DEBUGGER_SUPPORT
  204 }
  205 
  206 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  207 void M6502::requestHalt()
  208 {
  209   if (!myOnHaltCallback) throw runtime_error("onHaltCallback not configured");
  210   myHaltRequested = true;
  211 }
  212 
  213 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  214 inline void M6502::handleHalt()
  215 {
  216   if (myHaltRequested) {
  217     myOnHaltCallback();
  218     myHaltRequested = false;
  219   }
  220 }
  221 
  222 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  223 void M6502::execute(uInt64 number, DispatchResult& result)
  224 {
  225   _execute(number, result);
  226 
  227 #ifdef DEBUGGER_SUPPORT
  228   // Debugger hack: this ensures that stepping a "STA WSYNC" will actually end at the
  229   // beginning of the next line (otherwise, the next instruction would be stepped in order for
  230   // the halt to take effect). This is safe because as we know that the next cycle will be a read
  231   // cycle anyway.
  232   handleHalt();
  233 #endif
  234 
  235   // Make sure that the hardware state matches the current system clock. This is necessary
  236   // to maintain a consistent state for the debugger after stepping and to make sure
  237   // that audio samples are generated for the whole timeslice.
  238   mySystem->tia().updateEmulation();
  239   mySystem->m6532().updateEmulation();
  240 }
  241 
  242 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  243 bool M6502::execute(uInt64 number)
  244 {
  245   DispatchResult result;
  246 
  247   execute(number, result);
  248 
  249   return result.isSuccess();
  250 }
  251 
  252 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  253 inline void M6502::_execute(uInt64 cycles, DispatchResult& result)
  254 {
  255   myExecutionStatus = 0;
  256 
  257 #ifdef DEBUGGER_SUPPORT
  258   TIA& tia = mySystem->tia();
  259   M6532& riot = mySystem->m6532();
  260 #endif
  261 
  262   uInt64 previousCycles = mySystem->cycles();
  263   uInt64 currentCycles = 0;
  264 
  265   // Loop until execution is stopped or a fatal error occurs
  266   for(;;)
  267   {
  268     while (!myExecutionStatus && currentCycles < cycles * SYSTEM_CYCLES_PER_CPU)
  269     {
  270   #ifdef DEBUGGER_SUPPORT
  271       // Don't break if we haven't actually executed anything yet
  272       if (myLastBreakCycle != mySystem->cycles()) {
  273         if(myJustHitReadTrapFlag || myJustHitWriteTrapFlag)
  274         {
  275           bool read = myJustHitReadTrapFlag;
  276           myJustHitReadTrapFlag = myJustHitWriteTrapFlag = false;
  277 
  278           myLastBreakCycle = mySystem->cycles();
  279           result.setDebugger(currentCycles, myHitTrapInfo.message, myHitTrapInfo.address, read);
  280           return;
  281         }
  282 
  283         if(myBreakPoints.isInitialized() && myBreakPoints.isSet(PC)) {
  284           myLastBreakCycle = mySystem->cycles();
  285           result.setDebugger(currentCycles, "BP: ", PC);
  286           return;
  287         }
  288 
  289         int cond = evalCondBreaks();
  290         if(cond > -1)
  291         {
  292           ostringstream msg;
  293           msg << "CBP[" << Common::Base::HEX2 << cond << "]: " << myCondBreakNames[cond];
  294 
  295           myLastBreakCycle = mySystem->cycles();
  296           result.setDebugger(currentCycles, msg.str());
  297           return;
  298         }
  299       }
  300 
  301       int cond = evalCondSaveStates();
  302       if(cond > -1)
  303       {
  304         ostringstream msg;
  305         msg << "conditional savestate [" << Common::Base::HEX2 << cond << "]";
  306         myDebugger->addState(msg.str());
  307       }
  308 
  309       mySystem->cart().clearAllRAMAccesses();
  310   #endif  // DEBUGGER_SUPPORT
  311 
  312       uInt16 operandAddress = 0, intermediateAddress = 0;
  313       uInt8 operand = 0;
  314 
  315       // Reset the peek/poke address pointers
  316       myLastPeekAddress = myLastPokeAddress = myDataAddressForPoke = 0;
  317 
  318       try {
  319         icycles = 0;
  320     #ifdef DEBUGGER_SUPPORT
  321         uInt16 oldPC = PC;
  322     #endif
  323 
  324         // Fetch instruction at the program counter
  325         IR = peek(PC++, DISASM_CODE);  // This address represents a code section
  326 
  327         // Call code to execute the instruction
  328         switch(IR)
  329         {
  330           // 6502 instruction emulation is generated by an M4 macro file
  331           #include "M6502.ins"
  332 
  333           default:
  334             FatalEmulationError::raise("invalid instruction");
  335         }
  336 
  337     #ifdef DEBUGGER_SUPPORT
  338         if(myReadFromWritePortBreak)
  339         {
  340           uInt16 rwpAddr = mySystem->cart().getIllegalRAMAccess();
  341           if(rwpAddr)
  342           {
  343             ostringstream msg;
  344             msg << "RWP[@ $" << Common::Base::HEX4 << rwpAddr << "]: ";
  345             result.setDebugger(currentCycles, msg.str(), oldPC);
  346             return;
  347           }
  348         }
  349     #endif  // DEBUGGER_SUPPORT
  350       } catch (const FatalEmulationError& e) {
  351         myExecutionStatus |= FatalErrorBit;
  352         result.setMessage(e.what());
  353       } catch (const EmulationWarning& e) {
  354         result.setDebugger(currentCycles, e.what(), PC);
  355         return;
  356       }
  357 
  358       currentCycles = (mySystem->cycles() - previousCycles);
  359 
  360   #ifdef DEBUGGER_SUPPORT
  361       if(myStepStateByInstruction)
  362       {
  363         // Check out M6502::execute for an explanation.
  364         handleHalt();
  365 
  366         tia.updateEmulation();
  367         riot.updateEmulation();
  368       }
  369   #endif
  370     }
  371 
  372     // See if we need to handle an interrupt
  373     if((myExecutionStatus & MaskableInterruptBit) ||
  374         (myExecutionStatus & NonmaskableInterruptBit))
  375     {
  376       // Yes, so handle the interrupt
  377       interruptHandler();
  378     }
  379 
  380     // See if a fatal error has occurred
  381     if(myExecutionStatus & FatalErrorBit)
  382     {
  383       // Yes, so answer that something when wrong. The message has already been set when
  384       // the exception was handled.
  385       result.setFatal(currentCycles);
  386       return;
  387     }
  388 
  389     // See if execution has been stopped
  390     if(myExecutionStatus & StopExecutionBit)
  391     {
  392       // Yes, so answer that everything finished fine
  393       result.setOk(currentCycles);
  394       return;
  395     }
  396 
  397     if (currentCycles >= cycles * SYSTEM_CYCLES_PER_CPU) {
  398       result.setOk(currentCycles);
  399       return;
  400     }
  401   }
  402 }
  403 
  404 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  405 void M6502::interruptHandler()
  406 {
  407   // Handle the interrupt
  408   if((myExecutionStatus & MaskableInterruptBit) && !I)
  409   {
  410     mySystem->incrementCycles(7 * SYSTEM_CYCLES_PER_CPU);
  411     mySystem->poke(0x0100 + SP--, (PC - 1) >> 8);
  412     mySystem->poke(0x0100 + SP--, (PC - 1) & 0x00ff);
  413     mySystem->poke(0x0100 + SP--, PS() & (~0x10));
  414     D = false;
  415     I = true;
  416     PC = uInt16(mySystem->peek(0xFFFE)) | (uInt16(mySystem->peek(0xFFFF)) << 8);
  417   }
  418   else if(myExecutionStatus & NonmaskableInterruptBit)
  419   {
  420     mySystem->incrementCycles(7 * SYSTEM_CYCLES_PER_CPU);
  421     mySystem->poke(0x0100 + SP--, (PC - 1) >> 8);
  422     mySystem->poke(0x0100 + SP--, (PC - 1) & 0x00ff);
  423     mySystem->poke(0x0100 + SP--, PS() & (~0x10));
  424     D = false;
  425     PC = uInt16(mySystem->peek(0xFFFA)) | (uInt16(mySystem->peek(0xFFFB)) << 8);
  426   }
  427 
  428   // Clear the interrupt bits in myExecutionStatus
  429   myExecutionStatus &= ~(MaskableInterruptBit | NonmaskableInterruptBit);
  430 }
  431 
  432 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  433 bool M6502::save(Serializer& out) const
  434 {
  435   try
  436   {
  437     out.putByte(A);    // Accumulator
  438     out.putByte(X);    // X index register
  439     out.putByte(Y);    // Y index register
  440     out.putByte(SP);   // Stack Pointer
  441     out.putByte(IR);   // Instruction register
  442     out.putShort(PC);  // Program Counter
  443 
  444     out.putBool(N);    // N flag for processor status register
  445     out.putBool(V);    // V flag for processor status register
  446     out.putBool(B);    // B flag for processor status register
  447     out.putBool(D);    // D flag for processor status register
  448     out.putBool(I);    // I flag for processor status register
  449     out.putBool(notZ); // Z flag complement for processor status register
  450     out.putBool(C);    // C flag for processor status register
  451 
  452     out.putByte(myExecutionStatus);
  453 
  454     // Indicates the number of distinct memory accesses
  455     out.putInt(myNumberOfDistinctAccesses);
  456     // Indicates the last address(es) which was accessed
  457     out.putShort(myLastAddress);
  458     out.putShort(myLastPeekAddress);
  459     out.putShort(myLastPokeAddress);
  460     out.putShort(myDataAddressForPoke);
  461     out.putInt(myLastSrcAddressS);
  462     out.putInt(myLastSrcAddressA);
  463     out.putInt(myLastSrcAddressX);
  464     out.putInt(myLastSrcAddressY);
  465     out.putByte(myFlags);
  466 
  467     out.putBool(myHaltRequested);
  468     out.putLong(myLastBreakCycle);
  469   }
  470   catch(...)
  471   {
  472     cerr << "ERROR: M6502::save" << endl;
  473     return false;
  474   }
  475 
  476   return true;
  477 }
  478 
  479 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  480 bool M6502::load(Serializer& in)
  481 {
  482   try
  483   {
  484     A = in.getByte();    // Accumulator
  485     X = in.getByte();    // X index register
  486     Y = in.getByte();    // Y index register
  487     SP = in.getByte();   // Stack Pointer
  488     IR = in.getByte();   // Instruction register
  489     PC = in.getShort();  // Program Counter
  490 
  491     N = in.getBool();    // N flag for processor status register
  492     V = in.getBool();    // V flag for processor status register
  493     B = in.getBool();    // B flag for processor status register
  494     D = in.getBool();    // D flag for processor status register
  495     I = in.getBool();    // I flag for processor status register
  496     notZ = in.getBool(); // Z flag complement for processor status register
  497     C = in.getBool();    // C flag for processor status register
  498 
  499     myExecutionStatus = in.getByte();
  500 
  501     // Indicates the number of distinct memory accesses
  502     myNumberOfDistinctAccesses = in.getInt();
  503     // Indicates the last address(es) which was accessed
  504     myLastAddress = in.getShort();
  505     myLastPeekAddress = in.getShort();
  506     myLastPokeAddress = in.getShort();
  507     myDataAddressForPoke = in.getShort();
  508     myLastSrcAddressS = in.getInt();
  509     myLastSrcAddressA = in.getInt();
  510     myLastSrcAddressX = in.getInt();
  511     myLastSrcAddressY = in.getInt();
  512     myFlags = in.getByte();
  513 
  514     myHaltRequested = in.getBool();
  515     myLastBreakCycle = in.getLong();
  516 
  517   #ifdef DEBUGGER_SUPPORT
  518     updateStepStateByInstruction();
  519   #endif
  520   }
  521   catch(...)
  522   {
  523     cerr << "ERROR: M6502::load" << endl;
  524     return false;
  525   }
  526 
  527   return true;
  528 }
  529 
  530 #ifdef DEBUGGER_SUPPORT
  531 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  532 void M6502::attach(Debugger& debugger)
  533 {
  534   // Remember the debugger for this microprocessor
  535   myDebugger = &debugger;
  536 }
  537 
  538 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  539 uInt32 M6502::addCondBreak(Expression* e, const string& name)
  540 {
  541   myCondBreaks.emplace_back(e);
  542   myCondBreakNames.push_back(name);
  543 
  544   updateStepStateByInstruction();
  545 
  546   return uInt32(myCondBreaks.size() - 1);
  547 }
  548 
  549 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  550 bool M6502::delCondBreak(uInt32 idx)
  551 {
  552   if(idx < myCondBreaks.size())
  553   {
  554     Vec::removeAt(myCondBreaks, idx);
  555     Vec::removeAt(myCondBreakNames, idx);
  556 
  557     updateStepStateByInstruction();
  558 
  559     return true;
  560   }
  561   return false;
  562 }
  563 
  564 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  565 void M6502::clearCondBreaks()
  566 {
  567   myCondBreaks.clear();
  568   myCondBreakNames.clear();
  569 
  570   updateStepStateByInstruction();
  571 }
  572 
  573 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  574 const StringList& M6502::getCondBreakNames() const
  575 {
  576   return myCondBreakNames;
  577 }
  578 
  579 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  580 uInt32 M6502::addCondSaveState(Expression* e, const string& name)
  581 {
  582   myCondSaveStates.emplace_back(e);
  583   myCondSaveStateNames.push_back(name);
  584 
  585   updateStepStateByInstruction();
  586 
  587   return uInt32(myCondSaveStates.size() - 1);
  588 }
  589 
  590 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  591 bool M6502::delCondSaveState(uInt32 idx)
  592 {
  593   if(idx < myCondSaveStates.size())
  594   {
  595     Vec::removeAt(myCondSaveStates, idx);
  596     Vec::removeAt(myCondSaveStateNames, idx);
  597 
  598     updateStepStateByInstruction();
  599 
  600     return true;
  601   }
  602   return false;
  603 }
  604 
  605 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  606 void M6502::clearCondSaveStates()
  607 {
  608   myCondSaveStates.clear();
  609   myCondSaveStateNames.clear();
  610 
  611   updateStepStateByInstruction();
  612 }
  613 
  614 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  615 const StringList& M6502::getCondSaveStateNames() const
  616 {
  617   return myCondSaveStateNames;
  618 }
  619 
  620 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  621 uInt32 M6502::addCondTrap(Expression* e, const string& name)
  622 {
  623   myTrapConds.emplace_back(e);
  624   myTrapCondNames.push_back(name);
  625 
  626   updateStepStateByInstruction();
  627 
  628   return uInt32(myTrapConds.size() - 1);
  629 }
  630 
  631 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  632 bool M6502::delCondTrap(uInt32 brk)
  633 {
  634   if(brk < myTrapConds.size())
  635   {
  636     Vec::removeAt(myTrapConds, brk);
  637     Vec::removeAt(myTrapCondNames, brk);
  638 
  639     updateStepStateByInstruction();
  640 
  641     return true;
  642   }
  643   return false;
  644 }
  645 
  646 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  647 void M6502::clearCondTraps()
  648 {
  649   myTrapConds.clear();
  650   myTrapCondNames.clear();
  651 
  652   updateStepStateByInstruction();
  653 }
  654 
  655 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  656 const StringList& M6502::getCondTrapNames() const
  657 {
  658   return myTrapCondNames;
  659 }
  660 
  661 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  662 void M6502::updateStepStateByInstruction()
  663 {
  664   myStepStateByInstruction = myCondBreaks.size() || myCondSaveStates.size() ||
  665                              myTrapConds.size();
  666 }
  667 #endif  // DEBUGGER_SUPPORT