all repos — mgba @ 3b379d7e8db493693eb549b5cb6aff76803e7c83

mGBA Game Boy Advance Emulator

Merge branch 'master' (early part) into medusa
Vicki Pfau vi@endrift.com
Fri, 28 Jun 2019 15:41:23 -0700
commit

3b379d7e8db493693eb549b5cb6aff76803e7c83

parent

4d137e7f85e1e0a1f17f53aa1ac126a855559b37

M CHANGESCHANGES

@@ -65,6 +65,12 @@ - Core: Fix ROM patches not being unloaded when disabled (fixes mgba.io/i/962)

- GBA I/O: Fix writing to DISPCNT CGB flag (fixes mgba.io/i/902) - GBA Memory: Partially revert prefetch changes (fixes mgba.io/i/840) - PSP2: Fix issues causing poor audio + - Wii: Fix screen tear when unpausing + - GBA: Fix some GBA ROM misdetection (fixes mgba.io/i/978) + - GBA Hardware: RTC accuracy improvements + - GB Timer: Minor accuracy improvements + - GB Audio: Clock frame events on DIV + - GBA: Fix SharkPort saves for EEPROM games Misc: - GBA Timer: Use global cycles for timers - GBA: Extend oddly-sized ROMs to full address space (fixes mgba.io/i/722)
M include/mgba/internal/gb/audio.hinclude/mgba/internal/gb/audio.h

@@ -232,6 +232,8 @@ void GBAudioWriteNR50(struct GBAudio* audio, uint8_t);

void GBAudioWriteNR51(struct GBAudio* audio, uint8_t); void GBAudioWriteNR52(struct GBAudio* audio, uint8_t); +void GBAudioUpdateFrame(struct GBAudio* audio, struct mTiming* timing); + void GBAudioSamplePSG(struct GBAudio* audio, int16_t* left, int16_t* right); struct GBSerializedPSGState;
M include/mgba/internal/gba/savedata.hinclude/mgba/internal/gba/savedata.h

@@ -102,7 +102,7 @@ void GBASavedataDeinit(struct GBASavedata* savedata);

void GBASavedataMask(struct GBASavedata* savedata, struct VFile* vf, bool writeback); void GBASavedataUnmask(struct GBASavedata* savedata); -size_t GBASavedataSize(struct GBASavedata* savedata); +size_t GBASavedataSize(const struct GBASavedata* savedata); bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out); bool GBASavedataLoad(struct GBASavedata* savedata, struct VFile* in); void GBASavedataForceType(struct GBASavedata* savedata, enum SavedataType type, bool realisticTiming);
M src/gb/audio.csrc/gb/audio.c

@@ -109,9 +109,11 @@ mTimingDeschedule(audio->timing, &audio->ch3Event);

mTimingDeschedule(audio->timing, &audio->ch3Fade); mTimingDeschedule(audio->timing, &audio->ch4Event); mTimingDeschedule(audio->timing, &audio->sampleEvent); - mTimingSchedule(audio->timing, &audio->frameEvent, 0); if (audio->style != GB_AUDIO_GBA) { mTimingSchedule(audio->timing, &audio->sampleEvent, 0); + } + if (audio->style == GB_AUDIO_GBA) { + mTimingSchedule(audio->timing, &audio->frameEvent, 0); } audio->ch1 = (struct GBAudioSquareChannel) { .envelope = { .dead = 2 } }; audio->ch2 = (struct GBAudioSquareChannel) { .envelope = { .dead = 2 } };

@@ -486,7 +488,13 @@ }

void _updateFrame(struct mTiming* timing, void* user, uint32_t cyclesLate) { struct GBAudio* audio = user; + GBAudioUpdateFrame(audio, timing); + if (audio->style == GB_AUDIO_GBA) { + mTimingSchedule(timing, &audio->frameEvent, audio->timingFactor * FRAME_CYCLES - cyclesLate); + } +} +void GBAudioUpdateFrame(struct GBAudio* audio, struct mTiming* timing) { int frame = (audio->frame + 1) & 7; audio->frame = frame;

@@ -576,8 +584,6 @@ }

} break; } - - mTimingSchedule(timing, &audio->frameEvent, audio->timingFactor * FRAME_CYCLES - cyclesLate); } void GBAudioSamplePSG(struct GBAudio* audio, int16_t* left, int16_t* right) {

@@ -959,8 +965,10 @@ audio->playingCh3 = !!(*audio->nr52 & 0x0004);

audio->playingCh4 = !!(*audio->nr52 & 0x0008); audio->enable = GBAudioEnableGetEnable(*audio->nr52); - LOAD_32LE(when, 0, &state->ch1.nextFrame); - mTimingSchedule(audio->timing, &audio->frameEvent, when); + if (audio->style == GB_AUDIO_GBA) { + LOAD_32LE(when, 0, &state->ch1.nextFrame); + mTimingSchedule(audio->timing, &audio->frameEvent, when); + } LOAD_32LE(flags, 0, flagsIn); audio->frame = GBSerializedAudioFlagsGetFrame(flags);
M src/gb/io.csrc/gb/io.c

@@ -380,8 +380,17 @@ _writeSGBBits(gb, (value >> 4) & 3);

} break; case REG_TIMA: + if (value && mTimingUntil(&gb->timing, &gb->timer.irq) > 1) { + mTimingDeschedule(&gb->timing, &gb->timer.irq); + } + if (mTimingUntil(&gb->timing, &gb->timer.irq) == -1) { + return; + } + break; case REG_TMA: - // Handled transparently by the registers + if (mTimingUntil(&gb->timing, &gb->timer.irq) == -1) { + gb->memory.io[REG_TIMA] = value; + } break; case REG_TAC: value = GBTimerUpdateTAC(&gb->timer, value);
M src/gb/timer.csrc/gb/timer.c

@@ -27,8 +27,12 @@ // Make sure to trigger when the correct bit is a falling edge

if (timer->timaPeriod > 0 && (timer->internalDiv & (timer->timaPeriod - 1)) == timer->timaPeriod - 1) { ++timer->p->memory.io[REG_TIMA]; if (!timer->p->memory.io[REG_TIMA]) { - mTimingSchedule(&timer->p->timing, &timer->irq, 4 - cyclesLate); + mTimingSchedule(&timer->p->timing, &timer->irq, 7 - ((timer->p->cpu->executionState - cyclesLate) & 3)); } + } + int timingFactor = 0x3FF >> !timer->p->doubleSpeed; + if ((timer->internalDiv & timingFactor) == timingFactor) { + GBAudioUpdateFrame(&timer->p->audio, &timer->p->timing); } ++timer->internalDiv; timer->p->memory.io[REG_DIV] = timer->internalDiv >> 4;

@@ -69,12 +73,16 @@

void GBTimerDivReset(struct GBTimer* timer) { timer->nextDiv -= mTimingUntil(&timer->p->timing, &timer->event); mTimingDeschedule(&timer->p->timing, &timer->event); - _GBTimerDivIncrement(timer, (timer->p->cpu->executionState + 1) & 3); - if (timer->internalDiv & (timer->timaPeriod >> 1)) { + _GBTimerDivIncrement(timer, 0); + if (((timer->internalDiv << 1) | ((timer->nextDiv >> 3) & 1)) & timer->timaPeriod) { ++timer->p->memory.io[REG_TIMA]; if (!timer->p->memory.io[REG_TIMA]) { - mTimingSchedule(&timer->p->timing, &timer->irq, 4 - ((timer->p->cpu->executionState + 1) & 3)); + mTimingSchedule(&timer->p->timing, &timer->irq, 7 - (timer->p->cpu->executionState & 3)); } + } + int timingFactor = 0x200 >> !timer->p->doubleSpeed; + if (timer->internalDiv & 0x200) { + GBAudioUpdateFrame(&timer->p->audio, &timer->p->timing); } timer->p->memory.io[REG_DIV] = 0; timer->internalDiv = 0;

@@ -101,7 +109,7 @@ }

timer->nextDiv -= mTimingUntil(&timer->p->timing, &timer->event); mTimingDeschedule(&timer->p->timing, &timer->event); - _GBTimerDivIncrement(timer, (timer->p->cpu->executionState + 1) & 3); + _GBTimerDivIncrement(timer, (timer->p->cpu->executionState + 2) & 3); timer->nextDiv += GB_DMG_DIV_PERIOD; mTimingSchedule(&timer->p->timing, &timer->event, timer->nextDiv); } else {
M src/gba/gba.csrc/gba/gba.c

@@ -33,6 +33,9 @@

static const size_t GBA_ROM_MAGIC_OFFSET = 3; static const uint8_t GBA_ROM_MAGIC[] = { 0xEA }; +static const size_t GBA_ROM_MAGIC_OFFSET2 = 0xB2; +static const uint8_t GBA_ROM_MAGIC2[] = { 0x96 }; + static const size_t GBA_MB_MAGIC_OFFSET = 0xC0; static void GBAInit(void* cpu, struct mCPUComponent* component);

@@ -542,17 +545,48 @@ #endif

if (!vf) { return false; } + + uint8_t signature[sizeof(GBA_ROM_MAGIC) + sizeof(GBA_ROM_MAGIC2)]; if (vf->seek(vf, GBA_ROM_MAGIC_OFFSET, SEEK_SET) < 0) { return false; } - uint8_t signature[sizeof(GBA_ROM_MAGIC)]; - if (vf->read(vf, &signature, sizeof(signature)) != sizeof(signature)) { + if (vf->read(vf, &signature, sizeof(GBA_ROM_MAGIC)) != sizeof(GBA_ROM_MAGIC)) { + return false; + } + if (memcmp(signature, GBA_ROM_MAGIC, sizeof(GBA_ROM_MAGIC)) != 0) { + return false; + } + + if (vf->seek(vf, GBA_ROM_MAGIC_OFFSET2, SEEK_SET) < 0) { + return false; + } + if (vf->read(vf, &signature, sizeof(GBA_ROM_MAGIC2)) != sizeof(GBA_ROM_MAGIC2)) { return false; } + if (memcmp(signature, GBA_ROM_MAGIC2, sizeof(GBA_ROM_MAGIC2)) != 0) { + // If the signature byte is missing then we must be using an unfixed ROM + uint32_t buffer[0x9C / sizeof(uint32_t)]; + if (vf->seek(vf, 0x4, SEEK_SET) < 0) { + return false; + } + if (vf->read(vf, &buffer, sizeof(buffer)) != sizeof(buffer)) { + return false; + } + uint32_t bits = 0; + size_t i; + for (i = 0; i < sizeof(buffer) / sizeof(*buffer); ++i) { + bits |= buffer[i]; + } + if (bits) { + return false; + } + } + + if (GBAIsBIOS(vf)) { return false; } - return memcmp(signature, GBA_ROM_MAGIC, sizeof(signature)) == 0; + return true; } bool GBAIsMB(struct VFile* vf) {
M src/gba/hardware.csrc/gba/hardware.c

@@ -63,7 +63,7 @@ }

void GBAHardwareClear(struct GBACartridgeHardware* hw) { hw->devices = HW_NONE | (hw->devices & HW_GB_PLAYER_DETECTION); - hw->direction = GPIO_WRITE_ONLY; + hw->readWrite = GPIO_WRITE_ONLY; hw->pinState = 0; hw->direction = 0;

@@ -79,7 +79,7 @@ }

switch (address) { case GPIO_REG_DATA: hw->pinState &= ~hw->direction; - hw->pinState |= value; + hw->pinState |= value & hw->direction; _readPins(hw); break; case GPIO_REG_DIRECTION:

@@ -92,13 +92,13 @@ default:

mLOG(GBA_HW, WARN, "Invalid GPIO address"); } if (hw->readWrite) { - uint16_t old; - LOAD_16(old, 0, hw->gpioBase); - old &= ~hw->direction; - old |= hw->pinState; - STORE_16(old, 0, hw->gpioBase); + STORE_16(hw->pinState, 0, hw->gpioBase); + STORE_16(hw->direction, 2, hw->gpioBase); + STORE_16(hw->readWrite, 4, hw->gpioBase); } else { hw->gpioBase[0] = 0; + hw->gpioBase[1] = 0; + hw->gpioBase[2] = 0; } }

@@ -165,11 +165,15 @@ case 0:

if ((hw->pinState & 5) == 1) { hw->rtc.transferStep = 1; } + _outputPins(hw, 1); break; case 1: if ((hw->pinState & 5) == 5) { hw->rtc.transferStep = 2; + } else { + hw->rtc.transferStep = 0; } + _outputPins(hw, 5); break; case 2: if (!(hw->pinState & 1)) {

@@ -177,11 +181,7 @@ hw->rtc.bits &= ~(1 << hw->rtc.bitsRead);

hw->rtc.bits |= ((hw->pinState & 2) >> 1) << hw->rtc.bitsRead; } else { if (hw->pinState & 4) { - // GPIO direction should always != reading - if (hw->direction & 2) { - if (RTCCommandDataIsReading(hw->rtc.command)) { - mLOG(GBA_HW, GAME_ERROR, "Attempting to write to RTC while in read mode"); - } + if (!RTCCommandDataIsReading(hw->rtc.command)) { ++hw->rtc.bitsRead; if (hw->rtc.bitsRead == 8) { GBARTCProcessByte(&hw->rtc, hw->p->rtcSource);

@@ -193,7 +193,7 @@ if (hw->rtc.bitsRead == 8) {

--hw->rtc.bytesRemaining; if (hw->rtc.bytesRemaining <= 0) { hw->rtc.commandActive = 0; - hw->rtc.command = RTCCommandDataClearReading(hw->rtc.command); + hw->rtc.command = 0; } hw->rtc.bitsRead = 0; }

@@ -202,8 +202,9 @@ } else {

hw->rtc.bitsRead = 0; hw->rtc.bytesRemaining = 0; hw->rtc.commandActive = 0; - hw->rtc.command = RTCCommandDataClearReading(hw->rtc.command); - hw->rtc.transferStep = 0; + hw->rtc.command = 0; + hw->rtc.transferStep = hw->pinState & 1; + _outputPins(hw, 1); } } break;

@@ -269,6 +270,10 @@ }

unsigned GBARTCOutput(struct GBARTC* rtc) { uint8_t outputByte = 0; + if (!rtc->commandActive) { + mLOG(GBA_HW, GAME_ERROR, "Attempting to use RTC without an active command"); + return 0; + } switch (RTCCommandDataGetCommand(rtc->command)) { case RTC_STATUS1: outputByte = rtc->control;
M src/gba/savedata.csrc/gba/savedata.c

@@ -138,7 +138,7 @@ }

return true; } -size_t GBASavedataSize(struct GBASavedata* savedata) { +size_t GBASavedataSize(const struct GBASavedata* savedata) { switch (savedata->type) { case SAVEDATA_SRAM: return SIZE_CART_SRAM;
M src/gba/sharkport.csrc/gba/sharkport.c

@@ -130,8 +130,21 @@ case SAVEDATA_AUTODETECT:

goto cleanup; } - memcpy(gba->memory.savedata.data, &payload[0x1C], copySize); - gba->memory.savedata.vf && gba->memory.savedata.vf->sync(gba->memory.savedata.vf, gba->memory.savedata.data, size); + if (gba->memory.savedata.type == SAVEDATA_EEPROM) { + size_t i; + for (i = 0; i < copySize; i += 8) { + uint32_t lo, hi; + LOAD_32BE(lo, i + 0x1C, payload); + LOAD_32BE(hi, i + 0x20, payload); + STORE_32LE(hi, i, gba->memory.savedata.data); + STORE_32LE(lo, i + 4, gba->memory.savedata.data); + } + } else { + memcpy(gba->memory.savedata.data, &payload[0x1C], copySize); + } + if (gba->memory.savedata.vf) { + gba->memory.savedata.vf->sync(gba->memory.savedata.vf, gba->memory.savedata.data, size); + } free(payload); return true;

@@ -146,7 +159,7 @@ union {

char c[0x1C]; int32_t i; } buffer; - int32_t size = strlen(SHARKPORT_HEADER); + uint32_t size = strlen(SHARKPORT_HEADER); STORE_32(size, 0, &buffer.i); if (vf->write(vf, &buffer.i, 4) < 4) { return false;

@@ -187,22 +200,8 @@ return false;

} // Write payload - size = 0x1C; - switch (gba->memory.savedata.type) { - case SAVEDATA_SRAM: - size += SIZE_CART_SRAM; - break; - case SAVEDATA_FLASH512: - size += SIZE_CART_FLASH512; - break; - case SAVEDATA_FLASH1M: - size += SIZE_CART_FLASH1M; - break; - case SAVEDATA_EEPROM: - size += SIZE_CART_EEPROM; - break; - case SAVEDATA_FORCE_NONE: - case SAVEDATA_AUTODETECT: + size = 0x1C + GBASavedataSize(&gba->memory.savedata); + if (size == 0x1C) { return false; } STORE_32(size, 0, &buffer.i);

@@ -229,17 +228,24 @@ return false;

} uint32_t checksum = 0; - int i; + size_t i; for (i = 0; i < 0x1C; ++i) { checksum += buffer.c[i] << (checksum % 24); } - if (vf->write(vf, gba->memory.savedata.data, size) < size) { + + if (gba->memory.savedata.type == SAVEDATA_EEPROM) { + for (i = 0; i < size; ++i) { + char byte = gba->memory.savedata.data[i ^ 7]; + checksum += byte << (checksum % 24); + vf->write(vf, &byte, 1); + } + } else if (vf->write(vf, gba->memory.savedata.data, size) < size) { return false; - } - - for (i = 0; i < size; ++i) { - checksum += ((char) gba->memory.savedata.data[i]) << (checksum % 24); + } else { + for (i = 0; i < size; ++i) { + checksum += ((char) gba->memory.savedata.data[i]) << (checksum % 24); + } } STORE_32(checksum, 0, &buffer.i);
M src/platform/qt/DisplayGL.cppsrc/platform/qt/DisplayGL.cpp

@@ -5,7 +5,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this

* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "DisplayGL.h" -#if defined(BUILD_GL) || defined(BUILD_GLES) +#if defined(BUILD_GL) || defined(BUILD_GLES2) #include "CoreController.h"
M src/platform/qt/DisplayGL.hsrc/platform/qt/DisplayGL.h

@@ -5,7 +5,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this

* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #pragma once -#if defined(BUILD_GL) || defined(BUILD_GLES) +#if defined(BUILD_GL) || defined(BUILD_GLES2) #include "Display.h"
M src/platform/qt/SettingsView.cppsrc/platform/qt/SettingsView.cpp

@@ -291,13 +291,13 @@ m_ui.tabs->addItem(tr("Shortcuts"));

} SettingsView::~SettingsView() { -#if defined(BUILD_GL) || defined(BUILD_GLES) +#if defined(BUILD_GL) || defined(BUILD_GLES2) setShaderSelector(nullptr); #endif } void SettingsView::setShaderSelector(ShaderSelector* shaderSelector) { -#if defined(BUILD_GL) || defined(BUILD_GLES) +#if defined(BUILD_GL) || defined(BUILD_GLES2) if (m_shader) { auto items = m_ui.tabs->findItems(tr("Shaders"), Qt::MatchFixedString); for (const auto& item : items) {
M src/platform/qt/ShaderSelector.cppsrc/platform/qt/ShaderSelector.cpp

@@ -21,7 +21,7 @@ #include <mgba/core/version.h>

#include <mgba-util/vfs.h> #include "platform/video-backend.h" -#if defined(BUILD_GL) || defined(BUILD_GLES) +#if defined(BUILD_GL) || defined(BUILD_GLES2) #if !defined(_WIN32) || defined(USE_EPOXY) #include "platform/opengl/gles2.h"
M src/platform/qt/ShaderSelector.hsrc/platform/qt/ShaderSelector.h

@@ -5,7 +5,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this

* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #pragma once -#if defined(BUILD_GL) || defined(BUILD_GLES) +#if defined(BUILD_GL) || defined(BUILD_GLES2) #include <QDialog>
M src/platform/qt/Window.cppsrc/platform/qt/Window.cpp

@@ -852,7 +852,7 @@ m_display->stopDrawing();

detachWidget(m_display.get()); } m_display = std::move(std::unique_ptr<Display>(Display::create(this))); -#if defined(BUILD_GL) || defined(BUILD_GLES) +#if defined(BUILD_GL) || defined(BUILD_GLES2) m_shaderView.reset(); m_shaderView = std::make_unique<ShaderSelector>(m_display.get(), m_config); #endif

@@ -871,7 +871,7 @@

const mCoreOptions* opts = m_config->options(); m_display->lockAspectRatio(opts->lockAspectRatio); m_display->filter(opts->resampleVideo); -#if defined(BUILD_GL) || defined(BUILD_GLES) +#if defined(BUILD_GL) || defined(BUILD_GLES2) if (opts->shader) { struct VDir* shader = VDirOpen(opts->shader); if (shader && m_display->supportsShaders()) {
M src/platform/qt/Window.hsrc/platform/qt/Window.h

@@ -187,7 +187,7 @@ QList<QString> m_mruFiles;

QMenu* m_mruMenu = nullptr; QMenu* m_videoLayers; QMenu* m_audioChannels; -#if defined(BUILD_GL) || defined(BUILD_GLES) +#if defined(BUILD_GL) || defined(BUILD_GLES2) std::unique_ptr<ShaderSelector> m_shaderView; #endif bool m_fullscreenOnStart = false;
M src/platform/sdl/gles2-sdl.csrc/platform/sdl/gles2-sdl.c

@@ -123,15 +123,15 @@ struct mCoreThread* context = user;

SDL_Event event; struct VideoBackend* v = &renderer->gl2.d; - while (context->state < THREAD_EXITING) { + while (mCoreThreadIsActive(context)) { while (SDL_PollEvent(&event)) { mSDLHandleEvent(context, &renderer->player, &event); } - if (mCoreSyncWaitFrameStart(&context->sync)) { + if (mCoreSyncWaitFrameStart(&context->impl->sync)) { v->postFrame(v, renderer->outputBuffer); } - mCoreSyncWaitFrameEnd(&context->sync); + mCoreSyncWaitFrameEnd(&context->impl->sync); v->drawFrame(v); #ifdef BUILD_RASPI eglSwapBuffers(renderer->display, renderer->surface);
M src/platform/wii/main.csrc/platform/wii/main.c

@@ -733,6 +733,7 @@ }

void _unpaused(struct mGUIRunner* runner) { u32 level = 0; + VIDEO_WaitVSync(); _CPU_ISR_Disable(level); referenceRetraceCount = retraceCount; _CPU_ISR_Restore(level);