Merge branch 'master' (early part) into medusa

This commit is contained in:
Vicki Pfau 2022-05-30 17:27:10 -07:00
commit c144b40709
13 changed files with 262 additions and 82 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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);

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
}

View File

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

View File

@ -9,11 +9,17 @@
#include <mgba/core/config.h>
#include <mgba/core/core.h>
#include <mgba/core/serialize.h>
#ifdef M_CORE_GBA
#include <mgba/internal/gba/gba.h>
#endif
#ifdef M_CORE_GB
#include <mgba/internal/sm83/sm83.h>
#endif
#include <mgba/feature/commandline.h>
#include <mgba-util/vfs.h>
#include <errno.h>
#include <signal.h>
#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;