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