static void _processVReadCommand(struct GDBStub* stub, const char* message) { stub->outgoing[0] = '\0'; if (!strncmp("Attach", message, 6)) { strncpy(stub->outgoing, "1", GDB_STUB_MAX_LINE - 4); mDebuggerEnter(&stub->d, DEBUGGER_ENTER_MANUAL, 0); } _sendMessage(stub); }
static void LR35902DebuggerCheckBreakpoints(struct mDebuggerPlatform* d) { struct LR35902Debugger* debugger = (struct LR35902Debugger*) d; struct LR35902DebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->breakpoints, debugger->cpu->pc - 1); if (!breakpoint) { return; } // TODO: Segments struct mDebuggerEntryInfo info = { .address = breakpoint->address }; mDebuggerEnter(d->p, DEBUGGER_ENTER_BREAKPOINT, &info); } static void LR35902DebuggerInit(void* cpu, struct mDebuggerPlatform* platform); static void LR35902DebuggerDeinit(struct mDebuggerPlatform* platform); static void LR35902DebuggerEnter(struct mDebuggerPlatform* d, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info); static void LR35902DebuggerSetBreakpoint(struct mDebuggerPlatform*, uint32_t address); static void LR35902DebuggerClearBreakpoint(struct mDebuggerPlatform*, uint32_t address); static void LR35902DebuggerCheckBreakpoints(struct mDebuggerPlatform*); static bool LR35902DebuggerHasBreakpoints(struct mDebuggerPlatform*); struct mDebuggerPlatform* LR35902DebuggerPlatformCreate(void) { struct mDebuggerPlatform* platform = (struct mDebuggerPlatform*) malloc(sizeof(struct LR35902Debugger)); platform->entered = LR35902DebuggerEnter; platform->init = LR35902DebuggerInit; platform->deinit = LR35902DebuggerDeinit; platform->setBreakpoint = LR35902DebuggerSetBreakpoint; platform->clearBreakpoint = LR35902DebuggerClearBreakpoint; platform->setWatchpoint = NULL; platform->clearWatchpoint = NULL; platform->checkBreakpoints = LR35902DebuggerCheckBreakpoints; platform->hasBreakpoints = LR35902DebuggerHasBreakpoints; return platform; } void LR35902DebuggerInit(void* cpu, struct mDebuggerPlatform* platform) { struct LR35902Debugger* debugger = (struct LR35902Debugger*) platform; debugger->cpu = cpu; LR35902DebugBreakpointListInit(&debugger->breakpoints, 0); LR35902DebugWatchpointListInit(&debugger->watchpoints, 0); } void LR35902DebuggerDeinit(struct mDebuggerPlatform* platform) { struct LR35902Debugger* debugger = (struct LR35902Debugger*) platform; LR35902DebugBreakpointListDeinit(&debugger->breakpoints); LR35902DebugWatchpointListDeinit(&debugger->watchpoints); } static void LR35902DebuggerEnter(struct mDebuggerPlatform* platform, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) { UNUSED(reason); UNUSED(info); struct LR35902Debugger* debugger = (struct LR35902Debugger*) platform; struct LR35902Core* cpu = debugger->cpu; cpu->nextEvent = cpu->cycles; }
void GDBStubUpdate(struct GDBStub* stub) { if (stub->socket == INVALID_SOCKET) { if (stub->d.state == DEBUGGER_PAUSED) { stub->d.state = DEBUGGER_RUNNING; } return; } if (stub->connection == INVALID_SOCKET) { if (stub->shouldBlock) { Socket reads = stub->socket; SocketPoll(1, &reads, 0, 0, SOCKET_TIMEOUT); } stub->connection = SocketAccept(stub->socket, 0); if (!SOCKET_FAILED(stub->connection)) { if (!SocketSetBlocking(stub->connection, false)) { goto connectionLost; } mDebuggerEnter(&stub->d, DEBUGGER_ENTER_ATTACHED, 0); } else if (SocketWouldBlock()) { return; } else { goto connectionLost; } } while (true) { if (stub->shouldBlock) { Socket reads = stub->connection; SocketPoll(1, &reads, 0, 0, SOCKET_TIMEOUT); } ssize_t messageLen = SocketRecv(stub->connection, stub->line, GDB_STUB_MAX_LINE - 1); if (messageLen == 0) { goto connectionLost; } if (messageLen == -1) { if (SocketWouldBlock()) { return; } goto connectionLost; } stub->line[messageLen] = '\0'; mLOG(DEBUGGER, DEBUG, "< %s", stub->line); ssize_t position = 0; while (position < messageLen) { position += _parseGDBMessage(stub, &stub->line[position]); } } connectionLost: mLOG(DEBUGGER, WARN, "Connection lost"); GDBStubHangup(stub); }
int main(int argc, char** argv) { struct mSDLRenderer renderer = {}; struct mCoreOptions opts = { .useBios = true, .rewindEnable = true, .audioBuffers = 512, .videoSync = false, .audioSync = true, .volume = 0x100, }; struct mArguments args; struct mGraphicsOpts graphicsOpts; struct mSubParser subparser; initParserForGraphics(&subparser, &graphicsOpts); bool parsed = parseArguments(&args, argc, argv, &subparser); if (!parsed || args.showHelp) { usage(argv[0], subparser.usage); freeArguments(&args); return !parsed; } if (args.showVersion) { version(argv[0]); freeArguments(&args); return 0; } renderer.core = mCoreFind(args.fname); if (!renderer.core) { printf("Could not run game. Are you sure the file exists and is a compatible game?\n"); freeArguments(&args); return 1; } renderer.core->desiredVideoDimensions(renderer.core, &renderer.width, &renderer.height); #ifdef BUILD_GL mSDLGLCreate(&renderer); #elif defined(BUILD_GLES2) || defined(USE_EPOXY) mSDLGLES2Create(&renderer); #else mSDLSWCreate(&renderer); #endif renderer.ratio = graphicsOpts.multiplier; if (renderer.ratio == 0) { renderer.ratio = 1; } opts.width = renderer.width * renderer.ratio; opts.height = renderer.height * renderer.ratio; if (!renderer.core->init(renderer.core)) { freeArguments(&args); return 1; } mInputMapInit(&renderer.core->inputMap, &GBAInputInfo); mCoreInitConfig(renderer.core, PORT); applyArguments(&args, &subparser, &renderer.core->config); mCoreConfigLoadDefaults(&renderer.core->config, &opts); mCoreLoadConfig(renderer.core); renderer.viewportWidth = renderer.core->opts.width; renderer.viewportHeight = renderer.core->opts.height; #if SDL_VERSION_ATLEAST(2, 0, 0) renderer.player.fullscreen = renderer.core->opts.fullscreen; renderer.player.windowUpdated = 0; #else renderer.fullscreen = renderer.core->opts.fullscreen; #endif renderer.lockAspectRatio = renderer.core->opts.lockAspectRatio; renderer.filter = renderer.core->opts.resampleVideo; if (!mSDLInit(&renderer)) { freeArguments(&args); renderer.core->deinit(renderer.core); return 1; } renderer.player.bindings = &renderer.core->inputMap; mSDLInitBindingsGBA(&renderer.core->inputMap); mSDLInitEvents(&renderer.events); mSDLEventsLoadConfig(&renderer.events, mCoreConfigGetInput(&renderer.core->config)); mSDLAttachPlayer(&renderer.events, &renderer.player); mSDLPlayerLoadConfig(&renderer.player, mCoreConfigGetInput(&renderer.core->config)); int ret; // TODO: Use opts and config ret = mSDLRun(&renderer, &args); mSDLDetachPlayer(&renderer.events, &renderer.player); mInputMapDeinit(&renderer.core->inputMap); mSDLDeinit(&renderer); freeArguments(&args); mCoreConfigFreeOpts(&opts); mCoreConfigDeinit(&renderer.core->config); renderer.core->deinit(renderer.core); return ret; } int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) { struct mCoreThread thread = { .core = renderer->core }; if (!mCoreLoadFile(renderer->core, args->fname)) { return 1; } mCoreAutoloadSave(renderer->core); struct mDebugger* debugger = mDebuggerCreate(args->debuggerType, renderer->core); if (debugger) { mDebuggerAttach(debugger, renderer->core); mDebuggerEnter(debugger, DEBUGGER_ENTER_MANUAL, NULL); } if (args->patch) { struct VFile* patch = VFileOpen(args->patch, O_RDONLY); if (patch) { renderer->core->loadPatch(renderer->core, patch); } } else { mCoreAutoloadPatch(renderer->core); } renderer->audio.samples = renderer->core->opts.audioBuffers; renderer->audio.sampleRate = 44100; bool didFail = !mSDLInitAudio(&renderer->audio, &thread); if (!didFail) { #if SDL_VERSION_ATLEAST(2, 0, 0) mSDLSetScreensaverSuspendable(&renderer->events, renderer->core->opts.suspendScreensaver); mSDLSuspendScreensaver(&renderer->events); #endif if (mCoreThreadStart(&thread)) { renderer->runloop(renderer, &thread); mSDLPauseAudio(&renderer->audio); mCoreThreadJoin(&thread); } else { didFail = true; printf("Could not run game. Are you sure the file exists and is a compatible game?\n"); } #if SDL_VERSION_ATLEAST(2, 0, 0) mSDLResumeScreensaver(&renderer->events); mSDLSetScreensaverSuspendable(&renderer->events, false); #endif if (mCoreThreadHasCrashed(&thread)) { didFail = true; printf("The game crashed!\n"); } } renderer->core->unloadROM(renderer->core); return didFail; } static bool mSDLInit(struct mSDLRenderer* renderer) { if (SDL_Init(SDL_INIT_VIDEO) < 0) { printf("Could not initialize video: %s\n", SDL_GetError()); return false; } return renderer->init(renderer); } static void mSDLDeinit(struct mSDLRenderer* renderer) { mSDLDeinitEvents(&renderer->events); mSDLDeinitAudio(&renderer->audio); #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_DestroyWindow(renderer->window); #endif renderer->deinit(renderer); SDL_Quit(); }
static void _breakIntoDefault(int signal) { UNUSED(signal); mDebuggerEnter(&_activeDebugger->d, DEBUGGER_ENTER_MANUAL, 0); }
size_t _parseGDBMessage(struct GDBStub* stub, const char* message) { uint8_t checksum = 0; int parsed = 1; switch (*message) { case '+': stub->lineAck = GDB_ACK_RECEIVED; return parsed; case '-': stub->lineAck = GDB_NAK_RECEIVED; return parsed; case '$': ++message; break; case '\x03': mDebuggerEnter(&stub->d, DEBUGGER_ENTER_MANUAL, 0); return parsed; default: _nak(stub); return parsed; } int i; char messageType = message[0]; for (i = 0; message[i] != '#'; ++i, ++parsed) { checksum += message[i]; } if (!message[i]) { _nak(stub); return parsed; } ++i; ++parsed; if (!message[i]) { _nak(stub); return parsed; } else if (!message[i + 1]) { ++parsed; _nak(stub); return parsed; } parsed += 2; int networkChecksum = _hex2int(&message[i], 2); if (networkChecksum != checksum) { mLOG(DEBUGGER, WARN, "Checksum error: expected %02x, got %02x", checksum, networkChecksum); _nak(stub); return parsed; } _ack(stub); ++message; switch (messageType) { case '?': snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGINT); _sendMessage(stub); break; case 'c': _continue(stub, message); break; case 'G': _writeGPRs(stub, message); break; case 'g': _readGPRs(stub, message); break; case 'H': // This is faked because we only have one thread strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4); _sendMessage(stub); break; case 'M': _writeMemory(stub, message); break; case 'm': _readMemory(stub, message); break; case 'P': _writeRegister(stub, message); break; case 'p': _readRegister(stub, message); break; case 'Q': _processQWriteCommand(stub, message); break; case 'q': _processQReadCommand(stub, message); break; case 's': _step(stub, message); break; case 'V': _processVWriteCommand(stub, message); break; case 'v': _processVReadCommand(stub, message); break; case 'X': _writeMemoryBinary(stub, message); break; case 'Z': _setBreakpoint(stub, message); break; case 'z': _clearBreakpoint(stub, message); break; default: _error(stub, GDB_UNSUPPORTED_COMMAND); break; } return parsed; }
void GBStop(struct LR35902Core* cpu) { struct GB* gb = (struct GB*) cpu->master; if (cpu->bus) { mLOG(GB, GAME_ERROR, "Hit illegal stop at address %04X:%02X\n", cpu->pc, cpu->bus); } if (gb->memory.io[REG_KEY1] & 1) { gb->doubleSpeed ^= 1; gb->memory.io[REG_KEY1] = 0; gb->memory.io[REG_KEY1] |= gb->doubleSpeed << 7; } else if (cpu->bus) { if (cpu->components && cpu->components[CPU_COMPONENT_DEBUGGER]) { struct mDebuggerEntryInfo info = { .address = cpu->pc - 1, .a.c.opcode = 0x1000 | cpu->bus }; mDebuggerEnter((struct mDebugger*) cpu->components[CPU_COMPONENT_DEBUGGER], DEBUGGER_ENTER_ILLEGAL_OP, &info); } // Hang forever gb->memory.ime = 0; cpu->pc -= 2; } // TODO: Actually stop } void GBIllegal(struct LR35902Core* cpu) { struct GB* gb = (struct GB*) cpu->master; mLOG(GB, GAME_ERROR, "Hit illegal opcode at address %04X:%02X\n", cpu->pc, cpu->bus); if (cpu->components && cpu->components[CPU_COMPONENT_DEBUGGER]) { struct mDebuggerEntryInfo info = { .address = cpu->pc, .a.c.opcode = cpu->bus }; mDebuggerEnter((struct mDebugger*) cpu->components[CPU_COMPONENT_DEBUGGER], DEBUGGER_ENTER_ILLEGAL_OP, &info); } // Hang forever gb->memory.ime = 0; --cpu->pc; } bool GBIsROM(struct VFile* vf) { vf->seek(vf, 0x104, SEEK_SET); uint8_t header[4]; static const uint8_t knownHeader[4] = { 0xCE, 0xED, 0x66, 0x66}; if (vf->read(vf, &header, sizeof(header)) < (ssize_t) sizeof(header)) { return false; } if (memcmp(header, knownHeader, sizeof(header))) { return false; } return true; } void GBGetGameTitle(struct GB* gb, char* out) { const struct GBCartridge* cart = NULL; if (gb->memory.rom) { cart = (const struct GBCartridge*) &gb->memory.rom[0x100]; } if (gb->pristineRom) { cart = (const struct GBCartridge*) &((uint8_t*) gb->pristineRom)[0x100]; } if (!cart) { return; } if (cart->oldLicensee != 0x33) { memcpy(out, cart->titleLong, 16); } else { memcpy(out, cart->titleShort, 11); } } void GBGetGameCode(struct GB* gb, char* out) { memset(out, 0, 8); const struct GBCartridge* cart = NULL; if (gb->memory.rom) { cart = (const struct GBCartridge*) &gb->memory.rom[0x100]; } if (gb->pristineRom) { cart = (const struct GBCartridge*) &((uint8_t*) gb->pristineRom)[0x100]; } if (!cart) { return; } if (cart->cgb == 0xC0) { memcpy(out, "CGB-????", 8); } else { memcpy(out, "DMG-????", 8); } if (cart->oldLicensee == 0x33) { memcpy(&out[4], cart->maker, 4); } }