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