diff --git a/CHANGES b/CHANGES index 6beccae59..a227b1077 100644 --- a/CHANGES +++ b/CHANGES @@ -70,6 +70,7 @@ Misc: - Qt: Add tile range selection to tile viewer (closes mgba.io/i/2455) - Qt: Show warning if XQ audio is toggled while loaded (fixes mgba.io/i/2295) - Qt: Add e-Card passing to the command line (closes mgba.io/i/2474) + - Qt: Boot both a multiboot image and ROM with CLI args (closes mgba.io/i/1941) - Windows: Attach to console if present - Vita: Add bilinear filtering option (closes mgba.io/i/344) diff --git a/include/mgba/internal/gba/gba.h b/include/mgba/internal/gba/gba.h index d98cdab39..2563b96b3 100644 --- a/include/mgba/internal/gba/gba.h +++ b/include/mgba/internal/gba/gba.h @@ -98,6 +98,7 @@ struct GBA { uint32_t romCrc32; struct VFile* romVf; struct VFile* biosVf; + struct VFile* mbVf; struct mAVStream* stream; struct mKeyCallback* keyCallback; @@ -171,6 +172,8 @@ void GBALoadBIOS(struct GBA* gba, struct VFile* vf); void GBAApplyPatch(struct GBA* gba, struct Patch* patch); bool GBALoadMB(struct GBA* gba, struct VFile* vf); +void GBAUnloadMB(struct GBA* gba); + bool GBALoadNull(struct GBA* gba); void GBAGetGameCode(const struct GBA* gba, char* out); diff --git a/src/gba/gba.c b/src/gba/gba.c index 285b06615..a39823666 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -139,7 +139,7 @@ void GBAUnloadROM(struct GBA* gba) { if (gba->romVf) { #ifndef FIXED_ROM_BUFFER - if (gba->isPristine) { + if (gba->isPristine && gba->memory.rom) { gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->pristineRomSize); } #endif @@ -147,6 +147,8 @@ void GBAUnloadROM(struct GBA* gba) { gba->romVf = NULL; } gba->memory.rom = NULL; + gba->memory.romSize = 0; + gba->memory.romMask = 0; gba->isPristine = false; if (!gba->memory.savedata.dirty) { @@ -163,6 +165,7 @@ void GBAUnloadROM(struct GBA* gba) { void GBADestroy(struct GBA* gba) { GBAUnloadROM(gba); + GBAUnloadMB(gba); if (gba->biosVf) { gba->biosVf->unmap(gba->biosVf, gba->memory.bios, SIZE_BIOS); @@ -228,16 +231,18 @@ void GBAReset(struct ARMCore* cpu) { bool isELF = false; #ifdef USE_ELF - struct ELF* elf = ELFOpen(gba->romVf); - if (elf) { - isELF = true; - ELFClose(elf); + if (gba->mbVf) { + struct ELF* elf = ELFOpen(gba->mbVf); + if (elf) { + isELF = true; + ELFClose(elf); + } } #endif - if (GBAIsMB(gba->romVf) && !isELF) { - gba->romVf->seek(gba->romVf, 0, SEEK_SET); - gba->romVf->read(gba->romVf, gba->memory.wram, gba->pristineRomSize); + if (GBAIsMB(gba->mbVf) && !isELF) { + gba->mbVf->seek(gba->mbVf, 0, SEEK_SET); + gba->mbVf->read(gba->mbVf, gba->memory.wram, SIZE_WORKING_RAM); } gba->lastJump = 0; @@ -367,25 +372,24 @@ bool GBALoadNull(struct GBA* gba) { } bool GBALoadMB(struct GBA* gba, struct VFile* vf) { - GBAUnloadROM(gba); - gba->romVf = vf; - gba->pristineRomSize = vf->size(vf); + GBAUnloadMB(gba); + gba->mbVf = vf; vf->seek(vf, 0, SEEK_SET); - if (gba->pristineRomSize > SIZE_WORKING_RAM) { - gba->pristineRomSize = SIZE_WORKING_RAM; - } - gba->isPristine = true; memset(gba->memory.wram, 0, SIZE_WORKING_RAM); - gba->yankedRomSize = 0; - gba->memory.romSize = 0; - gba->memory.romMask = 0; - gba->romCrc32 = doCrc32(gba->memory.wram, gba->pristineRomSize); + vf->read(vf, gba->memory.wram, SIZE_WORKING_RAM); if (gba->cpu && gba->memory.activeRegion == REGION_WORKING_RAM) { gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]); } return true; } +void GBAUnloadMB(struct GBA* gba) { + if (gba->mbVf) { + gba->mbVf->close(gba->mbVf); + gba->mbVf = NULL; + } +} + bool GBALoadROM(struct GBA* gba, struct VFile* vf) { if (!vf) { return false; diff --git a/src/gba/hle-bios.c b/src/gba/hle-bios.c index 27dff4642..91a48711b 100644 --- a/src/gba/hle-bios.c +++ b/src/gba/hle-bios.c @@ -5,8 +5,8 @@ const uint8_t hleBios[SIZE_BIOS] = { 0x06, 0x00, 0x00, 0xea, 0x66, 0x00, 0x00, 0xea, 0x0c, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe1, - 0x59, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x02, 0x03, 0xa0, 0xe3, - 0x03, 0x10, 0xd0, 0xe5, 0xea, 0x00, 0x51, 0xe3, 0x4c, 0x01, 0x9f, 0x15, + 0x59, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x58, 0x01, 0x9f, 0xe5, + 0x00, 0x10, 0x90, 0xe5, 0x00, 0x00, 0x51, 0xe3, 0x02, 0x03, 0xa0, 0x03, 0x10, 0xff, 0x2f, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x29, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xe3, 0x01, 0xd3, 0xa0, 0x03, 0x20, 0xd0, 0x4d, 0x02, 0x00, 0x58, 0x2d, 0xe9, 0x02, 0xc0, 0x5e, 0xe5, diff --git a/src/gba/hle-bios.s b/src/gba/hle-bios.s index f2148fb1b..3f9ae905e 100644 --- a/src/gba/hle-bios.s +++ b/src/gba/hle-bios.s @@ -17,10 +17,10 @@ b irqBase b fiqBase resetBase: -mov r0, #0x8000000 -ldrb r1, [r0, #3] -cmp r1, #0xEA -ldrne r0, =0x20000C0 +ldr r0, =0x20000C0 +ldr r1, [r0] +cmp r1, #0 +moveq r0, #0x8000000 bx r0 .word 0 .word 0xE129F000 diff --git a/src/platform/qt/ConfigController.cpp b/src/platform/qt/ConfigController.cpp index f7039e188..66c89b4d5 100644 --- a/src/platform/qt/ConfigController.cpp +++ b/src/platform/qt/ConfigController.cpp @@ -15,6 +15,7 @@ static const mOption s_frontendOptions[] = { { "ecard", true, '\0' }, + { "mb", true, '\0' }, { 0 } }; @@ -136,6 +137,7 @@ ConfigController::ConfigController(QObject* parent) m_subparsers[1].usage = "Frontend options:\n" " --ecard FILE Scan an e-Reader card in the first loaded game\n" " Can be paassed multiple times for multiple cards\n" + " --mb FILE Boot a multiboot image with FILE inserted into the ROM slot"; m_subparsers[1].parse = nullptr; m_subparsers[1].parseLong = [](struct mSubParser* parser, const char* option, const char* arg) { ConfigController* self = static_cast(parser->opts); @@ -149,6 +151,10 @@ ConfigController::ConfigController(QObject* parent) self->m_argvOptions[optionName] = ecards; return true; } + if (optionName == QLatin1String("mb")) { + self->m_argvOptions[optionName] = QString::fromUtf8(arg); + return true; + } return false; }; m_subparsers[1].apply = nullptr; diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 8c69efc4f..87b6a8530 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -2057,6 +2057,15 @@ void Window::setController(CoreController* controller, const QString& fname) { connect(m_controller.get(), &CoreController::failed, this, &Window::gameFailed); connect(m_controller.get(), &CoreController::unimplementedBiosCall, this, &Window::unimplementedBiosCall); +#ifdef M_CORE_GBA + if (m_controller->platform() == mPLATFORM_GBA) { + QVariant mb = m_config->takeArgvOption(QString("mb")); + if (mb.canConvert(QMetaType::QString)) { + m_controller->replaceGame(mb.toString()); + } + } +#endif + #ifdef USE_GDB_STUB if (m_gdbController) { m_gdbController->setController(m_controller);