diff --git a/CHANGES b/CHANGES index 3d768720b..2051daf5e 100644 --- a/CHANGES +++ b/CHANGES @@ -54,15 +54,18 @@ Features: Emulation fixes: - ARM7: Fix unsigned multiply timing - GB Memory: Add cursory cartridge open bus emulation (fixes mgba.io/i/2032) + - GB Serialize: Fix loading MBC1 states that affect bank 0 (fixes mgba.io/i/2402) - GB Video: Draw SGB border pieces that overlap GB graphics (fixes mgba.io/i/1339) - GBA: Improve timing when not booting from BIOS - GBA BIOS: Work around IRQ handling hiccup in Mario & Luigi (fixes mgba.io/i/1059) + - GBA BIOS: Initial HLE timing estimation of UnLz77 functions (fixes mgba.io/i/2141) - GBA DMA: Fix DMA source direction bits being cleared (fixes mgba.io/i/2410) - GBA I/O: Redo internal key input, enabling edge-based key IRQs - GBA I/O: Disable open bus behavior on invalid register 06A - GBA Memory: Fix misaligned 32-bit I/O loads (fixes mgba.io/i/2307) - GBA Video: Fix OpenGL rendering on M1 Macs - GBA Video: Ignore horizontally off-screen sprite timing (fixes mgba.io/i/2391) + - GBA Video: Fix Hblank timing (fixes mgba.io/i/2131, mgba.io/i/2310) Other fixes: - Core: Don't attempt to restore rewind diffs past start of rewind - FFmpeg: Fix crash when encoding audio with some containers diff --git a/CMakeLists.txt b/CMakeLists.txt index e10f61d23..e4cc4866d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,6 +72,7 @@ if(NOT LIBMGBA_ONLY) set(BUILD_TEST OFF CACHE BOOL "Build testing harness") set(BUILD_SUITE OFF CACHE BOOL "Build test suite") set(BUILD_CINEMA OFF CACHE BOOL "Build video tests suite") + set(BUILD_ROM_TEST OFF CACHE BOOL "Build ROM test tool") set(BUILD_EXAMPLE OFF CACHE BOOL "Build example frontends") set(BUILD_PYTHON OFF CACHE BOOL "Build Python bindings") set(BUILD_STATIC OFF CACHE BOOL "Build a static library") @@ -1250,12 +1251,14 @@ if(NOT QUIET AND NOT LIBMGBA_ONLY) message(STATUS "Frontends:") message(STATUS " Qt: ${BUILD_QT}") message(STATUS " SDL (${SDL_VERSION}): ${BUILD_SDL}") + message(STATUS " Python bindings: ${BUILD_PYTHON}") + message(STATUS " Examples: ${BUILD_EXAMPLE}") + message(STATUS "Test tools:") message(STATUS " Profiling: ${BUILD_PERF}") message(STATUS " Test harness: ${BUILD_TEST}") message(STATUS " Test suite: ${BUILD_SUITE}") message(STATUS " Video test suite: ${BUILD_CINEMA}") - message(STATUS " Python bindings: ${BUILD_PYTHON}") - message(STATUS " Examples: ${BUILD_EXAMPLE}") + message(STATUS " ROM tester: ${BUILD_ROM_TEST}") message(STATUS "Cores:") message(STATUS " Libretro core: ${BUILD_LIBRETRO}") if(APPLE) diff --git a/include/mgba/internal/gba/renderers/video-software.h b/include/mgba/internal/gba/renderers/video-software.h index cdc4f37bd..b95aa7a03 100644 --- a/include/mgba/internal/gba/renderers/video-software.h +++ b/include/mgba/internal/gba/renderers/video-software.h @@ -46,6 +46,9 @@ struct GBAVideoSoftwareBackground { uint16_t mapCache[64]; color_t* extPalette; color_t* variantPalette; + uint32_t flags; + uint32_t objwinFlags; + bool variant; int32_t offsetX; int32_t offsetY; bool highlight; diff --git a/include/mgba/internal/gba/video.h b/include/mgba/internal/gba/video.h index 94ac19f3e..0b7a65ce0 100644 --- a/include/mgba/internal/gba/video.h +++ b/include/mgba/internal/gba/video.h @@ -18,9 +18,8 @@ mLOG_DECLARE_CATEGORY(GBA_VIDEO); enum { VIDEO_HBLANK_PIXELS = 68, - VIDEO_HDRAW_LENGTH = 960, - VIDEO_HBLANK_LENGTH = 272, - VIDEO_HBLANK_FLIP = 46, + VIDEO_HDRAW_LENGTH = 1008, + VIDEO_HBLANK_LENGTH = 224, VIDEO_HORIZONTAL_LENGTH = 1232, VIDEO_VBLANK_PIXELS = 68, diff --git a/src/gb/memory.c b/src/gb/memory.c index 175950e82..a89cbbcce 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -820,7 +820,7 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { memory->mbcState.mbc1.bankHi = memory->currentBank >> memory->mbcState.mbc1.multicartStride; } if (memory->mbcState.mbc1.mode) { - GBMBCSwitchBank0(gb, memory->mbcState.mbc1.bankHi); + GBMBCSwitchBank0(gb, memory->mbcState.mbc1.bankHi << memory->mbcState.mbc1.multicartStride); } break; case GB_MBC3_RTC: diff --git a/src/gba/bios.c b/src/gba/bios.c index 05782c39d..585c99dc3 100644 --- a/src/gba/bios.c +++ b/src/gba/bios.c @@ -536,6 +536,7 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { case REGION_WORKING_RAM: case REGION_WORKING_IRAM: case REGION_VRAM: + useStall = true; _unLz77(gba, immediate == GBA_SWI_LZ77_UNCOMP_WRAM ? 1 : 2); break; } @@ -644,7 +645,8 @@ static void _unLz77(struct GBA* gba, int width) { struct ARMCore* cpu = gba->cpu; uint32_t source = cpu->gprs[0]; uint32_t dest = cpu->gprs[1]; - int remaining = (cpu->memory.load32(cpu, source, 0) & 0xFFFFFF00) >> 8; + int32_t cycles = 20; + int remaining = (cpu->memory.load32(cpu, source, &cycles) & 0xFFFFFF00) >> 8; // We assume the signature byte (0x10) is correct int blockheader = 0; // Some compilers warn if this isn't set, even though it's trivially provably always set source += 4; @@ -654,14 +656,17 @@ static void _unLz77(struct GBA* gba, int width) { int byte; int halfword = 0; while (remaining > 0) { + cycles += 14; if (blocksRemaining) { + cycles += 18; if (blockheader & 0x80) { // Compressed - int block = cpu->memory.load8(cpu, source + 1, 0) | (cpu->memory.load8(cpu, source, 0) << 8); + int block = cpu->memory.load8(cpu, source + 1, &cycles) | (cpu->memory.load8(cpu, source, &cycles) << 8); source += 2; disp = dest - (block & 0x0FFF) - 1; bytes = (block >> 12) + 3; while (bytes--) { + cycles += 10; if (remaining) { --remaining; } else { @@ -673,35 +678,36 @@ static void _unLz77(struct GBA* gba, int width) { } } if (width == 2) { - byte = (int16_t) cpu->memory.load16(cpu, disp & ~1, 0); + byte = (int16_t) cpu->memory.load16(cpu, disp & ~1, &cycles); if (dest & 1) { byte >>= (disp & 1) * 8; halfword |= byte << 8; - cpu->memory.store16(cpu, dest ^ 1, halfword, 0); + cpu->memory.store16(cpu, dest ^ 1, halfword, &cycles); } else { byte >>= (disp & 1) * 8; halfword = byte & 0xFF; } + cycles += 4; } else { - byte = cpu->memory.load8(cpu, disp, 0); - cpu->memory.store8(cpu, dest, byte, 0); + byte = cpu->memory.load8(cpu, disp, &cycles); + cpu->memory.store8(cpu, dest, byte, &cycles); } ++disp; ++dest; } } else { // Uncompressed - byte = cpu->memory.load8(cpu, source, 0); + byte = cpu->memory.load8(cpu, source, &cycles); ++source; if (width == 2) { if (dest & 1) { halfword |= byte << 8; - cpu->memory.store16(cpu, dest ^ 1, halfword, 0); + cpu->memory.store16(cpu, dest ^ 1, halfword, &cycles); } else { halfword = byte; } } else { - cpu->memory.store8(cpu, dest, byte, 0); + cpu->memory.store8(cpu, dest, byte, &cycles); } ++dest; --remaining; @@ -709,7 +715,7 @@ static void _unLz77(struct GBA* gba, int width) { blockheader <<= 1; --blocksRemaining; } else { - blockheader = cpu->memory.load8(cpu, source, 0); + blockheader = cpu->memory.load8(cpu, source, &cycles); ++source; blocksRemaining = 8; } @@ -717,6 +723,7 @@ static void _unLz77(struct GBA* gba, int width) { cpu->gprs[0] = source; cpu->gprs[1] = dest; cpu->gprs[3] = 0; + gba->biosStall = cycles; } DECL_BITFIELD(HuffmanNode, uint8_t); diff --git a/src/gba/hle-bios.c b/src/gba/hle-bios.c index da4e07111..27dff4642 100644 --- a/src/gba/hle-bios.c +++ b/src/gba/hle-bios.c @@ -26,7 +26,7 @@ const uint8_t hleBios[SIZE_BIOS] = { 0x48, 0x03, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00, 0x14, 0x02, 0x00, 0x00, 0xa8, 0x02, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, - 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, + 0xb0, 0x01, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, diff --git a/src/gba/hle-bios.s b/src/gba/hle-bios.s index 52d5c88dc..f2148fb1b 100644 --- a/src/gba/hle-bios.s +++ b/src/gba/hle-bios.s @@ -137,8 +137,6 @@ GetBiosChecksum: BgAffineSet: ObjAffineSet: BitUnPack: -Lz77UnCompWram: -Lz77UnCompVram: HuffmanUnComp: RlUnCompWram: RlUnCompVram: @@ -308,6 +306,8 @@ DivArm: Sqrt: ArcTan: ArcTan2: +Lz77UnCompWram: +Lz77UnCompVram: StallCall: subs r11, #4 diff --git a/src/gba/renderers/software-mode0.c b/src/gba/renderers/software-mode0.c index 1b375d811..5303c1ce0 100644 --- a/src/gba/renderers/software-mode0.c +++ b/src/gba/renderers/software-mode0.c @@ -747,19 +747,12 @@ void GBAVideoSoftwareRendererDrawBackgroundMode0(struct GBAVideoSoftwareRenderer unsigned xBase; - uint32_t flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND; - flags |= FLAG_TARGET_2 * background->target2; - int objwinFlags = FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(renderer->objwin.packed)); - objwinFlags |= flags; - flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed)); - if (renderer->blendEffect == BLEND_ALPHA && renderer->blda == 0x10 && renderer->bldb == 0) { - flags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2); - objwinFlags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2); - } + uint32_t flags = background->flags; + uint32_t objwinFlags = background->objwinFlags; + bool variant = background->variant; uint32_t screenBase; uint32_t charBase; - int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); color_t* mainPalette = renderer->normalPalette; if (background->multipalette && background->extPalette) { mainPalette = background->extPalette; diff --git a/src/gba/renderers/software-private.h b/src/gba/renderers/software-private.h index 2ab5b3964..14d737145 100644 --- a/src/gba/renderers/software-private.h +++ b/src/gba/renderers/software-private.h @@ -15,6 +15,8 @@ #define VIDEO_CHECKS true #endif +#define ENABLED_MAX 4 + void GBAVideoSoftwareRendererDrawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y); void GBAVideoSoftwareRendererDrawBackgroundMode2(struct GBAVideoSoftwareRenderer* renderer, @@ -191,19 +193,9 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re localY += background->sy + startX * background->dy; \ } \ \ - uint32_t flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND; \ - flags |= FLAG_TARGET_2 * background->target2; \ - int objwinFlags = FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && \ - GBAWindowControlIsBlendEnable(renderer->objwin.packed)); \ - objwinFlags |= flags; \ - flags |= FLAG_TARGET_1 * (background->target1 && renderer->blendEffect == BLEND_ALPHA && \ - GBAWindowControlIsBlendEnable(renderer->currentWindow.packed)); \ - if (renderer->blendEffect == BLEND_ALPHA && renderer->blda == 0x10 && renderer->bldb == 0) { \ - flags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2); \ - objwinFlags &= ~(FLAG_TARGET_1 | FLAG_TARGET_2); \ - } \ - int variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && \ - (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); \ + uint32_t flags = background->flags; \ + uint32_t objwinFlags = background->objwinFlags; \ + bool variant = background->variant; \ color_t* palette = renderer->normalPalette; \ if (renderer->d.highlightAmount && background->highlight) { \ palette = renderer->highlightPalette; \ @@ -219,7 +211,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re #define TEST_LAYER_ENABLED(X) \ !softwareRenderer->d.disableBG[X] && \ - (softwareRenderer->bg[X].enabled == 4 && \ + (softwareRenderer->bg[X].enabled == ENABLED_MAX && \ (GBAWindowControlIsBg ## X ## Enable(softwareRenderer->currentWindow.packed) || \ (GBARegisterDISPCNTIsObjwinEnable(softwareRenderer->dispcnt) && GBAWindowControlIsBg ## X ## Enable (softwareRenderer->objwin.packed))) && \ softwareRenderer->bg[X].priority == priority) diff --git a/src/gba/renderers/video-software.c b/src/gba/renderers/video-software.c index 82b80b5a8..2d24c98fc 100644 --- a/src/gba/renderers/video-software.c +++ b/src/gba/renderers/video-software.c @@ -36,6 +36,7 @@ static void GBAVideoSoftwareRendererWriteBGY_HI(struct GBAVideoSoftwareBackgroun static void GBAVideoSoftwareRendererWriteBLDCNT(struct GBAVideoSoftwareRenderer* renderer, uint16_t value); static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer); +static void _updateFlags(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg); static void _breakWindow(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win, int y); static void _breakWindowInner(struct GBAVideoSoftwareRenderer* softwareRenderer, struct WindowN* win); @@ -563,11 +564,11 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render if (!dirty) { if (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) != 0) { - if (softwareRenderer->bg[2].enabled == 4) { + if (softwareRenderer->bg[2].enabled == ENABLED_MAX) { softwareRenderer->bg[2].sx += softwareRenderer->bg[2].dmx; softwareRenderer->bg[2].sy += softwareRenderer->bg[2].dmy; } - if (softwareRenderer->bg[3].enabled == 4) { + if (softwareRenderer->bg[3].enabled == ENABLED_MAX) { softwareRenderer->bg[3].sx += softwareRenderer->bg[3].dmx; softwareRenderer->bg[3].sy += softwareRenderer->bg[3].dmy; } @@ -603,6 +604,40 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render softwareRenderer->start = softwareRenderer->end; softwareRenderer->end = softwareRenderer->windows[w].endX; softwareRenderer->currentWindow = softwareRenderer->windows[w].control; + switch (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt)) { + case 0: + if (softwareRenderer->bg[0].enabled == ENABLED_MAX) { + _updateFlags(softwareRenderer, &softwareRenderer->bg[0]); + } + if (softwareRenderer->bg[1].enabled == ENABLED_MAX) { + _updateFlags(softwareRenderer, &softwareRenderer->bg[1]); + } + // Fall through + case 2: + if (softwareRenderer->bg[3].enabled == ENABLED_MAX) { + _updateFlags(softwareRenderer, &softwareRenderer->bg[3]); + } + // Fall through + case 3: + case 4: + case 5: + if (softwareRenderer->bg[2].enabled == ENABLED_MAX) { + _updateFlags(softwareRenderer, &softwareRenderer->bg[2]); + } + break; + case 1: + if (softwareRenderer->bg[0].enabled == ENABLED_MAX) { + _updateFlags(softwareRenderer, &softwareRenderer->bg[0]); + } + if (softwareRenderer->bg[1].enabled == ENABLED_MAX) { + _updateFlags(softwareRenderer, &softwareRenderer->bg[1]); + } + if (softwareRenderer->bg[2].enabled == ENABLED_MAX) { + _updateFlags(softwareRenderer, &softwareRenderer->bg[2]); + } + break; + } + for (priority = 0; priority < 4; ++priority) { if (spriteLayers & (1 << priority)) { GBAVideoSoftwareRendererPostprocessSprite(softwareRenderer, priority); @@ -649,29 +684,29 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render GBAVideoSoftwareRendererPostprocessBuffer(softwareRenderer); if (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) != 0) { - if (softwareRenderer->bg[2].enabled == 4) { + if (softwareRenderer->bg[2].enabled == ENABLED_MAX) { softwareRenderer->bg[2].sx += softwareRenderer->bg[2].dmx; softwareRenderer->bg[2].sy += softwareRenderer->bg[2].dmy; } - if (softwareRenderer->bg[3].enabled == 4) { + if (softwareRenderer->bg[3].enabled == ENABLED_MAX) { softwareRenderer->bg[3].sx += softwareRenderer->bg[3].dmx; softwareRenderer->bg[3].sy += softwareRenderer->bg[3].dmy; } } - if (softwareRenderer->bg[0].enabled != 0 && softwareRenderer->bg[0].enabled < 4) { + if (softwareRenderer->bg[0].enabled != 0 && softwareRenderer->bg[0].enabled < ENABLED_MAX) { ++softwareRenderer->bg[0].enabled; DIRTY_SCANLINE(softwareRenderer, y); } - if (softwareRenderer->bg[1].enabled != 0 && softwareRenderer->bg[1].enabled < 4) { + if (softwareRenderer->bg[1].enabled != 0 && softwareRenderer->bg[1].enabled < ENABLED_MAX) { ++softwareRenderer->bg[1].enabled; DIRTY_SCANLINE(softwareRenderer, y); } - if (softwareRenderer->bg[2].enabled != 0 && softwareRenderer->bg[2].enabled < 4) { + if (softwareRenderer->bg[2].enabled != 0 && softwareRenderer->bg[2].enabled < ENABLED_MAX) { ++softwareRenderer->bg[2].enabled; DIRTY_SCANLINE(softwareRenderer, y); } - if (softwareRenderer->bg[3].enabled != 0 && softwareRenderer->bg[3].enabled < 4) { + if (softwareRenderer->bg[3].enabled != 0 && softwareRenderer->bg[3].enabled < ENABLED_MAX) { ++softwareRenderer->bg[3].enabled; DIRTY_SCANLINE(softwareRenderer, y); } @@ -717,16 +752,16 @@ static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* rendere softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy; if (softwareRenderer->bg[0].enabled > 0) { - softwareRenderer->bg[0].enabled = 4; + softwareRenderer->bg[0].enabled = ENABLED_MAX; } if (softwareRenderer->bg[1].enabled > 0) { - softwareRenderer->bg[1].enabled = 4; + softwareRenderer->bg[1].enabled = ENABLED_MAX; } if (softwareRenderer->bg[2].enabled > 0) { - softwareRenderer->bg[2].enabled = 4; + softwareRenderer->bg[2].enabled = ENABLED_MAX; } if (softwareRenderer->bg[3].enabled > 0) { - softwareRenderer->bg[3].enabled = 4; + softwareRenderer->bg[3].enabled = ENABLED_MAX; } } @@ -749,22 +784,22 @@ static void GBAVideoSoftwareRendererPutPixels(struct GBAVideoRenderer* renderer, static void _enableBg(struct GBAVideoSoftwareRenderer* renderer, int bg, bool active) { int wasActive = renderer->bg[bg].enabled; if (!active) { - if (renderer->nextY == 0 || (wasActive > 0 && wasActive < 4)) { + if (renderer->nextY == 0 || (wasActive > 0 && wasActive < ENABLED_MAX)) { renderer->bg[bg].enabled = 0; - } else if (wasActive == 4) { + } else if (wasActive == ENABLED_MAX) { renderer->bg[bg].enabled = -2; } } else if (!wasActive && active) { if (renderer->nextY == 0) { // TODO: Investigate in more depth how switching background works in different modes - renderer->bg[bg].enabled = 4; + renderer->bg[bg].enabled = ENABLED_MAX; } else if (GBARegisterDISPCNTGetMode(renderer->dispcnt) > 2) { renderer->bg[bg].enabled = 2; } else { renderer->bg[bg].enabled = 1; } } else if (wasActive < 0 && active) { - renderer->bg[bg].enabled = 4; + renderer->bg[bg].enabled = ENABLED_MAX; } } @@ -788,6 +823,8 @@ static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* bg->size = GBARegisterBGCNTGetSize(value); bg->control = value; bg->yCache = -1; + + _updateFlags(renderer, bg); } static void GBAVideoSoftwareRendererWriteBGX_LO(struct GBAVideoSoftwareBackground* bg, uint16_t value) { @@ -1037,3 +1074,27 @@ static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) { } } } + +void _updateFlags(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background) { + uint32_t flags = (background->priority << OFFSET_PRIORITY) | (background->index << OFFSET_INDEX) | FLAG_IS_BACKGROUND; + if (background->target2) { + flags |= FLAG_TARGET_2; + } + uint32_t objwinFlags = flags; + if (renderer->blendEffect == BLEND_ALPHA) { + if (renderer->blda == 0x10 && renderer->bldb == 0) { + flags &= ~FLAG_TARGET_2; + objwinFlags &= ~FLAG_TARGET_2; + } else if (background->target1) { + if (GBAWindowControlIsBlendEnable(renderer->currentWindow.packed)) { + flags |= FLAG_TARGET_1; + } + if (GBAWindowControlIsBlendEnable(renderer->objwin.packed)) { + objwinFlags |= FLAG_TARGET_1; + } + } + } + background->flags = flags; + background->objwinFlags = objwinFlags; + background->variant = background->target1 && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); +} diff --git a/src/gba/video.c b/src/gba/video.c index b8d5d4cd6..1d82a49f0 100644 --- a/src/gba/video.c +++ b/src/gba/video.c @@ -31,7 +31,6 @@ static void GBAVideoDummyRendererGetPixels(struct GBAVideoRenderer* renderer, si static void GBAVideoDummyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels); static void _startHblank(struct mTiming*, void* context, uint32_t cyclesLate); -static void _midHblank(struct mTiming*, void* context, uint32_t cyclesLate); static void _startHdraw(struct mTiming*, void* context, uint32_t cyclesLate); static uint16_t _zeroes[0x2000] = {0}; @@ -161,15 +160,6 @@ void GBAVideoAssociateRenderer(struct GBAVideo* video, struct GBAVideoRenderer* } } -void _midHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) { - struct GBAVideo* video = context; - GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1]; - dispstat = GBARegisterDISPSTATClearInHblank(dispstat); - video->p->memory.io[REG_DISPSTAT >> 1] = dispstat; - video->event.callback = _startHdraw; - mTimingSchedule(timing, &video->event, VIDEO_HBLANK_FLIP - cyclesLate); -} - void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) { struct GBAVideo* video = context; video->event.callback = _startHblank; @@ -186,6 +176,7 @@ void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) { } GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1]; + dispstat = GBARegisterDISPSTATClearInHblank(dispstat); if (video->vcount == GBARegisterDISPSTATGetVcountSetting(dispstat)) { dispstat = GBARegisterDISPSTATFillVcounter(dispstat); if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) { @@ -227,8 +218,8 @@ void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) { void _startHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) { struct GBAVideo* video = context; - video->event.callback = _midHblank; - mTimingSchedule(timing, &video->event, VIDEO_HBLANK_LENGTH - VIDEO_HBLANK_FLIP - cyclesLate); + video->event.callback = _startHdraw; + mTimingSchedule(timing, &video->event, VIDEO_HBLANK_LENGTH - cyclesLate); // Begin Hblank GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1]; @@ -244,7 +235,7 @@ void _startHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) { GBADMARunDisplayStart(video->p, -cyclesLate); } if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) { - GBARaiseIRQ(video->p, IRQ_HBLANK, cyclesLate); + GBARaiseIRQ(video->p, IRQ_HBLANK, cyclesLate - 6); // TODO: Where does this fudge factor come from? } video->shouldStall = 0; video->p->memory.io[REG_DISPSTAT >> 1] = dispstat; @@ -366,8 +357,6 @@ void GBAVideoSerialize(const struct GBAVideo* video, struct GBASerializedState* flags = GBASerializedVideoFlagsSetMode(flags, 1); } else if (video->event.callback == _startHblank) { flags = GBASerializedVideoFlagsSetMode(flags, 2); - } else if (video->event.callback == _midHblank) { - flags = GBASerializedVideoFlagsSetMode(flags, 3); } STORE_32(flags, 0, &state->video.flags); STORE_32(video->frameCounter, 0, &state->video.frameCounter); @@ -407,7 +396,7 @@ void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState video->shouldStall = 1; break; case 3: - video->event.callback = _midHblank; + video->event.callback = _startHdraw; break; } uint32_t when; diff --git a/src/platform/test/rom-test-main.c b/src/platform/test/rom-test-main.c index d015f40c0..916df85b7 100644 --- a/src/platform/test/rom-test-main.c +++ b/src/platform/test/rom-test-main.c @@ -9,11 +9,17 @@ #include #include #include +#ifdef M_CORE_GBA #include +#endif +#ifdef M_CORE_GB +#include +#endif #include #include +#include #include #define ROM_TEST_OPTIONS "S:R:" @@ -48,6 +54,21 @@ void (*_armSwi16)(struct ARMCore* cpu, int immediate); void (*_armSwi32)(struct ARMCore* cpu, int immediate); #endif +#ifdef M_CORE_GB +enum GBReg { + GB_REG_A = 16, + GB_REG_F, + GB_REG_B, + GB_REG_C, + GB_REG_D, + GB_REG_E, + GB_REG_H, + GB_REG_L +}; + +static void _romTestGBCallback(void* context); +#endif + int main(int argc, char * argv[]) { signal(SIGINT, _romTestShutdown); @@ -82,17 +103,22 @@ int main(int argc, char * argv[]) { mCoreConfigSetDefaultValue(&core->config, "idleOptimization", "remove"); + bool cleanExit = false; + struct mCoreCallbacks callbacks = {0}; + callbacks.context = core; + switch (core->platform(core)) { #ifdef M_CORE_GBA - if (core->platform(core) == mPLATFORM_GBA) { + case mPLATFORM_GBA: ((struct GBA*) core->board)->hardCrash = false; + if (romTestOpts.returnCodeRegister >= 16) { + goto loadError; + } _exitSwiImmediate = romTestOpts.exitSwiImmediate; _returnCodeRegister = romTestOpts.returnCodeRegister; if (_exitSwiImmediate == 3) { // Hook into SWI 3 (shutdown) - struct mCoreCallbacks callbacks = {0}; - callbacks.context = core; callbacks.shutdown = _romTestSwi3Callback; core->addCoreCallbacks(core, &callbacks); } else { @@ -102,12 +128,25 @@ int main(int argc, char * argv[]) { _armSwi32 = ((struct GBA*) core->board)->cpu->irqh.swi32; ((struct GBA*) core->board)->cpu->irqh.swi32 = _romTestSwi32; } - } + break; #endif +#ifdef M_CORE_GB + case mPLATFORM_GB: + if (romTestOpts.returnCodeRegister < GB_REG_A) { + goto loadError; + } + + _returnCodeRegister = romTestOpts.returnCodeRegister; + + callbacks.shutdown = _romTestGBCallback; + core->addCoreCallbacks(core, &callbacks); + break; +#endif + default: + goto loadError; + } - bool cleanExit = true; if (!mCoreLoadFile(core, args.fname)) { - cleanExit = false; goto loadError; } if (args.patch) { @@ -142,6 +181,7 @@ int main(int argc, char * argv[]) { } while (!_dispatchExiting); core->unloadROM(core); + cleanExit = true; loadError: freeArguments(&args); @@ -182,6 +222,41 @@ static void _romTestSwi32(struct ARMCore* cpu, int immediate) { } #endif +#ifdef M_CORE_GB +static void _romTestGBCallback(void* context) { + struct mCore* core = context; + struct SM83Core* cpu = core->cpu; + + switch (_returnCodeRegister) { + case GB_REG_A: + _exitCode = cpu->a; + break; + case GB_REG_B: + _exitCode = cpu->b; + break; + case GB_REG_C: + _exitCode = cpu->c; + break; + case GB_REG_D: + _exitCode = cpu->d; + break; + case GB_REG_E: + _exitCode = cpu->e; + break; + case GB_REG_F: + _exitCode = cpu->f.packed; + break; + case GB_REG_H: + _exitCode = cpu->h; + break; + case GB_REG_L: + _exitCode = cpu->l; + break; + } + _dispatchExiting = true; +} +#endif + static bool _parseRomTestOpts(struct mSubParser* parser, int option, const char* arg) { struct RomTestOpts* opts = parser->opts; errno = 0; @@ -206,8 +281,63 @@ static bool _parseSwi(const char* swiStr, int* oSwi) { } static bool _parseNamedRegister(const char* regStr, unsigned int* oRegister) { - if (regStr[0] == 'r' || regStr[0] == 'R') { +#ifdef M_CORE_GB + static const enum GBReg gbMapping[] = { + ['a' - 'a'] = GB_REG_A, + ['b' - 'a'] = GB_REG_B, + ['c' - 'a'] = GB_REG_C, + ['d' - 'a'] = GB_REG_D, + ['e' - 'a'] = GB_REG_E, + ['f' - 'a'] = GB_REG_F, + ['h' - 'a'] = GB_REG_H, + ['l' - 'a'] = GB_REG_L, + }; +#endif + + switch (regStr[0]) { + case 'r': + case 'R': ++regStr; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; +#ifdef M_CORE_GB + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'h': + case 'l': + if (regStr[1] != '\0') { + return false; + } + *oRegister = gbMapping[regStr[0] - 'a']; + break; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'H': + case 'L': + if (regStr[1] != '\0') { + return false; + } + *oRegister = gbMapping[regStr[0] - 'A']; + return true; +#endif } char* parseEnd;