M6502.cxx 19.7 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
//============================================================================
//
// MM     MM  6666  555555  0000   2222
// MMMM MMMM 66  66 55     00  00 22  22
// MM MMM MM 66     55     00  00     22
// MM  M  MM 66666  55555  00  00  22222  --  "A 6502 Microprocessor Emulator"
// MM     MM 66  66     55 00  00 22
// MM     MM 66  66 55  55 00  00 22
// MM     MM  6666   5555   0000  222222
//
11
// Copyright (c) 1995-2018 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
16
17
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================

stephena's avatar
stephena committed
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
26

  // Flags for disassembly types
  #define DISASM_CODE  CartDebug::CODE
27
28
//   #define DISASM_GFX   CartDebug::GFX
//   #define DISASM_PGFX  CartDebug::PGFX
29
  #define DISASM_DATA  CartDebug::DATA
30
//   #define DISASM_ROW   CartDebug::ROW
31
  #define DISASM_WRITE CartDebug::WRITE
32
33
34
35
  #define DISASM_NONE  0
#else
  // Flags for disassembly types
  #define DISASM_CODE  0
36
37
//   #define DISASM_GFX   0
//   #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
48
#include "TIA.hxx"
#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
61
    A(0), X(0), Y(0), SP(0), IR(0), PC(0),
    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
69
    myLastPeekBaseAddress(0),
    myLastPokeBaseAddress(0),
70
    myFlags(DISASM_NONE),
71
72
73
74
    myLastSrcAddressS(-1),
    myLastSrcAddressA(-1),
    myLastSrcAddressX(-1),
    myLastSrcAddressY(-1),
75
    myDataAddressForPoke(0),
76
    myOnHaltCallback(nullptr),
77
    myHaltRequested(false),
78
79
    myGhostReadsTrap(false),
    myReadFromWritePortBreak(false),
80
    myStepStateByInstruction(false)
81
{
stephena's avatar
stephena committed
82
#ifdef DEBUGGER_SUPPORT
83
  myDebugger = nullptr;
84
  myJustHitReadTrapFlag = myJustHitWriteTrapFlag = false;
85
#endif
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void M6502::install(System& system)
{
  // Remember which system I'm installed in
  mySystem = &system;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void M6502::reset()
{
  // Clear the execution status flags
  myExecutionStatus = 0;

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") ?
Christian Speckner's avatar
Christian Speckner committed
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;
thrust26's avatar
thrust26 committed
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
141
  if(address != myLastAddress)
  {
142
    ++myNumberOfDistinctAccesses;
143
144
    myLastAddress = address;
  }
145
  ////////////////////////////////////////////////
146
  mySystem->incrementCycles(SYSTEM_CYCLES_PER_CPU);
147
  icycles += SYSTEM_CYCLES_PER_CPU;
148
  myFlags = flags;
thrust26's avatar
thrust26 committed
149
150
  uInt8 result = mySystem->peek(address, flags);
  myLastPeekAddress = address;
151
152

#ifdef DEBUGGER_SUPPORT
thrust26's avatar
thrust26 committed
153
154
  if(myReadTraps.isInitialized() && myReadTraps.isSet(address)
     && (myGhostReadsTrap || flags != DISASM_NONE))
155
  {
thrust26's avatar
thrust26 committed
156
    myLastPeekBaseAddress = myDebugger->getBaseAddress(myLastPeekAddress, true); // mirror handling
thrust26's avatar
thrust26 committed
157
158
159
    int cond = evalCondTraps();
    if(cond > -1)
    {
160
      myJustHitReadTrapFlag = true;
161
      stringstream msg;
thrust26's avatar
thrust26 committed
162
163
      msg << "RTrap" << (flags == DISASM_NONE ? "G[" : "[") << Common::Base::HEX2 << cond << "]"
        << (myTrapCondNames[cond].empty() ? ": " : "If: {" + myTrapCondNames[cond] + "} ");
164
      myHitTrapInfo.message = msg.str();
thrust26's avatar
thrust26 committed
165
166
      myHitTrapInfo.address = address;
    }
thrust26's avatar
thrust26 committed
167
  }
168
#endif  // DEBUGGER_SUPPORT
169
170
171
172
173

  return result;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
174
inline void M6502::poke(uInt16 address, uInt8 value, uInt8 flags)
175
{
176
177
  ////////////////////////////////////////////////
  // TODO - move this logic directly into CartAR
178
179
  if(address != myLastAddress)
  {
180
    ++myNumberOfDistinctAccesses;
181
182
    myLastAddress = address;
  }
183
  ////////////////////////////////////////////////
184
  mySystem->incrementCycles(SYSTEM_CYCLES_PER_CPU);
185
  icycles += SYSTEM_CYCLES_PER_CPU;
186
  mySystem->poke(address, value, flags);
thrust26's avatar
thrust26 committed
187
  myLastPokeAddress = address;
188
189

#ifdef DEBUGGER_SUPPORT
190
  if(myWriteTraps.isInitialized() && myWriteTraps.isSet(address))
191
  {
thrust26's avatar
thrust26 committed
192
    myLastPokeBaseAddress = myDebugger->getBaseAddress(myLastPokeAddress, false); // mirror handling
thrust26's avatar
thrust26 committed
193
194
195
    int cond = evalCondTraps();
    if(cond > -1)
    {
196
      myJustHitWriteTrapFlag = true;
197
198
199
      stringstream msg;
      msg << "WTrap[" << Common::Base::HEX2 << cond << "]" << (myTrapCondNames[cond].empty() ? ": " : "If: {" + myTrapCondNames[cond] + "} ");
      myHitTrapInfo.message = msg.str();
thrust26's avatar
thrust26 committed
200
201
      myHitTrapInfo.address = address;
    }
thrust26's avatar
thrust26 committed
202
  }
203
#endif  // DEBUGGER_SUPPORT
204
205
}

206
207
208
209
210
211
212
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void M6502::requestHalt()
{
  if (!myOnHaltCallback) throw runtime_error("onHaltCallback not configured");
  myHaltRequested = true;
}

213
214
215
216
217
218
219
220
221
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
inline void M6502::handleHalt()
{
  if (myHaltRequested) {
    myOnHaltCallback();
    myHaltRequested = false;
  }
}

222
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
223
void M6502::execute(uInt64 number, DispatchResult& result)
224
{
225
  _execute(number, result);
226

227
#ifdef DEBUGGER_SUPPORT
228
229
230
231
232
  // Debugger hack: this ensures that stepping a "STA WSYNC" will actually end at the
  // beginning of the next line (otherwise, the next instruction would be stepped in order for
  // the halt to take effect). This is safe because as we know that the next cycle will be a read
  // cycle anyway.
  handleHalt();
233
#endif
234
235

  // Make sure that the hardware state matches the current system clock. This is necessary
236
237
  // to maintain a consistent state for the debugger after stepping and to make sure
  // that audio samples are generated for the whole timeslice.
238
239
  mySystem->tia().updateEmulation();
  mySystem->m6532().updateEmulation();
240
241
242
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
243
bool M6502::execute(uInt64 number)
244
245
246
{
  DispatchResult result;

Christian Speckner's avatar
Christian Speckner committed
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
258
259
260
261
#ifdef DEBUGGER_SUPPORT
  TIA& tia = mySystem->tia();
  M6532& riot = mySystem->m6532();
#endif

262
  uInt64 previousCycles = mySystem->cycles();
263
  uInt64 currentCycles = 0;
264

265
266
267
  // Loop until execution is stopped or a fatal error occurs
  for(;;)
  {
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
274
275
276
277
        if(myJustHitReadTrapFlag || myJustHitWriteTrapFlag)
        {
          bool read = myJustHitReadTrapFlag;
          myJustHitReadTrapFlag = myJustHitWriteTrapFlag = false;

278
          myLastBreakCycle = mySystem->cycles();
279
280
281
282
283
          result.setDebugger(currentCycles, myHitTrapInfo.message, myHitTrapInfo.address, read);
          return;
        }

        if(myBreakPoints.isInitialized() && myBreakPoints.isSet(PC)) {
284
          myLastBreakCycle = mySystem->cycles();
285
286
287
288
289
290
291
          result.setDebugger(currentCycles, "BP: ", PC);
          return;
        }

        int cond = evalCondBreaks();
        if(cond > -1)
        {
292
          ostringstream msg;
293
294
          msg << "CBP[" << Common::Base::HEX2 << cond << "]: " << myCondBreakNames[cond];

295
          myLastBreakCycle = mySystem->cycles();
296
297
298
          result.setDebugger(currentCycles, msg.str());
          return;
        }
299
      }
300

301
      int cond = evalCondSaveStates();
302
303
      if(cond > -1)
      {
304
        ostringstream msg;
305
306
        msg << "conditional savestate [" << Common::Base::HEX2 << cond << "]";
        myDebugger->addState(msg.str());
307
      }
308
309

      mySystem->cart().clearAllRAMAccesses();
310
  #endif  // DEBUGGER_SUPPORT
311

312
      uInt16 operandAddress = 0, intermediateAddress = 0;
313
314
315
      uInt8 operand = 0;

      // Reset the peek/poke address pointers
316
      myLastPeekAddress = myLastPokeAddress = myDataAddressForPoke = 0;
317

318
319
      try {
        icycles = 0;
320
321
322
    #ifdef DEBUGGER_SUPPORT
        uInt16 oldPC = PC;
    #endif
323

324
325
326
327
328
329
330
331
        // Fetch instruction at the program counter
        IR = peek(PC++, DISASM_CODE);  // This address represents a code section

        // Call code to execute the instruction
        switch(IR)
        {
          // 6502 instruction emulation is generated by an M4 macro file
          #include "M6502.ins"
332

333
334
335
          default:
            FatalEmulationError::raise("invalid instruction");
        }
336
337
338
339
340
341
342
343
344
345
346
347
348
349

    #ifdef DEBUGGER_SUPPORT
        if(myReadFromWritePortBreak)
        {
          uInt16 rwpAddr = mySystem->cart().getIllegalRAMAccess();
          if(rwpAddr)
          {
            ostringstream msg;
            msg << "RWP[@ $" << Common::Base::HEX4 << rwpAddr << "]: ";
            result.setDebugger(currentCycles, msg.str(), oldPC);
            return;
          }
        }
    #endif  // DEBUGGER_SUPPORT
350
      } catch (const FatalEmulationError& e) {
351
352
        myExecutionStatus |= FatalErrorBit;
        result.setMessage(e.what());
353
354
355
      } catch (const EmulationWarning& e) {
        result.setDebugger(currentCycles, e.what(), PC);
        return;
356
      }
357

358
359
      currentCycles = (mySystem->cycles() - previousCycles);

360
361
362
  #ifdef DEBUGGER_SUPPORT
      if(myStepStateByInstruction)
      {
363
364
365
        // Check out M6502::execute for an explanation.
        handleHalt();

366
367
368
        tia.updateEmulation();
        riot.updateEmulation();
      }
369
  #endif
370
371
372
    }

    // See if we need to handle an interrupt
373
    if((myExecutionStatus & MaskableInterruptBit) ||
374
375
376
377
378
379
        (myExecutionStatus & NonmaskableInterruptBit))
    {
      // Yes, so handle the interrupt
      interruptHandler();
    }

380
    // See if a fatal error has occurred
381
382
    if(myExecutionStatus & FatalErrorBit)
    {
383
384
385
      // Yes, so answer that something when wrong. The message has already been set when
      // the exception was handled.
      result.setFatal(currentCycles);
386
      return;
387
388
    }

389
390
    // See if execution has been stopped
    if(myExecutionStatus & StopExecutionBit)
391
392
    {
      // Yes, so answer that everything finished fine
393
394
      result.setOk(currentCycles);
      return;
395
    }
396
397
398
399
400

    if (currentCycles >= cycles * SYSTEM_CYCLES_PER_CPU) {
      result.setOk(currentCycles);
      return;
    }
401
402
403
404
405
406
407
408
409
  }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void M6502::interruptHandler()
{
  // Handle the interrupt
  if((myExecutionStatus & MaskableInterruptBit) && !I)
  {
410
    mySystem->incrementCycles(7 * SYSTEM_CYCLES_PER_CPU);
411
412
413
414
415
    mySystem->poke(0x0100 + SP--, (PC - 1) >> 8);
    mySystem->poke(0x0100 + SP--, (PC - 1) & 0x00ff);
    mySystem->poke(0x0100 + SP--, PS() & (~0x10));
    D = false;
    I = true;
416
    PC = uInt16(mySystem->peek(0xFFFE)) | (uInt16(mySystem->peek(0xFFFF)) << 8);
417
418
419
  }
  else if(myExecutionStatus & NonmaskableInterruptBit)
  {
420
    mySystem->incrementCycles(7 * SYSTEM_CYCLES_PER_CPU);
421
422
423
424
    mySystem->poke(0x0100 + SP--, (PC - 1) >> 8);
    mySystem->poke(0x0100 + SP--, (PC - 1) & 0x00ff);
    mySystem->poke(0x0100 + SP--, PS() & (~0x10));
    D = false;
425
    PC = uInt16(mySystem->peek(0xFFFA)) | (uInt16(mySystem->peek(0xFFFB)) << 8);
426
427
428
429
430
431
432
  }

  // Clear the interrupt bits in myExecutionStatus
  myExecutionStatus &= ~(MaskableInterruptBit | NonmaskableInterruptBit);
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
433
bool M6502::save(Serializer& out) const
434
435
436
{
  try
  {
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
    out.putByte(A);    // Accumulator
    out.putByte(X);    // X index register
    out.putByte(Y);    // Y index register
    out.putByte(SP);   // Stack Pointer
    out.putByte(IR);   // Instruction register
    out.putShort(PC);  // Program Counter

    out.putBool(N);    // N flag for processor status register
    out.putBool(V);    // V flag for processor status register
    out.putBool(B);    // B flag for processor status register
    out.putBool(D);    // D flag for processor status register
    out.putBool(I);    // I flag for processor status register
    out.putBool(notZ); // Z flag complement for processor status register
    out.putBool(C);    // C flag for processor status register

    out.putByte(myExecutionStatus);
453
454
455

    // Indicates the number of distinct memory accesses
    out.putInt(myNumberOfDistinctAccesses);
456
    // Indicates the last address(es) which was accessed
457
458
459
460
    out.putShort(myLastAddress);
    out.putShort(myLastPeekAddress);
    out.putShort(myLastPokeAddress);
    out.putShort(myDataAddressForPoke);
461
462
463
464
    out.putInt(myLastSrcAddressS);
    out.putInt(myLastSrcAddressA);
    out.putInt(myLastSrcAddressX);
    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
474
475
476
477
478
479
    return false;
  }

  return true;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
480
bool M6502::load(Serializer& in)
481
482
483
{
  try
  {
484
485
486
487
488
489
    A = in.getByte();    // Accumulator
    X = in.getByte();    // X index register
    Y = in.getByte();    // Y index register
    SP = in.getByte();   // Stack Pointer
    IR = in.getByte();   // Instruction register
    PC = in.getShort();  // Program Counter
490

491
492
493
494
495
496
497
    N = in.getBool();    // N flag for processor status register
    V = in.getBool();    // V flag for processor status register
    B = in.getBool();    // B flag for processor status register
    D = in.getBool();    // D flag for processor status register
    I = in.getBool();    // I flag for processor status register
    notZ = in.getBool(); // Z flag complement for processor status register
    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
505
506
507
    myLastAddress = in.getShort();
    myLastPeekAddress = in.getShort();
    myLastPokeAddress = in.getShort();
    myDataAddressForPoke = in.getShort();
508
509
510
511
    myLastSrcAddressS = in.getInt();
    myLastSrcAddressA = in.getInt();
    myLastSrcAddressX = in.getInt();
    myLastSrcAddressY = in.getInt();
512
    myFlags = in.getByte();
513
514

    myHaltRequested = in.getBool();
515
    myLastBreakCycle = in.getLong();
516
517
518
519

  #ifdef DEBUGGER_SUPPORT
    updateStepStateByInstruction();
  #endif
520
  }
521
  catch(...)
522
  {
523
    cerr << "ERROR: M6502::load" << endl;
524
525
526
527
528
529
    return false;
  }

  return true;
}

530
531
532
533
534
535
536
537
538
#ifdef DEBUGGER_SUPPORT
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void M6502::attach(Debugger& debugger)
{
  // Remember the debugger for this microprocessor
  myDebugger = &debugger;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
539
uInt32 M6502::addCondBreak(Expression* e, const string& name)
540
{
541
542
  myCondBreaks.emplace_back(e);
  myCondBreakNames.push_back(name);
543
544
545

  updateStepStateByInstruction();

546
  return uInt32(myCondBreaks.size() - 1);
547
548
549
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
550
bool M6502::delCondBreak(uInt32 idx)
551
{
552
  if(idx < myCondBreaks.size())
553
  {
554
555
    Vec::removeAt(myCondBreaks, idx);
    Vec::removeAt(myCondBreakNames, idx);
556
557
558

    updateStepStateByInstruction();

thrust26's avatar
thrust26 committed
559
    return true;
560
  }
thrust26's avatar
thrust26 committed
561
  return false;
562
563
564
565
566
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void M6502::clearCondBreaks()
{
567
568
  myCondBreaks.clear();
  myCondBreakNames.clear();
569
570

  updateStepStateByInstruction();
571
572
573
574
575
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const StringList& M6502::getCondBreakNames() const
{
576
577
578
579
580
581
582
583
  return myCondBreakNames;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 M6502::addCondSaveState(Expression* e, const string& name)
{
  myCondSaveStates.emplace_back(e);
  myCondSaveStateNames.push_back(name);
584
585
586

  updateStepStateByInstruction();

587
588
589
590
591
592
593
594
595
596
  return uInt32(myCondSaveStates.size() - 1);
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool M6502::delCondSaveState(uInt32 idx)
{
  if(idx < myCondSaveStates.size())
  {
    Vec::removeAt(myCondSaveStates, idx);
    Vec::removeAt(myCondSaveStateNames, idx);
597
598
599

    updateStepStateByInstruction();

600
601
602
603
604
605
606
607
608
609
    return true;
  }
  return false;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void M6502::clearCondSaveStates()
{
  myCondSaveStates.clear();
  myCondSaveStateNames.clear();
610
611

  updateStepStateByInstruction();
612
613
614
615
616
617
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const StringList& M6502::getCondSaveStateNames() const
{
  return myCondSaveStateNames;
618
}
thrust26's avatar
thrust26 committed
619

thrust26's avatar
thrust26 committed
620
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
thrust26's avatar
thrust26 committed
621
622
uInt32 M6502::addCondTrap(Expression* e, const string& name)
{
thrust26's avatar
thrust26 committed
623
  myTrapConds.emplace_back(e);
thrust26's avatar
thrust26 committed
624
  myTrapCondNames.push_back(name);
625
626
627

  updateStepStateByInstruction();

thrust26's avatar
thrust26 committed
628
629
630
631
  return uInt32(myTrapConds.size() - 1);
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
thrust26's avatar
thrust26 committed
632
bool M6502::delCondTrap(uInt32 brk)
thrust26's avatar
thrust26 committed
633
634
635
{
  if(brk < myTrapConds.size())
  {
thrust26's avatar
thrust26 committed
636
    Vec::removeAt(myTrapConds, brk);
thrust26's avatar
thrust26 committed
637
    Vec::removeAt(myTrapCondNames, brk);
638
639
640

    updateStepStateByInstruction();

thrust26's avatar
thrust26 committed
641
    return true;
thrust26's avatar
thrust26 committed
642
  }
thrust26's avatar
thrust26 committed
643
  return false;
644
}
thrust26's avatar
thrust26 committed
645
646
647
648
649
650

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void M6502::clearCondTraps()
{
  myTrapConds.clear();
  myTrapCondNames.clear();
651
652

  updateStepStateByInstruction();
thrust26's avatar
thrust26 committed
653
654
655
656
657
658
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const StringList& M6502::getCondTrapNames() const
{
  return myTrapCondNames;
thrust26's avatar
thrust26 committed
659
}
660
661
662
663
664
665
666

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void M6502::updateStepStateByInstruction()
{
  myStepStateByInstruction = myCondBreaks.size() || myCondSaveStates.size() ||
                             myTrapConds.size();
}
667
#endif  // DEBUGGER_SUPPORT