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