| 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 | |