From 271c6dc1298f07eb34338f886fe2dbdcfdc04ceb Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 16 Sep 2024 03:51:08 -0700 Subject: [PATCH 001/114] Res: Update gba-colors shader (closes #2976) --- res/shaders/gba-color.shader/gba-color.fs | 35 +++++++---------------- res/shaders/gba-color.shader/gba-color.vs | 34 ++++++++++++++++++++++ res/shaders/gba-color.shader/manifest.ini | 8 ++++++ 3 files changed, 53 insertions(+), 24 deletions(-) create mode 100644 res/shaders/gba-color.shader/gba-color.vs diff --git a/res/shaders/gba-color.shader/gba-color.fs b/res/shaders/gba-color.shader/gba-color.fs index 09177ddfe..461228c20 100644 --- a/res/shaders/gba-color.shader/gba-color.fs +++ b/res/shaders/gba-color.shader/gba-color.fs @@ -1,34 +1,21 @@ +// Shader that replicates the LCD Colorspace from Gameboy Advance -- varying vec2 texCoord; +varying mat4 profile; uniform sampler2D tex; uniform vec2 texSize; uniform float darken_screen; -const float target_gamma = 2.2; -const float display_gamma = 2.5; -const float sat = 1.0; -const float lum = 0.99; -const float contrast = 1.0; -const vec3 bl = vec3(0.0, 0.0, 0.0); -const vec3 r = vec3(0.84, 0.09, 0.15); -const vec3 g = vec3(0.18, 0.67, 0.10); -const vec3 b = vec3(0.0, 0.26, 0.73); +const float target_gamma = 2.0; +const float display_gamma = 2.0; void main() { + // bring out our stored luminance value + float lum = profile[3].w; + + // our adjustments need to happen in linear gamma vec4 screen = pow(texture2D(tex, texCoord), vec4(target_gamma + darken_screen)).rgba; - vec4 avglum = vec4(0.5); - screen = mix(screen, avglum, (1.0 - contrast)); - - mat4 color = mat4( r.r, r.g, r.b, 0.0, - g.r, g.g, g.b, 0.0, - b.r, b.g, b.b, 0.0, - bl.r, bl.g, bl.b, 1.0); - - mat4 adjust = mat4( (1.0 - sat) * 0.3086 + sat, (1.0 - sat) * 0.3086, (1.0 - sat) * 0.3086, 1.0, - (1.0 - sat) * 0.6094, (1.0 - sat) * 0.6094 + sat, (1.0 - sat) * 0.6094, 1.0, - (1.0 - sat) * 0.0820, (1.0 - sat) * 0.0820, (1.0 - sat) * 0.0820 + sat, 1.0, - 0.0, 0.0, 0.0, 1.0); - color *= adjust; + screen = clamp(screen * lum, 0.0, 1.0); - screen = color * screen; - gl_FragColor = pow(screen, vec4(1.0 / display_gamma + (darken_screen * 0.125))); + screen = profile * screen; + gl_FragColor = pow(screen, vec4(1.0 / display_gamma)); } diff --git a/res/shaders/gba-color.shader/gba-color.vs b/res/shaders/gba-color.shader/gba-color.vs new file mode 100644 index 000000000..bbf406278 --- /dev/null +++ b/res/shaders/gba-color.shader/gba-color.vs @@ -0,0 +1,34 @@ +uniform int color_mode; +attribute vec4 position; +varying vec2 texCoord; +varying mat4 profile; + +const mat4 GBA_sRGB = mat4( + 0.80, 0.135, 0.195, 0.0, //red channel + 0.275, 0.64, 0.155, 0.0, //green channel + -0.075, 0.225, 0.65, 0.0, //blue channel + 0.0, 0.0, 0.0, 0.93 //alpha channel +); + +const mat4 GBA_DCI = mat4( + 0.685, 0.16, 0.20, 0.0, //red channel + 0.34, 0.629, 0.19, 0.0, //green channel + -0.025, 0.211, 0.61, 0.0, //blue channel + 0.0, 0.0, 0.0, 0.975 //alpha channel +); + +const mat4 GBA_Rec2020 = mat4( + 0.555, 0.1825, 0.20, 0.0, //red channel + 0.395, 0.61, 0.195, 0.0, //green channel + 0.05, 0.2075, 0.605, 0.0, //blue channel + 0.0, 0.0, 0.0, 1.0 //alpha channel +); + +void main() { + if (color_mode == 1) profile = GBA_sRGB; + else if (color_mode == 2) profile = GBA_DCI; + else if (color_mode == 3) profile = GBA_Rec2020; + + gl_Position = position; + texCoord = (position.st + vec2(1.0, 1.0)) * vec2(0.5, 0.5); +} diff --git a/res/shaders/gba-color.shader/manifest.ini b/res/shaders/gba-color.shader/manifest.ini index 8f9735aa3..ed16900cd 100644 --- a/res/shaders/gba-color.shader/manifest.ini +++ b/res/shaders/gba-color.shader/manifest.ini @@ -6,6 +6,7 @@ passes=1 [pass.0] fragmentShader=gba-color.fs +vertexShader=gba-color.vs blend=1 width=-1 height=-1 @@ -14,3 +15,10 @@ height=-1 type=float default=0.5 readableName=Darken Screen + +[pass.0.uniform.color_mode] +type=int +default=1 +min=1 +max=3 +readableName=Color Profile (1=sRGB, 2=DCI, 3=Rec2020) From 11787df6cd48e93ef4552b1b61393d5a734fe3c0 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 16 Sep 2024 03:58:39 -0700 Subject: [PATCH 002/114] Res: Port NSO-gba-colors shader (closes #2834) --- CHANGES | 1 + res/shaders/nso-gba-color.shader/manifest.ini | 24 +++++++++++++ .../nso-gba-color.shader/nso-gba-color.fs | 21 ++++++++++++ .../nso-gba-color.shader/nso-gba-color.vs | 34 +++++++++++++++++++ 4 files changed, 80 insertions(+) create mode 100644 res/shaders/nso-gba-color.shader/manifest.ini create mode 100644 res/shaders/nso-gba-color.shader/nso-gba-color.fs create mode 100644 res/shaders/nso-gba-color.shader/nso-gba-color.vs diff --git a/CHANGES b/CHANGES index 3aef9e246..ef075bd9c 100644 --- a/CHANGES +++ b/CHANGES @@ -52,6 +52,7 @@ Misc: - Qt: Remove maligned double-click-to-fullscreen shortcut (closes mgba.io/i/2632) - Qt: Pass logging context through to video proxy thread (fixes mgba.io/i/3095) - Qt: Show maker code and game version in ROM info + - Res: Port NSO-gba-colors shader (closes mgba.io/i/2834) - Scripting: Add `callbacks:oneshot` for single-call callbacks - Switch: Add bilinear filtering option (closes mgba.io/i/3111) - Vita: Add imc0 and xmc0 mount point support diff --git a/res/shaders/nso-gba-color.shader/manifest.ini b/res/shaders/nso-gba-color.shader/manifest.ini new file mode 100644 index 000000000..42faa25bd --- /dev/null +++ b/res/shaders/nso-gba-color.shader/manifest.ini @@ -0,0 +1,24 @@ +[shader] +name=NSO GBA Color +author=Pokefan531 and hunterk +description=Shader that replicates the Nintendo Switch Online's GBA color filter. +passes=1 + +[pass.0] +fragmentShader=nso-gba-color.fs +vertexShader=nso-gba-color.vs +blend=1 +width=-1 +height=-1 + +[pass.0.uniform.darken_screen] +type=float +default=0.8 +readableName=Darken Screen + +[pass.0.uniform.color_mode] +type=int +default=1 +min=1 +max=3 +readableName=Color Profile (1=sRGB, 2=DCI, 3=Rec2020) diff --git a/res/shaders/nso-gba-color.shader/nso-gba-color.fs b/res/shaders/nso-gba-color.shader/nso-gba-color.fs new file mode 100644 index 000000000..8d8a0ec92 --- /dev/null +++ b/res/shaders/nso-gba-color.shader/nso-gba-color.fs @@ -0,0 +1,21 @@ +// Shader that replicates the LCD Colorspace from Gameboy Advance -- +varying vec2 texCoord; +varying mat4 profile; +uniform sampler2D tex; +uniform vec2 texSize; + +uniform float darken_screen; +const float target_gamma = 2.2; +const float display_gamma = 2.2; + +void main() { + // bring out our stored luminance value + float lum = profile[3].w; + + // our adjustments need to happen in linear gamma + vec4 screen = pow(texture2D(tex, texCoord), vec4(target_gamma + darken_screen)).rgba; + + screen = clamp(screen * lum, 0.0, 1.0); + screen = profile * screen; + gl_FragColor = pow(screen, vec4(1.0 / display_gamma)); +} diff --git a/res/shaders/nso-gba-color.shader/nso-gba-color.vs b/res/shaders/nso-gba-color.shader/nso-gba-color.vs new file mode 100644 index 000000000..c9c89181b --- /dev/null +++ b/res/shaders/nso-gba-color.shader/nso-gba-color.vs @@ -0,0 +1,34 @@ +uniform int color_mode; +attribute vec4 position; +varying vec2 texCoord; +varying mat4 profile; + +const mat4 GBA_sRGB = mat4( + 0.865, 0.0575, 0.0575, 0.0, //red channel + 0.1225, 0.925, 0.1225, 0.0, //green channel + 0.0125, 0.0125, 0.82, 0.0, //blue channel + 0.0, 0.0, 0.0, 1.0 //alpha channel +); + +const mat4 GBA_DCI = mat4( + 0.72, 0.0875, 0.0725, 0.0, //red channel + 0.2675, 0.9, 0.185, 0.0, //green channel + 0.0125, 0.0125, 0.7425, 0.0, //blue channel + 0.0, 0.0, 0.0, 1.0 //alpha channel +); + +const mat4 GBA_Rec2020 = mat4( + 0.57, 0.115, 0.0725, 0.0, //red channel + 0.3825, 0.8625, 0.195, 0.0, //green channel + 0.0475, 0.0225, 0.7325, 0.0, //blue channel + 0.0, 0.0, 0.0, 1.0 //alpha channel +); + +void main() { + if (color_mode == 1) profile = GBA_sRGB; + else if (color_mode == 2) profile = GBA_DCI; + else if (color_mode == 3) profile = GBA_Rec2020; + + gl_Position = position; + texCoord = (position.st + vec2(1.0, 1.0)) * vec2(0.5, 0.5); +} From c64dbd6631d0eed88b6ef1cbceddc47cde3000ec Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 19 Sep 2024 00:05:58 -0700 Subject: [PATCH 003/114] Qt: Make window corners square on Windows 11 (fixes #3285) --- CHANGES | 1 + src/platform/qt/Window.cpp | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/CHANGES b/CHANGES index ef075bd9c..f225b8b15 100644 --- a/CHANGES +++ b/CHANGES @@ -52,6 +52,7 @@ Misc: - Qt: Remove maligned double-click-to-fullscreen shortcut (closes mgba.io/i/2632) - Qt: Pass logging context through to video proxy thread (fixes mgba.io/i/3095) - Qt: Show maker code and game version in ROM info + - Qt: Make window corners square on Windows 11 (fixes mgba.io/i/3285) - Res: Port NSO-gba-colors shader (closes mgba.io/i/2834) - Scripting: Add `callbacks:oneshot` for single-call callbacks - Switch: Add bilinear filtering option (closes mgba.io/i/3111) diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 010dde782..0e3d73b00 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -14,6 +14,10 @@ #include #include +#ifdef Q_OS_WIN +#include +#endif + #ifdef USE_SQLITE3 #include "ArchiveInspector.h" #include "library/LibraryController.h" @@ -717,6 +721,11 @@ void Window::showEvent(QShowEvent* event) { return; } m_wasOpened = true; +#ifdef Q_OS_WIN + HWND hwnd = reinterpret_cast(winId()); + DWM_WINDOW_CORNER_PREFERENCE cornerPref = DWMWCP_DONOTROUND; + DwmSetWindowAttribute(hwnd, DWMWA_WINDOW_CORNER_PREFERENCE, &cornerPref, sizeof(cornerPref)); +#endif if (m_initialSize.isValid()) { resizeFrame(m_initialSize); } From f75f9fd5fdf0868a01732a60aa5b971e828ebecd Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 19 Sep 2024 01:01:35 -0700 Subject: [PATCH 004/114] Appveyor: Use Windows 11 SDK --- .appveyor.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 552fdbd2f..586eb059e 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -12,7 +12,11 @@ install: - vcpkg --no-dry-run upgrade - rd /Q /S C:\Tools\vcpkg\buildtrees before_build: -- cmake . -DCMAKE_PREFIX_PATH=C:\Qt\5.15\msvc2019_64 -DCMAKE_TOOLCHAIN_FILE=C:\Tools\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-release -DCMAKE_CONFIGURATION_TYPES=Release +- cmake . -DCMAKE_PREFIX_PATH=C:\Qt\5.15\msvc2019_64 \ + -DCMAKE_TOOLCHAIN_FILE=C:\Tools\vcpkg\scripts\buildsystems\vcpkg.cmake \ + -DVCPKG_TARGET_TRIPLET=x64-windows-release \ + -DCMAKE_CONFIGURATION_TYPES=Release \ + -DCMAKE_SYSTEM_VERSION=10.0.22000.1 build: parallel: true project: mGBA.sln From 49fa1a30c581cd868365c76d6136885a1bdcf238 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 19 Sep 2024 02:09:20 -0700 Subject: [PATCH 005/114] Qt: Fix Windows shared build --- src/platform/qt/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 041ad0830..51aa22401 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -427,6 +427,7 @@ set_target_properties(${BINARY_NAME}-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${PR if(WIN32) set_target_properties(${BINARY_NAME}-qt PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") + target_link_libraries(${BINARY_NAME}-qt dwmapi) if(NOT MSVC) target_link_libraries(${BINARY_NAME}-qt -municode) endif() @@ -445,7 +446,7 @@ if(QT_STATIC) if(CMAKE_CROSSCOMPILING) set(QWINDOWS_DEPS ${QT}EventDispatcherSupport ${QT}FontDatabaseSupport ${QT}ThemeSupport ${QT}WindowsUIAutomationSupport) endif() - list(APPEND QT_LIBRARIES ${QT}::QWindowsIntegrationPlugin ${QWINDOWS_DEPS} amstrmid dwmapi uxtheme imm32 -static-libgcc -static-libstdc++) + list(APPEND QT_LIBRARIES ${QT}::QWindowsIntegrationPlugin ${QWINDOWS_DEPS} amstrmid uxtheme imm32 -static-libgcc -static-libstdc++) set_target_properties(${QT}::Core PROPERTIES INTERFACE_LINK_LIBRARIES "${QTPCRE};version;winmm;ssl;crypto;ws2_32;iphlpapi;crypt32;userenv;netapi32;wtsapi32") set_target_properties(${QT}::Gui PROPERTIES INTERFACE_LINK_LIBRARIES ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY}) elseif(APPLE) From 1636078b34195b9801748eed68a7641771b7ef96 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 20 Sep 2024 03:12:06 -0700 Subject: [PATCH 006/114] GBA I/O: Fix audio register 8-bit write behavior (fixes #3086) --- CHANGES | 1 + src/gba/io.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 98 insertions(+), 9 deletions(-) diff --git a/CHANGES b/CHANGES index f225b8b15..9a1694d69 100644 --- a/CHANGES +++ b/CHANGES @@ -16,6 +16,7 @@ Emulation fixes: - GBA: Add baseline CP0 (Wii U VC) and CP1 (DCC) implementations - GBA GPIO: Fix gyro read-out start (fixes mgba.io/i/3141) - GBA I/O: Fix HALTCNT access behavior (fixes mgba.io/i/2309) + - GBA I/O: Fix audio register 8-bit write behavior (fixes mgba.io/i/3086) - GBA Serialize: Fix some minor save state edge cases - GBA SIO: Fix MULTI mode SIOCNT bit 7 writes on secondary GBAs (fixes mgba.io/i/3110) - GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722) diff --git a/src/gba/io.c b/src/gba/io.c index 397eb9253..8853e4d6f 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -326,17 +326,19 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) { break; case GBA_REG_SOUND1CNT_HI: GBAAudioWriteSOUND1CNT_HI(&gba->audio, value); + value &= 0xFFC0; break; case GBA_REG_SOUND1CNT_X: GBAAudioWriteSOUND1CNT_X(&gba->audio, value); - value &= 0x47FF; + value &= 0x4000; break; case GBA_REG_SOUND2CNT_LO: GBAAudioWriteSOUND2CNT_LO(&gba->audio, value); + value &= 0xFFC0; break; case GBA_REG_SOUND2CNT_HI: GBAAudioWriteSOUND2CNT_HI(&gba->audio, value); - value &= 0x47FF; + value &= 0x4000; break; case GBA_REG_SOUND3CNT_LO: GBAAudioWriteSOUND3CNT_LO(&gba->audio, value); @@ -344,16 +346,15 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) { break; case GBA_REG_SOUND3CNT_HI: GBAAudioWriteSOUND3CNT_HI(&gba->audio, value); - value &= 0xE03F; + value &= 0xE000; break; case GBA_REG_SOUND3CNT_X: GBAAudioWriteSOUND3CNT_X(&gba->audio, value); - // TODO: The low bits need to not be readable, but still 8-bit writable - value &= 0x47FF; + value &= 0x4000; break; case GBA_REG_SOUND4CNT_LO: GBAAudioWriteSOUND4CNT_LO(&gba->audio, value); - value &= 0xFF3F; + value &= 0xFF00; break; case GBA_REG_SOUND4CNT_HI: GBAAudioWriteSOUND4CNT_HI(&gba->audio, value); @@ -585,9 +586,96 @@ void GBAIOWrite8(struct GBA* gba, uint32_t address, uint8_t value) { if (address > GBA_SIZE_IO) { return; } - uint16_t value16 = value << (8 * (address & 1)); - value16 |= (gba->memory.io[(address & (GBA_SIZE_IO - 1)) >> 1]) & ~(0xFF << (8 * (address & 1))); - GBAIOWrite(gba, address & 0xFFFFFFFE, value16); + uint16_t value16; + + switch (address) { + case GBA_REG_SOUND1CNT_HI: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR11(&gba->audio.psg, value); + gba->memory.io[GBA_REG(SOUND1CNT_HI)] &= 0xFF00; + gba->memory.io[GBA_REG(SOUND1CNT_HI)] |= value & 0xC0; + break; + case GBA_REG_SOUND1CNT_HI + 1: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR12(&gba->audio.psg, value); + gba->memory.io[GBA_REG(SOUND1CNT_HI)] &= 0x00C0; + gba->memory.io[GBA_REG(SOUND1CNT_HI)] |= value << 8; + break; + case GBA_REG_SOUND1CNT_X: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR13(&gba->audio.psg, value); + break; + case GBA_REG_SOUND1CNT_X + 1: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR14(&gba->audio.psg, value); + gba->memory.io[GBA_REG(SOUND1CNT_X)] = (value & 0x40) << 8; + break; + case GBA_REG_SOUND2CNT_LO: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR21(&gba->audio.psg, value); + gba->memory.io[GBA_REG(SOUND2CNT_LO)] &= 0xFF00; + gba->memory.io[GBA_REG(SOUND2CNT_LO)] |= value & 0xC0; + break; + case GBA_REG_SOUND2CNT_LO + 1: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR22(&gba->audio.psg, value); + gba->memory.io[GBA_REG(SOUND2CNT_LO)] &= 0x00C0; + gba->memory.io[GBA_REG(SOUND2CNT_LO)] |= value << 8; + break; + case GBA_REG_SOUND2CNT_HI: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR23(&gba->audio.psg, value); + break; + case GBA_REG_SOUND2CNT_HI + 1: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR24(&gba->audio.psg, value); + gba->memory.io[GBA_REG(SOUND2CNT_HI)] = (value & 0x40) << 8; + break; + case GBA_REG_SOUND3CNT_HI: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR31(&gba->audio.psg, value); + break; + case GBA_REG_SOUND3CNT_HI + 1: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + gba->audio.psg.ch3.volume = GBAudioRegisterBankVolumeGetVolumeGBA(value); + gba->memory.io[GBA_REG(SOUND3CNT_HI)] = (value & 0xE0) << 8; + break; + case GBA_REG_SOUND3CNT_X: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR33(&gba->audio.psg, value); + break; + case GBA_REG_SOUND3CNT_X + 1: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR34(&gba->audio.psg, value); + gba->memory.io[GBA_REG(SOUND3CNT_X)] = (value & 0x40) << 8; + break; + case GBA_REG_SOUND4CNT_LO: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR41(&gba->audio.psg, value); + break; + case GBA_REG_SOUND4CNT_LO + 1: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR42(&gba->audio.psg, value); + gba->memory.io[GBA_REG(SOUND4CNT_LO)] = value << 8; + break; + case GBA_REG_SOUND4CNT_HI: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR43(&gba->audio.psg, value); + gba->memory.io[GBA_REG(SOUND4CNT_HI)] &= 0x4000; + gba->memory.io[GBA_REG(SOUND4CNT_HI)] |= value; + break; + case GBA_REG_SOUND4CNT_HI + 1: + GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); + GBAudioWriteNR44(&gba->audio.psg, value); + gba->memory.io[GBA_REG(SOUND4CNT_HI)] &= 0x00FF; + gba->memory.io[GBA_REG(SOUND4CNT_HI)] = (value & 0x40) << 8; + break; + default: + value16 = value << (8 * (address & 1)); + value16 |= (gba->memory.io[(address & (GBA_SIZE_IO - 1)) >> 1]) & ~(0xFF << (8 * (address & 1))); + GBAIOWrite(gba, address & 0xFFFFFFFE, value16); + break; + } } void GBAIOWrite32(struct GBA* gba, uint32_t address, uint32_t value) { From c564a20970312ab463f5fcee7f8b72f78380b7c0 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 20 Sep 2024 23:21:14 -0700 Subject: [PATCH 007/114] GBA I/O: Fix 8-bit NR44 write --- src/gba/io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gba/io.c b/src/gba/io.c index 8853e4d6f..ea9d8d8b0 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -668,7 +668,7 @@ void GBAIOWrite8(struct GBA* gba, uint32_t address, uint8_t value) { GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing)); GBAudioWriteNR44(&gba->audio.psg, value); gba->memory.io[GBA_REG(SOUND4CNT_HI)] &= 0x00FF; - gba->memory.io[GBA_REG(SOUND4CNT_HI)] = (value & 0x40) << 8; + gba->memory.io[GBA_REG(SOUND4CNT_HI)] |= (value & 0x40) << 8; break; default: value16 = value << (8 * (address & 1)); From 4b0b6b5d37406f1bb8a4d58ec4a378d756082569 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 26 Sep 2024 21:22:08 -0700 Subject: [PATCH 008/114] Scripting: Fix readRegister return type --- src/core/scripting.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/scripting.c b/src/core/scripting.c index a00d5cbdc..dc56c1783 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -520,7 +520,7 @@ mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite16, 2, U32, address, U16, va mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite32, 2, U32, address, U32, value); // Register functions -mSCRIPT_DECLARE_STRUCT_METHOD(mCore, WSTR, readRegister, _mScriptCoreReadRegister, 1, CHARP, regName); +mSCRIPT_DECLARE_STRUCT_METHOD(mCore, WRAPPER, readRegister, _mScriptCoreReadRegister, 1, CHARP, regName); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mCore, writeRegister, _mScriptCoreWriteRegister, 2, CHARP, regName, S32, value); // Savestate functions From 4cfa9c65457e92ffce0a49985e9f6d4a319482d1 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 12 Sep 2024 02:40:03 -0700 Subject: [PATCH 009/114] Python: Remove SIO API pending revamp --- src/platform/python/mgba/gba.py | 66 ------------------------- src/platform/python/sio.c | 88 --------------------------------- src/platform/python/sio.h | 42 ---------------- 3 files changed, 196 deletions(-) delete mode 100644 src/platform/python/sio.c delete mode 100644 src/platform/python/sio.h diff --git a/src/platform/python/mgba/gba.py b/src/platform/python/mgba/gba.py index 109de4d50..0c249cec5 100644 --- a/src/platform/python/mgba/gba.py +++ b/src/platform/python/mgba/gba.py @@ -52,72 +52,6 @@ class GBA(Core): super(GBA, self)._load() self.memory = GBAMemory(self._core, self._native.memory.romSize) - def attach_sio(self, link, mode=lib.GBA_SIO_MULTI): - self._sio.add(mode) - lib.GBASIOSetDriver(ffi.addressof(self._native.sio), link._native, mode) - - def __del__(self): - for mode in self._sio: - lib.GBASIOSetDriver(ffi.addressof(self._native.sio), ffi.NULL, mode) - - -create_callback("GBASIOPythonDriver", "init") -create_callback("GBASIOPythonDriver", "deinit") -create_callback("GBASIOPythonDriver", "load") -create_callback("GBASIOPythonDriver", "unload") -create_callback("GBASIOPythonDriver", "writeRegister") - - -class GBASIODriver(object): - def __init__(self): - super(GBASIODriver, self).__init__() - - self._handle = ffi.new_handle(self) - self._native = ffi.gc(lib.GBASIOPythonDriverCreate(self._handle), lib.free) - - def init(self): - return True - - def deinit(self): - pass - - def load(self): - return True - - def unload(self): - return True - - def write_register(self, address, value): - return value - - -class GBASIOJOYDriver(GBASIODriver): - RESET = lib.JOY_RESET - POLL = lib.JOY_POLL - TRANS = lib.JOY_TRANS - RECV = lib.JOY_RECV - - def __init__(self): - super(GBASIOJOYDriver, self).__init__() - - self._native = ffi.gc(lib.GBASIOJOYPythonDriverCreate(self._handle), lib.free) - - def send_command(self, cmd, data): - buffer = ffi.new('uint8_t[5]') - try: - buffer[0] = data[0] - buffer[1] = data[1] - buffer[2] = data[2] - buffer[3] = data[3] - buffer[4] = data[4] - except IndexError: - pass - - outlen = lib.GBASIOJOYSendCommand(self._native, cmd, buffer) - if outlen > 0 and outlen <= 5: - return bytes(buffer[0:outlen]) - return None - class GBAMemory(Memory): def __init__(self, core, romSize=lib.SIZE_CART0): diff --git a/src/platform/python/sio.c b/src/platform/python/sio.c deleted file mode 100644 index fbb407831..000000000 --- a/src/platform/python/sio.c +++ /dev/null @@ -1,88 +0,0 @@ -/* Copyright (c) 2013-2017 Jeffrey Pfau - * - * This Source Code Form is subject to the terms of the Mozilla Public - * 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 - -#define CREATE_SHIM(PLAT, NAME, RETURN) \ - RETURN _py ## PLAT ## SIOPythonDriver ## NAME (void* driver); \ - static RETURN _py ## PLAT ## SIOPythonDriver ## NAME ## Shim(struct PLAT ## SIODriver* driver) { \ - struct PLAT ## SIODriver* py = (struct PLAT ## SIODriver*) driver; \ - return _py ## PLAT ## SIOPythonDriver ## NAME(py); \ - } - -#define CREATE_SHIM_ARGS(PLAT, NAME, RETURN, TYPES, ...) \ - RETURN _py ## PLAT ## SIOPythonDriver ## NAME TYPES; \ - static RETURN _py ## PLAT ## SIOPythonDriver ## NAME ## Shim TYPES { \ - struct PLAT ## SIODriver* py = (struct PLAT ## SIODriver*) driver; \ - return _py ## PLAT ## SIOPythonDriver ## NAME(py, __VA_ARGS__); \ - } - -#ifdef M_CORE_GBA - -#include - -struct GBASIOPythonDriver { - struct GBASIODriver d; - void* pyobj; -}; - -CREATE_SHIM(GBA, Init, bool); -CREATE_SHIM(GBA, Deinit, void); -CREATE_SHIM(GBA, Load, bool); -CREATE_SHIM(GBA, Unload, bool); -CREATE_SHIM_ARGS(GBA, WriteRegister, uint16_t, (struct GBASIODriver* driver, uint32_t address, uint16_t value), address, value); - -struct GBASIODriver* GBASIOPythonDriverCreate(void* pyobj) { - struct GBASIOPythonDriver* driver = malloc(sizeof(*driver)); - driver->d.init = _pyGBASIOPythonDriverInitShim; - driver->d.deinit = _pyGBASIOPythonDriverDeinitShim; - driver->d.load = _pyGBASIOPythonDriverLoadShim; - driver->d.unload = _pyGBASIOPythonDriverUnloadShim; - driver->d.writeRegister = _pyGBASIOPythonDriverWriteRegisterShim; - - driver->pyobj = pyobj; - return &driver->d; -} - -struct GBASIODriver* GBASIOJOYPythonDriverCreate(void* pyobj) { - struct GBASIOPythonDriver* driver = malloc(sizeof(*driver)); - GBASIOJOYCreate(&driver->d); - driver->d.init = _pyGBASIOPythonDriverInitShim; - driver->d.deinit = _pyGBASIOPythonDriverDeinitShim; - driver->d.load = _pyGBASIOPythonDriverLoadShim; - driver->d.unload = _pyGBASIOPythonDriverUnloadShim; - - driver->pyobj = pyobj; - return &driver->d; -} - -#endif - -#ifdef M_CORE_GB - -#include - -struct GBSIOPythonDriver { - struct GBSIODriver d; - void* pyobj; -}; - -CREATE_SHIM(GB, Init, bool); -CREATE_SHIM(GB, Deinit, void); -CREATE_SHIM_ARGS(GB, WriteSB, void, (struct GBSIODriver* driver, uint8_t value), value); -CREATE_SHIM_ARGS(GB, WriteSC, uint8_t, (struct GBSIODriver* driver, uint8_t value), value); - -struct GBSIODriver* GBSIOPythonDriverCreate(void* pyobj) { - struct GBSIOPythonDriver* driver = malloc(sizeof(*driver)); - driver->d.init = _pyGBSIOPythonDriverInitShim; - driver->d.deinit = _pyGBSIOPythonDriverDeinitShim; - driver->d.writeSB = _pyGBSIOPythonDriverWriteSBShim; - driver->d.writeSC = _pyGBSIOPythonDriverWriteSCShim; - - driver->pyobj = pyobj; - return &driver->d; -} - -#endif diff --git a/src/platform/python/sio.h b/src/platform/python/sio.h deleted file mode 100644 index 23b059427..000000000 --- a/src/platform/python/sio.h +++ /dev/null @@ -1,42 +0,0 @@ -/* Copyright (c) 2013-2017 Jeffrey Pfau - * - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ -#ifdef M_CORE_GBA - -#include - -struct GBASIOPythonDriver { - struct GBASIODriver d; - void* pyobj; -}; - -struct GBASIODriver* GBASIOPythonDriverCreate(void* pyobj); -struct GBASIODriver* GBASIOJOYPythonDriverCreate(void* pyobj); - -PYEXPORT bool _pyGBASIOPythonDriverInit(void* driver); -PYEXPORT void _pyGBASIOPythonDriverDeinit(void* driver); -PYEXPORT bool _pyGBASIOPythonDriverLoad(void* driver); -PYEXPORT bool _pyGBASIOPythonDriverUnload(void* driver); -PYEXPORT uint16_t _pyGBASIOPythonDriverWriteRegister(void* driver, uint32_t address, uint16_t value); - -#endif - -#ifdef M_CORE_GB - -#include - -struct GBSIOPythonDriver { - struct GBSIODriver d; - void* pyobj; -}; - -struct GBSIODriver* GBSIOPythonDriverCreate(void* pyobj); - -PYEXPORT bool _pyGBSIOPythonDriverInit(void* driver); -PYEXPORT void _pyGBSIOPythonDriverDeinit(void* driver); -PYEXPORT void _pyGBSIOPythonDriverWriteSB(void* driver, uint8_t value); -PYEXPORT uint8_t _pyGBSIOPythonDriverWriteSC(void* driver, uint8_t value); - -#endif From 451da0f8a4d486987192e1c34348ad1fa3ab08fe Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 16 May 2024 15:11:19 -0700 Subject: [PATCH 010/114] GBA SIO: Start adding new SIO callbacks for revamped API --- include/mgba/gba/interface.h | 5 ++++ src/gba/extra/battlechip.c | 22 +++++++++++++++ src/gba/sio.c | 9 +++++++ src/gba/sio/gbp.c | 24 ++++++++++++++--- src/gba/sio/joybus.c | 16 +++++++++++ src/gba/sio/lockstep.c | 52 +++++++++++++++++++++++++++++++++++- 6 files changed, 123 insertions(+), 5 deletions(-) diff --git a/include/mgba/gba/interface.h b/include/mgba/gba/interface.h index 97e0a5b9c..6b663b4a7 100644 --- a/include/mgba/gba/interface.h +++ b/include/mgba/gba/interface.h @@ -110,8 +110,13 @@ struct GBASIODriver { bool (*init)(struct GBASIODriver* driver); void (*deinit)(struct GBASIODriver* driver); + void (*reset)(struct GBASIODriver* driver); bool (*load)(struct GBASIODriver* driver); bool (*unload)(struct GBASIODriver* driver); + void (*setMode)(struct GBASIODriver* driver, enum GBASIOMode mode); + bool (*handlesMode)(struct GBASIODriver* driver, enum GBASIOMode mode); + int (*connectedDevices)(struct GBASIODriver* driver); + int (*deviceId)(struct GBASIODriver* driver); uint16_t (*writeRegister)(struct GBASIODriver* driver, uint32_t address, uint16_t value); }; diff --git a/src/gba/extra/battlechip.c b/src/gba/extra/battlechip.c index 3ab9f1403..841981fb7 100644 --- a/src/gba/extra/battlechip.c +++ b/src/gba/extra/battlechip.c @@ -35,6 +35,8 @@ enum { static bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver); static uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value); +static bool GBASIOBattlechipGateHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode); +static int GBASIOBattlechipGateConnectedDevices(struct GBASIODriver* driver); static void _battlechipTransfer(struct GBASIOBattlechipGate* gate); static void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate); @@ -45,6 +47,10 @@ void GBASIOBattlechipGateCreate(struct GBASIOBattlechipGate* gate) { gate->d.load = GBASIOBattlechipGateLoad; gate->d.unload = NULL; gate->d.writeRegister = GBASIOBattlechipGateWriteRegister; + gate->d.setMode = NULL; + gate->d.handlesMode = GBASIOBattlechipGateHandlesMode; + gate->d.connectedDevices = GBASIOBattlechipGateConnectedDevices; + gate->d.deviceId = NULL; gate->event.context = gate; gate->event.callback = _battlechipTransferEvent; @@ -82,6 +88,22 @@ uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t return value; } +static bool GBASIOBattlechipGateHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode) { + UNUSED(driver); + switch (mode) { + case GBA_SIO_NORMAL_32: + case GBA_SIO_MULTI: + return true; + default: + return false; + } +} + +static int GBASIOBattlechipGateConnectedDevices(struct GBASIODriver* driver) { + UNUSED(driver); + return 1; +} + void _battlechipTransfer(struct GBASIOBattlechipGate* gate) { int32_t cycles; if (gate->d.p->mode == GBA_SIO_NORMAL_32) { diff --git a/src/gba/sio.c b/src/gba/sio.c index 4ffeb6388..ae1132dfe 100644 --- a/src/gba/sio.c +++ b/src/gba/sio.c @@ -103,6 +103,15 @@ void GBASIOReset(struct GBASIO* sio) { if (sio->activeDriver && sio->activeDriver->unload) { sio->activeDriver->unload(sio->activeDriver); } + if (sio->drivers.multiplayer && sio->drivers.multiplayer->reset) { + sio->drivers.multiplayer->reset(sio->drivers.multiplayer); + } + if (sio->drivers.joybus && sio->drivers.joybus->reset) { + sio->drivers.joybus->reset(sio->drivers.joybus); + } + if (sio->drivers.normal && sio->drivers.normal->reset) { + sio->drivers.normal->reset(sio->drivers.normal); + } sio->rcnt = RCNT_INITIAL; sio->siocnt = 0; sio->mode = -1; diff --git a/src/gba/sio/gbp.c b/src/gba/sio/gbp.c index d992ebe2e..e1f008274 100644 --- a/src/gba/sio/gbp.c +++ b/src/gba/sio/gbp.c @@ -14,6 +14,8 @@ static uint16_t _gbpRead(struct mKeyCallback*); static uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value); +static bool _gbpSioHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode); +static int _gbpSioConnectedDevices(struct GBASIODriver* driver); static void _gbpSioProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLate); static const uint8_t _logoPalette[] = { @@ -43,11 +45,15 @@ void GBASIOPlayerInit(struct GBASIOPlayer* gbp) { gbp->callback.d.readKeys = _gbpRead; gbp->callback.d.requireOpposingDirections = true; gbp->callback.p = gbp; - gbp->d.init = 0; - gbp->d.deinit = 0; - gbp->d.load = 0; - gbp->d.unload = 0; + gbp->d.init = NULL; + gbp->d.deinit = NULL; + gbp->d.load = NULL; + gbp->d.unload = NULL; gbp->d.writeRegister = _gbpSioWriteRegister; + gbp->d.setMode = NULL; + gbp->d.handlesMode = _gbpSioHandlesMode; + gbp->d.connectedDevices = _gbpSioConnectedDevices; + gbp->d.deviceId = NULL; gbp->event.context = gbp; gbp->event.name = "GBA SIO Game Boy Player"; gbp->event.callback = _gbpSioProcessEvents; @@ -125,6 +131,16 @@ uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uin return value; } +static bool _gbpSioHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode) { + UNUSED(driver); + return mode == GBA_SIO_NORMAL_32; +} + +static int _gbpSioConnectedDevices(struct GBASIODriver* driver) { + UNUSED(driver); + return 1; +} + void _gbpSioProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLate) { UNUSED(timing); UNUSED(cyclesLate); diff --git a/src/gba/sio/joybus.c b/src/gba/sio/joybus.c index 941527439..f1e86d6ed 100644 --- a/src/gba/sio/joybus.c +++ b/src/gba/sio/joybus.c @@ -9,6 +9,8 @@ #include static uint16_t GBASIOJOYWriteRegister(struct GBASIODriver* sio, uint32_t address, uint16_t value); +static bool GBASIOJOYHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode); +static int GBASIOJOYConnectedDevices(struct GBASIODriver* driver); void GBASIOJOYCreate(struct GBASIODriver* sio) { sio->init = NULL; @@ -16,6 +18,10 @@ void GBASIOJOYCreate(struct GBASIODriver* sio) { sio->load = NULL; sio->unload = NULL; sio->writeRegister = GBASIOJOYWriteRegister; + sio->setMode = NULL; + sio->handlesMode = GBASIOJOYHandlesMode; + sio->connectedDevices = GBASIOJOYConnectedDevices; + sio->deviceId = NULL; } uint16_t GBASIOJOYWriteRegister(struct GBASIODriver* sio, uint32_t address, uint16_t value) { @@ -41,6 +47,16 @@ uint16_t GBASIOJOYWriteRegister(struct GBASIODriver* sio, uint32_t address, uint return value; } +static bool GBASIOJOYHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode) { + UNUSED(driver); + return mode == GBA_SIO_JOYBUS; +} + +static int GBASIOJOYConnectedDevices(struct GBASIODriver* driver) { + UNUSED(driver); + return 1; +} + int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command, uint8_t* data) { switch (command) { case JOY_RESET: diff --git a/src/gba/sio/lockstep.c b/src/gba/sio/lockstep.c index ba620911e..9ac1e7174 100644 --- a/src/gba/sio/lockstep.c +++ b/src/gba/sio/lockstep.c @@ -15,6 +15,10 @@ static bool GBASIOLockstepNodeInit(struct GBASIODriver* driver); static void GBASIOLockstepNodeDeinit(struct GBASIODriver* driver); static bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver); static bool GBASIOLockstepNodeUnload(struct GBASIODriver* driver); +static void GBASIOLockstepNodeSetMode(struct GBASIODriver* driver, enum GBASIOMode mode); +static bool GBASIOLockstepNodeHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode); +static int GBASIOLockstepNodeConnectedDevices(struct GBASIODriver* driver); +static int GBASIOLockstepNodeDeviceId(struct GBASIODriver* driver); static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value); static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value); static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* driver, uint32_t cyclesLate); @@ -38,7 +42,11 @@ void GBASIOLockstepNodeCreate(struct GBASIOLockstepNode* node) { node->d.deinit = GBASIOLockstepNodeDeinit; node->d.load = GBASIOLockstepNodeLoad; node->d.unload = GBASIOLockstepNodeUnload; - node->d.writeRegister = 0; + node->d.setMode = GBASIOLockstepNodeSetMode; + node->d.handlesMode = GBASIOLockstepNodeHandlesMode; + node->d.connectedDevices = GBASIOLockstepNodeConnectedDevices; + node->d.deviceId = GBASIOLockstepNodeDeviceId; + node->d.writeRegister = NULL; } bool GBASIOLockstepAttachNode(struct GBASIOLockstep* lockstep, struct GBASIOLockstepNode* node) { @@ -186,6 +194,48 @@ bool GBASIOLockstepNodeUnload(struct GBASIODriver* driver) { return true; } +static void GBASIOLockstepNodeSetMode(struct GBASIODriver* driver, enum GBASIOMode mode) { + struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver; + mLockstepLock(&node->p->d); + node->mode = mode; + mLockstepUnlock(&node->p->d); +} + +static bool GBASIOLockstepNodeHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode) { + UNUSED(driver); + switch (mode) { + case GBA_SIO_NORMAL_8: + case GBA_SIO_NORMAL_32: + case GBA_SIO_MULTI: + return true; + default: + return false; + } +} + +static int GBASIOLockstepNodeConnectedDevices(struct GBASIODriver* driver) { + struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver; + int attached = 0; + + switch (node->mode) { + case GBA_SIO_NORMAL_8: + case GBA_SIO_NORMAL_32: + ATOMIC_LOAD(attached, node->p->attachedNormal); + break; + case GBA_SIO_MULTI: + ATOMIC_LOAD(attached, node->p->attachedMulti); + break; + default: + break; + } + return attached - 1; +} + +static int GBASIOLockstepNodeDeviceId(struct GBASIODriver* driver) { + struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver; + return node->id; +} + static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) { struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver; From 09a69a32c0da1bbb21b725786449e1b5cfd7a199 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 16 May 2024 23:06:18 -0700 Subject: [PATCH 011/114] GBA SIO: Start moving common SIO logic out of drivers --- include/mgba/gba/interface.h | 2 - src/gba/CMakeLists.txt | 1 - src/gba/sio.c | 100 ++++++++++++++++++++++++++++++-- src/gba/sio/dolphin.c | 18 +++++- src/gba/sio/joybus.c | 108 ----------------------------------- src/gba/sio/lockstep.c | 2 +- 6 files changed, 114 insertions(+), 117 deletions(-) delete mode 100644 src/gba/sio/joybus.c diff --git a/include/mgba/gba/interface.h b/include/mgba/gba/interface.h index 6b663b4a7..c2a4fb5d2 100644 --- a/include/mgba/gba/interface.h +++ b/include/mgba/gba/interface.h @@ -120,8 +120,6 @@ struct GBASIODriver { uint16_t (*writeRegister)(struct GBASIODriver* driver, uint32_t address, uint16_t value); }; -void GBASIOJOYCreate(struct GBASIODriver* sio); - enum GBASIOBattleChipGateFlavor { GBA_FLAVOR_BATTLECHIP_GATE = 4, GBA_FLAVOR_PROGRESS_GATE = 5, diff --git a/src/gba/CMakeLists.txt b/src/gba/CMakeLists.txt index 933ba3fac..01a968a41 100644 --- a/src/gba/CMakeLists.txt +++ b/src/gba/CMakeLists.txt @@ -31,7 +31,6 @@ set(SOURCE_FILES sharkport.c sio.c sio/gbp.c - sio/joybus.c timer.c video.c) diff --git a/src/gba/sio.c b/src/gba/sio.c index ae1132dfe..27702dfbf 100644 --- a/src/gba/sio.c +++ b/src/gba/sio.c @@ -186,7 +186,32 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) { sio->siocnt = value & 0x3000; _switchMode(sio); } - if (sio->activeDriver && sio->activeDriver->writeRegister) { + int id = 0; + int connected = 0; + bool handled = false; + if (sio->activeDriver) { + handled = sio->activeDriver->handlesMode(sio->activeDriver, sio->mode); + if (handled) { + if (sio->activeDriver->deviceId) { + id = sio->activeDriver->deviceId(sio->activeDriver); + } + connected = sio->activeDriver->connectedDevices(sio->activeDriver); + handled = !!sio->activeDriver->writeRegister; + } + } + + switch (sio->mode) { + case GBA_SIO_MULTI: + value &= 0xFF83; + value = GBASIOMultiplayerSetSlave(value, id || !connected); + value = GBASIOMultiplayerSetId(value, id); + value |= sio->siocnt & 0x00FC; + break; + default: + // TODO + break; + } + if (handled) { value = sio->activeDriver->writeRegister(sio->activeDriver, GBA_REG_SIOCNT, value); } else { // Dummy drivers @@ -203,8 +228,7 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) { } break; case GBA_SIO_MULTI: - value &= 0xFF83; - value |= 0xC; + value = GBASIOMultiplayerFillReady(value); break; default: // TODO @@ -215,7 +239,12 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) { } uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t value) { - if (sio->activeDriver && sio->activeDriver->writeRegister) { + bool handled = false; + if (sio->activeDriver) { + handled = sio->activeDriver->writeRegister && sio->activeDriver->handlesMode(sio->activeDriver, sio->mode); + } + + if (handled) { return sio->activeDriver->writeRegister(sio->activeDriver, address, value); } // Dummy drivers @@ -223,9 +252,22 @@ uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t valu case GBA_SIO_JOYBUS: switch (address) { case GBA_REG_JOYCNT: + mLOG(GBA_SIO, DEBUG, "JOY write: CNT <- %04X", value); return (value & 0x0040) | (sio->p->memory.io[GBA_REG(JOYCNT)] & ~(value & 0x7) & ~0x0040); case GBA_REG_JOYSTAT: + mLOG(GBA_SIO, DEBUG, "JOY write: STAT <- %04X", value); return (value & 0x0030) | (sio->p->memory.io[GBA_REG(JOYSTAT)] & ~0x30); + case GBA_REG_JOY_TRANS_LO: + mLOG(GBA_SIO, DEBUG, "JOY write: TRANS_LO <- %04X", value); + break; + case GBA_REG_JOY_TRANS_HI: + mLOG(GBA_SIO, DEBUG, "JOY write: TRANS_HI <- %04X", value); + break; + default: + mLOG(GBA_SIO, DEBUG, "JOY write: Unknown reg %03X <- %04X", address, value); + break; + case GBA_REG_RCNT: + break; } break; default: @@ -234,3 +276,53 @@ uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t valu } return value; } + +int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command, uint8_t* data) { + switch (command) { + case JOY_RESET: + sio->p->p->memory.io[GBA_REG(JOYCNT)] |= JOYCNT_RESET; + if (sio->p->p->memory.io[GBA_REG(JOYCNT)] & 0x40) { + GBARaiseIRQ(sio->p->p, GBA_IRQ_SIO, 0); + } + // Fall through + case JOY_POLL: + data[0] = 0x00; + data[1] = 0x04; + data[2] = sio->p->p->memory.io[GBA_REG(JOYSTAT)]; + + mLOG(GBA_SIO, DEBUG, "JOY %s: %02X (%02X)", command == JOY_POLL ? "poll" : "reset", data[2], sio->p->p->memory.io[GBA_REG(JOYCNT)]); + return 3; + case JOY_RECV: + sio->p->p->memory.io[GBA_REG(JOYCNT)] |= JOYCNT_RECV; + sio->p->p->memory.io[GBA_REG(JOYSTAT)] |= JOYSTAT_RECV; + + sio->p->p->memory.io[GBA_REG(JOY_RECV_LO)] = data[0] | (data[1] << 8); + sio->p->p->memory.io[GBA_REG(JOY_RECV_HI)] = data[2] | (data[3] << 8); + + data[0] = sio->p->p->memory.io[GBA_REG(JOYSTAT)]; + + mLOG(GBA_SIO, DEBUG, "JOY recv: %02X (%02X)", data[0], sio->p->p->memory.io[GBA_REG(JOYCNT)]); + + if (sio->p->p->memory.io[GBA_REG(JOYCNT)] & 0x40) { + GBARaiseIRQ(sio->p->p, GBA_IRQ_SIO, 0); + } + return 1; + case JOY_TRANS: + data[0] = sio->p->p->memory.io[GBA_REG(JOY_TRANS_LO)]; + data[1] = sio->p->p->memory.io[GBA_REG(JOY_TRANS_LO)] >> 8; + data[2] = sio->p->p->memory.io[GBA_REG(JOY_TRANS_HI)]; + data[3] = sio->p->p->memory.io[GBA_REG(JOY_TRANS_HI)] >> 8; + data[4] = sio->p->p->memory.io[GBA_REG(JOYSTAT)]; + + sio->p->p->memory.io[GBA_REG(JOYCNT)] |= JOYCNT_TRANS; + sio->p->p->memory.io[GBA_REG(JOYSTAT)] &= ~JOYSTAT_TRANS; + + mLOG(GBA_SIO, DEBUG, "JOY trans: %02X%02X%02X%02X:%02X (%02X)", data[0], data[1], data[2], data[3], data[4], sio->p->p->memory.io[GBA_REG(JOYCNT)]); + + if (sio->p->p->memory.io[GBA_REG(JOYCNT)] & 0x40) { + GBARaiseIRQ(sio->p->p, GBA_IRQ_SIO, 0); + } + return 5; + } + return 0; +} diff --git a/src/gba/sio/dolphin.c b/src/gba/sio/dolphin.c index 896d17d3e..badbd05f7 100644 --- a/src/gba/sio/dolphin.c +++ b/src/gba/sio/dolphin.c @@ -25,16 +25,22 @@ enum { static bool GBASIODolphinInit(struct GBASIODriver* driver); static bool GBASIODolphinLoad(struct GBASIODriver* driver); static bool GBASIODolphinUnload(struct GBASIODriver* driver); +static bool GBASIODolphinHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode); +static int GBASIODolphinConnectedDevices(struct GBASIODriver* driver); static void GBASIODolphinProcessEvents(struct mTiming* timing, void* context, uint32_t cyclesLate); static int32_t _processCommand(struct GBASIODolphin* dol, uint32_t cyclesLate); static void _flush(struct GBASIODolphin* dol); void GBASIODolphinCreate(struct GBASIODolphin* dol) { - GBASIOJOYCreate(&dol->d); dol->d.init = GBASIODolphinInit; dol->d.load = GBASIODolphinLoad; dol->d.unload = GBASIODolphinUnload; + dol->d.writeRegister = NULL; + dol->d.setMode = NULL; + dol->d.handlesMode = GBASIODolphinHandlesMode; + dol->d.connectedDevices = GBASIODolphinConnectedDevices; + dol->d.deviceId = NULL; dol->event.context = dol; dol->event.name = "GB SIO Lockstep"; dol->event.callback = GBASIODolphinProcessEvents; @@ -116,6 +122,16 @@ static bool GBASIODolphinUnload(struct GBASIODriver* driver) { return true; } +static bool GBASIODolphinHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode) { + UNUSED(driver); + return mode == GBA_SIO_JOYBUS; +} + +static int GBASIODolphinConnectedDevices(struct GBASIODriver* driver) { + UNUSED(driver); + return 1; +} + void GBASIODolphinProcessEvents(struct mTiming* timing, void* context, uint32_t cyclesLate) { struct GBASIODolphin* dol = context; if (SOCKET_FAILED(dol->data)) { diff --git a/src/gba/sio/joybus.c b/src/gba/sio/joybus.c deleted file mode 100644 index f1e86d6ed..000000000 --- a/src/gba/sio/joybus.c +++ /dev/null @@ -1,108 +0,0 @@ -/* Copyright (c) 2013-2017 Jeffrey Pfau - * - * This Source Code Form is subject to the terms of the Mozilla Public - * 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 - -#include -#include - -static uint16_t GBASIOJOYWriteRegister(struct GBASIODriver* sio, uint32_t address, uint16_t value); -static bool GBASIOJOYHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode); -static int GBASIOJOYConnectedDevices(struct GBASIODriver* driver); - -void GBASIOJOYCreate(struct GBASIODriver* sio) { - sio->init = NULL; - sio->deinit = NULL; - sio->load = NULL; - sio->unload = NULL; - sio->writeRegister = GBASIOJOYWriteRegister; - sio->setMode = NULL; - sio->handlesMode = GBASIOJOYHandlesMode; - sio->connectedDevices = GBASIOJOYConnectedDevices; - sio->deviceId = NULL; -} - -uint16_t GBASIOJOYWriteRegister(struct GBASIODriver* sio, uint32_t address, uint16_t value) { - switch (address) { - case GBA_REG_JOYCNT: - mLOG(GBA_SIO, DEBUG, "JOY write: CNT <- %04X", value); - return (value & 0x0040) | (sio->p->p->memory.io[GBA_REG(JOYCNT)] & ~(value & 0x7) & ~0x0040); - case GBA_REG_JOYSTAT: - mLOG(GBA_SIO, DEBUG, "JOY write: STAT <- %04X", value); - return (value & 0x0030) | (sio->p->p->memory.io[GBA_REG(JOYSTAT)] & ~0x30); - case GBA_REG_JOY_TRANS_LO: - mLOG(GBA_SIO, DEBUG, "JOY write: TRANS_LO <- %04X", value); - break; - case GBA_REG_JOY_TRANS_HI: - mLOG(GBA_SIO, DEBUG, "JOY write: TRANS_HI <- %04X", value); - break; - default: - mLOG(GBA_SIO, DEBUG, "JOY write: Unknown reg %03X <- %04X", address, value); - // Fall through - case GBA_REG_RCNT: - break; - } - return value; -} - -static bool GBASIOJOYHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode) { - UNUSED(driver); - return mode == GBA_SIO_JOYBUS; -} - -static int GBASIOJOYConnectedDevices(struct GBASIODriver* driver) { - UNUSED(driver); - return 1; -} - -int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command, uint8_t* data) { - switch (command) { - case JOY_RESET: - sio->p->p->memory.io[GBA_REG(JOYCNT)] |= JOYCNT_RESET; - if (sio->p->p->memory.io[GBA_REG(JOYCNT)] & 0x40) { - GBARaiseIRQ(sio->p->p, GBA_IRQ_SIO, 0); - } - // Fall through - case JOY_POLL: - data[0] = 0x00; - data[1] = 0x04; - data[2] = sio->p->p->memory.io[GBA_REG(JOYSTAT)]; - - mLOG(GBA_SIO, DEBUG, "JOY %s: %02X (%02X)", command == JOY_POLL ? "poll" : "reset", data[2], sio->p->p->memory.io[GBA_REG(JOYCNT)]); - return 3; - case JOY_RECV: - sio->p->p->memory.io[GBA_REG(JOYCNT)] |= JOYCNT_RECV; - sio->p->p->memory.io[GBA_REG(JOYSTAT)] |= JOYSTAT_RECV; - - sio->p->p->memory.io[GBA_REG(JOY_RECV_LO)] = data[0] | (data[1] << 8); - sio->p->p->memory.io[GBA_REG(JOY_RECV_HI)] = data[2] | (data[3] << 8); - - data[0] = sio->p->p->memory.io[GBA_REG(JOYSTAT)]; - - mLOG(GBA_SIO, DEBUG, "JOY recv: %02X (%02X)", data[0], sio->p->p->memory.io[GBA_REG(JOYCNT)]); - - if (sio->p->p->memory.io[GBA_REG(JOYCNT)] & 0x40) { - GBARaiseIRQ(sio->p->p, GBA_IRQ_SIO, 0); - } - return 1; - case JOY_TRANS: - data[0] = sio->p->p->memory.io[GBA_REG(JOY_TRANS_LO)]; - data[1] = sio->p->p->memory.io[GBA_REG(JOY_TRANS_LO)] >> 8; - data[2] = sio->p->p->memory.io[GBA_REG(JOY_TRANS_HI)]; - data[3] = sio->p->p->memory.io[GBA_REG(JOY_TRANS_HI)] >> 8; - data[4] = sio->p->p->memory.io[GBA_REG(JOYSTAT)]; - - sio->p->p->memory.io[GBA_REG(JOYCNT)] |= JOYCNT_TRANS; - sio->p->p->memory.io[GBA_REG(JOYSTAT)] &= ~JOYSTAT_TRANS; - - mLOG(GBA_SIO, DEBUG, "JOY trans: %02X%02X%02X%02X:%02X (%02X)", data[0], data[1], data[2], data[3], data[4], sio->p->p->memory.io[GBA_REG(JOYCNT)]); - - if (sio->p->p->memory.io[GBA_REG(JOYCNT)] & 0x40) { - GBARaiseIRQ(sio->p->p, GBA_IRQ_SIO, 0); - } - return 5; - } - return 0; -} diff --git a/src/gba/sio/lockstep.c b/src/gba/sio/lockstep.c index 9ac1e7174..edd170748 100644 --- a/src/gba/sio/lockstep.c +++ b/src/gba/sio/lockstep.c @@ -215,7 +215,7 @@ static bool GBASIOLockstepNodeHandlesMode(struct GBASIODriver* driver, enum GBAS static int GBASIOLockstepNodeConnectedDevices(struct GBASIODriver* driver) { struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver; - int attached = 0; + int attached = 1; switch (node->mode) { case GBA_SIO_NORMAL_8: From f9e15c53d953b8a23611fb125870513a816fc27b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 18 May 2024 16:26:33 -0700 Subject: [PATCH 012/114] GBA SIO: Move more SIO logic out of drivers --- src/gba/sio.c | 63 ++++++++++++++++++++++++++++++++++-------- src/gba/sio/lockstep.c | 5 ---- 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/src/gba/sio.c b/src/gba/sio.c index 27702dfbf..4eb9cfa3c 100644 --- a/src/gba/sio.c +++ b/src/gba/sio.c @@ -69,6 +69,23 @@ static void _switchMode(struct GBASIO* sio) { if (sio->activeDriver && sio->activeDriver->load) { sio->activeDriver->load(sio->activeDriver); } + + int id = 0; + switch (newMode) { + case GBA_SIO_MULTI: + if (sio->activeDriver && sio->activeDriver->deviceId) { + id = sio->activeDriver->deviceId(sio->activeDriver); + } + if (id) { + sio->rcnt |= 4; + } else { + sio->rcnt &= ~4; + } + break; + default: + // TODO + break; + } } } @@ -239,24 +256,17 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) { } uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t value) { - bool handled = false; - if (sio->activeDriver) { - handled = sio->activeDriver->writeRegister && sio->activeDriver->handlesMode(sio->activeDriver, sio->mode); - } - - if (handled) { - return sio->activeDriver->writeRegister(sio->activeDriver, address, value); - } - // Dummy drivers switch (sio->mode) { case GBA_SIO_JOYBUS: switch (address) { case GBA_REG_JOYCNT: mLOG(GBA_SIO, DEBUG, "JOY write: CNT <- %04X", value); - return (value & 0x0040) | (sio->p->memory.io[GBA_REG(JOYCNT)] & ~(value & 0x7) & ~0x0040); + value = (value & 0x0040) | (sio->p->memory.io[GBA_REG(JOYCNT)] & ~(value & 0x7) & ~0x0040); + break; case GBA_REG_JOYSTAT: mLOG(GBA_SIO, DEBUG, "JOY write: STAT <- %04X", value); - return (value & 0x0030) | (sio->p->memory.io[GBA_REG(JOYSTAT)] & ~0x30); + value = (value & 0x0030) | (sio->p->memory.io[GBA_REG(JOYSTAT)] & ~0x30); + break; case GBA_REG_JOY_TRANS_LO: mLOG(GBA_SIO, DEBUG, "JOY write: TRANS_LO <- %04X", value); break; @@ -270,10 +280,41 @@ uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t valu break; } break; + case GBA_SIO_NORMAL_8: + switch (address) { + case GBA_REG_SIODATA8: + mLOG(GBA_SIO, DEBUG, "NORMAL8 write: SIODATA8 <- %02X", value); + break; + default: + mLOG(GBA_SIO, DEBUG, "NORMAL8 write: Unknown reg %03X <- %04X", address, value); + break; + case GBA_REG_RCNT: + break; + } + break; + case GBA_SIO_NORMAL_32: + switch (address) { + case GBA_REG_SIODATA32_LO: + mLOG(GBA_SIO, DEBUG, "NORMAL32 write: SIODATA32_LO <- %04X", value); + break; + case GBA_REG_SIODATA32_HI: + mLOG(GBA_SIO, DEBUG, "NORMAL32 write: SIODATA32_HI <- %04X", value); + break; + default: + mLOG(GBA_SIO, DEBUG, "NORMAL32 write: Unknown reg %03X <- %04X", address, value); + break; + case GBA_REG_RCNT: + break; + } + break; default: // TODO break; } + + if (sio->activeDriver && sio->activeDriver->writeRegister && sio->activeDriver->handlesMode(sio->activeDriver, sio->mode)) { + sio->activeDriver->writeRegister(sio->activeDriver, address, value); + } return value; } diff --git a/src/gba/sio/lockstep.c b/src/gba/sio/lockstep.c index edd170748..9709f633b 100644 --- a/src/gba/sio/lockstep.c +++ b/src/gba/sio/lockstep.c @@ -117,8 +117,6 @@ bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver) { node->d.p->siocnt = GBASIOMultiplayerSetReady(node->d.p->siocnt, node->p->attachedMulti == node->p->d.attached); if (node->id) { node->d.p->rcnt |= 4; - node->d.p->siocnt = GBASIOMultiplayerFillSlave(node->d.p->siocnt); - int try; for (try = 0; try < 3; ++try) { uint16_t masterSiocnt; @@ -127,9 +125,6 @@ bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver) { break; } } - } else { - node->d.p->rcnt &= ~4; - node->d.p->siocnt = GBASIOMultiplayerClearSlave(node->d.p->siocnt); } break; case GBA_SIO_NORMAL_8: From cd4132fba3c94ea303264bfc1f2d0231c997a749 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 18 May 2024 16:42:20 -0700 Subject: [PATCH 013/114] GBA SIO: Move MULTI finishing logic out of drivers --- include/mgba/internal/gba/sio.h | 2 ++ src/gba/extra/battlechip.c | 16 +++++----------- src/gba/sio.c | 17 +++++++++++++++++ src/gba/sio/lockstep.c | 11 +---------- 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/include/mgba/internal/gba/sio.h b/include/mgba/internal/gba/sio.h index 260772f55..a546017dc 100644 --- a/include/mgba/internal/gba/sio.h +++ b/include/mgba/internal/gba/sio.h @@ -85,6 +85,8 @@ void GBASIOWriteRCNT(struct GBASIO* sio, uint16_t value); void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value); uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t value); +void GBASIOMultiplayerFinishTransfer(struct GBASIO* sio, uint16_t data[4], uint32_t cyclesLate); + int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command, uint8_t* data); CXX_GUARD_END diff --git a/src/gba/extra/battlechip.c b/src/gba/extra/battlechip.c index 841981fb7..12750f67b 100644 --- a/src/gba/extra/battlechip.c +++ b/src/gba/extra/battlechip.c @@ -131,11 +131,6 @@ void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cycle uint16_t cmd = gate->d.p->p->memory.io[GBA_REG(SIOMLT_SEND)]; uint16_t reply = 0xFFFF; - gate->d.p->p->memory.io[GBA_REG(SIOMULTI0)] = cmd; - gate->d.p->p->memory.io[GBA_REG(SIOMULTI2)] = 0xFFFF; - gate->d.p->p->memory.io[GBA_REG(SIOMULTI3)] = 0xFFFF; - gate->d.p->siocnt = GBASIOMultiplayerClearBusy(gate->d.p->siocnt); - gate->d.p->siocnt = GBASIOMultiplayerSetId(gate->d.p->siocnt, 0); mLOG(GBA_BATTLECHIP, DEBUG, "Game: %04X (%i)", cmd, gate->state); @@ -168,7 +163,7 @@ void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cycle case 0xA3D0: // EXE 4 case 0xA6C0: - mLOG(GBA_BATTLECHIP, DEBUG, "Resync detected"); + mLOG(GBA_BATTLECHIP, DEBUG, "Resync detected"); gate->state = BATTLECHIP_STATE_SYNC; break; } @@ -213,9 +208,8 @@ void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cycle mLOG(GBA_BATTLECHIP, DEBUG, "Gate: %04X (%i)", reply, gate->state); ++gate->state; - gate->d.p->p->memory.io[GBA_REG(SIOMULTI1)] = reply; - - if (GBASIOMultiplayerIsIrq(gate->d.p->siocnt)) { - GBARaiseIRQ(gate->d.p->p, GBA_IRQ_SIO, cyclesLate); - } + uint16_t data[4] = { + cmd, reply, 0xFFFF, 0xFFFF + }; + GBASIOMultiplayerFinishTransfer(gate->d.p, data, cyclesLate); } diff --git a/src/gba/sio.c b/src/gba/sio.c index 4eb9cfa3c..cdccf1ea3 100644 --- a/src/gba/sio.c +++ b/src/gba/sio.c @@ -318,6 +318,23 @@ uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t valu return value; } +void GBASIOMultiplayerFinishTransfer(struct GBASIO* sio, uint16_t data[4], uint32_t cyclesLate) { + int id = 0; + if (sio->activeDriver && sio->activeDriver->deviceId) { + id = sio->activeDriver->deviceId(sio->activeDriver); + } + sio->p->memory.io[GBA_REG(SIOMULTI0)] = data[0]; + sio->p->memory.io[GBA_REG(SIOMULTI1)] = data[1]; + sio->p->memory.io[GBA_REG(SIOMULTI2)] = data[2]; + sio->p->memory.io[GBA_REG(SIOMULTI3)] = data[3]; + sio->rcnt |= 1; + sio->siocnt = GBASIOMultiplayerClearBusy(sio->siocnt); + sio->siocnt = GBASIOMultiplayerSetId(sio->siocnt, id); + if (GBASIOMultiplayerIsIrq(sio->siocnt)) { + GBARaiseIRQ(sio->p, GBA_IRQ_SIO, cyclesLate); + } +} + int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command, uint8_t* data) { switch (command) { case JOY_RESET: diff --git a/src/gba/sio/lockstep.c b/src/gba/sio/lockstep.c index 9709f633b..b7f5752b4 100644 --- a/src/gba/sio/lockstep.c +++ b/src/gba/sio/lockstep.c @@ -280,16 +280,7 @@ static void _finishTransfer(struct GBASIOLockstepNode* node) { struct GBASIO* sio = node->d.p; switch (node->mode) { case GBA_SIO_MULTI: - sio->p->memory.io[GBA_REG(SIOMULTI0)] = node->p->multiRecv[0]; - sio->p->memory.io[GBA_REG(SIOMULTI1)] = node->p->multiRecv[1]; - sio->p->memory.io[GBA_REG(SIOMULTI2)] = node->p->multiRecv[2]; - sio->p->memory.io[GBA_REG(SIOMULTI3)] = node->p->multiRecv[3]; - sio->rcnt |= 1; - sio->siocnt = GBASIOMultiplayerClearBusy(sio->siocnt); - sio->siocnt = GBASIOMultiplayerSetId(sio->siocnt, node->id); - if (GBASIOMultiplayerIsIrq(sio->siocnt)) { - GBARaiseIRQ(sio->p, GBA_IRQ_SIO, 0); - } + GBASIOMultiplayerFinishTransfer(sio, node->p->multiRecv, 0); break; case GBA_SIO_NORMAL_8: // TODO From 1b0b540de6743d306e1e53583b1ed24215a25334 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 21 May 2024 23:24:06 -0700 Subject: [PATCH 014/114] GBA SIO: Move cycle estimation values into core --- include/mgba/internal/gba/sio.h | 4 ++-- src/gba/extra/battlechip.c | 7 +------ src/gba/sio.c | 27 ++++++++++++++++++++++++++- src/gba/sio/lockstep.c | 14 +++----------- 4 files changed, 32 insertions(+), 20 deletions(-) diff --git a/include/mgba/internal/gba/sio.h b/include/mgba/internal/gba/sio.h index a546017dc..2576dfffe 100644 --- a/include/mgba/internal/gba/sio.h +++ b/include/mgba/internal/gba/sio.h @@ -16,8 +16,6 @@ CXX_GUARD_START #define MAX_GBAS 4 -extern const int GBASIOCyclesPerTransfer[4][MAX_GBAS]; - mLOG_DECLARE_CATEGORY(GBA_SIO); enum { @@ -85,6 +83,8 @@ void GBASIOWriteRCNT(struct GBASIO* sio, uint16_t value); void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value); uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t value); +int32_t GBASIOTransferCycles(struct GBASIO* sio); + void GBASIOMultiplayerFinishTransfer(struct GBASIO* sio, uint16_t data[4], uint32_t cyclesLate); int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command, uint8_t* data); diff --git a/src/gba/extra/battlechip.c b/src/gba/extra/battlechip.c index 12750f67b..3314ff02c 100644 --- a/src/gba/extra/battlechip.c +++ b/src/gba/extra/battlechip.c @@ -105,12 +105,7 @@ static int GBASIOBattlechipGateConnectedDevices(struct GBASIODriver* driver) { } void _battlechipTransfer(struct GBASIOBattlechipGate* gate) { - int32_t cycles; - if (gate->d.p->mode == GBA_SIO_NORMAL_32) { - cycles = GBA_ARM7TDMI_FREQUENCY / 0x40000; - } else { - cycles = GBASIOCyclesPerTransfer[GBASIOMultiplayerGetBaud(gate->d.p->siocnt)][1]; - } + int32_t cycles = GBASIOTransferCycles(gate->d.p); mTimingDeschedule(&gate->d.p->p->timing, &gate->event); mTimingSchedule(&gate->d.p->p->timing, &gate->event, cycles); } diff --git a/src/gba/sio.c b/src/gba/sio.c index cdccf1ea3..76d2ea144 100644 --- a/src/gba/sio.c +++ b/src/gba/sio.c @@ -11,7 +11,7 @@ mLOG_DEFINE_CATEGORY(GBA_SIO, "GBA Serial I/O", "gba.sio"); -const int GBASIOCyclesPerTransfer[4][MAX_GBAS] = { +static const int GBASIOCyclesPerTransfer[4][MAX_GBAS] = { { 31976, 63427, 94884, 125829 }, { 8378, 16241, 24104, 31457 }, { 5750, 10998, 16241, 20972 }, @@ -318,6 +318,31 @@ uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t valu return value; } +int32_t GBASIOTransferCycles(struct GBASIO* sio) { + int connected = 0; + if (sio->activeDriver) { + connected = sio->activeDriver->connectedDevices(sio->activeDriver); + } + + if (connected < 0 || connected >= MAX_GBAS) { + mLOG(GBA_SIO, ERROR, "SIO driver returned invalid device count %i", connected); + return 0; + } + + switch (sio->mode) { + case GBA_SIO_MULTI: + return GBASIOCyclesPerTransfer[GBASIOMultiplayerGetBaud(sio->siocnt)][connected]; + case GBA_SIO_NORMAL_8: + return 8 * GBA_ARM7TDMI_FREQUENCY / ((GBASIONormalIsInternalSc(sio->siocnt) ? 2048 : 256) * 1024); + case GBA_SIO_NORMAL_32: + return 32 * GBA_ARM7TDMI_FREQUENCY / ((GBASIONormalIsInternalSc(sio->siocnt) ? 2048 : 256) * 1024); + default: + mLOG(GBA_SIO, STUB, "No cycle count implemented for mode %s", _modeName(sio->mode)); + break; + } + return 0; +} + void GBASIOMultiplayerFinishTransfer(struct GBASIO* sio, uint16_t data[4], uint32_t cyclesLate) { int id = 0; if (sio->activeDriver && sio->activeDriver->deviceId) { diff --git a/src/gba/sio/lockstep.c b/src/gba/sio/lockstep.c index b7f5752b4..08c586ae5 100644 --- a/src/gba/sio/lockstep.c +++ b/src/gba/sio/lockstep.c @@ -250,7 +250,7 @@ static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver if (!node->id && attached > 1 && GBASIOMultiplayerIsReady(node->d.p->siocnt)) { mLOG(GBA_SIO, DEBUG, "Lockstep %i: Transfer initiated", node->id); ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING); - ATOMIC_STORE(node->p->d.transferCycles, GBASIOCyclesPerTransfer[GBASIOMultiplayerGetBaud(node->d.p->siocnt)][node->p->d.attached - 1]); + ATOMIC_STORE(node->p->d.transferCycles, GBASIOTransferCycles(node->d.p)); if (mTimingIsScheduled(&driver->p->p->timing, &node->event)) { node->eventDiff -= node->event.when - mTimingCurrentTime(&driver->p->p->timing); @@ -502,7 +502,7 @@ static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* user, if (node->p->d.attached < 2) { switch (node->mode) { case GBA_SIO_MULTI: - cycles = GBASIOCyclesPerTransfer[GBASIOMultiplayerGetBaud(node->d.p->siocnt)][0]; + cycles = GBASIOTransferCycles(node->d.p); break; case GBA_SIO_NORMAL_8: case GBA_SIO_NORMAL_32: @@ -568,15 +568,7 @@ static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* drive if ((value & 0x0081) == 0x0081) { if (!node->id) { // Frequency - int32_t cycles; - if (value & 2) { - cycles = 8 * 8; - } else { - cycles = 64 * 8; - } - if (value & 0x1000) { - cycles *= 4; - } + int32_t cycles = GBASIOTransferCycles(node->d.p); if (transferActive == TRANSFER_IDLE) { mLOG(GBA_SIO, DEBUG, "Lockstep %i: Transfer initiated", node->id); From 54c9e9d411bbc03a27ab9ae418227162c3855aef Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 21 May 2024 23:39:15 -0700 Subject: [PATCH 015/114] GBA SIO: Move NORMAL finishing logic out of drivers --- include/mgba/internal/gba/sio.h | 2 ++ src/gba/extra/battlechip.c | 7 +------ src/gba/sio.c | 17 +++++++++++++++++ src/gba/sio/gbp.c | 9 +-------- src/gba/sio/lockstep.c | 20 ++++---------------- 5 files changed, 25 insertions(+), 30 deletions(-) diff --git a/include/mgba/internal/gba/sio.h b/include/mgba/internal/gba/sio.h index 2576dfffe..43acaa7b8 100644 --- a/include/mgba/internal/gba/sio.h +++ b/include/mgba/internal/gba/sio.h @@ -86,6 +86,8 @@ uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t valu int32_t GBASIOTransferCycles(struct GBASIO* sio); void GBASIOMultiplayerFinishTransfer(struct GBASIO* sio, uint16_t data[4], uint32_t cyclesLate); +void GBASIONormal8FinishTransfer(struct GBASIO* sio, uint8_t data, uint32_t cyclesLate); +void GBASIONormal32FinishTransfer(struct GBASIO* sio, uint32_t data, uint32_t cyclesLate); int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command, uint8_t* data); diff --git a/src/gba/extra/battlechip.c b/src/gba/extra/battlechip.c index 3314ff02c..040ec80b8 100644 --- a/src/gba/extra/battlechip.c +++ b/src/gba/extra/battlechip.c @@ -115,12 +115,7 @@ void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cycle struct GBASIOBattlechipGate* gate = user; if (gate->d.p->mode == GBA_SIO_NORMAL_32) { - gate->d.p->p->memory.io[GBA_REG(SIODATA32_LO)] = 0; - gate->d.p->p->memory.io[GBA_REG(SIODATA32_HI)] = 0; - gate->d.p->siocnt = GBASIONormalClearStart(gate->d.p->siocnt); - if (GBASIONormalIsIrq(gate->d.p->siocnt)) { - GBARaiseIRQ(gate->d.p->p, GBA_IRQ_SIO, cyclesLate); - } + GBASIONormal32FinishTransfer(gate->d.p, 0, cyclesLate); return; } diff --git a/src/gba/sio.c b/src/gba/sio.c index 76d2ea144..56dc55a9f 100644 --- a/src/gba/sio.c +++ b/src/gba/sio.c @@ -360,6 +360,23 @@ void GBASIOMultiplayerFinishTransfer(struct GBASIO* sio, uint16_t data[4], uint3 } } +void GBASIONormal8FinishTransfer(struct GBASIO* sio, uint8_t data, uint32_t cyclesLate) { + sio->siocnt = GBASIONormalClearStart(sio->siocnt); + sio->p->memory.io[GBA_REG(SIODATA8)] = data; + if (GBASIONormalIsIrq(sio->siocnt)) { + GBARaiseIRQ(sio->p, GBA_IRQ_SIO, cyclesLate); + } +} + +void GBASIONormal32FinishTransfer(struct GBASIO* sio, uint32_t data, uint32_t cyclesLate) { + sio->siocnt = GBASIONormalClearStart(sio->siocnt); + sio->p->memory.io[GBA_REG(SIODATA32_LO)] = data; + sio->p->memory.io[GBA_REG(SIODATA32_HI)] = data >> 16; + if (GBASIONormalIsIrq(sio->siocnt)) { + GBARaiseIRQ(sio->p, GBA_IRQ_SIO, cyclesLate); + } +} + int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command, uint8_t* data) { switch (command) { case JOY_RESET: diff --git a/src/gba/sio/gbp.c b/src/gba/sio/gbp.c index e1f008274..37944d596 100644 --- a/src/gba/sio/gbp.c +++ b/src/gba/sio/gbp.c @@ -143,7 +143,6 @@ static int _gbpSioConnectedDevices(struct GBASIODriver* driver) { void _gbpSioProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLate) { UNUSED(timing); - UNUSED(cyclesLate); struct GBASIOPlayer* gbp = user; uint32_t tx = 0; int txPosition = gbp->txPosition; @@ -155,11 +154,5 @@ void _gbpSioProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLat } tx = _gbpTxData[txPosition]; ++gbp->txPosition; - gbp->p->memory.io[GBA_REG(SIODATA32_LO)] = tx; - gbp->p->memory.io[GBA_REG(SIODATA32_HI)] = tx >> 16; - if (GBASIONormalIsIrq(gbp->d.p->siocnt)) { - GBARaiseIRQ(gbp->p, GBA_IRQ_SIO, cyclesLate); - } - gbp->d.p->siocnt = GBASIONormalClearStart(gbp->d.p->siocnt); - gbp->p->memory.io[GBA_REG(SIOCNT)] = gbp->d.p->siocnt & ~0x0080; + GBASIONormal32FinishTransfer(gbp->d.p, tx, cyclesLate); } diff --git a/src/gba/sio/lockstep.c b/src/gba/sio/lockstep.c index 08c586ae5..67b359234 100644 --- a/src/gba/sio/lockstep.c +++ b/src/gba/sio/lockstep.c @@ -283,31 +283,19 @@ static void _finishTransfer(struct GBASIOLockstepNode* node) { GBASIOMultiplayerFinishTransfer(sio, node->p->multiRecv, 0); break; case GBA_SIO_NORMAL_8: - // TODO - sio->siocnt = GBASIONormalClearStart(sio->siocnt); if (node->id) { sio->siocnt = GBASIONormalSetSi(sio->siocnt, GBASIONormalGetIdleSo(node->p->players[node->id - 1]->d.p->siocnt)); - node->d.p->p->memory.io[GBA_REG(SIODATA8)] = node->p->normalRecv[node->id - 1] & 0xFF; + GBASIONormal8FinishTransfer(sio, node->p->normalRecv[node->id - 1], 0); } else { - node->d.p->p->memory.io[GBA_REG(SIODATA8)] = 0xFFFF; - } - if (GBASIONormalIsIrq(sio->siocnt)) { - GBARaiseIRQ(sio->p, GBA_IRQ_SIO, 0); + GBASIONormal8FinishTransfer(sio, 0xFF, 0); } break; case GBA_SIO_NORMAL_32: - // TODO - sio->siocnt = GBASIONormalClearStart(sio->siocnt); if (node->id) { sio->siocnt = GBASIONormalSetSi(sio->siocnt, GBASIONormalGetIdleSo(node->p->players[node->id - 1]->d.p->siocnt)); - node->d.p->p->memory.io[GBA_REG(SIODATA32_LO)] = node->p->normalRecv[node->id - 1]; - node->d.p->p->memory.io[GBA_REG(SIODATA32_HI)] = node->p->normalRecv[node->id - 1] >> 16; + GBASIONormal32FinishTransfer(sio, node->p->normalRecv[node->id - 1], 0); } else { - node->d.p->p->memory.io[GBA_REG(SIODATA32_LO)] = 0xFFFF; - node->d.p->p->memory.io[GBA_REG(SIODATA32_HI)] = 0xFFFF; - } - if (GBASIONormalIsIrq(sio->siocnt)) { - GBARaiseIRQ(sio->p, GBA_IRQ_SIO, 0); + GBASIONormal32FinishTransfer(sio, 0xFFFFFFFF, 0); } break; default: From 9998de48803887633ff052fe12ef11a46c5ef3ff Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 21 May 2024 23:52:55 -0700 Subject: [PATCH 016/114] GBA SIO: Move more write logging out of drivers --- src/gba/sio.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/gba/sio.c b/src/gba/sio.c index 56dc55a9f..e0b0a6d1d 100644 --- a/src/gba/sio.c +++ b/src/gba/sio.c @@ -256,6 +256,11 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) { } uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t value) { + int id = 0; + if (sio->activeDriver && sio->activeDriver->deviceId) { + id = sio->activeDriver->deviceId(sio->activeDriver); + } + switch (sio->mode) { case GBA_SIO_JOYBUS: switch (address) { @@ -283,10 +288,10 @@ uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t valu case GBA_SIO_NORMAL_8: switch (address) { case GBA_REG_SIODATA8: - mLOG(GBA_SIO, DEBUG, "NORMAL8 write: SIODATA8 <- %02X", value); + mLOG(GBA_SIO, DEBUG, "NORMAL8 %i write: SIODATA8 <- %02X", id, value); break; default: - mLOG(GBA_SIO, DEBUG, "NORMAL8 write: Unknown reg %03X <- %04X", address, value); + mLOG(GBA_SIO, DEBUG, "NORMAL8 %i write: Unknown reg %03X <- %04X", id, address, value); break; case GBA_REG_RCNT: break; @@ -295,13 +300,25 @@ uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t valu case GBA_SIO_NORMAL_32: switch (address) { case GBA_REG_SIODATA32_LO: - mLOG(GBA_SIO, DEBUG, "NORMAL32 write: SIODATA32_LO <- %04X", value); + mLOG(GBA_SIO, DEBUG, "NORMAL32 %i write: SIODATA32_LO <- %04X", id, value); break; case GBA_REG_SIODATA32_HI: - mLOG(GBA_SIO, DEBUG, "NORMAL32 write: SIODATA32_HI <- %04X", value); + mLOG(GBA_SIO, DEBUG, "NORMAL32 %i write: SIODATA32_HI <- %04X", id, value); break; default: - mLOG(GBA_SIO, DEBUG, "NORMAL32 write: Unknown reg %03X <- %04X", address, value); + mLOG(GBA_SIO, DEBUG, "NORMAL32 %i write: Unknown reg %03X <- %04X", id, address, value); + break; + case GBA_REG_RCNT: + break; + } + break; + case GBA_SIO_MULTI: + switch (address) { + case GBA_REG_SIOMLT_SEND: + mLOG(GBA_SIO, DEBUG, "MULTI %i write: SIOMLT_SEND <- %04X", id, value); + break; + default: + mLOG(GBA_SIO, DEBUG, "MULTI %i write: Unknown reg %03X <- %04X", id, address, value); break; case GBA_REG_RCNT: break; From 5da4b1fc4d61888564894d5b7f6662428b4647e5 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 22 May 2024 01:40:01 -0700 Subject: [PATCH 017/114] GBA SIO: Replace writeRegister with writeSIOCNT --- include/mgba/gba/interface.h | 2 +- src/gba/extra/battlechip.c | 24 ++----- src/gba/sio.c | 11 +-- src/gba/sio/dolphin.c | 2 +- src/gba/sio/gbp.c | 39 +++++------ src/gba/sio/lockstep.c | 130 ++++++++++++++++------------------- 6 files changed, 88 insertions(+), 120 deletions(-) diff --git a/include/mgba/gba/interface.h b/include/mgba/gba/interface.h index c2a4fb5d2..b698f4030 100644 --- a/include/mgba/gba/interface.h +++ b/include/mgba/gba/interface.h @@ -117,7 +117,7 @@ struct GBASIODriver { bool (*handlesMode)(struct GBASIODriver* driver, enum GBASIOMode mode); int (*connectedDevices)(struct GBASIODriver* driver); int (*deviceId)(struct GBASIODriver* driver); - uint16_t (*writeRegister)(struct GBASIODriver* driver, uint32_t address, uint16_t value); + uint16_t (*writeSIOCNT)(struct GBASIODriver* driver, uint16_t value); }; enum GBASIOBattleChipGateFlavor { diff --git a/src/gba/extra/battlechip.c b/src/gba/extra/battlechip.c index 040ec80b8..b71b4e33e 100644 --- a/src/gba/extra/battlechip.c +++ b/src/gba/extra/battlechip.c @@ -34,7 +34,7 @@ enum { }; static bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver); -static uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value); +static uint16_t GBASIOBattlechipGateWriteSIOCNT(struct GBASIODriver* driver, uint16_t value); static bool GBASIOBattlechipGateHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode); static int GBASIOBattlechipGateConnectedDevices(struct GBASIODriver* driver); @@ -46,7 +46,7 @@ void GBASIOBattlechipGateCreate(struct GBASIOBattlechipGate* gate) { gate->d.deinit = NULL; gate->d.load = GBASIOBattlechipGateLoad; gate->d.unload = NULL; - gate->d.writeRegister = GBASIOBattlechipGateWriteRegister; + gate->d.writeSIOCNT = GBASIOBattlechipGateWriteSIOCNT; gate->d.setMode = NULL; gate->d.handlesMode = GBASIOBattlechipGateHandlesMode; gate->d.connectedDevices = GBASIOBattlechipGateConnectedDevices; @@ -68,22 +68,12 @@ bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver) { return true; } -uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) { +uint16_t GBASIOBattlechipGateWriteSIOCNT(struct GBASIODriver* driver, uint16_t value) { struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver; - switch (address) { - case GBA_REG_SIOCNT: - value &= ~0xC; - value |= 0x8; - if (value & 0x80) { - _battlechipTransfer(gate); - } - break; - case GBA_REG_SIOMLT_SEND: - break; - case GBA_REG_RCNT: - break; - default: - break; + value &= ~0xC; + value |= 0x8; + if (value & 0x80) { + _battlechipTransfer(gate); } return value; } diff --git a/src/gba/sio.c b/src/gba/sio.c index e0b0a6d1d..68b8fcb48 100644 --- a/src/gba/sio.c +++ b/src/gba/sio.c @@ -193,9 +193,6 @@ void GBASIOWriteRCNT(struct GBASIO* sio, uint16_t value) { sio->rcnt &= 0xF; sio->rcnt |= value & ~0xF; _switchMode(sio); - if (sio->activeDriver && sio->activeDriver->writeRegister) { - sio->activeDriver->writeRegister(sio->activeDriver, GBA_REG_RCNT, value); - } } void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) { @@ -213,7 +210,7 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) { id = sio->activeDriver->deviceId(sio->activeDriver); } connected = sio->activeDriver->connectedDevices(sio->activeDriver); - handled = !!sio->activeDriver->writeRegister; + handled = !!sio->activeDriver->writeSIOCNT; } } @@ -229,7 +226,7 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) { break; } if (handled) { - value = sio->activeDriver->writeRegister(sio->activeDriver, GBA_REG_SIOCNT, value); + value = sio->activeDriver->writeSIOCNT(sio->activeDriver, value); } else { // Dummy drivers switch (sio->mode) { @@ -328,10 +325,6 @@ uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t valu // TODO break; } - - if (sio->activeDriver && sio->activeDriver->writeRegister && sio->activeDriver->handlesMode(sio->activeDriver, sio->mode)) { - sio->activeDriver->writeRegister(sio->activeDriver, address, value); - } return value; } diff --git a/src/gba/sio/dolphin.c b/src/gba/sio/dolphin.c index badbd05f7..039f9b528 100644 --- a/src/gba/sio/dolphin.c +++ b/src/gba/sio/dolphin.c @@ -36,7 +36,7 @@ void GBASIODolphinCreate(struct GBASIODolphin* dol) { dol->d.init = GBASIODolphinInit; dol->d.load = GBASIODolphinLoad; dol->d.unload = GBASIODolphinUnload; - dol->d.writeRegister = NULL; + dol->d.writeSIOCNT = NULL; dol->d.setMode = NULL; dol->d.handlesMode = GBASIODolphinHandlesMode; dol->d.connectedDevices = GBASIODolphinConnectedDevices; diff --git a/src/gba/sio/gbp.c b/src/gba/sio/gbp.c index 37944d596..7aeef1b0b 100644 --- a/src/gba/sio/gbp.c +++ b/src/gba/sio/gbp.c @@ -13,7 +13,7 @@ #include static uint16_t _gbpRead(struct mKeyCallback*); -static uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value); +static uint16_t _gbpSioWriteSIOCNT(struct GBASIODriver* driver, uint16_t value); static bool _gbpSioHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode); static int _gbpSioConnectedDevices(struct GBASIODriver* driver); static void _gbpSioProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLate); @@ -49,7 +49,7 @@ void GBASIOPlayerInit(struct GBASIOPlayer* gbp) { gbp->d.deinit = NULL; gbp->d.load = NULL; gbp->d.unload = NULL; - gbp->d.writeRegister = _gbpSioWriteRegister; + gbp->d.writeSIOCNT = _gbpSioWriteSIOCNT; gbp->d.setMode = NULL; gbp->d.handlesMode = _gbpSioHandlesMode; gbp->d.connectedDevices = _gbpSioConnectedDevices; @@ -106,28 +106,27 @@ uint16_t _gbpRead(struct mKeyCallback* callback) { return 0; } -uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) { +uint16_t _gbpSioWriteSIOCNT(struct GBASIODriver* driver, uint16_t value) { struct GBASIOPlayer* gbp = (struct GBASIOPlayer*) driver; - if (address == GBA_REG_SIOCNT) { - if (value & 0x0080) { - uint32_t rx = gbp->p->memory.io[GBA_REG(SIODATA32_LO)] | (gbp->p->memory.io[GBA_REG(SIODATA32_HI)] << 16); - if (gbp->txPosition < 12 && gbp->txPosition > 0) { - // TODO: Check expected - } else if (gbp->txPosition >= 12) { - // 0x00 = Stop - // 0x11 = Hard Stop - // 0x22 = Start - if (gbp->p->rumble) { - int32_t currentTime = mTimingCurrentTime(&gbp->p->timing); - gbp->p->rumble->setRumble(gbp->p->rumble, (rx & 0x33) == 0x22, currentTime - gbp->p->lastRumble); - gbp->p->lastRumble = currentTime; - } + if (value & 0x0080) { + uint32_t rx = gbp->p->memory.io[GBA_REG(SIODATA32_LO)] | (gbp->p->memory.io[GBA_REG(SIODATA32_HI)] << 16); + if (gbp->txPosition < 12 && gbp->txPosition > 0) { + // TODO: Check expected + } else if (gbp->txPosition >= 12) { + uint32_t mask = 0x33; + // 0x00 = Stop + // 0x11 = Hard Stop + // 0x22 = Start + if (gbp->p->rumble) { + int32_t currentTime = mTimingCurrentTime(&gbp->p->timing); + gbp->p->rumble->setRumble(gbp->p->rumble, (rx & 0x33) == 0x22, currentTime - gbp->p->lastRumble); + gbp->p->lastRumble = currentTime; } - mTimingDeschedule(&gbp->p->timing, &gbp->event); - mTimingSchedule(&gbp->p->timing, &gbp->event, 2048); } - value &= 0x78FB; + mTimingDeschedule(&gbp->p->timing, &gbp->event); + mTimingSchedule(&gbp->p->timing, &gbp->event, 2048); } + value &= 0x78FB; return value; } diff --git a/src/gba/sio/lockstep.c b/src/gba/sio/lockstep.c index 67b359234..526d83575 100644 --- a/src/gba/sio/lockstep.c +++ b/src/gba/sio/lockstep.c @@ -19,8 +19,8 @@ static void GBASIOLockstepNodeSetMode(struct GBASIODriver* driver, enum GBASIOMo static bool GBASIOLockstepNodeHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode); static int GBASIOLockstepNodeConnectedDevices(struct GBASIODriver* driver); static int GBASIOLockstepNodeDeviceId(struct GBASIODriver* driver); -static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value); -static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value); +static uint16_t GBASIOLockstepNodeMultiWriteSIOCNT(struct GBASIODriver* driver, uint16_t value); +static uint16_t GBASIOLockstepNodeNormalWriteSIOCNT(struct GBASIODriver* driver, uint16_t value); static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* driver, uint32_t cyclesLate); static void _finishTransfer(struct GBASIOLockstepNode* node); @@ -46,7 +46,7 @@ void GBASIOLockstepNodeCreate(struct GBASIOLockstepNode* node) { node->d.handlesMode = GBASIOLockstepNodeHandlesMode; node->d.connectedDevices = GBASIOLockstepNodeConnectedDevices; node->d.deviceId = GBASIOLockstepNodeDeviceId; - node->d.writeRegister = NULL; + node->d.writeSIOCNT = NULL; } bool GBASIOLockstepAttachNode(struct GBASIOLockstep* lockstep, struct GBASIOLockstepNode* node) { @@ -112,7 +112,7 @@ bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver) { switch (node->mode) { case GBA_SIO_MULTI: - node->d.writeRegister = GBASIOLockstepNodeMultiWriteRegister; + node->d.writeSIOCNT = GBASIOLockstepNodeMultiWriteSIOCNT; ATOMIC_ADD(node->p->attachedMulti, 1); node->d.p->siocnt = GBASIOMultiplayerSetReady(node->d.p->siocnt, node->p->attachedMulti == node->p->d.attached); if (node->id) { @@ -134,7 +134,7 @@ bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver) { } else { node->d.p->siocnt = GBASIONormalClearSi(node->d.p->siocnt); } - node->d.writeRegister = GBASIOLockstepNodeNormalWriteRegister; + node->d.writeSIOCNT = GBASIOLockstepNodeNormalWriteSIOCNT; break; default: break; @@ -231,41 +231,35 @@ static int GBASIOLockstepNodeDeviceId(struct GBASIODriver* driver) { return node->id; } -static uint16_t GBASIOLockstepNodeMultiWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) { +static uint16_t GBASIOLockstepNodeMultiWriteSIOCNT(struct GBASIODriver* driver, uint16_t value) { struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver; mLockstepLock(&node->p->d); - if (address == GBA_REG_SIOCNT) { - mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04X", node->id, value); + mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04X", node->id, value); - enum mLockstepPhase transferActive; - int attached; - ATOMIC_LOAD(transferActive, node->p->d.transferActive); - ATOMIC_LOAD(attached, node->p->d.attached); + enum mLockstepPhase transferActive; + int attached; + ATOMIC_LOAD(transferActive, node->p->d.transferActive); + ATOMIC_LOAD(attached, node->p->d.attached); - driver->p->siocnt = GBASIOMultiplayerSetSlave(driver->p->siocnt, node->id || attached < 2); + driver->p->siocnt = GBASIOMultiplayerSetSlave(driver->p->siocnt, node->id || attached < 2); - if (value & 0x0080 && transferActive == TRANSFER_IDLE) { - if (!node->id && attached > 1 && GBASIOMultiplayerIsReady(node->d.p->siocnt)) { - mLOG(GBA_SIO, DEBUG, "Lockstep %i: Transfer initiated", node->id); - ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING); - ATOMIC_STORE(node->p->d.transferCycles, GBASIOTransferCycles(node->d.p)); + if (value & 0x0080 && transferActive == TRANSFER_IDLE) { + if (!node->id && attached > 1 && GBASIOMultiplayerIsReady(node->d.p->siocnt)) { + mLOG(GBA_SIO, DEBUG, "Lockstep %i: Transfer initiated", node->id); + ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING); + ATOMIC_STORE(node->p->d.transferCycles, GBASIOTransferCycles(node->d.p)); - if (mTimingIsScheduled(&driver->p->p->timing, &node->event)) { - node->eventDiff -= node->event.when - mTimingCurrentTime(&driver->p->p->timing); - mTimingDeschedule(&driver->p->p->timing, &node->event); - } - mTimingSchedule(&driver->p->p->timing, &node->event, 0); + if (mTimingIsScheduled(&driver->p->p->timing, &node->event)) { + node->eventDiff -= node->event.when - mTimingCurrentTime(&driver->p->p->timing); + mTimingDeschedule(&driver->p->p->timing, &node->event); } + mTimingSchedule(&driver->p->p->timing, &node->event, 0); } - value &= 0xFF83; - value |= driver->p->siocnt & 0x00FC; - } else if (address == GBA_REG_SIOMLT_SEND) { - mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOMLT_SEND <- %04X", node->id, value); - } else { - mLOG(GBA_SIO, STUB, "Lockstep %i: Unknown reg %03X <- %04X", node->id, address, value); } + value &= 0xFF83; + value |= driver->p->siocnt & 0x00FC; mLockstepUnlock(&node->p->d); @@ -525,62 +519,54 @@ static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* user, mLockstepUnlock(&node->p->d); } -static uint16_t GBASIOLockstepNodeNormalWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) { +static uint16_t GBASIOLockstepNodeNormalWriteSIOCNT(struct GBASIODriver* driver, uint16_t value) { struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver; mLockstepLock(&node->p->d); - if (address == GBA_REG_SIOCNT) { - mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04X", node->id, value); - int attached; - ATOMIC_LOAD(attached, node->p->attachedNormal); - value &= 0xFF8B; - if (node->id > 0) { - value = GBASIONormalSetSi(value, GBASIONormalGetIdleSo(node->p->players[node->id - 1]->d.p->siocnt)); - } else { - value = GBASIONormalClearSi(value); - } + mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04X", node->id, value); + int attached; + ATOMIC_LOAD(attached, node->p->attachedNormal); + value &= 0xFF8B; + if (node->id > 0) { + value = GBASIONormalSetSi(value, GBASIONormalGetIdleSo(node->p->players[node->id - 1]->d.p->siocnt)); + } else { + value = GBASIONormalClearSi(value); + } - enum mLockstepPhase transferActive; - ATOMIC_LOAD(transferActive, node->p->d.transferActive); - if (node->id < 3 && attached > node->id + 1 && transferActive == TRANSFER_IDLE) { - int try; - for (try = 0; try < 3; ++try) { - GBASIONormal nextSiocnct; - ATOMIC_LOAD(nextSiocnct, node->p->players[node->id + 1]->d.p->siocnt); - if (ATOMIC_CMPXCHG(node->p->players[node->id + 1]->d.p->siocnt, nextSiocnct, GBASIONormalSetSi(nextSiocnct, GBASIONormalGetIdleSo(value)))) { - break; - } + enum mLockstepPhase transferActive; + ATOMIC_LOAD(transferActive, node->p->d.transferActive); + if (node->id < 3 && attached > node->id + 1 && transferActive == TRANSFER_IDLE) { + int try; + for (try = 0; try < 3; ++try) { + GBASIONormal nextSiocnct; + ATOMIC_LOAD(nextSiocnct, node->p->players[node->id + 1]->d.p->siocnt); + if (ATOMIC_CMPXCHG(node->p->players[node->id + 1]->d.p->siocnt, nextSiocnct, GBASIONormalSetSi(nextSiocnct, GBASIONormalGetIdleSo(value)))) { + break; } } - if ((value & 0x0081) == 0x0081) { - if (!node->id) { - // Frequency - int32_t cycles = GBASIOTransferCycles(node->d.p); + } + if ((value & 0x0081) == 0x0081) { + if (!node->id) { + // Frequency + int32_t cycles = GBASIOTransferCycles(node->d.p); - if (transferActive == TRANSFER_IDLE) { - mLOG(GBA_SIO, DEBUG, "Lockstep %i: Transfer initiated", node->id); - ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING); - ATOMIC_STORE(node->p->d.transferCycles, cycles); + if (transferActive == TRANSFER_IDLE) { + mLOG(GBA_SIO, DEBUG, "Lockstep %i: Transfer initiated", node->id); + ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING); + ATOMIC_STORE(node->p->d.transferCycles, cycles); - if (mTimingIsScheduled(&driver->p->p->timing, &node->event)) { - node->eventDiff -= node->event.when - mTimingCurrentTime(&driver->p->p->timing); - mTimingDeschedule(&driver->p->p->timing, &node->event); - } - mTimingSchedule(&driver->p->p->timing, &node->event, 0); - } else { - value &= ~0x0080; + if (mTimingIsScheduled(&driver->p->p->timing, &node->event)) { + node->eventDiff -= node->event.when - mTimingCurrentTime(&driver->p->p->timing); + mTimingDeschedule(&driver->p->p->timing, &node->event); } + mTimingSchedule(&driver->p->p->timing, &node->event, 0); } else { - // TODO + value &= ~0x0080; } + } else { + // TODO } - } else if (address == GBA_REG_SIODATA32_LO) { - mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_LO <- %04X", node->id, value); - } else if (address == GBA_REG_SIODATA32_HI) { - mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_HI <- %04X", node->id, value); - } else if (address == GBA_REG_SIODATA8) { - mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA8 <- %02X", node->id, value); } mLockstepUnlock(&node->p->d); From aad552ff47deb92b929f6146fd6d795125c869af Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 22 May 2024 22:42:58 -0700 Subject: [PATCH 018/114] GBA SIO: Add writeRCNT callback, mostly for GPIO mode --- include/mgba/gba/interface.h | 1 + src/gba/extra/battlechip.c | 1 + src/gba/sio.c | 12 ++++-------- src/gba/sio/dolphin.c | 1 + src/gba/sio/gbp.c | 1 + src/gba/sio/lockstep.c | 1 + 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/include/mgba/gba/interface.h b/include/mgba/gba/interface.h index b698f4030..f83f9e717 100644 --- a/include/mgba/gba/interface.h +++ b/include/mgba/gba/interface.h @@ -118,6 +118,7 @@ struct GBASIODriver { int (*connectedDevices)(struct GBASIODriver* driver); int (*deviceId)(struct GBASIODriver* driver); uint16_t (*writeSIOCNT)(struct GBASIODriver* driver, uint16_t value); + uint16_t (*writeRCNT)(struct GBASIODriver* driver, uint16_t value); }; enum GBASIOBattleChipGateFlavor { diff --git a/src/gba/extra/battlechip.c b/src/gba/extra/battlechip.c index b71b4e33e..4070626d8 100644 --- a/src/gba/extra/battlechip.c +++ b/src/gba/extra/battlechip.c @@ -51,6 +51,7 @@ void GBASIOBattlechipGateCreate(struct GBASIOBattlechipGate* gate) { gate->d.handlesMode = GBASIOBattlechipGateHandlesMode; gate->d.connectedDevices = GBASIOBattlechipGateConnectedDevices; gate->d.deviceId = NULL; + gate->d.writeRCNT = NULL; gate->event.context = gate; gate->event.callback = _battlechipTransferEvent; diff --git a/src/gba/sio.c b/src/gba/sio.c index 68b8fcb48..14edd7b7a 100644 --- a/src/gba/sio.c +++ b/src/gba/sio.c @@ -193,6 +193,10 @@ void GBASIOWriteRCNT(struct GBASIO* sio, uint16_t value) { sio->rcnt &= 0xF; sio->rcnt |= value & ~0xF; _switchMode(sio); + if (sio->activeDriver && sio->activeDriver->writeRCNT) { + sio->rcnt &= 0xC000; + sio->rcnt |= sio->activeDriver->writeRCNT(sio->activeDriver, value) & 0x01FF; + } } void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) { @@ -278,8 +282,6 @@ uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t valu default: mLOG(GBA_SIO, DEBUG, "JOY write: Unknown reg %03X <- %04X", address, value); break; - case GBA_REG_RCNT: - break; } break; case GBA_SIO_NORMAL_8: @@ -290,8 +292,6 @@ uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t valu default: mLOG(GBA_SIO, DEBUG, "NORMAL8 %i write: Unknown reg %03X <- %04X", id, address, value); break; - case GBA_REG_RCNT: - break; } break; case GBA_SIO_NORMAL_32: @@ -305,8 +305,6 @@ uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t valu default: mLOG(GBA_SIO, DEBUG, "NORMAL32 %i write: Unknown reg %03X <- %04X", id, address, value); break; - case GBA_REG_RCNT: - break; } break; case GBA_SIO_MULTI: @@ -317,8 +315,6 @@ uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t valu default: mLOG(GBA_SIO, DEBUG, "MULTI %i write: Unknown reg %03X <- %04X", id, address, value); break; - case GBA_REG_RCNT: - break; } break; default: diff --git a/src/gba/sio/dolphin.c b/src/gba/sio/dolphin.c index 039f9b528..f7aad747b 100644 --- a/src/gba/sio/dolphin.c +++ b/src/gba/sio/dolphin.c @@ -41,6 +41,7 @@ void GBASIODolphinCreate(struct GBASIODolphin* dol) { dol->d.handlesMode = GBASIODolphinHandlesMode; dol->d.connectedDevices = GBASIODolphinConnectedDevices; dol->d.deviceId = NULL; + dol->d.writeSIOCNT = NULL; dol->event.context = dol; dol->event.name = "GB SIO Lockstep"; dol->event.callback = GBASIODolphinProcessEvents; diff --git a/src/gba/sio/gbp.c b/src/gba/sio/gbp.c index 7aeef1b0b..f7b3747a1 100644 --- a/src/gba/sio/gbp.c +++ b/src/gba/sio/gbp.c @@ -54,6 +54,7 @@ void GBASIOPlayerInit(struct GBASIOPlayer* gbp) { gbp->d.handlesMode = _gbpSioHandlesMode; gbp->d.connectedDevices = _gbpSioConnectedDevices; gbp->d.deviceId = NULL; + gbp->d.writeRCNT = NULL; gbp->event.context = gbp; gbp->event.name = "GBA SIO Game Boy Player"; gbp->event.callback = _gbpSioProcessEvents; diff --git a/src/gba/sio/lockstep.c b/src/gba/sio/lockstep.c index 526d83575..30092c3ec 100644 --- a/src/gba/sio/lockstep.c +++ b/src/gba/sio/lockstep.c @@ -47,6 +47,7 @@ void GBASIOLockstepNodeCreate(struct GBASIOLockstepNode* node) { node->d.connectedDevices = GBASIOLockstepNodeConnectedDevices; node->d.deviceId = GBASIOLockstepNodeDeviceId; node->d.writeSIOCNT = NULL; + node->d.writeRCNT = NULL; } bool GBASIOLockstepAttachNode(struct GBASIOLockstep* lockstep, struct GBASIOLockstepNode* node) { From aeb547e3dc621a6ba4f14cbcb3b04941049e54c6 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 22 May 2024 23:04:44 -0700 Subject: [PATCH 019/114] GBA SIO: Finish up GBASIOWriteRegister logging --- src/gba/sio.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/gba/sio.c b/src/gba/sio.c index 14edd7b7a..ec29a62a5 100644 --- a/src/gba/sio.c +++ b/src/gba/sio.c @@ -280,7 +280,7 @@ uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t valu mLOG(GBA_SIO, DEBUG, "JOY write: TRANS_HI <- %04X", value); break; default: - mLOG(GBA_SIO, DEBUG, "JOY write: Unknown reg %03X <- %04X", address, value); + mLOG(GBA_SIO, GAME_ERROR, "JOY write: Unknown reg %03X <- %04X", address, value); break; } break; @@ -290,7 +290,7 @@ uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t valu mLOG(GBA_SIO, DEBUG, "NORMAL8 %i write: SIODATA8 <- %02X", id, value); break; default: - mLOG(GBA_SIO, DEBUG, "NORMAL8 %i write: Unknown reg %03X <- %04X", id, address, value); + mLOG(GBA_SIO, GAME_ERROR, "NORMAL8 %i write: Unknown reg %03X <- %04X", id, address, value); break; } break; @@ -303,7 +303,7 @@ uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t valu mLOG(GBA_SIO, DEBUG, "NORMAL32 %i write: SIODATA32_HI <- %04X", id, value); break; default: - mLOG(GBA_SIO, DEBUG, "NORMAL32 %i write: Unknown reg %03X <- %04X", id, address, value); + mLOG(GBA_SIO, GAME_ERROR, "NORMAL32 %i write: Unknown reg %03X <- %04X", id, address, value); break; } break; @@ -313,12 +313,22 @@ uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t valu mLOG(GBA_SIO, DEBUG, "MULTI %i write: SIOMLT_SEND <- %04X", id, value); break; default: - mLOG(GBA_SIO, DEBUG, "MULTI %i write: Unknown reg %03X <- %04X", id, address, value); + mLOG(GBA_SIO, GAME_ERROR, "MULTI %i write: Unknown reg %03X <- %04X", id, address, value); break; } break; - default: - // TODO + case GBA_SIO_UART: + switch (address) { + case GBA_REG_SIODATA8: + mLOG(GBA_SIO, DEBUG, "UART write: SIODATA8 <- %02X", value); + break; + default: + mLOG(GBA_SIO, GAME_ERROR, "UART write: Unknown reg %03X <- %04X", address, value); + break; + } + break; + case GBA_SIO_GPIO: + mLOG(GBA_SIO, GAME_ERROR, "GPIO %i write: Unknown reg %03X <- %04X", id, address, value); break; } return value; From b572e8b09fbe6ac6052da30d730601d89425b17b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 26 Sep 2024 02:52:25 -0700 Subject: [PATCH 020/114] GBA SIO: Fix SIO register writing per mode --- src/gba/io.c | 1 + src/gba/sio.c | 48 ++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/gba/io.c b/src/gba/io.c index ea9d8d8b0..d29fb8a4b 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -483,6 +483,7 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) { // SIO case GBA_REG_SIOCNT: + value &= 0x7FFF; GBASIOWriteSIOCNT(&gba->sio, value); break; case GBA_REG_RCNT: diff --git a/src/gba/sio.c b/src/gba/sio.c index ec29a62a5..62ce0adb7 100644 --- a/src/gba/sio.c +++ b/src/gba/sio.c @@ -262,9 +262,13 @@ uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t valu id = sio->activeDriver->deviceId(sio->activeDriver); } + bool handled = true; switch (sio->mode) { case GBA_SIO_JOYBUS: switch (address) { + case GBA_REG_SIODATA8: + mLOG(GBA_SIO, DEBUG, "JOY write: SIODATA8 (?) <- %04X", value); + break; case GBA_REG_JOYCNT: mLOG(GBA_SIO, DEBUG, "JOY write: CNT <- %04X", value); value = (value & 0x0040) | (sio->p->memory.io[GBA_REG(JOYCNT)] & ~(value & 0x7) & ~0x0040); @@ -280,17 +284,23 @@ uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t valu mLOG(GBA_SIO, DEBUG, "JOY write: TRANS_HI <- %04X", value); break; default: - mLOG(GBA_SIO, GAME_ERROR, "JOY write: Unknown reg %03X <- %04X", address, value); + mLOG(GBA_SIO, GAME_ERROR, "JOY write: Unhandled %s <- %04X", GBAIORegisterNames[address >> 1], value); + handled = false; break; } break; case GBA_SIO_NORMAL_8: switch (address) { case GBA_REG_SIODATA8: - mLOG(GBA_SIO, DEBUG, "NORMAL8 %i write: SIODATA8 <- %02X", id, value); + mLOG(GBA_SIO, DEBUG, "NORMAL8 %i write: SIODATA8 <- %04X", id, value); + break; + case GBA_REG_JOYCNT: + mLOG(GBA_SIO, DEBUG, "NORMAL8 %i write: JOYCNT (?) <- %04X", id, value); + value = (value & 0x0040) | (sio->p->memory.io[GBA_REG(JOYCNT)] & ~(value & 0x7) & ~0x0040); break; default: - mLOG(GBA_SIO, GAME_ERROR, "NORMAL8 %i write: Unknown reg %03X <- %04X", id, address, value); + mLOG(GBA_SIO, GAME_ERROR, "NORMAL8 %i write: Unhandled %s <- %04X", id, GBAIORegisterNames[address >> 1], value); + handled = false; break; } break; @@ -302,8 +312,16 @@ uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t valu case GBA_REG_SIODATA32_HI: mLOG(GBA_SIO, DEBUG, "NORMAL32 %i write: SIODATA32_HI <- %04X", id, value); break; + case GBA_REG_SIODATA8: + mLOG(GBA_SIO, DEBUG, "NORMAL32 %i write: SIODATA8 (?) <- %04X", id, value); + break; + case GBA_REG_JOYCNT: + mLOG(GBA_SIO, DEBUG, "NORMAL32 %i write: JOYCNT (?) <- %04X", id, value); + value = (value & 0x0040) | (sio->p->memory.io[GBA_REG(JOYCNT)] & ~(value & 0x7) & ~0x0040); + break; default: - mLOG(GBA_SIO, GAME_ERROR, "NORMAL32 %i write: Unknown reg %03X <- %04X", id, address, value); + mLOG(GBA_SIO, GAME_ERROR, "NORMAL32 %i write: Unhandled %s <- %04X", id, GBAIORegisterNames[address >> 1], value); + handled = false; break; } break; @@ -312,25 +330,39 @@ uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t valu case GBA_REG_SIOMLT_SEND: mLOG(GBA_SIO, DEBUG, "MULTI %i write: SIOMLT_SEND <- %04X", id, value); break; + case GBA_REG_JOYCNT: + mLOG(GBA_SIO, DEBUG, "MULTI %i write: JOYCNT (?) <- %04X", id, value); + value = (value & 0x0040) | (sio->p->memory.io[GBA_REG(JOYCNT)] & ~(value & 0x7) & ~0x0040); + break; default: - mLOG(GBA_SIO, GAME_ERROR, "MULTI %i write: Unknown reg %03X <- %04X", id, address, value); + mLOG(GBA_SIO, GAME_ERROR, "MULTI %i write: Unhandled %s <- %04X", id, GBAIORegisterNames[address >> 1], value); + handled = false; break; } break; case GBA_SIO_UART: switch (address) { case GBA_REG_SIODATA8: - mLOG(GBA_SIO, DEBUG, "UART write: SIODATA8 <- %02X", value); + mLOG(GBA_SIO, DEBUG, "UART write: SIODATA8 <- %04X", value); + break; + case GBA_REG_JOYCNT: + mLOG(GBA_SIO, DEBUG, "UART write: JOYCNT (?) <- %04X", value); + value = (value & 0x0040) | (sio->p->memory.io[GBA_REG(JOYCNT)] & ~(value & 0x7) & ~0x0040); break; default: - mLOG(GBA_SIO, GAME_ERROR, "UART write: Unknown reg %03X <- %04X", address, value); + mLOG(GBA_SIO, GAME_ERROR, "UART write: Unhandled %s <- %04X", GBAIORegisterNames[address >> 1], value); + handled = false; break; } break; case GBA_SIO_GPIO: - mLOG(GBA_SIO, GAME_ERROR, "GPIO %i write: Unknown reg %03X <- %04X", id, address, value); + mLOG(GBA_SIO, STUB, "GPIO write: Unhandled %s <- %04X", GBAIORegisterNames[address >> 1], value); + handled = false; break; } + if (!handled) { + value = sio->p->memory.io[address >> 1]; + } return value; } From ab655db3f8b6a72d167fbd60e4963dd0c6805666 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 24 May 2024 01:32:55 -0700 Subject: [PATCH 021/114] GBA SIO: Fix NORMAL timing out --- include/mgba/internal/gba/sio.h | 1 + src/gba/sio.c | 42 ++++++++++++++++++++++++++++----- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/include/mgba/internal/gba/sio.h b/include/mgba/internal/gba/sio.h index 43acaa7b8..804f0f52a 100644 --- a/include/mgba/internal/gba/sio.h +++ b/include/mgba/internal/gba/sio.h @@ -70,6 +70,7 @@ struct GBASIO { uint16_t siocnt; struct GBASIOPlayer gbp; + struct mTimingEvent completeEvent; }; void GBASIOInit(struct GBASIO* sio); diff --git a/src/gba/sio.c b/src/gba/sio.c index 62ce0adb7..710cd9d45 100644 --- a/src/gba/sio.c +++ b/src/gba/sio.c @@ -18,6 +18,8 @@ static const int GBASIOCyclesPerTransfer[4][MAX_GBAS] = { { 3140, 5755, 8376, 10486 } }; +static void _sioFinish(struct mTiming* timing, void* user, uint32_t cyclesLate); + static struct GBASIODriver* _lookupDriver(struct GBASIO* sio, enum GBASIOMode mode) { switch (mode) { case GBA_SIO_NORMAL_8: @@ -90,10 +92,15 @@ static void _switchMode(struct GBASIO* sio) { } void GBASIOInit(struct GBASIO* sio) { - sio->drivers.normal = 0; - sio->drivers.multiplayer = 0; - sio->drivers.joybus = 0; - sio->activeDriver = 0; + sio->drivers.normal = NULL; + sio->drivers.multiplayer = NULL; + sio->drivers.joybus = NULL; + sio->activeDriver = NULL; + + sio->completeEvent.context = sio; + sio->completeEvent.name = "GBA SIO Complete"; + sio->completeEvent.callback = _sioFinish; + sio->completeEvent.priority = 0x80; sio->gbp.p = sio->p; GBASIOPlayerInit(&sio->gbp); @@ -239,10 +246,12 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) { value = GBASIONormalFillSi(value); if ((value & 0x0081) == 0x0081) { if (GBASIONormalIsIrq(value)) { + mTimingDeschedule(&sio->p->timing, &sio->completeEvent); + mTimingSchedule(&sio->p->timing, &sio->completeEvent, GBASIOTransferCycles(sio)); + } else { // TODO: Test this on hardware to see if this is correct - GBARaiseIRQ(sio->p, GBA_IRQ_SIO, 0); + value = GBASIONormalClearStart(value); } - value = GBASIONormalClearStart(value); } break; case GBA_SIO_MULTI: @@ -425,6 +434,27 @@ void GBASIONormal32FinishTransfer(struct GBASIO* sio, uint32_t data, uint32_t cy } } +static void _sioFinish(struct mTiming* timing, void* user, uint32_t cyclesLate) { + UNUSED(timing); + struct GBASIO* sio = user; + uint16_t data[4] = {0, 0, 0, 0}; + switch (sio->mode) { + case GBA_SIO_MULTI: + GBASIOMultiplayerFinishTransfer(sio, data, cyclesLate); + break; + case GBA_SIO_NORMAL_8: + GBASIONormal8FinishTransfer(sio, 0, cyclesLate); + break; + case GBA_SIO_NORMAL_32: + GBASIONormal32FinishTransfer(sio, 0, cyclesLate); + break; + default: + // TODO + mLOG(GBA_SIO, STUB, "No dummy finish implemented for mode %s", _modeName(sio->mode)); + break; + } +} + int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command, uint8_t* data) { switch (command) { case JOY_RESET: From 914d8798116c4f713c19ad050394d195417c047d Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 24 May 2024 03:01:44 -0700 Subject: [PATCH 022/114] GBA SIO: Allow seamless mode switching if driver supports it --- src/gba/sio.c | 22 +++++++++++++++------- src/gba/sio/lockstep.c | 10 +--------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/gba/sio.c b/src/gba/sio.c index 710cd9d45..54fff7ea1 100644 --- a/src/gba/sio.c +++ b/src/gba/sio.c @@ -60,16 +60,24 @@ static void _switchMode(struct GBASIO* sio) { newMode = (enum GBASIOMode) (mode & 0xC); } if (newMode != sio->mode) { - if (sio->activeDriver && sio->activeDriver->unload) { - sio->activeDriver->unload(sio->activeDriver); - } + struct GBASIODriver* driver = _lookupDriver(sio, newMode); if (sio->mode != (enum GBASIOMode) -1) { mLOG(GBA_SIO, DEBUG, "Switching mode from %s to %s", _modeName(sio->mode), _modeName(newMode)); } - sio->mode = newMode; - sio->activeDriver = _lookupDriver(sio, sio->mode); - if (sio->activeDriver && sio->activeDriver->load) { - sio->activeDriver->load(sio->activeDriver); + if (driver != sio->activeDriver || (driver && !driver->setMode)) { + if (sio->activeDriver && sio->activeDriver->unload) { + sio->activeDriver->unload(sio->activeDriver); + } + sio->mode = newMode; + sio->activeDriver = driver; + if (sio->activeDriver && sio->activeDriver->load) { + sio->activeDriver->load(sio->activeDriver); + } + } else { + sio->mode = newMode; + if (sio->activeDriver && sio->activeDriver->setMode) { + sio->activeDriver->setMode(sio->activeDriver, newMode); + } } int id = 0; diff --git a/src/gba/sio/lockstep.c b/src/gba/sio/lockstep.c index 30092c3ec..f7b82f089 100644 --- a/src/gba/sio/lockstep.c +++ b/src/gba/sio/lockstep.c @@ -15,7 +15,6 @@ static bool GBASIOLockstepNodeInit(struct GBASIODriver* driver); static void GBASIOLockstepNodeDeinit(struct GBASIODriver* driver); static bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver); static bool GBASIOLockstepNodeUnload(struct GBASIODriver* driver); -static void GBASIOLockstepNodeSetMode(struct GBASIODriver* driver, enum GBASIOMode mode); static bool GBASIOLockstepNodeHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode); static int GBASIOLockstepNodeConnectedDevices(struct GBASIODriver* driver); static int GBASIOLockstepNodeDeviceId(struct GBASIODriver* driver); @@ -42,7 +41,7 @@ void GBASIOLockstepNodeCreate(struct GBASIOLockstepNode* node) { node->d.deinit = GBASIOLockstepNodeDeinit; node->d.load = GBASIOLockstepNodeLoad; node->d.unload = GBASIOLockstepNodeUnload; - node->d.setMode = GBASIOLockstepNodeSetMode; + node->d.setMode = NULL; node->d.handlesMode = GBASIOLockstepNodeHandlesMode; node->d.connectedDevices = GBASIOLockstepNodeConnectedDevices; node->d.deviceId = GBASIOLockstepNodeDeviceId; @@ -190,13 +189,6 @@ bool GBASIOLockstepNodeUnload(struct GBASIODriver* driver) { return true; } -static void GBASIOLockstepNodeSetMode(struct GBASIODriver* driver, enum GBASIOMode mode) { - struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver; - mLockstepLock(&node->p->d); - node->mode = mode; - mLockstepUnlock(&node->p->d); -} - static bool GBASIOLockstepNodeHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode) { UNUSED(driver); switch (mode) { From 0425dadee9bdb1eebaae80f523037fd62c39e004 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 26 May 2024 00:46:00 -0700 Subject: [PATCH 023/114] GBA SIO: Add RCNT bitfield --- include/mgba/internal/gba/sio.h | 9 +++++++++ src/gba/sio.c | 16 ++++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/include/mgba/internal/gba/sio.h b/include/mgba/internal/gba/sio.h index 804f0f52a..bb7b2a78c 100644 --- a/include/mgba/internal/gba/sio.h +++ b/include/mgba/internal/gba/sio.h @@ -52,6 +52,15 @@ DECL_BITS(GBASIOMultiplayer, Id, 4, 2); DECL_BIT(GBASIOMultiplayer, Error, 6); DECL_BIT(GBASIOMultiplayer, Busy, 7); DECL_BIT(GBASIOMultiplayer, Irq, 14); +DECL_BITFIELD(GBASIORegisterRCNT, uint16_t); +DECL_BIT(GBASIORegisterRCNT, Sc, 0); +DECL_BIT(GBASIORegisterRCNT, Sd, 1); +DECL_BIT(GBASIORegisterRCNT, Si, 2); +DECL_BIT(GBASIORegisterRCNT, So, 3); +DECL_BIT(GBASIORegisterRCNT, ScDirection, 4); +DECL_BIT(GBASIORegisterRCNT, SdDirection, 5); +DECL_BIT(GBASIORegisterRCNT, SiDirection, 6); +DECL_BIT(GBASIORegisterRCNT, SoDirection, 7); struct GBASIODriverSet { struct GBASIODriver* normal; diff --git a/src/gba/sio.c b/src/gba/sio.c index 54fff7ea1..b10d2c257 100644 --- a/src/gba/sio.c +++ b/src/gba/sio.c @@ -86,11 +86,7 @@ static void _switchMode(struct GBASIO* sio) { if (sio->activeDriver && sio->activeDriver->deviceId) { id = sio->activeDriver->deviceId(sio->activeDriver); } - if (id) { - sio->rcnt |= 4; - } else { - sio->rcnt &= ~4; - } + sio->rcnt = GBASIORegisterRCNTSetSi(sio->rcnt, !!id); break; default: // TODO @@ -417,9 +413,17 @@ void GBASIOMultiplayerFinishTransfer(struct GBASIO* sio, uint16_t data[4], uint3 sio->p->memory.io[GBA_REG(SIOMULTI1)] = data[1]; sio->p->memory.io[GBA_REG(SIOMULTI2)] = data[2]; sio->p->memory.io[GBA_REG(SIOMULTI3)] = data[3]; - sio->rcnt |= 1; + sio->siocnt = GBASIOMultiplayerClearBusy(sio->siocnt); sio->siocnt = GBASIOMultiplayerSetId(sio->siocnt, id); + + // This SC level is actually a transient pulse, and probably a hardware glitch. + // Based on analog sampling it seems to just be a spike when the other lines deassert. + // It rapidly falls down to GND but it's high enough that it's read out as a 1 for + // several microseconds, likely around 300-500 cycles. I have not measured if and when + // it returns to 0 afterwards. + sio->rcnt = GBASIORegisterRCNTFillSc(sio->rcnt); + if (GBASIOMultiplayerIsIrq(sio->siocnt)) { GBARaiseIRQ(sio->p, GBA_IRQ_SIO, cyclesLate); } From 435c4aa2436addc27e7d0f8439a3aa0281b820bb Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 15 Sep 2024 23:26:58 -0700 Subject: [PATCH 024/114] GBA SIO: Improve RCNT emulation --- src/gba/sio.c | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/src/gba/sio.c b/src/gba/sio.c index b10d2c257..acc45e607 100644 --- a/src/gba/sio.c +++ b/src/gba/sio.c @@ -201,12 +201,23 @@ void GBASIOSetDriver(struct GBASIO* sio, struct GBASIODriver* driver, enum GBASI } void GBASIOWriteRCNT(struct GBASIO* sio, uint16_t value) { - sio->rcnt &= 0xF; - sio->rcnt |= value & ~0xF; + sio->rcnt &= 0x1FF; + sio->rcnt |= value & 0xC000; _switchMode(sio); if (sio->activeDriver && sio->activeDriver->writeRCNT) { + switch (sio->mode) { + case GBA_SIO_GPIO: + sio->rcnt = (sio->activeDriver->writeRCNT(sio->activeDriver, value) & 0x01FF) | (sio->rcnt & 0xC000); + break; + default: + sio->rcnt = (sio->activeDriver->writeRCNT(sio->activeDriver, value) & 0x01F0) | (sio->rcnt & 0xC00F); + } + } else if (sio->mode == GBA_SIO_GPIO) { sio->rcnt &= 0xC000; - sio->rcnt |= sio->activeDriver->writeRCNT(sio->activeDriver, value) & 0x01FF; + sio->rcnt |= value & 0x1FF; + } else { + sio->rcnt &= 0xC00F; + sio->rcnt |= value & 0x1F0; } } @@ -235,6 +246,24 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) { value = GBASIOMultiplayerSetSlave(value, id || !connected); value = GBASIOMultiplayerSetId(value, id); value |= sio->siocnt & 0x00FC; + + // SC appears to float in multi mode when not doing a transfer. While + // it does spike at the end of a transfer, it appears to die down after + // around 20-30 microseconds. However, the docs on akkit.org + // (http://www.akkit.org/info/gba_comms.html) say this is high until + // a transfer starts and low while active. Further, the Mario Bros. + // multiplayer expects SC to be high in multi mode. This needs better + // investigation than I managed, apparently. + sio->rcnt = GBASIORegisterRCNTFillSc(sio->rcnt); + + break; + case GBA_SIO_NORMAL_8: + case GBA_SIO_NORMAL_32: + // This line is pulled up by the clock owner while the clock is idle. + // If there is no clock owner it's just hi-Z. + if (GBASIONormalGetSc(value)) { + sio->rcnt = GBASIORegisterRCNTFillSc(sio->rcnt); + } break; default: // TODO @@ -417,11 +446,6 @@ void GBASIOMultiplayerFinishTransfer(struct GBASIO* sio, uint16_t data[4], uint3 sio->siocnt = GBASIOMultiplayerClearBusy(sio->siocnt); sio->siocnt = GBASIOMultiplayerSetId(sio->siocnt, id); - // This SC level is actually a transient pulse, and probably a hardware glitch. - // Based on analog sampling it seems to just be a spike when the other lines deassert. - // It rapidly falls down to GND but it's high enough that it's read out as a 1 for - // several microseconds, likely around 300-500 cycles. I have not measured if and when - // it returns to 0 afterwards. sio->rcnt = GBASIORegisterRCNTFillSc(sio->rcnt); if (GBASIOMultiplayerIsIrq(sio->siocnt)) { From 621eb4d4257ce2fc1894812bc6278e6404dc094b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 27 May 2024 23:11:22 -0700 Subject: [PATCH 025/114] GBA SIO: Move starting/end timing logic out of drivers --- include/mgba/gba/interface.h | 5 ++- include/mgba/internal/gba/serialize.h | 4 +- include/mgba/internal/gba/sio/gbp.h | 1 - src/gba/cart/gpio.c | 14 +++---- src/gba/extra/battlechip.c | 45 +++++--------------- src/gba/sio.c | 60 +++++++++++++++++++++------ src/gba/sio/dolphin.c | 5 +-- src/gba/sio/gbp.c | 59 +++++++++++--------------- src/gba/sio/lockstep.c | 14 ++++--- 9 files changed, 104 insertions(+), 103 deletions(-) diff --git a/include/mgba/gba/interface.h b/include/mgba/gba/interface.h index f83f9e717..3d52e7743 100644 --- a/include/mgba/gba/interface.h +++ b/include/mgba/gba/interface.h @@ -119,6 +119,10 @@ struct GBASIODriver { int (*deviceId)(struct GBASIODriver* driver); uint16_t (*writeSIOCNT)(struct GBASIODriver* driver, uint16_t value); uint16_t (*writeRCNT)(struct GBASIODriver* driver, uint16_t value); + bool (*start)(struct GBASIODriver* driver); + void (*finishMultiplayer)(struct GBASIODriver* driver, uint16_t data[4]); + uint8_t (*finishNormal8)(struct GBASIODriver* driver); + uint32_t (*finishNormal32)(struct GBASIODriver* driver); }; enum GBASIOBattleChipGateFlavor { @@ -130,7 +134,6 @@ enum GBASIOBattleChipGateFlavor { struct GBASIOBattlechipGate { struct GBASIODriver d; - struct mTimingEvent event; uint16_t chipId; uint16_t data[2]; int state; diff --git a/include/mgba/internal/gba/serialize.h b/include/mgba/internal/gba/serialize.h index 9bd21821a..c824e85aa 100644 --- a/include/mgba/internal/gba/serialize.h +++ b/include/mgba/internal/gba/serialize.h @@ -191,7 +191,7 @@ mLOG_DECLARE_CATEGORY(GBA_STATE); * | bits 2 - 3: GB Player inputs posted * | bits 4 - 8: GB Player transmit position * | bits 9 - 23: Reserved - * 0x002C4 - 0x002C7: Game Boy Player next event + * 0x002C4 - 0x002C7: SIO next event * 0x002C8 - 0x002CB: Current DMA transfer word * 0x002CC - 0x002CF: Last DMA transfer PC * 0x002D0 - 0x002DF: Matrix memory command buffer @@ -370,7 +370,7 @@ struct GBASerializedState { uint8_t lightSample; GBASerializedHWFlags2 flags2; GBASerializedHWFlags3 flags3; - uint32_t gbpNextEvent; + uint32_t sioNextEvent; } hw; uint32_t dmaTransferRegister; diff --git a/include/mgba/internal/gba/sio/gbp.h b/include/mgba/internal/gba/sio/gbp.h index 6713cdcaf..fe32cd8c1 100644 --- a/include/mgba/internal/gba/sio/gbp.h +++ b/include/mgba/internal/gba/sio/gbp.h @@ -21,7 +21,6 @@ struct GBASIOPlayer { struct GBA* p; unsigned inputsPosted; int txPosition; - struct mTimingEvent event; struct GBASIOPlayerKeyCallback callback; bool oldOpposingDirections; struct mKeyCallback* oldCallback; diff --git a/src/gba/cart/gpio.c b/src/gba/cart/gpio.c index fde4e3714..a04a5f6b3 100644 --- a/src/gba/cart/gpio.c +++ b/src/gba/cart/gpio.c @@ -486,10 +486,10 @@ void GBAHardwareSerialize(const struct GBACartridgeHardware* hw, struct GBASeria flags2 = GBASerializedHWFlags2SetTiltState(flags2, hw->tiltState); flags2 = GBASerializedHWFlags1SetLightCounter(flags2, hw->lightCounter); - // GBP stuff is only here for legacy reasons + // GBP/SIO stuff is only here for legacy reasons flags2 = GBASerializedHWFlags2SetGbpInputsPosted(flags2, hw->p->sio.gbp.inputsPosted); flags2 = GBASerializedHWFlags2SetGbpTxPosition(flags2, hw->p->sio.gbp.txPosition); - STORE_32(hw->p->sio.gbp.event.when - mTimingCurrentTime(&hw->p->timing), 0, &state->hw.gbpNextEvent); + STORE_32(hw->p->sio.completeEvent.when - mTimingCurrentTime(&hw->p->timing), 0, &state->hw.sioNextEvent); state->hw.flags2 = flags2; } @@ -520,16 +520,16 @@ void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, const struct GBASer hw->lightSample = state->hw.lightSample; hw->lightEdge = GBASerializedHWFlags1GetLightEdge(flags1); - // GBP stuff is only here for legacy reasons + // GBP/SIO stuff is only here for legacy reasons hw->p->sio.gbp.inputsPosted = GBASerializedHWFlags2GetGbpInputsPosted(state->hw.flags2); hw->p->sio.gbp.txPosition = GBASerializedHWFlags2GetGbpTxPosition(state->hw.flags2); uint32_t when; - LOAD_32(when, 0, &state->hw.gbpNextEvent); + LOAD_32(when, 0, &state->hw.sioNextEvent); if (hw->devices & HW_GB_PLAYER) { GBASIOSetDriver(&hw->p->sio, &hw->p->sio.gbp.d, GBA_SIO_NORMAL_32); - if (hw->p->memory.io[GBA_REG(SIOCNT)] & 0x0080) { - mTimingSchedule(&hw->p->timing, &hw->p->sio.gbp.event, when); - } + } + if ((hw->p->memory.io[GBA_REG(SIOCNT)] & 0x0080) && when < 0x20000) { + mTimingSchedule(&hw->p->timing, &hw->p->sio.completeEvent, when); } } diff --git a/src/gba/extra/battlechip.c b/src/gba/extra/battlechip.c index 4070626d8..a6eda3bdf 100644 --- a/src/gba/extra/battlechip.c +++ b/src/gba/extra/battlechip.c @@ -37,25 +37,15 @@ static bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver); static uint16_t GBASIOBattlechipGateWriteSIOCNT(struct GBASIODriver* driver, uint16_t value); static bool GBASIOBattlechipGateHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode); static int GBASIOBattlechipGateConnectedDevices(struct GBASIODriver* driver); - -static void _battlechipTransfer(struct GBASIOBattlechipGate* gate); -static void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate); +static void GBASIOBattlechipGateFinishMultiplayer(struct GBASIODriver* driver, uint16_t data[4]); void GBASIOBattlechipGateCreate(struct GBASIOBattlechipGate* gate) { - gate->d.init = NULL; - gate->d.deinit = NULL; + memset(&gate->d, 0, sizeof(gate->d)); gate->d.load = GBASIOBattlechipGateLoad; - gate->d.unload = NULL; gate->d.writeSIOCNT = GBASIOBattlechipGateWriteSIOCNT; - gate->d.setMode = NULL; gate->d.handlesMode = GBASIOBattlechipGateHandlesMode; gate->d.connectedDevices = GBASIOBattlechipGateConnectedDevices; - gate->d.deviceId = NULL; - gate->d.writeRCNT = NULL; - - gate->event.context = gate; - gate->event.callback = _battlechipTransferEvent; - gate->event.priority = 0x80; + gate->d.finishMultiplayer = GBASIOBattlechipGateFinishMultiplayer; gate->chipId = 0; gate->flavor = GBA_FLAVOR_BATTLECHIP_GATE; @@ -70,12 +60,9 @@ bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver) { } uint16_t GBASIOBattlechipGateWriteSIOCNT(struct GBASIODriver* driver, uint16_t value) { - struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver; + UNUSED(driver); value &= ~0xC; value |= 0x8; - if (value & 0x80) { - _battlechipTransfer(gate); - } return value; } @@ -95,20 +82,8 @@ static int GBASIOBattlechipGateConnectedDevices(struct GBASIODriver* driver) { return 1; } -void _battlechipTransfer(struct GBASIOBattlechipGate* gate) { - int32_t cycles = GBASIOTransferCycles(gate->d.p); - mTimingDeschedule(&gate->d.p->p->timing, &gate->event); - mTimingSchedule(&gate->d.p->p->timing, &gate->event, cycles); -} - -void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate) { - UNUSED(timing); - struct GBASIOBattlechipGate* gate = user; - - if (gate->d.p->mode == GBA_SIO_NORMAL_32) { - GBASIONormal32FinishTransfer(gate->d.p, 0, cyclesLate); - return; - } +static void GBASIOBattlechipGateFinishMultiplayer(struct GBASIODriver* driver, uint16_t data[4]) { + struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver; uint16_t cmd = gate->d.p->p->memory.io[GBA_REG(SIOMLT_SEND)]; uint16_t reply = 0xFFFF; @@ -189,8 +164,8 @@ void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cycle mLOG(GBA_BATTLECHIP, DEBUG, "Gate: %04X (%i)", reply, gate->state); ++gate->state; - uint16_t data[4] = { - cmd, reply, 0xFFFF, 0xFFFF - }; - GBASIOMultiplayerFinishTransfer(gate->d.p, data, cyclesLate); + data[0] = cmd; + data[1] = reply; + data[2] = 0xFFFF; + data[3] = 0xFFFF; } diff --git a/src/gba/sio.c b/src/gba/sio.c index acc45e607..971eb9502 100644 --- a/src/gba/sio.c +++ b/src/gba/sio.c @@ -221,6 +221,17 @@ void GBASIOWriteRCNT(struct GBASIO* sio, uint16_t value) { } } +static void _startTransfer(struct GBASIO* sio) { + if (sio->activeDriver && sio->activeDriver->start) { + if (!sio->activeDriver->start(sio->activeDriver)) { + // Transfer completion is handled internally to the driver + return; + } + } + mTimingDeschedule(&sio->p->timing, &sio->completeEvent); + mTimingSchedule(&sio->p->timing, &sio->completeEvent, GBASIOTransferCycles(sio)); +} + void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) { if ((value ^ sio->siocnt) & 0x3000) { sio->siocnt = value & 0x3000; @@ -256,6 +267,18 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) { // investigation than I managed, apparently. sio->rcnt = GBASIORegisterRCNTFillSc(sio->rcnt); + if (GBASIOMultiplayerIsBusy(value) && !GBASIOMultiplayerIsBusy(sio->siocnt)) { + if (!id) { + sio->p->memory.io[GBA_REG(SIOMULTI0)] = 0xFFFF; + sio->p->memory.io[GBA_REG(SIOMULTI1)] = 0xFFFF; + sio->p->memory.io[GBA_REG(SIOMULTI2)] = 0xFFFF; + sio->p->memory.io[GBA_REG(SIOMULTI3)] = 0xFFFF; + sio->rcnt = GBASIORegisterRCNTClearSc(sio->rcnt); + _startTransfer(sio); + } else { + // TODO + } + } break; case GBA_SIO_NORMAL_8: case GBA_SIO_NORMAL_32: @@ -264,6 +287,13 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) { if (GBASIONormalGetSc(value)) { sio->rcnt = GBASIORegisterRCNTFillSc(sio->rcnt); } + if (GBASIONormalIsStart(value) && !GBASIONormalIsStart(sio->siocnt)) { + if (GBASIONormalIsSc(value)) { + _startTransfer(sio); + } else { + // TODO + } + } break; default: // TODO @@ -277,15 +307,6 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) { case GBA_SIO_NORMAL_8: case GBA_SIO_NORMAL_32: value = GBASIONormalFillSi(value); - if ((value & 0x0081) == 0x0081) { - if (GBASIONormalIsIrq(value)) { - mTimingDeschedule(&sio->p->timing, &sio->completeEvent); - mTimingSchedule(&sio->p->timing, &sio->completeEvent, GBASIOTransferCycles(sio)); - } else { - // TODO: Test this on hardware to see if this is correct - value = GBASIONormalClearStart(value); - } - } break; case GBA_SIO_MULTI: value = GBASIOMultiplayerFillReady(value); @@ -473,16 +494,29 @@ void GBASIONormal32FinishTransfer(struct GBASIO* sio, uint32_t data, uint32_t cy static void _sioFinish(struct mTiming* timing, void* user, uint32_t cyclesLate) { UNUSED(timing); struct GBASIO* sio = user; - uint16_t data[4] = {0, 0, 0, 0}; + union { + uint16_t multi[4]; + uint8_t normal8; + uint32_t normal32; + } data = {0}; switch (sio->mode) { case GBA_SIO_MULTI: - GBASIOMultiplayerFinishTransfer(sio, data, cyclesLate); + if (sio->activeDriver && sio->activeDriver->finishMultiplayer) { + sio->activeDriver->finishMultiplayer(sio->activeDriver, data.multi); + } + GBASIOMultiplayerFinishTransfer(sio, data.multi, cyclesLate); break; case GBA_SIO_NORMAL_8: - GBASIONormal8FinishTransfer(sio, 0, cyclesLate); + if (sio->activeDriver && sio->activeDriver->finishNormal8) { + data.normal8 = sio->activeDriver->finishNormal8(sio->activeDriver); + } + GBASIONormal8FinishTransfer(sio, data.normal8, cyclesLate); break; case GBA_SIO_NORMAL_32: - GBASIONormal32FinishTransfer(sio, 0, cyclesLate); + if (sio->activeDriver && sio->activeDriver->finishNormal32) { + data.normal32 = sio->activeDriver->finishNormal32(sio->activeDriver); + } + GBASIONormal32FinishTransfer(sio, data.normal32, cyclesLate); break; default: // TODO diff --git a/src/gba/sio/dolphin.c b/src/gba/sio/dolphin.c index f7aad747b..e36dfb8a5 100644 --- a/src/gba/sio/dolphin.c +++ b/src/gba/sio/dolphin.c @@ -33,15 +33,12 @@ static int32_t _processCommand(struct GBASIODolphin* dol, uint32_t cyclesLate); static void _flush(struct GBASIODolphin* dol); void GBASIODolphinCreate(struct GBASIODolphin* dol) { + memset(&dol->d, 0, sizeof(dol->d)); dol->d.init = GBASIODolphinInit; dol->d.load = GBASIODolphinLoad; dol->d.unload = GBASIODolphinUnload; - dol->d.writeSIOCNT = NULL; - dol->d.setMode = NULL; dol->d.handlesMode = GBASIODolphinHandlesMode; dol->d.connectedDevices = GBASIODolphinConnectedDevices; - dol->d.deviceId = NULL; - dol->d.writeSIOCNT = NULL; dol->event.context = dol; dol->event.name = "GB SIO Lockstep"; dol->event.callback = GBASIODolphinProcessEvents; diff --git a/src/gba/sio/gbp.c b/src/gba/sio/gbp.c index f7b3747a1..3335905a1 100644 --- a/src/gba/sio/gbp.c +++ b/src/gba/sio/gbp.c @@ -16,7 +16,8 @@ static uint16_t _gbpRead(struct mKeyCallback*); static uint16_t _gbpSioWriteSIOCNT(struct GBASIODriver* driver, uint16_t value); static bool _gbpSioHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode); static int _gbpSioConnectedDevices(struct GBASIODriver* driver); -static void _gbpSioProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLate); +static bool _gbpSioStart(struct GBASIODriver* driver); +static uint32_t _gbpSioFinishNormal32(struct GBASIODriver* driver); static const uint8_t _logoPalette[] = { 0xDF, 0xFF, 0x0C, 0x64, 0x0C, 0xE4, 0x2D, 0xE4, 0x4E, 0x64, 0x4E, 0xE4, 0x6E, 0xE4, 0xAF, 0x68, @@ -45,20 +46,12 @@ void GBASIOPlayerInit(struct GBASIOPlayer* gbp) { gbp->callback.d.readKeys = _gbpRead; gbp->callback.d.requireOpposingDirections = true; gbp->callback.p = gbp; - gbp->d.init = NULL; - gbp->d.deinit = NULL; - gbp->d.load = NULL; - gbp->d.unload = NULL; + memset(&gbp->d, 0, sizeof(gbp->d)); gbp->d.writeSIOCNT = _gbpSioWriteSIOCNT; - gbp->d.setMode = NULL; gbp->d.handlesMode = _gbpSioHandlesMode; gbp->d.connectedDevices = _gbpSioConnectedDevices; - gbp->d.deviceId = NULL; - gbp->d.writeRCNT = NULL; - gbp->event.context = gbp; - gbp->event.name = "GBA SIO Game Boy Player"; - gbp->event.callback = _gbpSioProcessEvents; - gbp->event.priority = 0x80; + gbp->d.start = _gbpSioStart; + gbp->d.finishNormal32 = _gbpSioFinishNormal32; } void GBASIOPlayerReset(struct GBASIOPlayer* gbp) { @@ -108,27 +101,26 @@ uint16_t _gbpRead(struct mKeyCallback* callback) { } uint16_t _gbpSioWriteSIOCNT(struct GBASIODriver* driver, uint16_t value) { + UNUSED(driver); + return value & 0x78FB; +} + +bool _gbpSioStart(struct GBASIODriver* driver) { struct GBASIOPlayer* gbp = (struct GBASIOPlayer*) driver; - if (value & 0x0080) { - uint32_t rx = gbp->p->memory.io[GBA_REG(SIODATA32_LO)] | (gbp->p->memory.io[GBA_REG(SIODATA32_HI)] << 16); - if (gbp->txPosition < 12 && gbp->txPosition > 0) { - // TODO: Check expected - } else if (gbp->txPosition >= 12) { - uint32_t mask = 0x33; - // 0x00 = Stop - // 0x11 = Hard Stop - // 0x22 = Start - if (gbp->p->rumble) { - int32_t currentTime = mTimingCurrentTime(&gbp->p->timing); - gbp->p->rumble->setRumble(gbp->p->rumble, (rx & 0x33) == 0x22, currentTime - gbp->p->lastRumble); - gbp->p->lastRumble = currentTime; - } + uint32_t rx = gbp->p->memory.io[GBA_REG(SIODATA32_LO)] | (gbp->p->memory.io[GBA_REG(SIODATA32_HI)] << 16); + if (gbp->txPosition < 12 && gbp->txPosition > 0) { + // TODO: Check expected + } else if (gbp->txPosition >= 12) { + // 0x00 = Stop + // 0x11 = Hard Stop + // 0x22 = Start + if (gbp->p->rumble) { + int32_t currentTime = mTimingCurrentTime(&gbp->p->timing); + gbp->p->rumble->setRumble(gbp->p->rumble, (rx & 0x33) == 0x22, currentTime - gbp->p->lastRumble); + gbp->p->lastRumble = currentTime; } - mTimingDeschedule(&gbp->p->timing, &gbp->event); - mTimingSchedule(&gbp->p->timing, &gbp->event, 2048); } - value &= 0x78FB; - return value; + return true; } static bool _gbpSioHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode) { @@ -141,9 +133,8 @@ static int _gbpSioConnectedDevices(struct GBASIODriver* driver) { return 1; } -void _gbpSioProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLate) { - UNUSED(timing); - struct GBASIOPlayer* gbp = user; +uint32_t _gbpSioFinishNormal32(struct GBASIODriver* driver) { + struct GBASIOPlayer* gbp = (struct GBASIOPlayer*) driver; uint32_t tx = 0; int txPosition = gbp->txPosition; if (txPosition > 16) { @@ -154,5 +145,5 @@ void _gbpSioProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLat } tx = _gbpTxData[txPosition]; ++gbp->txPosition; - GBASIONormal32FinishTransfer(gbp->d.p, tx, cyclesLate); + return tx; } diff --git a/src/gba/sio/lockstep.c b/src/gba/sio/lockstep.c index f7b82f089..ed74ed6d4 100644 --- a/src/gba/sio/lockstep.c +++ b/src/gba/sio/lockstep.c @@ -18,6 +18,7 @@ static bool GBASIOLockstepNodeUnload(struct GBASIODriver* driver); static bool GBASIOLockstepNodeHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode); static int GBASIOLockstepNodeConnectedDevices(struct GBASIODriver* driver); static int GBASIOLockstepNodeDeviceId(struct GBASIODriver* driver); +static bool GBASIOLockstepNodeStart(struct GBASIODriver* driver); static uint16_t GBASIOLockstepNodeMultiWriteSIOCNT(struct GBASIODriver* driver, uint16_t value); static uint16_t GBASIOLockstepNodeNormalWriteSIOCNT(struct GBASIODriver* driver, uint16_t value); static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* driver, uint32_t cyclesLate); @@ -37,16 +38,16 @@ void GBASIOLockstepInit(struct GBASIOLockstep* lockstep) { } void GBASIOLockstepNodeCreate(struct GBASIOLockstepNode* node) { + memset(&node->d, 0, sizeof(node->d)); node->d.init = GBASIOLockstepNodeInit; node->d.deinit = GBASIOLockstepNodeDeinit; node->d.load = GBASIOLockstepNodeLoad; node->d.unload = GBASIOLockstepNodeUnload; - node->d.setMode = NULL; node->d.handlesMode = GBASIOLockstepNodeHandlesMode; node->d.connectedDevices = GBASIOLockstepNodeConnectedDevices; node->d.deviceId = GBASIOLockstepNodeDeviceId; + node->d.start = GBASIOLockstepNodeStart; node->d.writeSIOCNT = NULL; - node->d.writeRCNT = NULL; } bool GBASIOLockstepAttachNode(struct GBASIOLockstep* lockstep, struct GBASIOLockstepNode* node) { @@ -224,6 +225,11 @@ static int GBASIOLockstepNodeDeviceId(struct GBASIODriver* driver) { return node->id; } +static bool GBASIOLockstepNodeStart(struct GBASIODriver* driver) { + UNUSED(driver); + return false; +} + static uint16_t GBASIOLockstepNodeMultiWriteSIOCNT(struct GBASIODriver* driver, uint16_t value) { struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver; @@ -319,10 +325,6 @@ static int32_t _masterUpdate(struct GBASIOLockstepNode* node) { switch (node->mode) { case GBA_SIO_MULTI: node->p->multiRecv[0] = node->d.p->p->memory.io[GBA_REG(SIOMLT_SEND)]; - node->d.p->p->memory.io[GBA_REG(SIOMULTI0)] = 0xFFFF; - node->d.p->p->memory.io[GBA_REG(SIOMULTI1)] = 0xFFFF; - node->d.p->p->memory.io[GBA_REG(SIOMULTI2)] = 0xFFFF; - node->d.p->p->memory.io[GBA_REG(SIOMULTI3)] = 0xFFFF; node->p->multiRecv[1] = 0xFFFF; node->p->multiRecv[2] = 0xFFFF; node->p->multiRecv[3] = 0xFFFF; From 0b9cf1270cd1779d489ad276527aab973adebeee Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 5 Sep 2024 03:33:35 -0700 Subject: [PATCH 026/114] GBA SIO: Modify GBASIOTransferCycles to not require SIO struct --- include/mgba/internal/gba/sio.h | 2 +- src/gba/sio.c | 25 ++++++++++++------------- src/gba/sio/lockstep.c | 6 +++--- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/include/mgba/internal/gba/sio.h b/include/mgba/internal/gba/sio.h index bb7b2a78c..ca271abc4 100644 --- a/include/mgba/internal/gba/sio.h +++ b/include/mgba/internal/gba/sio.h @@ -93,7 +93,7 @@ void GBASIOWriteRCNT(struct GBASIO* sio, uint16_t value); void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value); uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t value); -int32_t GBASIOTransferCycles(struct GBASIO* sio); +int32_t GBASIOTransferCycles(enum GBASIOMode mode, uint16_t siocnt, int connected); void GBASIOMultiplayerFinishTransfer(struct GBASIO* sio, uint16_t data[4], uint32_t cyclesLate); void GBASIONormal8FinishTransfer(struct GBASIO* sio, uint8_t data, uint32_t cyclesLate); diff --git a/src/gba/sio.c b/src/gba/sio.c index 971eb9502..4b635db18 100644 --- a/src/gba/sio.c +++ b/src/gba/sio.c @@ -228,8 +228,12 @@ static void _startTransfer(struct GBASIO* sio) { return; } } + int connected = 0; + if (sio->activeDriver && sio->activeDriver->connectedDevices) { + connected = sio->activeDriver->connectedDevices(sio->activeDriver); + } mTimingDeschedule(&sio->p->timing, &sio->completeEvent); - mTimingSchedule(&sio->p->timing, &sio->completeEvent, GBASIOTransferCycles(sio)); + mTimingSchedule(&sio->p->timing, &sio->completeEvent, GBASIOTransferCycles(sio->mode, sio->siocnt, connected)); } void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) { @@ -429,26 +433,21 @@ uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t valu return value; } -int32_t GBASIOTransferCycles(struct GBASIO* sio) { - int connected = 0; - if (sio->activeDriver) { - connected = sio->activeDriver->connectedDevices(sio->activeDriver); - } - +int32_t GBASIOTransferCycles(enum GBASIOMode mode, uint16_t siocnt, int connected) { if (connected < 0 || connected >= MAX_GBAS) { - mLOG(GBA_SIO, ERROR, "SIO driver returned invalid device count %i", connected); + mLOG(GBA_SIO, ERROR, "Invalid device count %i", connected); return 0; } - switch (sio->mode) { + switch (mode) { case GBA_SIO_MULTI: - return GBASIOCyclesPerTransfer[GBASIOMultiplayerGetBaud(sio->siocnt)][connected]; + return GBASIOCyclesPerTransfer[GBASIOMultiplayerGetBaud(siocnt)][connected]; case GBA_SIO_NORMAL_8: - return 8 * GBA_ARM7TDMI_FREQUENCY / ((GBASIONormalIsInternalSc(sio->siocnt) ? 2048 : 256) * 1024); + return 8 * GBA_ARM7TDMI_FREQUENCY / ((GBASIONormalIsInternalSc(siocnt) ? 2048 : 256) * 1024); case GBA_SIO_NORMAL_32: - return 32 * GBA_ARM7TDMI_FREQUENCY / ((GBASIONormalIsInternalSc(sio->siocnt) ? 2048 : 256) * 1024); + return 32 * GBA_ARM7TDMI_FREQUENCY / ((GBASIONormalIsInternalSc(siocnt) ? 2048 : 256) * 1024); default: - mLOG(GBA_SIO, STUB, "No cycle count implemented for mode %s", _modeName(sio->mode)); + mLOG(GBA_SIO, STUB, "No cycle count implemented for mode %s", _modeName(mode)); break; } return 0; diff --git a/src/gba/sio/lockstep.c b/src/gba/sio/lockstep.c index ed74ed6d4..2008638bd 100644 --- a/src/gba/sio/lockstep.c +++ b/src/gba/sio/lockstep.c @@ -248,7 +248,7 @@ static uint16_t GBASIOLockstepNodeMultiWriteSIOCNT(struct GBASIODriver* driver, if (!node->id && attached > 1 && GBASIOMultiplayerIsReady(node->d.p->siocnt)) { mLOG(GBA_SIO, DEBUG, "Lockstep %i: Transfer initiated", node->id); ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING); - ATOMIC_STORE(node->p->d.transferCycles, GBASIOTransferCycles(node->d.p)); + ATOMIC_STORE(node->p->d.transferCycles, GBASIOTransferCycles(node->d.p->mode, node->d.p->siocnt, attached)); if (mTimingIsScheduled(&driver->p->p->timing, &node->event)) { node->eventDiff -= node->event.when - mTimingCurrentTime(&driver->p->p->timing); @@ -479,7 +479,7 @@ static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* user, if (node->p->d.attached < 2) { switch (node->mode) { case GBA_SIO_MULTI: - cycles = GBASIOTransferCycles(node->d.p); + cycles = GBASIOTransferCycles(node->d.p->mode, node->d.p->siocnt, node->p->d.attached); break; case GBA_SIO_NORMAL_8: case GBA_SIO_NORMAL_32: @@ -544,7 +544,7 @@ static uint16_t GBASIOLockstepNodeNormalWriteSIOCNT(struct GBASIODriver* driver, if ((value & 0x0081) == 0x0081) { if (!node->id) { // Frequency - int32_t cycles = GBASIOTransferCycles(node->d.p); + int32_t cycles = GBASIOTransferCycles(node->d.p->mode, node->d.p->siocnt, attached); if (transferActive == TRANSFER_IDLE) { mLOG(GBA_SIO, DEBUG, "Lockstep %i: Transfer initiated", node->id); From 3180d432e5b1352b5605b5eb9f46aaee439137d1 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 21 Aug 2024 23:59:56 -0700 Subject: [PATCH 027/114] Core: Add new mLockstepUser API for upcoming lockstep rewrite --- include/mgba/core/lockstep.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/include/mgba/core/lockstep.h b/include/mgba/core/lockstep.h index ac6cb3f84..8936d263e 100644 --- a/include/mgba/core/lockstep.h +++ b/include/mgba/core/lockstep.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016 Jeffrey Pfau +/* Copyright (c) 2013-2024 Jeffrey Pfau * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -53,6 +53,14 @@ static inline void mLockstepUnlock(struct mLockstep* lockstep) { } } +struct mLockstepUser { + void (*sleep)(struct mLockstepUser*); + void (*wake)(struct mLockstepUser*); + + int (*requestedId)(struct mLockstepUser*); + void (*playerIdChanged)(struct mLockstepUser*, int id); +}; + CXX_GUARD_END #endif From 36c1a8cfbc759eae52958dd5e927a6ab4dd8a5ed Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 22 Aug 2024 22:23:27 -0700 Subject: [PATCH 028/114] Core: Implement mLockstepUser for mCoreThread --- include/mgba/core/lockstep.h | 11 +++++++++++ src/core/lockstep.c | 25 +++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/include/mgba/core/lockstep.h b/include/mgba/core/lockstep.h index 8936d263e..b83d17657 100644 --- a/include/mgba/core/lockstep.h +++ b/include/mgba/core/lockstep.h @@ -61,6 +61,17 @@ struct mLockstepUser { void (*playerIdChanged)(struct mLockstepUser*, int id); }; +#ifndef DISABLE_THREADING +struct mCoreThread; +struct mLockstepThreadUser { + struct mLockstepUser d; + + struct mCoreThread* thread; +}; + +void mLockstepThreadUserInit(struct mLockstepThreadUser* lockstep, struct mCoreThread* thread); +#endif + CXX_GUARD_END #endif diff --git a/src/core/lockstep.c b/src/core/lockstep.c index 587cff2b5..c058a80fc 100644 --- a/src/core/lockstep.c +++ b/src/core/lockstep.c @@ -1,10 +1,14 @@ -/* Copyright (c) 2013-2016 Jeffrey Pfau +/* Copyright (c) 2013-2024 Jeffrey Pfau * * This Source Code Form is subject to the terms of the Mozilla Public * 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 +#ifndef DISABLE_THREADING +#include +#endif + void mLockstepInit(struct mLockstep* lockstep) { lockstep->attached = 0; lockstep->transferActive = 0; @@ -19,4 +23,21 @@ void mLockstepDeinit(struct mLockstep* lockstep) { UNUSED(lockstep); } -// TODO: Migrate nodes +#ifndef DISABLE_THREADING +static void mLockstepThreadUserSleep(struct mLockstepUser* user) { + struct mLockstepThreadUser* lockstep = (struct mLockstepThreadUser*) user; + mCoreThreadWaitFromThread(lockstep->thread); +} + +static void mLockstepThreadUserWake(struct mLockstepUser* user) { + struct mLockstepThreadUser* lockstep = (struct mLockstepThreadUser*) user; + mCoreThreadStopWaiting(lockstep->thread); +} + +void mLockstepThreadUserInit(struct mLockstepThreadUser* lockstep, struct mCoreThread* thread) { + memset(lockstep, 0, sizeof(*lockstep)); + lockstep->d.sleep = mLockstepThreadUserSleep; + lockstep->d.wake = mLockstepThreadUserWake; + lockstep->thread = thread; +} +#endif From 0955b9446624d63743b74dd1cb18cb71500e1ebc Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 22 Aug 2024 00:00:10 -0700 Subject: [PATCH 029/114] GBA SIO: Bring up new lockstep driver --- include/mgba/internal/gba/sio/lockstep.h | 77 ++- src/gba/sio/lockstep.c | 769 ++++++++++++++++++++++- 2 files changed, 844 insertions(+), 2 deletions(-) diff --git a/include/mgba/internal/gba/sio/lockstep.h b/include/mgba/internal/gba/sio/lockstep.h index 13c181039..313285a44 100644 --- a/include/mgba/internal/gba/sio/lockstep.h +++ b/include/mgba/internal/gba/sio/lockstep.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015 Jeffrey Pfau +/* Copyright (c) 2013-2024 Jeffrey Pfau * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -13,6 +13,9 @@ CXX_GUARD_START #include #include #include +#include +#include +#include struct GBASIOLockstep { struct mLockstep d; @@ -48,6 +51,78 @@ void GBASIOLockstepNodeCreate(struct GBASIOLockstepNode*); bool GBASIOLockstepAttachNode(struct GBASIOLockstep*, struct GBASIOLockstepNode*); void GBASIOLockstepDetachNode(struct GBASIOLockstep*, struct GBASIOLockstepNode*); +#define MAX_LOCKSTEP_EVENTS 8 + +enum GBASIOLockstepEventType { + SIO_EV_ATTACH, + SIO_EV_DETACH, + SIO_EV_HARD_SYNC, + SIO_EV_MODE_SET, + SIO_EV_TRANSFER_START, +}; + +struct GBASIOLockstepCoordinator { + struct Table players; + Mutex mutex; + + unsigned nextId; + + unsigned attachedPlayers[MAX_GBAS]; + int nAttached; + uint32_t waiting; + + bool transferActive; + enum GBASIOMode transferMode; + + int32_t cycle; + + uint16_t multiData[4]; + uint32_t normalData[4]; +}; + +struct GBASIOLockstepEvent { + enum GBASIOLockstepEventType type; + int32_t timestamp; + struct GBASIOLockstepEvent* next; + int playerId; + union { + enum GBASIOMode mode; + int32_t finishCycle; + }; +}; + +struct GBASIOLockstepPlayer { + struct GBASIOLockstepDriver* driver; + int playerId; + enum GBASIOMode mode; + enum GBASIOMode otherModes[MAX_GBAS]; + bool asleep; + int32_t cycleOffset; + struct GBASIOLockstepEvent* queue; + bool dataReceived; + + struct GBASIOLockstepEvent buffer[MAX_LOCKSTEP_EVENTS]; + struct GBASIOLockstepEvent* freeList; +}; + +struct GBASIOLockstepDriver { + struct GBASIODriver d; + struct GBASIOLockstepCoordinator* coordinator; + struct mTimingEvent event; + unsigned lockstepId; + + struct mLockstepUser* user; +}; + +void GBASIOLockstepCoordinatorInit(struct GBASIOLockstepCoordinator*); +void GBASIOLockstepCoordinatorDeinit(struct GBASIOLockstepCoordinator*); + +void GBASIOLockstepCoordinatorAttach(struct GBASIOLockstepCoordinator*, struct GBASIOLockstepDriver*); +void GBASIOLockstepCoordinatorDetach(struct GBASIOLockstepCoordinator*, struct GBASIOLockstepDriver*); +size_t GBASIOLockstepCoordinatorAttached(struct GBASIOLockstepCoordinator*); + +void GBASIOLockstepDriverCreate(struct GBASIOLockstepDriver*, struct mLockstepUser*); + CXX_GUARD_END #endif diff --git a/src/gba/sio/lockstep.c b/src/gba/sio/lockstep.c index 2008638bd..18603e054 100644 --- a/src/gba/sio/lockstep.c +++ b/src/gba/sio/lockstep.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015 Jeffrey Pfau +/* Copyright (c) 2013-2024 Jeffrey Pfau * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -10,6 +10,7 @@ #define LOCKSTEP_INCREMENT 2000 #define LOCKSTEP_TRANSFER 512 +#define QUEUE_SIZE 16 static bool GBASIOLockstepNodeInit(struct GBASIODriver* driver); static void GBASIOLockstepNodeDeinit(struct GBASIODriver* driver); @@ -568,3 +569,769 @@ static uint16_t GBASIOLockstepNodeNormalWriteSIOCNT(struct GBASIODriver* driver, return value; } + +#define TARGET(P) (1 << (P)) +#define TARGET_ALL 0xF +#define TARGET_PRIMARY 0x1 +#define TARGET_SECONDARY ((TARGET_ALL) & ~(TARGET_PRIMARY)) + +static bool GBASIOLockstepDriverInit(struct GBASIODriver* driver); +static void GBASIOLockstepDriverDeinit(struct GBASIODriver* driver); +static void GBASIOLockstepDriverReset(struct GBASIODriver* driver); +static bool GBASIOLockstepDriverLoad(struct GBASIODriver* driver); +static bool GBASIOLockstepDriverUnload(struct GBASIODriver* driver); +static void GBASIOLockstepDriverSetMode(struct GBASIODriver* driver, enum GBASIOMode mode); +static bool GBASIOLockstepDriverHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode); +static int GBASIOLockstepDriverConnectedDevices(struct GBASIODriver* driver); +static int GBASIOLockstepDriverDeviceId(struct GBASIODriver* driver); +static uint16_t GBASIOLockstepDriverWriteSIOCNT(struct GBASIODriver* driver, uint16_t value); +static uint16_t GBASIOLockstepDriverWriteRCNT(struct GBASIODriver* driver, uint16_t value); +static bool GBASIOLockstepDriverStart(struct GBASIODriver* driver); +static void GBASIOLockstepDriverFinishMultiplayer(struct GBASIODriver* driver, uint16_t data[4]); +static uint8_t GBASIOLockstepDriverFinishNormal8(struct GBASIODriver* driver); +static uint32_t GBASIOLockstepDriverFinishNormal32(struct GBASIODriver* driver); + +static void GBASIOLockstepCoordinatorWaitOnPlayers(struct GBASIOLockstepCoordinator*, struct GBASIOLockstepPlayer*); +static void GBASIOLockstepCoordinatorAckPlayer(struct GBASIOLockstepCoordinator*, struct GBASIOLockstepPlayer*); +static void GBASIOLockstepCoordinatorWakePlayers(struct GBASIOLockstepCoordinator*); + +static int32_t GBASIOLockstepTime(struct GBASIOLockstepPlayer*); +static void GBASIOLockstepPlayerWake(struct GBASIOLockstepPlayer*); +static void GBASIOLockstepPlayerSleep(struct GBASIOLockstepPlayer*); + +static void _advanceCycle(struct GBASIOLockstepCoordinator*, struct GBASIOLockstepPlayer*); +static void _removePlayer(struct GBASIOLockstepCoordinator*, struct GBASIOLockstepPlayer*); +static void _reconfigPlayers(struct GBASIOLockstepCoordinator*); +static int32_t _untilNextSync(struct GBASIOLockstepCoordinator*, struct GBASIOLockstepPlayer*); +static void _enqueueEvent(struct GBASIOLockstepCoordinator*, const struct GBASIOLockstepEvent*, uint32_t target); +static void _setData(struct GBASIOLockstepCoordinator*, uint32_t id, struct GBASIO* sio); +static void _setReady(struct GBASIOLockstepCoordinator*, struct GBASIOLockstepPlayer* activePlayer, int playerId, enum GBASIOMode mode); +static void _hardSync(struct GBASIOLockstepCoordinator*, struct GBASIOLockstepPlayer*); + +static void _lockstepEvent(struct mTiming*, void* context, uint32_t cyclesLate); + +void GBASIOLockstepDriverCreate(struct GBASIOLockstepDriver* driver, struct mLockstepUser* user) { + memset(driver, 0, sizeof(*driver)); + driver->d.init = GBASIOLockstepDriverInit; + driver->d.deinit = GBASIOLockstepDriverDeinit; + driver->d.reset = GBASIOLockstepDriverReset; + driver->d.load = GBASIOLockstepDriverLoad; + driver->d.unload = GBASIOLockstepDriverUnload; + driver->d.setMode = GBASIOLockstepDriverSetMode; + driver->d.handlesMode = GBASIOLockstepDriverHandlesMode; + driver->d.deviceId = GBASIOLockstepDriverDeviceId; + driver->d.connectedDevices = GBASIOLockstepDriverConnectedDevices; + driver->d.writeSIOCNT = GBASIOLockstepDriverWriteSIOCNT; + driver->d.writeRCNT = GBASIOLockstepDriverWriteRCNT; + driver->d.start = GBASIOLockstepDriverStart; + driver->d.finishMultiplayer = GBASIOLockstepDriverFinishMultiplayer; + driver->d.finishNormal8 = GBASIOLockstepDriverFinishNormal8; + driver->d.finishNormal32 = GBASIOLockstepDriverFinishNormal32; + driver->event.context = driver; + driver->event.callback = _lockstepEvent; + driver->event.name = "GBA SIO Lockstep"; + driver->event.priority = 0x80; + driver->user = user; +} + +static bool GBASIOLockstepDriverInit(struct GBASIODriver* driver) { + GBASIOLockstepDriverReset(driver); + return true; +} + +static void GBASIOLockstepDriverDeinit(struct GBASIODriver* driver) { + struct GBASIOLockstepDriver* lockstep = (struct GBASIOLockstepDriver*) driver; + struct GBASIOLockstepCoordinator* coordinator = lockstep->coordinator; + MutexLock(&coordinator->mutex); + struct GBASIOLockstepPlayer* player = TableLookup(&coordinator->players, lockstep->lockstepId); + if (player) { + _removePlayer(coordinator, player); + } + MutexUnlock(&coordinator->mutex); + mTimingDeschedule(&lockstep->d.p->p->timing, &lockstep->event); + lockstep->lockstepId = 0; +} + +static void GBASIOLockstepDriverReset(struct GBASIODriver* driver) { + struct GBASIOLockstepDriver* lockstep = (struct GBASIOLockstepDriver*) driver; + struct GBASIOLockstepCoordinator* coordinator = lockstep->coordinator; + if (!lockstep->lockstepId) { + struct GBASIOLockstepPlayer* player = calloc(1, sizeof(*player)); + unsigned id; + player->driver = lockstep; + player->mode = driver->p->mode; + player->playerId = -1; + + int i; + for (i = 0; i < MAX_LOCKSTEP_EVENTS - 1; ++i) { + player->buffer[i].next = &player->buffer[i + 1]; + } + player->freeList = &player->buffer[0]; + + MutexLock(&coordinator->mutex); + while (true) { + if (coordinator->nextId == UINT_MAX) { + coordinator->nextId = 0; + } + ++coordinator->nextId; + id = coordinator->nextId; + if (!TableLookup(&coordinator->players, id)) { + TableInsert(&coordinator->players, id, player); + lockstep->lockstepId = id; + break; + } + } + _reconfigPlayers(coordinator); + if (player->playerId != 0) { + player->cycleOffset = mTimingCurrentTime(&driver->p->p->timing) - coordinator->cycle + LOCKSTEP_INCREMENT; + struct GBASIOLockstepEvent event = { + .type = SIO_EV_ATTACH, + .playerId = player->playerId, + .timestamp = GBASIOLockstepTime(player), + }; + _enqueueEvent(coordinator, &event, TARGET_ALL & ~TARGET(player->playerId)); + } + MutexUnlock(&coordinator->mutex); + } + + if (mTimingIsScheduled(&lockstep->d.p->p->timing, &lockstep->event)) { + return; + } + + int32_t nextEvent; + MutexLock(&coordinator->mutex); + struct GBASIOLockstepPlayer* player = TableLookup(&coordinator->players, lockstep->lockstepId); + _setReady(coordinator, player, player->playerId, player->mode); + if (TableSize(&coordinator->players) == 1) { + coordinator->cycle = mTimingCurrentTime(&lockstep->d.p->p->timing); + nextEvent = LOCKSTEP_INCREMENT; + } else { + _setReady(coordinator, player, 0, coordinator->transferMode); + nextEvent = _untilNextSync(lockstep->coordinator, player); + } + MutexUnlock(&coordinator->mutex); + mTimingSchedule(&lockstep->d.p->p->timing, &lockstep->event, nextEvent); +} + +static bool GBASIOLockstepDriverLoad(struct GBASIODriver* driver) { + struct GBASIOLockstepDriver* lockstep = (struct GBASIOLockstepDriver*) driver; + if (lockstep->lockstepId) { + struct GBASIOLockstepCoordinator* coordinator = lockstep->coordinator; + MutexLock(&coordinator->mutex); + struct GBASIOLockstepPlayer* player = TableLookup(&coordinator->players, lockstep->lockstepId); + _setReady(coordinator, player, 0, coordinator->transferMode); + MutexUnlock(&coordinator->mutex); + GBASIOLockstepDriverSetMode(driver, driver->p->mode); + } + return true; +} + +static bool GBASIOLockstepDriverUnload(struct GBASIODriver* driver) { + struct GBASIOLockstepDriver* lockstep = (struct GBASIOLockstepDriver*) driver; + if (lockstep->lockstepId) { + GBASIOLockstepDriverSetMode(driver, -1); + } + return true; +} + +static void GBASIOLockstepDriverSetMode(struct GBASIODriver* driver, enum GBASIOMode mode) { + struct GBASIOLockstepDriver* lockstep = (struct GBASIOLockstepDriver*) driver; + struct GBASIOLockstepCoordinator* coordinator = lockstep->coordinator; + MutexLock(&coordinator->mutex); + struct GBASIOLockstepPlayer* player = TableLookup(&coordinator->players, lockstep->lockstepId); + if (mode != player->mode) { + player->mode = mode; + struct GBASIOLockstepEvent event = { + .type = SIO_EV_MODE_SET, + .playerId = player->playerId, + .timestamp = GBASIOLockstepTime(player), + .mode = mode, + }; + if (player->playerId == 0) { + mASSERT(!coordinator->transferActive); // TODO + coordinator->transferMode = mode; + GBASIOLockstepCoordinatorWaitOnPlayers(coordinator, player); + } + _setReady(coordinator, player, player->playerId, mode); + _enqueueEvent(coordinator, &event, TARGET_ALL & ~TARGET(player->playerId)); + } + MutexUnlock(&coordinator->mutex); +} + +static bool GBASIOLockstepDriverHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode) { + UNUSED(driver); + UNUSED(mode); + return true; +} + +static int GBASIOLockstepDriverConnectedDevices(struct GBASIODriver* driver) { + struct GBASIOLockstepDriver* lockstep = (struct GBASIOLockstepDriver*) driver; + struct GBASIOLockstepCoordinator* coordinator = lockstep->coordinator; + if (!lockstep->lockstepId) { + return 0; + } + MutexLock(&coordinator->mutex); + int attached = coordinator->nAttached - 1; + MutexUnlock(&coordinator->mutex); + return attached; +} + +static int GBASIOLockstepDriverDeviceId(struct GBASIODriver* driver) { + struct GBASIOLockstepDriver* lockstep = (struct GBASIOLockstepDriver*) driver; + struct GBASIOLockstepCoordinator* coordinator = lockstep->coordinator; + int playerId = 0; + MutexLock(&coordinator->mutex); + struct GBASIOLockstepPlayer* player = TableLookup(&coordinator->players, lockstep->lockstepId); + if (player && player->playerId >= 0) { + playerId = player->playerId; + } + MutexUnlock(&coordinator->mutex); + return playerId; +} + +static uint16_t GBASIOLockstepDriverWriteSIOCNT(struct GBASIODriver* driver, uint16_t value) { + UNUSED(driver); + mLOG(GBA_SIO, DEBUG, "Lockstep: SIOCNT <- %04X", value); + return value; +} + +static uint16_t GBASIOLockstepDriverWriteRCNT(struct GBASIODriver* driver, uint16_t value) { + UNUSED(driver); + mLOG(GBA_SIO, DEBUG, "Lockstep: RCNT <- %04X", value); + return value; +} + +static bool GBASIOLockstepDriverStart(struct GBASIODriver* driver) { + struct GBASIOLockstepDriver* lockstep = (struct GBASIOLockstepDriver*) driver; + struct GBASIOLockstepCoordinator* coordinator = lockstep->coordinator; + bool ret = false; + MutexLock(&coordinator->mutex); + if (coordinator->transferActive) { + mLOG(GBA_SIO, ERROR, "Transfer restarted unexpectedly"); + goto out; + } + struct GBASIOLockstepPlayer* player = TableLookup(&coordinator->players, lockstep->lockstepId); + if (player->playerId != 0) { + mLOG(GBA_SIO, DEBUG, "Secondary player attempted to start transfer"); + goto out; + } + mLOG(GBA_SIO, DEBUG, "Transfer starting at %08X", coordinator->cycle); + memset(coordinator->multiData, 0xFF, sizeof(coordinator->multiData)); + _setData(coordinator, 0, player->driver->d.p); + + int32_t timestamp = GBASIOLockstepTime(player); + struct GBASIOLockstepEvent event = { + .type = SIO_EV_TRANSFER_START, + .timestamp = timestamp, + .finishCycle = timestamp + GBASIOTransferCycles(player->mode, player->driver->d.p->siocnt, coordinator->nAttached - 1), + }; + _enqueueEvent(coordinator, &event, TARGET_SECONDARY); + GBASIOLockstepCoordinatorWaitOnPlayers(coordinator, player); + coordinator->transferActive = true; + ret = true; +out: + MutexUnlock(&coordinator->mutex); + return ret; +} + +static void GBASIOLockstepDriverFinishMultiplayer(struct GBASIODriver* driver, uint16_t data[4]) { + struct GBASIOLockstepDriver* lockstep = (struct GBASIOLockstepDriver*) driver; + struct GBASIOLockstepCoordinator* coordinator = lockstep->coordinator; + MutexLock(&coordinator->mutex); + if (coordinator->transferMode == GBA_SIO_MULTI) { + struct GBASIOLockstepPlayer* player = TableLookup(&coordinator->players, lockstep->lockstepId); + if (!player->dataReceived) { + mLOG(GBA_SIO, WARN, "MULTI did not receive data. Are we running behind?"); + memset(data, 0xFF, sizeof(uint16_t) * 4); + } else { + mLOG(GBA_SIO, INFO, "MULTI transfer finished: %04X %04X %04X %04X", + coordinator->multiData[0], + coordinator->multiData[1], + coordinator->multiData[2], + coordinator->multiData[3]); + memcpy(data, coordinator->multiData, sizeof(uint16_t) * 4); + } + player->dataReceived = false; + if (player->playerId == 0) { + _hardSync(coordinator, player); + } + } + MutexUnlock(&coordinator->mutex); +} + +static uint8_t GBASIOLockstepDriverFinishNormal8(struct GBASIODriver* driver) { + struct GBASIOLockstepDriver* lockstep = (struct GBASIOLockstepDriver*) driver; + struct GBASIOLockstepCoordinator* coordinator = lockstep->coordinator; + uint8_t data = 0xFF; + MutexLock(&coordinator->mutex); + if (coordinator->transferMode == GBA_SIO_NORMAL_8) { + struct GBASIOLockstepPlayer* player = TableLookup(&coordinator->players, lockstep->lockstepId); + if (player->playerId > 0) { + if (!player->dataReceived) { + mLOG(GBA_SIO, WARN, "NORMAL did not receive data. Are we running behind?"); + } else { + data = coordinator->normalData[player->playerId - 1]; + mLOG(GBA_SIO, INFO, "NORMAL8 transfer finished: %02X", data); + } + } + player->dataReceived = false; + if (player->playerId == 0) { + _hardSync(coordinator, player); + } + } + MutexUnlock(&coordinator->mutex); + return data; +} + +static uint32_t GBASIOLockstepDriverFinishNormal32(struct GBASIODriver* driver) { + struct GBASIOLockstepDriver* lockstep = (struct GBASIOLockstepDriver*) driver; + struct GBASIOLockstepCoordinator* coordinator = lockstep->coordinator; + uint32_t data = 0xFFFFFFFF; + MutexLock(&coordinator->mutex); + if (coordinator->transferMode == GBA_SIO_NORMAL_32) { + struct GBASIOLockstepPlayer* player = TableLookup(&coordinator->players, lockstep->lockstepId); + if (player->playerId > 0) { + if (!player->dataReceived) { + mLOG(GBA_SIO, WARN, "Did not receive data. Are we running behind?"); + } else { + data = coordinator->normalData[player->playerId - 1]; + mLOG(GBA_SIO, INFO, "NORMAL32 transfer finished: %08X", data); + } + } + player->dataReceived = false; + if (player->playerId == 0) { + _hardSync(coordinator, player); + } + } + MutexUnlock(&coordinator->mutex); + return data; +} + +void GBASIOLockstepCoordinatorInit(struct GBASIOLockstepCoordinator* coordinator) { + memset(coordinator, 0, sizeof(*coordinator)); + MutexInit(&coordinator->mutex); + TableInit(&coordinator->players, 8, free); +} + +void GBASIOLockstepCoordinatorDeinit(struct GBASIOLockstepCoordinator* coordinator) { + MutexDeinit(&coordinator->mutex); + TableDeinit(&coordinator->players); +} + +void GBASIOLockstepCoordinatorAttach(struct GBASIOLockstepCoordinator* coordinator, struct GBASIOLockstepDriver* driver) { + if (driver->coordinator && driver->coordinator != coordinator) { + // TODO + abort(); + } + driver->coordinator = coordinator; +} + +void GBASIOLockstepCoordinatorDetach(struct GBASIOLockstepCoordinator* coordinator, struct GBASIOLockstepDriver* driver) { + if (driver->coordinator != coordinator) { + // TODO + abort(); + return; + } + MutexLock(&coordinator->mutex); + struct GBASIOLockstepPlayer* player = TableLookup(&coordinator->players, driver->lockstepId); + if (player) { + _removePlayer(coordinator, player); + } + MutexUnlock(&coordinator->mutex); + driver->coordinator = NULL; +} + +int32_t _untilNextSync(struct GBASIOLockstepCoordinator* coordinator, struct GBASIOLockstepPlayer* player) { + int32_t cycle = coordinator->cycle - GBASIOLockstepTime(player); + if (player->playerId == 0) { + cycle += LOCKSTEP_INCREMENT; + } + return cycle; +} + +void _advanceCycle(struct GBASIOLockstepCoordinator* coordinator, struct GBASIOLockstepPlayer* player) { + int32_t newCycle = GBASIOLockstepTime(player); + mASSERT(newCycle - coordinator->cycle >= 0); + //mLOG(GBA_SIO, DEBUG, "Advancing from cycle %08X to %08X (%i cycles)", coordinator->cycle, newCycle, newCycle - coordinator->cycle); + coordinator->cycle = newCycle; +} + +void _removePlayer(struct GBASIOLockstepCoordinator* coordinator, struct GBASIOLockstepPlayer* player) { + struct GBASIOLockstepEvent event = { + .type = SIO_EV_DETACH, + .playerId = player->playerId, + .timestamp = GBASIOLockstepTime(player), + }; + _enqueueEvent(coordinator, &event, TARGET_ALL & ~TARGET(player->playerId)); + GBASIOLockstepCoordinatorWakePlayers(coordinator); + if (player->playerId != 0) { + GBASIOLockstepCoordinatorAckPlayer(coordinator, player); + } + TableRemove(&coordinator->players, player->driver->lockstepId); + _reconfigPlayers(coordinator); +} + +void _reconfigPlayers(struct GBASIOLockstepCoordinator* coordinator) { + size_t players = TableSize(&coordinator->players); + memset(coordinator->attachedPlayers, 0, sizeof(coordinator->attachedPlayers)); + if (players == 0) { + mLOG(GBA_SIO, WARN, "Reconfiguring player IDs with no players attached somehow?"); + } else if (players == 1) { + struct TableIterator iter; + mASSERT(TableIteratorStart(&coordinator->players, &iter)); + unsigned p0 = TableIteratorGetKey(&coordinator->players, &iter); + coordinator->attachedPlayers[0] = p0; + + struct GBASIOLockstepPlayer* player = TableIteratorGetValue(&coordinator->players, &iter); + coordinator->cycle = mTimingCurrentTime(&player->driver->d.p->p->timing); + + if (player->playerId != 0) { + player->playerId = 0; + if (player->driver->user->playerIdChanged) { + player->driver->user->playerIdChanged(player->driver->user, player->playerId); + } + } + + if (!coordinator->transferActive) { + coordinator->transferMode = player->mode; + } + } else { + struct UIntList playerPreferences[MAX_GBAS]; + + int i; + for (i = 0; i < MAX_GBAS; ++i) { + UIntListInit(&playerPreferences[i], 4); + } + + // Collect the first four players' requested player IDs so we can sort through them later + int seen = 0; + struct TableIterator iter; + mASSERT(TableIteratorStart(&coordinator->players, &iter)); + do { + unsigned pid = TableIteratorGetKey(&coordinator->players, &iter); + struct GBASIOLockstepPlayer* player = TableIteratorGetValue(&coordinator->players, &iter); + int requested = MAX_GBAS - 1; + if (player->driver->user->requestedId) { + requested = player->driver->user->requestedId(player->driver->user); + } + if (requested < 0) { + continue; + } + if (requested >= MAX_GBAS) { + requested = MAX_GBAS - 1; + } + + *UIntListAppend(&playerPreferences[requested]) = pid; + ++seen; + } while (TableIteratorNext(&coordinator->players, &iter) && seen < MAX_GBAS); + + // Now sort each requested player ID to figure out who gets which ID + seen = 0; + for (i = 0; i < MAX_GBAS; ++i) { + int j; + for (j = 0; j <= i; ++j) { + while (UIntListSize(&playerPreferences[j]) && seen < MAX_GBAS) { + unsigned pid = *UIntListGetPointer(&playerPreferences[j], 0); + UIntListShift(&playerPreferences[j], 0, 1); + struct GBASIOLockstepPlayer* player = TableLookup(&coordinator->players, pid); + if (!player) { + mLOG(GBA_SIO, ERROR, "Player list appears to have changed unexpectedly. PID %u missing.", pid); + continue; + } + coordinator->attachedPlayers[seen] = pid; + if (player->playerId != seen) { + player->playerId = seen; + if (player->driver->user->playerIdChanged) { + player->driver->user->playerIdChanged(player->driver->user, player->playerId); + } + } + ++seen; + } + } + } + + for (i = 0; i < MAX_GBAS; ++i) { + UIntListDeinit(&playerPreferences[i]); + } + } + + int nAttached = 0; + size_t i; + for (i = 0; i < MAX_GBAS; ++i) { + unsigned pid = coordinator->attachedPlayers[i]; + if (!pid) { + continue; + } + struct GBASIOLockstepPlayer* player = TableLookup(&coordinator->players, pid); + if (!player) { + coordinator->attachedPlayers[i] = 0; + } else { + ++nAttached; + } + } + coordinator->nAttached = nAttached; +} + +static void _setData(struct GBASIOLockstepCoordinator* coordinator, uint32_t id, struct GBASIO* sio) { + switch (coordinator->transferMode) { + case GBA_SIO_MULTI: + coordinator->multiData[id] = sio->p->memory.io[GBA_REG(SIOMLT_SEND)]; + break; + case GBA_SIO_NORMAL_8: + coordinator->normalData[id] = sio->p->memory.io[GBA_REG(SIODATA8)]; + break; + case GBA_SIO_NORMAL_32: + coordinator->normalData[id] = sio->p->memory.io[GBA_REG(SIODATA32_LO)]; + coordinator->normalData[id] |= sio->p->memory.io[GBA_REG(SIODATA32_HI)] << 16; + break; + case GBA_SIO_UART: + case GBA_SIO_GPIO: + case GBA_SIO_JOYBUS: + mLOG(GBA_SIO, ERROR, "Unsupported mode %i in lockstep", coordinator->transferMode); + // TODO: Should we handle this or just abort? + abort(); + break; + } +} + +void _setReady(struct GBASIOLockstepCoordinator* coordinator, struct GBASIOLockstepPlayer* activePlayer, int playerId, enum GBASIOMode mode) { + activePlayer->otherModes[playerId] = mode; + bool ready = true; + int i; + for (i = 0; ready && i < coordinator->nAttached; ++i) { + ready = activePlayer->otherModes[i] == activePlayer->mode; + } + if (activePlayer->mode == GBA_SIO_MULTI) { + struct GBASIO* sio = activePlayer->driver->d.p; + sio->siocnt = GBASIOMultiplayerSetReady(sio->siocnt, ready); + sio->rcnt = GBASIORegisterRCNTSetSd(sio->rcnt, ready); + } +} + +void _hardSync(struct GBASIOLockstepCoordinator* coordinator, struct GBASIOLockstepPlayer* player) { + mASSERT_DEBUG(player->playerId == 0); + struct GBASIOLockstepEvent event = { + .type = SIO_EV_HARD_SYNC, + .playerId = 0, + .timestamp = GBASIOLockstepTime(player), + }; + _enqueueEvent(coordinator, &event, TARGET_SECONDARY); + GBASIOLockstepCoordinatorWaitOnPlayers(coordinator, player); +} + +void _enqueueEvent(struct GBASIOLockstepCoordinator* coordinator, const struct GBASIOLockstepEvent* event, uint32_t target) { + mLOG(GBA_SIO, DEBUG, "Enqueuing event of type %X from %i for target %X at timestamp %X", + event->type, event->playerId, target, event->timestamp); + + int i; + for (i = 0; i < coordinator->nAttached; ++i) { + if (!(target & TARGET(i))) { + continue; + } + struct GBASIOLockstepPlayer* player = TableLookup(&coordinator->players, coordinator->attachedPlayers[i]); + mASSERT_LOG(GBA_SIO, player->freeList, "No free events"); + struct GBASIOLockstepEvent* newEvent = player->freeList; + player->freeList = newEvent->next; + + memcpy(newEvent, event, sizeof(*event)); + struct GBASIOLockstepEvent** previous = &player->queue; + struct GBASIOLockstepEvent* next = player->queue; + while (next) { + int32_t until = newEvent->timestamp - next->timestamp; + if (until < 0) { + break; + } + previous = &next->next; + next = next->next; + } + newEvent->next = next; + *previous = newEvent; + } +} + +void _lockstepEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) { + struct GBASIOLockstepDriver* lockstep = context; + struct GBASIOLockstepCoordinator* coordinator = lockstep->coordinator; + MutexLock(&coordinator->mutex); + struct GBASIOLockstepPlayer* player = TableLookup(&coordinator->players, lockstep->lockstepId); + struct GBASIO* sio = player->driver->d.p; + mASSERT(player->playerId >= 0 && player->playerId < 4); + + bool wasDetach = false; + if (player->queue && player->queue->type == SIO_EV_DETACH) { + mLOG(GBA_SIO, DEBUG, "Player %i detached at timestamp %X, picking up the pieces", + player->queue->playerId, player->queue->timestamp); + wasDetach = true; + } + if (player->playerId == 0) { + // We are the clock owner; advance the shared clock + _advanceCycle(coordinator, player); + if (!coordinator->transferActive) { + GBASIOLockstepCoordinatorWakePlayers(coordinator); + } + } + + int32_t nextEvent = _untilNextSync(coordinator, player); + //mASSERT_DEBUG(nextEvent + cyclesLate > 0); + while (true) { + struct GBASIOLockstepEvent* event = player->queue; + if (!event) { + break; + } + if (event->timestamp > GBASIOLockstepTime(player)) { + break; + } + player->queue = event->next; + struct GBASIOLockstepEvent reply = { + .playerId = player->playerId, + .timestamp = GBASIOLockstepTime(player), + }; + mLOG(GBA_SIO, DEBUG, "Got event of type %X from %i at timestamp %X", + event->type, event->playerId, event->timestamp); + switch (event->type) { + case SIO_EV_ATTACH: + _setReady(coordinator, player, event->playerId, -1); + if (player->playerId == 0) { + struct GBASIO* sio = player->driver->d.p; + sio->siocnt = GBASIOMultiplayerClearSlave(sio->siocnt); + } + reply.mode = player->mode; + reply.type = SIO_EV_MODE_SET; + _enqueueEvent(coordinator, &reply, TARGET(event->playerId)); + break; + case SIO_EV_HARD_SYNC: + GBASIOLockstepCoordinatorAckPlayer(coordinator, player); + break; + case SIO_EV_TRANSFER_START: + _setData(coordinator, player->playerId, sio); + nextEvent = event->finishCycle - GBASIOLockstepTime(player) - cyclesLate; + player->driver->d.p->siocnt |= 0x80; + mTimingDeschedule(&sio->p->timing, &sio->completeEvent); + mTimingSchedule(&sio->p->timing, &sio->completeEvent, nextEvent); + GBASIOLockstepCoordinatorAckPlayer(coordinator, player); + break; + case SIO_EV_MODE_SET: + _setReady(coordinator, player, event->playerId, event->mode); + if (event->playerId == 0) { + GBASIOLockstepCoordinatorAckPlayer(coordinator, player); + } + break; + case SIO_EV_DETACH: + _setReady(coordinator, player, event->playerId, -1); + _setReady(coordinator, player, player->playerId, player->mode); + reply.mode = player->mode; + reply.type = SIO_EV_MODE_SET; + _enqueueEvent(coordinator, &reply, ~TARGET(event->playerId)); + if (player->mode == GBA_SIO_MULTI) { + sio->siocnt = GBASIOMultiplayerSetId(sio->siocnt, player->playerId); + sio->siocnt = GBASIOMultiplayerSetSlave(sio->siocnt, player->playerId || coordinator->nAttached < 2); + } + wasDetach = true; + break; + } + event->next = player->freeList; + player->freeList = event; + } + if (player->queue && player->queue->timestamp - GBASIOLockstepTime(player) < nextEvent) { + nextEvent = player->queue->timestamp - GBASIOLockstepTime(player); + } + + if (player->playerId != 0 && nextEvent <= LOCKSTEP_INCREMENT) { + if (!player->queue || wasDetach) { + GBASIOLockstepPlayerSleep(player); + // XXX: Is there a better way to gain sync lock at the beginning? + if (nextEvent < 4) { + nextEvent = 4; + } + } + } + MutexUnlock(&coordinator->mutex); + + mASSERT_DEBUG(nextEvent > 0); + mTimingSchedule(timing, &lockstep->event, nextEvent); +} + +int32_t GBASIOLockstepTime(struct GBASIOLockstepPlayer* player) { + return mTimingCurrentTime(&player->driver->d.p->p->timing) - player->cycleOffset; +} + +void GBASIOLockstepCoordinatorWaitOnPlayers(struct GBASIOLockstepCoordinator* coordinator, struct GBASIOLockstepPlayer* player) { + mASSERT(!coordinator->waiting); + mASSERT(!player->asleep); + mASSERT(player->playerId == 0); + if (coordinator->nAttached < 2) { + return; + } + + _advanceCycle(coordinator, player); + mLOG(GBA_SIO, DEBUG, "Primary waiting for players to ack"); + coordinator->waiting = ((1 << coordinator->nAttached) - 1) & ~TARGET(player->playerId); + GBASIOLockstepPlayerSleep(player); + GBASIOLockstepCoordinatorWakePlayers(coordinator); +} + +void GBASIOLockstepCoordinatorWakePlayers(struct GBASIOLockstepCoordinator* coordinator) { + //mLOG(GBA_SIO, DEBUG, "Waking all secondary players"); + int i; + for (i = 1; i < coordinator->nAttached; ++i) { + if (!coordinator->attachedPlayers[i]) { + continue; + } + struct GBASIOLockstepPlayer* player = TableLookup(&coordinator->players, coordinator->attachedPlayers[i]); + GBASIOLockstepPlayerWake(player); + } +} + +void GBASIOLockstepPlayerWake(struct GBASIOLockstepPlayer* player) { + if (!player->asleep) { + return; + } + player->asleep = false; + player->driver->user->wake(player->driver->user); +} + +void GBASIOLockstepCoordinatorAckPlayer(struct GBASIOLockstepCoordinator* coordinator, struct GBASIOLockstepPlayer* player) { + if (player->playerId == 0) { + return; + } + mLOG(GBA_SIO, DEBUG, "Player %i acking primary", player->playerId); + coordinator->waiting &= ~TARGET(player->playerId); + if (!coordinator->waiting) { + mLOG(GBA_SIO, DEBUG, "All players acked, waking primary"); + if (coordinator->transferActive) { + int i; + for (i = 0; i < coordinator->nAttached; ++i) { + if (!coordinator->attachedPlayers[i]) { + continue; + } + struct GBASIOLockstepPlayer* player = TableLookup(&coordinator->players, coordinator->attachedPlayers[i]); + player->dataReceived = true; + } + + coordinator->transferActive = false; + } + + struct GBASIOLockstepPlayer* runner = TableLookup(&coordinator->players, coordinator->attachedPlayers[0]); + GBASIOLockstepPlayerWake(runner); + } + GBASIOLockstepPlayerSleep(player); +} + +void GBASIOLockstepPlayerSleep(struct GBASIOLockstepPlayer* player) { + if (player->asleep) { + return; + } + //mLOG(GBA_SIO, DEBUG, "Player %i going to sleep with %i cycles until sync", player->playerId, _untilNextSync(coordinator, player)); + player->asleep = true; + player->driver->user->sleep(player->driver->user); + player->driver->d.p->p->cpu->nextEvent = 0; + player->driver->d.p->p->earlyExit = true; +} + +size_t GBASIOLockstepCoordinatorAttached(struct GBASIOLockstepCoordinator* coordinator) { + size_t count; + MutexLock(&coordinator->mutex); + count = TableSize(&coordinator->players); + MutexUnlock(&coordinator->mutex); + return count; +} From cd8933415cfcc25452ecfcb95292faa8ea9c74b3 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 25 Aug 2024 02:48:50 -0700 Subject: [PATCH 030/114] Qt: Switch to new GBA lockstep driver --- src/platform/qt/MultiplayerController.cpp | 120 ++++++++++++++-------- src/platform/qt/MultiplayerController.h | 17 ++- 2 files changed, 88 insertions(+), 49 deletions(-) diff --git a/src/platform/qt/MultiplayerController.cpp b/src/platform/qt/MultiplayerController.cpp index 149df2386..c142fe126 100644 --- a/src/platform/qt/MultiplayerController.cpp +++ b/src/platform/qt/MultiplayerController.cpp @@ -7,6 +7,7 @@ #include "CoreController.h" #include "LogController.h" +#include "utils.h" #ifdef M_CORE_GBA #include @@ -27,8 +28,14 @@ MultiplayerController::Player::Player(CoreController* coreController) int MultiplayerController::Player::id() const { switch (controller->platform()) { #ifdef M_CORE_GBA - case mPLATFORM_GBA: - return node.gba->id; + case mPLATFORM_GBA: { + int id = node.gba->d.deviceId(&node.gba->d); + if (id >= 0) { + return id; + } else { + return preferredId; + } + } #endif #ifdef M_CORE_GB case mPLATFORM_GB: @@ -89,25 +96,7 @@ MultiplayerController::MultiplayerController() { switch (player->controller->platform()) { #ifdef M_CORE_GBA case mPLATFORM_GBA: - if (!id) { - for (int i = 1; i < controller->m_players.count(); ++i) { - player = controller->player(i); - if (player->node.gba->d.p->mode > GBA_SIO_MULTI) { - player->controller->setSync(true); - continue; - } - player->controller->setSync(false); - player->cyclesPosted += cycles; - if (player->awake < 1) { - player->node.gba->nextEvent += player->cyclesPosted; - } - mCoreThreadStopWaiting(player->controller->thread()); - player->awake = 1; - } - } else { - player->controller->setSync(true); - player->cyclesPosted += cycles; - } + abort(); break; #endif #ifdef M_CORE_GB @@ -169,7 +158,6 @@ MultiplayerController::MultiplayerController() { switch (player->controller->platform()) { #ifdef M_CORE_GBA case mPLATFORM_GBA: - player->cyclesPosted += reinterpret_cast(lockstep)->players[0]->eventDiff; break; #endif #ifdef M_CORE_GB @@ -184,7 +172,6 @@ MultiplayerController::MultiplayerController() { switch (player->controller->platform()) { #ifdef M_CORE_GBA case mPLATFORM_GBA: - player->node.gba->nextEvent += player->cyclesPosted; break; #endif #ifdef M_CORE_GB @@ -214,11 +201,11 @@ bool MultiplayerController::attachGame(CoreController* controller) { interrupters.append(p.controller); } - if (m_lockstep.attached == 0) { + if (attached() == 0) { switch (controller->platform()) { #ifdef M_CORE_GBA case mPLATFORM_GBA: - GBASIOLockstepInit(&m_gbaLockstep); + GBASIOLockstepCoordinatorInit(&m_gbaCoordinator); break; #endif #ifdef M_CORE_GB @@ -240,28 +227,53 @@ bool MultiplayerController::attachGame(CoreController* controller) { } Player player{controller}; + for (int i = 0; i < MAX_GBAS; ++i) { + if (m_claimedIds & (1 << i)) { + continue; + } + player.preferredId = i; + m_claimedIds |= 1 << i; + break; + } switch (controller->platform()) { #ifdef M_CORE_GBA case mPLATFORM_GBA: { - if (m_lockstep.attached >= MAX_GBAS) { + if (attached() >= MAX_GBAS) { return false; } GBA* gba = static_cast(thread->core->board); - GBASIOLockstepNode* node = new GBASIOLockstepNode; - GBASIOLockstepNodeCreate(node); - GBASIOLockstepAttachNode(&m_gbaLockstep, node); + GBASIOLockstepDriver* node = new GBASIOLockstepDriver; + LockstepUser* user = new LockstepUser; + mLockstepThreadUserInit(user, thread); + user->controller = this; + user->pid = m_nextPid; + user->d.requestedId = [](mLockstepUser* ctx) { + mLockstepThreadUser* tctx = reinterpret_cast(ctx); + LockstepUser* user = static_cast(tctx); + MultiplayerController* controller = user->controller; + const auto iter = controller->m_pids.find(user->pid); + if (iter == controller->m_pids.end()) { + return -1; + } + const Player& p = iter.value(); + return p.preferredId; + }; + + GBASIOLockstepDriverCreate(node, &user->d); + GBASIOLockstepCoordinatorAttach(&m_gbaCoordinator, node); player.node.gba = node; GBASIOSetDriver(&gba->sio, &node->d, GBA_SIO_MULTI); + GBASIOSetDriver(&gba->sio, &node->d, GBA_SIO_NORMAL_8); GBASIOSetDriver(&gba->sio, &node->d, GBA_SIO_NORMAL_32); break; } #endif #ifdef M_CORE_GB case mPLATFORM_GB: { - if (m_lockstep.attached >= 2) { + if (attached() >= 2) { return false; } @@ -281,7 +293,7 @@ bool MultiplayerController::attachGame(CoreController* controller) { } QPair path(controller->path(), controller->baseDirectory()); - int claimed = m_claimed[path]; + int claimed = m_claimedSaves[path]; int saveId = 0; mCoreConfigGetIntValue(&controller->thread()->core->config, "savePlayerId", &saveId); @@ -304,7 +316,7 @@ bool MultiplayerController::attachGame(CoreController* controller) { } else { player.saveId = 1; } - m_claimed[path] |= 1 << (player.saveId - 1); + m_claimedSaves[path] |= 1 << (player.saveId - 1); m_pids.insert(m_nextPid, player); ++m_nextPid; @@ -328,8 +340,7 @@ void MultiplayerController::detachGame(CoreController* controller) { for (int i = 0; i < m_players.count(); ++i) { Player* p = player(i); if (!p) { - LOG(QT, ERROR) << tr("Trying to detach a multiplayer player that's not attached"); - return; + continue; } CoreController* playerController = p->controller; if (playerController == controller) { @@ -337,15 +348,21 @@ void MultiplayerController::detachGame(CoreController* controller) { } interrupters.append(playerController); } + if (pid < 0) { + LOG(QT, WARN) << tr("Trying to detach a multiplayer player that's not attached"); + return; + } switch (controller->platform()) { #ifdef M_CORE_GBA case mPLATFORM_GBA: { GBA* gba = static_cast(thread->core->board); - GBASIOLockstepNode* node = reinterpret_cast(gba->sio.drivers.multiplayer); + GBASIOLockstepDriver* node = reinterpret_cast(gba->sio.drivers.multiplayer); GBASIOSetDriver(&gba->sio, nullptr, GBA_SIO_MULTI); + GBASIOSetDriver(&gba->sio, nullptr, GBA_SIO_NORMAL_8); GBASIOSetDriver(&gba->sio, nullptr, GBA_SIO_NORMAL_32); if (node) { - GBASIOLockstepDetachNode(&m_gbaLockstep, node); + GBASIOLockstepCoordinatorDetach(&m_gbaCoordinator, node); + delete node->user; delete node; } break; @@ -371,14 +388,20 @@ void MultiplayerController::detachGame(CoreController* controller) { QPair path(controller->path(), controller->baseDirectory()); Player& p = m_pids.find(pid).value(); if (!p.saveId) { - LOG(QT, ERROR) << "Clearing invalid save ID"; + LOG(QT, WARN) << "Clearing invalid save ID"; } else { - m_claimed[path] &= ~(1 << (p.saveId - 1)); - if (!m_claimed[path]) { - m_claimed.remove(path); + m_claimedSaves[path] &= ~(1 << (p.saveId - 1)); + if (!m_claimedSaves[path]) { + m_claimedSaves.remove(path); } } + if (p.preferredId < 0) { + LOG(QT, WARN) << "Clearing invalid preferred ID"; + } else { + m_claimedIds &= ~(1 << p.preferredId); + } + m_pids.remove(pid); if (m_pids.size() == 0) { m_platform = mPLATFORM_NONE; @@ -417,8 +440,17 @@ int MultiplayerController::saveId(CoreController* controller) const { } int MultiplayerController::attached() { - int num; - num = m_lockstep.attached; + int num = 0; + switch (m_platform) { + case mPLATFORM_GB: + num = m_lockstep.attached; + break; + case mPLATFORM_GBA: + num = saturateCast(GBASIOLockstepCoordinatorAttached(&m_gbaCoordinator)); + break; + default: + break; + } return num; } @@ -456,8 +488,8 @@ void MultiplayerController::fixOrder() { for (int pid : m_pids.keys()) { Player& p = m_pids.find(pid).value(); GBA* gba = static_cast(p.controller->thread()->core->board); - GBASIOLockstepNode* node = reinterpret_cast(gba->sio.drivers.multiplayer); - m_players[node->id] = pid; + GBASIOLockstepDriver* node = reinterpret_cast(gba->sio.drivers.multiplayer); + m_players[node->d.deviceId(&node->d)] = pid; } break; #endif diff --git a/src/platform/qt/MultiplayerController.h b/src/platform/qt/MultiplayerController.h index b5582319f..9eaf20b16 100644 --- a/src/platform/qt/MultiplayerController.h +++ b/src/platform/qt/MultiplayerController.h @@ -49,7 +49,7 @@ signals: private: union Node { GBSIOLockstepNode* gb; - GBASIOLockstepNode* gba; + GBASIOLockstepDriver* gba; }; struct Player { Player(CoreController* controller); @@ -63,6 +63,11 @@ private: int32_t cyclesPosted = 0; unsigned waitMask = 0; int saveId = 1; + int preferredId = 0; + }; + struct LockstepUser : mLockstepThreadUser { + MultiplayerController* controller; + int pid; }; Player* player(int id); @@ -73,18 +78,20 @@ private: mLockstep m_lockstep; #ifdef M_CORE_GB GBSIOLockstep m_gbLockstep; -#endif -#ifdef M_CORE_GBA - GBASIOLockstep m_gbaLockstep; #endif }; +#ifdef M_CORE_GBA + GBASIOLockstepCoordinator m_gbaCoordinator; +#endif + mPlatform m_platform = mPLATFORM_NONE; int m_nextPid = 0; + int m_claimedIds = 0; QHash m_pids; QList m_players; QMutex m_lock; - QHash, int> m_claimed; + QHash, int> m_claimedSaves; }; } From 26e20ca8461f76eefacc2a3906b74ec53dc39741 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 30 Aug 2024 23:26:29 -0700 Subject: [PATCH 031/114] GBA SIO: Remove old lockstep driver --- include/mgba/internal/gba/sio/lockstep.h | 34 -- src/gba/sio/lockstep.c | 561 ----------------------- 2 files changed, 595 deletions(-) diff --git a/include/mgba/internal/gba/sio/lockstep.h b/include/mgba/internal/gba/sio/lockstep.h index 313285a44..7baed0693 100644 --- a/include/mgba/internal/gba/sio/lockstep.h +++ b/include/mgba/internal/gba/sio/lockstep.h @@ -17,40 +17,6 @@ CXX_GUARD_START #include #include -struct GBASIOLockstep { - struct mLockstep d; - struct GBASIOLockstepNode* players[MAX_GBAS]; - int attachedMulti; - int attachedNormal; - - uint16_t multiRecv[MAX_GBAS]; - uint32_t normalRecv[MAX_GBAS]; -}; - -struct GBASIOLockstepNode { - struct GBASIODriver d; - struct GBASIOLockstep* p; - struct mTimingEvent event; - - volatile int32_t nextEvent; - int32_t eventDiff; - bool normalSO; - int id; - enum GBASIOMode mode; - bool transferFinished; -#ifndef NDEBUG - int transferId; - enum mLockstepPhase phase; -#endif -}; - -void GBASIOLockstepInit(struct GBASIOLockstep*); - -void GBASIOLockstepNodeCreate(struct GBASIOLockstepNode*); - -bool GBASIOLockstepAttachNode(struct GBASIOLockstep*, struct GBASIOLockstepNode*); -void GBASIOLockstepDetachNode(struct GBASIOLockstep*, struct GBASIOLockstepNode*); - #define MAX_LOCKSTEP_EVENTS 8 enum GBASIOLockstepEventType { diff --git a/src/gba/sio/lockstep.c b/src/gba/sio/lockstep.c index 18603e054..1493ca9f6 100644 --- a/src/gba/sio/lockstep.c +++ b/src/gba/sio/lockstep.c @@ -9,567 +9,6 @@ #include #define LOCKSTEP_INCREMENT 2000 -#define LOCKSTEP_TRANSFER 512 -#define QUEUE_SIZE 16 - -static bool GBASIOLockstepNodeInit(struct GBASIODriver* driver); -static void GBASIOLockstepNodeDeinit(struct GBASIODriver* driver); -static bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver); -static bool GBASIOLockstepNodeUnload(struct GBASIODriver* driver); -static bool GBASIOLockstepNodeHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode); -static int GBASIOLockstepNodeConnectedDevices(struct GBASIODriver* driver); -static int GBASIOLockstepNodeDeviceId(struct GBASIODriver* driver); -static bool GBASIOLockstepNodeStart(struct GBASIODriver* driver); -static uint16_t GBASIOLockstepNodeMultiWriteSIOCNT(struct GBASIODriver* driver, uint16_t value); -static uint16_t GBASIOLockstepNodeNormalWriteSIOCNT(struct GBASIODriver* driver, uint16_t value); -static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* driver, uint32_t cyclesLate); -static void _finishTransfer(struct GBASIOLockstepNode* node); - -void GBASIOLockstepInit(struct GBASIOLockstep* lockstep) { - lockstep->players[0] = 0; - lockstep->players[1] = 0; - lockstep->players[2] = 0; - lockstep->players[3] = 0; - lockstep->multiRecv[0] = 0xFFFF; - lockstep->multiRecv[1] = 0xFFFF; - lockstep->multiRecv[2] = 0xFFFF; - lockstep->multiRecv[3] = 0xFFFF; - lockstep->attachedMulti = 0; - lockstep->attachedNormal = 0; -} - -void GBASIOLockstepNodeCreate(struct GBASIOLockstepNode* node) { - memset(&node->d, 0, sizeof(node->d)); - node->d.init = GBASIOLockstepNodeInit; - node->d.deinit = GBASIOLockstepNodeDeinit; - node->d.load = GBASIOLockstepNodeLoad; - node->d.unload = GBASIOLockstepNodeUnload; - node->d.handlesMode = GBASIOLockstepNodeHandlesMode; - node->d.connectedDevices = GBASIOLockstepNodeConnectedDevices; - node->d.deviceId = GBASIOLockstepNodeDeviceId; - node->d.start = GBASIOLockstepNodeStart; - node->d.writeSIOCNT = NULL; -} - -bool GBASIOLockstepAttachNode(struct GBASIOLockstep* lockstep, struct GBASIOLockstepNode* node) { - if (lockstep->d.attached == MAX_GBAS) { - return false; - } - mLockstepLock(&lockstep->d); - lockstep->players[lockstep->d.attached] = node; - node->p = lockstep; - node->id = lockstep->d.attached; - node->normalSO = true; - node->transferFinished = true; - ++lockstep->d.attached; - mLockstepUnlock(&lockstep->d); - return true; -} - -void GBASIOLockstepDetachNode(struct GBASIOLockstep* lockstep, struct GBASIOLockstepNode* node) { - if (lockstep->d.attached == 0) { - return; - } - mLockstepLock(&lockstep->d); - int i; - for (i = 0; i < lockstep->d.attached; ++i) { - if (lockstep->players[i] != node) { - continue; - } - for (++i; i < lockstep->d.attached; ++i) { - lockstep->players[i - 1] = lockstep->players[i]; - lockstep->players[i - 1]->id = i - 1; - } - --lockstep->d.attached; - lockstep->players[lockstep->d.attached] = NULL; - break; - } - mLockstepUnlock(&lockstep->d); -} - -bool GBASIOLockstepNodeInit(struct GBASIODriver* driver) { - struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver; - node->d.p->siocnt = GBASIOMultiplayerSetSlave(node->d.p->siocnt, node->id > 0); - mLOG(GBA_SIO, DEBUG, "Lockstep %i: Node init", node->id); - node->event.context = node; - node->event.name = "GBA SIO Lockstep"; - node->event.callback = _GBASIOLockstepNodeProcessEvents; - node->event.priority = 0x80; - return true; -} - -void GBASIOLockstepNodeDeinit(struct GBASIODriver* driver) { - UNUSED(driver); -} - -bool GBASIOLockstepNodeLoad(struct GBASIODriver* driver) { - struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver; - node->nextEvent = 0; - node->eventDiff = 0; - mTimingSchedule(&driver->p->p->timing, &node->event, 0); - - mLockstepLock(&node->p->d); - - node->mode = driver->p->mode; - - switch (node->mode) { - case GBA_SIO_MULTI: - node->d.writeSIOCNT = GBASIOLockstepNodeMultiWriteSIOCNT; - ATOMIC_ADD(node->p->attachedMulti, 1); - node->d.p->siocnt = GBASIOMultiplayerSetReady(node->d.p->siocnt, node->p->attachedMulti == node->p->d.attached); - if (node->id) { - node->d.p->rcnt |= 4; - int try; - for (try = 0; try < 3; ++try) { - uint16_t masterSiocnt; - ATOMIC_LOAD(masterSiocnt, node->p->players[0]->d.p->siocnt); - if (ATOMIC_CMPXCHG(node->p->players[0]->d.p->siocnt, masterSiocnt, GBASIOMultiplayerClearSlave(masterSiocnt))) { - break; - } - } - } - break; - case GBA_SIO_NORMAL_8: - case GBA_SIO_NORMAL_32: - if (ATOMIC_ADD(node->p->attachedNormal, 1) > node->id + 1 && node->id > 0) { - node->d.p->siocnt = GBASIONormalSetSi(node->d.p->siocnt, GBASIONormalGetIdleSo(node->p->players[node->id - 1]->d.p->siocnt)); - } else { - node->d.p->siocnt = GBASIONormalClearSi(node->d.p->siocnt); - } - node->d.writeSIOCNT = GBASIOLockstepNodeNormalWriteSIOCNT; - break; - default: - break; - } -#ifndef NDEBUG - node->phase = node->p->d.transferActive; - node->transferId = node->p->d.transferId; -#endif - - mLockstepUnlock(&node->p->d); - - return true; -} - -bool GBASIOLockstepNodeUnload(struct GBASIODriver* driver) { - struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver; - - mLockstepLock(&node->p->d); - - node->mode = driver->p->mode; - switch (node->mode) { - case GBA_SIO_MULTI: - ATOMIC_SUB(node->p->attachedMulti, 1); - break; - case GBA_SIO_NORMAL_8: - case GBA_SIO_NORMAL_32: - ATOMIC_SUB(node->p->attachedNormal, 1); - break; - default: - break; - } - - // Flush ongoing transfer - if (mTimingIsScheduled(&driver->p->p->timing, &node->event)) { - node->eventDiff -= node->event.when - mTimingCurrentTime(&driver->p->p->timing); - mTimingDeschedule(&driver->p->p->timing, &node->event); - } - - node->p->d.unload(&node->p->d, node->id); - - _finishTransfer(node); - - if (!node->id) { - ATOMIC_STORE(node->p->d.transferActive, TRANSFER_IDLE); - } - - // Invalidate SIO mode - node->mode = GBA_SIO_GPIO; - - mLockstepUnlock(&node->p->d); - - return true; -} - -static bool GBASIOLockstepNodeHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode) { - UNUSED(driver); - switch (mode) { - case GBA_SIO_NORMAL_8: - case GBA_SIO_NORMAL_32: - case GBA_SIO_MULTI: - return true; - default: - return false; - } -} - -static int GBASIOLockstepNodeConnectedDevices(struct GBASIODriver* driver) { - struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver; - int attached = 1; - - switch (node->mode) { - case GBA_SIO_NORMAL_8: - case GBA_SIO_NORMAL_32: - ATOMIC_LOAD(attached, node->p->attachedNormal); - break; - case GBA_SIO_MULTI: - ATOMIC_LOAD(attached, node->p->attachedMulti); - break; - default: - break; - } - return attached - 1; -} - -static int GBASIOLockstepNodeDeviceId(struct GBASIODriver* driver) { - struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver; - return node->id; -} - -static bool GBASIOLockstepNodeStart(struct GBASIODriver* driver) { - UNUSED(driver); - return false; -} - -static uint16_t GBASIOLockstepNodeMultiWriteSIOCNT(struct GBASIODriver* driver, uint16_t value) { - struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver; - - mLockstepLock(&node->p->d); - - mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04X", node->id, value); - - enum mLockstepPhase transferActive; - int attached; - ATOMIC_LOAD(transferActive, node->p->d.transferActive); - ATOMIC_LOAD(attached, node->p->d.attached); - - driver->p->siocnt = GBASIOMultiplayerSetSlave(driver->p->siocnt, node->id || attached < 2); - - if (value & 0x0080 && transferActive == TRANSFER_IDLE) { - if (!node->id && attached > 1 && GBASIOMultiplayerIsReady(node->d.p->siocnt)) { - mLOG(GBA_SIO, DEBUG, "Lockstep %i: Transfer initiated", node->id); - ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING); - ATOMIC_STORE(node->p->d.transferCycles, GBASIOTransferCycles(node->d.p->mode, node->d.p->siocnt, attached)); - - if (mTimingIsScheduled(&driver->p->p->timing, &node->event)) { - node->eventDiff -= node->event.when - mTimingCurrentTime(&driver->p->p->timing); - mTimingDeschedule(&driver->p->p->timing, &node->event); - } - mTimingSchedule(&driver->p->p->timing, &node->event, 0); - } - } - value &= 0xFF83; - value |= driver->p->siocnt & 0x00FC; - - mLockstepUnlock(&node->p->d); - - return value; -} - -static void _finishTransfer(struct GBASIOLockstepNode* node) { - if (node->transferFinished) { - return; - } - - struct GBASIO* sio = node->d.p; - switch (node->mode) { - case GBA_SIO_MULTI: - GBASIOMultiplayerFinishTransfer(sio, node->p->multiRecv, 0); - break; - case GBA_SIO_NORMAL_8: - if (node->id) { - sio->siocnt = GBASIONormalSetSi(sio->siocnt, GBASIONormalGetIdleSo(node->p->players[node->id - 1]->d.p->siocnt)); - GBASIONormal8FinishTransfer(sio, node->p->normalRecv[node->id - 1], 0); - } else { - GBASIONormal8FinishTransfer(sio, 0xFF, 0); - } - break; - case GBA_SIO_NORMAL_32: - if (node->id) { - sio->siocnt = GBASIONormalSetSi(sio->siocnt, GBASIONormalGetIdleSo(node->p->players[node->id - 1]->d.p->siocnt)); - GBASIONormal32FinishTransfer(sio, node->p->normalRecv[node->id - 1], 0); - } else { - GBASIONormal32FinishTransfer(sio, 0xFFFFFFFF, 0); - } - break; - default: - break; - } - node->transferFinished = true; -#ifndef NDEBUG - ++node->transferId; -#endif -} - -static int32_t _masterUpdate(struct GBASIOLockstepNode* node) { - bool needsToWait = false; - int i; - - enum mLockstepPhase transferActive; - int attachedMulti, attached; - - ATOMIC_LOAD(transferActive, node->p->d.transferActive); - ATOMIC_LOAD(attachedMulti, node->p->attachedMulti); - ATOMIC_LOAD(attached, node->p->d.attached); - - switch (transferActive) { - case TRANSFER_IDLE: - // If the master hasn't initiated a transfer, it can keep going. - node->nextEvent += LOCKSTEP_INCREMENT; - if (node->mode == GBA_SIO_MULTI) { - node->d.p->siocnt = GBASIOMultiplayerSetReady(node->d.p->siocnt, attachedMulti == attached); - } - break; - case TRANSFER_STARTING: - // Start the transfer, but wait for the other GBAs to catch up - node->transferFinished = false; - switch (node->mode) { - case GBA_SIO_MULTI: - node->p->multiRecv[0] = node->d.p->p->memory.io[GBA_REG(SIOMLT_SEND)]; - node->p->multiRecv[1] = 0xFFFF; - node->p->multiRecv[2] = 0xFFFF; - node->p->multiRecv[3] = 0xFFFF; - break; - case GBA_SIO_NORMAL_8: - node->p->multiRecv[0] = 0xFFFF; - node->p->normalRecv[0] = node->d.p->p->memory.io[GBA_REG(SIODATA8)] & 0xFF; - break; - case GBA_SIO_NORMAL_32: - node->p->multiRecv[0] = 0xFFFF; - mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_LO <- %04X", node->id, node->d.p->p->memory.io[GBA_REG(SIODATA32_LO)]); - mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIODATA32_HI <- %04X", node->id, node->d.p->p->memory.io[GBA_REG(SIODATA32_HI)]); - node->p->normalRecv[0] = node->d.p->p->memory.io[GBA_REG(SIODATA32_LO)]; - node->p->normalRecv[0] |= node->d.p->p->memory.io[GBA_REG(SIODATA32_HI)] << 16; - break; - default: - node->p->multiRecv[0] = 0xFFFF; - break; - } - needsToWait = true; - ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTED); - node->nextEvent += LOCKSTEP_TRANSFER; - break; - case TRANSFER_STARTED: - // All the other GBAs have caught up and are sleeping, we can all continue now - node->nextEvent += LOCKSTEP_TRANSFER; - ATOMIC_STORE(node->p->d.transferActive, TRANSFER_FINISHING); - break; - case TRANSFER_FINISHING: - // Finish the transfer - // We need to make sure the other GBAs catch up so they don't get behind - node->nextEvent += node->p->d.transferCycles - 1024; // Split the cycles to avoid waiting too long -#ifndef NDEBUG - ATOMIC_ADD(node->p->d.transferId, 1); -#endif - needsToWait = true; - ATOMIC_STORE(node->p->d.transferActive, TRANSFER_FINISHED); - break; - case TRANSFER_FINISHED: - // Everything's settled. We're done. - _finishTransfer(node); - node->nextEvent += LOCKSTEP_INCREMENT; - ATOMIC_STORE(node->p->d.transferActive, TRANSFER_IDLE); - break; - } - int mask = 0; - for (i = 1; i < node->p->d.attached; ++i) { - if (node->p->players[i]->mode == node->mode) { - mask |= 1 << i; - } - } - if (mask) { - if (needsToWait) { - if (!node->p->d.wait(&node->p->d, mask)) { - abort(); - } - } else { - node->p->d.signal(&node->p->d, mask); - } - } - // Tell the other GBAs they can continue up to where we were - node->p->d.addCycles(&node->p->d, 0, node->eventDiff); -#ifndef NDEBUG - node->phase = node->p->d.transferActive; -#endif - - if (needsToWait) { - return 0; - } - return node->nextEvent; -} - -static uint32_t _slaveUpdate(struct GBASIOLockstepNode* node) { - enum mLockstepPhase transferActive; - int attached; - int attachedMode; - - ATOMIC_LOAD(transferActive, node->p->d.transferActive); - ATOMIC_LOAD(attached, node->p->d.attached); - - if (node->mode == GBA_SIO_MULTI) { - ATOMIC_LOAD(attachedMode, node->p->attachedMulti); - node->d.p->siocnt = GBASIOMultiplayerSetReady(node->d.p->siocnt, attachedMode == attached); - } else { - ATOMIC_LOAD(attachedMode, node->p->attachedNormal); - } - bool signal = false; - switch (transferActive) { - case TRANSFER_IDLE: - if (attachedMode != attached) { - node->p->d.addCycles(&node->p->d, node->id, LOCKSTEP_INCREMENT); - } - break; - case TRANSFER_STARTING: - case TRANSFER_FINISHING: - break; - case TRANSFER_STARTED: - if (node->p->d.unusedCycles(&node->p->d, node->id) > node->eventDiff) { - break; - } - node->transferFinished = false; - switch (node->mode) { - case GBA_SIO_MULTI: - node->d.p->rcnt &= ~1; - node->p->multiRecv[node->id] = node->d.p->p->memory.io[GBA_REG(SIOMLT_SEND)]; - node->d.p->p->memory.io[GBA_REG(SIOMULTI0)] = 0xFFFF; - node->d.p->p->memory.io[GBA_REG(SIOMULTI1)] = 0xFFFF; - node->d.p->p->memory.io[GBA_REG(SIOMULTI2)] = 0xFFFF; - node->d.p->p->memory.io[GBA_REG(SIOMULTI3)] = 0xFFFF; - node->d.p->siocnt = GBASIOMultiplayerFillBusy(node->d.p->siocnt); - break; - case GBA_SIO_NORMAL_8: - node->p->multiRecv[node->id] = 0xFFFF; - node->p->normalRecv[node->id] = node->d.p->p->memory.io[GBA_REG(SIODATA8)] & 0xFF; - break; - case GBA_SIO_NORMAL_32: - node->p->multiRecv[node->id] = 0xFFFF; - node->p->normalRecv[node->id] = node->d.p->p->memory.io[GBA_REG(SIODATA32_LO)]; - node->p->normalRecv[node->id] |= node->d.p->p->memory.io[GBA_REG(SIODATA32_HI)] << 16; - break; - default: - node->p->multiRecv[node->id] = 0xFFFF; - break; - } - signal = true; - break; - case TRANSFER_FINISHED: - if (node->p->d.unusedCycles(&node->p->d, node->id) > node->eventDiff) { - break; - } - _finishTransfer(node); - signal = true; - break; - } -#ifndef NDEBUG - node->phase = node->p->d.transferActive; -#endif - if (signal) { - node->p->d.signal(&node->p->d, 1 << node->id); - } - - return 0; -} - -static void _GBASIOLockstepNodeProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLate) { - struct GBASIOLockstepNode* node = user; - mLockstepLock(&node->p->d); - - int32_t cycles = node->nextEvent; - node->nextEvent -= cyclesLate; - node->eventDiff += cyclesLate; - if (node->p->d.attached < 2) { - switch (node->mode) { - case GBA_SIO_MULTI: - cycles = GBASIOTransferCycles(node->d.p->mode, node->d.p->siocnt, node->p->d.attached); - break; - case GBA_SIO_NORMAL_8: - case GBA_SIO_NORMAL_32: - if (node->nextEvent <= 0) { - cycles = _masterUpdate(node); - node->eventDiff = 0; - } - break; - default: - break; - } - } else if (node->nextEvent <= 0) { - if (!node->id) { - cycles = _masterUpdate(node); - } else { - cycles = _slaveUpdate(node); - cycles += node->p->d.useCycles(&node->p->d, node->id, node->eventDiff); - } - node->eventDiff = 0; - } - if (cycles > 0) { - node->nextEvent = 0; - node->eventDiff += cycles; - mTimingDeschedule(timing, &node->event); - mTimingSchedule(timing, &node->event, cycles); - } else { - node->d.p->p->earlyExit = true; - node->eventDiff += 1; - mTimingSchedule(timing, &node->event, 1); - } - - mLockstepUnlock(&node->p->d); -} - -static uint16_t GBASIOLockstepNodeNormalWriteSIOCNT(struct GBASIODriver* driver, uint16_t value) { - struct GBASIOLockstepNode* node = (struct GBASIOLockstepNode*) driver; - - mLockstepLock(&node->p->d); - - mLOG(GBA_SIO, DEBUG, "Lockstep %i: SIOCNT <- %04X", node->id, value); - int attached; - ATOMIC_LOAD(attached, node->p->attachedNormal); - value &= 0xFF8B; - if (node->id > 0) { - value = GBASIONormalSetSi(value, GBASIONormalGetIdleSo(node->p->players[node->id - 1]->d.p->siocnt)); - } else { - value = GBASIONormalClearSi(value); - } - - enum mLockstepPhase transferActive; - ATOMIC_LOAD(transferActive, node->p->d.transferActive); - if (node->id < 3 && attached > node->id + 1 && transferActive == TRANSFER_IDLE) { - int try; - for (try = 0; try < 3; ++try) { - GBASIONormal nextSiocnct; - ATOMIC_LOAD(nextSiocnct, node->p->players[node->id + 1]->d.p->siocnt); - if (ATOMIC_CMPXCHG(node->p->players[node->id + 1]->d.p->siocnt, nextSiocnct, GBASIONormalSetSi(nextSiocnct, GBASIONormalGetIdleSo(value)))) { - break; - } - } - } - if ((value & 0x0081) == 0x0081) { - if (!node->id) { - // Frequency - int32_t cycles = GBASIOTransferCycles(node->d.p->mode, node->d.p->siocnt, attached); - - if (transferActive == TRANSFER_IDLE) { - mLOG(GBA_SIO, DEBUG, "Lockstep %i: Transfer initiated", node->id); - ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING); - ATOMIC_STORE(node->p->d.transferCycles, cycles); - - if (mTimingIsScheduled(&driver->p->p->timing, &node->event)) { - node->eventDiff -= node->event.when - mTimingCurrentTime(&driver->p->p->timing); - mTimingDeschedule(&driver->p->p->timing, &node->event); - } - mTimingSchedule(&driver->p->p->timing, &node->event, 0); - } else { - value &= ~0x0080; - } - } else { - // TODO - } - } - - mLockstepUnlock(&node->p->d); - - return value; -} - #define TARGET(P) (1 << (P)) #define TARGET_ALL 0xF #define TARGET_PRIMARY 0x1 From 630e3a591a89b24a268be772a209de33d8a07824 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 7 Sep 2024 21:12:19 -0700 Subject: [PATCH 032/114] GBA SIO: Add support for side data in save states --- include/mgba/gba/interface.h | 3 +++ include/mgba/internal/gba/serialize.h | 1 + src/gba/core.c | 37 ++++++++++++++++++++++++++- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/include/mgba/gba/interface.h b/include/mgba/gba/interface.h index 3d52e7743..cba586632 100644 --- a/include/mgba/gba/interface.h +++ b/include/mgba/gba/interface.h @@ -113,6 +113,9 @@ struct GBASIODriver { void (*reset)(struct GBASIODriver* driver); bool (*load)(struct GBASIODriver* driver); bool (*unload)(struct GBASIODriver* driver); + uint32_t (*driverId)(const struct GBASIODriver* renderer); + bool (*loadState)(struct GBASIODriver* renderer, const void* state, size_t size); + void (*saveState)(struct GBASIODriver* renderer, void** state, size_t* size); void (*setMode)(struct GBASIODriver* driver, enum GBASIOMode mode); bool (*handlesMode)(struct GBASIODriver* driver, enum GBASIOMode mode); int (*connectedDevices)(struct GBASIODriver* driver); diff --git a/include/mgba/internal/gba/serialize.h b/include/mgba/internal/gba/serialize.h index c824e85aa..8b1823a17 100644 --- a/include/mgba/internal/gba/serialize.h +++ b/include/mgba/internal/gba/serialize.h @@ -287,6 +287,7 @@ DECL_BITS(GBASerializedMiscFlags, KeyIRQKeys, 4, 11); enum { GBA_SUBSYSTEM_VIDEO_RENDERER = 0, + GBA_SUBSYSTEM_SIO_DRIVER = 1, GBA_SUBSYSTEM_MAX, }; diff --git a/src/gba/core.c b/src/gba/core.c index a6939406e..00f5ccf97 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -842,7 +842,21 @@ static bool _GBACoreLoadExtraState(struct mCore* core, const struct mStateExtdat if (type == gba->video.renderer->rendererId(gba->video.renderer)) { ok = gba->video.renderer->loadState(gba->video.renderer, (void*) ((uintptr_t) item.data + sizeof(uint32_t)), - item.size - sizeof(type)); + item.size - sizeof(type)) && ok; + } + } else if (item.data) { + ok = false; + } + } + if (gba->sio.activeDriver && gba->sio.activeDriver->driverId && gba->sio.activeDriver->loadState && + mStateExtdataGet(extdata, EXTDATA_SUBSYSTEM_START + GBA_SUBSYSTEM_SIO_DRIVER, &item)) { + if ((uint32_t) item.size > sizeof(uint32_t)) { + uint32_t type; + LOAD_32(type, 0, item.data); + if (type == gba->sio.activeDriver->driverId(gba->sio.activeDriver)) { + ok = gba->sio.activeDriver->loadState(gba->sio.activeDriver, + (void*) ((uintptr_t) item.data + sizeof(uint32_t)), + item.size - sizeof(type)) && ok; } } else if (item.data) { ok = false; @@ -868,6 +882,27 @@ static bool _GBACoreSaveExtraState(struct mCore* core, struct mStateExtdata* ext } if (buffer) { free(buffer); + buffer = NULL; + } + size = 0; + + if (gba->sio.activeDriver && gba->sio.activeDriver->driverId && gba->sio.activeDriver->saveState) { + gba->sio.activeDriver->saveState(gba->sio.activeDriver, &buffer, &size); + if (size > 0 && buffer) { + struct mStateExtdataItem item; + item.size = size + sizeof(uint32_t); + item.data = malloc(item.size); + item.clean = free; + uint32_t type = gba->sio.activeDriver->driverId(gba->sio.activeDriver); + STORE_32(type, 0, item.data); + memcpy((void*) ((uintptr_t) item.data + sizeof(uint32_t)), buffer, size); + mStateExtdataPut(extdata, EXTDATA_SUBSYSTEM_START + GBA_SUBSYSTEM_SIO_DRIVER, &item); + } + if (buffer) { + free(buffer); + buffer = NULL; + } + size = 0; } return true; From f2bbf8e66c0dfcd1166939a5701a278f104199ec Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 8 Sep 2024 04:17:41 -0700 Subject: [PATCH 033/114] GBA SIO: Support save states in lockstep driver --- src/gba/sio/lockstep.c | 271 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 271 insertions(+) diff --git a/src/gba/sio/lockstep.c b/src/gba/sio/lockstep.c index 1493ca9f6..074b7d0e8 100644 --- a/src/gba/sio/lockstep.c +++ b/src/gba/sio/lockstep.c @@ -8,17 +8,82 @@ #include #include +#define DRIVER_ID 0x6B636F4C +#define DRIVER_STATE_VERSION 1 #define LOCKSTEP_INCREMENT 2000 #define TARGET(P) (1 << (P)) #define TARGET_ALL 0xF #define TARGET_PRIMARY 0x1 #define TARGET_SECONDARY ((TARGET_ALL) & ~(TARGET_PRIMARY)) +DECL_BITFIELD(GBASIOLockstepSerializedFlags, uint32_t); +DECL_BITS(GBASIOLockstepSerializedFlags, DriverMode, 0, 3); +DECL_BITS(GBASIOLockstepSerializedFlags, NumEvents, 3, 4); +DECL_BIT(GBASIOLockstepSerializedFlags, Asleep, 7); +DECL_BIT(GBASIOLockstepSerializedFlags, DataReceived, 8); +DECL_BIT(GBASIOLockstepSerializedFlags, EventScheduled, 9); +DECL_BITS(GBASIOLockstepSerializedFlags, Player0Mode, 10, 3); +DECL_BITS(GBASIOLockstepSerializedFlags, Player1Mode, 13, 3); +DECL_BITS(GBASIOLockstepSerializedFlags, Player2Mode, 16, 3); +DECL_BITS(GBASIOLockstepSerializedFlags, Player3Mode, 19, 3); +DECL_BITS(GBASIOLockstepSerializedFlags, TransferMode, 28, 3); +DECL_BIT(GBASIOLockstepSerializedFlags, TransferActive, 31); + +DECL_BITFIELD(GBASIOLockstepSerializedEventFlags, uint32_t); +DECL_BITS(GBASIOLockstepSerializedEventFlags, Type, 0, 3); + +struct GBASIOLockstepSerializedEvent { + int32_t timestamp; + int32_t playerId; + GBASIOLockstepSerializedEventFlags flags; + int32_t reserved[5]; + union { + int32_t mode; + int32_t finishCycle; + int32_t padding[4]; + }; +}; +static_assert(sizeof(struct GBASIOLockstepSerializedEvent) == 0x30, "GBA lockstep event savestate struct sized wrong"); + +struct GBASIOLockstepSerializedState { + uint32_t version; + GBASIOLockstepSerializedFlags flags; + uint32_t reserved[2]; + + struct { + int32_t nextEvent; + uint32_t reservedDriver[7]; + } driver; + + struct { + int32_t playerId; + int32_t cycleOffset; + uint32_t reservedPlayer[2]; + struct GBASIOLockstepSerializedEvent events[MAX_LOCKSTEP_EVENTS]; + } player; + + // playerId 0 only + struct { + int32_t cycle; + uint32_t waiting; + uint32_t reservedCoordinator[4]; + uint16_t multiData[4]; + uint32_t normalData[4]; + } coordinator; +}; +static_assert(offsetof(struct GBASIOLockstepSerializedState, driver) == 0x10, "GBA lockstep savestate driver offset wrong"); +static_assert(offsetof(struct GBASIOLockstepSerializedState, player) == 0x30, "GBA lockstep savestate player offset wrong"); +static_assert(offsetof(struct GBASIOLockstepSerializedState, coordinator) == 0x1C0, "GBA lockstep savestate coordinator offset wrong"); +static_assert(sizeof(struct GBASIOLockstepSerializedState) == 0x1F0, "GBA lockstep savestate struct sized wrong"); + static bool GBASIOLockstepDriverInit(struct GBASIODriver* driver); static void GBASIOLockstepDriverDeinit(struct GBASIODriver* driver); static void GBASIOLockstepDriverReset(struct GBASIODriver* driver); static bool GBASIOLockstepDriverLoad(struct GBASIODriver* driver); static bool GBASIOLockstepDriverUnload(struct GBASIODriver* driver); +static uint32_t GBASIOLockstepDriverId(const struct GBASIODriver* driver); +static bool GBASIOLockstepDriverLoadState(struct GBASIODriver* driver, const void* state, size_t size); +static void GBASIOLockstepDriverSaveState(struct GBASIODriver* driver, void** state, size_t* size); static void GBASIOLockstepDriverSetMode(struct GBASIODriver* driver, enum GBASIOMode mode); static bool GBASIOLockstepDriverHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode); static int GBASIOLockstepDriverConnectedDevices(struct GBASIODriver* driver); @@ -56,6 +121,9 @@ void GBASIOLockstepDriverCreate(struct GBASIOLockstepDriver* driver, struct mLoc driver->d.reset = GBASIOLockstepDriverReset; driver->d.load = GBASIOLockstepDriverLoad; driver->d.unload = GBASIOLockstepDriverUnload; + driver->d.driverId = GBASIOLockstepDriverId; + driver->d.loadState = GBASIOLockstepDriverLoadState; + driver->d.saveState = GBASIOLockstepDriverSaveState; driver->d.setMode = GBASIOLockstepDriverSetMode; driver->d.handlesMode = GBASIOLockstepDriverHandlesMode; driver->d.deviceId = GBASIOLockstepDriverDeviceId; @@ -173,6 +241,209 @@ static bool GBASIOLockstepDriverUnload(struct GBASIODriver* driver) { return true; } +static uint32_t GBASIOLockstepDriverId(const struct GBASIODriver* driver) { + UNUSED(driver); + return DRIVER_ID; +} + +static unsigned _modeEnumToInt(enum GBASIOMode mode) { + switch ((int) mode) { + case -1: + default: + return 0; + case GBA_SIO_MULTI: + return 1; + case GBA_SIO_NORMAL_8: + return 2; + case GBA_SIO_NORMAL_32: + return 3; + case GBA_SIO_GPIO: + return 4; + case GBA_SIO_UART: + return 5; + case GBA_SIO_JOYBUS: + return 6; + } +} + +static enum GBASIOMode _modeIntToEnum(unsigned mode) { + const enum GBASIOMode modes[8] = { + -1, GBA_SIO_MULTI, GBA_SIO_NORMAL_8, GBA_SIO_NORMAL_32, GBA_SIO_GPIO, GBA_SIO_UART, GBA_SIO_JOYBUS, -1 + }; + return modes[mode & 7]; +} + +static bool GBASIOLockstepDriverLoadState(struct GBASIODriver* driver, const void* data, size_t size) { + struct GBASIOLockstepDriver* lockstep = (struct GBASIOLockstepDriver*) driver; + struct GBASIOLockstepCoordinator* coordinator = lockstep->coordinator; + if (size != sizeof(struct GBASIOLockstepSerializedState)) { + mLOG(GBA_SIO, WARN, "Incorrect state size: expected %" PRIz "X, got %" PRIz "X", sizeof(struct GBASIOLockstepSerializedState), size); + return false; + } + const struct GBASIOLockstepSerializedState* state = data; + bool error = false; + uint32_t ucheck; + int32_t check; + LOAD_32LE(ucheck, 0, &state->version); + if (ucheck > DRIVER_STATE_VERSION) { + mLOG(GBA_SIO, WARN, "Invalid or too new save state: expected %u, got %u", DRIVER_STATE_VERSION, ucheck); + return false; + } + + MutexLock(&coordinator->mutex); + struct GBASIOLockstepPlayer* player = TableLookup(&coordinator->players, lockstep->lockstepId); + LOAD_32LE(check, 0, &state->player.playerId); + if (check != player->playerId) { + mLOG(GBA_SIO, WARN, "State is for different player: expected %d, got %d", player->playerId, check); + error = true; + goto out; + } + + GBASIOLockstepSerializedFlags flags = 0; + LOAD_32LE(flags, 0, &state->flags); + LOAD_32LE(player->cycleOffset, 0, &state->player.cycleOffset); + player->dataReceived = GBASIOLockstepSerializedFlagsGetDataReceived(flags); + player->mode = _modeIntToEnum(GBASIOLockstepSerializedFlagsGetDriverMode(flags)); + + player->otherModes[0] = _modeIntToEnum(GBASIOLockstepSerializedFlagsGetPlayer0Mode(flags)); + player->otherModes[1] = _modeIntToEnum(GBASIOLockstepSerializedFlagsGetPlayer1Mode(flags)); + player->otherModes[2] = _modeIntToEnum(GBASIOLockstepSerializedFlagsGetPlayer2Mode(flags)); + player->otherModes[3] = _modeIntToEnum(GBASIOLockstepSerializedFlagsGetPlayer3Mode(flags)); + + if (GBASIOLockstepSerializedFlagsGetEventScheduled(flags)) { + int32_t when; + LOAD_32LE(when, 0, &state->driver.nextEvent); + mTimingSchedule(&driver->p->p->timing, &lockstep->event, when); + } + + if (GBASIOLockstepSerializedFlagsGetAsleep(flags)) { + if (!player->asleep && player->driver->user->sleep) { + player->driver->user->sleep(player->driver->user); + } + player->asleep = true; + } else { + if (player->asleep && player->driver->user->wake) { + player->driver->user->wake(player->driver->user); + } + player->asleep = false; + } + + unsigned i; + for (i = 0; i < MAX_LOCKSTEP_EVENTS - 1; ++i) { + player->buffer[i].next = &player->buffer[i + 1]; + } + player->freeList = &player->buffer[0]; + player->queue = NULL; + + struct GBASIOLockstepEvent** lastEvent = &player->queue; + for (i = 0; i < GBASIOLockstepSerializedFlagsGetNumEvents(flags) && i < MAX_LOCKSTEP_EVENTS; ++i) { + struct GBASIOLockstepEvent* event = player->freeList; + const struct GBASIOLockstepSerializedEvent* stateEvent = &state->player.events[i]; + player->freeList = player->freeList->next; + *lastEvent = event; + lastEvent = &event->next; + + GBASIOLockstepSerializedEventFlags flags; + LOAD_32LE(flags, 0, &stateEvent->flags); + LOAD_32LE(event->timestamp, 0, &stateEvent->timestamp); + LOAD_32LE(event->playerId, 0, &stateEvent->playerId); + event->type = GBASIOLockstepSerializedEventFlagsGetType(flags); + switch (event->type) { + case SIO_EV_ATTACH: + case SIO_EV_DETACH: + case SIO_EV_HARD_SYNC: + break; + case SIO_EV_MODE_SET: + LOAD_32LE(event->mode, 0, &stateEvent->mode); + break; + case SIO_EV_TRANSFER_START: + LOAD_32LE(event->finishCycle, 0, &stateEvent->finishCycle); + break; + } + } + + if (player->playerId == 0) { + LOAD_32LE(coordinator->cycle, 0, &state->coordinator.cycle); + LOAD_32LE(coordinator->waiting, 0, &state->coordinator.waiting); + for (i = 0; i < 4; ++i) { + LOAD_16LE(coordinator->multiData[i], 0, &state->coordinator.multiData[i]); + LOAD_32LE(coordinator->normalData[i], 0, &state->coordinator.normalData[i]); + } + coordinator->transferMode = _modeIntToEnum(GBASIOLockstepSerializedFlagsGetTransferMode(flags)); + coordinator->transferActive = GBASIOLockstepSerializedFlagsGetTransferActive(flags); + } +out: + MutexUnlock(&coordinator->mutex); + if (!error) { + mTimingInterrupt(&driver->p->p->timing); + } + return !error; +} + +static void GBASIOLockstepDriverSaveState(struct GBASIODriver* driver, void** stateOut, size_t* size) { + struct GBASIOLockstepDriver* lockstep = (struct GBASIOLockstepDriver*) driver; + struct GBASIOLockstepCoordinator* coordinator = lockstep->coordinator; + struct GBASIOLockstepSerializedState* state = calloc(1, sizeof(*state)); + + STORE_32LE(DRIVER_STATE_VERSION, 0, &state->version); + + STORE_32LE(lockstep->event.when - mTimingCurrentTime(&driver->p->p->timing), 0, &state->driver.nextEvent); + + MutexLock(&coordinator->mutex); + struct GBASIOLockstepPlayer* player = TableLookup(&coordinator->players, lockstep->lockstepId); + GBASIOLockstepSerializedFlags flags = 0; + STORE_32LE(player->playerId, 0, &state->player.playerId); + STORE_32LE(player->cycleOffset, 0, &state->player.cycleOffset); + flags = GBASIOLockstepSerializedFlagsSetAsleep(flags, player->asleep); + flags = GBASIOLockstepSerializedFlagsSetDataReceived(flags, player->dataReceived); + flags = GBASIOLockstepSerializedFlagsSetDriverMode(flags, _modeEnumToInt(player->mode)); + flags = GBASIOLockstepSerializedFlagsSetEventScheduled(flags, mTimingIsScheduled(&driver->p->p->timing, &lockstep->event)); + + flags = GBASIOLockstepSerializedFlagsSetPlayer0Mode(flags, _modeEnumToInt(player->otherModes[0])); + flags = GBASIOLockstepSerializedFlagsSetPlayer1Mode(flags, _modeEnumToInt(player->otherModes[1])); + flags = GBASIOLockstepSerializedFlagsSetPlayer2Mode(flags, _modeEnumToInt(player->otherModes[2])); + flags = GBASIOLockstepSerializedFlagsSetPlayer3Mode(flags, _modeEnumToInt(player->otherModes[3])); + + struct GBASIOLockstepEvent* event = player->queue; + size_t i; + for (i = 0; i < MAX_LOCKSTEP_EVENTS && event; ++i, event = event->next) { + struct GBASIOLockstepSerializedEvent* stateEvent = &state->player.events[i]; + GBASIOLockstepSerializedEventFlags flags = GBASIOLockstepSerializedEventFlagsSetType(0, event->type); + STORE_32LE(event->timestamp, 0, &stateEvent->timestamp); + STORE_32LE(event->playerId, 0, &stateEvent->playerId); + switch (event->type) { + case SIO_EV_ATTACH: + case SIO_EV_DETACH: + case SIO_EV_HARD_SYNC: + break; + case SIO_EV_MODE_SET: + STORE_32LE(event->mode, 0, &stateEvent->mode); + break; + case SIO_EV_TRANSFER_START: + STORE_32LE(event->finishCycle, 0, &stateEvent->finishCycle); + break; + } + STORE_32LE(flags, 0, &stateEvent->flags); + } + flags = GBASIOLockstepSerializedFlagsSetNumEvents(flags, i); + + if (player->playerId == 0) { + STORE_32LE(coordinator->cycle, 0, &state->coordinator.cycle); + STORE_32LE(coordinator->waiting, 0, &state->coordinator.waiting); + for (i = 0; i < 4; ++i) { + STORE_16LE(coordinator->multiData[i], 0, &state->coordinator.multiData[i]); + STORE_32LE(coordinator->normalData[i], 0, &state->coordinator.normalData[i]); + } + flags = GBASIOLockstepSerializedFlagsSetTransferMode(flags, _modeEnumToInt(coordinator->transferMode)); + flags = GBASIOLockstepSerializedFlagsSetTransferActive(flags, coordinator->transferActive); + } + MutexUnlock(&lockstep->coordinator->mutex); + + STORE_32LE(flags, 0, &state->flags); + *stateOut = state; + *size = sizeof(*state); +} + static void GBASIOLockstepDriverSetMode(struct GBASIODriver* driver, enum GBASIOMode mode) { struct GBASIOLockstepDriver* lockstep = (struct GBASIOLockstepDriver*) driver; struct GBASIOLockstepCoordinator* coordinator = lockstep->coordinator; From 0823797671d678206f4bf1cf042a15e1299782d4 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 10 Sep 2024 03:19:02 -0700 Subject: [PATCH 034/114] GBA SIO: Remove driver sets and driver load/unload concepts --- include/mgba/gba/interface.h | 2 - include/mgba/internal/gba/sio.h | 12 +- src/gba/cart/gpio.c | 2 +- src/gba/core.c | 19 ++- src/gba/extra/battlechip.c | 6 +- src/gba/gba.c | 4 +- src/gba/sio.c | 157 ++++++---------------- src/gba/sio/dolphin.c | 21 ++- src/gba/sio/gbp.c | 6 +- src/gba/sio/lockstep.c | 25 ---- src/platform/qt/CoreController.cpp | 5 +- src/platform/qt/MultiplayerController.cpp | 17 +-- 12 files changed, 77 insertions(+), 199 deletions(-) diff --git a/include/mgba/gba/interface.h b/include/mgba/gba/interface.h index cba586632..9b8f6e001 100644 --- a/include/mgba/gba/interface.h +++ b/include/mgba/gba/interface.h @@ -111,8 +111,6 @@ struct GBASIODriver { bool (*init)(struct GBASIODriver* driver); void (*deinit)(struct GBASIODriver* driver); void (*reset)(struct GBASIODriver* driver); - bool (*load)(struct GBASIODriver* driver); - bool (*unload)(struct GBASIODriver* driver); uint32_t (*driverId)(const struct GBASIODriver* renderer); bool (*loadState)(struct GBASIODriver* renderer, const void* state, size_t size); void (*saveState)(struct GBASIODriver* renderer, void** state, size_t* size); diff --git a/include/mgba/internal/gba/sio.h b/include/mgba/internal/gba/sio.h index ca271abc4..14a2136b8 100644 --- a/include/mgba/internal/gba/sio.h +++ b/include/mgba/internal/gba/sio.h @@ -62,18 +62,11 @@ DECL_BIT(GBASIORegisterRCNT, SdDirection, 5); DECL_BIT(GBASIORegisterRCNT, SiDirection, 6); DECL_BIT(GBASIORegisterRCNT, SoDirection, 7); -struct GBASIODriverSet { - struct GBASIODriver* normal; - struct GBASIODriver* multiplayer; - struct GBASIODriver* joybus; -}; - struct GBASIO { struct GBA* p; enum GBASIOMode mode; - struct GBASIODriverSet drivers; - struct GBASIODriver* activeDriver; + struct GBASIODriver* driver; uint16_t rcnt; uint16_t siocnt; @@ -86,8 +79,7 @@ void GBASIOInit(struct GBASIO* sio); void GBASIODeinit(struct GBASIO* sio); void GBASIOReset(struct GBASIO* sio); -void GBASIOSetDriverSet(struct GBASIO* sio, struct GBASIODriverSet* drivers); -void GBASIOSetDriver(struct GBASIO* sio, struct GBASIODriver* driver, enum GBASIOMode mode); +void GBASIOSetDriver(struct GBASIO* sio, struct GBASIODriver* driver); void GBASIOWriteRCNT(struct GBASIO* sio, uint16_t value); void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value); diff --git a/src/gba/cart/gpio.c b/src/gba/cart/gpio.c index a04a5f6b3..3386ac3c6 100644 --- a/src/gba/cart/gpio.c +++ b/src/gba/cart/gpio.c @@ -527,7 +527,7 @@ void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, const struct GBASer uint32_t when; LOAD_32(when, 0, &state->hw.sioNextEvent); if (hw->devices & HW_GB_PLAYER) { - GBASIOSetDriver(&hw->p->sio, &hw->p->sio.gbp.d, GBA_SIO_NORMAL_32); + GBASIOSetDriver(&hw->p->sio, &hw->p->sio.gbp.d); } if ((hw->p->memory.io[GBA_REG(SIOCNT)] & 0x0080) && when < 0x20000) { mTimingSchedule(&hw->p->timing, &hw->p->sio.completeEvent, when); diff --git a/src/gba/core.c b/src/gba/core.c index 00f5ccf97..898745e9e 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -848,15 +848,15 @@ static bool _GBACoreLoadExtraState(struct mCore* core, const struct mStateExtdat ok = false; } } - if (gba->sio.activeDriver && gba->sio.activeDriver->driverId && gba->sio.activeDriver->loadState && + if (gba->sio.driver && gba->sio.driver->driverId && gba->sio.driver->loadState && mStateExtdataGet(extdata, EXTDATA_SUBSYSTEM_START + GBA_SUBSYSTEM_SIO_DRIVER, &item)) { if ((uint32_t) item.size > sizeof(uint32_t)) { uint32_t type; LOAD_32(type, 0, item.data); - if (type == gba->sio.activeDriver->driverId(gba->sio.activeDriver)) { - ok = gba->sio.activeDriver->loadState(gba->sio.activeDriver, - (void*) ((uintptr_t) item.data + sizeof(uint32_t)), - item.size - sizeof(type)) && ok; + if (type == gba->sio.driver->driverId(gba->sio.driver)) { + ok = gba->sio.driver->loadState(gba->sio.driver, + (void*) ((uintptr_t) item.data + sizeof(uint32_t)), + item.size - sizeof(type)) && ok; } } else if (item.data) { ok = false; @@ -886,14 +886,14 @@ static bool _GBACoreSaveExtraState(struct mCore* core, struct mStateExtdata* ext } size = 0; - if (gba->sio.activeDriver && gba->sio.activeDriver->driverId && gba->sio.activeDriver->saveState) { - gba->sio.activeDriver->saveState(gba->sio.activeDriver, &buffer, &size); + if (gba->sio.driver && gba->sio.driver->driverId && gba->sio.driver->saveState) { + gba->sio.driver->saveState(gba->sio.driver, &buffer, &size); if (size > 0 && buffer) { struct mStateExtdataItem item; item.size = size + sizeof(uint32_t); item.data = malloc(item.size); item.clean = free; - uint32_t type = gba->sio.activeDriver->driverId(gba->sio.activeDriver); + uint32_t type = gba->sio.driver->driverId(gba->sio.driver); STORE_32(type, 0, item.data); memcpy((void*) ((uintptr_t) item.data + sizeof(uint32_t)), buffer, size); mStateExtdataPut(extdata, EXTDATA_SUBSYSTEM_START + GBA_SUBSYSTEM_SIO_DRIVER, &item); @@ -963,8 +963,7 @@ static void _GBACoreSetPeripheral(struct mCore* core, int type, void* periph) { gba->luminanceSource = periph; break; case mPERIPH_GBA_BATTLECHIP_GATE: - GBASIOSetDriver(&gba->sio, periph, GBA_SIO_MULTI); - GBASIOSetDriver(&gba->sio, periph, GBA_SIO_NORMAL_32); + GBASIOSetDriver(&gba->sio, periph); break; default: return; diff --git a/src/gba/extra/battlechip.c b/src/gba/extra/battlechip.c index a6eda3bdf..d7bf35cce 100644 --- a/src/gba/extra/battlechip.c +++ b/src/gba/extra/battlechip.c @@ -33,7 +33,7 @@ enum { BATTLECHIP_CONTINUE = 0xFFFF, }; -static bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver); +static bool GBASIOBattlechipGateInit(struct GBASIODriver* driver); static uint16_t GBASIOBattlechipGateWriteSIOCNT(struct GBASIODriver* driver, uint16_t value); static bool GBASIOBattlechipGateHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode); static int GBASIOBattlechipGateConnectedDevices(struct GBASIODriver* driver); @@ -41,7 +41,7 @@ static void GBASIOBattlechipGateFinishMultiplayer(struct GBASIODriver* driver, u void GBASIOBattlechipGateCreate(struct GBASIOBattlechipGate* gate) { memset(&gate->d, 0, sizeof(gate->d)); - gate->d.load = GBASIOBattlechipGateLoad; + gate->d.init = GBASIOBattlechipGateInit; gate->d.writeSIOCNT = GBASIOBattlechipGateWriteSIOCNT; gate->d.handlesMode = GBASIOBattlechipGateHandlesMode; gate->d.connectedDevices = GBASIOBattlechipGateConnectedDevices; @@ -51,7 +51,7 @@ void GBASIOBattlechipGateCreate(struct GBASIOBattlechipGate* gate) { gate->flavor = GBA_FLAVOR_BATTLECHIP_GATE; } -bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver) { +bool GBASIOBattlechipGateInit(struct GBASIODriver* driver) { struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver; gate->state = BATTLECHIP_STATE_SYNC; gate->data[0] = 0x00FE; diff --git a/src/gba/gba.c b/src/gba/gba.c index aab2cd3ec..187d83373 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -263,8 +263,8 @@ void GBAReset(struct ARMCore* cpu) { // GB Player SIO control should not be engaged before detection, even if we already know it's GBP gba->memory.hw.devices &= ~HW_GB_PLAYER; - if (gba->sio.drivers.normal == &gba->sio.gbp.d) { - GBASIOSetDriver(&gba->sio, NULL, GBA_SIO_NORMAL_32); + if (gba->sio.driver == &gba->sio.gbp.d) { + GBASIOSetDriver(&gba->sio, NULL); } bool isELF = false; diff --git a/src/gba/sio.c b/src/gba/sio.c index 4b635db18..60c7c2286 100644 --- a/src/gba/sio.c +++ b/src/gba/sio.c @@ -20,20 +20,6 @@ static const int GBASIOCyclesPerTransfer[4][MAX_GBAS] = { static void _sioFinish(struct mTiming* timing, void* user, uint32_t cyclesLate); -static struct GBASIODriver* _lookupDriver(struct GBASIO* sio, enum GBASIOMode mode) { - switch (mode) { - case GBA_SIO_NORMAL_8: - case GBA_SIO_NORMAL_32: - return sio->drivers.normal; - case GBA_SIO_MULTI: - return sio->drivers.multiplayer; - case GBA_SIO_JOYBUS: - return sio->drivers.joybus; - default: - return 0; - } -} - static const char* _modeName(enum GBASIOMode mode) { switch (mode) { case GBA_SIO_NORMAL_8: @@ -60,31 +46,19 @@ static void _switchMode(struct GBASIO* sio) { newMode = (enum GBASIOMode) (mode & 0xC); } if (newMode != sio->mode) { - struct GBASIODriver* driver = _lookupDriver(sio, newMode); if (sio->mode != (enum GBASIOMode) -1) { mLOG(GBA_SIO, DEBUG, "Switching mode from %s to %s", _modeName(sio->mode), _modeName(newMode)); } - if (driver != sio->activeDriver || (driver && !driver->setMode)) { - if (sio->activeDriver && sio->activeDriver->unload) { - sio->activeDriver->unload(sio->activeDriver); - } - sio->mode = newMode; - sio->activeDriver = driver; - if (sio->activeDriver && sio->activeDriver->load) { - sio->activeDriver->load(sio->activeDriver); - } - } else { - sio->mode = newMode; - if (sio->activeDriver && sio->activeDriver->setMode) { - sio->activeDriver->setMode(sio->activeDriver, newMode); - } + sio->mode = newMode; + if (sio->driver && sio->driver->setMode) { + sio->driver->setMode(sio->driver, newMode); } int id = 0; switch (newMode) { case GBA_SIO_MULTI: - if (sio->activeDriver && sio->activeDriver->deviceId) { - id = sio->activeDriver->deviceId(sio->activeDriver); + if (sio->driver && sio->driver->deviceId) { + id = sio->driver->deviceId(sio->driver); } sio->rcnt = GBASIORegisterRCNTSetSi(sio->rcnt, !!id); break; @@ -96,10 +70,7 @@ static void _switchMode(struct GBASIO* sio) { } void GBASIOInit(struct GBASIO* sio) { - sio->drivers.normal = NULL; - sio->drivers.multiplayer = NULL; - sio->drivers.joybus = NULL; - sio->activeDriver = NULL; + sio->driver = NULL; sio->completeEvent.context = sio; sio->completeEvent.name = "GBA SIO Complete"; @@ -113,73 +84,28 @@ void GBASIOInit(struct GBASIO* sio) { } void GBASIODeinit(struct GBASIO* sio) { - if (sio->activeDriver && sio->activeDriver->unload) { - sio->activeDriver->unload(sio->activeDriver); - } - if (sio->drivers.multiplayer && sio->drivers.multiplayer->deinit) { - sio->drivers.multiplayer->deinit(sio->drivers.multiplayer); - } - if (sio->drivers.joybus && sio->drivers.joybus->deinit) { - sio->drivers.joybus->deinit(sio->drivers.joybus); - } - if (sio->drivers.normal && sio->drivers.normal->deinit) { - sio->drivers.normal->deinit(sio->drivers.normal); + if (sio->driver && sio->driver->deinit) { + sio->driver->deinit(sio->driver); } } void GBASIOReset(struct GBASIO* sio) { - if (sio->activeDriver && sio->activeDriver->unload) { - sio->activeDriver->unload(sio->activeDriver); - } - if (sio->drivers.multiplayer && sio->drivers.multiplayer->reset) { - sio->drivers.multiplayer->reset(sio->drivers.multiplayer); - } - if (sio->drivers.joybus && sio->drivers.joybus->reset) { - sio->drivers.joybus->reset(sio->drivers.joybus); - } - if (sio->drivers.normal && sio->drivers.normal->reset) { - sio->drivers.normal->reset(sio->drivers.normal); + if (sio->driver && sio->driver->reset) { + sio->driver->reset(sio->driver); } sio->rcnt = RCNT_INITIAL; sio->siocnt = 0; sio->mode = -1; - sio->activeDriver = NULL; _switchMode(sio); GBASIOPlayerReset(&sio->gbp); } -void GBASIOSetDriverSet(struct GBASIO* sio, struct GBASIODriverSet* drivers) { - GBASIOSetDriver(sio, drivers->normal, GBA_SIO_NORMAL_8); - GBASIOSetDriver(sio, drivers->multiplayer, GBA_SIO_MULTI); - GBASIOSetDriver(sio, drivers->joybus, GBA_SIO_JOYBUS); -} - -void GBASIOSetDriver(struct GBASIO* sio, struct GBASIODriver* driver, enum GBASIOMode mode) { - struct GBASIODriver** driverLoc; - switch (mode) { - case GBA_SIO_NORMAL_8: - case GBA_SIO_NORMAL_32: - driverLoc = &sio->drivers.normal; - break; - case GBA_SIO_MULTI: - driverLoc = &sio->drivers.multiplayer; - break; - case GBA_SIO_JOYBUS: - driverLoc = &sio->drivers.joybus; - break; - default: - mLOG(GBA_SIO, ERROR, "Setting an unsupported SIO driver: %x", mode); - return; - } - if (*driverLoc) { - if ((*driverLoc)->unload) { - (*driverLoc)->unload(*driverLoc); - } - if ((*driverLoc)->deinit) { - (*driverLoc)->deinit(*driverLoc); - } +void GBASIOSetDriver(struct GBASIO* sio, struct GBASIODriver* driver) { + if (sio->driver && sio->driver->deinit) { + sio->driver->deinit(sio->driver); } + sio->driver = driver; if (driver) { driver->p = sio; @@ -191,26 +117,19 @@ void GBASIOSetDriver(struct GBASIO* sio, struct GBASIODriver* driver, enum GBASI } } } - if (sio->activeDriver == *driverLoc) { - sio->activeDriver = driver; - if (driver && driver->load) { - driver->load(driver); - } - } - *driverLoc = driver; } void GBASIOWriteRCNT(struct GBASIO* sio, uint16_t value) { sio->rcnt &= 0x1FF; sio->rcnt |= value & 0xC000; _switchMode(sio); - if (sio->activeDriver && sio->activeDriver->writeRCNT) { + if (sio->driver && sio->driver->writeRCNT) { switch (sio->mode) { case GBA_SIO_GPIO: - sio->rcnt = (sio->activeDriver->writeRCNT(sio->activeDriver, value) & 0x01FF) | (sio->rcnt & 0xC000); + sio->rcnt = (sio->driver->writeRCNT(sio->driver, value) & 0x01FF) | (sio->rcnt & 0xC000); break; default: - sio->rcnt = (sio->activeDriver->writeRCNT(sio->activeDriver, value) & 0x01F0) | (sio->rcnt & 0xC00F); + sio->rcnt = (sio->driver->writeRCNT(sio->driver, value) & 0x01F0) | (sio->rcnt & 0xC00F); } } else if (sio->mode == GBA_SIO_GPIO) { sio->rcnt &= 0xC000; @@ -222,15 +141,15 @@ void GBASIOWriteRCNT(struct GBASIO* sio, uint16_t value) { } static void _startTransfer(struct GBASIO* sio) { - if (sio->activeDriver && sio->activeDriver->start) { - if (!sio->activeDriver->start(sio->activeDriver)) { + if (sio->driver && sio->driver->start) { + if (!sio->driver->start(sio->driver)) { // Transfer completion is handled internally to the driver return; } } int connected = 0; - if (sio->activeDriver && sio->activeDriver->connectedDevices) { - connected = sio->activeDriver->connectedDevices(sio->activeDriver); + if (sio->driver && sio->driver->connectedDevices) { + connected = sio->driver->connectedDevices(sio->driver); } mTimingDeschedule(&sio->p->timing, &sio->completeEvent); mTimingSchedule(&sio->p->timing, &sio->completeEvent, GBASIOTransferCycles(sio->mode, sio->siocnt, connected)); @@ -244,14 +163,14 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) { int id = 0; int connected = 0; bool handled = false; - if (sio->activeDriver) { - handled = sio->activeDriver->handlesMode(sio->activeDriver, sio->mode); + if (sio->driver) { + handled = sio->driver->handlesMode(sio->driver, sio->mode); if (handled) { - if (sio->activeDriver->deviceId) { - id = sio->activeDriver->deviceId(sio->activeDriver); + if (sio->driver->deviceId) { + id = sio->driver->deviceId(sio->driver); } - connected = sio->activeDriver->connectedDevices(sio->activeDriver); - handled = !!sio->activeDriver->writeSIOCNT; + connected = sio->driver->connectedDevices(sio->driver); + handled = !!sio->driver->writeSIOCNT; } } @@ -304,7 +223,7 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) { break; } if (handled) { - value = sio->activeDriver->writeSIOCNT(sio->activeDriver, value); + value = sio->driver->writeSIOCNT(sio->driver, value); } else { // Dummy drivers switch (sio->mode) { @@ -325,8 +244,8 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) { uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t value) { int id = 0; - if (sio->activeDriver && sio->activeDriver->deviceId) { - id = sio->activeDriver->deviceId(sio->activeDriver); + if (sio->driver && sio->driver->deviceId) { + id = sio->driver->deviceId(sio->driver); } bool handled = true; @@ -455,8 +374,8 @@ int32_t GBASIOTransferCycles(enum GBASIOMode mode, uint16_t siocnt, int connecte void GBASIOMultiplayerFinishTransfer(struct GBASIO* sio, uint16_t data[4], uint32_t cyclesLate) { int id = 0; - if (sio->activeDriver && sio->activeDriver->deviceId) { - id = sio->activeDriver->deviceId(sio->activeDriver); + if (sio->driver && sio->driver->deviceId) { + id = sio->driver->deviceId(sio->driver); } sio->p->memory.io[GBA_REG(SIOMULTI0)] = data[0]; sio->p->memory.io[GBA_REG(SIOMULTI1)] = data[1]; @@ -500,20 +419,20 @@ static void _sioFinish(struct mTiming* timing, void* user, uint32_t cyclesLate) } data = {0}; switch (sio->mode) { case GBA_SIO_MULTI: - if (sio->activeDriver && sio->activeDriver->finishMultiplayer) { - sio->activeDriver->finishMultiplayer(sio->activeDriver, data.multi); + if (sio->driver && sio->driver->finishMultiplayer) { + sio->driver->finishMultiplayer(sio->driver, data.multi); } GBASIOMultiplayerFinishTransfer(sio, data.multi, cyclesLate); break; case GBA_SIO_NORMAL_8: - if (sio->activeDriver && sio->activeDriver->finishNormal8) { - data.normal8 = sio->activeDriver->finishNormal8(sio->activeDriver); + if (sio->driver && sio->driver->finishNormal8) { + data.normal8 = sio->driver->finishNormal8(sio->driver); } GBASIONormal8FinishTransfer(sio, data.normal8, cyclesLate); break; case GBA_SIO_NORMAL_32: - if (sio->activeDriver && sio->activeDriver->finishNormal32) { - data.normal32 = sio->activeDriver->finishNormal32(sio->activeDriver); + if (sio->driver && sio->driver->finishNormal32) { + data.normal32 = sio->driver->finishNormal32(sio->driver); } GBASIONormal32FinishTransfer(sio, data.normal32, cyclesLate); break; diff --git a/src/gba/sio/dolphin.c b/src/gba/sio/dolphin.c index e36dfb8a5..0bb783e76 100644 --- a/src/gba/sio/dolphin.c +++ b/src/gba/sio/dolphin.c @@ -23,8 +23,8 @@ enum { }; static bool GBASIODolphinInit(struct GBASIODriver* driver); -static bool GBASIODolphinLoad(struct GBASIODriver* driver); -static bool GBASIODolphinUnload(struct GBASIODriver* driver); +static void GBASIODolphinReset(struct GBASIODriver* driver); +static void GBASIODolphinSetMode(struct GBASIODriver* driver, enum GBASIOMode mode); static bool GBASIODolphinHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode); static int GBASIODolphinConnectedDevices(struct GBASIODriver* driver); static void GBASIODolphinProcessEvents(struct mTiming* timing, void* context, uint32_t cyclesLate); @@ -35,8 +35,8 @@ static void _flush(struct GBASIODolphin* dol); void GBASIODolphinCreate(struct GBASIODolphin* dol) { memset(&dol->d, 0, sizeof(dol->d)); dol->d.init = GBASIODolphinInit; - dol->d.load = GBASIODolphinLoad; - dol->d.unload = GBASIODolphinUnload; + dol->d.reset = GBASIODolphinReset; + dol->d.setMode = GBASIODolphinSetMode; dol->d.handlesMode = GBASIODolphinHandlesMode; dol->d.connectedDevices = GBASIODolphinConnectedDevices; dol->event.context = dol; @@ -98,26 +98,23 @@ bool GBASIODolphinConnect(struct GBASIODolphin* dol, const struct Address* addre static bool GBASIODolphinInit(struct GBASIODriver* driver) { struct GBASIODolphin* dol = (struct GBASIODolphin*) driver; - dol->active = false; dol->clockSlice = 0; dol->state = WAIT_FOR_FIRST_CLOCK; - _flush(dol); + GBASIODolphinReset(driver); return true; } -static bool GBASIODolphinLoad(struct GBASIODriver* driver) { +static void GBASIODolphinReset(struct GBASIODriver* driver) { struct GBASIODolphin* dol = (struct GBASIODolphin*) driver; - dol->active = true; + dol->active = false; _flush(dol); mTimingDeschedule(&dol->d.p->p->timing, &dol->event); mTimingSchedule(&dol->d.p->p->timing, &dol->event, 0); - return true; } -static bool GBASIODolphinUnload(struct GBASIODriver* driver) { +static void GBASIODolphinSetMode(struct GBASIODriver* driver, enum GBASIOMode mode) { struct GBASIODolphin* dol = (struct GBASIODolphin*) driver; - dol->active = false; - return true; + dol->active = mode == GBA_SIO_JOYBUS; } static bool GBASIODolphinHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode) { diff --git a/src/gba/sio/gbp.c b/src/gba/sio/gbp.c index 3335905a1..8edd92388 100644 --- a/src/gba/sio/gbp.c +++ b/src/gba/sio/gbp.c @@ -55,8 +55,8 @@ void GBASIOPlayerInit(struct GBASIOPlayer* gbp) { } void GBASIOPlayerReset(struct GBASIOPlayer* gbp) { - if (gbp->p->sio.drivers.normal == &gbp->d) { - GBASIOSetDriver(&gbp->p->sio, NULL, GBA_SIO_NORMAL_32); + if (gbp->p->sio.driver == &gbp->d) { + GBASIOSetDriver(&gbp->p->sio, NULL); } } @@ -88,7 +88,7 @@ void GBASIOPlayerUpdate(struct GBA* gba) { gba->sio.gbp.oldCallback = gba->keyCallback; gba->keyCallback = &gba->sio.gbp.callback.d; // TODO: Check if the SIO driver is actually used first - GBASIOSetDriver(&gba->sio, &gba->sio.gbp.d, GBA_SIO_NORMAL_32); + GBASIOSetDriver(&gba->sio, &gba->sio.gbp.d); } } diff --git a/src/gba/sio/lockstep.c b/src/gba/sio/lockstep.c index 074b7d0e8..f71f9b323 100644 --- a/src/gba/sio/lockstep.c +++ b/src/gba/sio/lockstep.c @@ -79,8 +79,6 @@ static_assert(sizeof(struct GBASIOLockstepSerializedState) == 0x1F0, "GBA lockst static bool GBASIOLockstepDriverInit(struct GBASIODriver* driver); static void GBASIOLockstepDriverDeinit(struct GBASIODriver* driver); static void GBASIOLockstepDriverReset(struct GBASIODriver* driver); -static bool GBASIOLockstepDriverLoad(struct GBASIODriver* driver); -static bool GBASIOLockstepDriverUnload(struct GBASIODriver* driver); static uint32_t GBASIOLockstepDriverId(const struct GBASIODriver* driver); static bool GBASIOLockstepDriverLoadState(struct GBASIODriver* driver, const void* state, size_t size); static void GBASIOLockstepDriverSaveState(struct GBASIODriver* driver, void** state, size_t* size); @@ -119,8 +117,6 @@ void GBASIOLockstepDriverCreate(struct GBASIOLockstepDriver* driver, struct mLoc driver->d.init = GBASIOLockstepDriverInit; driver->d.deinit = GBASIOLockstepDriverDeinit; driver->d.reset = GBASIOLockstepDriverReset; - driver->d.load = GBASIOLockstepDriverLoad; - driver->d.unload = GBASIOLockstepDriverUnload; driver->d.driverId = GBASIOLockstepDriverId; driver->d.loadState = GBASIOLockstepDriverLoadState; driver->d.saveState = GBASIOLockstepDriverSaveState; @@ -220,27 +216,6 @@ static void GBASIOLockstepDriverReset(struct GBASIODriver* driver) { mTimingSchedule(&lockstep->d.p->p->timing, &lockstep->event, nextEvent); } -static bool GBASIOLockstepDriverLoad(struct GBASIODriver* driver) { - struct GBASIOLockstepDriver* lockstep = (struct GBASIOLockstepDriver*) driver; - if (lockstep->lockstepId) { - struct GBASIOLockstepCoordinator* coordinator = lockstep->coordinator; - MutexLock(&coordinator->mutex); - struct GBASIOLockstepPlayer* player = TableLookup(&coordinator->players, lockstep->lockstepId); - _setReady(coordinator, player, 0, coordinator->transferMode); - MutexUnlock(&coordinator->mutex); - GBASIOLockstepDriverSetMode(driver, driver->p->mode); - } - return true; -} - -static bool GBASIOLockstepDriverUnload(struct GBASIODriver* driver) { - struct GBASIOLockstepDriver* lockstep = (struct GBASIOLockstepDriver*) driver; - if (lockstep->lockstepId) { - GBASIOLockstepDriverSetMode(driver, -1); - } - return true; -} - static uint32_t GBASIOLockstepDriverId(const struct GBASIODriver* driver) { UNUSED(driver); return DRIVER_ID; diff --git a/src/platform/qt/CoreController.cpp b/src/platform/qt/CoreController.cpp index 08b96bd81..032bdbc4f 100644 --- a/src/platform/qt/CoreController.cpp +++ b/src/platform/qt/CoreController.cpp @@ -424,7 +424,7 @@ bool CoreController::attachDolphin(const Address& address) { } if (GBASIODolphinConnect(&m_dolphin, &address, 0, 0)) { GBA* gba = static_cast(m_threadContext.core->board); - GBASIOSetDriver(&gba->sio, &m_dolphin.d, GBA_SIO_JOYBUS); + GBASIOSetDriver(&gba->sio, &m_dolphin.d); return true; } return false; @@ -433,7 +433,8 @@ bool CoreController::attachDolphin(const Address& address) { void CoreController::detachDolphin() { if (platform() == mPLATFORM_GBA) { GBA* gba = static_cast(m_threadContext.core->board); - GBASIOSetDriver(&gba->sio, nullptr, GBA_SIO_JOYBUS); + // TODO: Reattach to multiplayer controller + GBASIOSetDriver(&gba->sio, nullptr); } GBASIODolphinDestroy(&m_dolphin); } diff --git a/src/platform/qt/MultiplayerController.cpp b/src/platform/qt/MultiplayerController.cpp index c142fe126..616a252b5 100644 --- a/src/platform/qt/MultiplayerController.cpp +++ b/src/platform/qt/MultiplayerController.cpp @@ -265,9 +265,7 @@ bool MultiplayerController::attachGame(CoreController* controller) { GBASIOLockstepCoordinatorAttach(&m_gbaCoordinator, node); player.node.gba = node; - GBASIOSetDriver(&gba->sio, &node->d, GBA_SIO_MULTI); - GBASIOSetDriver(&gba->sio, &node->d, GBA_SIO_NORMAL_8); - GBASIOSetDriver(&gba->sio, &node->d, GBA_SIO_NORMAL_32); + GBASIOSetDriver(&gba->sio, &node->d); break; } #endif @@ -356,10 +354,8 @@ void MultiplayerController::detachGame(CoreController* controller) { #ifdef M_CORE_GBA case mPLATFORM_GBA: { GBA* gba = static_cast(thread->core->board); - GBASIOLockstepDriver* node = reinterpret_cast(gba->sio.drivers.multiplayer); - GBASIOSetDriver(&gba->sio, nullptr, GBA_SIO_MULTI); - GBASIOSetDriver(&gba->sio, nullptr, GBA_SIO_NORMAL_8); - GBASIOSetDriver(&gba->sio, nullptr, GBA_SIO_NORMAL_32); + GBASIOLockstepDriver* node = reinterpret_cast(gba->sio.driver); + GBASIOSetDriver(&gba->sio, nullptr); if (node) { GBASIOLockstepCoordinatorDetach(&m_gbaCoordinator, node); delete node->user; @@ -485,12 +481,13 @@ void MultiplayerController::fixOrder() { switch (m_platform) { #ifdef M_CORE_GBA case mPLATFORM_GBA: - for (int pid : m_pids.keys()) { + // TODO: fix + /*for (int pid : m_pids.keys()) { Player& p = m_pids.find(pid).value(); GBA* gba = static_cast(p.controller->thread()->core->board); - GBASIOLockstepDriver* node = reinterpret_cast(gba->sio.drivers.multiplayer); + GBASIOLockstepDriver* node = reinterpret_cast(gba->sio.driver); m_players[node->d.deviceId(&node->d)] = pid; - } + }*/ break; #endif #ifdef M_CORE_GB From 7fa572e3ff39164c64a453646a0a74d43f21720c Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 11 Sep 2024 02:02:38 -0700 Subject: [PATCH 035/114] GBA SIO: Split lockstep interval for linked and unlinked times --- src/gba/sio/lockstep.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/gba/sio/lockstep.c b/src/gba/sio/lockstep.c index f71f9b323..7d6f921d9 100644 --- a/src/gba/sio/lockstep.c +++ b/src/gba/sio/lockstep.c @@ -10,7 +10,8 @@ #define DRIVER_ID 0x6B636F4C #define DRIVER_STATE_VERSION 1 -#define LOCKSTEP_INCREMENT 2000 +#define LOCKSTEP_INTERVAL 2048 +#define UNLOCKED_INTERVAL 4096 #define TARGET(P) (1 << (P)) #define TARGET_ALL 0xF #define TARGET_PRIMARY 0x1 @@ -186,7 +187,7 @@ static void GBASIOLockstepDriverReset(struct GBASIODriver* driver) { } _reconfigPlayers(coordinator); if (player->playerId != 0) { - player->cycleOffset = mTimingCurrentTime(&driver->p->p->timing) - coordinator->cycle + LOCKSTEP_INCREMENT; + player->cycleOffset = mTimingCurrentTime(&driver->p->p->timing) - coordinator->cycle + LOCKSTEP_INTERVAL; struct GBASIOLockstepEvent event = { .type = SIO_EV_ATTACH, .playerId = player->playerId, @@ -207,7 +208,7 @@ static void GBASIOLockstepDriverReset(struct GBASIODriver* driver) { _setReady(coordinator, player, player->playerId, player->mode); if (TableSize(&coordinator->players) == 1) { coordinator->cycle = mTimingCurrentTime(&lockstep->d.p->p->timing); - nextEvent = LOCKSTEP_INCREMENT; + nextEvent = LOCKSTEP_INTERVAL; } else { _setReady(coordinator, player, 0, coordinator->transferMode); nextEvent = _untilNextSync(lockstep->coordinator, player); @@ -629,7 +630,11 @@ void GBASIOLockstepCoordinatorDetach(struct GBASIOLockstepCoordinator* coordinat int32_t _untilNextSync(struct GBASIOLockstepCoordinator* coordinator, struct GBASIOLockstepPlayer* player) { int32_t cycle = coordinator->cycle - GBASIOLockstepTime(player); if (player->playerId == 0) { - cycle += LOCKSTEP_INCREMENT; + if (coordinator->nAttached < 2) { + cycle += UNLOCKED_INTERVAL; + } else { + cycle += LOCKSTEP_INTERVAL; + } } return cycle; } @@ -921,7 +926,7 @@ void _lockstepEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) nextEvent = player->queue->timestamp - GBASIOLockstepTime(player); } - if (player->playerId != 0 && nextEvent <= LOCKSTEP_INCREMENT) { + if (player->playerId != 0 && nextEvent <= LOCKSTEP_INTERVAL) { if (!player->queue || wasDetach) { GBASIOLockstepPlayerSleep(player); // XXX: Is there a better way to gain sync lock at the beginning? From 5db42e83c97d705a0a1d564148458bf29082c4bc Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 12 Sep 2024 02:13:38 -0700 Subject: [PATCH 036/114] Qt: Delay attaching SIO driver until a second player is connected --- src/platform/qt/MultiplayerController.cpp | 39 ++++++++++++++++------- src/platform/qt/MultiplayerController.h | 1 + 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/platform/qt/MultiplayerController.cpp b/src/platform/qt/MultiplayerController.cpp index 616a252b5..0d12359bb 100644 --- a/src/platform/qt/MultiplayerController.cpp +++ b/src/platform/qt/MultiplayerController.cpp @@ -201,7 +201,8 @@ bool MultiplayerController::attachGame(CoreController* controller) { interrupters.append(p.controller); } - if (attached() == 0) { + bool doDelayedAttach = false; + if (m_platform == mPLATFORM_NONE) { switch (controller->platform()) { #ifdef M_CORE_GBA case mPLATFORM_GBA: @@ -242,8 +243,6 @@ bool MultiplayerController::attachGame(CoreController* controller) { return false; } - GBA* gba = static_cast(thread->core->board); - GBASIOLockstepDriver* node = new GBASIOLockstepDriver; LockstepUser* user = new LockstepUser; mLockstepThreadUserInit(user, thread); @@ -262,10 +261,11 @@ bool MultiplayerController::attachGame(CoreController* controller) { }; GBASIOLockstepDriverCreate(node, &user->d); - GBASIOLockstepCoordinatorAttach(&m_gbaCoordinator, node); player.node.gba = node; - GBASIOSetDriver(&gba->sio, &node->d); + if (m_pids.size()) { + doDelayedAttach = true; + } break; } #endif @@ -281,6 +281,7 @@ bool MultiplayerController::attachGame(CoreController* controller) { GBSIOLockstepNodeCreate(node); GBSIOLockstepAttachNode(&m_gbLockstep, node); player.node.gb = node; + player.attached = true; GBSIOSetDriver(&gb->sio, &node->d); break; @@ -320,6 +321,19 @@ bool MultiplayerController::attachGame(CoreController* controller) { ++m_nextPid; fixOrder(); + if (doDelayedAttach) { + for (auto pid: m_players) { + Player& player = m_pids.find(pid).value(); + if (player.attached) { + continue; + } + GBA* gba = static_cast(player.controller->thread()->core->board); + GBASIOLockstepCoordinatorAttach(&m_gbaCoordinator, player.node.gba); + GBASIOSetDriver(&gba->sio, &player.node.gba->d); + player.attached = true; + } + } + emit gameAttached(); return true; } @@ -354,13 +368,16 @@ void MultiplayerController::detachGame(CoreController* controller) { #ifdef M_CORE_GBA case mPLATFORM_GBA: { GBA* gba = static_cast(thread->core->board); - GBASIOLockstepDriver* node = reinterpret_cast(gba->sio.driver); - GBASIOSetDriver(&gba->sio, nullptr); - if (node) { - GBASIOLockstepCoordinatorDetach(&m_gbaCoordinator, node); - delete node->user; - delete node; + Player& p = m_pids.find(pid).value(); + GBASIODriver* node = gba->sio.driver; + if (node == &p.node.gba->d) { + GBASIOSetDriver(&gba->sio, nullptr); } + if (p.attached) { + GBASIOLockstepCoordinatorDetach(&m_gbaCoordinator, p.node.gba); + } + delete p.node.gba->user; + delete p.node.gba; break; } #endif diff --git a/src/platform/qt/MultiplayerController.h b/src/platform/qt/MultiplayerController.h index 9eaf20b16..666cb390b 100644 --- a/src/platform/qt/MultiplayerController.h +++ b/src/platform/qt/MultiplayerController.h @@ -64,6 +64,7 @@ private: unsigned waitMask = 0; int saveId = 1; int preferredId = 0; + bool attached = false; }; struct LockstepUser : mLockstepThreadUser { MultiplayerController* controller; From 1d584edb77db9693e050d0b00dda2b06d9582c1c Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 12 Sep 2024 02:44:26 -0700 Subject: [PATCH 037/114] GBA: Expose setting the link port device as a peripheral --- include/mgba/gba/interface.h | 2 +- src/gba/core.c | 2 +- src/platform/qt/CoreController.cpp | 11 +++++------ src/platform/qt/MultiplayerController.cpp | 6 +++--- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/include/mgba/gba/interface.h b/include/mgba/gba/interface.h index 9b8f6e001..cbbb4d23a 100644 --- a/include/mgba/gba/interface.h +++ b/include/mgba/gba/interface.h @@ -81,7 +81,7 @@ extern MGBA_EXPORT const int GBA_LUX_LEVELS[10]; enum { mPERIPH_GBA_LUMINANCE = 0x1000, - mPERIPH_GBA_BATTLECHIP_GATE, + mPERIPH_GBA_LINK_PORT, }; struct GBACartridgeOverride { diff --git a/src/gba/core.c b/src/gba/core.c index 898745e9e..67550bd7a 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -962,7 +962,7 @@ static void _GBACoreSetPeripheral(struct mCore* core, int type, void* periph) { case mPERIPH_GBA_LUMINANCE: gba->luminanceSource = periph; break; - case mPERIPH_GBA_BATTLECHIP_GATE: + case mPERIPH_GBA_LINK_PORT: GBASIOSetDriver(&gba->sio, periph); break; default: diff --git a/src/platform/qt/CoreController.cpp b/src/platform/qt/CoreController.cpp index 032bdbc4f..807a86685 100644 --- a/src/platform/qt/CoreController.cpp +++ b/src/platform/qt/CoreController.cpp @@ -423,8 +423,8 @@ bool CoreController::attachDolphin(const Address& address) { return false; } if (GBASIODolphinConnect(&m_dolphin, &address, 0, 0)) { - GBA* gba = static_cast(m_threadContext.core->board); - GBASIOSetDriver(&gba->sio, &m_dolphin.d); + clearMultiplayerController(); + m_threadContext.core->setPeripheral(m_threadContext.core, mPERIPH_GBA_LINK_PORT, &m_dolphin.d); return true; } return false; @@ -432,9 +432,8 @@ bool CoreController::attachDolphin(const Address& address) { void CoreController::detachDolphin() { if (platform() == mPLATFORM_GBA) { - GBA* gba = static_cast(m_threadContext.core->board); // TODO: Reattach to multiplayer controller - GBASIOSetDriver(&gba->sio, nullptr); + m_threadContext.core->setPeripheral(m_threadContext.core, mPERIPH_GBA_LINK_PORT, NULL); } GBASIODolphinDestroy(&m_dolphin); } @@ -1095,7 +1094,7 @@ void CoreController::attachBattleChipGate() { Interrupter interrupter(this); clearMultiplayerController(); GBASIOBattlechipGateCreate(&m_battlechip); - m_threadContext.core->setPeripheral(m_threadContext.core, mPERIPH_GBA_BATTLECHIP_GATE, &m_battlechip); + m_threadContext.core->setPeripheral(m_threadContext.core, mPERIPH_GBA_LINK_PORT, &m_battlechip); } void CoreController::detachBattleChipGate() { @@ -1103,7 +1102,7 @@ void CoreController::detachBattleChipGate() { return; } Interrupter interrupter(this); - m_threadContext.core->setPeripheral(m_threadContext.core, mPERIPH_GBA_BATTLECHIP_GATE, nullptr); + m_threadContext.core->setPeripheral(m_threadContext.core, mPERIPH_GBA_LINK_PORT, nullptr); } void CoreController::setBattleChipId(uint16_t id) { diff --git a/src/platform/qt/MultiplayerController.cpp b/src/platform/qt/MultiplayerController.cpp index 0d12359bb..5839b0f84 100644 --- a/src/platform/qt/MultiplayerController.cpp +++ b/src/platform/qt/MultiplayerController.cpp @@ -327,9 +327,9 @@ bool MultiplayerController::attachGame(CoreController* controller) { if (player.attached) { continue; } - GBA* gba = static_cast(player.controller->thread()->core->board); + struct mCore* core = player.controller->thread()->core; GBASIOLockstepCoordinatorAttach(&m_gbaCoordinator, player.node.gba); - GBASIOSetDriver(&gba->sio, &player.node.gba->d); + core->setPeripheral(core, mPERIPH_GBA_LINK_PORT, &player.node.gba->d); player.attached = true; } } @@ -371,7 +371,7 @@ void MultiplayerController::detachGame(CoreController* controller) { Player& p = m_pids.find(pid).value(); GBASIODriver* node = gba->sio.driver; if (node == &p.node.gba->d) { - GBASIOSetDriver(&gba->sio, nullptr); + thread->core->setPeripheral(thread->core, mPERIPH_GBA_LINK_PORT, NULL); } if (p.attached) { GBASIOLockstepCoordinatorDetach(&m_gbaCoordinator, p.node.gba); From 39d90e5e443cd41c68b4562b66d08fc40ff1fe57 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 13 Sep 2024 01:13:48 -0700 Subject: [PATCH 038/114] GBA SIO: Only set up GBP driver if no other driver is loaded --- src/gba/sio/gbp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gba/sio/gbp.c b/src/gba/sio/gbp.c index 8edd92388..977e67de4 100644 --- a/src/gba/sio/gbp.c +++ b/src/gba/sio/gbp.c @@ -87,8 +87,9 @@ void GBASIOPlayerUpdate(struct GBA* gba) { gba->sio.gbp.inputsPosted = 0; gba->sio.gbp.oldCallback = gba->keyCallback; gba->keyCallback = &gba->sio.gbp.callback.d; - // TODO: Check if the SIO driver is actually used first - GBASIOSetDriver(&gba->sio, &gba->sio.gbp.d); + if (!gba->sio.driver) { + GBASIOSetDriver(&gba->sio, &gba->sio.gbp.d); + } } } From 79ed790a4e397451d810824f86c50e322fb1ec08 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 15 Sep 2024 04:43:18 -0700 Subject: [PATCH 039/114] GBA IO: Fix SIOCNT/RCNT serialization --- src/gba/io.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gba/io.c b/src/gba/io.c index d29fb8a4b..8a4ea7fc2 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -214,8 +214,8 @@ static const int _isRSpecialRegister[GBA_REG(INTERNAL_MAX)] = { /* 10 */ 1, 1, 1, 1, 1, 1, 1, 1, /* 11 */ 0, 0, 0, 0, 0, 0, 0, 0, /* SIO */ - /* 12 */ 1, 1, 1, 1, 1, 0, 0, 0, - /* 13 */ 1, 1, 1, 0, 0, 0, 0, 0, + /* 12 */ 1, 1, 1, 1, 0, 0, 0, 0, + /* 13 */ 1, 1, 0, 0, 0, 0, 0, 0, /* 14 */ 1, 0, 0, 0, 0, 0, 0, 0, /* 15 */ 1, 1, 1, 1, 1, 0, 0, 0, /* 16 */ 0, 0, 0, 0, 0, 0, 0, 0, From 2c1fcf96915523fa527fb97df672c1b70f43962b Mon Sep 17 00:00:00 2001 From: CasualPokePlayer <50538166+CasualPokePlayer@users.noreply.github.com> Date: Sat, 28 Sep 2024 08:55:21 -0700 Subject: [PATCH 040/114] Fix savestates not writing back GPIO variables to gpioBase Resolves #3294 Also see https://github.com/TASEmulators/BizHawk/issues/4060 (this is fixed with this patch). --- src/gba/cart/gpio.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/gba/cart/gpio.c b/src/gba/cart/gpio.c index fde4e3714..15f082849 100644 --- a/src/gba/cart/gpio.c +++ b/src/gba/cart/gpio.c @@ -465,6 +465,18 @@ void GBAHardwareSerialize(const struct GBACartridgeHardware* hw, struct GBASeria STORE_16(hw->direction, 0, &state->hw.pinDirection); state->hw.devices = hw->devices; + if (hw->gpioBase) { + if (hw->readWrite) { + 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; + } + } + STORE_32(hw->rtc.bytesRemaining, 0, &state->hw.rtcBytesRemaining); STORE_32(hw->rtc.transferStep, 0, &state->hw.rtcTransferStep); STORE_32(hw->rtc.bitsRead, 0, &state->hw.rtcBitsRead); From a26971cdc7fe564a572a06e7320e115cc959895d Mon Sep 17 00:00:00 2001 From: CasualPokePlayer <50538166+CasualPokePlayer@users.noreply.github.com> Date: Sat, 28 Sep 2024 16:39:19 -0700 Subject: [PATCH 041/114] This should be in Deserialize not Serialize --- src/gba/cart/gpio.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/gba/cart/gpio.c b/src/gba/cart/gpio.c index 15f082849..562a42b63 100644 --- a/src/gba/cart/gpio.c +++ b/src/gba/cart/gpio.c @@ -465,18 +465,6 @@ void GBAHardwareSerialize(const struct GBACartridgeHardware* hw, struct GBASeria STORE_16(hw->direction, 0, &state->hw.pinDirection); state->hw.devices = hw->devices; - if (hw->gpioBase) { - if (hw->readWrite) { - 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; - } - } - STORE_32(hw->rtc.bytesRemaining, 0, &state->hw.rtcBytesRemaining); STORE_32(hw->rtc.transferStep, 0, &state->hw.rtcTransferStep); STORE_32(hw->rtc.bitsRead, 0, &state->hw.rtcBitsRead); @@ -514,6 +502,18 @@ void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, const struct GBASer LOAD_16(hw->direction, 0, &state->hw.pinDirection); hw->devices = state->hw.devices; + if (hw->gpioBase) { + if (hw->readWrite) { + 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; + } + } + LOAD_32(hw->rtc.bytesRemaining, 0, &state->hw.rtcBytesRemaining); LOAD_32(hw->rtc.transferStep, 0, &state->hw.rtcTransferStep); LOAD_32(hw->rtc.bitsRead, 0, &state->hw.rtcBitsRead); From 4008ccea3d1fcdfee476fcdc0a3511f3206a9a21 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 29 Sep 2024 01:00:32 -0700 Subject: [PATCH 042/114] CHANGES: Update --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index 9a1694d69..b30f3c95f 100644 --- a/CHANGES +++ b/CHANGES @@ -18,6 +18,7 @@ Emulation fixes: - GBA I/O: Fix HALTCNT access behavior (fixes mgba.io/i/2309) - GBA I/O: Fix audio register 8-bit write behavior (fixes mgba.io/i/3086) - GBA Serialize: Fix some minor save state edge cases + - GBA Serialize: Properly restore GPIO register state (fixes mgba.io/i/3294) - GBA SIO: Fix MULTI mode SIOCNT bit 7 writes on secondary GBAs (fixes mgba.io/i/3110) - GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722) - GBA Video: Improve emulation of window start/end conditions (fixes mgba.io/i/1945) From 7089a5572b21e9ba82448538b40cd31e9a538f19 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 17 Sep 2024 00:35:31 -0700 Subject: [PATCH 043/114] GBA SIO: Add periodic hard sync so the runner doesn't get too ahead --- include/mgba/internal/gba/sio/lockstep.h | 1 + src/gba/sio/lockstep.c | 26 +++++++++++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/include/mgba/internal/gba/sio/lockstep.h b/include/mgba/internal/gba/sio/lockstep.h index 7baed0693..0053e6b94 100644 --- a/include/mgba/internal/gba/sio/lockstep.h +++ b/include/mgba/internal/gba/sio/lockstep.h @@ -41,6 +41,7 @@ struct GBASIOLockstepCoordinator { enum GBASIOMode transferMode; int32_t cycle; + int32_t nextHardSync; uint16_t multiData[4]; uint32_t normalData[4]; diff --git a/src/gba/sio/lockstep.c b/src/gba/sio/lockstep.c index 7d6f921d9..d1375c8a0 100644 --- a/src/gba/sio/lockstep.c +++ b/src/gba/sio/lockstep.c @@ -12,6 +12,7 @@ #define DRIVER_STATE_VERSION 1 #define LOCKSTEP_INTERVAL 2048 #define UNLOCKED_INTERVAL 4096 +#define HARD_SYNC_INTERVAL 0x80000 #define TARGET(P) (1 << (P)) #define TARGET_ALL 0xF #define TARGET_PRIMARY 0x1 @@ -67,7 +68,8 @@ struct GBASIOLockstepSerializedState { struct { int32_t cycle; uint32_t waiting; - uint32_t reservedCoordinator[4]; + int32_t nextHardSync; + uint32_t reservedCoordinator[3]; uint16_t multiData[4]; uint32_t normalData[4]; } coordinator; @@ -159,9 +161,10 @@ static void GBASIOLockstepDriverDeinit(struct GBASIODriver* driver) { static void GBASIOLockstepDriverReset(struct GBASIODriver* driver) { struct GBASIOLockstepDriver* lockstep = (struct GBASIOLockstepDriver*) driver; struct GBASIOLockstepCoordinator* coordinator = lockstep->coordinator; + struct GBASIOLockstepPlayer* player; if (!lockstep->lockstepId) { - struct GBASIOLockstepPlayer* player = calloc(1, sizeof(*player)); unsigned id; + player = calloc(1, sizeof(*player)); player->driver = lockstep; player->mode = driver->p->mode; player->playerId = -1; @@ -195,16 +198,19 @@ static void GBASIOLockstepDriverReset(struct GBASIODriver* driver) { }; _enqueueEvent(coordinator, &event, TARGET_ALL & ~TARGET(player->playerId)); } - MutexUnlock(&coordinator->mutex); + } else { + player = TableLookup(&coordinator->players, lockstep->lockstepId); + if (player->playerId != 0) { + player->cycleOffset = mTimingCurrentTime(&driver->p->p->timing) - coordinator->cycle + LOCKSTEP_INTERVAL; + } } if (mTimingIsScheduled(&lockstep->d.p->p->timing, &lockstep->event)) { + MutexUnlock(&coordinator->mutex); return; } int32_t nextEvent; - MutexLock(&coordinator->mutex); - struct GBASIOLockstepPlayer* player = TableLookup(&coordinator->players, lockstep->lockstepId); _setReady(coordinator, player, player->playerId, player->mode); if (TableSize(&coordinator->players) == 1) { coordinator->cycle = mTimingCurrentTime(&lockstep->d.p->p->timing); @@ -341,6 +347,7 @@ static bool GBASIOLockstepDriverLoadState(struct GBASIODriver* driver, const voi if (player->playerId == 0) { LOAD_32LE(coordinator->cycle, 0, &state->coordinator.cycle); LOAD_32LE(coordinator->waiting, 0, &state->coordinator.waiting); + LOAD_32LE(coordinator->nextHardSync, 0, &state->coordinator.nextHardSync); for (i = 0; i < 4; ++i) { LOAD_16LE(coordinator->multiData[i], 0, &state->coordinator.multiData[i]); LOAD_32LE(coordinator->normalData[i], 0, &state->coordinator.normalData[i]); @@ -406,6 +413,7 @@ static void GBASIOLockstepDriverSaveState(struct GBASIODriver* driver, void** st if (player->playerId == 0) { STORE_32LE(coordinator->cycle, 0, &state->coordinator.cycle); STORE_32LE(coordinator->waiting, 0, &state->coordinator.waiting); + STORE_32LE(coordinator->nextHardSync, 0, &state->coordinator.nextHardSync); for (i = 0; i < 4; ++i) { STORE_16LE(coordinator->multiData[i], 0, &state->coordinator.multiData[i]); STORE_32LE(coordinator->normalData[i], 0, &state->coordinator.normalData[i]); @@ -643,6 +651,7 @@ void _advanceCycle(struct GBASIOLockstepCoordinator* coordinator, struct GBASIOL int32_t newCycle = GBASIOLockstepTime(player); mASSERT(newCycle - coordinator->cycle >= 0); //mLOG(GBA_SIO, DEBUG, "Advancing from cycle %08X to %08X (%i cycles)", coordinator->cycle, newCycle, newCycle - coordinator->cycle); + coordinator->nextHardSync -= newCycle - coordinator->cycle; coordinator->cycle = newCycle; } @@ -674,6 +683,7 @@ void _reconfigPlayers(struct GBASIOLockstepCoordinator* coordinator) { struct GBASIOLockstepPlayer* player = TableIteratorGetValue(&coordinator->players, &iter); coordinator->cycle = mTimingCurrentTime(&player->driver->d.p->p->timing); + coordinator->nextHardSync = HARD_SYNC_INTERVAL; if (player->playerId != 0) { player->playerId = 0; @@ -859,6 +869,12 @@ void _lockstepEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) if (!coordinator->transferActive) { GBASIOLockstepCoordinatorWakePlayers(coordinator); } + if (coordinator->nextHardSync < 0) { + if (!coordinator->waiting) { + _hardSync(coordinator, player); + } + coordinator->nextHardSync += HARD_SYNC_INTERVAL; + } } int32_t nextEvent = _untilNextSync(coordinator, player); From 3f7cfb3e32775919c803c105c08b23a3dd33093f Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 24 Sep 2024 01:47:16 -0700 Subject: [PATCH 044/114] GBA SIO: Attempt to clean up resyncing on disconnects --- src/gba/sio/lockstep.c | 46 +++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/src/gba/sio/lockstep.c b/src/gba/sio/lockstep.c index d1375c8a0..e73dcbb09 100644 --- a/src/gba/sio/lockstep.c +++ b/src/gba/sio/lockstep.c @@ -115,6 +115,23 @@ static void _hardSync(struct GBASIOLockstepCoordinator*, struct GBASIOLockstepPl static void _lockstepEvent(struct mTiming*, void* context, uint32_t cyclesLate); +static void _verifyAwake(struct GBASIOLockstepCoordinator* coordinator) { +#ifdef NDEBUG + UNUSED(coordinator); +#else + int i; + int asleep = 0; + for (i = 0; i < coordinator->nAttached; ++i) { + if (!coordinator->attachedPlayers[i]) { + continue; + } + struct GBASIOLockstepPlayer* player = TableLookup(&coordinator->players, coordinator->attachedPlayers[i]); + asleep += player->asleep; + } + mASSERT_DEBUG(!asleep || asleep < coordinator->nAttached); +#endif +} + void GBASIOLockstepDriverCreate(struct GBASIOLockstepDriver* driver, struct mLockstepUser* user) { memset(driver, 0, sizeof(*driver)); driver->d.init = GBASIOLockstepDriverInit; @@ -189,8 +206,8 @@ static void GBASIOLockstepDriverReset(struct GBASIODriver* driver) { } } _reconfigPlayers(coordinator); + player->cycleOffset = mTimingCurrentTime(&driver->p->p->timing) - coordinator->cycle; if (player->playerId != 0) { - player->cycleOffset = mTimingCurrentTime(&driver->p->p->timing) - coordinator->cycle + LOCKSTEP_INTERVAL; struct GBASIOLockstepEvent event = { .type = SIO_EV_ATTACH, .playerId = player->playerId, @@ -200,9 +217,7 @@ static void GBASIOLockstepDriverReset(struct GBASIODriver* driver) { } } else { player = TableLookup(&coordinator->players, lockstep->lockstepId); - if (player->playerId != 0) { - player->cycleOffset = mTimingCurrentTime(&driver->p->p->timing) - coordinator->cycle + LOCKSTEP_INTERVAL; - } + player->cycleOffset = mTimingCurrentTime(&driver->p->p->timing) - coordinator->cycle; } if (mTimingIsScheduled(&lockstep->d.p->p->timing, &lockstep->event)) { @@ -662,12 +677,18 @@ void _removePlayer(struct GBASIOLockstepCoordinator* coordinator, struct GBASIOL .timestamp = GBASIOLockstepTime(player), }; _enqueueEvent(coordinator, &event, TARGET_ALL & ~TARGET(player->playerId)); - GBASIOLockstepCoordinatorWakePlayers(coordinator); - if (player->playerId != 0) { - GBASIOLockstepCoordinatorAckPlayer(coordinator, player); - } + + coordinator->waiting = 0; + coordinator->transferActive = false; + TableRemove(&coordinator->players, player->driver->lockstepId); _reconfigPlayers(coordinator); + + struct GBASIOLockstepPlayer* runner = TableLookup(&coordinator->players, coordinator->attachedPlayers[0]); + if (runner) { + GBASIOLockstepPlayerWake(runner); + } + _verifyAwake(coordinator); } void _reconfigPlayers(struct GBASIOLockstepCoordinator* coordinator) { @@ -863,8 +884,10 @@ void _lockstepEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) player->queue->playerId, player->queue->timestamp); wasDetach = true; } - if (player->playerId == 0) { - // We are the clock owner; advance the shared clock + if (player->playerId == 0 && GBASIOLockstepTime(player) - coordinator->cycle >= 0) { + // We are the clock owner; advance the shared clock. However, if we just became + // the clock owner (by the previous one disconnecting) we might be slightly + // behind the shared clock. We should wait a bit if needed in that case. _advanceCycle(coordinator, player); if (!coordinator->transferActive) { GBASIOLockstepCoordinatorWakePlayers(coordinator); @@ -949,6 +972,7 @@ void _lockstepEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) if (nextEvent < 4) { nextEvent = 4; } + _verifyAwake(coordinator); } } MutexUnlock(&coordinator->mutex); @@ -974,6 +998,8 @@ void GBASIOLockstepCoordinatorWaitOnPlayers(struct GBASIOLockstepCoordinator* co coordinator->waiting = ((1 << coordinator->nAttached) - 1) & ~TARGET(player->playerId); GBASIOLockstepPlayerSleep(player); GBASIOLockstepCoordinatorWakePlayers(coordinator); + + _verifyAwake(coordinator); } void GBASIOLockstepCoordinatorWakePlayers(struct GBASIOLockstepCoordinator* coordinator) { From f0d65b73e81a39208e6305127185a0792b844460 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 17 Sep 2024 00:18:58 -0700 Subject: [PATCH 045/114] GBA SIO: Late cleanup --- src/gba/sio/lockstep.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/gba/sio/lockstep.c b/src/gba/sio/lockstep.c index e73dcbb09..a429a31e2 100644 --- a/src/gba/sio/lockstep.c +++ b/src/gba/sio/lockstep.c @@ -10,7 +10,7 @@ #define DRIVER_ID 0x6B636F4C #define DRIVER_STATE_VERSION 1 -#define LOCKSTEP_INTERVAL 2048 +#define LOCKSTEP_INTERVAL 4096 #define UNLOCKED_INTERVAL 4096 #define HARD_SYNC_INTERVAL 0x80000 #define TARGET(P) (1 << (P)) @@ -457,7 +457,7 @@ static void GBASIOLockstepDriverSetMode(struct GBASIODriver* driver, enum GBASIO .mode = mode, }; if (player->playerId == 0) { - mASSERT(!coordinator->transferActive); // TODO + mASSERT_DEBUG(!coordinator->transferActive); // TODO coordinator->transferMode = mode; GBASIOLockstepCoordinatorWaitOnPlayers(coordinator, player); } @@ -664,8 +664,7 @@ int32_t _untilNextSync(struct GBASIOLockstepCoordinator* coordinator, struct GBA void _advanceCycle(struct GBASIOLockstepCoordinator* coordinator, struct GBASIOLockstepPlayer* player) { int32_t newCycle = GBASIOLockstepTime(player); - mASSERT(newCycle - coordinator->cycle >= 0); - //mLOG(GBA_SIO, DEBUG, "Advancing from cycle %08X to %08X (%i cycles)", coordinator->cycle, newCycle, newCycle - coordinator->cycle); + mASSERT_DEBUG(newCycle - coordinator->cycle >= 0); coordinator->nextHardSync -= newCycle - coordinator->cycle; coordinator->cycle = newCycle; } @@ -810,7 +809,6 @@ static void _setData(struct GBASIOLockstepCoordinator* coordinator, uint32_t id, case GBA_SIO_JOYBUS: mLOG(GBA_SIO, ERROR, "Unsupported mode %i in lockstep", coordinator->transferMode); // TODO: Should we handle this or just abort? - abort(); break; } } @@ -901,7 +899,6 @@ void _lockstepEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) } int32_t nextEvent = _untilNextSync(coordinator, player); - //mASSERT_DEBUG(nextEvent + cyclesLate > 0); while (true) { struct GBASIOLockstepEvent* event = player->queue; if (!event) { @@ -1003,7 +1000,6 @@ void GBASIOLockstepCoordinatorWaitOnPlayers(struct GBASIOLockstepCoordinator* co } void GBASIOLockstepCoordinatorWakePlayers(struct GBASIOLockstepCoordinator* coordinator) { - //mLOG(GBA_SIO, DEBUG, "Waking all secondary players"); int i; for (i = 1; i < coordinator->nAttached; ++i) { if (!coordinator->attachedPlayers[i]) { @@ -1026,7 +1022,6 @@ void GBASIOLockstepCoordinatorAckPlayer(struct GBASIOLockstepCoordinator* coordi if (player->playerId == 0) { return; } - mLOG(GBA_SIO, DEBUG, "Player %i acking primary", player->playerId); coordinator->waiting &= ~TARGET(player->playerId); if (!coordinator->waiting) { mLOG(GBA_SIO, DEBUG, "All players acked, waking primary"); @@ -1053,7 +1048,6 @@ void GBASIOLockstepPlayerSleep(struct GBASIOLockstepPlayer* player) { if (player->asleep) { return; } - //mLOG(GBA_SIO, DEBUG, "Player %i going to sleep with %i cycles until sync", player->playerId, _untilNextSync(coordinator, player)); player->asleep = true; player->driver->user->sleep(player->driver->user); player->driver->d.p->p->cpu->nextEvent = 0; From ed0a63d1b812611fa3832e5af1722037a9916e3d Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 29 Sep 2024 20:09:48 -0700 Subject: [PATCH 046/114] Python: Attempt to fix build --- src/platform/python/_builder.h | 1 - src/platform/python/_builder.py | 1 - 2 files changed, 2 deletions(-) diff --git a/src/platform/python/_builder.h b/src/platform/python/_builder.h index 0b51eb19d..ad12c107e 100644 --- a/src/platform/python/_builder.h +++ b/src/platform/python/_builder.h @@ -46,7 +46,6 @@ void free(void*); #define PYEXPORT extern "Python+C" #include "platform/python/core.h" #include "platform/python/log.h" -#include "platform/python/sio.h" #include "platform/python/vfs-py.h" #undef PYEXPORT diff --git a/src/platform/python/_builder.py b/src/platform/python/_builder.py index 1defca537..e144b6a51 100644 --- a/src/platform/python/_builder.py +++ b/src/platform/python/_builder.py @@ -45,7 +45,6 @@ ffi.set_source("mgba._pylib", """ #define PYEXPORT #include "platform/python/core.h" #include "platform/python/log.h" -#include "platform/python/sio.h" #include "platform/python/vfs-py.h" #undef PYEXPORT """, include_dirs=[incdir, srcdir], From 36f321f84889bc69b48541e0519401c091eeaeca Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 29 Sep 2024 20:27:00 -0700 Subject: [PATCH 047/114] Python: Actually fix build --- src/platform/python/_builder.h | 3 +-- src/platform/python/_builder.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/platform/python/_builder.h b/src/platform/python/_builder.h index ad12c107e..c39fcf143 100644 --- a/src/platform/python/_builder.h +++ b/src/platform/python/_builder.h @@ -22,8 +22,7 @@ #define CXX_GUARD_END #define PYCPARSE -#define va_list void* - +typedef ... va_list; typedef int... time_t; typedef int... off_t; typedef ...* png_structp; diff --git a/src/platform/python/_builder.py b/src/platform/python/_builder.py index e144b6a51..f18a3c95e 100644 --- a/src/platform/python/_builder.py +++ b/src/platform/python/_builder.py @@ -52,7 +52,7 @@ ffi.set_source("mgba._pylib", """ libraries=["mgba"], library_dirs=[bindir], runtime_library_dirs=[libdir], - sources=[os.path.join(pydir, path) for path in ["vfs-py.c", "core.c", "log.c", "sio.c"]]) + sources=[os.path.join(pydir, path) for path in ["vfs-py.c", "core.c", "log.c"]]) preprocessed = subprocess.check_output(cpp + ["-fno-inline", "-P"] + cppflags + [os.path.join(pydir, "_builder.h")], universal_newlines=True) From 8941f742927226c822ab0879347558aea6aa9b6e Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 29 Sep 2024 20:13:11 -0700 Subject: [PATCH 048/114] Qt: Add missing tr()s --- src/platform/qt/MultiplayerController.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/qt/MultiplayerController.cpp b/src/platform/qt/MultiplayerController.cpp index 5839b0f84..3b2146f7a 100644 --- a/src/platform/qt/MultiplayerController.cpp +++ b/src/platform/qt/MultiplayerController.cpp @@ -401,7 +401,7 @@ void MultiplayerController::detachGame(CoreController* controller) { QPair path(controller->path(), controller->baseDirectory()); Player& p = m_pids.find(pid).value(); if (!p.saveId) { - LOG(QT, WARN) << "Clearing invalid save ID"; + LOG(QT, WARN) << tr("Clearing invalid save ID"); } else { m_claimedSaves[path] &= ~(1 << (p.saveId - 1)); if (!m_claimedSaves[path]) { @@ -410,7 +410,7 @@ void MultiplayerController::detachGame(CoreController* controller) { } if (p.preferredId < 0) { - LOG(QT, WARN) << "Clearing invalid preferred ID"; + LOG(QT, WARN) << tr("Clearing invalid preferred ID"); } else { m_claimedIds &= ~(1 << p.preferredId); } From bfc52cd2f0156d82add7d634e107b68552cac538 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 30 Sep 2024 17:24:04 -0700 Subject: [PATCH 049/114] All: Fix whitespace errors --- include/mgba-util/geometry.h | 12 +++++----- include/mgba/feature/proxy-backend.h | 2 +- include/mgba/internal/defines.h | 24 +++++++++---------- src/arm/isa-thumb.c | 2 +- src/core/cheats.c | 2 +- src/core/thread.c | 2 +- src/feature/ffmpeg/ffmpeg-decoder.c | 2 +- src/feature/ffmpeg/ffmpeg-encoder.c | 2 +- src/feature/gui/gui-config.c | 2 +- src/feature/updater.c | 2 +- src/feature/video-logger.c | 6 ++--- src/gb/core.c | 2 +- src/gb/extra/proxy.c | 2 +- src/gb/gb.c | 4 ++-- src/gb/mbc.c | 2 +- src/gb/mbc/tama5.c | 2 +- src/gb/mbc/unlicensed.c | 10 ++++---- src/gb/video.c | 2 +- src/gba/extra/proxy.c | 2 +- src/gba/serialize.c | 2 +- src/platform/3ds/ctr-gpu.c | 2 +- src/platform/example/client-server/server.c | 2 +- src/platform/opengl/gl.c | 4 ++-- src/platform/opengl/gles2.c | 4 ++-- src/platform/psp2/psp2-common.h | 2 +- src/platform/qt/BattleChipView.cpp | 2 +- src/platform/qt/ConfigController.h | 2 +- src/platform/qt/DebuggerConsoleController.cpp | 2 +- src/platform/qt/Display.h | 2 +- src/platform/qt/DisplayGL.cpp | 2 +- src/platform/qt/ForwarderController.cpp | 4 ++-- src/platform/qt/ForwarderView.cpp | 6 ++--- src/platform/qt/MemorySearch.cpp | 2 +- src/platform/qt/SaveConverter.cpp | 16 ++++++------- src/platform/qt/TilePainter.cpp | 2 +- src/platform/qt/TileView.cpp | 4 ++-- src/platform/qt/Window.cpp | 6 ++--- src/platform/qt/library/LibraryTree.cpp | 2 +- .../qt/scripting/ScriptingTextBuffer.cpp | 2 +- src/script/engines/lua.c | 2 +- src/script/types.c | 2 +- src/tools/font-sdf.c | 2 +- src/util/audio-resampler.c | 2 +- src/util/crc32.c | 2 +- src/util/hash.c | 6 ++--- src/util/sfo.c | 2 +- 46 files changed, 86 insertions(+), 86 deletions(-) diff --git a/include/mgba-util/geometry.h b/include/mgba-util/geometry.h index aa7d65e94..5164b0b8d 100644 --- a/include/mgba-util/geometry.h +++ b/include/mgba-util/geometry.h @@ -11,15 +11,15 @@ CXX_GUARD_START struct mSize { - int width; - int height; + int width; + int height; }; struct mRectangle { - int x; - int y; - int width; - int height; + int x; + int y; + int width; + int height; }; void mRectangleUnion(struct mRectangle* dst, const struct mRectangle* add); diff --git a/include/mgba/feature/proxy-backend.h b/include/mgba/feature/proxy-backend.h index ffd38eb47..0851b3a68 100644 --- a/include/mgba/feature/proxy-backend.h +++ b/include/mgba/feature/proxy-backend.h @@ -57,7 +57,7 @@ struct mVideoBackendCommand { struct mVideoProxyBackend { struct VideoBackend d; struct VideoBackend* backend; - + struct RingFIFO in; struct RingFIFO out; diff --git a/include/mgba/internal/defines.h b/include/mgba/internal/defines.h index 8af603736..67b8ec8fd 100644 --- a/include/mgba/internal/defines.h +++ b/include/mgba/internal/defines.h @@ -9,23 +9,23 @@ #define mSAVEDATA_CLEANUP_THRESHOLD 15 enum { - mSAVEDATA_DIRT_NONE = 0, + mSAVEDATA_DIRT_NONE = 0, mSAVEDATA_DIRT_NEW = 1, mSAVEDATA_DIRT_SEEN = 2, }; static inline bool mSavedataClean(int* dirty, uint32_t* dirtAge, uint32_t frameCount) { - if (*dirty & mSAVEDATA_DIRT_NEW) { - *dirtAge = frameCount; - *dirty &= ~mSAVEDATA_DIRT_NEW; - if (!(*dirty & mSAVEDATA_DIRT_SEEN)) { - *dirty |= mSAVEDATA_DIRT_SEEN; - } - } else if ((*dirty & mSAVEDATA_DIRT_SEEN) && frameCount - *dirtAge > mSAVEDATA_CLEANUP_THRESHOLD) { - *dirty = 0; - return true; - } - return false; + if (*dirty & mSAVEDATA_DIRT_NEW) { + *dirtAge = frameCount; + *dirty &= ~mSAVEDATA_DIRT_NEW; + if (!(*dirty & mSAVEDATA_DIRT_SEEN)) { + *dirty |= mSAVEDATA_DIRT_SEEN; + } + } else if ((*dirty & mSAVEDATA_DIRT_SEEN) && frameCount - *dirtAge > mSAVEDATA_CLEANUP_THRESHOLD) { + *dirty = 0; + return true; + } + return false; } #endif diff --git a/src/arm/isa-thumb.c b/src/arm/isa-thumb.c index 0bc8fcb12..30fcfde9d 100644 --- a/src/arm/isa-thumb.c +++ b/src/arm/isa-thumb.c @@ -88,7 +88,7 @@ DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LSR1, } THUMB_NEUTRAL_S( , , cpu->gprs[rd]);) -DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(ASR1, +DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(ASR1, if (!immediate) { cpu->cpsr.c = ARM_SIGN(cpu->gprs[rm]); if (cpu->cpsr.c) { diff --git a/src/core/cheats.c b/src/core/cheats.c index 53f71e907..1ffe91adf 100644 --- a/src/core/cheats.c +++ b/src/core/cheats.c @@ -37,7 +37,7 @@ static uint32_t _patchMakeKey(struct mCheatPatch* patch) { patchKey >>= 2; break; default: - break; + break; } // TODO: More than one segment if (patch->segment > 0) { diff --git a/src/core/thread.c b/src/core/thread.c index 2462407cc..2b16a03c7 100644 --- a/src/core/thread.c +++ b/src/core/thread.c @@ -656,7 +656,7 @@ void mCoreThreadContinue(struct mCoreThread* threadContext) { if (threadContext->impl->requested) { threadContext->impl->state = mTHREAD_REQUEST; } else { - threadContext->impl->state = mTHREAD_RUNNING; + threadContext->impl->state = mTHREAD_RUNNING; } ConditionWake(&threadContext->impl->stateOnThreadCond); } diff --git a/src/feature/ffmpeg/ffmpeg-decoder.c b/src/feature/ffmpeg/ffmpeg-decoder.c index 09539ca61..189fb312f 100644 --- a/src/feature/ffmpeg/ffmpeg-decoder.c +++ b/src/feature/ffmpeg/ffmpeg-decoder.c @@ -71,7 +71,7 @@ bool FFmpegDecoderOpen(struct FFmpegDecoder* decoder, const char* infile) { codec = avcodec_find_decoder(context->codec_id); if (!codec) { FFmpegDecoderClose(decoder); - return false; + return false; } if (avcodec_open2(context, codec, NULL) < 0) { FFmpegDecoderClose(decoder); diff --git a/src/feature/ffmpeg/ffmpeg-encoder.c b/src/feature/ffmpeg/ffmpeg-encoder.c index 5ee54a5e8..6202e0fcd 100644 --- a/src/feature/ffmpeg/ffmpeg-encoder.c +++ b/src/feature/ffmpeg/ffmpeg-encoder.c @@ -893,7 +893,7 @@ void FFmpegEncoderSetInputFrameRate(struct FFmpegEncoder* encoder, int numerator void FFmpegEncoderSetInputSampleRate(struct FFmpegEncoder* encoder, int sampleRate) { encoder->isampleRate = sampleRate; - if (encoder->resampleContext) { + if (encoder->resampleContext) { av_freep(&encoder->audioBuffer); #ifdef USE_LIBAVRESAMPLE avresample_close(encoder->resampleContext); diff --git a/src/feature/gui/gui-config.c b/src/feature/gui/gui-config.c index e110ef768..6214abc15 100644 --- a/src/feature/gui/gui-config.c +++ b/src/feature/gui/gui-config.c @@ -295,7 +295,7 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t test.v.s = mCoreConfigGetValue(&runner->config, item->data.v.s); if (test.v.s && strcmp(test.v.s, v->v.s) == 0) { item->state = j; - break; + break; } break; case GUI_VARIANT_POINTER: diff --git a/src/feature/updater.c b/src/feature/updater.c index e50ed0fa2..ea9cea3c1 100644 --- a/src/feature/updater.c +++ b/src/feature/updater.c @@ -46,7 +46,7 @@ static void _updateList(const char* key, const char* value, void* user) { if (strncmp("medusa.", key, 7) == 0) { dotLoc = strchr(&key[7], '.'); } else { - dotLoc = strchr(key, '.'); + dotLoc = strchr(key, '.'); } if (!dotLoc) { return; diff --git a/src/feature/video-logger.c b/src/feature/video-logger.c index 4044a4928..ac5ef1777 100644 --- a/src/feature/video-logger.c +++ b/src/feature/video-logger.c @@ -339,17 +339,17 @@ bool mVideoLoggerRendererRunInjected(struct mVideoLogger* logger) { channel->injecting = true; bool res = mVideoLoggerRendererRun(logger, false); channel->injecting = false; - return res; + return res; } void mVideoLoggerInjectionPoint(struct mVideoLogger* logger, enum mVideoLoggerInjectionPoint injectionPoint) { struct mVideoLogChannel* channel = logger->dataContext; - channel->injectionPoint = injectionPoint; + channel->injectionPoint = injectionPoint; } void mVideoLoggerIgnoreAfterInjection(struct mVideoLogger* logger, uint32_t mask) { struct mVideoLogChannel* channel = logger->dataContext; - channel->ignorePackets = mask; + channel->ignorePackets = mask; } static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length) { diff --git a/src/gb/core.c b/src/gb/core.c index 2bfea28ef..e67fe4232 100644 --- a/src/gb/core.c +++ b/src/gb/core.c @@ -152,7 +152,7 @@ static bool _GBCoreInit(struct mCore* core) { #ifdef ENABLE_VFS mDirectorySetInit(&core->dirs); #endif - + return true; } diff --git a/src/gb/extra/proxy.c b/src/gb/extra/proxy.c index ba36c0090..c0c386f4c 100644 --- a/src/gb/extra/proxy.c +++ b/src/gb/extra/proxy.c @@ -267,7 +267,7 @@ void GBVideoProxyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, _copyExtraState(proxyRenderer); proxyRenderer->backend->drawRange(proxyRenderer->backend, startX, endX, y); } - mVideoLoggerRendererDrawRange(proxyRenderer->logger, startX, endX, y); + mVideoLoggerRendererDrawRange(proxyRenderer->logger, startX, endX, y); } void GBVideoProxyRendererFinishScanline(struct GBVideoRenderer* renderer, int y) { diff --git a/src/gb/gb.c b/src/gb/gb.c index 67dea55b8..fa0d6482f 100644 --- a/src/gb/gb.c +++ b/src/gb/gb.c @@ -152,7 +152,7 @@ bool GBLoadGBX(struct GBXMetadata* metadata, struct VFile* vf) { if (memcmp(footer, "MBC1", 4) == 0) { metadata->mapperVars.u8[0] = 5; } else if (memcmp(footer, "MB1M", 4) == 0) { - metadata->mapperVars.u8[0] = 4; + metadata->mapperVars.u8[0] = 4; } return true; } @@ -894,7 +894,7 @@ int GBValidModels(const uint8_t* bank0) { } else if (cart->cgb == 0xC0) { models = GB_MODEL_CGB; } else { - models = GB_MODEL_MGB; + models = GB_MODEL_MGB; } if (cart->sgb == 0x03 && cart->oldLicensee == 0x33) { models |= GB_MODEL_SGB; diff --git a/src/gb/mbc.c b/src/gb/mbc.c index c9ebed546..397b50088 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -163,7 +163,7 @@ static bool _isMulticart(const uint8_t* mem) { success = GBIsROM(vf); vf->close(vf); } - + return success; } diff --git a/src/gb/mbc/tama5.c b/src/gb/mbc/tama5.c index 505a35ed8..c4f0a1517 100644 --- a/src/gb/mbc/tama5.c +++ b/src/gb/mbc/tama5.c @@ -131,7 +131,7 @@ static void _latchTAMA6Rtc(struct mRTCSource* rtc, struct GBTAMA5State* tama5, t timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 24) / 10; } else { timerRegs[GBTAMA6_RTC_PA0_HOUR_1] = (diff % 12) % 10; - timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 12) / 10 + (diff / 12) * 2; + timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 12) / 10 + (diff / 12) * 2; } t /= 24; t += diff / 24; diff --git a/src/gb/mbc/unlicensed.c b/src/gb/mbc/unlicensed.c index 950ddf074..844c0d0cd 100644 --- a/src/gb/mbc/unlicensed.c +++ b/src/gb/mbc/unlicensed.c @@ -349,7 +349,7 @@ void _GBHitek(struct GB* gb, uint16_t address, uint8_t value) { break; case 0x300: // See hhugboy src/memory/mbc/MbcUnlHitek.cpp for commentary on this return - return; + return; } _GBMBC5(gb, address, value); } @@ -396,10 +396,10 @@ uint8_t _GBGGB81Read(struct GBMemory* memory, uint16_t address) { } void _GBLiCheng(struct GB* gb, uint16_t address, uint8_t value) { - if (address > 0x2100 && address < 0x3000) { - return; - } - _GBMBC5(gb, address, value); + if (address > 0x2100 && address < 0x3000) { + return; + } + _GBMBC5(gb, address, value); } void _GBSachen(struct GB* gb, uint16_t address, uint8_t value) { diff --git a/src/gb/video.c b/src/gb/video.c index 38be72a5a..d52e43dee 100644 --- a/src/gb/video.c +++ b/src/gb/video.c @@ -755,7 +755,7 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) { video->ly = 0; video->p->memory.io[GB_REG_LY] = 0; video->renderer->writePalette(video->renderer, 0, video->dmgPalette[0]); - + mTimingDeschedule(&video->p->timing, &video->modeEvent); mTimingDeschedule(&video->p->timing, &video->frameEvent); mTimingSchedule(&video->p->timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH << 1); diff --git a/src/gba/extra/proxy.c b/src/gba/extra/proxy.c index ee611247b..e33c7b568 100644 --- a/src/gba/extra/proxy.c +++ b/src/gba/extra/proxy.c @@ -324,7 +324,7 @@ void GBAVideoProxyRendererSaveState(struct GBAVideoRenderer* renderer, void** st proxyRenderer->logger->stateSize = 0; } else { proxyRenderer->backend->saveState(proxyRenderer->backend, state, size); - } + } } void GBAVideoProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) { diff --git a/src/gba/serialize.c b/src/gba/serialize.c index 1669eca3d..5eabaedea 100644 --- a/src/gba/serialize.c +++ b/src/gba/serialize.c @@ -195,7 +195,7 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) { if (GBASerializedMiscFlagsIsIrqPending(miscFlags)) { int32_t when; LOAD_32(when, 0, &state->nextIrq); - mTimingSchedule(&gba->timing, &gba->irqEvent, when); + mTimingSchedule(&gba->timing, &gba->irqEvent, when); } gba->cpuBlocked = GBASerializedMiscFlagsGetBlocked(miscFlags); gba->keysLast = GBASerializedMiscFlagsGetKeyIRQKeys(miscFlags); diff --git a/src/platform/3ds/ctr-gpu.c b/src/platform/3ds/ctr-gpu.c index 4a95c4640..d153f84a0 100644 --- a/src/platform/3ds/ctr-gpu.c +++ b/src/platform/3ds/ctr-gpu.c @@ -160,7 +160,7 @@ void ctrActivateTexture(const C3D_Tex* texture) { .m = { // Rows are in the order w z y x, because ctrulib 0.0f, 0.0f, 0.0f, 1.0f / activeTexture->width, - 0.0f, 0.0f, 1.0f / activeTexture->height, 0.0f + 0.0f, 0.0f, 1.0f / activeTexture->height, 0.0f } }; C3D_FVUnifMtx2x4(GPU_GEOMETRY_SHADER, GSH_FVEC_textureMtx, &textureMtx); diff --git a/src/platform/example/client-server/server.c b/src/platform/example/client-server/server.c index 0dcec9b15..71472a05c 100644 --- a/src/platform/example/client-server/server.c +++ b/src/platform/example/client-server/server.c @@ -53,7 +53,7 @@ int main(int argc, char** argv) { SocketClose(sock); SocketSubsystemDeinit(); didFail = true; - goto cleanup; + goto cleanup; } // Run the server diff --git a/src/platform/opengl/gl.c b/src/platform/opengl/gl.c index b0cef1f0d..de8354c1c 100644 --- a/src/platform/opengl/gl.c +++ b/src/platform/opengl/gl.c @@ -253,7 +253,7 @@ static void mGLContextImageSize(struct VideoBackend* v, enum VideoLayer layer, i *height = context->layerDims[layer].height; } else { *width = context->imageSizes[layer].width; - *height = context->imageSizes[layer].height; + *height = context->imageSizes[layer].height; } } @@ -266,7 +266,7 @@ void mGLContextPostFrame(struct VideoBackend* v, enum VideoLayer layer, const vo context->activeTex ^= 1; glBindTexture(GL_TEXTURE_2D, context->tex[context->activeTex]); } else { - glBindTexture(GL_TEXTURE_2D, context->layers[layer]); + glBindTexture(GL_TEXTURE_2D, context->layers[layer]); } int width = context->imageSizes[layer].width; diff --git a/src/platform/opengl/gles2.c b/src/platform/opengl/gles2.c index fa6716be9..e5ee98767 100644 --- a/src/platform/opengl/gles2.c +++ b/src/platform/opengl/gles2.c @@ -529,7 +529,7 @@ static void mGLES2ContextImageSize(struct VideoBackend* v, enum VideoLayer layer *height = context->layerDims[layer].height; } else { *width = context->imageSizes[layer].width; - *height = context->imageSizes[layer].height; + *height = context->imageSizes[layer].height; } } @@ -617,7 +617,7 @@ void mGLES2ShaderInit(struct mGLES2Shader* shader, const char* vs, const char* f if (shader->width > 0 && shader->height > 0) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, shader->width, shader->height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); } else { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 512, 512, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 512, 512, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); } glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, shader->tex, 0); diff --git a/src/platform/psp2/psp2-common.h b/src/platform/psp2/psp2-common.h index 7f34d1d10..dcb67b700 100644 --- a/src/platform/psp2/psp2-common.h +++ b/src/platform/psp2/psp2-common.h @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef PSP2_COMMON_H #define PSP2_COMMON_H - + #include #define PSP2_HORIZONTAL_PIXELS 960 diff --git a/src/platform/qt/BattleChipView.cpp b/src/platform/qt/BattleChipView.cpp index f7d6eb4c7..46b24e927 100644 --- a/src/platform/qt/BattleChipView.cpp +++ b/src/platform/qt/BattleChipView.cpp @@ -216,7 +216,7 @@ void BattleChipView::loadDeck() { error->show(); return; } - + QList newDeck; QStringList deck = ini.value("deck").toString().split(','); for (const auto& item : deck) { diff --git a/src/platform/qt/ConfigController.h b/src/platform/qt/ConfigController.h index 4c71c3943..4325a46ba 100644 --- a/src/platform/qt/ConfigController.h +++ b/src/platform/qt/ConfigController.h @@ -130,7 +130,7 @@ private: mGraphicsOpts m_graphicsOpts{}; std::array m_subparsers; bool m_parsed = false; - + QHash m_argvOptions; QHash m_optionSet; std::unique_ptr m_settings; diff --git a/src/platform/qt/DebuggerConsoleController.cpp b/src/platform/qt/DebuggerConsoleController.cpp index dffd71bcf..716c31639 100644 --- a/src/platform/qt/DebuggerConsoleController.cpp +++ b/src/platform/qt/DebuggerConsoleController.cpp @@ -162,7 +162,7 @@ void DebuggerConsoleController::historyLoad() { if (line.endsWith("\r\n")) { line.chop(2); } else if (line.endsWith("\n")) { - line.chop(1); + line.chop(1); } history.append(QString::fromUtf8(line)); } diff --git a/src/platform/qt/Display.h b/src/platform/qt/Display.h index bb16b268e..3cc2fc61f 100644 --- a/src/platform/qt/Display.h +++ b/src/platform/qt/Display.h @@ -62,7 +62,7 @@ public: virtual void setVideoProxy(std::shared_ptr proxy) { m_videoProxy = std::move(proxy); } std::shared_ptr videoProxy() { return m_videoProxy; } virtual VideoBackend* videoBackend(); - + signals: void drawingStarted(); void showCursor(); diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 5b60eb46e..ad4969943 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -314,7 +314,7 @@ bool DisplayGL::highestCompatible(QSurfaceFormat& format) { if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { format.setVersion(1, 4); } else { - format.setVersion(1, 1); + format.setVersion(1, 1); } format.setOption(QSurfaceFormat::DeprecatedFunctions); if (DisplayGL::supportsFormat(format)) { diff --git a/src/platform/qt/ForwarderController.cpp b/src/platform/qt/ForwarderController.cpp index 0934f1c69..abf2d27d0 100644 --- a/src/platform/qt/ForwarderController.cpp +++ b/src/platform/qt/ForwarderController.cpp @@ -160,7 +160,7 @@ void ForwarderController::gotManifest(QNetworkReply* reply) { mUpdaterGetUpdateForChannel(&context, platform.toUtf8().constData(), m_channel.toUtf8().constData(), &update); downloadBuild({bucket + update.path}); - mUpdaterDeinit(&context); + mUpdaterDeinit(&context); } void ForwarderController::downloadBuild(const QUrl& url) { @@ -174,7 +174,7 @@ void ForwarderController::downloadBuild(const QUrl& url) { .arg(extension)); if (!m_sourceFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { emit buildFailed(); - return; + return; } QNetworkReply* reply = GBAApp::app()->httpGet(url); diff --git a/src/platform/qt/ForwarderView.cpp b/src/platform/qt/ForwarderView.cpp index 922ef7c16..7261c0941 100644 --- a/src/platform/qt/ForwarderView.cpp +++ b/src/platform/qt/ForwarderView.cpp @@ -97,7 +97,7 @@ void ForwarderView::build() { if (m_ui.baseType->currentIndex() == 2) { m_controller.setBaseFilename(m_ui.baseFilename->text()); } else { - m_controller.clearBaseFilename(); + m_controller.clearBaseFilename(); } m_controller.startBuild(m_ui.outputFilename->text()); m_ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); @@ -235,14 +235,14 @@ void ForwarderView::updateProgress() { if (m_needsForwarderKit) { m_ui.progressBar->setValue(450 + m_downloadProgress * 50); } else { - m_ui.progressBar->setValue(m_downloadProgress * 100); + m_ui.progressBar->setValue(m_downloadProgress * 100); } break; case ForwarderController::BASE: if (m_needsForwarderKit) { m_ui.progressBar->setValue(500 + m_downloadProgress * 500); } else { - m_ui.progressBar->setValue(100 + m_downloadProgress * 900); + m_ui.progressBar->setValue(100 + m_downloadProgress * 900); } break; } diff --git a/src/platform/qt/MemorySearch.cpp b/src/platform/qt/MemorySearch.cpp index 34f87fe14..7a8446887 100644 --- a/src/platform/qt/MemorySearch.cpp +++ b/src/platform/qt/MemorySearch.cpp @@ -21,7 +21,7 @@ MemorySearch::MemorySearch(std::shared_ptr controller, QWidget* mCoreMemorySearchResultsInit(&m_results, 0); connect(m_ui.search, &QPushButton::clicked, this, &MemorySearch::search); - connect(m_ui.value, &QLineEdit::returnPressed, this, &MemorySearch::search); + connect(m_ui.value, &QLineEdit::returnPressed, this, &MemorySearch::search); connect(m_ui.searchWithin, &QPushButton::clicked, this, &MemorySearch::searchWithin); connect(m_ui.refresh, &QPushButton::clicked, this, &MemorySearch::refresh); connect(m_ui.numHex, &QPushButton::clicked, this, &MemorySearch::refresh); diff --git a/src/platform/qt/SaveConverter.cpp b/src/platform/qt/SaveConverter.cpp index 974f31af0..205a7563d 100644 --- a/src/platform/qt/SaveConverter.cpp +++ b/src/platform/qt/SaveConverter.cpp @@ -86,18 +86,18 @@ void SaveConverter::refreshInputTypes() { m_validSaves.clear(); m_ui.inputType->clear(); if (m_ui.inputFile->text().isEmpty()) { - m_ui.inputType->addItem(tr("No file selected")); + m_ui.inputType->addItem(tr("No file selected")); m_ui.inputType->setEnabled(false); return; } std::shared_ptr vf = std::make_shared(m_ui.inputFile->text(), QIODevice::ReadOnly); if (!vf->isOpen()) { - m_ui.inputType->addItem(tr("Could not open file")); + m_ui.inputType->addItem(tr("Could not open file")); m_ui.inputType->setEnabled(false); return; } - + detectFromSavestate(*vf); detectFromSize(vf); detectFromHeaders(vf); @@ -108,7 +108,7 @@ void SaveConverter::refreshInputTypes() { if (m_validSaves.count()) { m_ui.inputType->setEnabled(true); } else { - m_ui.inputType->addItem(tr("No valid formats found")); + m_ui.inputType->addItem(tr("No valid formats found")); m_ui.inputType->setEnabled(false); } } @@ -127,7 +127,7 @@ void SaveConverter::refreshOutputTypes() { if (m_validOutputs.count()) { m_ui.outputType->setEnabled(true); } else { - m_ui.outputType->addItem(tr("No valid conversions found")); + m_ui.outputType->addItem(tr("No valid conversions found")); m_ui.outputType->setEnabled(false); } checkCanConvert(); @@ -494,21 +494,21 @@ SaveConverter::AnnotatedSave::operator QString() const { if (size & 0xFF) { typeFormat = QCoreApplication::translate("QGBA::SaveConverter", "%1 SRAM + RTC"); } else { - typeFormat = QCoreApplication::translate("QGBA::SaveConverter", "%1 SRAM"); + typeFormat = QCoreApplication::translate("QGBA::SaveConverter", "%1 SRAM"); } break; case GB_MBC2: if (size == 0x100) { typeFormat = QCoreApplication::translate("QGBA::SaveConverter", "packed MBC2"); } else { - typeFormat = QCoreApplication::translate("QGBA::SaveConverter", "unpacked MBC2"); + typeFormat = QCoreApplication::translate("QGBA::SaveConverter", "unpacked MBC2"); } break; case GB_MBC6: if (size == GB_SIZE_MBC6_FLASH) { typeFormat = QCoreApplication::translate("QGBA::SaveConverter", "MBC6 flash"); } else if (size > GB_SIZE_MBC6_FLASH) { - typeFormat = QCoreApplication::translate("QGBA::SaveConverter", "MBC6 combined SRAM + flash"); + typeFormat = QCoreApplication::translate("QGBA::SaveConverter", "MBC6 combined SRAM + flash"); } else { typeFormat = QCoreApplication::translate("QGBA::SaveConverter", "MBC6 SRAM"); } diff --git a/src/platform/qt/TilePainter.cpp b/src/platform/qt/TilePainter.cpp index b8c8075e6..88f7b51b1 100644 --- a/src/platform/qt/TilePainter.cpp +++ b/src/platform/qt/TilePainter.cpp @@ -81,7 +81,7 @@ void TilePainter::setTileCount(int tiles) { int w = width() / m_size; int h = (tiles + w - 1) * m_size / w; setMinimumSize(m_size, h - (h % m_size)); - } else { + } else { int w = minimumSize().width() / m_size; if (!w) { w = 1; diff --git a/src/platform/qt/TileView.cpp b/src/platform/qt/TileView.cpp index dbfc15531..6ba409a0a 100644 --- a/src/platform/qt/TileView.cpp +++ b/src/platform/qt/TileView.cpp @@ -132,7 +132,7 @@ void TileView::updateTilesGBA(bool force) { if (m_ui.tilesBg->isChecked()) { m_ui.tiles->setTileCount(1024); } else if (m_ui.tilesObj->isChecked()) { - m_ui.tiles->setTileCount(512); + m_ui.tiles->setTileCount(512); } else { m_ui.tiles->setTileCount(1536); } @@ -165,7 +165,7 @@ void TileView::updateTilesGBA(bool force) { if (m_ui.tilesBg->isChecked()) { m_ui.tiles->setTileCount(2048); } else if (m_ui.tilesObj->isChecked()) { - m_ui.tiles->setTileCount(1024); + m_ui.tiles->setTileCount(1024); } else { m_ui.tiles->setTileCount(3072); } diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 0e3d73b00..77c7695fb 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -127,7 +127,7 @@ Window::Window(CoreManager* manager, ConfigController* config, int playerId, QWi if (value.toBool()) { attachWidget(m_libraryView); } else { - attachWidget(m_screenWidget); + attachWidget(m_screenWidget); } } }, this); @@ -136,8 +136,8 @@ Window::Window(CoreManager* manager, ConfigController* config, int playerId, QWi ConfigOption* showFilenameInLibrary = m_config->addOption("showFilenameInLibrary"); showFilenameInLibrary->connect([this](const QVariant& value) { m_libraryView->setShowFilename(value.toBool()); - }, this); - m_config->updateOption("showFilenameInLibrary"); + }, this); + m_config->updateOption("showFilenameInLibrary"); ConfigOption* libraryStyle = m_config->addOption("libraryStyle"); libraryStyle->connect([this](const QVariant& value) { m_libraryView->setViewStyle(static_cast(value.toInt())); diff --git a/src/platform/qt/library/LibraryTree.cpp b/src/platform/qt/library/LibraryTree.cpp index 37d07e3e7..c9029e963 100644 --- a/src/platform/qt/library/LibraryTree.cpp +++ b/src/platform/qt/library/LibraryTree.cpp @@ -181,7 +181,7 @@ void LibraryTree::rebuildTree() { QHash pathNodes; if (m_currentStyle == LibraryStyle::STYLE_TREE) { - for (const QString& folder : m_pathNodes.keys()) { + for (const QString& folder : m_pathNodes.keys()) { QTreeWidgetItem* i = new LibraryTreeItem; pathNodes.insert(folder, i); i->setText(0, folder.section("/", -1)); diff --git a/src/platform/qt/scripting/ScriptingTextBuffer.cpp b/src/platform/qt/scripting/ScriptingTextBuffer.cpp index fe85eda0e..9a5de818d 100644 --- a/src/platform/qt/scripting/ScriptingTextBuffer.cpp +++ b/src/platform/qt/scripting/ScriptingTextBuffer.cpp @@ -107,7 +107,7 @@ void ScriptingTextBuffer::moveCursor(const QPoint& pos) { m_shim.cursor.insertBlock(); } } else { - m_shim.cursor.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, y); + m_shim.cursor.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor, y); } int x = pos.x(); diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index f6bdabea2..732d54e99 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -1143,7 +1143,7 @@ bool _luaInvoke(struct mScriptEngineContextLua* luaContext, struct mScriptFrame* if (ret == LUA_ERRRUN) { luaContext->lastError = strdup(lua_tostring(luaContext->lua, -1)); lua_pop(luaContext->lua, 1); - + _luaError(luaContext); } mScriptContextDeactivate(luaContext->d.context); diff --git a/src/script/types.c b/src/script/types.c index be58a43bf..00422d20c 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -1443,7 +1443,7 @@ static struct mScriptClassMember* _findSetter(const struct mScriptTypeClass* cls if (m) { return m; } - + switch (type->base) { case mSCRIPT_TYPE_SINT: if (type->size < 2) { diff --git a/src/tools/font-sdf.c b/src/tools/font-sdf.c index e28c34eaf..0ddcdc645 100644 --- a/src/tools/font-sdf.c +++ b/src/tools/font-sdf.c @@ -24,7 +24,7 @@ void createSdf(const struct mImage* src, struct mImage* dst, int x, int y, int w if (mImageGetPixel(src, i, j) & 0xFFFFFF) { mImageSetPixelRaw(dst, i, j, 0xFF); } else { - mImageSetPixelRaw(dst, i, j, 1); + mImageSetPixelRaw(dst, i, j, 1); } } } diff --git a/src/util/audio-resampler.c b/src/util/audio-resampler.c index 3b1848190..554f4744b 100644 --- a/src/util/audio-resampler.c +++ b/src/util/audio-resampler.c @@ -60,7 +60,7 @@ void mAudioResamplerSetSource(struct mAudioResampler* resampler, struct mAudioBu void mAudioResamplerSetDestination(struct mAudioResampler* resampler, struct mAudioBuffer* destination, double rate) { resampler->destination = destination; - resampler->destRate = rate; + resampler->destRate = rate; } size_t mAudioResamplerProcess(struct mAudioResampler* resampler) { diff --git a/src/util/crc32.c b/src/util/crc32.c index 24f611111..7c1fcda55 100644 --- a/src/util/crc32.c +++ b/src/util/crc32.c @@ -103,7 +103,7 @@ uint32_t doCrc32(const void* buf, size_t size) { #ifndef HAVE_CRC32 uint32_t crc32(uint32_t crc, const void* buf, size_t size) { const uint8_t* p = buf; - + crc = ~crc; for (size_t i = 0; i < size; ++i) { crc = crc32Table[(crc ^ p[i]) & 0xFF] ^ (crc >> 8); diff --git a/src/util/hash.c b/src/util/hash.c index ed070e86c..1ac0b56e2 100644 --- a/src/util/hash.c +++ b/src/util/hash.c @@ -69,9 +69,9 @@ uint32_t hash32(const void* key, size_t len, uint32_t seed) { k1 *= c1; k1 = ROTL32(k1, 15); k1 *= c2; - + h1 ^= k1; - h1 = ROTL32(h1, 13); + h1 = ROTL32(h1, 13); h1 = h1 * 5 + 0xe6546b64; } @@ -105,4 +105,4 @@ uint32_t hash32(const void* key, size_t len, uint32_t seed) { h1 = fmix32(h1); return h1; -} +} diff --git a/src/util/sfo.c b/src/util/sfo.c index 97c4e3853..47d5f2702 100644 --- a/src/util/sfo.c +++ b/src/util/sfo.c @@ -91,7 +91,7 @@ bool SfoAddStrValue(struct Table* sfo, const char* name, const char* value) { HashTableInsert(sfo, name, entry); } entry->type = PSF_TYPE_STR; - entry->data.str = value; + entry->data.str = value; return true; } From c71cd4a81b8ad0d09084d28d56d1ebaadcb7d468 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 1 Oct 2024 02:02:18 -0700 Subject: [PATCH 050/114] Qt: Improve --script help --- src/platform/qt/ConfigController.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/platform/qt/ConfigController.cpp b/src/platform/qt/ConfigController.cpp index ccec60432..819b2479d 100644 --- a/src/platform/qt/ConfigController.cpp +++ b/src/platform/qt/ConfigController.cpp @@ -154,11 +154,12 @@ ConfigController::ConfigController(QObject* parent) mSubParserGraphicsInit(&m_subparsers[0], &m_graphicsOpts); m_subparsers[1].usage = "Frontend options:\n" - " --ecard FILE Scan an e-Reader card in the first loaded game\n" - " Can be passed multiple times for multiple cards\n" - " --mb FILE Boot a multiboot image with FILE inserted into the ROM slot" + " --ecard FILE Scan an e-Reader card in the first loaded game\n" + " Can be passed multiple times for multiple cards\n" + " --mb FILE Boot a multiboot image with FILE inserted into the ROM slot" #ifdef ENABLE_SCRIPTING - "\n --script FILE Script file to load on start" + "\n --script FILE Script file to load on start\n" + " Can be passed multiple times\n" #endif ; From 541ed9606cc3a53879f81f473f88a8d5b96fe451 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 4 Oct 2024 17:13:43 -0700 Subject: [PATCH 051/114] Core: Check for null when autoloading/saving cheats --- src/core/cheats.c | 3 +++ src/core/core.c | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/core/cheats.c b/src/core/cheats.c index 1ffe91adf..2fc97c3ac 100644 --- a/src/core/cheats.c +++ b/src/core/cheats.c @@ -627,6 +627,9 @@ void mCheatAutosave(struct mCheatDevice* device) { if (!device->autosave) { return; } + if (!device->p->dirs.cheats) { + return; + } struct VFile* vf = mDirectorySetOpenSuffix(&device->p->dirs, device->p->dirs.cheats, ".cheats", O_WRONLY | O_CREAT | O_TRUNC); if (!vf) { return; diff --git a/src/core/core.c b/src/core/core.c index 20f9fdefc..789656ea4 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -245,14 +245,16 @@ bool mCoreAutoloadPatch(struct mCore* core) { } bool mCoreAutoloadCheats(struct mCore* core) { - bool success = true; + bool success = !!core->dirs.cheats; int cheatAuto; - if (!mCoreConfigGetIntValue(&core->config, "cheatAutoload", &cheatAuto) || cheatAuto) { + if (success && (!mCoreConfigGetIntValue(&core->config, "cheatAutoload", &cheatAuto) || cheatAuto)) { struct VFile* vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.cheats, ".cheats", O_RDONLY); if (vf) { struct mCheatDevice* device = core->cheatDevice(core); success = mCheatParseFile(device, vf); vf->close(vf); + } else { + success = false; } } if (!mCoreConfigGetIntValue(&core->config, "cheatAutosave", &cheatAuto) || cheatAuto) { From 5fb7c5e3ee85343c2e9c397a3644fe568b6373f6 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 4 Oct 2024 17:17:47 -0700 Subject: [PATCH 052/114] Core: Add sanity check for mDirectorySetOpenSuffix --- src/core/directories.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/directories.c b/src/core/directories.c index c4551b6c7..e0748f829 100644 --- a/src/core/directories.c +++ b/src/core/directories.c @@ -110,6 +110,9 @@ struct VFile* mDirectorySetOpenPath(struct mDirectorySet* dirs, const char* path } struct VFile* mDirectorySetOpenSuffix(struct mDirectorySet* dirs, struct VDir* dir, const char* suffix, int mode) { + if (!dir) { + return NULL; + } char name[PATH_MAX + 1] = ""; snprintf(name, sizeof(name) - 1, "%s%s", dirs->baseName, suffix); return dir->openFile(dir, name, mode); From 7950279a09ce7f41a56a584a8fd1b6231f8074b2 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 4 Oct 2024 22:20:21 -0700 Subject: [PATCH 053/114] Core: Split out semantics of rumble integrator init/reset (fixes #3309) --- include/mgba/core/interface.h | 1 + src/core/interface.c | 16 ++++++++++------ src/platform/libretro/libretro.c | 2 +- src/platform/switch/main.c | 6 ++---- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/include/mgba/core/interface.h b/include/mgba/core/interface.h index 597387753..033047cac 100644 --- a/include/mgba/core/interface.h +++ b/include/mgba/core/interface.h @@ -133,6 +133,7 @@ struct mRumbleIntegrator { }; void mRumbleIntegratorInit(struct mRumbleIntegrator*); +void mRumbleIntegratorReset(struct mRumbleIntegrator*); struct mCoreChannelInfo { size_t id; diff --git a/src/core/interface.c b/src/core/interface.c index 10d3433ab..5771d80e1 100644 --- a/src/core/interface.c +++ b/src/core/interface.c @@ -110,14 +110,14 @@ void mRTCGenericSourceInit(struct mRTCGenericSource* rtc, struct mCore* core) { rtc->d.deserialize = _rtcGenericDeserialize; } -static void mRumbleIntegratorReset(struct mRumble* rumble, bool enable) { +static void _mRumbleIntegratorReset(struct mRumble* rumble, bool enable) { struct mRumbleIntegrator* integrator = (struct mRumbleIntegrator*) rumble; integrator->state = enable; integrator->timeOn = 0; integrator->totalTime = 0; } -static void mRumbleIntegratorSetRumble(struct mRumble* rumble, bool enable, uint32_t sinceLast) { +static void _mRumbleIntegratorSetRumble(struct mRumble* rumble, bool enable, uint32_t sinceLast) { struct mRumbleIntegrator* integrator = (struct mRumbleIntegrator*) rumble; if (integrator->state) { @@ -127,7 +127,7 @@ static void mRumbleIntegratorSetRumble(struct mRumble* rumble, bool enable, uint integrator->state = enable; } -static void mRumbleIntegratorIntegrate(struct mRumble* rumble, uint32_t period) { +static void _mRumbleIntegratorIntegrate(struct mRumble* rumble, uint32_t period) { if (!period) { return; } @@ -144,7 +144,11 @@ static void mRumbleIntegratorIntegrate(struct mRumble* rumble, uint32_t period) void mRumbleIntegratorInit(struct mRumbleIntegrator* integrator) { memset(integrator, 0, sizeof(*integrator)); - integrator->d.reset = mRumbleIntegratorReset; - integrator->d.setRumble = mRumbleIntegratorSetRumble; - integrator->d.integrate = mRumbleIntegratorIntegrate; + integrator->d.reset = _mRumbleIntegratorReset; + integrator->d.setRumble = _mRumbleIntegratorSetRumble; + integrator->d.integrate = _mRumbleIntegratorIntegrate; +} + +void mRumbleIntegratorReset(struct mRumbleIntegrator* integrator) { + _mRumbleIntegratorReset(&integrator->d, false); } diff --git a/src/platform/libretro/libretro.c b/src/platform/libretro/libretro.c index 232c4e6eb..d2a871546 100644 --- a/src/platform/libretro/libretro.c +++ b/src/platform/libretro/libretro.c @@ -834,7 +834,7 @@ static void _setupMaps(struct mCore* core) { void retro_reset(void) { core->reset(core); - mRumbleIntegratorInit(&rumble); + mRumbleIntegratorReset(&rumble); _setupMaps(core); } diff --git a/src/platform/switch/main.c b/src/platform/switch/main.c index 1f61cc279..6cd7bc3b8 100644 --- a/src/platform/switch/main.c +++ b/src/platform/switch/main.c @@ -296,7 +296,7 @@ static void _setup(struct mGUIRunner* runner) { } _updateRenderer(runner, fakeBool); - mRumbleIntegratorInit(&rumble.d); + mRumbleIntegratorReset(&rumble.d); runner->core->setPeripheral(runner->core, mPERIPH_RUMBLE, &rumble.d.d); runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation); runner->core->setAVStream(runner->core, &stream); @@ -363,13 +363,10 @@ static void _gameLoaded(struct mGUIRunner* runner) { runner->core->reloadConfigOption(runner->core, "videoScale", &runner->config); } } - - mRumbleIntegratorInit(&rumble.d); } static void _gameUnloaded(struct mGUIRunner* runner) { UNUSED(runner); - mRumbleIntegratorInit(&rumble.d); HidVibrationValue values[4]; memcpy(&values[0], &vibrationStop, sizeof(rumble.value)); memcpy(&values[1], &vibrationStop, sizeof(rumble.value)); @@ -812,6 +809,7 @@ static void hidSetup(void) { padConfigureInput(1, HidNpadStyleSet_NpadStandard); padInitializeDefault(&pad); + mRumbleIntegratorInit(&rumble.d); rumble.d.setRumble = _setRumble; rumble.value.freq_low = 120.0; rumble.value.freq_high = 180.0; From 0e52f7054fae93794706611b71a9190d726052b0 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 6 Oct 2024 03:20:49 -0700 Subject: [PATCH 054/114] Res: Add demo input display script --- res/scripts/input-display.lua | 122 ++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 res/scripts/input-display.lua diff --git a/res/scripts/input-display.lua b/res/scripts/input-display.lua new file mode 100644 index 000000000..e3db0340c --- /dev/null +++ b/res/scripts/input-display.lua @@ -0,0 +1,122 @@ +input_display = { + anchor = "top", + offset = { + x = 0, + y = 0, + } +} + +local state = { + drawButton = { + [0] = function(state) -- A + state.painter:drawCircle(27, 6, 4) + end, + [1] = function(state) -- B + state.painter:drawCircle(23, 8, 4) + end, + [2] = function(state) -- Select + state.painter:drawCircle(13, 11, 3) + end, + [3] = function(state) -- Start + state.painter:drawCircle(18, 11, 3) + end, + [4] = function(state) -- Right + state.painter:drawRectangle(9, 7, 4, 3) + end, + [5] = function(state) -- Left + state.painter:drawRectangle(2, 7, 4, 3) + end, + [6] = function(state) -- Up + state.painter:drawRectangle(6, 3, 3, 4) + end, + [7] = function(state) -- Down + state.painter:drawRectangle(6, 10, 3, 4) + end, + [8] = function(state) -- R + state.painter:drawRectangle(28, 0, 4, 3) + end, + [9] = function(state) -- L + state.painter:drawRectangle(0, 0, 4, 3) + end + }, + maxKey = { + [C.PLATFORM.GBA] = 9, + [C.PLATFORM.GB] = 7, + } +} +state.overlay = canvas:newLayer(32, 16) +state.painter = image.newPainter(state.overlay.image) +state.painter:setBlend(false) +state.painter:setFill(true) + +function state.update() + local keys = util.expandBitmask(emu:getKeys()) + local maxKey = state.maxKey[emu:platform()] + + for key = 0, maxKey do + if emu:getKey(key) ~= 0 then + state.painter:setFillColor(0x80FFFFFF) + else + state.painter:setFillColor(0x40404040) + end + state.drawButton[key](state) + end + state.overlay:update() +end + +function state.reset() + local endX = canvas:screenWidth() - 32 + local endY = canvas:screenHeight() - 16 + + local anchors = { + topLeft = { + x = 0, + y = 0 + }, + top = { + x = endX / 2, + y = 0 + }, + topRight = { + x = endX, + y = 0 + }, + left = { + x = 0, + y = endY / 2 + }, + center = { + x = endX / 2, + y = endY / 2 + }, + right = { + x = endX, + y = endY / 2 + }, + bottomLeft = { + x = 0, + y = endY + }, + bottom = { + x = endX / 2, + y = endY + }, + bottomRight = { + x = endX, + y = endY + }, + } + + local pos = anchors[input_display.anchor]; + pos.x = pos.x + input_display.offset.x; + pos.y = pos.y + input_display.offset.y; + + state.overlay:setPosition(pos.x, pos.y); + state.painter:setFillColor(0x40808080) + state.painter:drawRectangle(0, 0, 32, 16) + state.overlay:update() +end + +state.reset() +callbacks:add("frame", state.update) +callbacks:add("start", state.reset) From 58510ca2506679b31618d80dd75fc6f00c043959 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 8 Oct 2024 03:12:51 -0700 Subject: [PATCH 055/114] Util: Add MD5 implementation and consistency tests --- include/mgba-util/md5.h | 34 ++++++ src/util/CMakeLists.txt | 2 + src/util/md5.c | 228 ++++++++++++++++++++++++++++++++++++++++ src/util/test/hash.c | 130 +++++++++++++++++++++++ 4 files changed, 394 insertions(+) create mode 100644 include/mgba-util/md5.h create mode 100644 src/util/md5.c create mode 100644 src/util/test/hash.c diff --git a/include/mgba-util/md5.h b/include/mgba-util/md5.h new file mode 100644 index 000000000..9a2c58145 --- /dev/null +++ b/include/mgba-util/md5.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2013-2014 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * 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/. + * + * Based on https://github.com/Zunawe/md5-c + * Originally released under the Unlicense */ +#ifndef MD5_H +#define MD5_H + +#include + +CXX_GUARD_START + +struct MD5Context { + size_t size; // Size of input in bytes + uint32_t buffer[4]; // Current accumulation of hash + uint8_t input[0x40]; // Input to be used in the next step + uint8_t digest[0x10]; // Result of algorithm +}; + +void md5Init(struct MD5Context* ctx); +void md5Update(struct MD5Context* ctx, const void* input, size_t len); +void md5Finalize(struct MD5Context* ctx); + +void md5Buffer(const void* input, size_t len, uint8_t* result); + +struct VFile; +bool md5File(struct VFile* vf, uint8_t* result); + +CXX_GUARD_END + +#endif diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 66cd6ca47..2f4ba2dcd 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -6,6 +6,7 @@ set(BASE_SOURCE_FILES formatting.c gbk-table.c hash.c + md5.c string.c table.c vector.c @@ -41,6 +42,7 @@ set(TEST_FILES test/circle-buffer.c test/color.c test/geometry.c + test/hash.c test/image.c test/sfo.c test/string-parser.c diff --git a/src/util/md5.c b/src/util/md5.c new file mode 100644 index 000000000..08e06fb1d --- /dev/null +++ b/src/util/md5.c @@ -0,0 +1,228 @@ +/* Copyright (c) 2013-2014 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * 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/. + * + * Based on https://github.com/Zunawe/md5-c + * Originally released under the Unlicense + * + * Derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm + */ +#include + +#include + +/* + * Constants defined by the MD5 algorithm + */ +#define A 0x67452301 +#define B 0xEFCDAB89 +#define C 0x98BADCFE +#define D 0x10325476 + +static const uint32_t S[] = { 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, + 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, + 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 }; + +static const uint32_t K[] = { 0xD76AA478, 0xE8C7B756, 0x242070DB, 0xC1BDCEEE, + 0xF57C0FAF, 0x4787C62A, 0xA8304613, 0xFD469501, + 0x698098D8, 0x8B44F7AF, 0xFFFF5BB1, 0x895CD7BE, + 0x6B901122, 0xFD987193, 0xA679438E, 0x49B40821, + 0xF61E2562, 0xC040B340, 0x265E5A51, 0xE9B6C7AA, + 0xD62F105D, 0x02441453, 0xD8A1E681, 0xE7D3FBC8, + 0x21E1CDE6, 0xC33707D6, 0xF4D50D87, 0x455A14ED, + 0xA9E3E905, 0xFCEFA3F8, 0x676F02D9, 0x8D2A4C8A, + 0xFFFA3942, 0x8771F681, 0x6D9D6122, 0xFDE5380C, + 0xA4BEEA44, 0x4BDECFA9, 0xF6BB4B60, 0xBEBFBC70, + 0x289B7EC6, 0xEAA127FA, 0xD4EF3085, 0x04881D05, + 0xD9D4D039, 0xE6DB99E5, 0x1FA27CF8, 0xC4AC5665, + 0xF4292244, 0x432AFF97, 0xAB9423A7, 0xFC93A039, + 0x655B59C3, 0x8F0CCC92, 0xFFEFF47D, 0x85845DD1, + 0x6FA87E4F, 0xFE2CE6E0, 0xA3014314, 0x4E0811A1, + 0xF7537E82, 0xBD3AF235, 0x2AD7D2BB, 0xEB86D391 }; + +/* + * Padding used to make the size (in bits) of the input congruent to 448 mod 512 + */ +static const uint8_t PADDING[] = { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +/* + * Bit-manipulation functions defined by the MD5 algorithm + */ +#define F(X, Y, Z) (((X) & (Y)) | (~(X) & (Z))) +#define G(X, Y, Z) (((X) & (Z)) | ((Y) & ~(Z))) +#define H(X, Y, Z) ((X) ^ (Y) ^ (Z)) +#define I(X, Y, Z) ((Y) ^ ((X) | ~(Z))) + +/* + * Rotates a 32-bit word left by n bits + */ +static uint32_t rotateLeft(uint32_t x, uint32_t n) { + return (x << n) | (x >> (32 - n)); +} + +/* + * Step on 512 bits of input with the main MD5 algorithm. + */ +static void md5Step(uint32_t* buffer, const uint32_t* input) { + uint32_t AA = buffer[0]; + uint32_t BB = buffer[1]; + uint32_t CC = buffer[2]; + uint32_t DD = buffer[3]; + + uint32_t E; + + unsigned j; + + for (unsigned i = 0; i < 64; ++i) { + switch (i / 16) { + case 0: + E = F(BB, CC, DD); + j = i; + break; + case 1: + E = G(BB, CC, DD); + j = ((i * 5) + 1) & 0xF; + break; + case 2: + E = H(BB, CC, DD); + j = ((i * 3) + 5) & 0xF; + break; + default: + E = I(BB, CC, DD); + j = (i * 7) & 0xF; + break; + } + + uint32_t temp = DD; + DD = CC; + CC = BB; + BB += rotateLeft(AA + E + K[i] + input[j], S[i]); + AA = temp; + } + + buffer[0] += AA; + buffer[1] += BB; + buffer[2] += CC; + buffer[3] += DD; +} + +/* + * Initialize a context + */ +void md5Init(struct MD5Context* ctx) { + memset(ctx, 0, sizeof(*ctx)); + + ctx->buffer[0] = A; + ctx->buffer[1] = B; + ctx->buffer[2] = C; + ctx->buffer[3] = D; +} + +/* + * Add some amount of input to the context + * + * If the input fills out a block of 512 bits, apply the algorithm (md5Step) + * and save the result in the buffer. Also updates the overall size. + */ +void md5Update(struct MD5Context* ctx, const void* input, size_t len) { + uint32_t buffer[16]; + unsigned offset = ctx->size & 0x3F; + const uint8_t* inputBuffer = input; + ctx->size += len; + + // Copy each byte in input_buffer into the next space in our context input + unsigned i; + for (i = 0; i < len; ++i) { + ctx->input[offset] = inputBuffer[i]; + + // If we've filled our context input, copy it into our local array input + // then reset the offset to 0 and fill in a new buffer. + // Every time we fill out a chunk, we run it through the algorithm + // to enable some back and forth between cpu and i/o + if (offset < 0x3F) { + ++offset; + continue; + } + + unsigned j; + for (j = 0; j < 16; ++j) { + // Convert to little-endian + // The local variable `input` our 512-bit chunk separated into 32-bit words + // we can use in calculations + LOAD_32LE(buffer[j], j * 4, ctx->input); + } + md5Step(ctx->buffer, buffer); + offset = 0; + } +} + +/* + * Pad the current input to get to 448 bits, append the size in bits to the very end, + * and save the result of the final iteration into digest. + */ +void md5Finalize(struct MD5Context* ctx) { + uint32_t input[16]; + int offset = ctx->size & 0x3F; + unsigned paddingLength = offset < 56 ? 56 - offset : (56 + 64) - offset; + + // Fill in the padding and undo the changes to size that resulted from the update + md5Update(ctx, PADDING, paddingLength); + ctx->size -= paddingLength; + + // Do a final update (internal to this function) + // Last two 32-bit words are the two halves of the size (converted from bytes to bits) + unsigned j; + for (j = 0; j < 14; ++j) { + LOAD_32LE(input[j], j * 4, ctx->input); + } + input[14] = (uint32_t) (ctx->size * 8); + input[15] = (uint32_t) ((ctx->size * 8ULL) >> 32); + + md5Step(ctx->buffer, input); + + // Move the result into digest (convert from little-endian) + unsigned i; + for (i = 0; i < 4; ++i) { + STORE_32LE(ctx->buffer[i], i * 4, ctx->digest); + } +} + +void md5Buffer(const void* input, size_t len, uint8_t* result) { + struct MD5Context ctx; + md5Init(&ctx); + md5Update(&ctx, input, len); + md5Finalize(&ctx); + memcpy(result, ctx.digest, sizeof(ctx.digest)); +} + +bool md5File(struct VFile* vf, uint8_t* result) { + struct MD5Context ctx; + uint8_t buffer[2048]; + md5Init(&ctx); + + ssize_t read; + ssize_t position = vf->seek(vf, 0, SEEK_CUR); + if (vf->seek(vf, 0, SEEK_SET) < 0) { + return false; + } + while ((read = vf->read(vf, buffer, sizeof(buffer))) > 0) { + md5Update(&ctx, buffer, read); + } + vf->seek(vf, position, SEEK_SET); + if (read < 0) { + return false; + } + md5Finalize(&ctx); + memcpy(result, ctx.digest, sizeof(ctx.digest)); + return true; +} diff --git a/src/util/test/hash.c b/src/util/test/hash.c new file mode 100644 index 000000000..6d014435b --- /dev/null +++ b/src/util/test/hash.c @@ -0,0 +1,130 @@ +/* Copyright (c) 2013-2022 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * 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 "util/test/suite.h" + +#include +#include + +M_TEST_DEFINE(emptyCrc32) { + uint8_t buffer[1] = {0}; + assert_int_equal(doCrc32(buffer, 0), 0); +} + +M_TEST_DEFINE(newlineCrc32) { +uint8_t buffer[1] = { '\n' }; + assert_int_equal(doCrc32(buffer, 1), 0x32D70693); +} + +M_TEST_DEFINE(helloWorldCrc32) { + const char* buffer = "Hello, world!"; + assert_int_equal(doCrc32(buffer, strlen(buffer)), 0xEBE6C6E6); +} + +#ifndef HAVE_CRC32 +M_TEST_DEFINE(stagedCrc32) { + uint8_t buffer[1] = { '\n\n' }; + assert_int_equal(crc32(0, buffer, 1), 0x32D70693); + assert_int_equal(crc32(0, buffer, 2), 0x09304EBD); + assert_int_equal(crc32(0x32D70693, buffer, 1), 0x09304EBD); +} +#endif + +M_TEST_DEFINE(emptyMd5) { + uint8_t buffer[1] = {0}; + uint8_t digest[0x10] = {0}; + md5Buffer(buffer, 0, digest); + assert_memory_equal(digest, ((uint8_t[]) { + 0xD4, 0x1D, 0x8C, 0xD9, 0x8F, 0x00, 0xB2, 0x04, + 0xE9, 0x80, 0x09, 0x98, 0xEC, 0xF8, 0x42, 0x7E + }), 16); +} + +M_TEST_DEFINE(newlineMd5) { + uint8_t buffer[1] = { '\n' }; + uint8_t digest[0x10] = {0}; + md5Buffer(buffer, 1, digest); + assert_memory_equal(digest, ((uint8_t[]) { + 0x68, 0xB3, 0x29, 0xDA, 0x98, 0x93, 0xE3, 0x40, + 0x99, 0xC7, 0xD8, 0xAD, 0x5C, 0xB9, 0xC9, 0x40 + }), 16); +} + +M_TEST_DEFINE(fullBlockMd5) { + uint8_t buffer[56] = { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + }; + uint8_t digest[0x10] = {0}; + md5Buffer(buffer, 56, digest); + assert_memory_equal(digest, ((uint8_t[]) { + 0xA3, 0x31, 0x42, 0x53, 0x78, 0x54, 0xFE, 0xE2, + 0xAF, 0xD6, 0xCF, 0xF4, 0xC5, 0xA1, 0xDD, 0x39 + }), 16); +} + +M_TEST_DEFINE(overflowBlockMd5) { + uint8_t buffer[57] = { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x0a, + }; + uint8_t digest[0x10] = {0}; + md5Buffer(buffer, 57, digest); + assert_memory_equal(digest, ((uint8_t[]) { + 0xBA, 0x49, 0x77, 0x29, 0x15, 0x5B, 0x13, 0x5D, + 0xBA, 0x27, 0xF3, 0xD8, 0x53, 0xCF, 0xD2, 0x1A + }), 16); +} + +M_TEST_DEFINE(twoBlockMd5) { + uint8_t buffer[120] = { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + }; + uint8_t digest[0x10] = {0}; + md5Buffer(buffer, 120, digest); + assert_memory_equal(digest, ((uint8_t[]) { + 0xB5, 0x68, 0xA7, 0x7E, 0xD4, 0xC2, 0x39, 0xFB, + 0x4B, 0x74, 0xD7, 0x5B, 0xDB, 0xFD, 0x94, 0x93 + }), 16); +} + +M_TEST_SUITE_DEFINE(Hashes, + cmocka_unit_test(emptyCrc32), + cmocka_unit_test(newlineCrc32), + cmocka_unit_test(helloWorldCrc32), +#ifndef HAVE_CRC32 + cmocka_unit_test(stagedCrc32), +#endif + cmocka_unit_test(emptyMd5), + cmocka_unit_test(newlineMd5), + cmocka_unit_test(fullBlockMd5), + cmocka_unit_test(overflowBlockMd5), + cmocka_unit_test(twoBlockMd5), +) From 2ccfde0f33ff372f644d40aa4e8bf6d65fa677f8 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 8 Oct 2024 04:09:26 -0700 Subject: [PATCH 056/114] Core: Add MD5 hashing for ROMs --- CHANGES | 1 + include/mgba/core/core.h | 1 + src/gb/core.c | 10 ++ src/gba/core.c | 14 ++ src/platform/qt/ROMInfo.cpp | 6 + src/platform/qt/ROMInfo.ui | 300 ++++++++++++++++++++---------------- 6 files changed, 198 insertions(+), 134 deletions(-) diff --git a/CHANGES b/CHANGES index f0fe0a474..9a538caee 100644 --- a/CHANGES +++ b/CHANGES @@ -41,6 +41,7 @@ Other fixes: Misc: - Core: Handle relative paths for saves, screenshots, etc consistently (fixes mgba.io/i/2826) - Core: Improve rumble emulation by averaging state over entire frame (fixes mgba.io/i/3232) + - Core: Add MD5 hashing for ROMs - GB: Prevent incompatible BIOSes from being used on differing models - GB Serialize: Add missing savestate support for MBC6 and NT (newer) - GBA: Improve detection of valid ELF ROMs diff --git a/include/mgba/core/core.h b/include/mgba/core/core.h index e8c5df7bc..94dfb1114 100644 --- a/include/mgba/core/core.h +++ b/include/mgba/core/core.h @@ -30,6 +30,7 @@ enum mPlatform { enum mCoreChecksumType { mCHECKSUM_CRC32, + mCHECKSUM_MD5, }; struct mAudioBuffer; diff --git a/src/gb/core.c b/src/gb/core.c index e67fe4232..b97e1ee25 100644 --- a/src/gb/core.c +++ b/src/gb/core.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -529,6 +530,15 @@ static void _GBCoreChecksum(const struct mCore* core, void* data, enum mCoreChec case mCHECKSUM_CRC32: memcpy(data, &gb->romCrc32, sizeof(gb->romCrc32)); break; + case mCHECKSUM_MD5: + if (gb->romVf) { + md5File(gb->romVf, data); + } else if (gb->memory.rom && gb->isPristine) { + md5Buffer(gb->memory.rom, gb->pristineRomSize, data); + } else { + md5Buffer("", 0, data); + } + break; } return; } diff --git a/src/gba/core.c b/src/gba/core.c index 67550bd7a..e81e66dac 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -30,6 +30,7 @@ #ifdef USE_ELF #include #endif +#include #include #include #include @@ -676,6 +677,19 @@ static void _GBACoreChecksum(const struct mCore* core, void* data, enum mCoreChe case mCHECKSUM_CRC32: memcpy(data, &gba->romCrc32, sizeof(gba->romCrc32)); break; + case mCHECKSUM_MD5: + if (gba->romVf) { + md5File(gba->romVf, data); + } else if (gba->mbVf) { + md5File(gba->mbVf, data); + } else if (gba->memory.rom && gba->isPristine) { + md5Buffer(gba->memory.rom, gba->pristineRomSize, data); + } else if (gba->memory.rom) { + md5Buffer(gba->memory.rom, gba->memory.romSize, data); + } else { + md5Buffer("", 0, data); + } + break; } return; } diff --git a/src/platform/qt/ROMInfo.cpp b/src/platform/qt/ROMInfo.cpp index 9f2537439..25aafeda8 100644 --- a/src/platform/qt/ROMInfo.cpp +++ b/src/platform/qt/ROMInfo.cpp @@ -24,6 +24,7 @@ ROMInfo::ROMInfo(std::shared_ptr controller, QWidget* parent) const NoIntroDB* db = GBAApp::app()->gameDB(); #endif uint32_t crc32 = 0; + uint8_t md5[16]{}; CoreController::Interrupter interrupter(controller); mCore* core = controller->thread()->core; @@ -39,6 +40,7 @@ ROMInfo::ROMInfo(std::shared_ptr controller, QWidget* parent) m_ui.version->setText(QString::number(info.version)); core->checksum(core, &crc32, mCHECKSUM_CRC32); + core->checksum(core, &md5, mCHECKSUM_MD5); m_ui.size->setText(QString::number(core->romSize(core)) + tr(" bytes")); @@ -63,6 +65,10 @@ ROMInfo::ROMInfo(std::shared_ptr controller, QWidget* parent) m_ui.name->setText(tr("(unknown)")); } + m_ui.md5->setText(QString::asprintf("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + md5[0x0], md5[0x1], md5[0x2], md5[0x3], md5[0x4], md5[0x5], md5[0x6], md5[0x7], + md5[0x8], md5[0x9], md5[0xA], md5[0xB], md5[0xC], md5[0xD], md5[0xE], md5[0xF])); + QString savePath = controller->savePath(); if (!savePath.isEmpty()) { m_ui.savefile->setText(savePath); diff --git a/src/platform/qt/ROMInfo.ui b/src/platform/qt/ROMInfo.ui index 655d4810f..a4a179ca8 100644 --- a/src/platform/qt/ROMInfo.ui +++ b/src/platform/qt/ROMInfo.ui @@ -6,154 +6,186 @@ 0 0 - 178 - 198 + 180 + 298 ROM Info - + QLayout::SetFixedSize - - QFormLayout::FieldsStayAtSizeHint - - - - Game name: - - - - - - - {NAME} - - - true - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + File information + + + + + Game name: + + + + + + + {NAME} + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + File size: + + + + + + + {SIZE} + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + CRC32: + + + + + + + {CRC} + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + MD5 + + + + + + + {MD5} + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Save file: + + + + + + + {SAVEFILE} + + + + - - - Internal name: - - - - - - - {TITLE} - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - Game ID: - - - - - - - {ID} - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - Maker Code: - - - - - - - {MAKER} - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - Revision: - - - - - - - {VERSION} - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - File size: - - - - - - - {SIZE} - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - CRC32: - - - - - - - {CRC} - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - Save file: - - - - - - - {SAVEFILE} + + + ROM header + + + + + Internal name: + + + + + + + {TITLE} + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Game ID: + + + + + + + {ID} + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Maker Code: + + + + + + + {MAKER} + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Revision: + + + + + + + {VERSION} + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + From 7e474db93a62c148299616bb315e6c65afc8f00f Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 8 Oct 2024 04:32:43 -0700 Subject: [PATCH 057/114] Qt: Fix some new SIO cleanup issues --- src/platform/qt/MultiplayerController.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/platform/qt/MultiplayerController.cpp b/src/platform/qt/MultiplayerController.cpp index 3b2146f7a..e95498fad 100644 --- a/src/platform/qt/MultiplayerController.cpp +++ b/src/platform/qt/MultiplayerController.cpp @@ -192,6 +192,9 @@ MultiplayerController::MultiplayerController() { MultiplayerController::~MultiplayerController() { mLockstepDeinit(&m_lockstep); + if (m_platform == mPLATFORM_GBA) { + GBASIOLockstepCoordinatorDeinit(&m_gbaCoordinator); + } } bool MultiplayerController::attachGame(CoreController* controller) { @@ -376,7 +379,7 @@ void MultiplayerController::detachGame(CoreController* controller) { if (p.attached) { GBASIOLockstepCoordinatorDetach(&m_gbaCoordinator, p.node.gba); } - delete p.node.gba->user; + delete reinterpret_cast(p.node.gba->user); delete p.node.gba; break; } @@ -417,6 +420,9 @@ void MultiplayerController::detachGame(CoreController* controller) { m_pids.remove(pid); if (m_pids.size() == 0) { + if (m_platform == mPLATFORM_GBA) { + GBASIOLockstepCoordinatorDeinit(&m_gbaCoordinator); + } m_platform = mPLATFORM_NONE; } else { fixOrder(); From eaf45b9ab8d2a5902a80fbea5a9210fcda08a7a9 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 8 Oct 2024 04:34:30 -0700 Subject: [PATCH 058/114] GB, GBA: Clean up some corner cases with ROM fd closing --- src/gb/gb.c | 4 +--- src/gb/memory.c | 2 -- src/gba/core.c | 3 +++ src/gba/gba.c | 6 ++---- src/gba/memory.c | 2 -- 5 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/gb/gb.c b/src/gb/gb.c index fa0d6482f..875cbf86e 100644 --- a/src/gb/gb.c +++ b/src/gb/gb.c @@ -483,12 +483,10 @@ void GBApplyPatch(struct GB* gb, struct Patch* patch) { mappedMemoryFree(newRom, GB_SIZE_CART_MAX); return; } - if (gb->romVf) { + if (gb->romVf && gb->isPristine) { #ifndef FIXED_ROM_BUFFER gb->romVf->unmap(gb->romVf, gb->memory.rom, gb->pristineRomSize); #endif - gb->romVf->close(gb->romVf); - gb->romVf = NULL; } gb->isPristine = false; if (gb->memory.romBase == gb->memory.rom) { diff --git a/src/gb/memory.c b/src/gb/memory.c index e41050784..dccd91e08 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -1017,8 +1017,6 @@ void _pristineCow(struct GB* gb) { } if (gb->romVf) { gb->romVf->unmap(gb->romVf, gb->memory.rom, gb->memory.romSize); - gb->romVf->close(gb->romVf); - gb->romVf = NULL; } gb->memory.rom = newRom; GBMBCSwitchBank(gb, gb->memory.currentBank); diff --git a/src/gba/core.c b/src/gba/core.c index e81e66dac..1d3fffb9e 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -668,6 +668,9 @@ static size_t _GBACoreROMSize(const struct mCore* core) { if (gba->romVf) { return gba->romVf->size(gba->romVf); } + if (gba->mbVf) { + return gba->mbVf->size(gba->mbVf); + } return gba->pristineRomSize; } diff --git a/src/gba/gba.c b/src/gba/gba.c index 187d83373..6b833838e 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -557,16 +557,14 @@ void GBAApplyPatch(struct GBA* gba, struct Patch* patch) { mappedMemoryFree(newRom, GBA_SIZE_ROM0); return; } - if (gba->romVf) { + if (gba->memory.rom) { #ifndef FIXED_ROM_BUFFER if (!gba->isPristine) { - mappedMemoryFree(gba->memory.rom, GBA_SIZE_ROM0); + mappedMemoryFree(gba->memory.rom, gba->memory.romSize); } else { gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->pristineRomSize); } #endif - gba->romVf->close(gba->romVf); - gba->romVf = NULL; } gba->isPristine = false; gba->memory.rom = newRom; diff --git a/src/gba/memory.c b/src/gba/memory.c index 09d3e7a74..3243fc5a1 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -1894,8 +1894,6 @@ void _pristineCow(struct GBA* gba) { } if (gba->romVf) { gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->memory.romSize); - gba->romVf->close(gba->romVf); - gba->romVf = NULL; } gba->memory.rom = newRom; gba->memory.hw.gpioBase = &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1]; From 4ef98c7ddfcec1348a21da6f4a23c5f022f0209b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 8 Oct 2024 04:39:53 -0700 Subject: [PATCH 059/114] Core: Fix patch autoloading leaking the file handle --- CHANGES | 1 + src/core/core.c | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 9a538caee..a3d67111c 100644 --- a/CHANGES +++ b/CHANGES @@ -24,6 +24,7 @@ Emulation fixes: - GBA Video: Improve emulation of window start/end conditions (fixes mgba.io/i/1945) Other fixes: - Core: Fix inconsistencies with setting game-specific overrides (fixes mgba.io/i/2963) + - Core: Fix patch autoloading leaking the file handle - Debugger: Fix writing to specific segment in command-line debugger - GB: Fix uninitialized save data when loading undersized temporary saves - GB, GBA Core: Fix memory leak if reloading debug symbols diff --git a/src/core/core.c b/src/core/core.c index 789656ea4..ba7ef914c 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -239,9 +239,22 @@ bool mCoreAutoloadPatch(struct mCore* core) { if (!core->dirs.patch) { return false; } - return core->loadPatch(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".ups", O_RDONLY)) || - core->loadPatch(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".ips", O_RDONLY)) || - core->loadPatch(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".bps", O_RDONLY)); + struct VFile* vf = NULL; + if (!vf) { + vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".bps", O_RDONLY); + } + if (!vf) { + vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".ups", O_RDONLY); + } + if (!vf) { + vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".ips", O_RDONLY); + } + if (!vf) { + return false; + } + bool result = core->loadPatch(core, vf); + vf->close(vf); + return result; } bool mCoreAutoloadCheats(struct mCore* core) { From 67c3c40989cd5d8e0c6de335ba56c02abc7829d8 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 9 Oct 2024 00:26:10 -0700 Subject: [PATCH 060/114] Util: Shut Coverity up about a false positive --- src/util/md5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/md5.c b/src/util/md5.c index 08e06fb1d..9eaa4903b 100644 --- a/src/util/md5.c +++ b/src/util/md5.c @@ -87,7 +87,7 @@ static void md5Step(uint32_t* buffer, const uint32_t* input) { switch (i / 16) { case 0: E = F(BB, CC, DD); - j = i; + j = i & 0xF; break; case 1: E = G(BB, CC, DD); From afff68cfc0731c441ececa83bf43c3d8bebe5c5a Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 11 Oct 2024 21:52:48 -0700 Subject: [PATCH 061/114] Qt: Show a dummy shader settings tab if shaders aren't supported --- CHANGES | 1 + src/platform/qt/SettingsView.cpp | 36 +++++++++++++++++++------------- src/platform/qt/SettingsView.h | 4 ++-- src/platform/qt/Window.cpp | 8 ++++++- src/platform/qt/Window.h | 1 + 5 files changed, 33 insertions(+), 17 deletions(-) diff --git a/CHANGES b/CHANGES index a3d67111c..d6fd60848 100644 --- a/CHANGES +++ b/CHANGES @@ -58,6 +58,7 @@ Misc: - Qt: Pass logging context through to video proxy thread (fixes mgba.io/i/3095) - Qt: Show maker code and game version in ROM info - Qt: Make window corners square on Windows 11 (fixes mgba.io/i/3285) + - Qt: Show a dummy shader settings tab if shaders aren't supported - Res: Port NSO-gba-colors shader (closes mgba.io/i/2834) - Scripting: Add `callbacks:oneshot` for single-call callbacks - Switch: Add bilinear filtering option (closes mgba.io/i/3111) diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index 66c125466..c86dcf06e 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -397,29 +397,38 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC shortcutView->setController(shortcutController); shortcutView->setInputController(inputController); addPage(tr("Shortcuts"), shortcutView, Page::SHORTCUTS); + +#if defined(BUILD_GLES2) || defined(USE_EPOXY) + m_dummyShader = new QLabel(tr("Shaders are not supported when the display driver is not OpenGL.\n\n" + "If it is set to OpenGL and you still see this, your graphics card or drivers may be too old.")); + m_dummyShader->setWordWrap(true); + m_dummyShader->setAlignment(Qt::AlignCenter); + addPage(tr("Shaders"), m_dummyShader, Page::SHADERS); +#endif } SettingsView::~SettingsView() { -#if defined(BUILD_GL) || defined(BUILD_GLES2) - setShaderSelector(nullptr); +#if defined(BUILD_GLES2) || defined(USE_EPOXY) + if (m_shader) { + m_shader->setParent(nullptr); + } #endif } void SettingsView::setShaderSelector(ShaderSelector* shaderSelector) { -#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_ui.tabs->removeItemWidget(item); - } - m_ui.stackedWidget->removeWidget(m_shader); - m_shader->setParent(nullptr); +#if defined(BUILD_GLES2) || defined(USE_EPOXY) + auto items = m_ui.tabs->findItems(tr("Shaders"), Qt::MatchFixedString); + for (QListWidgetItem* item : items) { + delete item; + } + if (!m_shader) { + m_ui.stackedWidget->removeWidget(m_dummyShader); } m_shader = shaderSelector; if (shaderSelector) { - m_ui.stackedWidget->addWidget(m_shader); - m_ui.tabs->addItem(tr("Shaders")); - connect(m_ui.buttonBox, &QDialogButtonBox::accepted, m_shader, &ShaderSelector::saved); + addPage(tr("Shaders"), m_shader, Page::SHADERS); + } else { + addPage(tr("Shaders"), m_dummyShader, Page::SHADERS); } #endif } @@ -579,7 +588,6 @@ void SettingsView::updateConfig() { if (displayDriver != m_controller->getQtOption("displayDriver")) { m_controller->setQtOption("displayDriver", displayDriver); Display::setDriver(static_cast(displayDriver.toInt())); - setShaderSelector(nullptr); emit displayDriverChanged(); } diff --git a/src/platform/qt/SettingsView.h b/src/platform/qt/SettingsView.h index 010b4ef0d..85cf35a99 100644 --- a/src/platform/qt/SettingsView.h +++ b/src/platform/qt/SettingsView.h @@ -51,8 +51,6 @@ public: SettingsView(ConfigController* controller, InputController* inputController, ShortcutController* shortcutController, LogController* logController, QWidget* parent = nullptr); ~SettingsView(); - void setShaderSelector(ShaderSelector* shaderSelector); - signals: void biosLoaded(int platform, const QString&); void audioDriverChanged(); @@ -66,6 +64,7 @@ signals: public slots: void selectPage(Page); + void setShaderSelector(ShaderSelector* shaderSelector); private slots: void selectBios(QLineEdit*); @@ -81,6 +80,7 @@ private: ConfigController* m_controller; InputController* m_input; ShaderSelector* m_shader = nullptr; + QLabel* m_dummyShader; LogConfigModel m_logModel; QTimer m_checkTimer; diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 77c7695fb..ddf34f4f9 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -550,6 +550,7 @@ void Window::openSettingsWindow(SettingsView::Page page) { #ifdef USE_SQLITE3 connect(settingsWindow, &SettingsView::libraryCleared, m_libraryView, &LibraryController::clear); #endif + connect(this, &Window::shaderSelectorAdded, settingsWindow, &SettingsView::setShaderSelector); openView(settingsWindow); settingsWindow->selectPage(page); } @@ -1050,7 +1051,12 @@ void Window::reloadDisplayDriver() { } #if defined(BUILD_GL) || defined(BUILD_GLES2) m_shaderView.reset(); - m_shaderView = std::make_unique(m_display.get(), m_config); + if (m_display->supportsShaders()) { + m_shaderView = std::make_unique(m_display.get(), m_config); + emit shaderSelectorAdded(m_shaderView.get()); + } else { + emit shaderSelectorAdded(nullptr); + } #endif connect(m_display.get(), &QGBA::Display::hideCursor, [this]() { diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index f91e3ff30..cb467f89d 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -73,6 +73,7 @@ signals: void startDrawing(); void shutdown(); void paused(bool); + void shaderSelectorAdded(ShaderSelector*); public slots: void setController(CoreController* controller, const QString& fname); From 4a5a25e90c5ab0909a283df8a9cb10d9807f6e7a Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 18 Oct 2024 02:58:19 -0700 Subject: [PATCH 062/114] GBA DMA: Cache cycle estimation on first DMA --- include/mgba/internal/gba/dma.h | 2 ++ src/gba/audio.c | 1 + src/gba/dma.c | 35 ++++++++++++++++++++++++++++----- src/gba/io.c | 1 + src/gba/memory.c | 4 ++++ 5 files changed, 38 insertions(+), 5 deletions(-) diff --git a/include/mgba/internal/gba/dma.h b/include/mgba/internal/gba/dma.h index 192b3995a..58d590948 100644 --- a/include/mgba/internal/gba/dma.h +++ b/include/mgba/internal/gba/dma.h @@ -48,6 +48,7 @@ struct GBADMA { uint32_t nextDest; int32_t nextCount; uint32_t when; + int32_t cycles; }; struct GBA; @@ -65,6 +66,7 @@ void GBADMARunHblank(struct GBA* gba, int32_t cycles); void GBADMARunVblank(struct GBA* gba, int32_t cycles); void GBADMARunDisplayStart(struct GBA* gba, int32_t cycles); void GBADMAUpdate(struct GBA* gba); +void GBADMARecalculateCycles(struct GBA* gba); CXX_GUARD_END diff --git a/src/gba/audio.c b/src/gba/audio.c index a6ebafbae..ac394e995 100644 --- a/src/gba/audio.c +++ b/src/gba/audio.c @@ -309,6 +309,7 @@ void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles) { if (GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_CUSTOM) { dma->when = mTimingCurrentTime(&audio->p->timing) - cycles; dma->nextCount = 4; + GBADMARecalculateCycles(audio->p); GBADMASchedule(audio->p, channel->dmaSource, dma); } } diff --git a/src/gba/dma.c b/src/gba/dma.c index 290341361..5971b1b03 100644 --- a/src/gba/dma.c +++ b/src/gba/dma.c @@ -256,15 +256,21 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { if (info->count == info->nextCount) { if (width == 4) { cycles += memory->waitstatesNonseq32[sourceRegion] + memory->waitstatesNonseq32[destRegion]; + info->cycles = memory->waitstatesSeq32[sourceRegion] + memory->waitstatesSeq32[destRegion]; } else { cycles += memory->waitstatesNonseq16[sourceRegion] + memory->waitstatesNonseq16[destRegion]; + info->cycles = memory->waitstatesSeq16[sourceRegion] + memory->waitstatesSeq16[destRegion]; } } else { - if (width == 4) { - cycles += memory->waitstatesSeq32[sourceRegion] + memory->waitstatesSeq32[destRegion]; - } else { - cycles += memory->waitstatesSeq16[sourceRegion] + memory->waitstatesSeq16[destRegion]; + // Crossed region boundary; recalculate cached cycles + if (UNLIKELY(!(source & 0x00FFFFFC) || !(dest & 0x00FFFFFC))) { + if (width == 4) { + info->cycles = memory->waitstatesSeq32[sourceRegion] + memory->waitstatesSeq32[destRegion]; + } else { + info->cycles = memory->waitstatesSeq16[sourceRegion] + memory->waitstatesSeq16[destRegion]; + } } + cycles += info->cycles; } info->when += cycles; @@ -281,7 +287,7 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { memory->dmaTransferRegister = cpu->memory.load16(cpu, source, 0); memory->dmaTransferRegister |= memory->dmaTransferRegister << 16; } - if (destRegion == GBA_REGION_ROM2_EX) { + if (UNLIKELY(destRegion == GBA_REGION_ROM2_EX)) { if (memory->savedata.type == GBA_SAVEDATA_AUTODETECT) { mLOG(GBA_MEM, INFO, "Detected EEPROM savegame"); GBASavedataInitEEPROM(&memory->savedata); @@ -327,3 +333,22 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { } GBADMAUpdate(gba); } + +void GBADMARecalculateCycles(struct GBA* gba) { + int i; + for (i = 0; i < 4; ++i) { + struct GBADMA* dma = &gba->memory.dma[i]; + if (!GBADMARegisterIsEnable(dma->reg)) { + continue; + } + + uint32_t width = GBADMARegisterGetWidth(dma->reg); + uint32_t sourceRegion = dma->nextSource >> BASE_OFFSET; + uint32_t destRegion = dma->nextDest >> BASE_OFFSET; + if (width) { + dma->cycles = gba->memory.waitstatesSeq32[sourceRegion] + gba->memory.waitstatesSeq32[destRegion]; + } else { + dma->cycles = gba->memory.waitstatesSeq16[sourceRegion] + gba->memory.waitstatesSeq16[destRegion]; + } + } +} diff --git a/src/gba/io.c b/src/gba/io.c index 8a4ea7fc2..cb6058f7f 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -1079,6 +1079,7 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) { LOAD_32(gba->dmaPC, 0, &state->dmaBlockPC); LOAD_32(gba->bus, 0, &state->bus); + GBADMARecalculateCycles(gba); GBADMAUpdate(gba); GBAHardwareDeserialize(&gba->memory.hw, state); } diff --git a/src/gba/memory.c b/src/gba/memory.c index 3243fc5a1..c818c75c8 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -1734,6 +1734,10 @@ void GBAAdjustWaitstates(struct GBA* gba, uint16_t parameters) { STORE_32(memory->agbPrintFuncBackup, AGB_PRINT_FLUSH_ADDR | base, memory->rom); } } + + if (gba->performingDMA) { + GBADMARecalculateCycles(gba); + } } void GBAAdjustEWRAMWaitstates(struct GBA* gba, uint16_t parameters) { From 1c739e39e71dcbdd776bf0abf0b64761219e755e Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 18 Oct 2024 05:08:14 -0700 Subject: [PATCH 063/114] GBA DMA: Minor branch optimization --- src/gba/dma.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/gba/dma.c b/src/gba/dma.c index 5971b1b03..e9044d26a 100644 --- a/src/gba/dma.c +++ b/src/gba/dma.c @@ -319,9 +319,11 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { int i; for (i = 0; i < 4; ++i) { struct GBADMA* dma = &memory->dma[i]; - int32_t time = dma->when - info->when; - if (time < 0 && GBADMARegisterIsEnable(dma->reg) && dma->nextCount) { - dma->when = info->when; + if (GBADMARegisterIsEnable(dma->reg) && dma->nextCount) { + int32_t time = dma->when - info->when; + if (time < 0) { + dma->when = info->when; + } } } From 279485fc3ed54b0dafbbff448e901be47b25ef2f Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 20 Oct 2024 18:04:30 -0700 Subject: [PATCH 064/114] Qt: Fix saving named states breaking when screenshot states disabled (fixes #3320) --- CHANGES | 1 + src/platform/qt/CoreController.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index d6fd60848..e432bee67 100644 --- a/CHANGES +++ b/CHANGES @@ -38,6 +38,7 @@ Other fixes: - Qt: Fix potential crash when configuring shortcuts - Qt: Fix crash when applying changes to GB I/O registers in I/O view - Qt: Fix LCDC background priority/enable bit being mis-mapped in I/O view + - Qt: Fix saving named states breaking when screenshot states disabled (fixes mgba.io/i/3320) - Updater: Fix updating appimage across filesystems Misc: - Core: Handle relative paths for saves, screenshots, etc consistently (fixes mgba.io/i/2826) diff --git a/src/platform/qt/CoreController.cpp b/src/platform/qt/CoreController.cpp index 807a86685..2496f8039 100644 --- a/src/platform/qt/CoreController.cpp +++ b/src/platform/qt/CoreController.cpp @@ -736,7 +736,7 @@ void CoreController::saveState(const QString& path, int flags) { vf->read(vf, controller->m_backupSaveState.data(), controller->m_backupSaveState.size()); vf->close(vf); } - vf = VFileDevice::open(controller->m_statePath, O_WRONLY | O_CREAT | O_TRUNC); + vf = VFileDevice::open(controller->m_statePath, O_RDWR | O_CREAT | O_TRUNC); if (!vf) { return; } From 65b14b4ad984b41dff2e5c9604da3a565dea867e Mon Sep 17 00:00:00 2001 From: oltolm Date: Fri, 25 Oct 2024 19:07:04 +0200 Subject: [PATCH 065/114] fix assignment of modifier keys --- src/platform/qt/KeyEditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/qt/KeyEditor.cpp b/src/platform/qt/KeyEditor.cpp index 818b6c703..9ad981ce7 100644 --- a/src/platform/qt/KeyEditor.cpp +++ b/src/platform/qt/KeyEditor.cpp @@ -110,7 +110,7 @@ void KeyEditor::keyPressEvent(QKeyEvent* event) { m_key = Qt::Key_unknown; } m_lastKey.start(KEY_TIME); - setValue(ShortcutController::isModifierKey(event->key()) ? event->modifiers() : event->key() | event->modifiers()); + setValue(ShortcutController::isModifierKey(event->key()) ? event->key() : event->key() | event->modifiers()); } event->accept(); } From 47e5cd2432218d27cf778ee4e57c55c387774aab Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 29 Oct 2024 22:40:23 -0700 Subject: [PATCH 066/114] Qt: Fix "QFSFileEngine::open: No file name specified" warning --- src/platform/qt/Window.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index ddf34f4f9..8dca02078 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -1086,7 +1086,12 @@ void Window::reloadDisplayDriver() { m_display->setMinimumSize(GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS); #endif - m_display->setBackgroundImage(QImage{m_config->getOption("backgroundImage")}); + QString backgroundImage = m_config->getOption("backgroundImage"); + if (backgroundImage.isEmpty()) { + m_display->setBackgroundImage(QImage{}); + } else { + m_display->setBackgroundImage(QImage{backgroundImage}); + } if (!proxy) { proxy = std::make_shared(); @@ -1978,7 +1983,12 @@ void Window::setupOptions() { ConfigOption* backgroundImage = m_config->addOption("backgroundImage"); backgroundImage->connect([this](const QVariant& value) { if (m_display) { - m_display->setBackgroundImage(QImage{value.toString()}); + QString backgroundImage = value.toString(); + if (backgroundImage.isEmpty()) { + m_display->setBackgroundImage(QImage{}); + } else { + m_display->setBackgroundImage(QImage{backgroundImage}); + } } }, this); m_config->updateOption("backgroundImage"); From 377ddf50810a5cd1889e0cdfd86298c577b8a956 Mon Sep 17 00:00:00 2001 From: Jan200101 Date: Wed, 30 Oct 2024 23:48:25 +0100 Subject: [PATCH 067/114] Qt: Recreate Window to release old surface and create a OpenGL one --- src/platform/qt/DisplayGL.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index ad4969943..e62e9685e 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -192,6 +192,7 @@ DisplayGL::DisplayGL(const QSurfaceFormat& format, QWidget* parent) setAttribute(Qt::WA_NativeWindow); window()->windowHandle()->setFormat(format); windowHandle()->setSurfaceType(QSurface::OpenGLSurface); + windowHandle()->destroy(); windowHandle()->create(); #ifdef USE_SHARE_WIDGET From 26ea53b024717fb74a6023ac7a59d04a196b24ca Mon Sep 17 00:00:00 2001 From: oltolm Date: Wed, 30 Oct 2024 17:54:32 +0100 Subject: [PATCH 068/114] fix numpad shortcuts --- src/platform/qt/KeyEditor.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/platform/qt/KeyEditor.cpp b/src/platform/qt/KeyEditor.cpp index 9ad981ce7..6ce94fc55 100644 --- a/src/platform/qt/KeyEditor.cpp +++ b/src/platform/qt/KeyEditor.cpp @@ -110,7 +110,9 @@ void KeyEditor::keyPressEvent(QKeyEvent* event) { m_key = Qt::Key_unknown; } m_lastKey.start(KEY_TIME); - setValue(ShortcutController::isModifierKey(event->key()) ? event->key() : event->key() | event->modifiers()); + setValue(ShortcutController::isModifierKey(event->key()) ? + event->key() : + event->key() | (event->modifiers() & ~Qt::KeypadModifier)); } event->accept(); } From ef5646bbe3aaba0ac72f0ecce319684500852c52 Mon Sep 17 00:00:00 2001 From: Bo He Date: Tue, 15 Oct 2024 23:43:14 +0800 Subject: [PATCH 069/114] associate windows with the owning application --- res/mgba-qt.desktop | 1 + 1 file changed, 1 insertion(+) diff --git a/res/mgba-qt.desktop b/res/mgba-qt.desktop index d5d98ab37..2dc96901c 100644 --- a/res/mgba-qt.desktop +++ b/res/mgba-qt.desktop @@ -10,3 +10,4 @@ Comment=Nintendo Game Boy Advance Emulator Categories=Game;Emulator; MimeType=application/x-gameboy-advance-rom;application/x-agb-rom;application/x-gba-rom; Keywords=emulator;Nintendo;advance;gba;Game Boy Advance; +StartupWMClass=mGBA From a114207828b15be742608a1b5160b16e39f150d4 Mon Sep 17 00:00:00 2001 From: yeah-its-gloria <32610623+yeah-its-gloria@users.noreply.github.com> Date: Mon, 4 Nov 2024 01:19:05 +0100 Subject: [PATCH 070/114] Fix various macOS related problems - Disable a duplicate libraries warning that isn't needed - Quit using QApplication::quit instead of QWidget::close to prevent closing inside a dialog from crashing the application - Allow Qt 6 builds to use std::filesystem if the proper macOS SDK version is present - Stop looking for QtMultimedia plugins if Qt 6 is being used for macOS builds --- CMakeLists.txt | 6 ++++++ src/platform/qt/CMakeLists.txt | 21 ++++++++++++++++----- src/platform/qt/Window.cpp | 2 +- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 78fddfce4..7857e9c38 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -242,6 +242,12 @@ if(APPLE) execute_process(COMMAND xcrun --show-sdk-version OUTPUT_VARIABLE MACOSX_SDK) add_definitions(-D_DARWIN_C_SOURCE) list(APPEND OS_LIB "-framework Foundation") + + # Xcode 15 introduced a warning about duplicate libraries that CMake doesn't disable itself, we do it here globally + if(MACOSX_SDK VERSION_GREATER_EQUAL 10.15) + add_link_options(LINKER:-no_warn_duplicate_libraries) + endif() + if(NOT CMAKE_SYSTEM_VERSION VERSION_LESS "10.0") # Darwin 10.x is Mac OS X 10.6 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.6") endif() diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 51aa22401..3cfe83ad0 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -59,7 +59,10 @@ if(APPLE) list(APPEND QT_DEFINES USE_SHARE_WIDGET) endif() - if(Qt6Widgets_VERSION) + # Allows building with Qt that was built with std::filesystem support, which requires macOS 15 + if(Qt6Widgets_VERSION AND MACOSX_SDK VERSION_GREATER_EQUAL 10.15) + set(MIN_VER 10.15) + elseif(Qt6Widgets_VERSION) set(MIN_VER 10.14) elseif(Qt5Widgets_VERSION MATCHES "^5.15") set(MIN_VER 10.13) @@ -478,13 +481,21 @@ endif() if(APPLE) if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") get_target_property(QTCOCOA ${QT}::QCocoaIntegrationPlugin LOCATION) - get_target_property(COREAUDIO ${QT}::CoreAudioPlugin LOCATION) - get_target_property(QTAVFSERVICE ${QT}::AVFServicePlugin LOCATION) + if(${QT_V} LESS 6) + # QtMultimedia plugins were removed with Qt 6, skip checking for them + get_target_property(COREAUDIO ${QT}::CoreAudioPlugin LOCATION) + get_target_property(QTAVFSERVICE ${QT}::AVFServicePlugin LOCATION) + endif() + set(BUNDLE_PATH ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.app) target_sources(${BINARY_NAME}-qt PRIVATE "${PLUGINS}") + set_source_files_properties("${QTCOCOA}" PROPERTIES MACOSX_PACKAGE_LOCATION Contents/PlugIns) - set_source_files_properties("${COREAUDIO}" PROPERTIES MACOSX_PACKAGE_LOCATION Contents/PlugIns) - set_source_files_properties("${QTAVFSERVICE}" PROPERTIES MACOSX_PACKAGE_LOCATION Contents/PlugIns) + if(${QT_V} LESS 6) + set_source_files_properties("${COREAUDIO}" PROPERTIES MACOSX_PACKAGE_LOCATION Contents/PlugIns) + set_source_files_properties("${QTAVFSERVICE}" PROPERTIES MACOSX_PACKAGE_LOCATION Contents/PlugIns) + endif() + install(CODE " include(BundleUtilities) set(BU_CHMOD_BUNDLE_ITEMS ON) diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 8dca02078..bc956fcc4 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -1422,7 +1422,7 @@ void Window::setupMenu(QMenuBar* menubar) { #endif m_actions.addAction(tr("About..."), "about", openTView(), "file")->setRole(Action::Role::ABOUT); - m_actions.addAction(tr("E&xit"), "quit", static_cast(this), &QWidget::close, "file", QKeySequence::Quit)->setRole(Action::Role::QUIT); + m_actions.addAction(tr("E&xit"), "quit", &QApplication::quit, "file", QKeySequence::Quit)->setRole(Action::Role::QUIT); m_actions.addMenu(tr("&Emulation"), "emu"); addGameAction(tr("&Reset"), "reset", &CoreController::reset, "emu", QKeySequence("Ctrl+R")); From b27296896904929607da93fb01e9831aa8ee36ff Mon Sep 17 00:00:00 2001 From: Adam Higerd Date: Fri, 11 Oct 2024 19:50:05 -0500 Subject: [PATCH 071/114] Qt: handle newlines in TextBuffer print() --- .../qt/scripting/ScriptingTextBuffer.cpp | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/platform/qt/scripting/ScriptingTextBuffer.cpp b/src/platform/qt/scripting/ScriptingTextBuffer.cpp index 9a5de818d..da05bd2b9 100644 --- a/src/platform/qt/scripting/ScriptingTextBuffer.cpp +++ b/src/platform/qt/scripting/ScriptingTextBuffer.cpp @@ -51,26 +51,25 @@ void ScriptingTextBuffer::setBufferName(const QString& name) { void ScriptingTextBuffer::print(const QString& text) { QMutexLocker locker(&m_mutex); - QString split(text); - m_shim.cursor.beginEditBlock(); - while (m_shim.cursor.positionInBlock() + split.length() > m_dims.width()) { - int cut = m_dims.width() - m_shim.cursor.positionInBlock(); + for (QString split : text.split('\n')) { + while (m_shim.cursor.positionInBlock() + split.length() > m_dims.width()) { + int cut = m_dims.width() - m_shim.cursor.positionInBlock(); + if (!m_shim.cursor.atBlockEnd()) { + m_shim.cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); + } + m_shim.cursor.insertText(split.left(cut)); + if (m_shim.cursor.atEnd()) { + m_shim.cursor.insertBlock(); + } else { + m_shim.cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, 1); + } + split = split.mid(cut); + } if (!m_shim.cursor.atBlockEnd()) { - m_shim.cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); + m_shim.cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, split.length()); } - m_shim.cursor.insertText(split.left(cut)); - if (m_shim.cursor.atEnd()) { - m_shim.cursor.insertBlock(); - } else { - m_shim.cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, 1); - } - split = split.mid(cut); + m_shim.cursor.insertText(split); } - if (!m_shim.cursor.atBlockEnd()) { - m_shim.cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, split.length()); - } - m_shim.cursor.insertText(split); - m_shim.cursor.endEditBlock(); } void ScriptingTextBuffer::clear() { From 17b03b6f208f5fc8481d5fd693f185a24affcc50 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 3 Nov 2024 18:00:36 -0800 Subject: [PATCH 072/114] GBA Video: Fix potential shader compilation error --- src/gba/renderers/gl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gba/renderers/gl.c b/src/gba/renderers/gl.c index 24fb092c7..dbff67e4e 100644 --- a/src/gba/renderers/gl.c +++ b/src/gba/renderers/gl.c @@ -539,7 +539,7 @@ static const char* const _renderWindow = "}\n" "bool test(vec3 circle, vec4 top, vec4 bottom) {\n" - " if (circle.z > 0) {\n" + " if (circle.z > 0.) {\n" " return distance(circle.xy, texCoord.xy) <= circle.z;\n" " }\n" " return crop(interpolate(top, bottom));\n" From 77e747ebf6e2becd669312667999e39e79036ceb Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 3 Nov 2024 19:23:57 -0800 Subject: [PATCH 073/114] mGUI: Fix control remapping not reloading (fixes #3277) --- src/feature/gui/gui-runner.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/feature/gui/gui-runner.c b/src/feature/gui/gui-runner.c index 035522f02..76e15cdad 100644 --- a/src/feature/gui/gui-runner.c +++ b/src/feature/gui/gui-runner.c @@ -782,6 +782,9 @@ void mGUILoadInputMaps(struct mGUIRunner* runner) { size_t i; for (i = 0; runner->keySources[i].id; ++i) { mInputMapLoad(&runner->params.keyMap, runner->keySources[i].id, mCoreConfigGetInput(&runner->config)); + if (runner->core) { + mInputMapLoad(&runner->core->inputMap, runner->keySources[i].id, mCoreConfigGetInput(&runner->config)); + } } } From f89184d51fa4fb38c8d82a17de5d6525e6aa915e Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 31 Oct 2024 22:47:51 -0700 Subject: [PATCH 074/114] GBA BIOS: Fix locked BIOS read on boot --- src/gba/hle-bios.c | 4 ++-- src/gba/hle-bios.s | 5 +++-- src/gba/video.c | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/gba/hle-bios.c b/src/gba/hle-bios.c index 841caf543..8f689fa7a 100644 --- a/src/gba/hle-bios.c +++ b/src/gba/hle-bios.c @@ -75,9 +75,9 @@ const uint8_t hleBios[GBA_SIZE_BIOS] = { 0xf0, 0x07, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0xb0, 0x01, 0x00, 0x00, 0x04, 0xb0, 0x5b, 0xe2, 0xfd, 0xff, 0xff, 0x8a, 0x1e, 0xff, 0x2f, 0xe1, 0xc2, 0xe3, 0xa0, 0xe3, 0x03, 0x10, 0x5e, 0xe4, 0x00, 0x00, 0x51, 0xe3, - 0x00, 0x10, 0xa0, 0x13, 0x1e, 0xff, 0x2f, 0x11, 0x1c, 0xe0, 0x9f, 0xe5, + 0x00, 0x10, 0xa0, 0x13, 0x05, 0x00, 0x00, 0x1a, 0x1c, 0xe0, 0x9f, 0xe5, 0x00, 0x10, 0x9e, 0xe5, 0x00, 0x00, 0x51, 0xe3, 0x00, 0x10, 0xa0, 0xe3, - 0x1e, 0xff, 0x2f, 0x11, 0xc0, 0xe0, 0x4e, 0xe2, 0x1e, 0xff, 0x2f, 0xe1, + 0x00, 0x00, 0x00, 0x1a, 0xc0, 0xe0, 0x4e, 0xe2, 0x1e, 0xff, 0x2f, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x29, 0xe1, 0xc0, 0x00, 0x00, 0x02, 0x4c, 0xd0, 0x9f, 0xe5, 0x00, 0x50, 0x2d, 0xe9, 0x00, 0xc0, 0x4f, 0xe1, 0x00, 0xe0, 0x0f, 0xe1, 0x00, 0x50, 0x2d, 0xe9, 0x02, 0xe3, 0xa0, 0xe3, diff --git a/src/gba/hle-bios.s b/src/gba/hle-bios.s index 07f827b26..aa77160c1 100644 --- a/src/gba/hle-bios.s +++ b/src/gba/hle-bios.s @@ -318,13 +318,14 @@ mov lr, #0x8000003 ldrb r1, [lr], #-3 cmp r1, #0 movne r1, #0 -bxne lr +bne 1f ldr lr, =0x20000C0 ldr r1, [lr] cmp r1, #0 mov r1, #0 -bxne lr +bne 1f sub lr, #0xC0 +1: bx lr .word 0 .word 0xE129F000 diff --git a/src/gba/video.c b/src/gba/video.c index 63e7115c1..749a29be0 100644 --- a/src/gba/video.c +++ b/src/gba/video.c @@ -70,7 +70,7 @@ void GBAVideoReset(struct GBAVideo* video) { } else { // TODO: Verify exact scanline on hardware video->vcount = 0x7E; - nextEvent = 117; + nextEvent = 120; } video->p->memory.io[GBA_REG(VCOUNT)] = video->vcount; From 645d1cf34405a44a8677ecaaaa54a5268c9d1d11 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 1 Nov 2024 02:06:04 -0700 Subject: [PATCH 075/114] GBA Savedata: Improve save write edge cases --- src/gba/memory.c | 12 ++---------- src/gba/savedata.c | 4 +++- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/gba/memory.c b/src/gba/memory.c index c818c75c8..87e31f746 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -802,14 +802,7 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { mLOG(GBA_MEM, STUB, "Unimplemented memory Store32: 0x%08X", address); #define STORE_SRAM \ - if (address & 0x3) { \ - mLOG(GBA_MEM, GAME_ERROR, "Unaligned SRAM Store32: 0x%08X", address); \ - } else { \ - GBAStore8(cpu, address, value, cycleCounter); \ - GBAStore8(cpu, address | 1, value, cycleCounter); \ - GBAStore8(cpu, address | 2, value, cycleCounter); \ - GBAStore8(cpu, address | 3, value, cycleCounter); \ - } + GBAStore8(cpu, address, value >> (8 * (address & 3)), cycleCounter); #define STORE_BAD \ mLOG(GBA_MEM, GAME_ERROR, "Bad memory Store32: 0x%08X", address); @@ -989,10 +982,9 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle case GBA_REGION_SRAM_MIRROR: if (address & 1) { mLOG(GBA_MEM, GAME_ERROR, "Unaligned SRAM Store16: 0x%08X", address); - break; + value >>= 8; } GBAStore8(cpu, address, value, cycleCounter); - GBAStore8(cpu, address | 1, value, cycleCounter); break; default: mLOG(GBA_MEM, GAME_ERROR, "Bad memory Store16: 0x%08X", address); diff --git a/src/gba/savedata.c b/src/gba/savedata.c index 11be3e0ab..ca5d35cbb 100644 --- a/src/gba/savedata.c +++ b/src/gba/savedata.c @@ -376,7 +376,9 @@ uint8_t GBASavedataReadFlash(struct GBASavedata* savedata, uint16_t address) { } } if (mTimingIsScheduled(savedata->timing, &savedata->dust) && (address >> 12) == savedata->settling) { - return 0x5F; + // This should read /Q7 ("data# polling") and Q6 flipping ("toggle bit") every read, + // but just data# polling is sufficient for games to figure it out + return savedata->currentBank[address] ^ 0x80; } return savedata->currentBank[address]; } From 4d6fb5b3c503d65b170a466e3402f4a88730d8f1 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 8 Nov 2024 00:50:19 -0800 Subject: [PATCH 076/114] Scripting: Combine frame argument and return value stacks These were never used a the same time, and it reduces malloc/free churn --- include/mgba/script/macros.h | 24 ++-- include/mgba/script/types.h | 3 +- src/core/scripting.c | 10 +- src/script/context.c | 4 +- src/script/engines/lua.c | 25 ++-- src/script/test/classes.c | 122 +++++++++---------- src/script/test/lua.c | 10 +- src/script/test/types.c | 230 +++++++++++++++++------------------ src/script/types.c | 30 +++-- src/tools/docgen.c | 6 +- 10 files changed, 234 insertions(+), 230 deletions(-) diff --git a/include/mgba/script/macros.h b/include/mgba/script/macros.h index 70eed0af5..a1ef0d669 100644 --- a/include/mgba/script/macros.h +++ b/include/mgba/script/macros.h @@ -76,10 +76,14 @@ CXX_GUARD_START #define _mSCRIPT_FIELD_NAME(V) (V)->type->name #define _mSCRIPT_WRAPPED_FIELD_NAME(V) (V)->value.wrapped->type->name -#define _mSCRIPT_CALL_VOID(FUNCTION, NPARAMS) FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS)) +#define _mSCRIPT_CALL_VOID(FUNCTION, NPARAMS) \ + FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS)); \ + mScriptListClear(&frame->stack) + #define _mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS) \ mSCRIPT_TYPE_C_ ## RETURN out = FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS)); \ - mSCRIPT_PUSH(&frame->returnValues, RETURN, out) + mScriptListClear(&frame->stack); \ + mSCRIPT_PUSH(&frame->stack, RETURN, out) #define mSCRIPT_DECLARE_STRUCT(STRUCT) \ extern const struct mScriptType mSTStruct_ ## STRUCT; \ @@ -249,8 +253,8 @@ CXX_GUARD_START }, #define _mSCRIPT_STRUCT_METHOD_POP(TYPE, S, NPARAMS, ...) \ - _mCALL(_mCAT(mSCRIPT_POP_, _mSUCC_ ## NPARAMS), &frame->arguments, _mCOMMA_ ## NPARAMS(S(TYPE), __VA_ARGS__)); \ - if (mScriptListSize(&frame->arguments)) { \ + _mCALL(_mCAT(mSCRIPT_POP_, _mSUCC_ ## NPARAMS), &frame->stack, _mCOMMA_ ## NPARAMS(S(TYPE), __VA_ARGS__)); \ + if (mScriptListSize(&frame->stack)) { \ return false; \ } @@ -326,11 +330,11 @@ CXX_GUARD_START static const struct mScriptFunctionOverload _mSTStructBindingOverloads_ ## TYPE ## _ ## NAME[mSCRIPT_OVERLOADS_MAX]; \ static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ UNUSED(ctx); \ - const struct mScriptFunctionOverload* overload = mScriptFunctionFindOverload(_mSTStructBindingOverloads_ ## TYPE ## _ ## NAME, &frame->arguments); \ + const struct mScriptFunctionOverload* overload = mScriptFunctionFindOverload(_mSTStructBindingOverloads_ ## TYPE ## _ ## NAME, &frame->stack); \ if (!overload) { \ return false; \ } \ - if (!mScriptCoerceFrame(&overload->type->details.function.parameters, &frame->arguments, &frame->arguments)) { \ + if (!mScriptCoerceFrame(&overload->type->details.function.parameters, &frame->stack, &frame->stack)) { \ return false; \ } \ return overload->function->call(frame, overload->function->context); \ @@ -552,8 +556,8 @@ CXX_GUARD_START #define _mSCRIPT_BIND_N_FUNCTION(NAME, RETURN, FUNCTION, DEFAULTS, NPARAMS, ...) \ static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \ UNUSED(ctx); \ - _mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - if (mScriptListSize(&frame->arguments)) { \ + _mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->stack, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + if (mScriptListSize(&frame->stack)) { \ return false; \ } \ _mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS); \ @@ -564,8 +568,8 @@ CXX_GUARD_START #define _mSCRIPT_BIND_VOID_FUNCTION(NAME, FUNCTION, DEFAULTS, NPARAMS, ...) \ static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \ UNUSED(ctx); \ - _mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ - if (mScriptListSize(&frame->arguments)) { \ + _mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->stack, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ + if (mScriptListSize(&frame->stack)) { \ return false; \ } \ _mSCRIPT_CALL_VOID(FUNCTION, NPARAMS); \ diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 5b07abe3b..684059d32 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -325,8 +325,7 @@ struct mScriptString { }; struct mScriptFrame { - struct mScriptList arguments; - struct mScriptList returnValues; + struct mScriptList stack; // TODO: Exception/failure codes }; diff --git a/src/core/scripting.c b/src/core/scripting.c index dc56c1783..9e46b619c 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -1205,11 +1205,11 @@ static bool _callRotationCb(struct mScriptCoreAdapter* adapter, const char* cbNa struct mScriptValue* context = mScriptTableLookup(adapter->rotationCbTable, &mSCRIPT_MAKE_CHARP("context")); mScriptFrameInit(&frame); if (context) { - mScriptValueWrap(context, mScriptListAppend(&frame.arguments)); + mScriptValueWrap(context, mScriptListAppend(&frame.stack)); } bool ok = mScriptContextInvoke(adapter->context, cb, &frame); - if (ok && out && mScriptListSize(&frame.returnValues) == 1) { - if (!mScriptCast(mSCRIPT_TYPE_MS_F32, mScriptListGetPointer(&frame.returnValues, 0), out)) { + if (ok && out && mScriptListSize(&frame.stack) == 1) { + if (!mScriptCast(mSCRIPT_TYPE_MS_F32, mScriptListGetPointer(&frame.stack, 0), out)) { ok = false; } } @@ -1278,8 +1278,8 @@ static uint8_t _readLuminance(struct GBALuminanceSource* luminance) { mScriptFrameInit(&frame); bool ok = mScriptContextInvoke(adapter->context, adapter->luminanceCb, &frame); struct mScriptValue out = {0}; - if (ok && mScriptListSize(&frame.returnValues) == 1) { - if (!mScriptCast(mSCRIPT_TYPE_MS_U8, mScriptListGetPointer(&frame.returnValues, 0), &out)) { + if (ok && mScriptListSize(&frame.stack) == 1) { + if (!mScriptCast(mSCRIPT_TYPE_MS_U8, mScriptListGetPointer(&frame.stack, 0), &out)) { ok = false; } } diff --git a/src/script/context.c b/src/script/context.c index d9afd55a1..0d770dfde 100644 --- a/src/script/context.c +++ b/src/script/context.c @@ -266,7 +266,7 @@ void mScriptContextTriggerCallback(struct mScriptContext* context, const char* c if (fn) { mScriptFrameInit(&frame); if (args) { - mScriptListCopy(&frame.arguments, args); + mScriptListCopy(&frame.stack, args); } mScriptContextInvoke(context, fn, &frame); mScriptFrameDeinit(&frame); @@ -481,7 +481,7 @@ bool mScriptInvoke(const struct mScriptValue* val, struct mScriptFrame* frame) { return false; } const struct mScriptTypeFunction* signature = &val->type->details.function; - if (!mScriptCoerceFrame(&signature->parameters, &frame->arguments, &frame->arguments)) { + if (!mScriptCoerceFrame(&signature->parameters, &frame->stack, &frame->stack)) { return false; } const struct mScriptFunction* fn = val->value.opaque; diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index 732d54e99..f4f4026e9 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -912,12 +912,12 @@ void _luaError(struct mScriptEngineContextLua* luaContext) { if (ok) { struct mScriptFrame frame; mScriptFrameInit(&frame); - struct mScriptValue* this = mScriptListAppend(&frame.arguments); + struct mScriptValue* this = mScriptListAppend(&frame.stack); this->type = console->type; this->refs = mSCRIPT_VALUE_UNREF; this->flags = 0; this->value.opaque = console->value.opaque; - mSCRIPT_PUSH(&frame.arguments, CHARP, luaContext->lastError); + mSCRIPT_PUSH(&frame.stack, CHARP, luaContext->lastError); ok = mScriptInvoke(&error, &frame); mScriptFrameDeinit(&frame); } @@ -1115,7 +1115,7 @@ void _autofreeFrame(struct mScriptContext* context, struct mScriptList* frame) { bool _luaInvoke(struct mScriptEngineContextLua* luaContext, struct mScriptFrame* frame) { int nargs = 0; if (frame) { - nargs = mScriptListSize(&frame->arguments); + nargs = mScriptListSize(&frame->stack); } if (luaContext->lastError) { @@ -1127,9 +1127,12 @@ bool _luaInvoke(struct mScriptEngineContextLua* luaContext, struct mScriptFrame* return false; } - if (frame && !_luaPushFrame(luaContext, luaContext->lua, &frame->arguments)) { - mScriptContextDeactivate(luaContext->d.context); - return false; + if (frame) { + if (!_luaPushFrame(luaContext, luaContext->lua, &frame->stack)) { + mScriptContextDeactivate(luaContext->d.context); + return false; + } + mScriptListClear(&frame->stack); } lua_pushliteral(luaContext->lua, "mCtx"); @@ -1151,7 +1154,7 @@ bool _luaInvoke(struct mScriptEngineContextLua* luaContext, struct mScriptFrame* return false; } - if (frame && !_luaPopFrame(luaContext, luaContext->lua, &frame->returnValues)) { + if (frame && !_luaPopFrame(luaContext, luaContext->lua, &frame->stack)) { mScriptContextDrainPool(luaContext->d.context); return false; } @@ -1191,8 +1194,8 @@ int _luaThunk(lua_State* lua) { struct mScriptEngineContextLua* luaContext = _luaGetContext(lua); struct mScriptFrame frame; mScriptFrameInit(&frame); - if (!_luaPopFrame(luaContext, lua, &frame.arguments)) { - _freeFrame(&frame.arguments); + if (!_luaPopFrame(luaContext, lua, &frame.stack)) { + _freeFrame(&frame.stack); mScriptContextDrainPool(luaContext->d.context); mScriptFrameDeinit(&frame); luaL_traceback(lua, lua, "Error calling function (translating arguments into runtime)", 1); @@ -1200,7 +1203,7 @@ int _luaThunk(lua_State* lua) { } struct mScriptValue* fn = lua_touserdata(lua, lua_upvalueindex(1)); - _autofreeFrame(luaContext->d.context, &frame.arguments); + _autofreeFrame(luaContext->d.context, &frame.stack); if (!fn || !mScriptContextInvoke(luaContext->d.context, fn, &frame)) { mScriptContextDrainPool(luaContext->d.context); mScriptFrameDeinit(&frame); @@ -1208,7 +1211,7 @@ int _luaThunk(lua_State* lua) { return lua_error(lua); } - bool ok = _luaPushFrame(luaContext, lua, &frame.returnValues); + bool ok = _luaPushFrame(luaContext, lua, &frame.stack); mScriptContextDrainPool(luaContext->d.context); mScriptFrameDeinit(&frame); if (!ok) { diff --git a/src/script/test/classes.c b/src/script/test/classes.c index e0ce39d47..1b688cae4 100644 --- a/src/script/test/classes.c +++ b/src/script/test/classes.c @@ -519,106 +519,106 @@ M_TEST_DEFINE(testAStatic) { assert_true(mScriptObjectGet(&sval, "i0", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + mSCRIPT_PUSH(&frame.stack, S(TestA), &s); assert_true(mScriptInvoke(&val, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_true(mScriptPopS32(&frame.stack, &rval)); assert_int_equal(rval, 1); mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "i1", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); - mSCRIPT_PUSH(&frame.arguments, S32, 1); + mSCRIPT_PUSH(&frame.stack, S(TestA), &s); + mSCRIPT_PUSH(&frame.stack, S32, 1); assert_true(mScriptInvoke(&val, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_true(mScriptPopS32(&frame.stack, &rval)); assert_int_equal(rval, 2); mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "ic0", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, CS(TestA), &s); + mSCRIPT_PUSH(&frame.stack, CS(TestA), &s); assert_true(mScriptInvoke(&val, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_true(mScriptPopS32(&frame.stack, &rval)); assert_int_equal(rval, 1); mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "ic0", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + mSCRIPT_PUSH(&frame.stack, S(TestA), &s); assert_true(mScriptInvoke(&val, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_true(mScriptPopS32(&frame.stack, &rval)); assert_int_equal(rval, 1); mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "ic1", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, CS(TestA), &s); - mSCRIPT_PUSH(&frame.arguments, S32, 1); + mSCRIPT_PUSH(&frame.stack, CS(TestA), &s); + mSCRIPT_PUSH(&frame.stack, S32, 1); assert_true(mScriptInvoke(&val, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_true(mScriptPopS32(&frame.stack, &rval)); assert_int_equal(rval, 2); mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "ic1", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); - mSCRIPT_PUSH(&frame.arguments, S32, 1); + mSCRIPT_PUSH(&frame.stack, S(TestA), &s); + mSCRIPT_PUSH(&frame.stack, S32, 1); assert_true(mScriptInvoke(&val, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_true(mScriptPopS32(&frame.stack, &rval)); assert_int_equal(rval, 2); mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "v0", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + mSCRIPT_PUSH(&frame.stack, S(TestA), &s); assert_true(mScriptInvoke(&val, &frame)); mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "i0", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + mSCRIPT_PUSH(&frame.stack, S(TestA), &s); assert_true(mScriptInvoke(&val, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_true(mScriptPopS32(&frame.stack, &rval)); assert_int_equal(rval, 2); mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "ic0", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, CS(TestA), &s); + mSCRIPT_PUSH(&frame.stack, CS(TestA), &s); assert_true(mScriptInvoke(&val, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_true(mScriptPopS32(&frame.stack, &rval)); assert_int_equal(rval, 2); mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "v1", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); - mSCRIPT_PUSH(&frame.arguments, S32, 2); + mSCRIPT_PUSH(&frame.stack, S(TestA), &s); + mSCRIPT_PUSH(&frame.stack, S32, 2); assert_true(mScriptInvoke(&val, &frame)); mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "v2", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); - mSCRIPT_PUSH(&frame.arguments, S32, 1); - mSCRIPT_PUSH(&frame.arguments, S32, -2); + mSCRIPT_PUSH(&frame.stack, S(TestA), &s); + mSCRIPT_PUSH(&frame.stack, S32, 1); + mSCRIPT_PUSH(&frame.stack, S32, -2); assert_true(mScriptInvoke(&val, &frame)); mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "v2", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); - mSCRIPT_PUSH(&frame.arguments, S32, 1); + mSCRIPT_PUSH(&frame.stack, S(TestA), &s); + mSCRIPT_PUSH(&frame.stack, S32, 1); assert_true(mScriptInvoke(&val, &frame)); mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "i0", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + mSCRIPT_PUSH(&frame.stack, S(TestA), &s); assert_true(mScriptInvoke(&val, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_true(mScriptPopS32(&frame.stack, &rval)); assert_int_equal(rval, 4); mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "ic0", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, CS(TestA), &s); + mSCRIPT_PUSH(&frame.stack, CS(TestA), &s); assert_true(mScriptInvoke(&val, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_true(mScriptPopS32(&frame.stack, &rval)); assert_int_equal(rval, 4); mScriptFrameDeinit(&frame); @@ -659,93 +659,93 @@ M_TEST_DEFINE(testADynamic) { assert_true(mScriptObjectGet(&sval, "ifn0", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + mSCRIPT_PUSH(&frame.stack, S(TestA), &s); assert_true(mScriptInvoke(&val, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_true(mScriptPopS32(&frame.stack, &rval)); assert_int_equal(rval, 1); mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "ifn1", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); - mSCRIPT_PUSH(&frame.arguments, S32, 1); + mSCRIPT_PUSH(&frame.stack, S(TestA), &s); + mSCRIPT_PUSH(&frame.stack, S32, 1); assert_true(mScriptInvoke(&val, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_true(mScriptPopS32(&frame.stack, &rval)); assert_int_equal(rval, 2); mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "icfn0", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, CS(TestA), &s); + mSCRIPT_PUSH(&frame.stack, CS(TestA), &s); assert_true(mScriptInvoke(&val, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_true(mScriptPopS32(&frame.stack, &rval)); assert_int_equal(rval, 1); mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "icfn0", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + mSCRIPT_PUSH(&frame.stack, S(TestA), &s); assert_true(mScriptInvoke(&val, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_true(mScriptPopS32(&frame.stack, &rval)); assert_int_equal(rval, 1); mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "icfn1", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, CS(TestA), &s); - mSCRIPT_PUSH(&frame.arguments, S32, 1); + mSCRIPT_PUSH(&frame.stack, CS(TestA), &s); + mSCRIPT_PUSH(&frame.stack, S32, 1); assert_true(mScriptInvoke(&val, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_true(mScriptPopS32(&frame.stack, &rval)); assert_int_equal(rval, 2); mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "icfn1", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); - mSCRIPT_PUSH(&frame.arguments, S32, 1); + mSCRIPT_PUSH(&frame.stack, S(TestA), &s); + mSCRIPT_PUSH(&frame.stack, S32, 1); assert_true(mScriptInvoke(&val, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_true(mScriptPopS32(&frame.stack, &rval)); assert_int_equal(rval, 2); mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "vfn0", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + mSCRIPT_PUSH(&frame.stack, S(TestA), &s); assert_true(mScriptInvoke(&val, &frame)); mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "ifn0", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + mSCRIPT_PUSH(&frame.stack, S(TestA), &s); assert_true(mScriptInvoke(&val, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_true(mScriptPopS32(&frame.stack, &rval)); assert_int_equal(rval, 2); mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "icfn0", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, CS(TestA), &s); + mSCRIPT_PUSH(&frame.stack, CS(TestA), &s); assert_true(mScriptInvoke(&val, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_true(mScriptPopS32(&frame.stack, &rval)); assert_int_equal(rval, 2); mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "vfn1", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); - mSCRIPT_PUSH(&frame.arguments, S32, 2); + mSCRIPT_PUSH(&frame.stack, S(TestA), &s); + mSCRIPT_PUSH(&frame.stack, S32, 2); assert_true(mScriptInvoke(&val, &frame)); mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "ifn0", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(TestA), &s); + mSCRIPT_PUSH(&frame.stack, S(TestA), &s); assert_true(mScriptInvoke(&val, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_true(mScriptPopS32(&frame.stack, &rval)); assert_int_equal(rval, 4); mScriptFrameDeinit(&frame); assert_true(mScriptObjectGet(&sval, "icfn0", &val)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, CS(TestA), &s); + mSCRIPT_PUSH(&frame.stack, CS(TestA), &s); assert_true(mScriptInvoke(&val, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &rval)); + assert_true(mScriptPopS32(&frame.stack, &rval)); assert_int_equal(rval, 4); mScriptFrameDeinit(&frame); @@ -1228,16 +1228,16 @@ M_TEST_DEFINE(testOverloadsBasic) { assert_true(mScriptObjectGet(&sval, "call", &fn)); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(TestI), &s); - mSCRIPT_PUSH(&frame.arguments, U32, 1); + mSCRIPT_PUSH(&frame.stack, S(TestI), &s); + mSCRIPT_PUSH(&frame.stack, U32, 1); assert_true(mScriptInvoke(&fn, &frame)); mScriptFrameDeinit(&frame); assert_int_equal(s.num, 1); assert_null(s.str); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(TestI), &s); - mSCRIPT_PUSH(&frame.arguments, CHARP, "called"); + mSCRIPT_PUSH(&frame.stack, S(TestI), &s); + mSCRIPT_PUSH(&frame.stack, CHARP, "called"); assert_true(mScriptInvoke(&fn, &frame)); mScriptFrameDeinit(&frame); assert_int_equal(s.num, 1); diff --git a/src/script/test/lua.c b/src/script/test/lua.c index 49c5521d7..388d79084 100644 --- a/src/script/test/lua.c +++ b/src/script/test/lua.c @@ -328,10 +328,10 @@ M_TEST_DEFINE(callLuaFunc) { struct mScriptFrame frame; mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S32, 1); + mSCRIPT_PUSH(&frame.stack, S32, 1); assert_true(mScriptInvoke(fn, &frame)); int64_t val; - assert_true(mScriptPopS64(&frame.returnValues, &val)); + assert_true(mScriptPopS64(&frame.stack, &val)); assert_int_equal(val, 2); mScriptFrameDeinit(&frame); @@ -342,10 +342,10 @@ M_TEST_DEFINE(callLuaFunc) { assert_int_equal(fn->type->base, mSCRIPT_TYPE_FUNCTION); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S32, 1); - mSCRIPT_PUSH(&frame.arguments, S32, 2); + mSCRIPT_PUSH(&frame.stack, S32, 1); + mSCRIPT_PUSH(&frame.stack, S32, 2); assert_true(mScriptInvoke(fn, &frame)); - assert_true(mScriptPopS64(&frame.returnValues, &val)); + assert_true(mScriptPopS64(&frame.stack, &val)); assert_int_equal(val, 3); mScriptFrameDeinit(&frame); diff --git a/src/script/test/types.c b/src/script/test/types.c index 67b6fba9a..51420eecd 100644 --- a/src/script/test/types.c +++ b/src/script/test/types.c @@ -111,7 +111,7 @@ M_TEST_DEFINE(voidArgs) { mScriptFrameInit(&frame); assert_true(mScriptInvoke(&boundVoidOne, &frame)); int32_t val; - assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_true(mScriptPopS32(&frame.stack, &val)); assert_int_equal(val, 1); mScriptFrameDeinit(&frame); } @@ -119,7 +119,7 @@ M_TEST_DEFINE(voidArgs) { M_TEST_DEFINE(voidFunc) { struct mScriptFrame frame; mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S32, 1); + mSCRIPT_PUSH(&frame.stack, S32, 1); assert_true(mScriptInvoke(&boundDiscard, &frame)); mScriptFrameDeinit(&frame); } @@ -127,10 +127,10 @@ M_TEST_DEFINE(voidFunc) { M_TEST_DEFINE(identityFunctionS32) { struct mScriptFrame frame; mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S32, 1); + mSCRIPT_PUSH(&frame.stack, S32, 1); assert_true(mScriptInvoke(&boundIdentityInt, &frame)); int32_t val; - assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_true(mScriptPopS32(&frame.stack, &val)); assert_int_equal(val, 1); mScriptFrameDeinit(&frame); } @@ -138,10 +138,10 @@ M_TEST_DEFINE(identityFunctionS32) { M_TEST_DEFINE(identityFunctionS64) { struct mScriptFrame frame; mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S64, 1); + mSCRIPT_PUSH(&frame.stack, S64, 1); assert_true(mScriptInvoke(&boundIdentityInt64, &frame)); int64_t val; - assert_true(mScriptPopS64(&frame.returnValues, &val)); + assert_true(mScriptPopS64(&frame.stack, &val)); assert_int_equal(val, 1); mScriptFrameDeinit(&frame); } @@ -149,10 +149,10 @@ M_TEST_DEFINE(identityFunctionS64) { M_TEST_DEFINE(identityFunctionF32) { struct mScriptFrame frame; mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, F32, 3.125f); + mSCRIPT_PUSH(&frame.stack, F32, 3.125f); assert_true(mScriptInvoke(&boundIdentityFloat, &frame)); float val; - assert_true(mScriptPopF32(&frame.returnValues, &val)); + assert_true(mScriptPopF32(&frame.stack, &val)); assert_float_equal(val, 3.125f, 0.f); mScriptFrameDeinit(&frame); } @@ -161,10 +161,10 @@ M_TEST_DEFINE(identityFunctionStruct) { struct mScriptFrame frame; struct Test v = {}; mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(Test), &v); + mSCRIPT_PUSH(&frame.stack, S(Test), &v); assert_true(mScriptInvoke(&boundIdentityStruct, &frame)); struct Test* val; - assert_true(mScriptPopPointer(&frame.returnValues, (void**) &val)); + assert_true(mScriptPopPointer(&frame.stack, (void**) &val)); assert_ptr_equal(val, &v); mScriptFrameDeinit(&frame); } @@ -172,11 +172,11 @@ M_TEST_DEFINE(identityFunctionStruct) { M_TEST_DEFINE(addS32) { struct mScriptFrame frame; mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S32, 1); - mSCRIPT_PUSH(&frame.arguments, S32, 2); + mSCRIPT_PUSH(&frame.stack, S32, 1); + mSCRIPT_PUSH(&frame.stack, S32, 2); assert_true(mScriptInvoke(&boundAddInts, &frame)); int32_t val; - assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_true(mScriptPopS32(&frame.stack, &val)); assert_int_equal(val, 3); mScriptFrameDeinit(&frame); } @@ -186,17 +186,17 @@ M_TEST_DEFINE(addS32Defaults) { int32_t val; mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S32, 1); - mSCRIPT_PUSH(&frame.arguments, S32, 2); + mSCRIPT_PUSH(&frame.stack, S32, 1); + mSCRIPT_PUSH(&frame.stack, S32, 2); assert_true(mScriptInvoke(&boundAddIntWithDefaults, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_true(mScriptPopS32(&frame.stack, &val)); assert_int_equal(val, 3); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S32, 1); + mSCRIPT_PUSH(&frame.stack, S32, 1); assert_true(mScriptInvoke(&boundAddIntWithDefaults, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_true(mScriptPopS32(&frame.stack, &val)); assert_int_equal(val, 1); mScriptFrameDeinit(&frame); @@ -208,11 +208,11 @@ M_TEST_DEFINE(addS32Defaults) { M_TEST_DEFINE(subS32) { struct mScriptFrame frame; mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S32, 2); - mSCRIPT_PUSH(&frame.arguments, S32, 1); + mSCRIPT_PUSH(&frame.stack, S32, 2); + mSCRIPT_PUSH(&frame.stack, S32, 1); assert_true(mScriptInvoke(&boundSubInts, &frame)); int32_t val; - assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_true(mScriptPopS32(&frame.stack, &val)); assert_int_equal(val, 1); mScriptFrameDeinit(&frame); } @@ -227,8 +227,8 @@ M_TEST_DEFINE(wrongArgCountLo) { M_TEST_DEFINE(wrongArgCountHi) { struct mScriptFrame frame; mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S32, 1); - mSCRIPT_PUSH(&frame.arguments, S32, 1); + mSCRIPT_PUSH(&frame.stack, S32, 1); + mSCRIPT_PUSH(&frame.stack, S32, 1); assert_false(mScriptInvoke(&boundIdentityInt, &frame)); mScriptFrameDeinit(&frame); } @@ -236,7 +236,7 @@ M_TEST_DEFINE(wrongArgCountHi) { M_TEST_DEFINE(wrongArgType) { struct mScriptFrame frame; mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S32, 1); + mSCRIPT_PUSH(&frame.stack, S32, 1); assert_false(mScriptInvoke(&boundIdentityStruct, &frame)); mScriptFrameDeinit(&frame); } @@ -252,55 +252,55 @@ M_TEST_DEFINE(wrongPopType) { bool b; mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S32, 0); - assert_false(mScriptPopU32(&frame.arguments, &u32)); - assert_false(mScriptPopF32(&frame.arguments, &f32)); - assert_false(mScriptPopBool(&frame.arguments, &b)); + mSCRIPT_PUSH(&frame.stack, S32, 0); + assert_false(mScriptPopU32(&frame.stack, &u32)); + assert_false(mScriptPopF32(&frame.stack, &f32)); + assert_false(mScriptPopBool(&frame.stack, &b)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S64, 0); - assert_false(mScriptPopU64(&frame.arguments, &u64)); - assert_false(mScriptPopF64(&frame.arguments, &f64)); - assert_false(mScriptPopBool(&frame.arguments, &b)); + mSCRIPT_PUSH(&frame.stack, S64, 0); + assert_false(mScriptPopU64(&frame.stack, &u64)); + assert_false(mScriptPopF64(&frame.stack, &f64)); + assert_false(mScriptPopBool(&frame.stack, &b)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, U32, 0); - assert_false(mScriptPopS32(&frame.arguments, &s32)); - assert_false(mScriptPopF32(&frame.arguments, &f32)); - assert_false(mScriptPopBool(&frame.arguments, &b)); + mSCRIPT_PUSH(&frame.stack, U32, 0); + assert_false(mScriptPopS32(&frame.stack, &s32)); + assert_false(mScriptPopF32(&frame.stack, &f32)); + assert_false(mScriptPopBool(&frame.stack, &b)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, U64, 0); - assert_false(mScriptPopS64(&frame.arguments, &s64)); - assert_false(mScriptPopF64(&frame.arguments, &f64)); - assert_false(mScriptPopBool(&frame.arguments, &b)); + mSCRIPT_PUSH(&frame.stack, U64, 0); + assert_false(mScriptPopS64(&frame.stack, &s64)); + assert_false(mScriptPopF64(&frame.stack, &f64)); + assert_false(mScriptPopBool(&frame.stack, &b)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, F32, 0); - assert_false(mScriptPopS32(&frame.arguments, &s32)); - assert_false(mScriptPopU32(&frame.arguments, &u32)); - assert_false(mScriptPopBool(&frame.arguments, &b)); + mSCRIPT_PUSH(&frame.stack, F32, 0); + assert_false(mScriptPopS32(&frame.stack, &s32)); + assert_false(mScriptPopU32(&frame.stack, &u32)); + assert_false(mScriptPopBool(&frame.stack, &b)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, F64, 0); - assert_false(mScriptPopS64(&frame.arguments, &s64)); - assert_false(mScriptPopU64(&frame.arguments, &u64)); - assert_false(mScriptPopBool(&frame.arguments, &b)); + mSCRIPT_PUSH(&frame.stack, F64, 0); + assert_false(mScriptPopS64(&frame.stack, &s64)); + assert_false(mScriptPopU64(&frame.stack, &u64)); + assert_false(mScriptPopBool(&frame.stack, &b)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, BOOL, 0); - assert_false(mScriptPopS32(&frame.arguments, &s32)); - assert_false(mScriptPopU32(&frame.arguments, &u32)); - assert_false(mScriptPopS64(&frame.arguments, &s64)); - assert_false(mScriptPopU64(&frame.arguments, &u64)); - assert_false(mScriptPopF32(&frame.arguments, &f32)); - assert_false(mScriptPopF64(&frame.arguments, &f64)); + mSCRIPT_PUSH(&frame.stack, BOOL, 0); + assert_false(mScriptPopS32(&frame.stack, &s32)); + assert_false(mScriptPopU32(&frame.stack, &u32)); + assert_false(mScriptPopS64(&frame.stack, &s64)); + assert_false(mScriptPopU64(&frame.stack, &u64)); + assert_false(mScriptPopF32(&frame.stack, &f32)); + assert_false(mScriptPopF64(&frame.stack, &f64)); mScriptFrameDeinit(&frame); } @@ -314,33 +314,33 @@ M_TEST_DEFINE(wrongPopSize) { double f64; mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S32, 0); - assert_false(mScriptPopS64(&frame.arguments, &s64)); + mSCRIPT_PUSH(&frame.stack, S32, 0); + assert_false(mScriptPopS64(&frame.stack, &s64)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S64, 0); - assert_false(mScriptPopS32(&frame.arguments, &s32)); + mSCRIPT_PUSH(&frame.stack, S64, 0); + assert_false(mScriptPopS32(&frame.stack, &s32)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, U32, 0); - assert_false(mScriptPopU64(&frame.arguments, &u64)); + mSCRIPT_PUSH(&frame.stack, U32, 0); + assert_false(mScriptPopU64(&frame.stack, &u64)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, U64, 0); - assert_false(mScriptPopU32(&frame.arguments, &u32)); + mSCRIPT_PUSH(&frame.stack, U64, 0); + assert_false(mScriptPopU32(&frame.stack, &u32)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, F32, 0); - assert_false(mScriptPopF64(&frame.arguments, &f64)); + mSCRIPT_PUSH(&frame.stack, F32, 0); + assert_false(mScriptPopF64(&frame.stack, &f64)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, F64, 0); - assert_false(mScriptPopF32(&frame.arguments, &f32)); + mSCRIPT_PUSH(&frame.stack, F64, 0); + assert_false(mScriptPopF32(&frame.stack, &f32)); mScriptFrameDeinit(&frame); } @@ -370,63 +370,63 @@ M_TEST_DEFINE(wrongConst) { mScriptClassInit(mSCRIPT_TYPE_MS_CS(Test)->details.cls); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(Test), &a); + mSCRIPT_PUSH(&frame.stack, S(Test), &a); signature.entries[0] = mSCRIPT_TYPE_MS_S(Test); - assert_true(mScriptCoerceFrame(&signature, &frame.arguments, &frame.arguments)); + assert_true(mScriptCoerceFrame(&signature, &frame.stack, &frame.stack)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, CS(Test), &a); + mSCRIPT_PUSH(&frame.stack, CS(Test), &a); signature.entries[0] = mSCRIPT_TYPE_MS_CS(Test); - assert_true(mScriptCoerceFrame(&signature, &frame.arguments, &frame.arguments)); + assert_true(mScriptCoerceFrame(&signature, &frame.stack, &frame.stack)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(Test), &a); + mSCRIPT_PUSH(&frame.stack, S(Test), &a); signature.entries[0] = mSCRIPT_TYPE_MS_CS(Test); - assert_true(mScriptCoerceFrame(&signature, &frame.arguments, &frame.arguments)); + assert_true(mScriptCoerceFrame(&signature, &frame.stack, &frame.stack)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, CS(Test), &a); + mSCRIPT_PUSH(&frame.stack, CS(Test), &a); signature.entries[0] = mSCRIPT_TYPE_MS_S(Test); - assert_false(mScriptCoerceFrame(&signature, &frame.arguments, &frame.arguments)); + assert_false(mScriptCoerceFrame(&signature, &frame.stack, &frame.stack)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(Test), &a); - assert_true(mScriptPopSTest(&frame.arguments, &b)); + mSCRIPT_PUSH(&frame.stack, S(Test), &a); + assert_true(mScriptPopSTest(&frame.stack, &b)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(Test), &a); - assert_false(mScriptPopCSTest(&frame.arguments, &cb)); + mSCRIPT_PUSH(&frame.stack, S(Test), &a); + assert_false(mScriptPopCSTest(&frame.stack, &cb)); signature.entries[0] = mSCRIPT_TYPE_MS_CS(Test); - assert_true(mScriptCoerceFrame(&signature, &frame.arguments, &frame.arguments)); - assert_true(mScriptPopCSTest(&frame.arguments, &cb)); + assert_true(mScriptCoerceFrame(&signature, &frame.stack, &frame.stack)); + assert_true(mScriptPopCSTest(&frame.stack, &cb)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, CS(Test), &a); - assert_false(mScriptPopSTest(&frame.arguments, &b)); + mSCRIPT_PUSH(&frame.stack, CS(Test), &a); + assert_false(mScriptPopSTest(&frame.stack, &b)); signature.entries[0] = mSCRIPT_TYPE_MS_S(Test); - assert_false(mScriptCoerceFrame(&signature, &frame.arguments, &frame.arguments)); - assert_false(mScriptPopSTest(&frame.arguments, &b)); + assert_false(mScriptCoerceFrame(&signature, &frame.stack, &frame.stack)); + assert_false(mScriptPopSTest(&frame.stack, &b)); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, CS(Test), &a); - assert_true(mScriptPopCSTest(&frame.arguments, &cb)); + mSCRIPT_PUSH(&frame.stack, CS(Test), &a); + assert_true(mScriptPopCSTest(&frame.stack, &cb)); mScriptFrameDeinit(&frame); } M_TEST_DEFINE(coerceToFloat) { struct mScriptFrame frame; mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S32, 1); + mSCRIPT_PUSH(&frame.stack, S32, 1); assert_true(mScriptInvoke(&boundIdentityFloat, &frame)); float val; - assert_true(mScriptPopF32(&frame.returnValues, &val)); + assert_true(mScriptPopF32(&frame.stack, &val)); assert_float_equal(val, 1.f, 0.f); mScriptFrameDeinit(&frame); } @@ -434,10 +434,10 @@ M_TEST_DEFINE(coerceToFloat) { M_TEST_DEFINE(coerceFromFloat) { struct mScriptFrame frame; mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, F32, 1.25f); + mSCRIPT_PUSH(&frame.stack, F32, 1.25f); assert_true(mScriptInvoke(&boundIdentityInt, &frame)); int val; - assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_true(mScriptPopS32(&frame.stack, &val)); assert_int_equal(val, 1); mScriptFrameDeinit(&frame); } @@ -539,10 +539,10 @@ M_TEST_DEFINE(coerceFromBool) { M_TEST_DEFINE(coerceWiden) { struct mScriptFrame frame; mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S32, -1); + mSCRIPT_PUSH(&frame.stack, S32, -1); assert_true(mScriptInvoke(&boundIdentityInt64, &frame)); int64_t val; - assert_true(mScriptPopS64(&frame.returnValues, &val)); + assert_true(mScriptPopS64(&frame.stack, &val)); assert_true(val == -1LL); mScriptFrameDeinit(&frame); } @@ -550,10 +550,10 @@ M_TEST_DEFINE(coerceWiden) { M_TEST_DEFINE(coerceNarrow) { struct mScriptFrame frame; mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S64, -1); + mSCRIPT_PUSH(&frame.stack, S64, -1); assert_true(mScriptInvoke(&boundIdentityInt, &frame)); int32_t val; - assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_true(mScriptPopS32(&frame.stack, &val)); assert_true(val == -1); mScriptFrameDeinit(&frame); } @@ -1244,10 +1244,10 @@ M_TEST_DEFINE(hashTableString) { M_TEST_DEFINE(stringIsHello) { struct mScriptFrame frame; mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, CHARP, "hello"); + mSCRIPT_PUSH(&frame.stack, CHARP, "hello"); assert_true(mScriptInvoke(&boundIsHello, &frame)); int val; - assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_true(mScriptPopS32(&frame.stack, &val)); assert_int_equal(val, 1); mScriptFrameDeinit(&frame); } @@ -1255,10 +1255,10 @@ M_TEST_DEFINE(stringIsHello) { M_TEST_DEFINE(stringIsNotHello) { struct mScriptFrame frame; mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, CHARP, "world"); + mSCRIPT_PUSH(&frame.stack, CHARP, "world"); assert_true(mScriptInvoke(&boundIsHello, &frame)); int val; - assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_true(mScriptPopS32(&frame.stack, &val)); assert_int_equal(val, 0); mScriptFrameDeinit(&frame); } @@ -1271,33 +1271,33 @@ M_TEST_DEFINE(invokeList) { mScriptListInit(&list, 0); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, LIST, &list); + mSCRIPT_PUSH(&frame.stack, LIST, &list); assert_true(mScriptInvoke(&boundIsSequential, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_true(mScriptPopS32(&frame.stack, &val)); assert_int_equal(val, 1); mScriptFrameDeinit(&frame); *mScriptListAppend(&list) = mSCRIPT_MAKE_S32(1); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, LIST, &list); + mSCRIPT_PUSH(&frame.stack, LIST, &list); assert_true(mScriptInvoke(&boundIsSequential, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_true(mScriptPopS32(&frame.stack, &val)); assert_int_equal(val, 1); mScriptFrameDeinit(&frame); *mScriptListAppend(&list) = mSCRIPT_MAKE_S32(2); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, LIST, &list); + mSCRIPT_PUSH(&frame.stack, LIST, &list); assert_true(mScriptInvoke(&boundIsSequential, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_true(mScriptPopS32(&frame.stack, &val)); assert_int_equal(val, 1); mScriptFrameDeinit(&frame); *mScriptListAppend(&list) = mSCRIPT_MAKE_S32(4); mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, LIST, &list); + mSCRIPT_PUSH(&frame.stack, LIST, &list); assert_true(mScriptInvoke(&boundIsSequential, &frame)); - assert_true(mScriptPopS32(&frame.returnValues, &val)); + assert_true(mScriptPopS32(&frame.stack, &val)); assert_int_equal(val, 0); mScriptFrameDeinit(&frame); @@ -1309,14 +1309,14 @@ M_TEST_DEFINE(nullString) { bool res; mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, CHARP, "hi"); + mSCRIPT_PUSH(&frame.stack, CHARP, "hi"); assert_true(mScriptInvoke(&boundIsNullCharp, &frame)); - assert_true(mScriptPopBool(&frame.returnValues, &res)); + assert_true(mScriptPopBool(&frame.stack, &res)); assert_false(res); - mSCRIPT_PUSH(&frame.arguments, CHARP, NULL); + mSCRIPT_PUSH(&frame.stack, CHARP, NULL); assert_true(mScriptInvoke(&boundIsNullCharp, &frame)); - assert_true(mScriptPopBool(&frame.returnValues, &res)); + assert_true(mScriptPopBool(&frame.stack, &res)); assert_true(res); mScriptFrameDeinit(&frame); @@ -1328,14 +1328,14 @@ M_TEST_DEFINE(nullStruct) { bool res; mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, S(Test), &v); + mSCRIPT_PUSH(&frame.stack, S(Test), &v); assert_true(mScriptInvoke(&boundIsNullStruct, &frame)); - assert_true(mScriptPopBool(&frame.returnValues, &res)); + assert_true(mScriptPopBool(&frame.stack, &res)); assert_false(res); - mSCRIPT_PUSH(&frame.arguments, S(Test), NULL); + mSCRIPT_PUSH(&frame.stack, S(Test), NULL); assert_true(mScriptInvoke(&boundIsNullStruct, &frame)); - assert_true(mScriptPopBool(&frame.returnValues, &res)); + assert_true(mScriptPopBool(&frame.stack, &res)); assert_true(res); mScriptFrameDeinit(&frame); diff --git a/src/script/types.c b/src/script/types.c index 00422d20c..799c6fb71 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -1118,13 +1118,11 @@ bool mScriptTableIteratorLookup(struct mScriptValue* table, struct TableIterator } void mScriptFrameInit(struct mScriptFrame* frame) { - mScriptListInit(&frame->arguments, 4); - mScriptListInit(&frame->returnValues, 1); + mScriptListInit(&frame->stack, 4); } void mScriptFrameDeinit(struct mScriptFrame* frame) { - mScriptListDeinit(&frame->returnValues); - mScriptListDeinit(&frame->arguments); + mScriptListDeinit(&frame->stack); } struct mScriptValue* mScriptLambdaCreate0(struct mScriptValue* fn, struct mScriptList* args) { @@ -1150,15 +1148,15 @@ struct mScriptValue* mScriptLambdaCreate0(struct mScriptValue* fn, struct mScrip } bool _callLambda0(struct mScriptFrame* frame, void* context) { - if (mScriptListSize(&frame->arguments)) { + if (mScriptListSize(&frame->stack)) { return false; } struct mScriptLambda* lambda = context; struct mScriptFrame subframe; mScriptFrameInit(&subframe); - mScriptListCopy(&subframe.arguments, &lambda->arguments); + mScriptListCopy(&subframe.stack, &lambda->arguments); bool ok = mScriptInvoke(lambda->fn, &subframe); - if (mScriptListSize(&subframe.returnValues)) { + if (mScriptListSize(&subframe.stack)) { ok = false; } mScriptFrameDeinit(&subframe); @@ -1399,17 +1397,17 @@ bool mScriptObjectGet(struct mScriptValue* obj, const char* member, struct mScri } struct mScriptFrame frame; mScriptFrameInit(&frame); - struct mScriptValue* this = mScriptListAppend(&frame.arguments); + struct mScriptValue* this = mScriptListAppend(&frame.stack); this->type = obj->type; this->refs = mSCRIPT_VALUE_UNREF; this->flags = 0; this->value.opaque = obj->value.opaque; - mSCRIPT_PUSH(&frame.arguments, CHARP, member); - if (!mScriptInvoke(&getMember, &frame) || mScriptListSize(&frame.returnValues) != 1) { + mSCRIPT_PUSH(&frame.stack, CHARP, member); + if (!mScriptInvoke(&getMember, &frame) || mScriptListSize(&frame.stack) != 1) { mScriptFrameDeinit(&frame); return false; } - memcpy(val, mScriptListGetPointer(&frame.returnValues, 0), sizeof(*val)); + memcpy(val, mScriptListGetPointer(&frame.stack, 0), sizeof(*val)); mScriptFrameDeinit(&frame); return true; } @@ -1547,14 +1545,14 @@ bool mScriptObjectSet(struct mScriptValue* obj, const char* member, struct mScri } struct mScriptFrame frame; mScriptFrameInit(&frame); - struct mScriptValue* this = mScriptListAppend(&frame.arguments); + struct mScriptValue* this = mScriptListAppend(&frame.stack); this->type = obj->type; this->refs = mSCRIPT_VALUE_UNREF; this->flags = 0; this->value.opaque = obj->value.opaque; - mSCRIPT_PUSH(&frame.arguments, CHARP, member); - mScriptValueWrap(val, mScriptListAppend(&frame.arguments)); - if (!mScriptInvoke(&setMember, &frame) || mScriptListSize(&frame.returnValues) != 0) { + mSCRIPT_PUSH(&frame.stack, CHARP, member); + mScriptValueWrap(val, mScriptListAppend(&frame.stack)); + if (!mScriptInvoke(&setMember, &frame) || mScriptListSize(&frame.stack) != 0) { mScriptFrameDeinit(&frame); return false; } @@ -1665,7 +1663,7 @@ void mScriptObjectFree(struct mScriptValue* value) { if (_accessRawMember(value->type->details.cls->free, value->value.opaque, value->type->isConst, &deinitMember)) { struct mScriptFrame frame; mScriptFrameInit(&frame); - mSCRIPT_PUSH(&frame.arguments, WRAPPER, value); + mSCRIPT_PUSH(&frame.stack, WRAPPER, value); mScriptInvoke(&deinitMember, &frame); mScriptFrameDeinit(&frame); } diff --git a/src/tools/docgen.c b/src/tools/docgen.c index cdabb472c..e2c444ed2 100644 --- a/src/tools/docgen.c +++ b/src/tools/docgen.c @@ -362,7 +362,7 @@ bool call(struct mScriptValue* obj, const char* method, struct mScriptFrame* fra if (!mScriptObjectGet(obj, method, &fn)) { return false; } - mSCRIPT_PUSH(&frame->arguments, WRAPPER, obj); + mSCRIPT_PUSH(&frame->stack, WRAPPER, obj); return mScriptInvoke(&fn, frame); } @@ -394,12 +394,12 @@ void explainCore(struct mCore* core) { mScriptFrameInit(&frame); call(value, "base", &frame); - mScriptPopU32(&frame.returnValues, &baseVal); + mScriptPopU32(&frame.stack, &baseVal); mScriptFrameDeinit(&frame); mScriptFrameInit(&frame); call(value, "name", &frame); - shortName = mScriptValueUnwrap(mScriptListGetPointer(&frame.returnValues, 0)); + shortName = mScriptValueUnwrap(mScriptListGetPointer(&frame.stack, 0)); mScriptFrameDeinit(&frame); fprintf(out, " base: 0x%x\n", baseVal); From cfd30b7a95edf1dafaf5cfb6d691578d8b6554c5 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 8 Nov 2024 21:38:25 -0800 Subject: [PATCH 077/114] GBA Savedata: Fix erroneously setting the error bit --- src/gba/savedata.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/gba/savedata.c b/src/gba/savedata.c index ca5d35cbb..ced279b7f 100644 --- a/src/gba/savedata.c +++ b/src/gba/savedata.c @@ -376,9 +376,10 @@ uint8_t GBASavedataReadFlash(struct GBASavedata* savedata, uint16_t address) { } } if (mTimingIsScheduled(savedata->timing, &savedata->dust) && (address >> 12) == savedata->settling) { - // This should read /Q7 ("data# polling") and Q6 flipping ("toggle bit") every read, - // but just data# polling is sufficient for games to figure it out - return savedata->currentBank[address] ^ 0x80; + // This should read /Q7 ("data# polling"), Q6 flipping ("toggle bit") + // every read, and /Q5 ("error bit" cleared), but just data# polling + // is sufficient for games to figure it out + return (savedata->currentBank[address] ^ 0x80) & 0x80; } return savedata->currentBank[address]; } From 3d493239d77676a183e0a61426cf41036dbe2f1f Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 17 Nov 2024 02:27:19 -0800 Subject: [PATCH 078/114] GBA Savedata: Update flash busy comment --- src/gba/savedata.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gba/savedata.c b/src/gba/savedata.c index ced279b7f..a9cd682ca 100644 --- a/src/gba/savedata.c +++ b/src/gba/savedata.c @@ -376,9 +376,9 @@ uint8_t GBASavedataReadFlash(struct GBASavedata* savedata, uint16_t address) { } } if (mTimingIsScheduled(savedata->timing, &savedata->dust) && (address >> 12) == savedata->settling) { - // This should read /Q7 ("data# polling"), Q6 flipping ("toggle bit") - // every read, and /Q5 ("error bit" cleared), but just data# polling - // is sufficient for games to figure it out + // This should read Q7 XOR data bit 7 (data# polling), Q6 flipping + // every read (toggle bit), and /Q5 (error bit cleared), but implementing + // just data# polling is sufficient for games to figure it out return (savedata->currentBank[address] ^ 0x80) & 0x80; } return savedata->currentBank[address]; From d5fbd0ff1cbc88dd22d9ad81a5e660f1ddae9249 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 17 Nov 2024 02:24:44 -0800 Subject: [PATCH 079/114] GBA VFame: Refactor reordering code to be simpler --- src/gba/cart/vfame.c | 79 +++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 41 deletions(-) diff --git a/src/gba/cart/vfame.c b/src/gba/cart/vfame.c index 260db35b8..b66b7d756 100644 --- a/src/gba/cart/vfame.c +++ b/src/gba/cart/vfame.c @@ -1,4 +1,5 @@ /* Copyright (c) 2016 taizou + * Copyright (c) 2013-2024 Jeffrey Pfau * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -8,35 +9,42 @@ #include #include -static const uint8_t ADDRESS_REORDERING[4][16] = { - { 15, 14, 9, 1, 8, 10, 7, 3, 5, 11, 4, 0, 13, 12, 2, 6 }, - { 15, 7, 13, 5, 11, 6, 0, 9, 12, 2, 10, 14, 3, 1, 8, 4 }, - { 15, 0, 3, 12, 2, 4, 14, 13, 1, 8, 6, 7, 9, 5, 11, 10 } +#define DIGIMON_SAPPHIRE_CHINESE_CRC32 0x793A328F + +static const uint8_t ADDRESS_REORDERING[][3][16] = { + [VFAME_STANDARD] = { + { 15, 14, 9, 1, 8, 10, 7, 3, 5, 11, 4, 0, 13, 12, 2, 6 }, + { 15, 7, 13, 5, 11, 6, 0, 9, 12, 2, 10, 14, 3, 1, 8, 4 }, + { 15, 0, 3, 12, 2, 4, 14, 13, 1, 8, 6, 7, 9, 5, 11, 10 } + }, + [VFAME_GEORGE] = { + { 15, 7, 13, 1, 11, 10, 14, 9, 12, 2, 4, 0, 3, 5, 8, 6 }, + { 15, 14, 3, 12, 8, 4, 0, 13, 5, 11, 6, 7, 9, 1, 2, 10 }, + { 15, 0, 9, 5, 2, 6, 7, 3, 1, 8, 10, 14, 13, 12, 11, 4 } + }, + [VFAME_ALTERNATE] = { + { 15, 0, 13, 5, 8, 4, 7, 3, 1, 2, 10, 14, 9, 12, 11, 6 }, + { 15, 7, 9, 1, 2, 6, 14, 13, 12, 11, 4, 0, 3, 5, 8, 10 }, + { 15, 14, 3, 12, 11, 10, 0, 9, 5, 8, 6, 7, 13, 1, 2, 4 } + }, }; -static const uint8_t ADDRESS_REORDERING_GEORGE[4][16] = { - { 15, 7, 13, 1, 11, 10, 14, 9, 12, 2, 4, 0, 3, 5, 8, 6 }, - { 15, 14, 3, 12, 8, 4, 0, 13, 5, 11, 6, 7, 9, 1, 2, 10 }, - { 15, 0, 9, 5, 2, 6, 7, 3, 1, 8, 10, 14, 13, 12, 11, 4 } -}; -static const uint8_t ADDRESS_REORDERING_ALTERNATE[4][16] = { - { 15, 0, 13, 5, 8, 4, 7, 3, 1, 2, 10, 14, 9, 12, 11, 6 }, - { 15, 7, 9, 1, 2, 6, 14, 13, 12, 11, 4, 0, 3, 5, 8, 10 }, - { 15, 14, 3, 12, 11, 10, 0, 9, 5, 8, 6, 7, 13, 1, 2, 4 } -}; -static const uint8_t VALUE_REORDERING[4][16] = { - { 5, 4, 3, 2, 1, 0, 7, 6 }, - { 3, 2, 1, 0, 7, 6, 5, 4 }, - { 1, 0, 7, 6, 5, 4, 3, 2 } -}; -static const uint8_t VALUE_REORDERING_GEORGE[4][16] = { - { 3, 0, 7, 2, 1, 4, 5, 6 }, - { 1, 4, 3, 0, 5, 6, 7, 2 }, - { 5, 2, 1, 6, 7, 0, 3, 4 } -}; -static const uint8_t VALUE_REORDERING_ALTERNATE[4][16] = { - { 5, 4, 7, 2, 1, 0, 3, 6 }, - { 1, 2, 3, 0, 5, 6, 7, 4 }, - { 3, 0, 1, 6, 7, 4, 5, 2 } + +static const uint8_t VALUE_REORDERING[][3][8] = { + [VFAME_STANDARD] = { + { 5, 4, 3, 2, 1, 0, 7, 6 }, + { 3, 2, 1, 0, 7, 6, 5, 4 }, + { 1, 0, 7, 6, 5, 4, 3, 2 } + }, + [VFAME_GEORGE] = { + { 3, 0, 7, 2, 1, 4, 5, 6 }, + { 1, 4, 3, 0, 5, 6, 7, 2 }, + { 5, 2, 1, 6, 7, 0, 3, 4 } + }, + [VFAME_ALTERNATE] = { + { 5, 4, 7, 2, 1, 0, 3, 6 }, + { 1, 2, 3, 0, 5, 6, 7, 4 }, + { 3, 0, 1, 6, 7, 4, 5, 2 } + }, }; static const int8_t MODE_CHANGE_START_SEQUENCE[5] = { 0x99, 0x02, 0x05, 0x02, 0x03 }; @@ -268,25 +276,14 @@ static uint32_t _modifySramAddress(enum GBAVFameCartType type, uint32_t address, mode &= 0x3; if (mode == 0) { return address; - } else if (type == VFAME_GEORGE) { - return _reorderBits(address, ADDRESS_REORDERING_GEORGE[mode - 1], 16); - } else if (type == VFAME_ALTERNATE) { - return _reorderBits(address, ADDRESS_REORDERING_ALTERNATE[mode - 1], 16); - } else { - return _reorderBits(address, ADDRESS_REORDERING[mode - 1], 16); } + return _reorderBits(address, ADDRESS_REORDERING[type][mode - 1], 16); } static int8_t _modifySramValue(enum GBAVFameCartType type, uint8_t value, int mode) { int reorderType = (mode & 0xF) >> 2; if (reorderType != 0) { - if (type == VFAME_GEORGE) { - value = _reorderBits(value, VALUE_REORDERING_GEORGE[reorderType - 1], 8); - } else if (type == VFAME_ALTERNATE) { - value = _reorderBits(value, VALUE_REORDERING_ALTERNATE[reorderType - 1], 8); - } else { - value = _reorderBits(value, VALUE_REORDERING[reorderType - 1], 8); - } + value = _reorderBits(value, VALUE_REORDERING[type][reorderType - 1], 8); } if (mode & 0x80) { value ^= 0xAA; From 91cf8292617c912d6b49236e9342476a4e763fbd Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 17 Nov 2024 02:25:12 -0800 Subject: [PATCH 080/114] GBA Memory: Add initial support for N-in-1 style multicarts --- include/mgba/internal/gba/cart/unlicensed.h | 75 +++++++ include/mgba/internal/gba/cart/vfame.h | 41 ---- include/mgba/internal/gba/memory.h | 4 +- include/mgba/internal/gba/serialize.h | 58 +++++- src/gba/CMakeLists.txt | 1 + src/gba/cart/matrix.c | 4 +- src/gba/cart/unlicensed.c | 219 ++++++++++++++++++++ src/gba/cart/vfame.c | 29 +-- src/gba/gba.c | 5 +- src/gba/io.c | 2 + src/gba/memory.c | 15 +- src/gba/overrides.c | 6 + src/gba/serialize.c | 31 ++- 13 files changed, 409 insertions(+), 81 deletions(-) create mode 100644 include/mgba/internal/gba/cart/unlicensed.h delete mode 100644 include/mgba/internal/gba/cart/vfame.h create mode 100644 src/gba/cart/unlicensed.c diff --git a/include/mgba/internal/gba/cart/unlicensed.h b/include/mgba/internal/gba/cart/unlicensed.h new file mode 100644 index 000000000..ec13abb55 --- /dev/null +++ b/include/mgba/internal/gba/cart/unlicensed.h @@ -0,0 +1,75 @@ +/* Copyright (c) 2013-2024 Jeffrey Pfau + * Copyright (c) 2016 taizou + * + * This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ +#ifndef GBA_UNLICENSED_H +#define GBA_UNLICENSED_H + +#include + +CXX_GUARD_START + +#include + +enum GBAVFameCartType { + VFAME_STANDARD = 0, + VFAME_GEORGE = 1, + VFAME_ALTERNATE = 2, +}; + +enum GBAUnlCartType { + GBA_UNL_CART_NONE = 0, + GBA_UNL_CART_VFAME = 1, + GBA_UNL_CART_MULTICART = 2, +}; + +struct GBAVFameCart { + enum GBAVFameCartType cartType; + int sramMode; + int romMode; + int8_t writeSequence[5]; + bool acceptingModeChange; +}; + +struct GBAMulticart { + struct mTimingEvent settle; + uint32_t* rom; + size_t fullSize; + + uint8_t bank; + uint8_t offset; + uint8_t size; + bool sramActive; + uint8_t unk; +}; + +struct GBAUnlCart { + enum GBAUnlCartType type; + union { + struct GBAVFameCart vfame; + struct GBAMulticart multi; + }; +}; + +struct GBA; +struct GBAMemory; +void GBAUnlCartInit(struct GBA*); +void GBAUnlCartReset(struct GBA*); +void GBAUnlCartUnload(struct GBA*); +void GBAUnlCartDetect(struct GBA*); +void GBAUnlCartWriteSRAM(struct GBA*, uint32_t address, uint8_t value); + +struct GBASerializedState; +void GBAUnlCartSerialize(const struct GBA* gba, struct GBASerializedState* state); +void GBAUnlCartDeserialize(struct GBA* gba, const struct GBASerializedState* state); + +bool GBAVFameDetect(struct GBAVFameCart* cart, uint32_t* rom, size_t romSize, uint32_t crc32); +void GBAVFameSramWrite(struct GBAVFameCart* cart, uint32_t address, uint8_t value, uint8_t* sramData); +uint32_t GBAVFameModifyRomAddress(struct GBAVFameCart* cart, uint32_t address, size_t romSize); +uint32_t GBAVFameGetPatternValue(uint32_t address, int bits); + +CXX_GUARD_END + +#endif diff --git a/include/mgba/internal/gba/cart/vfame.h b/include/mgba/internal/gba/cart/vfame.h deleted file mode 100644 index 5bee28848..000000000 --- a/include/mgba/internal/gba/cart/vfame.h +++ /dev/null @@ -1,41 +0,0 @@ -/* Copyright (c) 2016 taizou - * - * This Source Code Form is subject to the terms of the Mozilla Public - * 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/. */ - -// Support for copy protected unlicensed games from the company Vast Fame - -#ifndef GBA_VFAME_H -#define GBA_VFAME_H - -#include - -CXX_GUARD_START - -#define DIGIMON_SAPPHIRE_CHINESE_CRC32 0x793A328F - -enum GBAVFameCartType { - VFAME_NO = 0, - VFAME_STANDARD = 1, - VFAME_GEORGE = 2, - VFAME_ALTERNATE = 3, -}; - -struct GBAVFameCart { - enum GBAVFameCartType cartType; - int sramMode; - int romMode; - int8_t writeSequence[5]; - bool acceptingModeChange; -}; - -void GBAVFameInit(struct GBAVFameCart* cart); -void GBAVFameDetect(struct GBAVFameCart* cart, uint32_t* rom, size_t romSize, uint32_t crc32); -void GBAVFameSramWrite(struct GBAVFameCart* cart, uint32_t address, uint8_t value, uint8_t* sramData); -uint32_t GBAVFameModifyRomAddress(struct GBAVFameCart* cart, uint32_t address, size_t romSize); -uint32_t GBAVFameGetPatternValue(uint32_t address, int bits); - -CXX_GUARD_END - -#endif diff --git a/include/mgba/internal/gba/memory.h b/include/mgba/internal/gba/memory.h index c88e1d092..1b32763fc 100644 --- a/include/mgba/internal/gba/memory.h +++ b/include/mgba/internal/gba/memory.h @@ -18,7 +18,7 @@ CXX_GUARD_START #include #include #include -#include +#include enum GBAMemoryRegion { GBA_REGION_BIOS = 0x0, @@ -108,8 +108,8 @@ struct GBAMemory { struct GBACartridgeHardware hw; struct GBASavedata savedata; - struct GBAVFameCart vfame; struct GBAMatrix matrix; + struct GBAUnlCart unl; struct GBACartEReader ereader; size_t romSize; uint32_t romMask; diff --git a/include/mgba/internal/gba/serialize.h b/include/mgba/internal/gba/serialize.h index 8b1823a17..48393ed7b 100644 --- a/include/mgba/internal/gba/serialize.h +++ b/include/mgba/internal/gba/serialize.h @@ -186,11 +186,14 @@ mLOG_DECLARE_CATEGORY(GBA_STATE); * | bit 3: Reserved * | bits 4 - 15: Light counter * | 0x002C0 - 0x002C0: Light sample - * | 0x002C1 - 0x002C3: Flags + * | 0x002C1: Flags * | bits 0 - 1: Tilt state machine * | bits 2 - 3: GB Player inputs posted - * | bits 4 - 8: GB Player transmit position - * | bits 9 - 23: Reserved + * | bits 4 - 7: GB Player transmit position + * | 0x002C2 - 0x002C3: Unlicensed cart flags + * | bits 0 - 4: Cartridge type + * | bits 5 - 7: Cartridge subtype + * | bits 8 - 15: Reserved * 0x002C4 - 0x002C7: SIO next event * 0x002C8 - 0x002CB: Current DMA transfer word * 0x002CC - 0x002CF: Last DMA transfer PC @@ -230,8 +233,23 @@ mLOG_DECLARE_CATEGORY(GBA_STATE); * | bits 15 - 31: Reserved * 0x00320 - 0x00323: Next IRQ event * 0x00324 - 0x00327: Interruptable BIOS stall cycles - * 0x00328 - 0x00367: Matrix memory mapping table - * 0x00368 - 0x0036F: Reserved (leave zero) + * 0x00328 - 0x0036F: Special cartridge state, one of: + * | Matrix Memory: + * | 0x00328 - 0x00367: Matrix memory mapping table + * | 0x00368 - 0x0036F: Reserved (leave zero) + * | Unlicensed multicart: + * | 0x00328: Bank value + * | 0x00329: Offset value + * | 0x0032A: Size value + * | 0x0032B: SRAM active value + * | 0x0032C: Unknown value + * | 0x0032D: Current size + * | 0x0032E - 0x0032F: Current bank/offset + * | 0x00330 - 0x00333: Next settle event + * | 0x00334 - 0x00337: Flags + * | bit 0: Is settling occuring? + * | bits 1 - 31: Reserved + * | 0x00338 - 0x0036F: Reserved (leave zero) * 0x00370 - 0x0037F: Audio FIFO A samples * 0x00380 - 0x0038F: Audio FIFO B samples * 0x00390 - 0x003CF: Audio rendered samples @@ -269,9 +287,14 @@ DECL_BITS(GBASerializedHWFlags1, LightCounter, 4, 12); DECL_BITFIELD(GBASerializedHWFlags2, uint8_t); DECL_BITS(GBASerializedHWFlags2, TiltState, 0, 2); DECL_BITS(GBASerializedHWFlags2, GbpInputsPosted, 2, 2); -DECL_BITS(GBASerializedHWFlags2, GbpTxPosition, 4, 5); +DECL_BITS(GBASerializedHWFlags2, GbpTxPosition, 4, 4); -DECL_BITFIELD(GBASerializedHWFlags3, uint16_t); +DECL_BITFIELD(GBASerializedUnlCartFlags, uint16_t); +DECL_BITS(GBASerializedUnlCartFlags, Type, 0, 5); +DECL_BITS(GBASerializedUnlCartFlags, Subtype, 5, 3); + +DECL_BITFIELD(GBASerializedMulticartFlags, uint32_t); +DECL_BIT(GBASerializedMulticartFlags, DustSettling, 0); DECL_BITFIELD(GBASerializedSavedataFlags, uint8_t); DECL_BITS(GBASerializedSavedataFlags, FlashState, 0, 2); @@ -370,7 +393,7 @@ struct GBASerializedState { GBASerializedHWFlags1 flags1; uint8_t lightSample; GBASerializedHWFlags2 flags2; - GBASerializedHWFlags3 flags3; + GBASerializedUnlCartFlags unlCartFlags; uint32_t sioNextEvent; } hw; @@ -407,8 +430,23 @@ struct GBASerializedState { uint32_t nextIrq; int32_t biosStall; - uint32_t matrixMappings[16]; - uint32_t reservedMatrix[2]; + union { + struct { + uint32_t mappings[16]; + uint32_t reserved[2]; + } matrix2; + struct { + uint8_t bank; + uint8_t offset; + uint8_t size; + uint8_t sramActive; + uint8_t unk; + uint8_t currentSize; + uint16_t currentOffset; + uint32_t settleNextEvent; + GBASerializedMulticartFlags flags; + } multicart; + }; struct { int8_t chA[16]; diff --git a/src/gba/CMakeLists.txt b/src/gba/CMakeLists.txt index 01a968a41..71ab7bbb1 100644 --- a/src/gba/CMakeLists.txt +++ b/src/gba/CMakeLists.txt @@ -6,6 +6,7 @@ set(SOURCE_FILES cart/ereader.c cart/gpio.c cart/matrix.c + cart/unlicensed.c cart/vfame.c cheats.c cheats/codebreaker.c diff --git a/src/gba/cart/matrix.c b/src/gba/cart/matrix.c index c594d66b9..9ccbe8832 100644 --- a/src/gba/cart/matrix.c +++ b/src/gba/cart/matrix.c @@ -109,7 +109,7 @@ void GBAMatrixSerialize(const struct GBA* gba, struct GBASerializedState* state) int i; for (i = 0; i < 16; ++i) { - STORE_32(gba->memory.matrix.mappings[i], i << 2, state->matrixMappings); + STORE_32(gba->memory.matrix.mappings[i], i << 2, state->matrix2.mappings); } } @@ -121,7 +121,7 @@ void GBAMatrixDeserialize(struct GBA* gba, const struct GBASerializedState* stat int i; for (i = 0; i < 16; ++i) { - LOAD_32(gba->memory.matrix.mappings[i], i << 2, state->matrixMappings); + LOAD_32(gba->memory.matrix.mappings[i], i << 2, state->matrix2.mappings); gba->memory.matrix.paddr = gba->memory.matrix.mappings[i]; gba->memory.matrix.vaddr = i << 9; _remapMatrix(gba); diff --git a/src/gba/cart/unlicensed.c b/src/gba/cart/unlicensed.c new file mode 100644 index 000000000..318ffe891 --- /dev/null +++ b/src/gba/cart/unlicensed.c @@ -0,0 +1,219 @@ +/* Copyright (c) 2013-2024 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * 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 + +#include +#include +#include +#include + +#define MULTI_SETTLE 512 +#define MULTI_BLOCK 0x80000 +#define MULTI_BANK 0x2000000 + +enum GBMulticartCfgOffset { + GBA_MULTICART_CFG_BANK = 0x2, + GBA_MULTICART_CFG_OFFSET = 0x3, + GBA_MULTICART_CFG_SIZE = 0x4, + GBA_MULTICART_CFG_SRAM = 0x5, + GBA_MULTICART_CFG_UNK = 0x6, +}; + +static void _multicartSettle(struct mTiming* timing, void* context, uint32_t cyclesLate); + +void GBAUnlCartInit(struct GBA* gba) { + memset(&gba->memory.unl, 0, sizeof(gba->memory.unl)); +} + +void GBAUnlCartDetect(struct GBA* gba) { + if (!gba->memory.rom) { + return; + } + + struct GBACartridge* cart = (struct GBACartridge*) gba->memory.rom; + if (GBAVFameDetect(&gba->memory.unl.vfame, gba->memory.rom, gba->memory.romSize, gba->romCrc32)) { + gba->memory.unl.type = GBA_UNL_CART_VFAME; + return; + } + + if (memcmp(&cart->id, "AXVJ01", 6) == 0) { + if (gba->romVf && gba->romVf->size(gba->romVf) >= 0x04000000) { + // Bootleg multicart + // TODO: Identify a bit more precisely + gba->isPristine = false; + GBASavedataInitSRAM(&gba->memory.savedata); + gba->memory.unl.type = GBA_UNL_CART_MULTICART; + + gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->memory.romSize); + gba->memory.unl.multi.fullSize = gba->romVf->size(gba->romVf); + gba->memory.unl.multi.rom = gba->romVf->map(gba->romVf, gba->memory.unl.multi.fullSize, MAP_READ); + gba->memory.rom = gba->memory.unl.multi.rom; + gba->memory.hw.gpioBase = NULL; + + gba->memory.unl.multi.settle.context = gba; + gba->memory.unl.multi.settle.callback = _multicartSettle; + gba->memory.unl.multi.settle.name = "GBA Unlicensed Multicart Settle"; + gba->memory.unl.multi.settle.priority = 0x71; + } + } +} + +void GBAUnlCartReset(struct GBA* gba) { + if (gba->memory.unl.type == GBA_UNL_CART_MULTICART) { + gba->memory.unl.multi.bank = 0; + gba->memory.unl.multi.offset = 0; + gba->memory.unl.multi.size = 0; + gba->memory.rom = gba->memory.unl.multi.rom; + gba->memory.romSize = GBA_SIZE_ROM0; + } +} + +void GBAUnlCartUnload(struct GBA* gba) { + if (gba->memory.unl.type == GBA_UNL_CART_MULTICART && gba->romVf) { + gba->romVf->unmap(gba->romVf, gba->memory.unl.multi.rom, gba->memory.unl.multi.size); + gba->memory.unl.multi.rom = NULL; + gba->memory.rom = NULL; + } +} + +void GBAUnlCartWriteSRAM(struct GBA* gba, uint32_t address, uint8_t value) { + struct GBAUnlCart* unl = &gba->memory.unl; + + switch (unl->type) { + case GBA_UNL_CART_VFAME: + GBAVFameSramWrite(&unl->vfame, address, value, gba->memory.savedata.data); + return; + case GBA_UNL_CART_MULTICART: + mLOG(GBA_MEM, DEBUG, "Multicart writing SRAM %06X:%02X", address, value); + switch (address) { + case GBA_MULTICART_CFG_BANK: + unl->multi.bank = value >> 4; + mTimingDeschedule(&gba->timing, &unl->multi.settle); + mTimingSchedule(&gba->timing, &unl->multi.settle, MULTI_SETTLE); + break; + case GBA_MULTICART_CFG_OFFSET: + unl->multi.offset = value & 0x3F; + mTimingDeschedule(&gba->timing, &unl->multi.settle); + mTimingSchedule(&gba->timing, &unl->multi.settle, MULTI_SETTLE); + break; + case GBA_MULTICART_CFG_SIZE: + unl->multi.size = 0x40 - (value & 0x3F); + mTimingDeschedule(&gba->timing, &unl->multi.settle); + mTimingSchedule(&gba->timing, &unl->multi.settle, MULTI_SETTLE); + break; + case GBA_MULTICART_CFG_SRAM: + if (value == 0 && unl->multi.sramActive) { + unl->multi.sramActive = false; + } else if (value == 1 && !unl->multi.sramActive) { + unl->multi.sramActive = true; + } + break; + case GBA_MULTICART_CFG_UNK: + // TODO: What does this do? + unl->multi.unk = value; + break; + default: + break; + } + break; + case GBA_UNL_CART_NONE: + break; + } + + gba->memory.savedata.data[address & (GBA_SIZE_SRAM - 1)] = value; +} + +static void _multicartSettle(struct mTiming* timing, void* context, uint32_t cyclesLate) { + UNUSED(timing); + UNUSED(cyclesLate); + struct GBA* gba = context; + mLOG(GBA_MEM, INFO, "Switching to bank %i offset %i, size %i", + gba->memory.unl.multi.bank, gba->memory.unl.multi.offset, gba->memory.unl.multi.size); + size_t offset = gba->memory.unl.multi.bank * (MULTI_BANK >> 2) + gba->memory.unl.multi.offset * (MULTI_BLOCK >> 2); + size_t size = gba->memory.unl.multi.size * MULTI_BLOCK; + if (offset * 4 >= gba->memory.unl.multi.fullSize || offset * 4 + size > gba->memory.unl.multi.fullSize) { + mLOG(GBA_MEM, GAME_ERROR, "Bank switch was out of bounds, %07" PRIz "X + %" PRIz "X > %07" PRIz "X", + offset * 4, size, gba->memory.unl.multi.fullSize); + return; + } + gba->memory.rom = gba->memory.unl.multi.rom + offset; + gba->memory.romSize = size; +} + +void GBAUnlCartSerialize(const struct GBA* gba, struct GBASerializedState* state) { + GBASerializedUnlCartFlags flags = 0; + const struct GBAUnlCart* unl = &gba->memory.unl; + switch (unl->type) { + case GBA_UNL_CART_NONE: + return; + case GBA_UNL_CART_VFAME: + flags = GBASerializedUnlCartFlagsSetType(flags, GBA_UNL_CART_VFAME); + flags = GBASerializedUnlCartFlagsSetSubtype(flags, unl->vfame.cartType); + mLOG(GBA_MEM, STUB, "Vast Fame save states are not yet implemented"); + break; + case GBA_UNL_CART_MULTICART: + flags = GBASerializedUnlCartFlagsSetType(0, GBA_UNL_CART_MULTICART); + state->multicart.bank = unl->multi.bank; + state->multicart.offset = unl->multi.offset; + state->multicart.size = unl->multi.size; + state->multicart.sramActive = unl->multi.sramActive; + state->multicart.unk = unl->multi.unk; + state->multicart.currentSize = gba->memory.romSize / MULTI_BLOCK; + STORE_16((gba->memory.rom - unl->multi.rom) / 0x20000, 0, &state->multicart.currentOffset); + STORE_32(unl->multi.settle.when, 0, &state->multicart.settleNextEvent); + if (mTimingIsScheduled(&gba->timing, &unl->multi.settle)) { + STORE_32(GBASerializedMulticartFlagsFillDustSettling(0), 0, &state->multicart.flags); + } + break; + } + STORE_32(flags, 0, &state->hw.unlCartFlags); +} + +void GBAUnlCartDeserialize(struct GBA* gba, const struct GBASerializedState* state) { + GBASerializedUnlCartFlags flags; + struct GBAUnlCart* unl = &gba->memory.unl; + + LOAD_32(flags, 0, &state->hw.unlCartFlags); + enum GBAUnlCartType type = GBASerializedUnlCartFlagsGetType(flags); + if (type != unl->type) { + mLOG(GBA_STATE, WARN, "Save state expects different bootleg type; not restoring bootleg state"); + return; + } + + uint32_t when; + uint32_t offset; + size_t size; + GBASerializedMulticartFlags multiFlags; + + switch (type) { + case GBA_UNL_CART_NONE: + return; + case GBA_UNL_CART_VFAME: + mLOG(GBA_MEM, STUB, "Vast Fame save states are not yet implemented"); + return; + case GBA_UNL_CART_MULTICART: + unl->multi.bank = state->multicart.bank; + unl->multi.offset = state->multicart.offset; + unl->multi.size = state->multicart.size; + unl->multi.sramActive = state->multicart.sramActive; + unl->multi.unk = state->multicart.unk; + size = state->multicart.currentSize * MULTI_BLOCK; + LOAD_16(offset, 0, &state->multicart.currentOffset); + offset *= 0x20000; + if (offset * 4 >= gba->memory.unl.multi.fullSize || offset * 4 + size > gba->memory.unl.multi.fullSize) { + mLOG(GBA_STATE, WARN, "Multicart save state has corrupted ROM offset"); + } else { + gba->memory.romSize = size; + gba->memory.rom = unl->multi.rom + offset; + } + LOAD_32(multiFlags, 0, &state->multicart.flags); + if (GBASerializedMulticartFlagsIsDustSettling(multiFlags)) { + LOAD_32(when, 0, &state->multicart.settleNextEvent); + mTimingSchedule(&gba->timing, &unl->multi.settle, when); + } + break; + } +} diff --git a/src/gba/cart/vfame.c b/src/gba/cart/vfame.c index b66b7d756..a45a420c0 100644 --- a/src/gba/cart/vfame.c +++ b/src/gba/cart/vfame.c @@ -4,7 +4,7 @@ * This Source Code Form is subject to the terms of the Mozilla Public * 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 +#include #include #include @@ -60,25 +60,19 @@ static int8_t _modifySramValue(enum GBAVFameCartType type, uint8_t value, int mo static uint32_t _modifySramAddress(enum GBAVFameCartType type, uint32_t address, int mode); static int _reorderBits(uint32_t value, const uint8_t* reordering, int reorderLength); -void GBAVFameInit(struct GBAVFameCart* cart) { - cart->cartType = VFAME_NO; - cart->sramMode = -1; - cart->romMode = -1; - cart->acceptingModeChange = false; -} - -void GBAVFameDetect(struct GBAVFameCart* cart, uint32_t* rom, size_t romSize, uint32_t crc32) { - cart->cartType = VFAME_NO; - +bool GBAVFameDetect(struct GBAVFameCart* cart, uint32_t* rom, size_t romSize, uint32_t crc32) { // The initialisation code is also present & run in the dumps of Digimon Ruby & Sapphire from hacked/deprotected reprint carts, // which would break if run in "proper" VFame mode so we need to exclude those.. if (romSize == 0x2000000) { // the deprotected dumps are 32MB but no real VF games are this size - return; + return false; } + bool detected = false; + // Most games have the same init sequence in the same place // but LOTR/Mo Jie Qi Bing doesn't, probably because it's based on the Kiki KaiKai engine, so just detect based on its title if (memcmp(INIT_SEQUENCE, &rom[0x57], sizeof(INIT_SEQUENCE)) == 0 || memcmp("\0LORD\0WORD\0\0AKIJ", &((struct GBACartridge*) rom)->title, 16) == 0) { + detected = true; cart->cartType = VFAME_STANDARD; mLOG(GBA_MEM, INFO, "Vast Fame game detected"); } @@ -87,13 +81,23 @@ void GBAVFameDetect(struct GBAVFameCart* cart, uint32_t* rom, size_t romSize, ui // Their initialisation seems to be identical so the difference must be in the cart HW itself // Other undumped games may have similar differences if (memcmp("George Sango", &((struct GBACartridge*) rom)->title, 12) == 0) { + detected = true; cart->cartType = VFAME_GEORGE; mLOG(GBA_MEM, INFO, "George mode"); } else if (crc32 == DIGIMON_SAPPHIRE_CHINESE_CRC32) { // Chinese version of Digimon Sapphire; header is identical to the English version which uses the normal reordering // so we have to use some other way to detect it + detected = true; cart->cartType = VFAME_ALTERNATE; } + + if (detected) { + cart->sramMode = -1; + cart->romMode = -1; + cart->acceptingModeChange = false; + } + + return detected; } // This is not currently being used but would be called on ROM reads @@ -235,7 +239,6 @@ static uint32_t _patternRightShift2(uint32_t addr) { } void GBAVFameSramWrite(struct GBAVFameCart* cart, uint32_t address, uint8_t value, uint8_t* sramData) { - address &= 0x00FFFFFF; // A certain sequence of writes to SRAM FFF8->FFFC can enable or disable "mode change" mode // Currently unknown if these writes have to be sequential, or what happens if you write different values, if anything if (address >= 0xFFF8 && address <= 0xFFFC) { diff --git a/src/gba/gba.c b/src/gba/gba.c index 6b833838e..1b84038ae 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -140,6 +140,9 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) { void GBAUnloadROM(struct GBA* gba) { GBAMemoryClearAGBPrint(gba); + if (gba->memory.unl.type) { + GBAUnlCartUnload(gba); + } if (gba->memory.rom && !gba->isPristine) { if (gba->yankedRomSize) { gba->yankedRomSize = 0; @@ -492,7 +495,7 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) { gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]); } GBAHardwareInit(&gba->memory.hw, &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1]); - GBAVFameDetect(&gba->memory.vfame, gba->memory.rom, gba->memory.romSize, gba->romCrc32); + GBAUnlCartDetect(gba); // TODO: error check return true; } diff --git a/src/gba/io.c b/src/gba/io.c index cb6058f7f..a72171c1f 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -1033,6 +1033,7 @@ void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) { STORE_32(gba->bus, 0, &state->bus); GBAHardwareSerialize(&gba->memory.hw, state); + GBAUnlCartSerialize(gba, state); } void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) { @@ -1082,4 +1083,5 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) { GBADMARecalculateCycles(gba); GBADMAUpdate(gba); GBAHardwareDeserialize(&gba->memory.hw, state); + GBAUnlCartDeserialize(gba, state); } diff --git a/src/gba/memory.c b/src/gba/memory.c index 87e31f746..53d0f7cc9 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -93,7 +93,7 @@ void GBAMemoryInit(struct GBA* gba) { gba->memory.iwram = &gba->memory.wram[GBA_SIZE_EWRAM >> 2]; GBADMAInit(gba); - GBAVFameInit(&gba->memory.vfame); + GBAUnlCartInit(gba); gba->memory.ereader.p = gba; gba->memory.ereader.dots = NULL; @@ -139,6 +139,7 @@ void GBAMemoryReset(struct GBA* gba) { } GBADMAReset(gba); + GBAUnlCartReset(gba); memset(&gba->memory.matrix, 0, sizeof(gba->memory.matrix)); } @@ -411,7 +412,7 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { wait += waitstatesRegion[address >> BASE_OFFSET]; \ if ((address & (GBA_SIZE_ROM0 - 4)) < memory->romSize) { \ LOAD_32(value, address & (GBA_SIZE_ROM0 - 4), memory->rom); \ - } else if (memory->vfame.cartType) { \ + } else if (memory->unl.type == GBA_UNL_CART_VFAME) { \ value = GBAVFameGetPatternValue(address, 32); \ } else { \ mLOG(GBA_MEM, GAME_ERROR, "Out of bounds ROM Load32: 0x%08X", address); \ @@ -576,7 +577,7 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { wait = memory->waitstatesNonseq16[address >> BASE_OFFSET]; if ((address & (GBA_SIZE_ROM0 - 2)) < memory->romSize) { LOAD_16(value, address & (GBA_SIZE_ROM0 - 2), memory->rom); - } else if (memory->vfame.cartType) { + } else if (memory->unl.type == GBA_UNL_CART_VFAME) { value = GBAVFameGetPatternValue(address, 16); } else if ((address & (GBA_SIZE_ROM0 - 2)) >= AGB_PRINT_BASE) { uint32_t agbPrintAddr = address & 0x00FFFFFF; @@ -601,7 +602,7 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { value = GBACartEReaderRead(&memory->ereader, address); } else if ((address & (GBA_SIZE_ROM0 - 2)) < memory->romSize) { LOAD_16(value, address & (GBA_SIZE_ROM0 - 2), memory->rom); - } else if (memory->vfame.cartType) { + } else if (memory->unl.type == GBA_UNL_CART_VFAME) { value = GBAVFameGetPatternValue(address, 16); } else { mLOG(GBA_MEM, GAME_ERROR, "Out of bounds ROM Load16: 0x%08X", address); @@ -692,7 +693,7 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { wait = memory->waitstatesNonseq16[address >> BASE_OFFSET]; if ((address & (GBA_SIZE_ROM0 - 1)) < memory->romSize) { value = ((uint8_t*) memory->rom)[address & (GBA_SIZE_ROM0 - 1)]; - } else if (memory->vfame.cartType) { + } else if (memory->unl.type == GBA_UNL_CART_VFAME) { value = GBAVFameGetPatternValue(address, 8); } else { mLOG(GBA_MEM, GAME_ERROR, "Out of bounds ROM Load8: 0x%08X", address); @@ -1056,8 +1057,8 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo } else if (memory->savedata.type == GBA_SAVEDATA_FLASH512 || memory->savedata.type == GBA_SAVEDATA_FLASH1M) { GBASavedataWriteFlash(&memory->savedata, address, value); } else if (memory->savedata.type == GBA_SAVEDATA_SRAM) { - if (memory->vfame.cartType) { - GBAVFameSramWrite(&memory->vfame, address, value, memory->savedata.data); + if (memory->unl.type) { + GBAUnlCartWriteSRAM(gba, address & 0xFFFF, value); } else { memory->savedata.data[address & (GBA_SIZE_SRAM - 1)] = value; } diff --git a/src/gba/overrides.c b/src/gba/overrides.c index a63616e61..1e3e9e6fc 100644 --- a/src/gba/overrides.c +++ b/src/gba/overrides.c @@ -377,6 +377,12 @@ void GBAOverrideApplyDefaults(struct GBA* gba, const struct Configuration* overr struct GBACartridgeOverride override = { .idleLoop = GBA_IDLE_LOOP_NONE }; const struct GBACartridge* cart = (const struct GBACartridge*) gba->memory.rom; if (cart) { + if (gba->memory.unl.type == GBA_UNL_CART_MULTICART) { + override.savetype = GBA_SAVEDATA_SRAM; + GBAOverrideApply(gba, &override); + return; + } + memcpy(override.id, &cart->id, sizeof(override.id)); static const uint32_t pokemonTable[] = { diff --git a/src/gba/serialize.c b/src/gba/serialize.c index 5eabaedea..9d6130385 100644 --- a/src/gba/serialize.c +++ b/src/gba/serialize.c @@ -32,8 +32,17 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) { STORE_64LE(gba->timing.globalCycles, 0, &state->globalCycles); if (gba->memory.rom) { - state->id = ((struct GBACartridge*) gba->memory.rom)->id; - memcpy(state->title, ((struct GBACartridge*) gba->memory.rom)->title, sizeof(state->title)); + switch (gba->memory.unl.type) { + case GBA_UNL_CART_NONE: + case GBA_UNL_CART_VFAME: + state->id = ((struct GBACartridge*) gba->memory.rom)->id; + memcpy(state->title, ((struct GBACartridge*) gba->memory.rom)->title, sizeof(state->title)); + break; + case GBA_UNL_CART_MULTICART: + state->id = ((struct GBACartridge*) gba->memory.unl.multi.rom)->id; + memcpy(state->title, ((struct GBACartridge*) gba->memory.unl.multi.rom)->title, sizeof(state->title)); + break; + } } else { state->id = 0; memset(state->title, 0, sizeof(state->title)); @@ -106,9 +115,21 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) { error = true; } } - if (gba->memory.rom && (state->id != ((struct GBACartridge*) gba->memory.rom)->id || memcmp(state->title, ((struct GBACartridge*) gba->memory.rom)->title, sizeof(state->title)))) { - mLOG(GBA_STATE, WARN, "Savestate is for a different game"); - error = true; + if (gba->memory.rom) { + struct GBACartridge* cart; + switch (gba->memory.unl.type) { + case GBA_UNL_CART_NONE: + case GBA_UNL_CART_VFAME: + cart = (struct GBACartridge*) gba->memory.rom; + break; + case GBA_UNL_CART_MULTICART: + cart = (struct GBACartridge*) gba->memory.unl.multi.rom; + break; + } + if (state->id != cart->id || memcmp(state->title, cart->title, sizeof(state->title))) { + mLOG(GBA_STATE, WARN, "Savestate is for a different game"); + error = true; + } } else if (!gba->memory.rom && state->id != 0) { mLOG(GBA_STATE, WARN, "Savestate is for a game, but no game loaded"); error = true; From dc263295dd58f6b03726767220f9b401a53a971c Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 17 Nov 2024 03:34:33 -0800 Subject: [PATCH 081/114] GBA Memory: Add unlicensed cart write handling stub --- include/mgba/internal/gba/cart/unlicensed.h | 1 + src/gba/cart/unlicensed.c | 13 +++++++++++++ src/gba/memory.c | 4 ++++ 3 files changed, 18 insertions(+) diff --git a/include/mgba/internal/gba/cart/unlicensed.h b/include/mgba/internal/gba/cart/unlicensed.h index ec13abb55..816aa1dfd 100644 --- a/include/mgba/internal/gba/cart/unlicensed.h +++ b/include/mgba/internal/gba/cart/unlicensed.h @@ -60,6 +60,7 @@ void GBAUnlCartReset(struct GBA*); void GBAUnlCartUnload(struct GBA*); void GBAUnlCartDetect(struct GBA*); void GBAUnlCartWriteSRAM(struct GBA*, uint32_t address, uint8_t value); +void GBAUnlCartWriteROM(struct GBA*, uint32_t address, uint16_t value); struct GBASerializedState; void GBAUnlCartSerialize(const struct GBA* gba, struct GBASerializedState* state); diff --git a/src/gba/cart/unlicensed.c b/src/gba/cart/unlicensed.c index 318ffe891..442ec9865 100644 --- a/src/gba/cart/unlicensed.c +++ b/src/gba/cart/unlicensed.c @@ -126,6 +126,19 @@ void GBAUnlCartWriteSRAM(struct GBA* gba, uint32_t address, uint8_t value) { gba->memory.savedata.data[address & (GBA_SIZE_SRAM - 1)] = value; } +void GBAUnlCartWriteROM(struct GBA* gba, uint32_t address, uint16_t value) { + struct GBAUnlCart* unl = &gba->memory.unl; + + switch (unl->type) { + case GBA_UNL_CART_VFAME: + case GBA_UNL_CART_NONE: + break; + case GBA_UNL_CART_MULTICART: + mLOG(GBA_MEM, STUB, "Unimplemented writing to ROM %07X:%04X", address, value); + break; + } +} + static void _multicartSettle(struct mTiming* timing, void* context, uint32_t cyclesLate) { UNUSED(timing); UNUSED(cyclesLate); diff --git a/src/gba/memory.c b/src/gba/memory.c index 53d0f7cc9..1561aef91 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -963,6 +963,10 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle break; } } + if (memory->unl.type) { + GBAUnlCartWriteROM(gba, address & (GBA_SIZE_ROM0 - 1), value); + break; + } mLOG(GBA_MEM, GAME_ERROR, "Bad cartridge Store16: 0x%08X", address); break; case GBA_REGION_ROM2_EX: From d9b43463d0e1d8b006c5333edccecb6c2639d43b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 18 Nov 2024 00:43:27 -0800 Subject: [PATCH 082/114] CMake: Set both -mmacosx-version-min and CMAKE_OSX_DEPLOYMENT_TARGET --- CMakeLists.txt | 2 +- src/platform/qt/CMakeLists.txt | 1 + src/platform/sdl/CMakeLists.txt | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7857e9c38..dd4a08829 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -249,7 +249,7 @@ if(APPLE) endif() if(NOT CMAKE_SYSTEM_VERSION VERSION_LESS "10.0") # Darwin 10.x is Mac OS X 10.6 - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.6") + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.6") endif() # Not supported until Xcode 9 if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_LESS "9") diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 3cfe83ad0..530fe4cfe 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -79,6 +79,7 @@ if(APPLE) endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=${MIN_VER}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=${MIN_VER}") + set(CMAKE_OSX_DEPLOYMENT_TARGET ${MIN_VER}) if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") endif() diff --git a/src/platform/sdl/CMakeLists.txt b/src/platform/sdl/CMakeLists.txt index d62c74694..e4dd17673 100644 --- a/src/platform/sdl/CMakeLists.txt +++ b/src/platform/sdl/CMakeLists.txt @@ -24,6 +24,7 @@ if (SDL_VERSION EQUAL "2") if(APPLE) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.7") + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.7") endif() set(SDLMAIN_LIBRARY ${SDL2MAIN_LIBRARY}) endif() From 2a0ed00d0d41e44f587168ea354f0a5ab15acba5 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 18 Nov 2024 00:55:55 -0800 Subject: [PATCH 083/114] CMake: I'm tired of putting out fires --- CMakeLists.txt | 1 + src/platform/qt/CMakeLists.txt | 2 -- src/platform/sdl/CMakeLists.txt | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dd4a08829..5daa78976 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -759,6 +759,7 @@ if(USE_ELF) endif() if (USE_DISCORD_RPC) + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.7") add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/third-party/discord-rpc discord-rpc EXCLUDE_FROM_ALL) list(APPEND FEATURES DISCORD_RPC) include_directories(AFTER ${CMAKE_CURRENT_SOURCE_DIR}/src/third-party/discord-rpc/include) diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 530fe4cfe..3134512f2 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -77,8 +77,6 @@ if(APPLE) else() set(MIN_VER 10.8) endif() - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=${MIN_VER}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=${MIN_VER}") set(CMAKE_OSX_DEPLOYMENT_TARGET ${MIN_VER}) if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") diff --git a/src/platform/sdl/CMakeLists.txt b/src/platform/sdl/CMakeLists.txt index e4dd17673..5246f959b 100644 --- a/src/platform/sdl/CMakeLists.txt +++ b/src/platform/sdl/CMakeLists.txt @@ -23,7 +23,6 @@ if (SDL_VERSION EQUAL "2") endif() if(APPLE) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.7") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.7") endif() set(SDLMAIN_LIBRARY ${SDL2MAIN_LIBRARY}) From cca3267ef6afb411ab24f76938a1c49a82923062 Mon Sep 17 00:00:00 2001 From: CasualPokePlayer <50538166+CasualPokePlayer@users.noreply.github.com> Date: Tue, 19 Nov 2024 22:46:23 -0800 Subject: [PATCH 084/114] Serialize light counter to the correct bitfield Resolves #3294 --- src/gba/cart/gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gba/cart/gpio.c b/src/gba/cart/gpio.c index 73ed859c5..0bffbc088 100644 --- a/src/gba/cart/gpio.c +++ b/src/gba/cart/gpio.c @@ -480,11 +480,11 @@ void GBAHardwareSerialize(const struct GBACartridgeHardware* hw, struct GBASeria STORE_16(hw->tiltY, 0, &state->hw.tiltSampleY); state->hw.lightSample = hw->lightSample; flags1 = GBASerializedHWFlags1SetLightEdge(flags1, hw->lightEdge); + flags1 = GBASerializedHWFlags1SetLightCounter(flags1, hw->lightCounter); STORE_16(flags1, 0, &state->hw.flags1); GBASerializedHWFlags2 flags2 = 0; flags2 = GBASerializedHWFlags2SetTiltState(flags2, hw->tiltState); - flags2 = GBASerializedHWFlags1SetLightCounter(flags2, hw->lightCounter); // GBP/SIO stuff is only here for legacy reasons flags2 = GBASerializedHWFlags2SetGbpInputsPosted(flags2, hw->p->sio.gbp.inputsPosted); From f930184efba64e4c155e67f3495a7d64147243ce Mon Sep 17 00:00:00 2001 From: Adam Higerd Date: Tue, 19 Nov 2024 16:41:42 -0600 Subject: [PATCH 085/114] Qt: save shader settings when OK/Apply clicked --- src/platform/qt/SettingsView.cpp | 5 +++++ src/platform/qt/SettingsView.h | 1 + src/platform/qt/ShaderSelector.cpp | 8 ++++---- src/platform/qt/ShaderSelector.h | 1 + 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index c86dcf06e..f9357bbcb 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -423,8 +423,11 @@ void SettingsView::setShaderSelector(ShaderSelector* shaderSelector) { } if (!m_shader) { m_ui.stackedWidget->removeWidget(m_dummyShader); + } else { + QObject::disconnect(m_shader, nullptr, this, nullptr); } m_shader = shaderSelector; + QObject::connect(this, &SettingsView::saveSettingsRequested, m_shader, &ShaderSelector::saveSettings); if (shaderSelector) { addPage(tr("Shaders"), m_shader, Page::SHADERS); } else { @@ -683,6 +686,8 @@ void SettingsView::updateConfig() { saveSetting("gb.colors", gbColors); #endif + emit saveSettingsRequested(); + m_controller->write(); emit pathsChanged(); diff --git a/src/platform/qt/SettingsView.h b/src/platform/qt/SettingsView.h index 85cf35a99..1e45ec87a 100644 --- a/src/platform/qt/SettingsView.h +++ b/src/platform/qt/SettingsView.h @@ -61,6 +61,7 @@ signals: void pathsChanged(); void languageChanged(); void libraryCleared(); + void saveSettingsRequested(); public slots: void selectPage(Page); diff --git a/src/platform/qt/ShaderSelector.cpp b/src/platform/qt/ShaderSelector.cpp index 44bca3855..ac214349e 100644 --- a/src/platform/qt/ShaderSelector.cpp +++ b/src/platform/qt/ShaderSelector.cpp @@ -49,6 +49,10 @@ ShaderSelector::~ShaderSelector() { clear(); } +void ShaderSelector::saveSettings() { + emit saved(); +} + void ShaderSelector::clear() { m_ui.shaderName->setText(tr("No shader active")); m_ui.description->clear(); @@ -269,10 +273,6 @@ void ShaderSelector::buttonPressed(QAbstractButton* button) { case QDialogButtonBox::Reset: emit reset(); break; - case QDialogButtonBox::Ok: - emit saved(); - close(); - break; case QDialogButtonBox::RestoreDefaults: emit resetToDefault(); break; diff --git a/src/platform/qt/ShaderSelector.h b/src/platform/qt/ShaderSelector.h index 4dc9214af..0726b5edf 100644 --- a/src/platform/qt/ShaderSelector.h +++ b/src/platform/qt/ShaderSelector.h @@ -28,6 +28,7 @@ public: ~ShaderSelector(); public slots: + void saveSettings(); void refreshShaders(); void clear(); From a2e7e5b902043c567167cc4753e74a973209eada Mon Sep 17 00:00:00 2001 From: Adam Higerd Date: Tue, 19 Nov 2024 16:50:12 -0600 Subject: [PATCH 086/114] Qt: don't save shader choice until OK/Apply is clicked --- src/platform/qt/SettingsView.cpp | 1 + src/platform/qt/ShaderSelector.cpp | 40 +++++++++++++++++++++++------- src/platform/qt/ShaderSelector.h | 7 +++--- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index f9357bbcb..a7be40269 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -428,6 +428,7 @@ void SettingsView::setShaderSelector(ShaderSelector* shaderSelector) { } m_shader = shaderSelector; QObject::connect(this, &SettingsView::saveSettingsRequested, m_shader, &ShaderSelector::saveSettings); + QObject::connect(m_ui.buttonBox, &QDialogButtonBox::rejected, m_shader, &ShaderSelector::revert); if (shaderSelector) { addPage(tr("Shaders"), m_shader, Page::SHADERS); } else { diff --git a/src/platform/qt/ShaderSelector.cpp b/src/platform/qt/ShaderSelector.cpp index ac214349e..473b1d7e9 100644 --- a/src/platform/qt/ShaderSelector.cpp +++ b/src/platform/qt/ShaderSelector.cpp @@ -50,7 +50,25 @@ ShaderSelector::~ShaderSelector() { } void ShaderSelector::saveSettings() { - emit saved(); + QString oldPath = config->getOption("shader"); + if (oldPath != m_shaderPath) { + if (m_shaderPath.isEmpty()) { + clearShader(true); + } else { + loadShader(m_shaderPath, true); + } + } + emit saveSettingsRequested(); +} + +void ShaderSelector::revert() { + QString shaderPath = m_config->getOption("shader"); + if (shaderPath.isEmpty()) { + clearShader(); + } else { + loadShader(shaderPath); + emit reset(); + } } void ShaderSelector::clear() { @@ -78,7 +96,7 @@ void ShaderSelector::selectShader() { } } -void ShaderSelector::loadShader(const QString& path) { +void ShaderSelector::loadShader(const QString& path, bool saveToSettings) { VDir* shader = VFileDevice::openDir(path); if (!shader) { shader = VFileDevice::openArchive(path); @@ -89,14 +107,18 @@ void ShaderSelector::loadShader(const QString& path) { m_display->setShaders(shader); shader->close(shader); m_shaderPath = path; - m_config->setOption("shader", m_shaderPath); + if (saveToSettings) { + m_config->setOption("shader", path); + } } -void ShaderSelector::clearShader() { +void ShaderSelector::clearShader(bool saveToSettings) { m_display->clearShaders(); - refreshShaders(); m_shaderPath = ""; - m_config->setOption("shader", m_shaderPath); + if (saveToSettings) { + m_config->setOption("shader", m_shaderPath); + } + refreshShaders(); } void ShaderSelector::refreshShaders() { @@ -121,7 +143,7 @@ void ShaderSelector::refreshShaders() { m_ui.author->clear(); } - disconnect(this, &ShaderSelector::saved, 0, 0); + disconnect(this, &ShaderSelector::saveSettingsRequested, 0, 0); disconnect(this, &ShaderSelector::reset, 0, 0); disconnect(this, &ShaderSelector::resetToDefault, 0, 0); @@ -160,7 +182,7 @@ void ShaderSelector::addUniform(QGridLayout* settings, const QString& section, c connect(f, static_cast(&QDoubleSpinBox::valueChanged), [value](double v) { *value = v; }); - connect(this, &ShaderSelector::saved, [this, section, name, f]() { + connect(this, &ShaderSelector::saveSettingsRequested, [this, section, name, f]() { m_config->setQtOption(name, f->value(), section); }); connect(this, &ShaderSelector::reset, [this, section, name, f]() { @@ -194,7 +216,7 @@ void ShaderSelector::addUniform(QGridLayout* settings, const QString& section, c connect(i, static_cast(&QSpinBox::valueChanged), [value](int v) { *value = v; }); - connect(this, &ShaderSelector::saved, [this, section, name, i]() { + connect(this, &ShaderSelector::saveSettingsRequested, [this, section, name, i]() { m_config->setQtOption(name, i->value(), section); }); connect(this, &ShaderSelector::reset, [this, section, name, i]() { diff --git a/src/platform/qt/ShaderSelector.h b/src/platform/qt/ShaderSelector.h index 0726b5edf..4ba074236 100644 --- a/src/platform/qt/ShaderSelector.h +++ b/src/platform/qt/ShaderSelector.h @@ -31,15 +31,16 @@ public slots: void saveSettings(); void refreshShaders(); void clear(); + void revert(); private slots: void selectShader(); - void loadShader(const QString& path); - void clearShader(); + void loadShader(const QString& path, bool saveToSettings = false); + void clearShader(bool saveToSettings = false); void buttonPressed(QAbstractButton*); signals: - void saved(); + void saveSettingsRequested(); void reset(); void resetToDefault(); From 122128eae56840775132bd2bbe1f856149db5aa3 Mon Sep 17 00:00:00 2001 From: Adam Higerd Date: Tue, 19 Nov 2024 17:20:48 -0600 Subject: [PATCH 087/114] Qt: load unpacked shaders, warn on shader load error --- src/platform/qt/Display.h | 2 +- src/platform/qt/DisplayGL.cpp | 16 +++++++++----- src/platform/qt/DisplayGL.h | 4 ++-- src/platform/qt/DisplayQt.h | 2 +- src/platform/qt/ShaderSelector.cpp | 34 ++++++++++++++++++++---------- 5 files changed, 38 insertions(+), 20 deletions(-) diff --git a/src/platform/qt/Display.h b/src/platform/qt/Display.h index 3cc2fc61f..ef525162f 100644 --- a/src/platform/qt/Display.h +++ b/src/platform/qt/Display.h @@ -81,7 +81,7 @@ public slots: virtual void filter(bool filter); virtual void swapInterval(int interval) = 0; virtual void framePosted() = 0; - virtual void setShaders(struct VDir*) = 0; + virtual bool setShaders(struct VDir*) = 0; virtual void clearShaders() = 0; virtual void resizeContext() = 0; diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index e62e9685e..b3a7d62c8 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -462,8 +462,10 @@ void DisplayGL::framePosted() { QMetaObject::invokeMethod(m_painter.get(), "draw"); } -void DisplayGL::setShaders(struct VDir* shaders) { - QMetaObject::invokeMethod(m_painter.get(), "setShaders", Qt::BlockingQueuedConnection, Q_ARG(struct VDir*, shaders)); +bool DisplayGL::setShaders(struct VDir* shaders) { + bool success = false; + QMetaObject::invokeMethod(m_painter.get(), "setShaders", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, success), Q_ARG(struct VDir*, shaders)); + return success; } void DisplayGL::clearShaders() { @@ -996,10 +998,11 @@ void PainterGL::interrupt() { m_interrupter.interrupt(m_context); } -void PainterGL::setShaders(struct VDir* dir) { +bool PainterGL::setShaders(struct VDir* dir) { if (!supportsShaders()) { - return; + return false; } + bool success = false; #if defined(BUILD_GLES2) || defined(BUILD_GLES3) if (!m_started) { makeCurrent(); @@ -1009,7 +1012,9 @@ void PainterGL::setShaders(struct VDir* dir) { mGLES2ShaderDetach(reinterpret_cast(m_backend)); mGLES2ShaderFree(&m_shader); } - if (mGLES2ShaderLoad(&m_shader, dir)) { + + success = mGLES2ShaderLoad(&m_shader, dir); + if (success) { mGLES2ShaderAttach(reinterpret_cast(m_backend), static_cast(m_shader.passes), m_shader.nPasses); } @@ -1017,6 +1022,7 @@ void PainterGL::setShaders(struct VDir* dir) { m_gl->doneCurrent(); } #endif + return success; } void PainterGL::clearShaders() { diff --git a/src/platform/qt/DisplayGL.h b/src/platform/qt/DisplayGL.h index 5b0aab2cd..18a7f7911 100644 --- a/src/platform/qt/DisplayGL.h +++ b/src/platform/qt/DisplayGL.h @@ -113,7 +113,7 @@ public slots: void filter(bool filter) override; void swapInterval(int interval) override; void framePosted() override; - void setShaders(struct VDir*) override; + bool setShaders(struct VDir*) override; void clearShaders() override; void resizeContext() override; void setVideoScale(int scale) override; @@ -184,7 +184,7 @@ public slots: void resizeContext(); void setBackgroundImage(const QImage&); - void setShaders(struct VDir*); + bool setShaders(struct VDir*); void clearShaders(); VideoShader* shaders(); QSize contentSize() const; diff --git a/src/platform/qt/DisplayQt.h b/src/platform/qt/DisplayQt.h index a6816a18b..8a3ec988e 100644 --- a/src/platform/qt/DisplayQt.h +++ b/src/platform/qt/DisplayQt.h @@ -41,7 +41,7 @@ public slots: void swapInterval(int) override {}; void filter(bool filter) override; void framePosted() override; - void setShaders(struct VDir*) override {} + bool setShaders(struct VDir*) override { return false; } void clearShaders() override {} void resizeContext() override; void setBackgroundImage(const QImage&) override; diff --git a/src/platform/qt/ShaderSelector.cpp b/src/platform/qt/ShaderSelector.cpp index 473b1d7e9..204ad7252 100644 --- a/src/platform/qt/ShaderSelector.cpp +++ b/src/platform/qt/ShaderSelector.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -50,7 +51,7 @@ ShaderSelector::~ShaderSelector() { } void ShaderSelector::saveSettings() { - QString oldPath = config->getOption("shader"); + QString oldPath = m_config->getOption("shader"); if (oldPath != m_shaderPath) { if (m_shaderPath.isEmpty()) { clearShader(true); @@ -88,7 +89,8 @@ void ShaderSelector::selectShader() { #if !defined(USE_LIBZIP) && !defined(USE_MINIZIP) QString name = GBAApp::app()->getOpenDirectoryName(this, tr("Load shader"), path.absolutePath()); #else - QString name = GBAApp::app()->getOpenFileName(this, tr("Load shader"), "mGBA Shaders (*.shader)", path.absolutePath()); + QString filters = QStringLiteral("%1 (*.shader manifest.ini)").arg(tr("mGBA Shaders")); + QString name = GBAApp::app()->getOpenFileName(this, tr("Load shader"), filters, path.absolutePath()); #endif if (!name.isNull()) { loadShader(name); @@ -97,18 +99,28 @@ void ShaderSelector::selectShader() { } void ShaderSelector::loadShader(const QString& path, bool saveToSettings) { - VDir* shader = VFileDevice::openDir(path); - if (!shader) { - shader = VFileDevice::openArchive(path); + static const QString manifestIni = "/manifest.ini"; + QString shaderPath = path; + if (shaderPath.endsWith(manifestIni)) { + shaderPath.chop(manifestIni.length()); } + VDir* shader = VFileDevice::openDir(shaderPath); if (!shader) { - return; + shader = VFileDevice::openArchive(shaderPath); } - m_display->setShaders(shader); - shader->close(shader); - m_shaderPath = path; - if (saveToSettings) { - m_config->setOption("shader", path); + bool error = !shader || !m_display->setShaders(shader); + if (!error) { + if (saveToSettings) { + m_config->setOption("shader", shaderPath); + } + m_shaderPath = shaderPath; + refreshShaders(); + } + if (shader) { + shader->close(shader); + } + if (error) { + QMessageBox::warning(this, tr("Error loading shader"), tr("The shader \"%1\" could not be loaded successfully.").arg(shaderPath)); } } From bdb0057fb3f363ce8e88be148c06af7413b87b69 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 23 Nov 2024 03:23:05 -0800 Subject: [PATCH 088/114] GBA Serialize: Add support for Vast Fame state --- include/mgba/internal/gba/serialize.h | 6 ++++++ src/gba/cart/unlicensed.c | 13 +++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/include/mgba/internal/gba/serialize.h b/include/mgba/internal/gba/serialize.h index 48393ed7b..eda589dfd 100644 --- a/include/mgba/internal/gba/serialize.h +++ b/include/mgba/internal/gba/serialize.h @@ -446,6 +446,12 @@ struct GBASerializedState { uint32_t settleNextEvent; GBASerializedMulticartFlags flags; } multicart; + struct { + int16_t sramMode; + int16_t romMode; + int8_t writeSequence[5]; + bool acceptingModeChange; + } vfame; }; struct { diff --git a/src/gba/cart/unlicensed.c b/src/gba/cart/unlicensed.c index 442ec9865..3338a60e0 100644 --- a/src/gba/cart/unlicensed.c +++ b/src/gba/cart/unlicensed.c @@ -22,6 +22,9 @@ enum GBMulticartCfgOffset { GBA_MULTICART_CFG_UNK = 0x6, }; +static_assert(sizeof(((struct GBASerializedState*)(NULL))->vfame.writeSequence) == + sizeof(((struct GBAVFameCart*)(NULL))->writeSequence), "GBA savestate vfame writeSequence size mismatch"); + static void _multicartSettle(struct mTiming* timing, void* context, uint32_t cyclesLate); void GBAUnlCartInit(struct GBA* gba) { @@ -165,7 +168,10 @@ void GBAUnlCartSerialize(const struct GBA* gba, struct GBASerializedState* state case GBA_UNL_CART_VFAME: flags = GBASerializedUnlCartFlagsSetType(flags, GBA_UNL_CART_VFAME); flags = GBASerializedUnlCartFlagsSetSubtype(flags, unl->vfame.cartType); - mLOG(GBA_MEM, STUB, "Vast Fame save states are not yet implemented"); + STORE_16(unl->vfame.sramMode, 0, &state->vfame.sramMode); + STORE_16(unl->vfame.romMode, 0, &state->vfame.romMode); + memcpy(state->vfame.writeSequence, unl->vfame.writeSequence, sizeof(state->vfame.writeSequence)); + state->vfame.acceptingModeChange = unl->vfame.acceptingModeChange; break; case GBA_UNL_CART_MULTICART: flags = GBASerializedUnlCartFlagsSetType(0, GBA_UNL_CART_MULTICART); @@ -205,7 +211,10 @@ void GBAUnlCartDeserialize(struct GBA* gba, const struct GBASerializedState* sta case GBA_UNL_CART_NONE: return; case GBA_UNL_CART_VFAME: - mLOG(GBA_MEM, STUB, "Vast Fame save states are not yet implemented"); + LOAD_16(unl->vfame.sramMode, 0, &state->vfame.sramMode); + LOAD_16(unl->vfame.romMode, 0, &state->vfame.romMode); + memcpy(unl->vfame.writeSequence, state->vfame.writeSequence, sizeof(state->vfame.writeSequence)); + unl->vfame.acceptingModeChange = state->vfame.acceptingModeChange; return; case GBA_UNL_CART_MULTICART: unl->multi.bank = state->multicart.bank; From 822a2c8df5fbffc011660231923fc5b1dafb7645 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 23 Nov 2024 03:23:45 -0800 Subject: [PATCH 089/114] GBA Unlicensed Carts: Improve bank swapping behavior --- src/gba/cart/unlicensed.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/gba/cart/unlicensed.c b/src/gba/cart/unlicensed.c index 3338a60e0..ed3b9ce3a 100644 --- a/src/gba/cart/unlicensed.c +++ b/src/gba/cart/unlicensed.c @@ -10,7 +10,7 @@ #include #include -#define MULTI_SETTLE 512 +#define MULTI_SETTLE 300 #define MULTI_BLOCK 0x80000 #define MULTI_BANK 0x2000000 @@ -94,18 +94,24 @@ void GBAUnlCartWriteSRAM(struct GBA* gba, uint32_t address, uint8_t value) { switch (address) { case GBA_MULTICART_CFG_BANK: unl->multi.bank = value >> 4; - mTimingDeschedule(&gba->timing, &unl->multi.settle); - mTimingSchedule(&gba->timing, &unl->multi.settle, MULTI_SETTLE); + if (!(unl->multi.offset & 0x80)) { + mTimingDeschedule(&gba->timing, &unl->multi.settle); + mTimingSchedule(&gba->timing, &unl->multi.settle, MULTI_SETTLE); + } break; case GBA_MULTICART_CFG_OFFSET: - unl->multi.offset = value & 0x3F; - mTimingDeschedule(&gba->timing, &unl->multi.settle); - mTimingSchedule(&gba->timing, &unl->multi.settle, MULTI_SETTLE); + unl->multi.offset = value; + if (!(unl->multi.offset & 0x80)) { + mTimingDeschedule(&gba->timing, &unl->multi.settle); + mTimingSchedule(&gba->timing, &unl->multi.settle, MULTI_SETTLE); + } break; case GBA_MULTICART_CFG_SIZE: unl->multi.size = 0x40 - (value & 0x3F); - mTimingDeschedule(&gba->timing, &unl->multi.settle); - mTimingSchedule(&gba->timing, &unl->multi.settle, MULTI_SETTLE); + if (!(unl->multi.offset & 0x80)) { + mTimingDeschedule(&gba->timing, &unl->multi.settle); + mTimingSchedule(&gba->timing, &unl->multi.settle, MULTI_SETTLE); + } break; case GBA_MULTICART_CFG_SRAM: if (value == 0 && unl->multi.sramActive) { @@ -147,8 +153,8 @@ static void _multicartSettle(struct mTiming* timing, void* context, uint32_t cyc UNUSED(cyclesLate); struct GBA* gba = context; mLOG(GBA_MEM, INFO, "Switching to bank %i offset %i, size %i", - gba->memory.unl.multi.bank, gba->memory.unl.multi.offset, gba->memory.unl.multi.size); - size_t offset = gba->memory.unl.multi.bank * (MULTI_BANK >> 2) + gba->memory.unl.multi.offset * (MULTI_BLOCK >> 2); + gba->memory.unl.multi.bank, gba->memory.unl.multi.offset & 0x3F, gba->memory.unl.multi.size); + size_t offset = gba->memory.unl.multi.bank * (MULTI_BANK >> 2) + (gba->memory.unl.multi.offset & 0x3F) * (MULTI_BLOCK >> 2); size_t size = gba->memory.unl.multi.size * MULTI_BLOCK; if (offset * 4 >= gba->memory.unl.multi.fullSize || offset * 4 + size > gba->memory.unl.multi.fullSize) { mLOG(GBA_MEM, GAME_ERROR, "Bank switch was out of bounds, %07" PRIz "X + %" PRIz "X > %07" PRIz "X", From a5e3e746b2f1bf9a4f1ed45e722676116c251c73 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 23 Nov 2024 19:46:44 -0800 Subject: [PATCH 090/114] GBA Unlicensed Carts: Prevent multicarts from remapping after being locked --- include/mgba/internal/gba/cart/unlicensed.h | 1 + include/mgba/internal/gba/serialize.h | 1 + src/gba/cart/unlicensed.c | 20 ++++++++++++++------ src/gba/io.c | 2 -- src/gba/serialize.c | 4 ++++ 5 files changed, 20 insertions(+), 8 deletions(-) diff --git a/include/mgba/internal/gba/cart/unlicensed.h b/include/mgba/internal/gba/cart/unlicensed.h index 816aa1dfd..f9adbee7e 100644 --- a/include/mgba/internal/gba/cart/unlicensed.h +++ b/include/mgba/internal/gba/cart/unlicensed.h @@ -42,6 +42,7 @@ struct GBAMulticart { uint8_t offset; uint8_t size; bool sramActive; + bool locked; uint8_t unk; }; diff --git a/include/mgba/internal/gba/serialize.h b/include/mgba/internal/gba/serialize.h index eda589dfd..6e9a3124a 100644 --- a/include/mgba/internal/gba/serialize.h +++ b/include/mgba/internal/gba/serialize.h @@ -295,6 +295,7 @@ DECL_BITS(GBASerializedUnlCartFlags, Subtype, 5, 3); DECL_BITFIELD(GBASerializedMulticartFlags, uint32_t); DECL_BIT(GBASerializedMulticartFlags, DustSettling, 0); +DECL_BIT(GBASerializedMulticartFlags, Locked, 1); DECL_BITFIELD(GBASerializedSavedataFlags, uint8_t); DECL_BITS(GBASerializedSavedataFlags, FlashState, 0, 2); diff --git a/src/gba/cart/unlicensed.c b/src/gba/cart/unlicensed.c index ed3b9ce3a..a286850e5 100644 --- a/src/gba/cart/unlicensed.c +++ b/src/gba/cart/unlicensed.c @@ -69,6 +69,7 @@ void GBAUnlCartReset(struct GBA* gba) { gba->memory.unl.multi.bank = 0; gba->memory.unl.multi.offset = 0; gba->memory.unl.multi.size = 0; + gba->memory.unl.multi.locked = false; gba->memory.rom = gba->memory.unl.multi.rom; gba->memory.romSize = GBA_SIZE_ROM0; } @@ -93,22 +94,25 @@ void GBAUnlCartWriteSRAM(struct GBA* gba, uint32_t address, uint8_t value) { mLOG(GBA_MEM, DEBUG, "Multicart writing SRAM %06X:%02X", address, value); switch (address) { case GBA_MULTICART_CFG_BANK: - unl->multi.bank = value >> 4; - if (!(unl->multi.offset & 0x80)) { + if (!unl->multi.locked) { + unl->multi.bank = value >> 4; mTimingDeschedule(&gba->timing, &unl->multi.settle); mTimingSchedule(&gba->timing, &unl->multi.settle, MULTI_SETTLE); } break; case GBA_MULTICART_CFG_OFFSET: - unl->multi.offset = value; - if (!(unl->multi.offset & 0x80)) { + if (!unl->multi.locked) { + unl->multi.offset = value; mTimingDeschedule(&gba->timing, &unl->multi.settle); mTimingSchedule(&gba->timing, &unl->multi.settle, MULTI_SETTLE); + if (unl->multi.offset & 0x80) { + unl->multi.locked = true; + } } break; case GBA_MULTICART_CFG_SIZE: unl->multi.size = 0x40 - (value & 0x3F); - if (!(unl->multi.offset & 0x80)) { + if (!unl->multi.locked) { mTimingDeschedule(&gba->timing, &unl->multi.settle); mTimingSchedule(&gba->timing, &unl->multi.settle, MULTI_SETTLE); } @@ -167,6 +171,7 @@ static void _multicartSettle(struct mTiming* timing, void* context, uint32_t cyc void GBAUnlCartSerialize(const struct GBA* gba, struct GBASerializedState* state) { GBASerializedUnlCartFlags flags = 0; + GBASerializedMulticartFlags multiFlags = 0; const struct GBAUnlCart* unl = &gba->memory.unl; switch (unl->type) { case GBA_UNL_CART_NONE: @@ -187,11 +192,13 @@ void GBAUnlCartSerialize(const struct GBA* gba, struct GBASerializedState* state state->multicart.sramActive = unl->multi.sramActive; state->multicart.unk = unl->multi.unk; state->multicart.currentSize = gba->memory.romSize / MULTI_BLOCK; + multiFlags = GBASerializedMulticartFlagsSetLocked(flags, unl->multi.locked); STORE_16((gba->memory.rom - unl->multi.rom) / 0x20000, 0, &state->multicart.currentOffset); STORE_32(unl->multi.settle.when, 0, &state->multicart.settleNextEvent); if (mTimingIsScheduled(&gba->timing, &unl->multi.settle)) { - STORE_32(GBASerializedMulticartFlagsFillDustSettling(0), 0, &state->multicart.flags); + multiFlags = GBASerializedMulticartFlagsFillDustSettling(multiFlags); } + STORE_32(multiFlags, 0, &state->multicart.flags); break; } STORE_32(flags, 0, &state->hw.unlCartFlags); @@ -238,6 +245,7 @@ void GBAUnlCartDeserialize(struct GBA* gba, const struct GBASerializedState* sta gba->memory.rom = unl->multi.rom + offset; } LOAD_32(multiFlags, 0, &state->multicart.flags); + unl->multi.locked = GBASerializedMulticartFlagsGetLocked(multiFlags); if (GBASerializedMulticartFlagsIsDustSettling(multiFlags)) { LOAD_32(when, 0, &state->multicart.settleNextEvent); mTimingSchedule(&gba->timing, &unl->multi.settle, when); diff --git a/src/gba/io.c b/src/gba/io.c index a72171c1f..cb6058f7f 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -1033,7 +1033,6 @@ void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) { STORE_32(gba->bus, 0, &state->bus); GBAHardwareSerialize(&gba->memory.hw, state); - GBAUnlCartSerialize(gba, state); } void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) { @@ -1083,5 +1082,4 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) { GBADMARecalculateCycles(gba); GBADMAUpdate(gba); GBAHardwareDeserialize(&gba->memory.hw, state); - GBAUnlCartDeserialize(gba, state); } diff --git a/src/gba/serialize.c b/src/gba/serialize.c index 9d6130385..3866c0c80 100644 --- a/src/gba/serialize.c +++ b/src/gba/serialize.c @@ -83,6 +83,7 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) { GBAMemorySerialize(&gba->memory, state); GBAIOSerialize(gba, state); + GBAUnlCartSerialize(gba, state); GBAVideoSerialize(&gba->video, state); GBAAudioSerialize(&gba->audio, state); GBASavedataSerialize(&gba->memory.savedata, state); @@ -180,6 +181,9 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) { mLOG(GBA_STATE, WARN, "Savestate has unaligned PC and is probably corrupted"); gba->cpu->gprs[ARM_PC] &= ~1; } + + // Since this can remap the ROM, we need to do this before we reset the pipeline + GBAUnlCartDeserialize(gba, state); gba->memory.activeRegion = -1; gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]); if (state->biosPrefetch) { From 7ec56763f9d6c68f8d3f7f16a9e34fffcd765492 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 24 Nov 2024 17:55:04 -0800 Subject: [PATCH 091/114] Qt: Fix installer updates if a version number is in the filename (fixes #3109) --- CHANGES | 1 + src/platform/qt/GBAApp.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index e432bee67..f0a3d785f 100644 --- a/CHANGES +++ b/CHANGES @@ -39,6 +39,7 @@ Other fixes: - Qt: Fix crash when applying changes to GB I/O registers in I/O view - Qt: Fix LCDC background priority/enable bit being mis-mapped in I/O view - Qt: Fix saving named states breaking when screenshot states disabled (fixes mgba.io/i/3320) + - Qt: Fix installer updates if a version number is in the filename (fixes mgba.io/i/3109) - Updater: Fix updating appimage across filesystems Misc: - Core: Handle relative paths for saves, screenshots, etc consistently (fixes mgba.io/i/2826) diff --git a/src/platform/qt/GBAApp.cpp b/src/platform/qt/GBAApp.cpp index fdc710c0c..90fc89170 100644 --- a/src/platform/qt/GBAApp.cpp +++ b/src/platform/qt/GBAApp.cpp @@ -370,7 +370,7 @@ void GBAApp::cleanupAfterUpdate() { void GBAApp::restartForUpdate() { QFileInfo updaterPath(m_updater.updateInfo().url.path()); QDir configDir(ConfigController::configDir()); - if (updaterPath.completeSuffix() == "exe") { + if (updaterPath.suffix() == "exe") { m_invokeOnExit = configDir.filePath(QLatin1String("update.exe")); } else { QFile updater(":/updater"); From f9851e6b752f1f792b98eb8c427bae070dee42ac Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 25 Nov 2024 19:22:03 -0800 Subject: [PATCH 092/114] GBA BIOS: Move SoftReset implementation to assembly This improves the timing of the SoftReset implementation (though it's still way faster than Nintendo's official implementation), which is important for some multicarts. --- CHANGES | 1 + src/gba/bios.c | 26 +------------------------- src/gba/hle-bios.c | 22 ++++++++++++++++++---- src/gba/hle-bios.s | 43 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 62 insertions(+), 30 deletions(-) diff --git a/CHANGES b/CHANGES index f0a3d785f..9f505f816 100644 --- a/CHANGES +++ b/CHANGES @@ -49,6 +49,7 @@ Misc: - GB Serialize: Add missing savestate support for MBC6 and NT (newer) - GBA: Improve detection of valid ELF ROMs - GBA Audio: Remove broken XQ audio pending rewrite + - GBA BIOS: Move SoftReset implementation to assembly - GBA Memory: Improve VRAM access stall cycle estimation - GBA SIO: Rewrite lockstep driver for improved stability - GBA Video: Add special circlular window handling in OpenGL renderer diff --git a/src/gba/bios.c b/src/gba/bios.c index caca48a0b..1ff5004d3 100644 --- a/src/gba/bios.c +++ b/src/gba/bios.c @@ -35,30 +35,6 @@ static int _mulWait(int32_t r) { } } -static void _SoftReset(struct GBA* gba) { - struct ARMCore* cpu = gba->cpu; - ARMSetPrivilegeMode(cpu, MODE_IRQ); - cpu->spsr.packed = 0; - cpu->gprs[ARM_LR] = 0; - cpu->gprs[ARM_SP] = GBA_SP_BASE_IRQ; - ARMSetPrivilegeMode(cpu, MODE_SUPERVISOR); - cpu->spsr.packed = 0; - cpu->gprs[ARM_LR] = 0; - cpu->gprs[ARM_SP] = GBA_SP_BASE_SUPERVISOR; - ARMSetPrivilegeMode(cpu, MODE_SYSTEM); - cpu->gprs[ARM_LR] = 0; - cpu->gprs[ARM_SP] = GBA_SP_BASE_SYSTEM; - int8_t flag = ((int8_t*) gba->memory.iwram)[0x7FFA]; - memset(((int8_t*) gba->memory.iwram) + GBA_SIZE_IWRAM - 0x200, 0, 0x200); - if (flag) { - cpu->gprs[ARM_PC] = GBA_BASE_EWRAM; - } else { - cpu->gprs[ARM_PC] = GBA_BASE_ROM0; - } - _ARMSetMode(cpu, MODE_ARM); - ARMWritePC(cpu); -} - static void _RegisterRamReset(struct GBA* gba) { uint32_t registers = gba->cpu->gprs[0]; struct ARMCore* cpu = gba->cpu; @@ -445,7 +421,7 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { bool useStall = false; switch (immediate) { case GBA_SWI_SOFT_RESET: - _SoftReset(gba); + ARMRaiseSWI(cpu); break; case GBA_SWI_REGISTER_RAM_RESET: _RegisterRamReset(gba); diff --git a/src/gba/hle-bios.c b/src/gba/hle-bios.c index 8f689fa7a..f3c58b62b 100644 --- a/src/gba/hle-bios.c +++ b/src/gba/hle-bios.c @@ -20,7 +20,7 @@ const uint8_t hleBios[GBA_SIZE_BIOS] = { 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x04, 0x40, 0xbd, 0xe8, 0x93, 0xf0, 0x29, 0xe3, 0x00, 0x08, 0xbd, 0xe8, 0x0b, 0xf0, 0x69, 0xe1, 0x00, 0x58, 0xbd, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x20, 0xa0, 0xe3, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, + 0x04, 0x20, 0xa0, 0xe3, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x03, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb4, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xcc, 0x01, 0x00, 0x00, 0xc4, 0x01, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00, @@ -79,12 +79,26 @@ const uint8_t hleBios[GBA_SIZE_BIOS] = { 0x00, 0x10, 0x9e, 0xe5, 0x00, 0x00, 0x51, 0xe3, 0x00, 0x10, 0xa0, 0xe3, 0x00, 0x00, 0x00, 0x1a, 0xc0, 0xe0, 0x4e, 0xe2, 0x1e, 0xff, 0x2f, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x29, 0xe1, 0xc0, 0x00, 0x00, 0x02, - 0x4c, 0xd0, 0x9f, 0xe5, 0x00, 0x50, 0x2d, 0xe9, 0x00, 0xc0, 0x4f, 0xe1, + 0xe0, 0xd0, 0x9f, 0xe5, 0x00, 0x50, 0x2d, 0xe9, 0x00, 0xc0, 0x4f, 0xe1, 0x00, 0xe0, 0x0f, 0xe1, 0x00, 0x50, 0x2d, 0xe9, 0x02, 0xe3, 0xa0, 0xe3, 0x9c, 0xc0, 0xde, 0xe5, 0xa5, 0x00, 0x5c, 0xe3, 0x04, 0x00, 0x00, 0x1a, 0xb4, 0xc0, 0xde, 0xe5, 0x80, 0x00, 0x1c, 0xe3, 0x04, 0xe0, 0x8f, 0xe2, - 0x20, 0xf0, 0x9f, 0x15, 0x20, 0xf0, 0x9f, 0x05, 0x14, 0xd0, 0x9f, 0xe5, + 0xb4, 0xf0, 0x9f, 0x15, 0xb4, 0xf0, 0x9f, 0x05, 0xa8, 0xd0, 0x9f, 0xe5, 0x10, 0xc0, 0x1d, 0xe5, 0x0c, 0xf0, 0x69, 0xe1, 0x00, 0x50, 0x3d, 0xe9, 0x04, 0xf0, 0x5e, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x04, 0xe0, 0xa0, 0x03, - 0xf0, 0x7f, 0x00, 0x03, 0x00, 0x20, 0xfe, 0x09, 0x00, 0xc0, 0xff, 0x09 + 0x00, 0xf0, 0x69, 0xe3, 0x00, 0xe0, 0xa0, 0xe3, 0x90, 0xd0, 0x9f, 0xe5, + 0x92, 0xf0, 0x21, 0xe3, 0x00, 0xf0, 0x69, 0xe3, 0x00, 0xe0, 0xa0, 0xe3, + 0x84, 0xd0, 0x9f, 0xe5, 0x9f, 0xf0, 0x21, 0xe3, 0x00, 0xf0, 0x69, 0xe3, + 0x00, 0xe0, 0xa0, 0xe3, 0x78, 0xd0, 0x9f, 0xe5, 0x93, 0xf0, 0x21, 0xe3, + 0x01, 0x03, 0xa0, 0xe3, 0x02, 0x1c, 0x40, 0xe2, 0x06, 0x00, 0x50, 0xe5, + 0x00, 0x20, 0xa0, 0xe3, 0x00, 0x30, 0xa0, 0xe3, 0x00, 0x40, 0xa0, 0xe3, + 0x00, 0x50, 0xa0, 0xe3, 0x00, 0x60, 0xa0, 0xe3, 0x00, 0x70, 0xa0, 0xe3, + 0x00, 0x80, 0xa0, 0xe3, 0x00, 0x90, 0xa0, 0xe3, 0x00, 0xa0, 0xa0, 0xe3, + 0x00, 0xb0, 0xa0, 0xe3, 0x00, 0xc0, 0xa0, 0xe3, 0xfc, 0x03, 0xa1, 0xe8, + 0x01, 0x03, 0x51, 0xe3, 0xfc, 0xff, 0xff, 0x1a, 0x00, 0x00, 0x50, 0xe3, + 0x00, 0x00, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, 0x02, 0xe3, 0xa0, 0x03, + 0x02, 0xe4, 0xa0, 0x13, 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf0, 0x29, 0xe1, 0xf0, 0x7f, 0x00, 0x03, 0x00, 0x20, 0xfe, 0x09, + 0x00, 0xc0, 0xff, 0x09, 0xe0, 0x7f, 0x00, 0x03, 0xa0, 0x7f, 0x00, 0x03, + 0x00, 0x7f, 0x00, 0x03 }; diff --git a/src/gba/hle-bios.s b/src/gba/hle-bios.s index aa77160c1..871d79849 100644 --- a/src/gba/hle-bios.s +++ b/src/gba/hle-bios.s @@ -129,7 +129,6 @@ subs pc, lr, #4 .word 0x03A0E004 @ Unimplemented -SoftReset: RegisterRamReset: Stop: GetBiosChecksum: @@ -358,3 +357,45 @@ ldmdb sp!, {r12, lr} subs pc, lr, #4 .word 0 .word 0x03A0E004 + +SoftReset: +msr spsr, #0 +mov lr, #0 +ldr sp, =0x03007FE0 +msr cpsr_c, #0x92 +msr spsr, #0 +mov lr, #0 +ldr sp, =0x03007FA0 +msr cpsr_c, #0x9F +msr spsr, #0 +mov lr, #0 +ldr sp, =0x03007F00 +msr cpsr_c, #0x93 +mov r0, #0x04000000 +sub r1, r0, #0x200 +ldrb r0, [r0, #-6] +mov r2, #0 +mov r3, #0 +mov r4, #0 +mov r5, #0 +mov r6, #0 +mov r7, #0 +mov r8, #0 +mov r9, #0 +mov r10, #0 +mov r11, #0 +mov r12, #0 +1: +stmia r1!, {r2, r3, r4, r5, r6, r7, r8, r9} +cmp r1, #0x04000000 +bne 1b +cmp r0, #0 +mov r0, #0 +mov r1, #0 +moveq lr, #0x08000000 +movne lr, #0x02000000 +movs pc, lr +.word 0 +.word 0xE129F000 + +.ltorg From a9bddb92e3152f58df6242f1f773fc8d9421af79 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 25 Nov 2024 19:27:51 -0800 Subject: [PATCH 093/114] GBA Unlicensed Carts: Detect "SPIDERMAN3" header too --- src/gba/cart/unlicensed.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gba/cart/unlicensed.c b/src/gba/cart/unlicensed.c index a286850e5..a0f18fc5e 100644 --- a/src/gba/cart/unlicensed.c +++ b/src/gba/cart/unlicensed.c @@ -42,7 +42,7 @@ void GBAUnlCartDetect(struct GBA* gba) { return; } - if (memcmp(&cart->id, "AXVJ01", 6) == 0) { + if (memcmp(&cart->id, "AXVJ01", 6) == 0 || memcmp(&cart->id, "BI3P52", 6) == 0) { if (gba->romVf && gba->romVf->size(gba->romVf) >= 0x04000000) { // Bootleg multicart // TODO: Identify a bit more precisely From 1eb130a28bd951b8e90ddc77a919f3aa84852d8b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 3 Dec 2024 23:04:41 -0800 Subject: [PATCH 094/114] GBA SIO: Allow externally-clocked NORMAL transfers (fixes #3360) --- src/gba/sio.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/gba/sio.c b/src/gba/sio.c index 60c7c2286..22b79675e 100644 --- a/src/gba/sio.c +++ b/src/gba/sio.c @@ -211,11 +211,7 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) { sio->rcnt = GBASIORegisterRCNTFillSc(sio->rcnt); } if (GBASIONormalIsStart(value) && !GBASIONormalIsStart(sio->siocnt)) { - if (GBASIONormalIsSc(value)) { - _startTransfer(sio); - } else { - // TODO - } + _startTransfer(sio); } break; default: From 938ee55d42445d57fcdb501af43bb190be27f4b9 Mon Sep 17 00:00:00 2001 From: Adam Higerd Date: Tue, 19 Nov 2024 10:41:34 -0600 Subject: [PATCH 095/114] Qt: honor control characters in ScriptingTextBuffer::print --- .../qt/scripting/ScriptingTextBuffer.cpp | 76 +++++++++++++++---- .../qt/scripting/ScriptingTextBuffer.h | 7 ++ 2 files changed, 67 insertions(+), 16 deletions(-) diff --git a/src/platform/qt/scripting/ScriptingTextBuffer.cpp b/src/platform/qt/scripting/ScriptingTextBuffer.cpp index da05bd2b9..c209adcd5 100644 --- a/src/platform/qt/scripting/ScriptingTextBuffer.cpp +++ b/src/platform/qt/scripting/ScriptingTextBuffer.cpp @@ -49,26 +49,70 @@ void ScriptingTextBuffer::setBufferName(const QString& name) { emit bufferNameChanged(name); } +void ScriptingTextBuffer::lineBreak() { + bool nextBlockExists = m_shim.cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, 1); + if (!nextBlockExists) { + m_shim.cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor, 1); + m_shim.cursor.insertBlock(); + } +} + +void ScriptingTextBuffer::carriageReturn() { + m_shim.cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor, 1); +} + +void ScriptingTextBuffer::tab() { + QTextCursor& cursor = m_shim.cursor; + int column = cursor.positionInBlock(); + int move = tabStop - (column % tabStop) + 1; + if (column + move >= m_dims.width()) { + lineBreak(); + } else if (column + move <= cursor.block().length()) { + cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, move - 1); + } else { + move = column + move - cursor.block().length(); + cursor.movePosition(QTextCursor::EndOfLine, QTextCursor::MoveAnchor, 1); + cursor.insertText(QString(move, ' ')); + } +} + +void ScriptingTextBuffer::insertString(const QString& text) { + QTextCursor& cursor = m_shim.cursor; + if (cursor.positionInBlock() >= m_dims.width()) { + lineBreak(); + } + cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, text.length()); + cursor.insertText(text); +} + void ScriptingTextBuffer::print(const QString& text) { QMutexLocker locker(&m_mutex); - for (QString split : text.split('\n')) { - while (m_shim.cursor.positionInBlock() + split.length() > m_dims.width()) { - int cut = m_dims.width() - m_shim.cursor.positionInBlock(); - if (!m_shim.cursor.atBlockEnd()) { - m_shim.cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); - } - m_shim.cursor.insertText(split.left(cut)); - if (m_shim.cursor.atEnd()) { - m_shim.cursor.insertBlock(); - } else { - m_shim.cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor, 1); - } - split = split.mid(cut); + + QTextCursor& cursor = m_shim.cursor; + QString toInsert; + + for (const QChar& ch : text) { + int column = cursor.positionInBlock(); + if (ch == '\t' || ch == '\n' || ch == '\r' || column + toInsert.length() >= m_dims.width()) { + insertString(toInsert); + toInsert.clear(); } - if (!m_shim.cursor.atBlockEnd()) { - m_shim.cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, split.length()); + switch (ch.unicode()) { + case '\t': + tab(); + break; + case '\n': + lineBreak(); + break; + case '\r': + carriageReturn(); + break; + default: + toInsert += ch; } - m_shim.cursor.insertText(split); + } + if (!toInsert.isEmpty()) { + insertString(toInsert); } } diff --git a/src/platform/qt/scripting/ScriptingTextBuffer.h b/src/platform/qt/scripting/ScriptingTextBuffer.h index b4a9c5b83..717a5d284 100644 --- a/src/platform/qt/scripting/ScriptingTextBuffer.h +++ b/src/platform/qt/scripting/ScriptingTextBuffer.h @@ -36,6 +36,8 @@ signals: void bufferNameChanged(const QString&); private: + enum { tabStop = 4 }; + struct ScriptingBufferShim : public mScriptTextBuffer { ScriptingTextBuffer* p; QTextCursor cursor; @@ -44,6 +46,11 @@ private: QMutex m_mutex; QString m_name; + void lineBreak(); + void carriageReturn(); + void tab(); + void insertString(const QString& text); + static void init(struct mScriptTextBuffer*, const char* name); static void deinit(struct mScriptTextBuffer*); From 00d17c6f42de52d051b84b52b4b45b9d784bb755 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 7 Dec 2024 17:06:01 -0800 Subject: [PATCH 096/114] GBA BIOS: Fix reseting SWI mode SPSR in SoftReset --- src/gba/hle-bios.c | 31 +++++++++++++++---------------- src/gba/hle-bios.s | 7 +++---- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/gba/hle-bios.c b/src/gba/hle-bios.c index f3c58b62b..5b1acd3ba 100644 --- a/src/gba/hle-bios.c +++ b/src/gba/hle-bios.c @@ -79,26 +79,25 @@ const uint8_t hleBios[GBA_SIZE_BIOS] = { 0x00, 0x10, 0x9e, 0xe5, 0x00, 0x00, 0x51, 0xe3, 0x00, 0x10, 0xa0, 0xe3, 0x00, 0x00, 0x00, 0x1a, 0xc0, 0xe0, 0x4e, 0xe2, 0x1e, 0xff, 0x2f, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x29, 0xe1, 0xc0, 0x00, 0x00, 0x02, - 0xe0, 0xd0, 0x9f, 0xe5, 0x00, 0x50, 0x2d, 0xe9, 0x00, 0xc0, 0x4f, 0xe1, + 0xdc, 0xd0, 0x9f, 0xe5, 0x00, 0x50, 0x2d, 0xe9, 0x00, 0xc0, 0x4f, 0xe1, 0x00, 0xe0, 0x0f, 0xe1, 0x00, 0x50, 0x2d, 0xe9, 0x02, 0xe3, 0xa0, 0xe3, 0x9c, 0xc0, 0xde, 0xe5, 0xa5, 0x00, 0x5c, 0xe3, 0x04, 0x00, 0x00, 0x1a, 0xb4, 0xc0, 0xde, 0xe5, 0x80, 0x00, 0x1c, 0xe3, 0x04, 0xe0, 0x8f, 0xe2, - 0xb4, 0xf0, 0x9f, 0x15, 0xb4, 0xf0, 0x9f, 0x05, 0xa8, 0xd0, 0x9f, 0xe5, + 0xb0, 0xf0, 0x9f, 0x15, 0xb0, 0xf0, 0x9f, 0x05, 0xa4, 0xd0, 0x9f, 0xe5, 0x10, 0xc0, 0x1d, 0xe5, 0x0c, 0xf0, 0x69, 0xe1, 0x00, 0x50, 0x3d, 0xe9, 0x04, 0xf0, 0x5e, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x04, 0xe0, 0xa0, 0x03, - 0x00, 0xf0, 0x69, 0xe3, 0x00, 0xe0, 0xa0, 0xe3, 0x90, 0xd0, 0x9f, 0xe5, + 0x00, 0xf0, 0x69, 0xe3, 0x00, 0xe0, 0xa0, 0xe3, 0x8c, 0xd0, 0x9f, 0xe5, 0x92, 0xf0, 0x21, 0xe3, 0x00, 0xf0, 0x69, 0xe3, 0x00, 0xe0, 0xa0, 0xe3, - 0x84, 0xd0, 0x9f, 0xe5, 0x9f, 0xf0, 0x21, 0xe3, 0x00, 0xf0, 0x69, 0xe3, - 0x00, 0xe0, 0xa0, 0xe3, 0x78, 0xd0, 0x9f, 0xe5, 0x93, 0xf0, 0x21, 0xe3, - 0x01, 0x03, 0xa0, 0xe3, 0x02, 0x1c, 0x40, 0xe2, 0x06, 0x00, 0x50, 0xe5, - 0x00, 0x20, 0xa0, 0xe3, 0x00, 0x30, 0xa0, 0xe3, 0x00, 0x40, 0xa0, 0xe3, - 0x00, 0x50, 0xa0, 0xe3, 0x00, 0x60, 0xa0, 0xe3, 0x00, 0x70, 0xa0, 0xe3, - 0x00, 0x80, 0xa0, 0xe3, 0x00, 0x90, 0xa0, 0xe3, 0x00, 0xa0, 0xa0, 0xe3, - 0x00, 0xb0, 0xa0, 0xe3, 0x00, 0xc0, 0xa0, 0xe3, 0xfc, 0x03, 0xa1, 0xe8, - 0x01, 0x03, 0x51, 0xe3, 0xfc, 0xff, 0xff, 0x1a, 0x00, 0x00, 0x50, 0xe3, - 0x00, 0x00, 0xa0, 0xe3, 0x00, 0x10, 0xa0, 0xe3, 0x02, 0xe3, 0xa0, 0x03, - 0x02, 0xe4, 0xa0, 0x13, 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xf0, 0x29, 0xe1, 0xf0, 0x7f, 0x00, 0x03, 0x00, 0x20, 0xfe, 0x09, - 0x00, 0xc0, 0xff, 0x09, 0xe0, 0x7f, 0x00, 0x03, 0xa0, 0x7f, 0x00, 0x03, - 0x00, 0x7f, 0x00, 0x03 + 0x80, 0xd0, 0x9f, 0xe5, 0x93, 0xf0, 0x21, 0xe3, 0x00, 0xf0, 0x69, 0xe3, + 0x00, 0xe0, 0xa0, 0xe3, 0x74, 0xd0, 0x9f, 0xe5, 0x01, 0x03, 0xa0, 0xe3, + 0x02, 0x1c, 0x40, 0xe2, 0x06, 0x00, 0x50, 0xe5, 0x00, 0x20, 0xa0, 0xe3, + 0x00, 0x30, 0xa0, 0xe3, 0x00, 0x40, 0xa0, 0xe3, 0x00, 0x50, 0xa0, 0xe3, + 0x00, 0x60, 0xa0, 0xe3, 0x00, 0x70, 0xa0, 0xe3, 0x00, 0x80, 0xa0, 0xe3, + 0x00, 0x90, 0xa0, 0xe3, 0x00, 0xa0, 0xa0, 0xe3, 0x00, 0xb0, 0xa0, 0xe3, + 0x00, 0xc0, 0xa0, 0xe3, 0xfc, 0x03, 0xa1, 0xe8, 0x01, 0x03, 0x51, 0xe3, + 0xfc, 0xff, 0xff, 0x1a, 0x00, 0x00, 0x50, 0xe3, 0x00, 0x00, 0xa0, 0xe3, + 0x00, 0x10, 0xa0, 0xe3, 0x02, 0xe3, 0xa0, 0x03, 0x02, 0xe4, 0xa0, 0x13, + 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x29, 0xe1, + 0xf0, 0x7f, 0x00, 0x03, 0x00, 0x20, 0xfe, 0x09, 0x00, 0xc0, 0xff, 0x09, + 0x00, 0x7f, 0x00, 0x03, 0xa0, 0x7f, 0x00, 0x03, 0xe0, 0x7f, 0x00, 0x03 }; diff --git a/src/gba/hle-bios.s b/src/gba/hle-bios.s index 871d79849..eccbaa143 100644 --- a/src/gba/hle-bios.s +++ b/src/gba/hle-bios.s @@ -361,16 +361,15 @@ subs pc, lr, #4 SoftReset: msr spsr, #0 mov lr, #0 -ldr sp, =0x03007FE0 +ldr sp, =0x03007F00 msr cpsr_c, #0x92 msr spsr, #0 mov lr, #0 ldr sp, =0x03007FA0 -msr cpsr_c, #0x9F +msr cpsr_c, #0x93 msr spsr, #0 mov lr, #0 -ldr sp, =0x03007F00 -msr cpsr_c, #0x93 +ldr sp, =0x03007FE0 mov r0, #0x04000000 sub r1, r0, #0x200 ldrb r0, [r0, #-6] From f4595f4578cf9943ae787519eb3346837484cb89 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 7 Dec 2024 21:27:03 -0800 Subject: [PATCH 097/114] CHANGES: Update for 0.10.4 --- CHANGES | 47 +++++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/CHANGES b/CHANGES index 9f505f816..08d9526ff 100644 --- a/CHANGES +++ b/CHANGES @@ -8,39 +8,20 @@ Features: - Debugger: Add range watchpoints Emulation fixes: - ARM: Add framework for coprocessor support - - GB Audio: Fix audio envelope timing resetting too often (fixes mgba.io/i/3164) - - GB I/O: Fix STAT writing IRQ trigger conditions (fixes mgba.io/i/2501) - GB Serialize: Add missing Pocket Cam state to savestates - GB Video: Implement DMG-style sprite ordering - GBA: Unhandled bkpt should be treated as an undefined exception - GBA: Add baseline CP0 (Wii U VC) and CP1 (DCC) implementations - - GBA GPIO: Fix gyro read-out start (fixes mgba.io/i/3141) - - GBA I/O: Fix HALTCNT access behavior (fixes mgba.io/i/2309) - - GBA I/O: Fix audio register 8-bit write behavior (fixes mgba.io/i/3086) - GBA Serialize: Fix some minor save state edge cases - - GBA Serialize: Properly restore GPIO register state (fixes mgba.io/i/3294) - - GBA SIO: Fix MULTI mode SIOCNT bit 7 writes on secondary GBAs (fixes mgba.io/i/3110) - GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722) - GBA Video: Improve emulation of window start/end conditions (fixes mgba.io/i/1945) Other fixes: - Core: Fix inconsistencies with setting game-specific overrides (fixes mgba.io/i/2963) - - Core: Fix patch autoloading leaking the file handle - Debugger: Fix writing to specific segment in command-line debugger - - GB: Fix uninitialized save data when loading undersized temporary saves - - GB, GBA Core: Fix memory leak if reloading debug symbols - GB Serialize: Prevent loading invalid states where LY >= 144 in modes other than 1 - GBA: Fix getting game info for multiboot ROMs - - GBA Audio: Fix crash if audio FIFOs and timers get out of sync - - GBA Audio: Fix crash in audio subsampling if timing lockstep breaks - - GBA Core: Fix loading symbols from ELF files if the file doesn't end with .elf - - GBA Memory: Let raw access read high MMIO addresses - Qt: Fix savestate preview sizes with different scales (fixes mgba.io/i/2560) - Qt: Fix potential crash when configuring shortcuts - - Qt: Fix crash when applying changes to GB I/O registers in I/O view - - Qt: Fix LCDC background priority/enable bit being mis-mapped in I/O view - - Qt: Fix saving named states breaking when screenshot states disabled (fixes mgba.io/i/3320) - - Qt: Fix installer updates if a version number is in the filename (fixes mgba.io/i/3109) - - Updater: Fix updating appimage across filesystems Misc: - Core: Handle relative paths for saves, screenshots, etc consistently (fixes mgba.io/i/2826) - Core: Improve rumble emulation by averaging state over entire frame (fixes mgba.io/i/3232) @@ -60,10 +41,36 @@ Misc: - Qt: Remove maligned double-click-to-fullscreen shortcut (closes mgba.io/i/2632) - Qt: Pass logging context through to video proxy thread (fixes mgba.io/i/3095) - Qt: Show maker code and game version in ROM info - - Qt: Make window corners square on Windows 11 (fixes mgba.io/i/3285) - Qt: Show a dummy shader settings tab if shaders aren't supported - Res: Port NSO-gba-colors shader (closes mgba.io/i/2834) - Scripting: Add `callbacks:oneshot` for single-call callbacks + +0.10.4: (2024-12-07) +Emulation fixes: + - GB Audio: Fix audio envelope timing resetting too often (fixes mgba.io/i/3164) + - GB I/O: Fix STAT writing IRQ trigger conditions (fixes mgba.io/i/2501) + - GBA GPIO: Fix gyro read-out start (fixes mgba.io/i/3141) + - GBA I/O: Fix HALTCNT access behavior (fixes mgba.io/i/2309) + - GBA I/O: Fix audio register 8-bit write behavior (fixes mgba.io/i/3086) + - GBA Serialize: Properly restore GPIO register state (fixes mgba.io/i/3294) + - GBA SIO: Fix MULTI mode SIOCNT bit 7 writes on secondary GBAs (fixes mgba.io/i/3110) +Other fixes: + - Core: Fix patch autoloading leaking the file handle + - GB: Fix uninitialized save data when loading undersized temporary saves + - GB, GBA Core: Fix memory leak if reloading debug symbols + - GB Serialize: Prevent loading invalid states where LY >= 144 in modes other than 1 + - GBA Audio: Fix crash if audio FIFOs and timers get out of sync + - GBA Audio: Fix crash in audio subsampling if timing lockstep breaks + - GBA Core: Fix loading symbols from ELF files if the file doesn't end with .elf + - GBA Memory: Let raw access read high MMIO addresses + - Qt: Fix crash when applying changes to GB I/O registers in I/O view + - Qt: Fix LCDC background priority/enable bit being mis-mapped in I/O view + - Qt: Fix saving named states breaking when screenshot states disabled (fixes mgba.io/i/3320) + - Qt: Fix potential crash on Wayland with OpenGL (fixes mgba.io/i/3276) + - Qt: Fix installer updates if a version number is in the filename (fixes mgba.io/i/3109) + - Updater: Fix updating appimage across filesystems +Misc: + - Qt: Make window corners square on Windows 11 (fixes mgba.io/i/3285) - Switch: Add bilinear filtering option (closes mgba.io/i/3111) - Vita: Add imc0 and xmc0 mount point support From 109fbe60f5fd983af394c8e518e97d9a88a304cd Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 11 Dec 2024 16:24:51 -0800 Subject: [PATCH 098/114] GBA Hardware: Fix loading states unconditionally overwriting GPIO memory --- CHANGES | 1 + include/mgba/gba/interface.h | 4 +++- src/gba/cart/gpio.c | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 08d9526ff..f3a8cc539 100644 --- a/CHANGES +++ b/CHANGES @@ -19,6 +19,7 @@ Other fixes: - Core: Fix inconsistencies with setting game-specific overrides (fixes mgba.io/i/2963) - Debugger: Fix writing to specific segment in command-line debugger - GB Serialize: Prevent loading invalid states where LY >= 144 in modes other than 1 + - GBA Hardware: Fix loading states unconditionally overwriting GPIO memory - GBA: Fix getting game info for multiboot ROMs - Qt: Fix savestate preview sizes with different scales (fixes mgba.io/i/2560) - Qt: Fix potential crash when configuring shortcuts diff --git a/include/mgba/gba/interface.h b/include/mgba/gba/interface.h index cbbb4d23a..298292dd3 100644 --- a/include/mgba/gba/interface.h +++ b/include/mgba/gba/interface.h @@ -68,7 +68,9 @@ enum GBAHardwareDevice { HW_TILT = 16, HW_GB_PLAYER = 32, HW_GB_PLAYER_DETECTION = 64, - HW_EREADER = 128 + HW_EREADER = 128, + + HW_GPIO = HW_RTC | HW_RUMBLE | HW_LIGHT_SENSOR | HW_GYRO | HW_TILT, }; struct Configuration; diff --git a/src/gba/cart/gpio.c b/src/gba/cart/gpio.c index 0bffbc088..48cb8c340 100644 --- a/src/gba/cart/gpio.c +++ b/src/gba/cart/gpio.c @@ -502,7 +502,7 @@ void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, const struct GBASer LOAD_16(hw->direction, 0, &state->hw.pinDirection); hw->devices = state->hw.devices; - if (hw->gpioBase) { + if ((hw->devices & HW_GPIO) && hw->gpioBase) { if (hw->readWrite) { STORE_16(hw->pinState, 0, hw->gpioBase); STORE_16(hw->direction, 2, hw->gpioBase); From 655211651ad664c910b8264d6c3cb616d2ae1d75 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 11 Dec 2024 16:25:17 -0800 Subject: [PATCH 099/114] GBA Memory: Properly narrow down GPIO write validity check --- src/gba/memory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gba/memory.c b/src/gba/memory.c index 1561aef91..bdaf22c36 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -915,7 +915,7 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle break; case GBA_REGION_ROM0: if (IS_GPIO_REGISTER(address & 0xFFFFFE)) { - if (memory->hw.devices == HW_NONE) { + if (!(memory->hw.devices & HW_GPIO)) { mLOG(GBA_HW, WARN, "Write to GPIO address %08X on cartridge without GPIO", address); break; } From edaa27796bbd2c991b5c9ccd2af6f43c361715a4 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 11 Dec 2024 17:24:22 -0800 Subject: [PATCH 100/114] GBA: Improve reset behavior of various hardware components --- include/mgba/internal/gba/cart/gpio.h | 1 + include/mgba/internal/gba/savedata.h | 1 + src/gba/cart/gpio.c | 15 +++++++++++++++ src/gba/memory.c | 10 ++++++++++ src/gba/savedata.c | 7 ++++++- 5 files changed, 33 insertions(+), 1 deletion(-) diff --git a/include/mgba/internal/gba/cart/gpio.h b/include/mgba/internal/gba/cart/gpio.h index 77785e612..be3cd9173 100644 --- a/include/mgba/internal/gba/cart/gpio.h +++ b/include/mgba/internal/gba/cart/gpio.h @@ -86,6 +86,7 @@ struct GBACartridgeHardware { }; void GBAHardwareInit(struct GBACartridgeHardware* gpio, uint16_t* gpioBase); +void GBAHardwareReset(struct GBACartridgeHardware* gpio); void GBAHardwareClear(struct GBACartridgeHardware* gpio); void GBAHardwareInitRTC(struct GBACartridgeHardware* gpio); diff --git a/include/mgba/internal/gba/savedata.h b/include/mgba/internal/gba/savedata.h index 274241a76..e69d07834 100644 --- a/include/mgba/internal/gba/savedata.h +++ b/include/mgba/internal/gba/savedata.h @@ -91,6 +91,7 @@ struct GBASavedataRTCBuffer { }; void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf); +void GBASavedataReset(struct GBASavedata* savedata); void GBASavedataDeinit(struct GBASavedata* savedata); void GBASavedataMask(struct GBASavedata* savedata, struct VFile* vf, bool writeback); diff --git a/src/gba/cart/gpio.c b/src/gba/cart/gpio.c index 48cb8c340..a44bab294 100644 --- a/src/gba/cart/gpio.c +++ b/src/gba/cart/gpio.c @@ -47,6 +47,20 @@ void GBAHardwareInit(struct GBACartridgeHardware* hw, uint16_t* base) { GBAHardwareClear(hw); } +void GBAHardwareReset(struct GBACartridgeHardware* hw) { + hw->readWrite = GPIO_WRITE_ONLY; + hw->pinState = 0; + hw->direction = 0; + hw->lightCounter = 0; + hw->lightEdge = false; + hw->lightSample = 0xFF; + hw->gyroSample = 0; + hw->gyroEdge = 0; + hw->tiltX = 0xFFF; + hw->tiltY = 0xFFF; + hw->tiltState = 0; +} + void GBAHardwareClear(struct GBACartridgeHardware* hw) { hw->devices = HW_NONE | (hw->devices & HW_GB_PLAYER_DETECTION); hw->readWrite = GPIO_WRITE_ONLY; @@ -503,6 +517,7 @@ void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, const struct GBASer hw->devices = state->hw.devices; if ((hw->devices & HW_GPIO) && hw->gpioBase) { + // TODO: This needs to update the pristine state somehow if (hw->readWrite) { STORE_16(hw->pinState, 0, hw->gpioBase); STORE_16(hw->direction, 2, hw->gpioBase); diff --git a/src/gba/memory.c b/src/gba/memory.c index bdaf22c36..4f074657a 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -138,6 +138,16 @@ void GBAMemoryReset(struct GBA* gba) { mLOG(GBA_MEM, FATAL, "Could not map memory"); } + if (!gba->memory.rom) { + gba->isPristine = false; + } + + if (gba->memory.hw.devices & HW_GPIO) { + _pristineCow(gba); + } + + GBASavedataReset(&gba->memory.savedata); + GBAHardwareReset(&gba->memory.hw); GBADMAReset(gba); GBAUnlCartReset(gba); memset(&gba->memory.matrix, 0, sizeof(gba->memory.matrix)); diff --git a/src/gba/savedata.c b/src/gba/savedata.c index a9cd682ca..c0d2c889a 100644 --- a/src/gba/savedata.c +++ b/src/gba/savedata.c @@ -45,7 +45,7 @@ static void _ashesToAshes(struct mTiming* timing, void* user, uint32_t cyclesLat void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) { savedata->type = GBA_SAVEDATA_AUTODETECT; - savedata->data = 0; + savedata->data = NULL; savedata->command = EEPROM_COMMAND_NULL; savedata->flashState = FLASH_STATE_RAW; savedata->vf = vf; @@ -63,6 +63,11 @@ void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) { savedata->dust.callback = _ashesToAshes; } +void GBASavedataReset(struct GBASavedata* savedata) { + savedata->command = EEPROM_COMMAND_NULL; + savedata->flashState = FLASH_STATE_RAW; +} + void GBASavedataDeinit(struct GBASavedata* savedata) { if (savedata->vf) { size_t size = GBASavedataSize(savedata); From d5d23143b4ac2ebe41e54a673a423a7ce9736043 Mon Sep 17 00:00:00 2001 From: Felipe Date: Mon, 16 Sep 2024 18:57:18 +0000 Subject: [PATCH 101/114] Qt: Update translation (Portuguese (Brazil)) Translation: mGBA/Qt Translate-URL: https://hosted.weblate.org/projects/mgba/mgba-qt/pt_BR/ --- src/platform/qt/ts/mgba-pt_BR.ts | 138 +++++++++++++++---------------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/src/platform/qt/ts/mgba-pt_BR.ts b/src/platform/qt/ts/mgba-pt_BR.ts index 5b8c0df52..3326cfcde 100644 --- a/src/platform/qt/ts/mgba-pt_BR.ts +++ b/src/platform/qt/ts/mgba-pt_BR.ts @@ -198,12 +198,12 @@ Tamanho do download: %3 Audio device is missing its core - O núcleo do dispositivo de áudio está ausente + O dispositivo de áudio está com o núcleo ausente Writing data to read-only audio device - Gravando dados no dispositivo somente-leitura do áudio + Gravando os dados no dispositivo somente-leitura do áudio @@ -292,7 +292,7 @@ Tamanho do download: %3 BattleChip data is missing. BattleChip Gates will still work, but some graphics will be missing. Would you like to download the data now? - Os dados do BattleChip estão ausente. Os Portais do BattleChip Gates ainda funcionarão mas alguns gráficos estarão ausentes. Você gostaria de baixar os dados agora? + Os dados do BattleChip estão ausentes. Os Portais do BattleChip ainda funcionarão mas alguns gráficos estarão ausentes. Você gostaria de baixar os dados agora? @@ -448,7 +448,7 @@ Tamanho do download: %3 Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). - Falhou em abrir o abrir o arquivo de save; Os saves dentro do jogo não podem ser atualizados. Por favor tenha certeza que o diretório de save seja gravável sem privilégios adicionais (ex: UAC no Windows). + Falhou em abrir o arquivo do save; Os saves dentro do jogo não podem ser atualizados. Por favor tenha certeza que o diretório do save seja gravável sem privilégios adicionais (ex: UAC no Windows). @@ -482,7 +482,7 @@ Tamanho do download: %3 Failed to create an OpenGL 3 context, trying old-style... - + Falhou em criar um contexto do OpenGL 3, tentando o estilo antigo... @@ -694,12 +694,12 @@ Tamanho do download: %3 Build finished - Compilação concluída + Build concluído Forwarder finished building - O encaminhador encerrou a compilação + O encaminhador concluiu a compilação @@ -752,7 +752,7 @@ Tamanho do download: %3 Disable scanline effects - Desativar efeitos da scanline + Desativar os efeitos da scanline @@ -886,7 +886,7 @@ Tamanho do download: %3 Break - Pausar + Interromper @@ -1168,17 +1168,17 @@ Tamanho do download: %3 Mode 0: 4 tile layers - Modo 0: 4 camadas de mosaicos + Modo 0: 4 camadas dos mosaicos Mode 1: 2 tile layers + 1 rotated/scaled tile layer - Modo 1: 2 camadas de mosaicos + 1 camada de mosaico rotacionada/redimensionada + Modo 1: 2 camadas dis mosaicos + 1 camada de mosaico rotacionada/redimensionada Mode 2: 2 rotated/scaled tile layers - Modo 2: 2 camadas de mosaicos rotacionadas/redimensionadas + Modo 2: 2 camadas dos mosaicos rotacionadas/redimensionadas @@ -1357,7 +1357,7 @@ Tamanho do download: %3 Overflow wraps - Embrulhos do excesso + Embrulhos da sobrecarga @@ -1439,7 +1439,7 @@ Tamanho do download: %3 End y - Final y + Y Final @@ -2281,7 +2281,7 @@ Tamanho do download: %3 Start timing - Iniciar timing + Iniciar a cronometragem @@ -2664,7 +2664,7 @@ Tamanho do download: %3 Gamepak prefetch - Pré-carga do Gamepak + Prefetch do Gamepak @@ -2699,7 +2699,7 @@ Tamanho do download: %3 Active face buttons - Botões de face ativos + Botões faciais ativos @@ -3054,7 +3054,7 @@ Tamanho do download: %3 %1 State - Estado %1 + State %1 @@ -3408,63 +3408,63 @@ Tamanho do download: %3 Memory access logging - + Registro de acesso a memória Log file - + Arquivo do registro Browse - Explorar + Explorar Log additional information (uses 3× space) - + Informação adicional do registro (usa 3x mais espaço) Load existing file if present - + Carregar o arquivo existente se estiver presente Regions - + Regiões Export ROM snapshot - + Exportar a snapshot da ROM Start - + Iniciar Stop - Parar + Parar Failed to open memory log file - + Falhou em abrir o arquivo do registro da memória Select access log file - + Selecionar o arquivo de registro do acesso Memory access logs (*.mal) - + Registros de acesso a memória (*.mal) @@ -3773,7 +3773,7 @@ Tamanho do download: %3 Signed Integer: - Inteiro com Sinal: + Inteiro Assinado: @@ -4254,7 +4254,7 @@ Tamanho do download: %3 Windows PAL (*.pal);;Adobe Color Table (*.act) - Windows PAL (*.pal);;Tabela de Cores da Adobe (*.act) + Windows PAL (*.pal);;Tabela de Cores do Adobe (*.act) @@ -4372,12 +4372,12 @@ Tamanho do download: %3 Maker Code: - + Código do Criador: Revision: - + Revisão: @@ -4430,7 +4430,7 @@ Tamanho do download: %3 Open issue list in browser - Abrir lista de problemas no navegador + Abrir a lista de problemas no navegador @@ -4488,7 +4488,7 @@ Tamanho do download: %3 No valid formats found - Não foram encontrados formatos válidos + Não foram achados formatos válidos @@ -4498,7 +4498,7 @@ Tamanho do download: %3 No valid conversions found - Não foram encontradas conversões válidas + Não foram achadas conversões válidas @@ -4660,7 +4660,7 @@ Tamanho do download: %3 &Load most recent - + &Carregar o mais recente @@ -4871,7 +4871,7 @@ Tamanho do download: %3 Less than an hour ago - Menos do que uma hora atrás + Menos do que a uma hora atrás @@ -5166,12 +5166,12 @@ Tamanho do download: %3 Show frame count in OSD - Mostrar contagem dos frames no OSD + Mostrar a contagem dos frames no OSD Show emulation info on reset - Mostrar informações da emulação ao resetar + Mostrar as informações da emulação ao resetar @@ -5191,7 +5191,7 @@ Tamanho do download: %3 Update channel: - Canal da atualização: + Canal de atualização: @@ -5271,7 +5271,7 @@ Tamanho do download: %3 Dynamically update window title - Atualizar título da janela dinamicamente + Atualizar o título da janela dinamicamente @@ -5297,7 +5297,7 @@ Tamanho do download: %3 Enable VBA bug compatibility in ROM hacks - Ativar compatibilidade dos bugs do VBA nos hacks das ROMs + Ativar a compatibilidade dos bugs do VBA nos hacks das ROMs @@ -5455,7 +5455,7 @@ Tamanho do download: %3 Enable Game Boy Player features by default - Ativar funções do Game Boy Player por padrão + Ativar as funções do Game Boy Player por padrão @@ -5475,7 +5475,7 @@ Tamanho do download: %3 High-resolution scale: - Escala de alta-resolução: + Escala da alta-resolução: @@ -5499,7 +5499,7 @@ Tamanho do download: %3 Use BIOS file if found - Usar o arquivo da BIOS se encontrado + Usar o arquivo da BIOS se for achado @@ -5677,7 +5677,7 @@ Tamanho do download: %3 Unload Shader - Descarregar Shader + Descarregar o Shader @@ -5986,7 +5986,7 @@ Tamanho do download: %3 Select e-Reader dotcode - Selecionar dotcode do e-Reader + Selecionar o dotcode do e-Reader @@ -6100,12 +6100,12 @@ Tamanho do download: %3 Load ROM in archive... - Carregar ROM no arquivo compactado... + Carregar a ROM no arquivo compactado... Add folder to library... - Adicionar pasta a biblioteca... + Adicionar a pasta a biblioteca... @@ -6130,7 +6130,7 @@ Tamanho do download: %3 Boot BIOS - Dar Boot na BIOS + Inicializar a BIOS @@ -6180,7 +6180,7 @@ Tamanho do download: %3 Load state file... - Carregar arquivo do state... + Carregar o arquivo do state... @@ -6190,7 +6190,7 @@ Tamanho do download: %3 Select save game - Selecione save do jogo + Selecione o save do jogo @@ -6206,7 +6206,7 @@ Tamanho do download: %3 Select e-Reader card images - Selecionar imagens do cartão do e-Reader + Selecionar as imagens do cartão do e-Reader @@ -6236,7 +6236,7 @@ Tamanho do download: %3 Convert e-Reader card image to raw... - Converter imagem do cartão do e-Reader pro natural... + Converter a imagem do cartão do e-Reader pro natural... @@ -6302,7 +6302,7 @@ Tamanho do download: %3 Reset needed - É necessário resetar + Um reset é necessário @@ -6357,7 +6357,7 @@ Tamanho do download: %3 Fast forward (held) - Avanço rápido (segurado) + Avanço rápido (pressionado) @@ -6392,7 +6392,7 @@ Tamanho do download: %3 Rewind (held) - Retroceder (segurado) + Retroceder (pressionado) @@ -6412,12 +6412,12 @@ Tamanho do download: %3 Increase solar level - Aumentar nível solar + Aumentar o nível solar Decrease solar level - Diminuir nível solar + Diminuir o nível solar @@ -6452,7 +6452,7 @@ Tamanho do download: %3 &Lock frame size - + &Travar o tamanho do frame @@ -6537,7 +6537,7 @@ Tamanho do download: %3 Adjust layer placement... - Ajustar posicionamento da camada... + Ajustar o posicionamento da camada... @@ -6567,7 +6567,7 @@ Tamanho do download: %3 Scan e-Reader dotcodes... - Escanear dotcodes do e-Reader... + Escanear os dotcodes do e-Reader... @@ -6597,7 +6597,7 @@ Tamanho do download: %3 Open debugger console... - Abrir console do debugger... + Abrir o console do debugger... @@ -6662,7 +6662,7 @@ Tamanho do download: %3 Log memory &accesses... - + Registrar os acessos a &memória... @@ -6682,7 +6682,7 @@ Tamanho do download: %3 GameShark Button (held) - Botão do GameShark (segurado) + Botão do GameShark (pressionado) @@ -6750,12 +6750,12 @@ Tamanho do download: %3 %1 kiB - %1 kiBs + %1 kBs %1 MiB - %1 MiBs + %1 MBs From 8475e5b035e08b49c736d10de17984bec6e72010 Mon Sep 17 00:00:00 2001 From: Hoseok Seo Date: Sun, 22 Sep 2024 03:11:33 +0000 Subject: [PATCH 102/114] Qt: Update translation (Korean) Translation: mGBA/Qt Translate-URL: https://hosted.weblate.org/projects/mgba/mgba-qt/ko/ --- src/platform/qt/ts/mgba-ko.ts | 36 +++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/platform/qt/ts/mgba-ko.ts b/src/platform/qt/ts/mgba-ko.ts index 5eb684915..2e5bdd0aa 100644 --- a/src/platform/qt/ts/mgba-ko.ts +++ b/src/platform/qt/ts/mgba-ko.ts @@ -482,7 +482,7 @@ Download size: %3 Failed to create an OpenGL 3 context, trying old-style... - + OpenGL 3 컨텍스트를 만들 수 없습니다. 이전 스타일을 시도 중입니다... @@ -3408,63 +3408,63 @@ Download size: %3 Memory access logging - + 메모리 접속 로깅 Log file - + 로그 파일 Browse - + 찾아보기 Log additional information (uses 3× space) - + 로그 추가 정보 (3× 공간 사용) Load existing file if present - + 기존 파일이 있으면 로드 Regions - + 지역 Export ROM snapshot - + ROM 스냅샷 내보내기 Start - 시작 + 시작 Stop - 정지 + 정지 Failed to open memory log file - + 메모리 로그 파일을 열 수 없음 Select access log file - + 접속 파일 선택 Memory access logs (*.mal) - + 메모리 접속 로그 (*.mal) @@ -4372,12 +4372,12 @@ Download size: %3 Maker Code: - + 메이커 코드: Revision: - + 리비전: @@ -4660,7 +4660,7 @@ Download size: %3 &Load most recent - + 최신 항목 로드 (&L) @@ -6374,7 +6374,7 @@ Download size: %3 &Lock frame size - + 프레임 크기 잠금 (&L) @@ -6655,7 +6655,7 @@ Download size: %3 Log memory &accesses... - + 메모리 및 접속 기록... From 2e65bb348bc91227f2408875c5e2d544ba2c6014 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 30 Jan 2022 08:07:04 +0100 Subject: [PATCH 103/114] Qt: Added translation (Chinese (Traditional)) Translation: mGBA/Qt Translate-URL: https://hosted.weblate.org/projects/mgba/mgba-qt/zh_Hant/ --- src/platform/qt/ts/mgba-zh_Hant.ts | 6174 ++++++++++++++++++++++++++++ 1 file changed, 6174 insertions(+) create mode 100644 src/platform/qt/ts/mgba-zh_Hant.ts diff --git a/src/platform/qt/ts/mgba-zh_Hant.ts b/src/platform/qt/ts/mgba-zh_Hant.ts new file mode 100644 index 000000000..9d127cda0 --- /dev/null +++ b/src/platform/qt/ts/mgba-zh_Hant.ts @@ -0,0 +1,6174 @@ + + + + + AboutScreen + + + About + + + + + <a href="http://mgba.io/">Website</a> • <a href="https://forums.mgba.io/">Forums / Support</a> • <a href="https://patreon.com/mgba">Donate</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">Source</a> + + + + + Branch: <tt>{gitBranch}</tt><br/>Revision: <tt>{gitCommit}</tt> + + + + + {projectName} would like to thank the following patrons from Patreon: + + + + + © 2013 – {year} Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0 +Game Boy Advance is a registered trademark of Nintendo Co., Ltd. + + + + + {projectName} is an open-source Game Boy Advance emulator + + + + + ApplicationUpdatePrompt + + + An update is available + + + + + ArchiveInspector + + + Open in archive... + + + + + Loading... + + + + + AssetTile + + + Tile # + + + + + Palette # + + + + + Address + + + + + Red + + + + + Green + + + + + Blue + + + + + BattleChipView + + + BattleChip Gate + + + + + Chip name + + + + + Insert + + + + + Save + + + + + Load + + + + + Add + + + + + Remove + + + + + Gate type + + + + + Inserted + + + + + Chip ID + + + + + Update Chip data + + + + + Show advanced + + + + + CheatsView + + + Cheats + + + + + Add New Code + + + + + Remove + + + + + Add Lines + + + + + Code type + + + + + Save + + + + + Load + + + + + Enter codes here... + + + + + DebuggerConsole + + + Debugger + + + + + Enter command (try `help` for more info) + + + + + Break + + + + + DolphinConnector + + + Connect to Dolphin + + + + + Local computer + + + + + IP address + + + + + Connect + + + + + Disconnect + + + + + Close + + + + + Reset on connect + + + + + FrameView + + + Inspect frame + + + + + Magnification + + + + + Freeze frame + + + + + Backdrop color + + + + + Disable scanline effects + + + + + Export + + + + + Reset + + + + + GIFView + + + Record GIF/WebP/APNG + + + + + Loop + + + + + Start + + + + + Stop + + + + + Select File + + + + + APNG + + + + + GIF + + + + + WebP + + + + + Frameskip + + + + + IOViewer + + + I/O Viewer + + + + + 0x0000 + + + + + B + + + + + LibraryTree + + + Name + + + + + Location + + + + + Platform + + + + + Size + + + + + CRC32 + + + + + LoadSaveState + + + + %1 State + + + + + + + + + + + + + No Save + + + + + 5 + + + + + 6 + + + + + 8 + + + + + 4 + + + + + 1 + + + + + 3 + + + + + 7 + + + + + 9 + + + + + 2 + + + + + Cancel + + + + + LogView + + + Logs + + + + + Enabled Levels + + + + + Debug + + + + + Stub + + + + + Info + + + + + Warning + + + + + Error + + + + + Fatal + + + + + Game Error + + + + + Advanced settings + + + + + Clear + + + + + Max Lines + + + + + MapView + + + Maps + + + + + Magnification + + + + + Export + + + + + Copy + + + + + MemoryDump + + + Save Memory Range + + + + + Start Address: + + + + + Byte Count: + + + + + Dump across banks + + + + + MemorySearch + + + Memory Search + + + + + Address + + + + + Current Value + + + + + + Type + + + + + Value + + + + + Numeric + + + + + Text + + + + + Width + + + + + + Guess + + + + + 1 Byte (8-bit) + + + + + 2 Bytes (16-bit) + + + + + 4 Bytes (32-bit) + + + + + Number type + + + + + Decimal + + + + + Hexadecimal + + + + + Search type + + + + + Equal to value + + + + + Greater than value + + + + + Less than value + + + + + Unknown/changed + + + + + Changed by value + + + + + Unchanged + + + + + Increased + + + + + Decreased + + + + + Search ROM + + + + + New Search + + + + + Search Within + + + + + Open in Memory Viewer + + + + + Refresh + + + + + MemoryView + + + Memory + + + + + Inspect Address: + + + + + Set Alignment: + + + + + &1 Byte + + + + + &2 Bytes + + + + + &4 Bytes + + + + + Unsigned Integer: + + + + + Signed Integer: + + + + + String: + + + + + Load TBL + + + + + Copy Selection + + + + + Paste + + + + + Save Selection + + + + + Save Range + + + + + Load + + + + + ObjView + + + Sprites + + + + + Address + + + + + Copy + + + + + Magnification + + + + + Geometry + + + + + Position + + + + + , + + + + + Dimensions + + + + + × + + + + + Matrix + + + + + Export + + + + + Attributes + + + + + Transform + + + + + Off + + + + + Palette + + + + + Double Size + + + + + + + Return, Ctrl+R + + + + + Flipped + + + + + H + Short for horizontal + + + + + V + Short for vertical + + + + + Mode + + + + + Normal + + + + + Mosaic + + + + + Enabled + + + + + Priority + + + + + Tile + + + + + OverrideView + + + Game Overrides + + + + + Game Boy Advance + + + + + + + + Autodetect + + + + + Realtime clock + + + + + Gyroscope + + + + + Tilt + + + + + Light sensor + + + + + Rumble + + + + + Save type + + + + + None + + + + + SRAM + + + + + Flash 512kb + + + + + Flash 1Mb + + + + + EEPROM 8kB + + + + + EEPROM 512 bytes + + + + + SRAM 64kB (bootlegs only) + + + + + Idle loop + + + + + Game Boy Player features + + + + + VBA bug compatibility mode + + + + + Game Boy + + + + + Game Boy model + + + + + Memory bank controller + + + + + Background Colors + + + + + Sprite Colors 1 + + + + + Sprite Colors 2 + + + + + Palette preset + + + + + PaletteView + + + Palette + + + + + Background + + + + + Objects + + + + + Selection + + + + + Red + + + + + Green + + + + + Blue + + + + + 16-bit value + + + + + Hex code + + + + + Palette index + + + + + Export BG + + + + + Export OBJ + + + + + PlacementControl + + + Adjust placement + + + + + All + + + + + Offset + + + + + X + + + + + Y + + + + + PrinterView + + + Game Boy Printer + + + + + Hurry up! + + + + + Tear off + + + + + Magnification + + + + + Copy + + + + + QGBA::AboutScreen + + + 2021 + + + + + QGBA::ApplicationUpdatePrompt + + + An update to %1 is available. + + + + + + +Do you want to download and install it now? You will need to restart the emulator when the download is complete. + + + + + +Auto-update is not available on this platform. If you wish to update you will need to do it manually. + + + + + Current version: %1 +New version: %2 +Download size: %3 + + + + + Downloading update... + + + + + Downloading failed. Please update manually. + + + + + Downloading done. Press OK to restart %1 and install the update. + + + + + QGBA::ApplicationUpdater + + + Stable + + + + + Development + + + + + Unknown + + + + + (None) + + + + + QGBA::AssetTile + + + %0%1%2 + + + + + + + 0x%0 (%1) + + + + + QGBA::CheatsModel + + + (untitled) + + + + + Failed to open cheats file: %1 + + + + + QGBA::CheatsView + + + + Autodetect (recommended) + + + + + + Select cheats file + + + + + QGBA::CoreController + + + Failed to open save file: %1 + + + + + Failed to open game file: %1 + + + + + Can't yank pack in unexpected platform! + + + + + Failed to open snapshot file for reading: %1 + + + + + Failed to open snapshot file for writing: %1 + + + + + QGBA::CoreManager + + + Failed to open game file: %1 + + + + + Could not load game. Are you sure it's in the correct format? + + + + + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). + + + + + QGBA::FrameView + + + Export frame + + + + + Portable Network Graphics (*.png) + + + + + None + + + + + Background + + + + + Window + + + + + Objwin + + + + + Sprite + + + + + Backdrop + + + + + Frame + + + + + %1 %2 + + + + + QGBA::GBAApp + + + Enable Discord Rich Presence + + + + + QGBA::GBAKeyEditor + + + Clear Button + + + + + Clear Analog + + + + + Refresh + + + + + Set all + + + + + QGBA::GDBWindow + + + Server settings + + + + + Local port + + + + + Bind address + + + + + Break + + + + + Stop + + + + + Start + + + + + Crash + + + + + Could not start GDB server + + + + + QGBA::GIFView + + + Failed to open output file: %1 + + + + + Select output file + + + + + Graphics Interchange Format (*.gif);;WebP ( *.webp);;Animated Portable Network Graphics (*.png *.apng) + + + + + QGBA::IOViewer + + + Background mode + + + + + Mode 0: 4 tile layers + + + + + Mode 1: 2 tile layers + 1 rotated/scaled tile layer + + + + + Mode 2: 2 rotated/scaled tile layers + + + + + Mode 3: Full 15-bit bitmap + + + + + Mode 4: Full 8-bit bitmap + + + + + Mode 5: Small 15-bit bitmap + + + + + CGB Mode + + + + + Frame select + + + + + Unlocked HBlank + + + + + Linear OBJ tile mapping + + + + + Force blank screen + + + + + Enable background 0 + + + + + Enable background 1 + + + + + Enable background 2 + + + + + Enable background 3 + + + + + Enable OBJ + + + + + Enable Window 0 + + + + + Enable Window 1 + + + + + Enable OBJ Window + + + + + Swap green components + + + + + Currently in VBlank + + + + + Currently in HBlank + + + + + Currently in VCounter + + + + + Enable VBlank IRQ generation + + + + + Enable HBlank IRQ generation + + + + + Enable VCounter IRQ generation + + + + + VCounter scanline + + + + + Current scanline + + + + + + + + Priority + + + + + + + + Tile data base (* 16kB) + + + + + + + + Enable mosaic + + + + + + + + Enable 256-color + + + + + + + + Tile map base (* 2kB) + + + + + + + + Background dimensions + + + + + + Overflow wraps + + + + + + + + + + Horizontal offset + + + + + + + + + + Vertical offset + + + + + + + + + + + + + + + + Fractional part + + + + + + + + + + + + Integer part + + + + + + + + Integer part (low) + + + + + + + + Integer part (high) + + + + + + End x + + + + + + Start x + + + + + + End y + + + + + + Start y + + + + + Window 0 enable BG 0 + + + + + Window 0 enable BG 1 + + + + + Window 0 enable BG 2 + + + + + Window 0 enable BG 3 + + + + + Window 0 enable OBJ + + + + + Window 0 enable blend + + + + + Window 1 enable BG 0 + + + + + Window 1 enable BG 1 + + + + + Window 1 enable BG 2 + + + + + Window 1 enable BG 3 + + + + + Window 1 enable OBJ + + + + + Window 1 enable blend + + + + + Outside window enable BG 0 + + + + + Outside window enable BG 1 + + + + + Outside window enable BG 2 + + + + + Outside window enable BG 3 + + + + + Outside window enable OBJ + + + + + Outside window enable blend + + + + + OBJ window enable BG 0 + + + + + OBJ window enable BG 1 + + + + + OBJ window enable BG 2 + + + + + OBJ window enable BG 3 + + + + + OBJ window enable OBJ + + + + + OBJ window enable blend + + + + + Background mosaic size vertical + + + + + Background mosaic size horizontal + + + + + Object mosaic size vertical + + + + + Object mosaic size horizontal + + + + + BG 0 target 1 + + + + + BG 1 target 1 + + + + + BG 2 target 1 + + + + + BG 3 target 1 + + + + + OBJ target 1 + + + + + Backdrop target 1 + + + + + Blend mode + + + + + Disabled + + + + + Additive blending + + + + + Brighten + + + + + Darken + + + + + BG 0 target 2 + + + + + BG 1 target 2 + + + + + BG 2 target 2 + + + + + BG 3 target 2 + + + + + OBJ target 2 + + + + + Backdrop target 2 + + + + + Blend A (target 1) + + + + + Blend B (target 2) + + + + + Blend Y + + + + + + Sweep shifts + + + + + + Sweep subtract + + + + + + Sweep time (in 1/128s) + + + + + + + + + + + + Sound length + + + + + + + + Duty cycle + + + + + + + + + + Envelope step time + + + + + + + + + + Envelope increase + + + + + + + + + + Initial volume + + + + + + + Sound frequency + + + + + + + + + + + + Timed + + + + + + + + + + + + Reset + + + + + Double-size wave table + + + + + Active wave table + + + + + + Enable channel 3 + + + + + + Volume + + + + + + 0% + + + + + + + 100% + + + + + + + 50% + + + + + + + 25% + + + + + + + + 75% + + + + + + Clock divider + + + + + + Register stages + + + + + + 15 + + + + + + 7 + + + + + + Shifter frequency + + + + + PSG volume right + + + + + PSG volume left + + + + + + Enable channel 1 right + + + + + + Enable channel 2 right + + + + + + Enable channel 3 right + + + + + + Enable channel 4 right + + + + + + Enable channel 1 left + + + + + + Enable channel 2 left + + + + + + Enable channel 3 left + + + + + + Enable channel 4 left + + + + + PSG master volume + + + + + Loud channel A + + + + + Loud channel B + + + + + Enable channel A right + + + + + Enable channel A left + + + + + Channel A timer + + + + + + 0 + + + + + + + + + + + + + 1 + + + + + Channel A reset + + + + + Enable channel B right + + + + + Enable channel B left + + + + + Channel B timer + + + + + Channel B reset + + + + + + Active channel 1 + + + + + + Active channel 2 + + + + + + Active channel 3 + + + + + + Active channel 4 + + + + + + Enable audio + + + + + Bias + + + + + Resolution + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sample + + + + + + + + + + + + Address (low) + + + + + + + + + + + + Address (high) + + + + + + + Sound frequency (low) + + + + + + + Sound frequency (high) + + + + + Source (high) + + + + + Source (low) + + + + + Destination (high) + + + + + Destination (low) + + + + + + Green (low) + + + + + + Green (high) + + + + + + + + Word count + + + + + + + + Destination offset + + + + + + + + + + + + Increment + + + + + + + + + + + + Decrement + + + + + + + + + + + + Fixed + + + + + + + + Increment and reload + + + + + + + + Source offset + + + + + + + + Repeat + + + + + + + + 32-bit + + + + + + + + Start timing + + + + + + + + + Immediate + + + + + + + + + + + + VBlank + + + + + + + + + + + HBlank + + + + + + + + + + + + + IRQ + + + + + + + + + + + + + + + Enable + + + + + + + Audio FIFO + + + + + Video Capture + + + + + DRQ + + + + + + + + + + + + Value + + + + + + + + Scale + + + + + + + + + 1/64 + + + + + + + + + 1/256 + + + + + + + + + 1/1024 + + + + + + + Cascade + + + + + + A + + + + + + B + + + + + + Select + + + + + + Start + + + + + + Right + + + + + + Left + + + + + + Up + + + + + + Down + + + + + + R + + + + + + L + + + + + Condition + + + + + SC + + + + + SD + + + + + SI + + + + + SO + + + + + + VCounter + + + + + + Timer 0 + + + + + + Timer 1 + + + + + + Timer 2 + + + + + + Timer 3 + + + + + + SIO + + + + + + DMA 0 + + + + + + DMA 1 + + + + + + DMA 2 + + + + + + DMA 3 + + + + + + Keypad + + + + + + Gamepak + + + + + SRAM wait + + + + + + + + + 4 + + + + + + + + 3 + + + + + + + + + 2 + + + + + + + + + 8 + + + + + Cart 0 non-sequential + + + + + Cart 0 sequential + + + + + Cart 1 non-sequential + + + + + Cart 1 sequential + + + + + Cart 2 non-sequential + + + + + Cart 2 sequential + + + + + PHI terminal + + + + + + Disable + + + + + 4.19MHz + + + + + 8.38MHz + + + + + 16.78MHz + + + + + Gamepak prefetch + + + + + Enable IRQs + + + + + Right/A + + + + + Left/B + + + + + Up/Select + + + + + Down/Start + + + + + Active D-pad + + + + + Active face buttons + + + + + Internal clock + + + + + 32× clocking (CGB only) + + + + + Transfer active + + + + + Divider + + + + + 1/16 + + + + + + LCD STAT + + + + + + Timer + + + + + + Serial + + + + + + Joypad + + + + + Volume right + + + + + Output right + + + + + Volume left + + + + + Output left + + + + + Background enable/priority + + + + + Enable sprites + + + + + Double-height sprites + + + + + Background tile map + + + + + + 0x9800 – 0x9BFF + + + + + + 0x9C00 – 0x9FFF + + + + + Background tile data + + + + + 0x8800 – 0x87FF + + + + + 0x8000 – 0x8FFF + + + + + Enable window + + + + + Window tile map + + + + + Enable LCD + + + + + Mode + + + + + 0: HBlank + + + + + 1: VBlank + + + + + 2: OAM scan + + + + + 3: HDraw + + + + + In LYC + + + + + Enable HBlank (mode 0) IRQ + + + + + Enable VBlank (mode 1) IRQ + + + + + Enable OAM (mode 2) IRQ + + + + + Enable LYC IRQ + + + + + Current Y coordinate + + + + + Comparison Y coordinate + + + + + Start upper byte + + + + + + + Color 0 shade + + + + + + + Color 1 shade + + + + + + + Color 2 shade + + + + + + + Color 3 shade + + + + + Prepare to switch speed + + + + + Double speed + + + + + VRAM bank + + + + + Length + + + + + Timing + + + + + Write bit + + + + + Read bit + + + + + + Unknown + + + + + + Current index + + + + + + Auto-increment + + + + + + Red + + + + + + Blue + + + + + Sprite ordering + + + + + OAM order + + + + + x coordinate sorting + + + + + WRAM bank + + + + + QGBA::KeyEditor + + + + --- + + + + + Super (L) + + + + + Super (R) + + + + + Menu + + + + + QGBA::LoadSaveState + + + Load State + + + + + Save State + + + + + Empty + + + + + Corrupted + + + + + Slot %1 + + + + + QGBA::LogConfigModel + + + + Default + + + + + Fatal + + + + + Error + + + + + Warning + + + + + Info + + + + + Debug + + + + + Stub + + + + + Game Error + + + + + QGBA::LogController + + + [%1] %2: %3 + + + + + An error occurred + + + + + DEBUG + + + + + STUB + + + + + INFO + + + + + WARN + + + + + ERROR + + + + + FATAL + + + + + GAME ERROR + + + + + QGBA::MapView + + + Priority + + + + + + Map base + + + + + + Tile base + + + + + Size + + + + + + Offset + + + + + Xform + + + + + Map Addr. + + + + + Mirror + + + + + None + + + + + Both + + + + + Horizontal + + + + + Vertical + + + + + + + N/A + + + + + Export map + + + + + Portable Network Graphics (*.png) + + + + + QGBA::MemoryDump + + + Save memory region + + + + + Failed to open output file: %1 + + + + + QGBA::MemoryModel + + + Copy selection + + + + + Save selection + + + + + Paste + + + + + Load + + + + + All + + + + + Load TBL + + + + + Save selected memory + + + + + Failed to open output file: %1 + + + + + Load memory + + + + + Failed to open input file: %1 + + + + + TBL + + + + + ISO-8859-1 + + + + + QGBA::MemorySearch + + + (%0/%1×) + + + + + (⅟%0×) + + + + + (%0×) + + + + + %1 byte%2 + + + + + QGBA::ObjView + + + + 0x%0 + + + + + Off + + + + + + + + + + + + --- + + + + + Normal + + + + + Trans + + + + + OBJWIN + + + + + Invalid + + + + + + N/A + + + + + Export sprite + + + + + Portable Network Graphics (*.png) + + + + + QGBA::OverrideView + + + Official MBCs + + + + + Licensed MBCs + + + + + Unlicensed MBCs + + + + + QGBA::PaletteView + + + #%0 + + + + + 0x%0 + + + + + + + + 0x%0 (%1) + + + + + Export palette + + + + + Windows PAL (*.pal);;Adobe Color Table (*.act) + + + + + Failed to open output palette file: %1 + + + + + QGBA::ROMInfo + + + + + + + (unknown) + + + + + + bytes + + + + + (no database present) + + + + + QGBA::ReportView + + + Bug report archive + + + + + ZIP archive (*.zip) + + + + + QGBA::SaveConverter + + + Save games and save states (%1) + + + + + Select save game or save state + + + + + Save games (%1) + + + + + Select save game + + + + + Conversion failed + + + + + Failed to convert the save game. This is probably a bug. + + + + + No file selected + + + + + Could not open file + + + + + No valid formats found + + + + + Please select a valid input file + + + + + No valid conversions found + + + + + Cannot convert save games between platforms + + + + + QGBA::SettingsView + + + + Qt Multimedia + + + + + SDL + + + + + Software (Qt) + + + + + OpenGL + + + + + OpenGL (force version 1.x) + + + + + None + + + + + None (Still Image) + + + + + Keyboard + + + + + Controllers + + + + + Shortcuts + + + + + + Shaders + + + + + Select BIOS + + + + + Select directory + + + + + (%1×%2) + + + + + Never + + + + + Just now + + + + + Less than an hour ago + + + + + %n hour(s) ago + + + + + + + %n day(s) ago + + + + + + + QGBA::ShaderSelector + + + No shader active + + + + + Load shader + + + + + No shader loaded + + + + + by %1 + + + + + Preprocessing + + + + + Pass %1 + + + + + QGBA::ShortcutModel + + + Action + + + + + Keyboard + + + + + Gamepad + + + + + QGBA::TileView + + + Export tiles + + + + + + Portable Network Graphics (*.png) + + + + + Export tile + + + + + QGBA::VideoView + + + Failed to open output video file: %1 + + + + + Native (%0x%1) + + + + + Select output file + + + + + QGBA::Window + + + Game Boy Advance ROMs (%1) + + + + + Game Boy ROMs (%1) + + + + + All ROMs (%1) + + + + + %1 Video Logs (*.mvl) + + + + + Archives (%1) + + + + + + + Select ROM + + + + + Select folder + + + + + + Select save + + + + + Select patch + + + + + Patches (*.ips *.ups *.bps) + + + + + Select e-Reader dotcode + + + + + e-Reader card (*.raw *.bin *.bmp) + + + + + Select image + + + + + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) + + + + + GameShark saves (*.sps *.xps) + + + + + Select video log + + + + + Video logs (*.mvl) + + + + + Crash + + + + + The game has crashed with the following error: + +%1 + + + + + Couldn't Start + + + + + Could not start game. + + + + + Unimplemented BIOS call + + + + + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. + + + + + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. + + + + + Really make portable? + + + + + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? + + + + + Restart needed + + + + + Some changes will not take effect until the emulator is restarted. + + + + + - Player %1 of %2 + + + + + %1 - %2 + + + + + %1 - %2 - %3 + + + + + %1 - %2 (%3 fps) - %4 + + + + + &File + + + + + Load &ROM... + + + + + Load ROM in archive... + + + + + Add folder to library... + + + + + Save games (%1) + + + + + Select save game + + + + + mGBA save state files (%1) + + + + + + Select save state + + + + + Select e-Reader card images + + + + + Image file (*.png *.jpg *.jpeg) + + + + + Conversion finished + + + + + %1 of %2 e-Reader cards converted successfully. + + + + + Load alternate save game... + + + + + Load temporary save game... + + + + + Load &patch... + + + + + Boot BIOS + + + + + Replace ROM... + + + + + Scan e-Reader dotcodes... + + + + + Convert e-Reader card image to raw... + + + + + ROM &info... + + + + + Recent + + + + + Make portable + + + + + &Load state + + + + + Load state file... + + + + + &Save state + + + + + Save state file... + + + + + Quick load + + + + + Quick save + + + + + Load recent + + + + + Save recent + + + + + Undo load state + + + + + Undo save state + + + + + + State &%1 + + + + + Load camera image... + + + + + Convert save game... + + + + + GameShark saves (*.gsv *.sps *.xps) + + + + + Import GameShark Save... + + + + + Export GameShark Save... + + + + + New multiplayer window + + + + + Connect to Dolphin... + + + + + Report bug... + + + + + About... + + + + + E&xit + + + + + &Emulation + + + + + &Reset + + + + + Sh&utdown + + + + + Yank game pak + + + + + &Pause + + + + + &Next frame + + + + + Fast forward (held) + + + + + &Fast forward + + + + + Fast forward speed + + + + + Unbounded + + + + + %0x + + + + + Rewind (held) + + + + + Re&wind + + + + + Step backwards + + + + + Sync to &video + + + + + Sync to &audio + + + + + Solar sensor + + + + + Increase solar level + + + + + Decrease solar level + + + + + Brightest solar level + + + + + Darkest solar level + + + + + Brightness %1 + + + + + Game Boy Printer... + + + + + BattleChip Gate... + + + + + Audio/&Video + + + + + Frame size + + + + + %1× + + + + + Toggle fullscreen + + + + + Lock aspect ratio + + + + + Force integer scaling + + + + + Interframe blending + + + + + Bilinear filtering + + + + + Frame&skip + + + + + Mute + + + + + FPS target + + + + + Native (59.7275) + + + + + Take &screenshot + + + + + F12 + + + + + Record A/V... + + + + + Record GIF/WebP/APNG... + + + + + Video layers + + + + + Audio channels + + + + + Adjust layer placement... + + + + + &Tools + + + + + View &logs... + + + + + Game &overrides... + + + + + Game Pak sensors... + + + + + &Cheats... + + + + + Settings... + + + + + Open debugger console... + + + + + Start &GDB server... + + + + + View &palette... + + + + + View &sprites... + + + + + View &tiles... + + + + + View &map... + + + + + &Frame inspector... + + + + + View memory... + + + + + Search memory... + + + + + View &I/O registers... + + + + + Record debug video log... + + + + + Stop debug video log + + + + + Exit fullscreen + + + + + GameShark Button (held) + + + + + Autofire + + + + + Autofire A + + + + + Autofire B + + + + + Autofire L + + + + + Autofire R + + + + + Autofire Start + + + + + Autofire Select + + + + + Autofire Up + + + + + Autofire Right + + + + + Autofire Down + + + + + Autofire Left + + + + + Clear + + + + + QObject + + + %1 byte + + + + + %1 kiB + + + + + %1 MiB + + + + + GBA + + + + + GB + + + + + ? + + + + + QShortcut + + + Shift + + + + + Control + + + + + Alt + + + + + Meta + + + + + ROMInfo + + + ROM Info + + + + + Game name: + + + + + Internal name: + + + + + Game ID: + + + + + File size: + + + + + CRC32: + + + + + ReportView + + + Generate Bug Report + + + + + <html><head/><body><p>To file a bug report, please first generate a report file to attach to the bug report you're about to file. It is recommended that you include the save files, as these often help with debugging issues. This will collect some information about the version of {projectName} you're running, your configuration, your computer, and the game you currently have open (if any). Once this collection is completed you can review all of the information gathered below and save it to a zip file. The collection will automatically attempt to redact any personal information, such as your username if it's in any of the paths gathered, but just in case you can edit it afterwards. After you have generated and saved it, please click the button below or go to <a href="https://mgba.io/i/"><span style=" text-decoration: underline; color:#2980b9;">mgba.io/i</span></a> to file the bug report on GitHub. Make sure to attach the report you generated!</p></body></html> + + + + + Generate report + + + + + Save + + + + + Open issue list in browser + + + + + Include save file + + + + + Create and include savestate + + + + + SaveConverter + + + Convert/Extract Save Game + + + + + Input file + + + + + + Browse + + + + + Output file + + + + + %1 %2 save game + + + + + little endian + + + + + big endian + + + + + SRAM + + + + + %1 flash + + + + + %1 EEPROM + + + + + %1 SRAM + RTC + + + + + %1 SRAM + + + + + packed MBC2 + + + + + unpacked MBC2 + + + + + MBC6 flash + + + + + MBC6 combined SRAM + flash + + + + + MBC6 SRAM + + + + + TAMA5 + + + + + %1 (%2) + + + + + %1 save state with embedded %2 save game + + + + + %1 SharkPort %2 save game + + + + + %1 GameShark Advance SP %2 save game + + + + + SensorView + + + Sensors + + + + + Realtime clock + + + + + Fixed time + + + + + System time + + + + + Start time at + + + + + Now + + + + + MM/dd/yy hh:mm:ss AP + + + + + Light sensor + + + + + Brightness + + + + + Tilt sensor + + + + + + Set Y + + + + + + Set X + + + + + Gyroscope + + + + + Sensitivity + + + + + SettingsView + + + Settings + + + + + Audio/Video + + + + + Interface + + + + + Update + + + + + Emulation + + + + + Enhancements + + + + + BIOS + + + + + Paths + + + + + Logging + + + + + Game Boy + + + + + Audio driver: + + + + + Audio buffer: + + + + + + 1536 + + + + + 512 + + + + + 768 + + + + + 1024 + + + + + 2048 + + + + + 3072 + + + + + 4096 + + + + + samples + + + + + Sample rate: + + + + + + 44100 + + + + + 22050 + + + + + 32000 + + + + + 48000 + + + + + Hz + + + + + Volume: + + + + + + + + Mute + + + + + Fast forward volume: + + + + + Audio in multiplayer: + + + + + All windows + + + + + Player 1 window only + + + + + Currently active player window + + + + + Display driver: + + + + + Frameskip: + + + + + Skip every + + + + + + frames + + + + + FPS target: + + + + + frames per second + + + + + Sync: + + + + + Video + + + + + Audio + + + + + Lock aspect ratio + + + + + Force integer scaling + + + + + Bilinear filtering + + + + + + Pause + + + + + When inactive: + + + + + When minimized: + + + + + Current channel: + + + + + Current version: + + + + + Update channel: + + + + + Available version: + + + + + (Unknown) + + + + + Last checked: + + + + + Automatically check on start + + + + + Check now + + + + + Default color palette only + + + + + SGB color palette if available + + + + + GBC color palette if available + + + + + SGB (preferred) or GBC color palette if available + + + + + Game Boy Camera + + + + + Driver: + + + + + Source: + + + + + Native (59.7275) + + + + + Interframe blending + + + + + Language + + + + + Library: + + + + + List view + + + + + Tree view + + + + + Show when no game open + + + + + Clear cache + + + + + Allow opposing input directions + + + + + Suspend screensaver + + + + + Dynamically update window title + + + + + Show filename instead of ROM name in title bar + + + + + Show OSD messages + + + + + Enable Discord Rich Presence + + + + + Automatically save state + + + + + Automatically load state + + + + + Automatically save cheats + + + + + Automatically load cheats + + + + + Show FPS in title bar + + + + + Fast forward speed: + + + + + + Unbounded + + + + + Fast forward (held) speed: + + + + + Autofire interval: + + + + + Enable rewind + + + + + Rewind history: + + + + + Idle loops: + + + + + Run all + + + + + Remove known + + + + + Detect and remove + + + + + Preload entire ROM into memory + + + + + Save state extra data: + + + + + + Save game + + + + + Load state extra data: + + + + + Models + + + + + GB only: + + + + + SGB compatible: + + + + + GBC only: + + + + + GBC compatible: + + + + + SGB and GBC compatible: + + + + + Game Boy palette + + + + + Preset: + + + + + + Screenshot + + + + + + Cheat codes + + + + + Enable Game Boy Player features by default + + + + + Enable VBA bug compatibility in ROM hacks + + + + + Video renderer: + + + + + Software + + + + + OpenGL + + + + + OpenGL enhancements + + + + + High-resolution scale: + + + + + (240×160) + + + + + XQ GBA audio (experimental) + + + + + GB BIOS file: + + + + + + + + + + + + + Browse + + + + + Use BIOS file if found + + + + + Skip BIOS intro + + + + + GBA BIOS file: + + + + + GBC BIOS file: + + + + + SGB BIOS file: + + + + + Save games + + + + + + + + + Same directory as the ROM + + + + + Save states + + + + + Screenshots + + + + + Patches + + + + + Cheats + + + + + Log to file + + + + + Log to console + + + + + Select Log File + + + + + Default BG colors: + + + + + Default sprite colors 1: + + + + + Default sprite colors 2: + + + + + Super Game Boy borders + + + + + ShaderSelector + + + Shaders + + + + + Active Shader: + + + + + Name + + + + + Author + + + + + Description + + + + + Unload Shader + + + + + Load New Shader + + + + + ShortcutView + + + Edit Shortcuts + + + + + Keyboard + + + + + Gamepad + + + + + Clear + + + + + TileView + + + Tiles + + + + + Export Selected + + + + + Export All + + + + + 256 colors + + + + + Magnification + + + + + Tiles per row + + + + + Fit to window + + + + + Copy Selected + + + + + Copy All + + + + + VideoView + + + Record Video + + + + + Start + + + + + Stop + + + + + Select File + + + + + Presets + + + + + High &Quality + + + + + &YouTube + + + + + + WebM + + + + + + MP4 + + + + + &Lossless + + + + + 4K + + + + + &1080p + + + + + &720p + + + + + &480p + + + + + &Native + + + + + Format + + + + + MKV + + + + + AVI + + + + + HEVC + + + + + HEVC (NVENC) + + + + + VP8 + + + + + VP9 + + + + + FFV1 + + + + + + None + + + + + FLAC + + + + + Opus + + + + + Vorbis + + + + + MP3 + + + + + AAC + + + + + Uncompressed + + + + + Bitrate (kbps) + + + + + ABR + + + + + H.264 + + + + + H.264 (NVENC) + + + + + VBR + + + + + CRF + + + + + Dimensions + + + + + Lock aspect ratio + + + + + Show advanced + + + + From 58cdebcccdecc2c52cf0b75bb045513c62d539e1 Mon Sep 17 00:00:00 2001 From: cyruuspmd5de3b0e46e4e4998 Date: Sun, 30 Jan 2022 07:29:05 +0000 Subject: [PATCH 104/114] Qt: Update translation (Chinese (Traditional)) Translation: mGBA/Qt Translate-URL: https://hosted.weblate.org/projects/mgba/mgba-qt/zh_Hant/ --- src/platform/qt/ts/mgba-zh_Hant.ts | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/platform/qt/ts/mgba-zh_Hant.ts b/src/platform/qt/ts/mgba-zh_Hant.ts index 9d127cda0..9198b253f 100644 --- a/src/platform/qt/ts/mgba-zh_Hant.ts +++ b/src/platform/qt/ts/mgba-zh_Hant.ts @@ -6,33 +6,34 @@ About - + 關於 <a href="http://mgba.io/">Website</a> • <a href="https://forums.mgba.io/">Forums / Support</a> • <a href="https://patreon.com/mgba">Donate</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">Source</a> - + <a href="http://mgba.io/">網站</a> • <a href="https://forums.mgba.io/">論壇、支持</a> • <a href="https://patreon.com/mgba">捐助</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">源代碼</a> Branch: <tt>{gitBranch}</tt><br/>Revision: <tt>{gitCommit}</tt> - + 分支: <tt>{gitBranch}</tt><br/>修訂版: <tt>{gitCommit}</tt> {projectName} would like to thank the following patrons from Patreon: - + {projectName} 感謝以下來自 Patreon 的讚助者: © 2013 – {year} Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0 Game Boy Advance is a registered trademark of Nintendo Co., Ltd. - + © 2013 – {year} Jeffrey Pfau,基於 Mozilla 公共許可證(版本 2.0)授權 +Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標。 {projectName} is an open-source Game Boy Advance emulator - + {projectName} 是開放源代碼的 Game Boy Advance 模擬器 @@ -40,7 +41,7 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. An update is available - + 有可用的更新 @@ -48,12 +49,12 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Open in archive... - + 在壓縮文件中打開... Loading... - + 正在載入... @@ -61,12 +62,12 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. Tile # - + 圖塊 # Palette # - + 調色板 # From 24aa17a6fcb8b393368f5e1736cca3a6ff5a5332 Mon Sep 17 00:00:00 2001 From: Still Hsu Date: Thu, 21 Apr 2022 04:10:02 +0000 Subject: [PATCH 105/114] Qt: Update translation (Chinese (Traditional)) Translation: mGBA/Qt Translate-URL: https://hosted.weblate.org/projects/mgba/mgba-qt/zh_Hant/ --- src/platform/qt/ts/mgba-zh_Hant.ts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/platform/qt/ts/mgba-zh_Hant.ts b/src/platform/qt/ts/mgba-zh_Hant.ts index 9198b253f..5950c0fb8 100644 --- a/src/platform/qt/ts/mgba-zh_Hant.ts +++ b/src/platform/qt/ts/mgba-zh_Hant.ts @@ -33,7 +33,7 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 {projectName} is an open-source Game Boy Advance emulator - {projectName} 是開放源代碼的 Game Boy Advance 模擬器 + {projectName} 是開源的 Game Boy Advance 模擬器 @@ -72,22 +72,22 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 Address - + 地址 Red - + Green - + Blue - + @@ -158,42 +158,42 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 Cheats - + 金手指 Add New Code - + 新增金手指 Remove - + 移除 Add Lines - + 新增行 Code type - + 代碼類型 Save - + 儲存 Load - + 載入 Enter codes here... - + 在此輸入代碼... From b740e3e18cf412e7a781d3d73337994bd0ae0597 Mon Sep 17 00:00:00 2001 From: reimu105 Date: Tue, 12 Dec 2023 13:15:37 +0000 Subject: [PATCH 106/114] Qt: Update translation (Chinese (Traditional)) Translation: mGBA/Qt Translate-URL: https://hosted.weblate.org/projects/mgba/mgba-qt/zh_Hant/ --- src/platform/qt/ts/mgba-zh_Hant.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/platform/qt/ts/mgba-zh_Hant.ts b/src/platform/qt/ts/mgba-zh_Hant.ts index 5950c0fb8..8acf4e607 100644 --- a/src/platform/qt/ts/mgba-zh_Hant.ts +++ b/src/platform/qt/ts/mgba-zh_Hant.ts @@ -110,22 +110,22 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 Save - + 存檔 Load - + 讀取 Add - + 加入 Remove - + 移除 @@ -229,17 +229,17 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 IP address - + IP位址 Connect - + 連接 Disconnect - + 斷開 From c00a0e8a0ac4b85f34601c5c860ffe8b8ff83323 Mon Sep 17 00:00:00 2001 From: Gabriel Kao Date: Thu, 24 Oct 2024 07:11:06 +0000 Subject: [PATCH 107/114] Qt: Update translation (Chinese (Traditional Han script)) Translation: mGBA/Qt Translate-URL: https://hosted.weblate.org/projects/mgba/mgba-qt/zh_Hant/ --- src/platform/qt/ts/mgba-zh_Hant.ts | 2161 ++++++++++++++-------------- 1 file changed, 1084 insertions(+), 1077 deletions(-) diff --git a/src/platform/qt/ts/mgba-zh_Hant.ts b/src/platform/qt/ts/mgba-zh_Hant.ts index 8acf4e607..279452fe5 100644 --- a/src/platform/qt/ts/mgba-zh_Hant.ts +++ b/src/platform/qt/ts/mgba-zh_Hant.ts @@ -11,7 +11,7 @@ <a href="http://mgba.io/">Website</a> • <a href="https://forums.mgba.io/">Forums / Support</a> • <a href="https://patreon.com/mgba">Donate</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">Source</a> - <a href="http://mgba.io/">網站</a> • <a href="https://forums.mgba.io/">論壇、支持</a> • <a href="https://patreon.com/mgba">捐助</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">源代碼</a> + <a href="http://mgba.io/">網站</a> • <a href="https://forums.mgba.io/">論壇、取得協助</a> • <a href="https://patreon.com/mgba">贊助</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">原始碼</a> @@ -21,7 +21,7 @@ {projectName} would like to thank the following patrons from Patreon: - {projectName} 感謝以下來自 Patreon 的讚助者: + {projectName} 感謝以下 Patreon 贊助者: @@ -67,17 +67,17 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 Palette # - 調色板 # + 調色盤 # Address - 地址 + 位址 Red - + @@ -95,17 +95,17 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 BattleChip Gate - + 戰鬥晶片閘 Chip name - + 晶片名稱 Insert - + 插入 @@ -130,27 +130,27 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 Gate type - + 閘門類別 Inserted - + 已插入 Chip ID - + 晶片ID Update Chip data - + 更新晶片資料 Show advanced - + 顯示進階選項 @@ -168,12 +168,12 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 Remove - 移除 + 移除 Add Lines - 新增行 + 新增一行 @@ -183,7 +183,7 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 Save - 儲存 + 儲存 @@ -201,17 +201,17 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 Debugger - + 除錯器 Enter command (try `help` for more info) - + 輸入指令 (更多資訊請見「幫助」) Break - + 中斷 @@ -219,12 +219,12 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 Connect to Dolphin - + 連接 Dolphin Local computer - + 區域電腦 @@ -239,17 +239,17 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 Disconnect - 斷開 + 中斷連接 Close - + 關閉 Reset on connect - + 連接後重新啟動 @@ -257,37 +257,37 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 Inspect frame - + 檢查畫格 Magnification - + 放大 Freeze frame - + 暫停畫格 Backdrop color - + 背景顏色 Disable scanline effects - + 停用掃描線特效 Export - + 匯出 Reset - + 重新啟動 @@ -295,47 +295,47 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 Record GIF/WebP/APNG - + 錄製 GIF/WebP/APNG Loop - + 循環 Start - + 開始 Stop - + 停止 Select File - + 選擇檔案 APNG - + APNG GIF - + GIF WebP - + WebP Frameskip - + 跳過畫格 @@ -343,17 +343,17 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 I/O Viewer - + I/O 檢視器 0x0000 - + 0x0000 B - + B @@ -361,27 +361,27 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 Name - + 名稱 Location - + 位置 Platform - + 平台 Size - + 大小 CRC32 - + CRC32 @@ -390,7 +390,7 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 %1 State - + %1 即時存檔 @@ -403,57 +403,57 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 No Save - + 無存檔 5 - + 5 6 - + 6 8 - + 8 4 - + 4 1 - + 1 3 - + 3 7 - + 7 9 - + 9 2 - + 2 Cancel - + 取消 @@ -461,62 +461,62 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 Logs - + 記錄 Enabled Levels - + 已啟用的關卡 Debug - + 除錯 Stub - + 測試樁 Info - + 資訊 Warning - + 警告 Error - + 錯誤 Fatal - + 嚴重 Game Error - + 遊戲錯誤 Advanced settings - + 進階設定 Clear - + 清除 Max Lines - + 最大行數 @@ -524,22 +524,22 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 Maps - + 映射 Magnification - + 放大 Export - + 匯出 Copy - + 複製 @@ -547,22 +547,22 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 Save Memory Range - + 記憶體儲存範圍 Start Address: - + 起始位址: Byte Count: - + 位元組數: Dump across banks - + 跨記憶庫傾印 @@ -570,149 +570,149 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 Memory Search - + 搜尋記憶體 Address - + 位址 Current Value - + 目前的值 Type - + 型別 Value - + Numeric - + 數字 Text - + 文字 Width - + 寬度 Guess - + 預測 1 Byte (8-bit) - + 1 位元組 (8 位元) 2 Bytes (16-bit) - + 2 位元組 (16 位元) 4 Bytes (32-bit) - + 4 位元組 (32 位元) Number type - + 數字型別 Decimal - + 十進位 Hexadecimal - + 十六進位 Search type - + 搜尋型別 Equal to value - + 等於值 Greater than value - + 大於值 Less than value - + 小於值 Unknown/changed - + 未知/已變更 Changed by value - + 數值變更 Unchanged - + 未變更 Increased - + 增加 Decreased - + 減少 Search ROM - + 搜尋 ROM New Search - + 新的搜尋 Search Within - + 在...裡搜尋 Open in Memory Viewer - + 在記憶體檢視器中打開 Refresh - + 重新整理 @@ -720,77 +720,77 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 Memory - + 記憶體 Inspect Address: - + 檢查位址: Set Alignment: - + 資料對齊設定: &1 Byte - + 1 位元組 (&1) &2 Bytes - + 2 位元組 (&2) &4 Bytes - + 4 位元組 (&4) Unsigned Integer: - + 無符號整數: Signed Integer: - + 有符號整數: String: - + 字串: Load TBL - + 讀取 TBL Copy Selection - + 複製所選 Paste - + 貼上 Save Selection - + 儲存所選 Save Range - + 儲存範圍 Load - + 讀取 @@ -798,136 +798,136 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 Sprites - + 角色圖 Address - + 位址 Copy - + 複製 Magnification - + 放大 Geometry - + 幾何 Position - + 位置 , - + , Dimensions - + 維度 × - + × Matrix - + 矩陣 Export - + 匯出 Attributes - + 屬性 Transform - + 變換 Off - + Palette - + 調色盤 Double Size - + 兩倍大小 Return, Ctrl+R - + 返回,Ctrl+R Flipped - + 已翻轉 H Short for horizontal - + H V Short for vertical - + V Mode - + 模式 Normal - + 正常 Mosaic - + 馬賽克 Enabled - + 已啟用 Priority - + 優先度 Tile - + 圖塊 @@ -935,12 +935,12 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 Game Overrides - + 遊戲替代 Game Boy Advance - + Game Boy Advance @@ -948,122 +948,122 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 Autodetect - + 自動偵測 Realtime clock - + 實時計時器 Gyroscope - + 陀螺儀 Tilt - + 傾斜 Light sensor - + 光感測器 Rumble - + 震動 Save type - + 存檔類型 None - + SRAM - + SRAM Flash 512kb - + Flash 512kb Flash 1Mb - + Flash 1Mb EEPROM 8kB - + EEPROM 8kB EEPROM 512 bytes - + EEPROM 512 bytes SRAM 64kB (bootlegs only) - + SRAM 64kB (僅用於盜版卡帶) Idle loop - + 閒置迴圈 Game Boy Player features - + Game Boy Player 特性 VBA bug compatibility mode - + VBA 錯誤相容性模式 Game Boy - + Game Boy Game Boy model - + Game Boy 型號 Memory bank controller - + 記憶庫控制器 Background Colors - + 背景顏色 Sprite Colors 1 - + 角色圖顏色 1 Sprite Colors 2 - + 角色圖顏色 2 Palette preset - + 預設調色盤 @@ -1071,62 +1071,62 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 Palette - + 調色盤 Background - + 背景 Objects - + 物件 Selection - + 選取 Red - + Green - + Blue - + 16-bit value - + 16 位元數值 Hex code - + 十六進位碼 Palette index - + 調色盤索引值 Export BG - + 匯出背景 Export OBJ - + 匯出 OBJ @@ -1134,27 +1134,27 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 Adjust placement - + 調整位置 All - + 全部 Offset - + 偏移量 X - + X Y - + Y @@ -1162,27 +1162,27 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 Game Boy Printer - + Game Boy Printer Hurry up! - + 快一點! Tear off - + 撕裂 Magnification - + 放大 Copy - + 複製 @@ -1190,7 +1190,7 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 2021 - + 2021 @@ -1199,41 +1199,46 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的註冊商標 An update to %1 is available. - + %1 有可用的更新。 + Do you want to download and install it now? You will need to restart the emulator when the download is complete. - + +您想要立即下載並安裝嗎?下載完成後,您需要重新啟動模擬器。 Auto-update is not available on this platform. If you wish to update you will need to do it manually. - + +此平台不支援自動更新。若您希望更新則必須手動進行。 Current version: %1 New version: %2 Download size: %3 - + 現在版本: %1 +新版本: %2 +下載的檔案大小: %3 Downloading update... - + 正在下載更新... Downloading failed. Please update manually. - + 下載失敗。請手動更新。 Downloading done. Press OK to restart %1 and install the update. - + 下載完成。按下 OK 重新啟動 %1 並安裝更新。 @@ -1241,22 +1246,22 @@ Download size: %3 Stable - + 穩定版本 Development - + 開發版本 Unknown - + 未知 (None) - + (無) @@ -1264,14 +1269,14 @@ Download size: %3 %0%1%2 - + %0%1%2 0x%0 (%1) - + 0x%0 (%1) @@ -1279,12 +1284,12 @@ Download size: %3 (untitled) - + (無標題) Failed to open cheats file: %1 - + 金手指開啟失敗: %1 @@ -1293,13 +1298,13 @@ Download size: %3 Autodetect (recommended) - + 自動偵測 (建議) Select cheats file - + 選擇金手指 @@ -1307,27 +1312,27 @@ Download size: %3 Failed to open save file: %1 - + 存檔開啟失敗: %1 Failed to open game file: %1 - + 遊戲開啟失敗: %1 Can't yank pack in unexpected platform! - + 無法在預料外的平台拔除卡帶! Failed to open snapshot file for reading: %1 - + 讀取快照失敗: %1 Failed to open snapshot file for writing: %1 - + 寫入快照失敗: %1 @@ -1335,17 +1340,17 @@ Download size: %3 Failed to open game file: %1 - + 遊戲開啟失敗: %1 Could not load game. Are you sure it's in the correct format? - + 無法讀取遊戲。您確定格式正確嗎? Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). - + 無法開啟存檔;遊戲內存檔無法更新。請確定存檔路徑的寫入不需要額外權限 (即 Windows 的 UAC)。 @@ -1353,52 +1358,52 @@ Download size: %3 Export frame - + 匯出畫格 Portable Network Graphics (*.png) - + 可攜式網路圖形 (*.png) None - + Background - + 背景 Window - + 視窗 Objwin - + Objwin Sprite - + 角色圖 Backdrop - + 背景 Frame - + 畫格 %1 %2 - + %1 %2 @@ -1406,7 +1411,7 @@ Download size: %3 Enable Discord Rich Presence - + 啟用 Discord Rich Presence @@ -1414,22 +1419,22 @@ Download size: %3 Clear Button - + 清除按鈕 Clear Analog - + 清除類比輸入 Refresh - + 重新整理 Set all - + 設定全部 @@ -1437,42 +1442,42 @@ Download size: %3 Server settings - + 伺服器設定 Local port - + 區域埠 Bind address - + 綁定位址 Break - + 中斷 Stop - + 停止 Start - + 開始 Crash - + 當機 Could not start GDB server - + 無法啟動 GDB 伺服器 @@ -1480,17 +1485,17 @@ Download size: %3 Failed to open output file: %1 - + 輸出檔案開啟失敗: %1 Select output file - + 選擇輸出的檔案 Graphics Interchange Format (*.gif);;WebP ( *.webp);;Animated Portable Network Graphics (*.png *.apng) - + 圖形互換格式 (*.gif);;WebP ( *.webp);;動態可攜式網路圖形 (*.png *.apng) @@ -1498,147 +1503,147 @@ Download size: %3 Background mode - + 背景模式 Mode 0: 4 tile layers - + 模式 0: 4 個圖塊層 Mode 1: 2 tile layers + 1 rotated/scaled tile layer - + 模式 1:2 個圖塊層 + 1 個可旋轉/縮放圖塊層 Mode 2: 2 rotated/scaled tile layers - + 模式 2:2 個可旋轉/縮放圖塊層 Mode 3: Full 15-bit bitmap - + 模式 3: 完全 15 位元點陣 Mode 4: Full 8-bit bitmap - + 模式 4: 完全 8 位元點陣 Mode 5: Small 15-bit bitmap - + 模式 5: 15 位元細小點陣 CGB Mode - + CGB 模式 Frame select - + 畫格選擇 Unlocked HBlank - + 解鎖的 HBlank Linear OBJ tile mapping - + 線性 OBJ 圖塊映射 Force blank screen - + 強制清空螢幕 Enable background 0 - + 啟用背景 0 Enable background 1 - + 啟用背景 1 Enable background 2 - + 啟用背景 2 Enable background 3 - + 啟用背景 3 Enable OBJ - + 啟用 OBJ Enable Window 0 - + 啟用視窗 0 Enable Window 1 - + 啟用視窗 1 Enable OBJ Window - + 啟用 OBJ 視窗 Swap green components - + 替換綠色組件 Currently in VBlank - + 目前 VBlank 值 Currently in HBlank - + 目前 HBlank 值 Currently in VCounter - + 目前 VCounter 值 Enable VBlank IRQ generation - + 允許 VBlank 生成中斷請求 (IRQ) Enable HBlank IRQ generation - + 允許 HBlank 生成中斷請求 (IRQ) Enable VCounter IRQ generation - + 允許 VCounter 生成中斷請求 (IRQ) VCounter scanline - + VCounter 掃描線 Current scanline - + 目前的掃描線 @@ -1646,7 +1651,7 @@ Download size: %3 Priority - + 優先度 @@ -1654,7 +1659,7 @@ Download size: %3 Tile data base (* 16kB) - + 圖塊資料基底 (* 16kB) @@ -1662,7 +1667,7 @@ Download size: %3 Enable mosaic - + 啟用馬賽克 @@ -1670,7 +1675,7 @@ Download size: %3 Enable 256-color - + 啟用 256 色 @@ -1678,7 +1683,7 @@ Download size: %3 Tile map base (* 2kB) - + 圖塊映射基底 (* 2kB) @@ -1686,13 +1691,13 @@ Download size: %3 Background dimensions - + 背景維度 Overflow wraps - + 溢位回繞 @@ -1702,7 +1707,7 @@ Download size: %3 Horizontal offset - + 水平偏移量 @@ -1712,7 +1717,7 @@ Download size: %3 Vertical offset - + 垂直偏移量 @@ -1728,7 +1733,7 @@ Download size: %3 Fractional part - + 分數部分 @@ -1740,7 +1745,7 @@ Download size: %3 Integer part - + 整數部分 @@ -1748,7 +1753,7 @@ Download size: %3 Integer part (low) - + 整數部分 (低位) @@ -1756,289 +1761,289 @@ Download size: %3 Integer part (high) - + 整數部分 (高位) End x - + 最終 x Start x - + 起始 x End y - + 最終 y Start y - + 起始 y Window 0 enable BG 0 - + 視窗 0 啟用 BG 0 Window 0 enable BG 1 - + 視窗 0 啟用 BG 1 Window 0 enable BG 2 - + 視窗 0 啟用 BG 2 Window 0 enable BG 3 - + 視窗 0 啟用 BG 3 Window 0 enable OBJ - + 視窗 0 啟用 OBJ Window 0 enable blend - + 視窗 0 啟用混合 Window 1 enable BG 0 - + 視窗 1 啟用 BG 0 Window 1 enable BG 1 - + 視窗 1 啟用 BG 1 Window 1 enable BG 2 - + 視窗 1 啟用 BG 2 Window 1 enable BG 3 - + 視窗 1 啟用 BG 3 Window 1 enable OBJ - + 視窗 1 啟用 OBJ Window 1 enable blend - + 視窗 1 啟用混合 Outside window enable BG 0 - + 外側視窗啟用 BG 0 Outside window enable BG 1 - + 外側視窗啟用 BG 1 Outside window enable BG 2 - + 外側視窗啟用 BG 2 Outside window enable BG 3 - + 外側視窗啟用 BG 3 Outside window enable OBJ - + 外側視窗啟用 OBJ Outside window enable blend - + 外側視窗啟用混合 OBJ window enable BG 0 - + OBJ 視窗啟用 BG 0 OBJ window enable BG 1 - + OBJ 視窗啟用 BG 1 OBJ window enable BG 2 - + OBJ 視窗啟用 BG 2 OBJ window enable BG 3 - + OBJ 視窗啟用 BG 3 OBJ window enable OBJ - + OBJ 視窗啟用 OBJ OBJ window enable blend - + OBJ 視窗啟用混合 Background mosaic size vertical - + 背景馬賽克垂直大小 Background mosaic size horizontal - + 背景馬賽克水平大小 Object mosaic size vertical - + 物件馬賽克垂直大小 Object mosaic size horizontal - + 物件馬賽克水平大小 BG 0 target 1 - + BG 0 目標 1 BG 1 target 1 - + BG 1 目標 1 BG 2 target 1 - + BG 2 目標 1 BG 3 target 1 - + BG 3 目標 1 OBJ target 1 - + OBJ 目標 1 Backdrop target 1 - + 背景目標 1 Blend mode - + 混合模式 Disabled - + 已停用 Additive blending - + 加算混合 Brighten - + 增亮 Darken - + 變暗 BG 0 target 2 - + BG 0 目標 2 BG 1 target 2 - + BG 1 目標 2 BG 2 target 2 - + BG 2 目標 2 BG 3 target 2 - + BG 3 目標 2 OBJ target 2 - + OBJ 目標 2 Backdrop target 2 - + 背景目標 2 Blend A (target 1) - + 混合 A (目標 1) Blend B (target 2) - + 混合 B (目標 2) Blend Y - + 混合 Y Sweep shifts - + 掃描位移 Sweep subtract - + 掃描相減 Sweep time (in 1/128s) - + 掃描時間 (1/128 秒) @@ -2050,7 +2055,7 @@ Download size: %3 Sound length - + 聲音長度 @@ -2058,7 +2063,7 @@ Download size: %3 Duty cycle - + 工作週期 @@ -2068,7 +2073,7 @@ Download size: %3 Envelope step time - + 包封步進時間 @@ -2078,7 +2083,7 @@ Download size: %3 Envelope increase - + 增加包封 @@ -2088,14 +2093,14 @@ Download size: %3 Initial volume - + 初始音量 Sound frequency - + 聲音頻率 @@ -2107,7 +2112,7 @@ Download size: %3 Timed - + 已排定時間 @@ -2119,56 +2124,56 @@ Download size: %3 Reset - + 重新啟動 Double-size wave table - + 雙倍尺寸波形表 Active wave table - + 使用中的波形表 Enable channel 3 - + 啟用頻道 3 Volume - + 音量 0% - + 0% 100% - + 100% 50% - + 50% 25% - + 25% @@ -2176,131 +2181,131 @@ Download size: %3 75% - + 75% Clock divider - + 時鐘分頻器 Register stages - + 暫存器階段 15 - + 15 7 - + 7 Shifter frequency - + 移位器頻率 PSG volume right - + 可程式化發聲器音量.右 PSG volume left - + 可程式化發聲器音量.左 Enable channel 1 right - + 啟用右側頻道 1 Enable channel 2 right - + 啟用右側頻道 2 Enable channel 3 right - + 啟用右側頻道 3 Enable channel 4 right - + 啟用右側頻道 4 Enable channel 1 left - + 啟用左側頻道 1 Enable channel 2 left - + 啟用左側頻道 2 Enable channel 3 left - + 啟用左側頻道 3 Enable channel 4 left - + 啟用左側頻道 4 PSG master volume - + 可程式化發聲器總音量 Loud channel A - + 揚聲頻道 A Loud channel B - + 揚聲頻道 B Enable channel A right - + 啟用右側頻道 A Enable channel A left - + 啟用左側頻道 A Channel A timer - + 頻道 A 計時器 0 - + 0 @@ -2313,72 +2318,72 @@ Download size: %3 1 - + 1 Channel A reset - + 頻道 A 重設 Enable channel B right - + 啟用右側頻道 B Enable channel B left - + 啟用左側頻道 B Channel B timer - + 頻道 B 計時器 Channel B reset - + 頻道 B 重設 Active channel 1 - + 使用中的頻道 1 Active channel 2 - + 使用中的頻道 2 Active channel 3 - + 使用中的頻道 3 Active channel 4 - + 使用中的頻道 4 Enable audio - + 啟用音訊 Bias - + 偏移 Resolution - + 解析度 @@ -2454,7 +2459,7 @@ Download size: %3 Sample - + 取樣 @@ -2466,7 +2471,7 @@ Download size: %3 Address (low) - + 位址 (低) @@ -2478,53 +2483,53 @@ Download size: %3 Address (high) - + 位址 (高) Sound frequency (low) - + 聲音頻率 (低) Sound frequency (high) - + 聲音頻率 (高) Source (high) - + 來源 (高) Source (low) - + 來源 (低) Destination (high) - + 目的地 (高) Destination (low) - + 目的地 (低) Green (low) - + 綠 (低) Green (high) - + 綠 (高) @@ -2532,7 +2537,7 @@ Download size: %3 Word count - + 字數 @@ -2540,7 +2545,7 @@ Download size: %3 Destination offset - + 目的地偏移量 @@ -2552,7 +2557,7 @@ Download size: %3 Increment - + 遞增 @@ -2564,7 +2569,7 @@ Download size: %3 Decrement - + 遞減 @@ -2576,7 +2581,7 @@ Download size: %3 Fixed - + 固定 @@ -2584,7 +2589,7 @@ Download size: %3 Increment and reload - + 遞增並重新讀取 @@ -2592,7 +2597,7 @@ Download size: %3 Source offset - + 來源偏移量 @@ -2600,7 +2605,7 @@ Download size: %3 Repeat - + 重複 @@ -2608,7 +2613,7 @@ Download size: %3 32-bit - + 32 位元 @@ -2616,7 +2621,7 @@ Download size: %3 Start timing - + 開始時間 @@ -2625,7 +2630,7 @@ Download size: %3 Immediate - + 立即 @@ -2637,7 +2642,7 @@ Download size: %3 VBlank - + VBlank @@ -2648,7 +2653,7 @@ Download size: %3 HBlank - + HBlank @@ -2661,7 +2666,7 @@ Download size: %3 IRQ - + 中斷請求 (IRQ) @@ -2676,24 +2681,24 @@ Download size: %3 Enable - + 啟用 Audio FIFO - + 音訊先進先出 Video Capture - + 視訊擷取 DRQ - + 資料請求 (DRQ) @@ -2705,7 +2710,7 @@ Download size: %3 Value - + @@ -2713,7 +2718,7 @@ Download size: %3 Scale - + 比例 @@ -2722,7 +2727,7 @@ Download size: %3 1/64 - + 1/64 @@ -2731,7 +2736,7 @@ Download size: %3 1/256 - + 1/256 @@ -2740,176 +2745,176 @@ Download size: %3 1/1024 - + 1/1024 Cascade - + 串聯 A - + A B - + B Select - + Select Start - + Start Right - + Left - + Up - + Down - + R - + R L - + L Condition - + Condition SC - + SC SD - + SD SI - + SI SO - + SO VCounter - + VCounter Timer 0 - + Timer 0 Timer 1 - + Timer 1 Timer 2 - + Timer 2 Timer 3 - + Timer 3 SIO - + SIO DMA 0 - + DMA 0 DMA 1 - + DMA 1 DMA 2 - + DMA 2 DMA 3 - + DMA 3 Keypad - + 鍵盤 Gamepak - + 遊戲卡帶 SRAM wait - + SRAM 等候 @@ -2918,7 +2923,7 @@ Download size: %3 4 - + 4 @@ -2926,7 +2931,7 @@ Download size: %3 3 - + 3 @@ -2935,7 +2940,7 @@ Download size: %3 2 - + 2 @@ -2944,412 +2949,412 @@ Download size: %3 8 - + 8 Cart 0 non-sequential - + 卡帶 0 (非按序) Cart 0 sequential - + 卡帶 0 (按序) Cart 1 non-sequential - + 卡帶 1 (非按序) Cart 1 sequential - + 卡帶 1 (按序) Cart 2 non-sequential - + 卡帶 2 (非按序) Cart 2 sequential - + 卡帶 2 (按序) PHI terminal - + PHI 終端 Disable - + 停用 4.19MHz - + 4.19MHz 8.38MHz - + 8.38MHz 16.78MHz - + 16.78MHz Gamepak prefetch - + 遊戲卡帶預讀 Enable IRQs - + 啟用中斷請求 (IRQ) Right/A - + 右/A Left/B - + 左/B Up/Select - + 上/Select Down/Start - + 下/Start Active D-pad - + 使用中的十字鍵 Active face buttons - + 使用中的正面按鈕 Internal clock - + 內部時鐘 32× clocking (CGB only) - + 32 倍時脈 (僅 CGB 適用) Transfer active - + 傳送使用狀態 Divider - + 除數 1/16 - + 1/16 LCD STAT - + LCD STAT Timer - + 計時器 Serial - + 串列 Joypad - + 搖桿 Volume right - + 右側音量 Output right - + 右側輸出 Volume left - + 左側音量 Output left - + 左側輸出 Background enable/priority - + 背景啟用/優先度 Enable sprites - + 啟用角色圖 Double-height sprites - + 兩倍高度角色圖 Background tile map - + 背景圖塊映射 0x9800 – 0x9BFF - + 0x9800 – 0x9BFF 0x9C00 – 0x9FFF - + 0x9C00 – 0x9FFF Background tile data - + 背景圖塊資料 0x8800 – 0x87FF - + 0x8800 – 0x87FF 0x8000 – 0x8FFF - + 0x8000 – 0x8FFF Enable window - + 啟用視窗 Window tile map - + 視窗圖塊映射 Enable LCD - + 啟用 LCD Mode - + 模式 0: HBlank - + 0: HBlank 1: VBlank - + 1: VBlank 2: OAM scan - + 2: 掃描 OAM 3: HDraw - + 3: HDraw In LYC - + 於 LYC Enable HBlank (mode 0) IRQ - + 啟用 HBlank (模式 0) 中斷請求 (IRQ) Enable VBlank (mode 1) IRQ - + 啟用 VBlank (模式 1) 中斷請求 (IRQ) Enable OAM (mode 2) IRQ - + 啟用 OAM (模式 2) 中斷請求 (IRQ) Enable LYC IRQ - + 啟用 LYC 中斷請求 (IRQ) Current Y coordinate - + 現在 Y 座標 Comparison Y coordinate - + 對照 Y 座標 Start upper byte - + 起始高位元組 Color 0 shade - + 色彩 0 陰影 Color 1 shade - + 色彩 1 陰影 Color 2 shade - + 色彩 2 陰影 Color 3 shade - + 色彩 3 陰影 Prepare to switch speed - + 準備切換速度 Double speed - + 雙倍速度 VRAM bank - + VRAM bank Length - + 長度 Timing - + 時間點 Write bit - + 寫入位元 Read bit - + 讀取位元 Unknown - + 未知 Current index - + 現在索引值 Auto-increment - + 自動遞增 Red - + Blue - + Sprite ordering - + 角色圖順序 OAM order - + OAM 順序 x coordinate sorting - + X 座標排序 WRAM bank - + WRAM bank @@ -3358,22 +3363,22 @@ Download size: %3 --- - + --- Super (L) - + Super (L) Super (R) - + Super (R) Menu - + 選單 @@ -3381,27 +3386,27 @@ Download size: %3 Load State - + 讀取即時存檔 Save State - + 儲存即時存檔 Empty - + Corrupted - + 損毀 Slot %1 - + 儲存槽 %1 @@ -3410,42 +3415,42 @@ Download size: %3 Default - + 預設 Fatal - + 嚴重 Error - + 錯誤 Warning - + 警告 Info - + 資訊 Debug - + 除錯 Stub - + 測試樁 Game Error - + 遊戲錯誤 @@ -3453,47 +3458,47 @@ Download size: %3 [%1] %2: %3 - + [%1] %2: %3 An error occurred - + 錯誤發生 DEBUG - + DEBUG STUB - + STUB INFO - + INFO WARN - + WARN ERROR - + ERROR FATAL - + FATAL GAME ERROR - + GAME ERROR @@ -3501,82 +3506,82 @@ Download size: %3 Priority - + 優先度 Map base - + 映射基底 Tile base - + 圖塊基底 Size - + 大小 Offset - + 偏移量 Xform - + Xform Map Addr. - + 映射位址 Mirror - + 鏡像 None - + Both - + 兩者 Horizontal - + 水平 Vertical - + 垂直 N/A - + N/A Export map - + 匯出映射 Portable Network Graphics (*.png) - + 可攜式網路圖形 (*.png) @@ -3584,12 +3589,12 @@ Download size: %3 Save memory region - + 儲存記憶體區域 Failed to open output file: %1 - + 輸出檔案開啟失敗: %1 @@ -3597,62 +3602,62 @@ Download size: %3 Copy selection - + 複製所選 Save selection - + 儲存所選 Paste - + 貼上 Load - + 讀取 All - + 全部 Load TBL - + 讀取 TBL Save selected memory - + 儲存選取的記憶體 Failed to open output file: %1 - + 輸出檔案開啟失敗: %1 Load memory - + 讀取記憶體 Failed to open input file: %1 - + 輸入檔案開啟失敗: %1 TBL - + TBL ISO-8859-1 - + ISO-8859-1 @@ -3660,22 +3665,22 @@ Download size: %3 (%0/%1×) - + (%0/%1×) (⅟%0×) - + (⅟%0×) (%0×) - + (%0×) %1 byte%2 - + %1 位元組%2 @@ -3684,12 +3689,12 @@ Download size: %3 0x%0 - + 0x%0 Off - + @@ -3701,43 +3706,43 @@ Download size: %3 --- - + --- Normal - + 正常 Trans - + Trans OBJWIN - + OBJWIN Invalid - + 無效 N/A - + N/A Export sprite - + 匯出角色圖 Portable Network Graphics (*.png) - + 可攜式網路圖形 (*.png) @@ -3745,17 +3750,17 @@ Download size: %3 Official MBCs - + 官方記憶庫控制器 (MBC) Licensed MBCs - + 已授權的記憶庫控制器 (MBC) Unlicensed MBCs - + 未授權的記憶庫控制器 (MBC) @@ -3763,12 +3768,12 @@ Download size: %3 #%0 - + #%0 0x%0 - + 0x%0 @@ -3776,22 +3781,22 @@ Download size: %3 0x%0 (%1) - + 0x%0 (%1) Export palette - + 匯出調色盤 Windows PAL (*.pal);;Adobe Color Table (*.act) - + Windows PAL (*.pal);;Adobe Color Table (*.act) Failed to open output palette file: %1 - + 輸出調色盤開啟失敗: %1 @@ -3803,18 +3808,18 @@ Download size: %3 (unknown) - + (未知) bytes - + 位元組 (no database present) - + (無資料庫存在) @@ -3822,12 +3827,12 @@ Download size: %3 Bug report archive - + 錯誤回報記錄 ZIP archive (*.zip) - + ZIP 壓縮檔 (*.zip) @@ -3835,62 +3840,62 @@ Download size: %3 Save games and save states (%1) - + 儲存遊戲並儲存即時存檔 (%1) Select save game or save state - + 選擇遊戲存檔或即時存檔 Save games (%1) - + 遊戲存檔 (%1) Select save game - + 選擇遊戲存檔 Conversion failed - + 轉檔失敗 Failed to convert the save game. This is probably a bug. - + 存檔轉換失敗。這可能是個 bug。 No file selected - + 未選取檔案 Could not open file - + 無法開啟檔案 No valid formats found - + 找不到有效的格式 Please select a valid input file - + 請選擇有效的輸入檔 No valid conversions found - + 找不到有效的轉換方法 Cannot convert save games between platforms - + 無法在不同平台間轉換存檔 @@ -3899,101 +3904,101 @@ Download size: %3 Qt Multimedia - + Qt 多媒體 SDL - + SDL Software (Qt) - + 軟體 (Qt) OpenGL - + OpenGL OpenGL (force version 1.x) - + OpenGL (強制使用 1.x 版) None - + None (Still Image) - + 無 (靜態影像) Keyboard - + 鍵盤 Controllers - + 控制器 Shortcuts - + 快捷鍵 Shaders - + 著色器 Select BIOS - + 選擇 BIOS Select directory - + 選擇資料夾 (%1×%2) - + (%1×%2) Never - + 永不 Just now - + 剛剛 Less than an hour ago - + 一小時內 %n hour(s) ago - - + + %n 小時前 %n day(s) ago - - + + %n 日前 @@ -4002,32 +4007,32 @@ Download size: %3 No shader active - + 無使用中的著色器 Load shader - + 讀取著色器 No shader loaded - + 未讀取著色器 by %1 - + 由 %1 Preprocessing - + 預處理 Pass %1 - + 通道 %1 @@ -4035,17 +4040,17 @@ Download size: %3 Action - + 動作 Keyboard - + 鍵盤 Gamepad - + 遊戲手把 @@ -4053,18 +4058,18 @@ Download size: %3 Export tiles - + 匯出圖塊 Portable Network Graphics (*.png) - + 可攜式網路圖形 (*.png) Export tile - + 匯出圖塊 @@ -4072,17 +4077,17 @@ Download size: %3 Failed to open output video file: %1 - + 輸出影片開啟失敗: %1 Native (%0x%1) - + 原始畫質 (%0x%1) Select output file - + 選擇輸出的檔案 @@ -4090,759 +4095,761 @@ Download size: %3 Game Boy Advance ROMs (%1) - + Game Boy Advance ROMs (%1) Game Boy ROMs (%1) - + Game Boy ROMs (%1) All ROMs (%1) - + 所有 ROMs (%1) %1 Video Logs (*.mvl) - + %1 影片記錄檔 (*.mvl) Archives (%1) - + 壓縮檔 (%1) Select ROM - + 選擇 ROM Select folder - + 選擇資料夾 Select save - + 選擇存檔 Select patch - + 選擇修補檔 Patches (*.ips *.ups *.bps) - + 修補檔 (*.ips *.ups *.bps) Select e-Reader dotcode - + 選擇 e-Reader 點碼 e-Reader card (*.raw *.bin *.bmp) - + e-Reader 卡片 (*.raw *.bin *.bmp) Select image - + 選擇圖片 Image file (*.png *.gif *.jpg *.jpeg);;All files (*) - + 圖檔 (*.png *.gif *.jpg *.jpeg);;所有檔案 (*) GameShark saves (*.sps *.xps) - + GameShark 存檔 (*.sps *.xps) Select video log - + 選擇影片記錄檔 Video logs (*.mvl) - + 影片記錄檔 (*.mvl) Crash - + 當機 The game has crashed with the following error: %1 - + 遊戲因以下錯誤當機: + +%1 Couldn't Start - + 無法啟動 Could not start game. - + 無法啟動遊戲。 Unimplemented BIOS call - + 未實裝的 BIOS 呼叫 This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. - + 此遊戲使用了未實裝的 BIOS 呼叫。為了獲得最佳體驗,請使用官方 BIOS。 Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. - + 建立適當的顯示裝置失敗,回歸到軟體顯示。遊戲可能會跑得很慢,尤其在使用大螢幕的時候。 Really make portable? - + 這真的可攜? This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? - + 模擬器會從執行檔所在的資料夾讀取設定檔。確定要繼續嗎? Restart needed - + 需要重新啟動 Some changes will not take effect until the emulator is restarted. - + 有些變更在模擬器重新啟動前不會生效。 - Player %1 of %2 - + - Player %1 of %2 %1 - %2 - + %1 - %2 %1 - %2 - %3 - + %1 - %2 - %3 %1 - %2 (%3 fps) - %4 - + %1 - %2 (%3 fps) - %4 &File - + 檔案 (&F) Load &ROM... - + 讀取 ROM (&R) ... Load ROM in archive... - + 從壓縮檔讀取ROM... Add folder to library... - + 加入資料夾至收藏... Save games (%1) - + 遊戲存檔 (%1) Select save game - + 選擇存檔 mGBA save state files (%1) - + mGBA 即時存檔 (%1) Select save state - + 選擇即時存檔 Select e-Reader card images - + 選取 e-Reader 卡片圖檔 Image file (*.png *.jpg *.jpeg) - + 圖檔 (*.png *.jpg *.jpeg) Conversion finished - + 轉檔完成 %1 of %2 e-Reader cards converted successfully. - + %1 張 e-Reader 卡片轉換成功。 (共 %2 張) Load alternate save game... - + 讀取另一份存檔... Load temporary save game... - + 讀取暫時存檔... Load &patch... - + 讀取並修補 (&P) ... Boot BIOS - + 啟動 BIOS Replace ROM... - + 取代 ROM... Scan e-Reader dotcodes... - + 掃描 e-Reader 點碼 Convert e-Reader card image to raw... - + 轉換 e-Reader 卡片圖檔為原始資料 ROM &info... - + ROM 資訊 (&I) Recent - + 最近使用 Make portable - + 可攜化 &Load state - + 讀取即時存檔 (&L) Load state file... - + 讀取即時存檔... &Save state - + 儲存即時存檔 (&S) Save state file... - + 儲存即時存檔... Quick load - + 快速讀取 Quick save - + 快速儲存 Load recent - + 讀取最近存檔 Save recent - + 儲存最近存檔 Undo load state - + 取消讀取即時存檔 Undo save state - + 取消儲存即時存檔 State &%1 - + 即時存檔 (&%1) Load camera image... - + 讀取相機影像... Convert save game... - + 轉換存檔... GameShark saves (*.gsv *.sps *.xps) - + GameShark 存檔 (*.gsv *.sps *.xps) Import GameShark Save... - + 匯入 GameShark 存檔 Export GameShark Save... - + 匯出 GameShark 存檔 New multiplayer window - + 新增多人遊戲視窗 Connect to Dolphin... - + 連接 Dolphin... Report bug... - + 回報錯誤... About... - + 關於... E&xit - + 離開 (&E) &Emulation - + 模擬 (&E) &Reset - + 重新啟動 (&R) Sh&utdown - + 關機 (&S) Yank game pak - + 拔除遊戲卡帶 &Pause - + 暫停 (&P) &Next frame - + 下一畫格 (&N) Fast forward (held) - + 快轉 (長按) &Fast forward - + 快轉 (&F) Fast forward speed - + 快轉速率 Unbounded - + 無限制 %0x - + %0x Rewind (held) - + 倒轉 (長按) Re&wind - + 倒轉 (&R) Step backwards - + 逐格倒轉 Sync to &video - + 與畫面同步 (&V) Sync to &audio - + 與音訊同步 (&A) Solar sensor - + 日光感測器 Increase solar level - + 增加日光等級 Decrease solar level - + 降低日光等級 Brightest solar level - + 最大日光等級 Darkest solar level - + 最小日光等級 Brightness %1 - + 亮度 %1 Game Boy Printer... - + Game Boy Printer... BattleChip Gate... - + 戰鬥晶片閘... Audio/&Video - + 音訊/視訊 (&V) Frame size - + 畫格大小 %1× - + %1× Toggle fullscreen - + 切換全螢幕 Lock aspect ratio - + 鎖定長寬比 Force integer scaling - + 強制整數倍放大 Interframe blending - + 畫格間混合 Bilinear filtering - + 雙線性過濾 Frame&skip - + 跳過畫格 (&S) Mute - + 靜音 FPS target - + FPS 目標 Native (59.7275) - + 原生 (59.7275) Take &screenshot - + 螢幕截圖 (&S) F12 - + F12 Record A/V... - + 錄製音訊/影像... Record GIF/WebP/APNG... - + 錄製 GIF/WebP/APNG... Video layers - + 視訊圖層 Audio channels - + 音訊通道 Adjust layer placement... - + 調整圖層位置... &Tools - + 工具 (&T) View &logs... - + 查看記錄檔 (&L) Game &overrides... - + 遊戲替代 (&O) Game Pak sensors... - + 遊戲卡帶感測器... &Cheats... - + 金手指 (&C) Settings... - + 設定... Open debugger console... - + 打開除錯器控制台... Start &GDB server... - + 啟動 GDB 伺服器 (&G) View &palette... - + 檢視調色盤 (&P) View &sprites... - + 檢視角色圖 (&S) View &tiles... - + 檢視圖塊 (&T) View &map... - + 檢視映射 (&M) &Frame inspector... - + 畫格檢視器 (&F) View memory... - + 檢視記憶體... Search memory... - + 搜尋記憶體... View &I/O registers... - + 檢視 I/O 暫存器 (&I) Record debug video log... - + 錄製除錯影片記錄檔。 Stop debug video log - + 停止除錯影片記錄檔 Exit fullscreen - + 離開全螢幕 GameShark Button (held) - + Gameshark 按鈕 (長按) Autofire - + 自動連射 Autofire A - + 自動連射 A Autofire B - + 自動連射 B Autofire L - + 自動連射 L Autofire R - + 自動連射 R Autofire Start - + 自動連射 Start Autofire Select - + 自動連射 Select Autofire Up - + 自動連射 上 Autofire Right - + 自動連射 右 Autofire Down - + 自動連射 下 Autofire Left - + 自動連射 左 Clear - + 清除 @@ -4850,32 +4857,32 @@ Download size: %3 %1 byte - + %1 位元組 %1 kiB - + %1 kiB %1 MiB - + %1 MiB GBA - + GBA GB - + GB ? - + ? @@ -4883,22 +4890,22 @@ Download size: %3 Shift - + Shift Control - + Control Alt - + Alt Meta - + Meta @@ -4906,32 +4913,32 @@ Download size: %3 ROM Info - + ROM 資訊 Game name: - + 遊戲名稱: Internal name: - + 內部名稱: Game ID: - + 遊戲 ID File size: - + 檔案大小: CRC32: - + CRC32: @@ -4939,37 +4946,37 @@ Download size: %3 Generate Bug Report - + 產生錯誤報告 <html><head/><body><p>To file a bug report, please first generate a report file to attach to the bug report you're about to file. It is recommended that you include the save files, as these often help with debugging issues. This will collect some information about the version of {projectName} you're running, your configuration, your computer, and the game you currently have open (if any). Once this collection is completed you can review all of the information gathered below and save it to a zip file. The collection will automatically attempt to redact any personal information, such as your username if it's in any of the paths gathered, but just in case you can edit it afterwards. After you have generated and saved it, please click the button below or go to <a href="https://mgba.io/i/"><span style=" text-decoration: underline; color:#2980b9;">mgba.io/i</span></a> to file the bug report on GitHub. Make sure to attach the report you generated!</p></body></html> - + <html><head/><body><p>若要提出錯誤報告,請先產生一份報告檔,再將報告檔附加到要提出的錯誤報告中。建議您附上存檔,因為存檔通常對除錯很有幫助。錯誤報告會收集以下資料:正在執行的 {projectName} 版本、設定、電腦資訊、目前開啟的遊戲 (若有的話)。在資料收集完成後,您可以檢查所有收集的資料,並儲存成 zip 壓縮檔。錯誤報告會嘗試自動塗銷所有個人資訊,例如路徑包含的使用者名稱。以防萬一您還是可以在這之後自行編輯。生成並儲存報告之後,請點擊下方按鈕,或前往 <a href="https://mgba.io/i/"><span style=" text-decoration: underline; color:#2980b9;">mgba.io/i</span></a> 以在 GitHub 上提出報告。請確定附上產生的報告!</p></body></html> Generate report - + 產生報告 Save - + 儲存 Open issue list in browser - + 在瀏覽器中打開問題清單 Include save file - + 附加存檔 Create and include savestate - + 建立並加入即時存檔 @@ -4977,113 +4984,113 @@ Download size: %3 Convert/Extract Save Game - + 轉換/擷取存檔 Input file - + 輸入檔案 Browse - + 瀏覽 Output file - + 輸出檔案 %1 %2 save game - + %1 %2 遊戲存檔 little endian - + 位元組由小到大 big endian - + 位元組由大到小 SRAM - + SRAM %1 flash - + %1 flash %1 EEPROM - + %1 EEPROM %1 SRAM + RTC - + %1 SRAM + RTC %1 SRAM - + %1 SRAM packed MBC2 - + 包裝 MBC2 unpacked MBC2 - + 未包裝 MBC2 MBC6 flash - + MBC6 flash MBC6 combined SRAM + flash - + MBC6 合併 SRAM + flash MBC6 SRAM - + MBC6 SRAM TAMA5 - + TAMA5 %1 (%2) - + %1 (%2) %1 save state with embedded %2 save game - + %1 即時存檔與嵌入的 %2 遊戲存檔 %1 SharkPort %2 save game - + %1 SharkPort %2 存檔 %1 GameShark Advance SP %2 save game - + %1 GameShark Advance SP %2 存檔 @@ -5091,74 +5098,74 @@ Download size: %3 Sensors - + 感測器 Realtime clock - + 實時計時器 Fixed time - + 固定時間 System time - + 系統時間 Start time at - + 開始時間於 Now - + 現在 MM/dd/yy hh:mm:ss AP - + yy/MM/dd hh:mm:ss AP Light sensor - + 光感測器 Brightness - + 亮度 Tilt sensor - + 傾斜感測器 Set Y - + 設定 Y Set X - + 設定 X Gyroscope - + 陀螺儀 Sensitivity - + 靈敏度 @@ -5166,139 +5173,139 @@ Download size: %3 Settings - + 設定 Audio/Video - + 音訊/視訊 Interface - + 介面 Update - + 更新 Emulation - + 模擬 Enhancements - + 增強 BIOS - + BIOS Paths - + 路徑 Logging - + 記錄 Game Boy - + Game Boy Audio driver: - + 音訊驅動: Audio buffer: - + 音訊緩衝: 1536 - + 1536 512 - + 512 768 - + 768 1024 - + 1024 2048 - + 2048 3072 - + 3072 4096 - + 4096 samples - + 取樣 Sample rate: - + 取樣率: 44100 - + 44100 22050 - + 22050 32000 - + 32000 48000 - + 48000 Hz - + Hz Volume: - + 音量: @@ -5306,453 +5313,453 @@ Download size: %3 Mute - + 靜音 Fast forward volume: - + 快轉音量: Audio in multiplayer: - + 多人遊戲音訊: All windows - + 所有視窗 Player 1 window only - + 僅玩家 1 視窗 Currently active player window - + 現在使用中的玩家視窗 Display driver: - + 顯示驅動: Frameskip: - + 跳過畫格: Skip every - + 每隔 N 畫格跳過 frames - + 畫格 FPS target: - + FPS 目標: frames per second - + 每秒畫格數 (FPS) Sync: - + 同步: Video - + 視訊 Audio - + 音訊 Lock aspect ratio - + 鎖定長寬比 Force integer scaling - + 強制整數倍放大 Bilinear filtering - + 雙線性過濾 Pause - + 暫停 When inactive: - + 閒置時: When minimized: - + 最小化時: Current channel: - + 目前頻道: Current version: - + 現在版本: Update channel: - + 更新頻道: Available version: - + 可用版本: (Unknown) - + (未知) Last checked: - + 上次檢查時間: Automatically check on start - + 啟動時自動檢查 Check now - + 現在檢查 Default color palette only - + 僅使用預設調色盤 SGB color palette if available - + 若可用,使用SGB調色盤 GBC color palette if available - + 若可用,使用GBC調色盤 SGB (preferred) or GBC color palette if available - + 若可用,使用 SGB (優先) 或 GBC調色盤 Game Boy Camera - + Game Boy Camera Driver: - + 驅動: Source: - + 來源: Native (59.7275) - + 原生 (59.7275) Interframe blending - + 畫格間混合 Language - + 語言 Library: - + 資料庫: List view - + 清單顯示 Tree view - + 樹狀顯示 Show when no game open - + 無遊戲啟動時顯示 Clear cache - + 清除快取 Allow opposing input directions - + 允許相反方向輸入 Suspend screensaver - + 暫停螢幕保護程式 Dynamically update window title - + 動態更新視窗標題 Show filename instead of ROM name in title bar - + 標題顯示檔案名稱而非 ROM 名稱 Show OSD messages - + 顯示 OSD 訊息 Enable Discord Rich Presence - + 啟用 Discord Rich Presence Automatically save state - + 自動儲存即時存檔 Automatically load state - + 自動讀取即時存檔 Automatically save cheats - + 自動儲存金手指 Automatically load cheats - + 自動讀取金手指 Show FPS in title bar - + 標題顯示 FPS Fast forward speed: - + 快轉速率: Unbounded - + 無限制 Fast forward (held) speed: - + 快轉 (長按) 速率: Autofire interval: - + 自動連射間隔: Enable rewind - + 啟用倒轉 Rewind history: - + 倒轉歷史: Idle loops: - + 閒置迴圈: Run all - + 執行全部 Remove known - + 移除已知 Detect and remove - + 偵測並移除 Preload entire ROM into memory - + 預先讀取整個 ROM 至記憶體中 Save state extra data: - + 儲存即時存檔額外資料: Save game - + 儲存遊戲 Load state extra data: - + 讀取即時存檔額外資料: Models - + 模型 GB only: - + 僅 GB: SGB compatible: - + SGB 相容: GBC only: - + 僅 GBC: GBC compatible: - + GBC 相容: SGB and GBC compatible: - + SGB & GBC 相容: Game Boy palette - + Game Boy 調色盤 Preset: - + 預設: Screenshot - + 螢幕截圖 Cheat codes - + 金手指代碼 Enable Game Boy Player features by default - + 預設啟用 Game Boy Player 功能 Enable VBA bug compatibility in ROM hacks - + ROM 修改啟用 VBA 錯誤相容性 Video renderer: - + 渲染器: Software - + 軟體 OpenGL - + OpenGL OpenGL enhancements - + OpenGL 增強 High-resolution scale: - + 高畫質比例: (240×160) - + (240×160) XQ GBA audio (experimental) - + XQ GBA 音訊 (測試版) GB BIOS file: - + GB BIOS 檔案: @@ -5765,37 +5772,37 @@ Download size: %3 Browse - + 瀏覽 Use BIOS file if found - + 若存在,使用 BIOS 檔案 Skip BIOS intro - + 跳過 BIOS 開頭畫面 GBA BIOS file: - + GBA BIOS 檔案: GBC BIOS file: - + GBC BIOS 檔案: SGB BIOS file: - + SGB BIOS 檔案: Save games - + 遊戲存檔 @@ -5804,62 +5811,62 @@ Download size: %3 Same directory as the ROM - + 與 ROM 同目錄 Save states - + 即時存檔 Screenshots - + 螢幕截圖 Patches - + 修補檔 Cheats - + 金手指 Log to file - + 記錄到檔案 Log to console - + 記錄到控制台 Select Log File - + 選擇記錄檔 Default BG colors: - + 預設 BG 顏色組: Default sprite colors 1: - + 預設角色圖顏色組 1 Default sprite colors 2: - + 預設角色圖顏色組 2 Super Game Boy borders - + Super Game Boy 邊界 @@ -5867,37 +5874,37 @@ Download size: %3 Shaders - + 著色器 Active Shader: - + 使用中的著色器: Name - + 名稱 Author - + 作者 Description - + 描述 Unload Shader - + 卸除著色器 Load New Shader - + 讀取新著色器 @@ -5905,22 +5912,22 @@ Download size: %3 Edit Shortcuts - + 編輯快捷鍵 Keyboard - + 鍵盤 Gamepad - + 遊戲手把 Clear - + 清除 @@ -5928,47 +5935,47 @@ Download size: %3 Tiles - + 圖塊 Export Selected - + 匯出所選 Export All - + 匯出全部 256 colors - + 256 色 Magnification - + 放大 Tiles per row - + 每行圖塊數 Fit to window - + 貼合視窗 Copy Selected - + 複製選取 Copy All - + 複製全部 @@ -5976,200 +5983,200 @@ Download size: %3 Record Video - + 錄影 Start - + Start Stop - + 停止 Select File - + 選擇檔案 Presets - + 預設 High &Quality - + 高畫質 (&Q) &YouTube - + YouTube (&Y) WebM - + WebM MP4 - + MP4 &Lossless - + 無損品質 (&L) 4K - + 4K &1080p - + 1080p (&1) &720p - + 720p (&7) &480p - + 480p (&4) &Native - + 原始畫質 (&N) Format - + 格式 MKV - + MKV AVI - + AVI HEVC - + HEVC HEVC (NVENC) - + HEVC (NVENC) VP8 - + VP8 VP9 - + VP9 FFV1 - + FFV1 None - + FLAC - + FLAC Opus - + Opus Vorbis - + Vorbis MP3 - + MP3 AAC - + AAC Uncompressed - + 未壓縮 Bitrate (kbps) - + 位元率 (kbps) ABR - + ABR H.264 - + H.264 H.264 (NVENC) - + H.264 (NVENC) VBR - + VBR CRF - + CRF Dimensions - + 維度 Lock aspect ratio - + 鎖定長寬比 Show advanced - + 顯示進階選項 From edc9cfef8dc869eeb812d5837e24a9f43efe584f Mon Sep 17 00:00:00 2001 From: Hexaae Date: Sat, 30 Nov 2024 11:47:20 +0000 Subject: [PATCH 108/114] Qt: Update translation (Italian) Translation: mGBA/Qt Translate-URL: https://hosted.weblate.org/projects/mgba/mgba-qt/it/ --- src/platform/qt/ts/mgba-it.ts | 68 +++++++++++++++++------------------ 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/platform/qt/ts/mgba-it.ts b/src/platform/qt/ts/mgba-it.ts index 7338f41f6..aa7093703 100644 --- a/src/platform/qt/ts/mgba-it.ts +++ b/src/platform/qt/ts/mgba-it.ts @@ -21,7 +21,7 @@ %1 Video Logs (*.mvl) - %1 log Video (*.mvl) + %1 registro Video (*.mvl) @@ -482,7 +482,7 @@ Dimensione del download: %3 Failed to create an OpenGL 3 context, trying old-style... - + Impossibile creare un contesto OpenGL 3, provo con il vecchio stile... @@ -3181,7 +3181,7 @@ Dimensione del download: %3 Stub - Matrice + Stub @@ -3242,7 +3242,7 @@ Dimensione del download: %3 Logs - Log + Registri @@ -3257,7 +3257,7 @@ Dimensione del download: %3 Stub - Matrice + Stub @@ -3408,63 +3408,63 @@ Dimensione del download: %3 Memory access logging - + Registrazione accessi alla memoria Log file - + File di registro Browse - Sfoglia + Sfoglia Log additional information (uses 3× space) - + Registra informazioni aggiuntive (utilizza 3× spazio) Load existing file if present - + Carica file esistente se presente Regions - + Regioni Export ROM snapshot - + Esporta istantanea ROM Start - + Avvia Stop - Ferma + Ferma Failed to open memory log file - + Impossibile aprire il file di registro della memoria Select access log file - + Seleziona il file di registro di accesso Memory access logs (*.mal) - + Registri degli accessi alla memoria (*.mal) @@ -4038,7 +4038,7 @@ Dimensione del download: %3 Realtime clock - Clock in tempo reale + Orologio in tempo reale @@ -4372,12 +4372,12 @@ Dimensione del download: %3 Maker Code: - + Codice fabbricante: Revision: - + Revisione: @@ -4660,7 +4660,7 @@ Dimensione del download: %3 &Load most recent - + &Carica il più recente @@ -4698,7 +4698,7 @@ Dimensione del download: %3 Realtime clock - Clock in tempo reale + Orologio in tempo reale @@ -4713,7 +4713,7 @@ Dimensione del download: %3 Start time at - Avvia tempo a + Avvia ora alle @@ -4932,7 +4932,7 @@ Dimensione del download: %3 Logging - Log + Registrazione @@ -5387,17 +5387,17 @@ Dimensione del download: %3 Log to file - Registro log in file + Registro salvato su file Log to console - Registro log in console + Registro mostrato in console Select Log File - Seleziona file log + Seleziona file registro @@ -6026,12 +6026,12 @@ Dimensione del download: %3 Select video log - Seleziona log video + Seleziona registro video Video logs (*.mvl) - Log video (*.mvl) + Registro video (*.mvl) @@ -6376,7 +6376,7 @@ Dimensione del download: %3 &Lock frame size - + &Blocca dimensione cornice @@ -6436,7 +6436,7 @@ Dimensione del download: %3 Game &overrides... - Valore specifico per il gioco... + Sovrascrivi funzioni del gioco... @@ -6657,17 +6657,17 @@ Dimensione del download: %3 Log memory &accesses... - + Registra memoria &accessi... Record debug video log... - Registra video log di debug... + Salva registro video di debug... Stop debug video log - Ferma video log di debug + Ferma registro video di debug From 6dfef6857f86cadccd0b6754e005419362f40ef9 Mon Sep 17 00:00:00 2001 From: ibizastweet Date: Wed, 4 Dec 2024 22:10:10 +0000 Subject: [PATCH 109/114] Qt: Update translation (French) Translation: mGBA/Qt Translate-URL: https://hosted.weblate.org/projects/mgba/mgba-qt/fr/ --- src/platform/qt/ts/mgba-fr.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/qt/ts/mgba-fr.ts b/src/platform/qt/ts/mgba-fr.ts index 27eb207a9..32db45014 100644 --- a/src/platform/qt/ts/mgba-fr.ts +++ b/src/platform/qt/ts/mgba-fr.ts @@ -557,7 +557,7 @@ Taille du téléchargement : %3 Banner - + Bannière From d4fea610bf7403f64cb8a0e86437f56b8d71bd0c Mon Sep 17 00:00:00 2001 From: Daniel Nylander Date: Sat, 7 Dec 2024 07:57:11 +0000 Subject: [PATCH 110/114] Qt: Update translation (Swedish) Translation: mGBA/Qt Translate-URL: https://hosted.weblate.org/projects/mgba/mgba-qt/sv/ --- src/platform/qt/ts/mgba-sv.ts | 2008 +++++++++++++++++---------------- 1 file changed, 1005 insertions(+), 1003 deletions(-) diff --git a/src/platform/qt/ts/mgba-sv.ts b/src/platform/qt/ts/mgba-sv.ts index 4017ea489..6f6d30651 100644 --- a/src/platform/qt/ts/mgba-sv.ts +++ b/src/platform/qt/ts/mgba-sv.ts @@ -6,22 +6,22 @@ Game Boy Advance ROMs (%1) - + Game Boy Advance ROM-filer (%1) Game Boy ROMs (%1) - + Game Boy ROM-filer (%1) All ROMs (%1) - + Alla ROM-filer (%1) %1 Video Logs (*.mvl) - + %1 Videologgar (*.mvl) @@ -29,12 +29,12 @@ About - Om mGBA + Om <a href="http://mgba.io/">Website</a> • <a href="https://forums.mgba.io/">Forums / Support</a> • <a href="https://patreon.com/mgba">Donate</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">Source</a> - <a href="http://mgba.io/">Hemsida</a> • <a href="https://forums.mgba.io/">Forum / Hjälp</a> • <a href="https://patreon.com/mgba">Stödja mGBA</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">Källkod</a> + <a href="http://mgba.io/">Webbsida</a> • <a href="https://forums.mgba.io/">Forum / Support</a> • <a href="https://patreon.com/mgba">Donera</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">Källkod</a> @@ -56,7 +56,7 @@ Game Boy Advance är ett registrerat varumärke av Nintendo Co., Ltd. {projectName} is an open-source Game Boy Advance emulator - {projectName} är en emulator för Game Boy Advance, byggt på öppen källkod + {projectName} är en emulator för Game Boy Advance som är byggt på öppen källkod @@ -64,13 +64,13 @@ Game Boy Advance är ett registrerat varumärke av Nintendo Co., Ltd. An update is available - En uppdatering är tillgänglig + En uppdatering finns tillgänglig An update to %1 is available. - En uppdatering till %1 är tillgänglig. + En uppdatering till %1 finns tillgänglig. @@ -78,38 +78,38 @@ Game Boy Advance är ett registrerat varumärke av Nintendo Co., Ltd. Do you want to download and install it now? You will need to restart the emulator when the download is complete. -Vill du ladda ner och installera det nu? Programmet måste startas om när nedladdningen är klar. +Vill du hämta ner och installera det nu? Programmet måste startas om när hämtningen är klar. Auto-update is not available on this platform. If you wish to update you will need to do it manually. -Det finns ej automatiska uppdateringar för detta operativ system. Önskar du uppdatera programmet måste du göra det manuellt. +Automatiska uppdateringar är inte tillgängligt för denna plattform. Vill du uppdatera så måste du göra det manuellt. Current version: %1 New version: %2 Download size: %3 - Nuvarande version: %1 -Nästa version: %2 -Nedladdningsstorlek: %3 + Aktuell version: %1 +Ny version: %2 +Hämtningsstorlek: %3 Downloading update... - Uppdatering laddas ner... + Uppdateringen hämtas... Downloading failed. Please update manually. - Nedladdningen misslyckades. Uppdateringen måste installeras manuellt. + Hämtningen misslyckades. Uppdatera manuellt. Downloading done. Press OK to restart %1 and install the update. - Nedladdning klar. Välj OK för att starta om %1 och installera uppdateringen. + Hämtningen är klar. Välj OK för att starta om %1 och installera uppdateringen. @@ -117,7 +117,7 @@ Nedladdningsstorlek: %3 Stable - Standard + Stabil @@ -140,12 +140,12 @@ Nedladdningsstorlek: %3 Open in archive... - Öppna i arkiveringsprogram... + Öppna i arkiv... Loading... - Laddas... + Läser in... @@ -232,7 +232,7 @@ Nedladdningsstorlek: %3 Chip name - Chip namn + Chipnamn @@ -247,7 +247,7 @@ Nedladdningsstorlek: %3 Load - Ladda + Läs in @@ -262,7 +262,7 @@ Nedladdningsstorlek: %3 Gate type - Gate typ + Gate-typ @@ -277,7 +277,7 @@ Nedladdningsstorlek: %3 Update Chip data - Uppdatera Chip-data + Uppdatera Chip-data @@ -321,7 +321,7 @@ Nedladdningsstorlek: %3 Failed to open cheats file: %1 - Misslyckades att öppna cheats-filen: %1 + Misslyckades att öppna fuskfilen: %1 @@ -329,7 +329,7 @@ Nedladdningsstorlek: %3 Cheats - Cheats + Fusk @@ -339,49 +339,49 @@ Nedladdningsstorlek: %3 Remove - Ta bort + Ta bort Add Lines - + Lägg till rader Code type - + Kodtyp Save - Spara + Spara Load - Ladda + Läs in Enter codes here... - + Ange koder här... Autodetect (recommended) - + Identifiera automatiskt (rekommenderas) Select cheats file - + Välj fuskfil Some cheats could not be added. Please ensure they're formatted correctly and/or try other cheat types. - + Vissa fusk kunde inte läggas till. Försäkra dig om att de är formaterade korrekt och/eller prova andra fusktyper. @@ -389,7 +389,7 @@ Nedladdningsstorlek: %3 Reset r%1-%2 %3 - + Starta om r%1-%2 %3 @@ -400,22 +400,22 @@ Nedladdningsstorlek: %3 Reset the game? - + Starta om spelet? Most games will require a reset to load the new save. Do you want to reset now? - + De flesta spel kommer att kräva en omstart för att läsa in ny sparning. Vill du starta om nu? Failed to open save file: %1 - + Misslyckades med att öppna sparad fil: %1 Failed to open game file: %1 - + Misslyckades med att öppna spelfil: %1 @@ -425,12 +425,12 @@ Nedladdningsstorlek: %3 Failed to open snapshot file for reading: %1 - + Misslyckades med att öppna ögonblicksfil för läsning: %1 Failed to open snapshot file for writing: %1 - + Misslyckades med att öppna ögonblicksfil för skrivning: %1 @@ -438,17 +438,17 @@ Nedladdningsstorlek: %3 Failed to open game file: %1 - + Misslyckades med att öppna spelfil: %1 Could not load game. Are you sure it's in the correct format? - + Kunde inte läsa in spelet. Är du säker på att det är i korrekt format? Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). - + Misslyckades med att öppna sparad fil; sparningar i spel kan inte uppdateras. Försäkra dig om att sparkatalogen är skrivbar utan att ytterligare behörighet krävs (t.ex UAC i Windows). @@ -456,17 +456,17 @@ Nedladdningsstorlek: %3 Debugger - + Felsökare Enter command (try `help` for more info) - + Ange kommando (prova 'help' för mer info) Break - + Bryt @@ -474,7 +474,7 @@ Nedladdningsstorlek: %3 Could not open CLI history for writing - + Kunde inte öppna CLI-historik för skrivning @@ -482,7 +482,7 @@ Nedladdningsstorlek: %3 Failed to create an OpenGL 3 context, trying old-style... - + Misslyckades med att skapa en OpenGL 3-kontext, provar old-style... @@ -490,47 +490,47 @@ Nedladdningsstorlek: %3 Connect to Dolphin - + Anslut till Dolphin Local computer - + Lokal dator IP address - + IP-adress Connect - + Anslut Disconnect - + Koppla från Close - + Stäng Reset on connect - + Starta om vid anslutning Couldn't Connect - + Kunde inte ansluta Could not connect to Dolphin. - + Kunde inte ansluta till Dolphin. @@ -538,12 +538,12 @@ Nedladdningsstorlek: %3 3DS - + 3DS Vita - + Vita @@ -551,12 +551,12 @@ Nedladdningsstorlek: %3 Icon - + Ikon Banner - + Banner @@ -564,17 +564,17 @@ Nedladdningsstorlek: %3 Bubble - + Bubbla Background - + Bakgrund Startup - + Uppstart @@ -587,24 +587,24 @@ Nedladdningsstorlek: %3 Files - + Filer ROM file: - + ROM-fil: Browse - + Bläddra Output filename: - + Filnamn för utdata: @@ -614,17 +614,17 @@ Nedladdningsstorlek: %3 Latest stable version - + Senaste stabila version Latest development build - + Senaste utvecklingsversionen Specific file - + Specifik fil @@ -634,57 +634,57 @@ Nedladdningsstorlek: %3 System - + System 3DS - + 3DS Vita - + Vita Presentation - + Presentation Title: - + Titel: Images: - + Bilder: Use default image - + Använd standardbild Preferred size: - + Föredragen storlek: Select image file - + Välj bildfil Select ROM file - + Välj ROM-fil Select output filename - + Välj filnamn för utdata @@ -694,7 +694,7 @@ Nedladdningsstorlek: %3 Build finished - + Byggnation färdig @@ -704,7 +704,7 @@ Nedladdningsstorlek: %3 Build failed - + Byggnation misslyckades @@ -714,17 +714,17 @@ Nedladdningsstorlek: %3 %1 installable package (*.%2) - + %1 installerbara paket (*.%2) Select an image - + Välj en bild Image files (*.png *.jpg *.bmp) - + Bildfiler (*.png *.jpg *.bmp) @@ -732,87 +732,87 @@ Nedladdningsstorlek: %3 Inspect frame - + Inspektera bildruta Magnification - + Förstoring Freeze frame - + Frys bildruta Backdrop color - + Bakgrundsfärg Disable scanline effects - + Inaktivera scanline-effekter Export - + Exportera Reset - + Nollställ Export frame - + Exportera bildruta Portable Network Graphics (*.png) - + Portable Network Graphics (*.png) None - + Ingen Background - + Bakgrund Window - + Fönster Objwin - + Objwin Sprite - + Sprite Backdrop - + Bakgrund Frame - + Bildruta %1 %2 - + %1 %2 @@ -820,7 +820,7 @@ Nedladdningsstorlek: %3 Enable Discord Rich Presence - + Aktivera Discord Rich Presence @@ -828,22 +828,22 @@ Nedladdningsstorlek: %3 Clear Button - + Töm knapp Clear Analog - + Töm analog Refresh - + Uppdatera Set all - + Ställ in alla @@ -851,17 +851,17 @@ Nedladdningsstorlek: %3 Server settings - + Serverinställningar Local port - + Lokal port Bind address - + Bunden adress @@ -871,42 +871,42 @@ Nedladdningsstorlek: %3 Standard GDB - + Standard GDB Internal change detection - + Upptäck interna ändringar Break on all writes - + Bryt vid alla skrivningar Break - + Bryt Stop - + Stoppa Start - + Starta Crash - + Krasch Could not start GDB server - + Kunde inte starta GDB-server @@ -914,62 +914,62 @@ Nedladdningsstorlek: %3 Record GIF/WebP/APNG - + Spela in GIF/WebP/APNG Loop - + Loopa Start - + Starta Stop - + Stoppa Select File - + Välj fil APNG - + APNG GIF - + GIF WebP - + WebP Frameskip - + Hoppa över bildrutor Failed to open output file: %1 - + Misslyckades med att öppna utdatafil: %1 Select output file - + Välj utdatafil Graphics Interchange Format (*.gif);;WebP ( *.webp);;Animated Portable Network Graphics (*.png *.apng) - + Graphics Interchange Format (*.gif);;WebP ( *.webp);;Animated Portable Network Graphics (*.png *.apng) @@ -978,167 +978,167 @@ Nedladdningsstorlek: %3 Autodetect - + Identifiera automatiskt Game Boy (DMG) - + Game Boy (DMG) Game Boy Pocket (MGB) - + Game Boy Pocket (MGB) Super Game Boy (SGB) - + Super Game Boy (SGB) Super Game Boy 2 (SGB) - + Super Game Boy 2 (SGB) Game Boy Color (CGB) - + Game Boy Color (CGB) Game Boy Advance (AGB) - + Game Boy Advance (AGB) Super Game Boy Color (SGB + CGB) - + Super Game Boy Color (SGB + CGB) ROM Only - + Endast ROM MBC1 - + MBC1 MBC2 - + MBC2 MBC3 - + MBC3 MBC3 + RTC - + MBC3 + RTC MBC5 - + MBC5 MBC5 + Rumble - + MBC5 + Rumble MBC6 - + MBC6 MBC7 (Tilt) - + MBC7 (Tilt) MMM01 - + MMM01 HuC-1 - + HuC-1 HuC-3 - + HuC-3 Pocket Cam - + Pocket Cam TAMA5 - + TAMA5 Wisdom Tree - + Wisdom Tree NT (old 1) - + NT (gammal 1) NT (old 2) - + NT (gammal 2) NT (new) - + NT (ny) Pokémon Jade/Diamond - + Pokémon Jade/Diamond BBD - + BBD Hitek - + Hitek GGB-81 - + GGB-81 Li Cheng - + Li Cheng Sachen (MMC1) - + Sachen (MMC1) Sachen (MMC2) - + Sachen (MMC2) @@ -1146,12 +1146,12 @@ Nedladdningsstorlek: %3 I/O Viewer - + I/O-visare 0x0000 - + 0x0000 @@ -1163,42 +1163,42 @@ Nedladdningsstorlek: %3 Background mode - + Bakgrundsläge Mode 0: 4 tile layers - + Läge 0: 4 bricklager Mode 1: 2 tile layers + 1 rotated/scaled tile layer - + Läge 1: 2 bricklager + 1 roterat/skalat bricklager Mode 2: 2 rotated/scaled tile layers - + Läge 2: 2 roterade/skalade bricklager Mode 3: Full 15-bit bitmap - + Läge 3: Full 15-bitars bitmap Mode 4: Full 8-bit bitmap - + Läge 4: Full 8-bitars bitmap Mode 5: Small 15-bit bitmap - + Läge 5: Liten 15-bitars bitmap CGB Mode - + CGB-läge @@ -1208,92 +1208,92 @@ Nedladdningsstorlek: %3 Unlocked HBlank - + Olåst HBlank Linear OBJ tile mapping - + Linjär OBJ-brickmappning Force blank screen - + Tvinga blank skärm Enable background 0 - + Aktivera bakgrund 0 Enable background 1 - + Aktivera bakgrund 1 Enable background 2 - + Aktivera bakgrund 2 Enable background 3 - + Aktivera bakgrund 3 Enable OBJ - + Aktivera OBJ Enable Window 0 - + Aktivera fönster 0 Enable Window 1 - + Aktivera fönster 1 Enable OBJ Window - + Aktivera OBJ-fönster Swap green components - + Byt gröna komponenter Currently in VBlank - + För närvarande i VBlank Currently in HBlank - + För närvarande i HBlank Currently in VCounter - + För närvarande i VCounter Enable VBlank IRQ generation - + Aktivera VBlank IRQ-generering Enable HBlank IRQ generation - + Aktivera HBlank IRQ-generering Enable VCounter IRQ generation - + Aktivera VCounter IRQ-generering @@ -1311,7 +1311,7 @@ Nedladdningsstorlek: %3 Priority - + Prioritet @@ -1319,7 +1319,7 @@ Nedladdningsstorlek: %3 Tile data base (* 16kB) - + Brickdatabas (* 16kB) @@ -1327,7 +1327,7 @@ Nedladdningsstorlek: %3 Enable mosaic - + Aktivera mosaik @@ -1335,7 +1335,7 @@ Nedladdningsstorlek: %3 Enable 256-color - + Aktivera 256-färger @@ -1343,7 +1343,7 @@ Nedladdningsstorlek: %3 Tile map base (* 2kB) - + Brickkartbas (* 2kB) @@ -1351,7 +1351,7 @@ Nedladdningsstorlek: %3 Background dimensions - + Bakgrundsdimensioner @@ -1367,7 +1367,7 @@ Nedladdningsstorlek: %3 Horizontal offset - + Horisontell marginal @@ -1377,7 +1377,7 @@ Nedladdningsstorlek: %3 Vertical offset - + Vertikal marginal @@ -1405,7 +1405,7 @@ Nedladdningsstorlek: %3 Integer part - + Heltalsdel @@ -1413,7 +1413,7 @@ Nedladdningsstorlek: %3 Integer part (low) - + Heltalsdel (låg) @@ -1421,7 +1421,7 @@ Nedladdningsstorlek: %3 Integer part (high) - + Heltalsdel (hög) @@ -1625,7 +1625,7 @@ Nedladdningsstorlek: %3 Disabled - + Inaktiverad @@ -1635,12 +1635,12 @@ Nedladdningsstorlek: %3 Brighten - + Ljusare Darken - + Mörkare @@ -1715,7 +1715,7 @@ Nedladdningsstorlek: %3 Sound length - + Ljudlängd @@ -1753,14 +1753,14 @@ Nedladdningsstorlek: %3 Initial volume - + Initial volym Sound frequency - + Ljudfrekvens @@ -1784,7 +1784,7 @@ Nedladdningsstorlek: %3 Reset - + Nollställ @@ -1800,40 +1800,40 @@ Nedladdningsstorlek: %3 Enable channel 3 - + Aktivera kanal 3 Volume - + Volym 0% - + 0% 100% - + 100% 50% - + 50% 25% - + 25% @@ -1841,7 +1841,7 @@ Nedladdningsstorlek: %3 75% - + 75% @@ -1859,13 +1859,13 @@ Nedladdningsstorlek: %3 15 - + 15 7 - + 7 @@ -1887,49 +1887,49 @@ Nedladdningsstorlek: %3 Enable channel 1 right - + Aktivera kanal 1 höger Enable channel 2 right - + Aktivera kanal 2 höger Enable channel 3 right - + Aktivera kanal 3 höger Enable channel 4 right - + Aktivera kanal 4 höger Enable channel 1 left - + Aktivera kanal 1 vänster Enable channel 2 left - + Aktivera kanal 2 vänster Enable channel 3 left - + Aktivera kanal 3 vänster Enable channel 4 left - + Aktivera kanal 4 vänster @@ -1949,12 +1949,12 @@ Nedladdningsstorlek: %3 Enable channel A right - + Aktivera kanal A höger Enable channel A left - + Aktivera kanal A vänster @@ -1965,7 +1965,7 @@ Nedladdningsstorlek: %3 0 - + 0 @@ -1978,7 +1978,7 @@ Nedladdningsstorlek: %3 1 - + 1 @@ -1988,12 +1988,12 @@ Nedladdningsstorlek: %3 Enable channel B right - + Aktivera kanal B höger Enable channel B left - + Aktivera kanal B vänster @@ -2009,31 +2009,31 @@ Nedladdningsstorlek: %3 Active channel 1 - + Aktiv kanal 1 Active channel 2 - + Aktiv kanal 2 Active channel 3 - + Aktiv kanal 3 Active channel 4 - + Aktiv kanal 4 Enable audio - + Aktivera ljud @@ -2043,7 +2043,7 @@ Nedladdningsstorlek: %3 Resolution - + Upplösning @@ -2119,7 +2119,7 @@ Nedladdningsstorlek: %3 Sample - + Sampling @@ -2131,7 +2131,7 @@ Nedladdningsstorlek: %3 Address (low) - + Adress (låg) @@ -2143,53 +2143,53 @@ Nedladdningsstorlek: %3 Address (high) - + Adress (hög) Sound frequency (low) - + Ljudfrekvens (låg) Sound frequency (high) - + Ljudfrekvens (hög) Source (high) - + Källa (hög) Source (low) - + Källa (låg) Destination (high) - + Destination (hög) Destination (low) - + Destination (låg) Green (low) - + Grön (låg) Green (high) - + Grön (hög) @@ -2197,7 +2197,7 @@ Nedladdningsstorlek: %3 Word count - + Antal ord @@ -2217,7 +2217,7 @@ Nedladdningsstorlek: %3 Increment - + Öka @@ -2229,7 +2229,7 @@ Nedladdningsstorlek: %3 Decrement - + Minska @@ -2241,7 +2241,7 @@ Nedladdningsstorlek: %3 Fixed - + Fast @@ -2249,7 +2249,7 @@ Nedladdningsstorlek: %3 Increment and reload - + Öka och läs om @@ -2265,7 +2265,7 @@ Nedladdningsstorlek: %3 Repeat - + Upprepa @@ -2290,7 +2290,7 @@ Nedladdningsstorlek: %3 Immediate - + Direkt @@ -2302,7 +2302,7 @@ Nedladdningsstorlek: %3 VBlank - + VBlank @@ -2313,7 +2313,7 @@ Nedladdningsstorlek: %3 HBlank - + HBlank @@ -2326,7 +2326,7 @@ Nedladdningsstorlek: %3 IRQ - + IRQ @@ -2341,24 +2341,24 @@ Nedladdningsstorlek: %3 Enable - + Aktivera Audio FIFO - + Ljud-FIFO Video Capture - + Videofångst DRQ - + DRQ @@ -2370,7 +2370,7 @@ Nedladdningsstorlek: %3 Value - + Värde @@ -2378,7 +2378,7 @@ Nedladdningsstorlek: %3 Scale - + Skala @@ -2387,7 +2387,7 @@ Nedladdningsstorlek: %3 1/64 - + 1/64 @@ -2396,7 +2396,7 @@ Nedladdningsstorlek: %3 1/256 - + 1/256 @@ -2405,7 +2405,7 @@ Nedladdningsstorlek: %3 1/1024 - + 1/1024 @@ -2418,55 +2418,55 @@ Nedladdningsstorlek: %3 A - + A Select - + Select Start - + Start Right - + Höger Left - + Vänster Up - + Upp Down - + Ner R - + R L - + L @@ -2476,28 +2476,28 @@ Nedladdningsstorlek: %3 SC - + SC SD - + SD SI - + SI SO - + SO VCounter - + VCounter @@ -2527,31 +2527,31 @@ Nedladdningsstorlek: %3 SIO - + SIO DMA 0 - + DMA 0 DMA 1 - + DMA 1 DMA 2 - + DMA 2 DMA 3 - + DMA 3 @@ -2563,7 +2563,7 @@ Nedladdningsstorlek: %3 Gamepak - + Gamepak @@ -2577,7 +2577,7 @@ Nedladdningsstorlek: %3 4 - + 4 @@ -2585,7 +2585,7 @@ Nedladdningsstorlek: %3 3 - + 3 @@ -2594,7 +2594,7 @@ Nedladdningsstorlek: %3 2 - + 2 @@ -2603,7 +2603,7 @@ Nedladdningsstorlek: %3 8 - + 8 @@ -2644,22 +2644,22 @@ Nedladdningsstorlek: %3 Disable - + Inaktivera 4.19MHz - + 4.19MHz 8.38MHz - + 8.38MHz 16.78MHz - + 16.78MHz @@ -2669,27 +2669,27 @@ Nedladdningsstorlek: %3 Enable IRQs - + Aktivera IRQer Right/A - + Höger/A Left/B - + Vänster/B Up/Select - + Upp/Select Down/Start - + Ner/Start @@ -2699,12 +2699,12 @@ Nedladdningsstorlek: %3 Active face buttons - + Aktiva handlingsknappar Internal clock - + Intern klocka @@ -2724,7 +2724,7 @@ Nedladdningsstorlek: %3 1/16 - + 1/16 @@ -2753,99 +2753,99 @@ Nedladdningsstorlek: %3 Volume right - + Volym höger Output right - + Utgång höger Volume left - + Volym vänster Output left - + Utgång vänster Background enable/priority - + Bakgrund aktivera/prioritet Enable sprites - + Aktivera sprites Double-height sprites - + Sprites med dubbelhöjd Background tile map - + Brickkarta för bakgrund 0x9800 – 0x9BFF - + 0x9800 – 0x9BFF 0x9C00 – 0x9FFF - + 0x9C00 – 0x9FFF Background tile data - + Brickdata för bakgrund 0x8800 – 0x87FF - + 0x8800 – 0x87FF 0x8000 – 0x8FFF - + 0x8000 – 0x8FFF Enable window - + Aktivera fönster Window tile map - + Brickkarta för fönster Enable LCD - + Aktivera LCD Mode - + Läge 0: HBlank - + 0: HBlank 1: VBlank - + 1: VBlank @@ -2855,7 +2855,7 @@ Nedladdningsstorlek: %3 3: HDraw - + 3: HDraw @@ -2865,27 +2865,27 @@ Nedladdningsstorlek: %3 Enable HBlank (mode 0) IRQ - + Aktivera HBlank (läge 0) IRQ Enable VBlank (mode 1) IRQ - + Aktivera VBlank (läge 1) IRQ Enable OAM (mode 2) IRQ - + Aktivera OAM (läge 2) IRQ Enable LYC IRQ - + Aktivera LYC IRQ Current Y coordinate - + Aktuell Y-koordinat @@ -2928,12 +2928,12 @@ Nedladdningsstorlek: %3 Prepare to switch speed - + Förbered att växla hastighet Double speed - + Dubbel hastighet @@ -2943,7 +2943,7 @@ Nedladdningsstorlek: %3 Length - + Längd @@ -2964,13 +2964,13 @@ Nedladdningsstorlek: %3 Unknown - Okänd + Okänd Current index - + Aktuellt index @@ -2982,23 +2982,23 @@ Nedladdningsstorlek: %3 Red - Röd + Röd Blue - Blå + Blå Sprite ordering - + Spriteordning OAM order - + OAM-ordning @@ -3008,7 +3008,7 @@ Nedladdningsstorlek: %3 WRAM bank - + WRAM-bank @@ -3017,7 +3017,7 @@ Nedladdningsstorlek: %3 --- - + --- @@ -3025,27 +3025,27 @@ Nedladdningsstorlek: %3 Name - + Namn Location - + Plats Platform - + Plattform Size - + Storlek CRC32 - + CRC32 @@ -3054,7 +3054,7 @@ Nedladdningsstorlek: %3 %1 State - + %1 tillstånd @@ -3067,82 +3067,82 @@ Nedladdningsstorlek: %3 No Save - + Inget sparat 5 - + 5 6 - + 6 8 - + 8 4 - + 4 1 - + 1 3 - + 3 7 - + 7 9 - + 9 2 - + 2 Cancel - + Avbryt Load State - + Läs in tillstånd Save State - + Spara tillstånd Empty - + Tom Corrupted - + Skadat Slot %1 - + Plats %1 @@ -3151,42 +3151,42 @@ Nedladdningsstorlek: %3 Default - + Standard Fatal - + Ödesdigert Error - + Fel Warning - + Varning Info - + Info Debug - + Felsökning Stub - + Stub Game Error - + Spelfel @@ -3194,47 +3194,47 @@ Nedladdningsstorlek: %3 [%1] %2: %3 - + [%1] %2: %3 An error occurred - + Ett fel inträffade DEBUG - + FELSÖK STUB - + STUB INFO - + INFO WARN - + VARN ERROR - + FEL FATAL - + ÖDESDIGERT GAME ERROR - + SPELFEL @@ -3242,62 +3242,62 @@ Nedladdningsstorlek: %3 Logs - + Loggar Enabled Levels - + Aktiverade nivåer Debug - + Felsök Stub - + Stub Info - + Info Warning - + Varning Error - + Fel Fatal - + Ödesdigert Game Error - + Spelfel Advanced settings - + Avancerade inställningar Clear - + Töm Max Lines - + Max rader @@ -3305,44 +3305,44 @@ Nedladdningsstorlek: %3 Maps - + Kartor Magnification - + Förstoring Export - + Exportera Copy - + Kopiera Priority - + Prioritet Map base - + Kartbas Tile base - + Brickbas Size - + Storlek @@ -3353,54 +3353,54 @@ Nedladdningsstorlek: %3 Xform - + Xform Map Addr. - + Kartadr. Mirror - + Spegla None - + Ingen Both - + Båda Horizontal - + Horisontal Vertical - + Vertikal N/A - + Inte tillgänglig Export map - + Exportera karta Portable Network Graphics (*.png) - + Portable Network Graphics (*.png) @@ -3408,63 +3408,63 @@ Nedladdningsstorlek: %3 Memory access logging - + Loggning av minnesåtkomst Log file - + Loggfil Browse - + Bläddra Log additional information (uses 3× space) - + Logga ytterligare information (använder 3× utrymme) Load existing file if present - + Läs in befintlig fil om tillgänglig Regions - + Regioner Export ROM snapshot - + Exportera ROM-ögonblicksbild Start - + Starta Stop - + Stoppa Failed to open memory log file - + Misslyckades med att öppna minnesloggfil Select access log file - + Välj åtkomstloggfil Memory access logs (*.mal) - + Minnesåtkomstloggar (*.mal) @@ -3472,12 +3472,12 @@ Nedladdningsstorlek: %3 Save Memory Range - + Spara minnesomfång Start Address: - + Startadress: @@ -3492,12 +3492,12 @@ Nedladdningsstorlek: %3 Save memory region - + Spara minnesregion Failed to open output file: %1 - + Misslyckades med att öppna utdatafil: %1 @@ -3505,62 +3505,62 @@ Nedladdningsstorlek: %3 Copy selection - + Kopiera markering Save selection - + Spara markering Paste - + Klistra in Load - Ladda + Läs in All - + Alla Load TBL - + Läs in TBL Save selected memory - + Spara markerat minne Failed to open output file: %1 - + Misslyckades med att öppna utdatafil: %1 Load memory - + Läs in minne Failed to open input file: %1 - + Misslyckades med att öppna indatafil: %1 TBL - + TBL ISO-8859-1 - + ISO-8859-1 @@ -3568,169 +3568,169 @@ Nedladdningsstorlek: %3 Memory Search - + Minnessökning Address - Adress + Adress Current Value - + Aktuellt värde Type - + Typ Value - + Värde Numeric - + Numerisk Text - + Text Width - + Bredd Guess - + Gissa 1 Byte (8-bit) - + 1 Byte (8-bit) 2 Bytes (16-bit) - + 2 Bytes (16-bit) 4 Bytes (32-bit) - + 4 Bytes (32-bit) Number type - + Nummertyp Decimal - + Decimal Hexadecimal - + Hexadecimal Search type - + Söktyp Equal to value - + Lika med värdet Greater than value - + Större än värdet Less than value - + Mindre än värdet Unknown/changed - + Okänt/ändrat Changed by value - + Ändrat med värdet Unchanged - + Oförändrat Increased - + Ökat Decreased - + Minskat Search ROM - + Sök ROM New Search - + Ny sökning Search Within - + Sök inom Open in Memory Viewer - + Öppna i minnesvisare Refresh - + Uppdatera (%0/%1×) - + (%0/%1×) (⅟%0×) - + (⅟%0×) (%0×) - + (%0×) %1 byte%2 - + %1 byte%2 @@ -3738,77 +3738,77 @@ Nedladdningsstorlek: %3 Memory - + Minne Inspect Address: - + Inspektera adress: Set Alignment: - + Ställ in justering: &1 Byte - + &1 Byte &2 Bytes - + &2 Bytes &4 Bytes - + &4 Bytes Unsigned Integer: - + Osignerat heltal: Signed Integer: - + Signerat heltal: String: - + Sträng: Load TBL - + Läs in TBL Copy Selection - + Kopiera markering Paste - + Klistra in Save Selection - + Spara markering Save Range - + Spara omfång Load - Ladda + Läs in @@ -3816,7 +3816,7 @@ Nedladdningsstorlek: %3 Frame %1 - + Bildruta %1 @@ -3842,134 +3842,134 @@ Nedladdningsstorlek: %3 Sprites - + Spritar Address - Adress + Adress Copy - + Kopiera Magnification - + Förstoring Geometry - + Geometri Position - + Position Dimensions - + Dimensioner Matrix - + Matris Export - + Exportera Attributes - + Attribut Transform - + Transformera Off - + Av Palette - + Palett Double Size - + Dubbel storlek Return, Ctrl+R - + Återgå, Ctrl+R Flipped - + Vänd H Short for horizontal - + H V Short for vertical - + V Mode - + Läge Normal - + Normal Mosaic - + Mosaik Enabled - + Aktiverad Priority - + Prioritet Tile - + Ruta 0x%0 - + 0x%0 @@ -3981,38 +3981,38 @@ Nedladdningsstorlek: %3 --- - + --- Trans - + Trans OBJWIN - + OBJWIN Invalid - + Ogiltig N/A - + Inte tillgänglig Export sprite - + Exportera sprite Portable Network Graphics (*.png) - + Portable Network Graphics (*.png) @@ -4025,7 +4025,7 @@ Nedladdningsstorlek: %3 Game Boy Advance - + Game Boy Advance @@ -4033,17 +4033,17 @@ Nedladdningsstorlek: %3 Autodetect - + Identifiera automatiskt Realtime clock - + Realtidsklocka Gyroscope - + Gyroskop @@ -4053,52 +4053,52 @@ Nedladdningsstorlek: %3 Light sensor - + Ljussensor Rumble - + Rumble Save type - + Sparningstyp None - + Ingen SRAM - + SRAM Flash 512kb - + Flash 512kb Flash 1Mb - + Flash 1Mb EEPROM 8kB - + EEPROM 8kB EEPROM 512 bytes - + EEPROM 512 bytes SRAM 64kB (bootlegs only) - + SRAM 64kB (endast bootlegs) @@ -4118,12 +4118,12 @@ Nedladdningsstorlek: %3 Game Boy - + Game Boy Game Boy model - + Game Boy-modell @@ -4133,7 +4133,7 @@ Nedladdningsstorlek: %3 Background Colors - + Bakgrundsfärger @@ -4148,22 +4148,22 @@ Nedladdningsstorlek: %3 Palette preset - + Palettförval Official MBCs - + Officiella MBCer Licensed MBCs - + Licensierade MBCer Unlicensed MBCs - + Olicensierade MBCer @@ -4171,72 +4171,72 @@ Nedladdningsstorlek: %3 Palette - + Palett Background - + Bakgrund Objects - + Objekt Selection - + Markering Red - Röd + Röd Green - Grön + Grön Blue - Blå + Blå 16-bit value - + 16-bitarsvärde Hex code - + Hexkod Palette index - + Palettindex Export BG - + Exportera BG Export OBJ - + Exportera OBJ #%0 - + #%0 0x%0 - + 0x%0 @@ -4244,22 +4244,22 @@ Nedladdningsstorlek: %3 0x%0 (%1) - 0x%0 (%1) + 0x%0 (%1) Export palette - + Exportera palett Windows PAL (*.pal);;Adobe Color Table (*.act) - + Windows PAL (*.pal);;Adobe Color Table (*.act) Failed to open output palette file: %1 - + Misslyckades med att öppna utdatafil för palett: %1 @@ -4267,27 +4267,27 @@ Nedladdningsstorlek: %3 Adjust placement - + Justera placering All - + Alla Offset - + Förskjut X - + X Y - + Y @@ -4295,37 +4295,37 @@ Nedladdningsstorlek: %3 Game Boy Printer - + Game Boy-skrivare Hurry up! - + Skynda dig! Tear off - + Riv av Magnification - + Förstoring Copy - + Kopia Save Printout - + Spara utskrift Portable Network Graphics (*.png) - + Portable Network Graphics (*.png) @@ -4337,62 +4337,62 @@ Nedladdningsstorlek: %3 (unknown) - + (okänt) bytes - + bytes (no database present) - + (finns ingen databas) ROM Info - + ROM-info Game name: - + Spelnamn: Internal name: - + Internt namn: Game ID: - + Spel-ID: Maker Code: - + Tillverkarkod: Revision: - + Revision: File size: - + Filstorlek: CRC32: - + CRC32: Save file: - + Sparad fil: @@ -4400,17 +4400,17 @@ Nedladdningsstorlek: %3 Bug report archive - + Felrapportarkiv ZIP archive (*.zip) - + ZIP-arkiv (*.zip) Generate Bug Report - + Skapa felrapport @@ -4420,27 +4420,27 @@ Nedladdningsstorlek: %3 Generate report - + Skapa rapport Save - Spara + Spara Open issue list in browser - + Öppna fellista i webbläsaren Include save file - + Inkludera sparad fil Create and include savestate - + Skapa och inkludera sparat tillstånd @@ -4448,178 +4448,178 @@ Nedladdningsstorlek: %3 Save games and save states (%1) - + Sparade spel och sparade tillstånd (%1) Select save game or save state - + Välj sparat spel eller sparat tillstånd Save games (%1) - + Sparade spel (%1) Select save game - + Välj sparat spel Conversion failed - + Konvertering misslyckades Failed to convert the save game. This is probably a bug. - + Misslyckades med att konvertera sparat spel. Detta är antagligen en bugg. No file selected - + Ingen fil vald Could not open file - + Kunde inte öppna filen No valid formats found - + Inga giltiga format hittades Please select a valid input file - + Välj en giltig indatafil No valid conversions found - + Inga giltiga konverteringar hittades Cannot convert save games between platforms - + Kan inte konvertera sparade spel mellan plattformar Convert/Extract Save Game - + Konvertera/Extrahera sparat spel Input file - + Indatafil Browse - + Bläddra Output file - + Utdatafil %1 %2 save game - + %1 %2 sparat spel little endian - + little endian big endian - + big endian SRAM - + SRAM %1 flash - + %1 flash %1 EEPROM - + %1 EEPROM + RTC - + + RTC %1 SRAM + RTC - + %1 SRAM + RTC %1 SRAM - + %1 SRAM packed MBC2 - + packad MBC2 unpacked MBC2 - + opackad MBC2 MBC6 flash - + MBC6 flash MBC6 combined SRAM + flash - + MBC6 kombinerat SRAM + flash MBC6 SRAM - + MBC6 SRAM TAMA5 - + TAMA5 %1 (%2) - + %1 (%2) %1 save state with embedded %2 save game - + %1 sparat tillstånd med inbäddat %2 sparat spel %1 SharkPort %2 save game - + %1 SharkPort %2 sparat spel %1 GameShark Advance SP %2 save game - + %1 GameShark Advance SP %2 sparat spel @@ -4627,7 +4627,7 @@ Nedladdningsstorlek: %3 Untitled buffer - + Ej namngiven buffert @@ -4635,57 +4635,57 @@ Nedladdningsstorlek: %3 Scripting - + Skriptning Run - + Kör File - + Fil Load recent script - + Läs in senaste skript Load script... - + Läs in skript... &Load most recent - + &Läs in det senaste &Reset - + &Nollställ 0 - + 0 Select script to load - + Välj skript att läsa in Lua scripts (*.lua) - + Lua-skript (*.lua) All files (*.*) - + Alla filer (*.*) @@ -4693,22 +4693,22 @@ Nedladdningsstorlek: %3 Sensors - + Sensorer Realtime clock - + Realtidsklocka Fixed time - + Fast tid System time - + Systemtid @@ -4718,17 +4718,17 @@ Nedladdningsstorlek: %3 Now - + Nu Offset time - + Förskjut tid sec - + s @@ -4738,17 +4738,17 @@ Nedladdningsstorlek: %3 Light sensor - + Ljussensor Brightness - + Ljusstyrka Tilt sensor - + Tiltsensor @@ -4765,12 +4765,12 @@ Nedladdningsstorlek: %3 Gyroscope - + Gyroskop Sensitivity - + Känslighet @@ -4779,257 +4779,257 @@ Nedladdningsstorlek: %3 Qt Multimedia - + Qt Multimedia SDL - + SDL Software (Qt) - + Programvara (Qt) OpenGL - + OpenGL OpenGL (force version 1.x) - + OpenGL (tvinga version 1.x) None - + Ingen None (Still Image) - + Ingen (stillbild) Keyboard - + Tangentbord Controllers - + Handkontroller Shortcuts - + Genvägar Shaders - + Shaders Select BIOS - + Välj BIOS Select directory - + Välj katalog Select image - + Välj bild Image file (*.png *.jpg *.jpeg) - + Bildfil (*.png *.jpg *.jpeg) (%1×%2) - + (%1×%2) Never - + Aldrig Just now - + Just nyss Less than an hour ago - + Mindre än en timme sedan %n hour(s) ago - - - + + %n timme sedan + %n timmar sedan %n day(s) ago - - - + + %n dag sedan + %n dagar sedan Settings - + Inställningar Audio/Video - + Ljud/Video Gameplay - + Spelning Interface - + Gränssnitt Update - + Uppdatering Emulation - + Emulering Enhancements - + Förbättringar BIOS - + BIOS Paths - + Sökvägar Logging - + Loggning Game Boy - + Game Boy Audio driver: - + Ljuddrivrutin: Audio buffer: - + Ljudbuffert: 1536 - + 1536 512 - + 512 768 - + 768 1024 - + 1024 2048 - + 2048 3072 - + 3072 4096 - + 4096 samples - + samplingar Sample rate: - + Samplingsfrekvens: 44100 - + 44100 22050 - + 22050 32000 - + 32000 48000 - + 48000 Hz - + Hz Volume: - + Volym: @@ -5037,53 +5037,53 @@ Nedladdningsstorlek: %3 Mute - + Tyst Fast forward volume: - + Volym för snabbspolning: Audio in multiplayer: - + Ljud i flerspelarläge: All windows - + Alla fönster Player 1 window only - + Endast Spelare 1-fönstret Currently active player window - + Det aktuella aktiva spelarfönstret Display driver: - + Skärmdrivrutin: Frameskip: - + Hoppa över bildrutor: Skip every - + Hoppa över var frames - + bildrutor @@ -5093,160 +5093,160 @@ Nedladdningsstorlek: %3 frames per second - + bildrutor per sekund Sync: - + Synk: Video - + Video Audio - + Ljud Lock aspect ratio - + Lås bildförhållandet Force integer scaling - + Tvinga heltalsskalning Bilinear filtering - + Bilinjär filtrering Show filename instead of ROM name in library view - + Visa filnamn istället för ROM-namn i biblioteksvyn Pause - + Paus When inactive: - + När inaktiv: On loading a game: - + Vid inläsning av ett spel: Load last state - + Läs in senaste tillstånd Load cheats - + Läs in fusk Save entered cheats - + Spara angivna fusk When minimized: - + När minimerad: Current channel: - + Aktuell kanal: Current version: - + Aktuell version: Update channel: - + Uppdateringskanal: Available version: - + Tillgänglig version: (Unknown) - + (Okänd) Last checked: - + Senast kontrollerad: Automatically check on start - + Kontrollera automatiskt vid start Check now - + Kontrollera nu Default color palette only - + Endast standardfärgpalett SGB color palette if available - + SGB-färgpalett om tillgänglig GBC color palette if available - + GBC-färgpalett om tillgänglig SGB (preferred) or GBC color palette if available - + SGB (föredragen) eller GBC-färgpalett om tillgänglig Game Boy Camera - + Game Boy Camera Driver: - + Drivrutin: Source: - + Källa: Native (59.7275) - + Inbyggd (59.7275) @@ -5256,32 +5256,32 @@ Nedladdningsstorlek: %3 Language - + Språk Library: - + Bibliotek: List view - + Listvy Tree view - + Trädvy Show when no game open - + Visa när inget spel är öppnat Clear cache - + Töm cache @@ -5291,27 +5291,27 @@ Nedladdningsstorlek: %3 Suspend screensaver - + Förhindra skärmsläckare Dynamically update window title - + Uppdatera fönstertiteln dynamiskt Show filename instead of ROM name in title bar - + Visa filnamn istället för ROM-namn i titelraden Show OSD messages - + Visa OSD-meddelanden Enable Discord Rich Presence - + Aktivera Discord Rich Presence @@ -5321,7 +5321,7 @@ Nedladdningsstorlek: %3 Show FPS in title bar - + Visa bilder/s i titelrad @@ -5331,7 +5331,7 @@ Nedladdningsstorlek: %3 Show emulation info on reset - + Visa emuleringsinfo vid omstart @@ -5341,7 +5341,7 @@ Nedladdningsstorlek: %3 Fast forward speed: - + Hastighet för snabbspolning: @@ -5382,22 +5382,22 @@ Nedladdningsstorlek: %3 Run all - + Kör alla Remove known - + Ta bort kända Detect and remove - + Upptäck och ta bort Preload entire ROM into memory - + Förinläs hela ROM till minnet @@ -5408,7 +5408,7 @@ Nedladdningsstorlek: %3 Save game - + Spara spel @@ -5418,59 +5418,59 @@ Nedladdningsstorlek: %3 Models - + Modeller GB only: - + Endast GB: SGB compatible: - + SGB-kompatibel: GBC only: - + Endast GBC: GBC compatible: - + GBC-kompatibel: SGB and GBC compatible: - + SGB och GBC-kompatibla: Game Boy palette - + Game Boy-palett Preset: - + Förval: Screenshot - + Skärmbild Cheat codes - + Fuskkoder Enable Game Boy Player features by default - + Aktivera Game Boy Player-funktioner som standard @@ -5480,32 +5480,32 @@ Nedladdningsstorlek: %3 Video renderer: - + Videorenderare: Software - + Programvara OpenGL enhancements - + OpenGL-förbättringar High-resolution scale: - + Högupplösningsskala: (240×160) - + (240×160) GB BIOS file: - + GB BIOS-fil: @@ -5519,37 +5519,37 @@ Nedladdningsstorlek: %3 Browse - + Bläddra Use BIOS file if found - + Använd BIOS-fil om hittad Skip BIOS intro - + Hoppa över BIOS-intro GBA BIOS file: - + GBA BIOS-fil: GBC BIOS file: - + GBC BIOS-fil: SGB BIOS file: - + SGB BIOS-fil: Save games - + Sparade spel @@ -5558,42 +5558,42 @@ Nedladdningsstorlek: %3 Same directory as the ROM - + Samma katalog som ROM Save states - + Sparade tillstånd Screenshots - + Skärmbilder Patches - + Patchar Cheats - Cheats + Fusk Log to file - + Logga till fil Log to console - + Logga till konsoll Select Log File - + Välj loggfil @@ -5621,58 +5621,58 @@ Nedladdningsstorlek: %3 No shader active - + Ingen shader aktiv Load shader - + Läs in shader No shader loaded - + Ingen shader inläst by %1 - + av %1 Preprocessing - + Förbehandling Pass %1 - + Pass %1 Shaders - + Shaders Active Shader: - + Aktiv shader: Name - + Namn Author - + Upphovsperson Description - + Beskrivning @@ -5682,7 +5682,7 @@ Nedladdningsstorlek: %3 Load New Shader - + Läs in ny shader @@ -5690,17 +5690,17 @@ Nedladdningsstorlek: %3 Action - + Åtgärd Keyboard - + Tangentbord Gamepad - + Gamepad @@ -5708,22 +5708,22 @@ Nedladdningsstorlek: %3 Edit Shortcuts - + Redigera genvägar Keyboard - + Tangentbord Gamepad - + Gamepad Clear - + Töm @@ -5731,88 +5731,88 @@ Nedladdningsstorlek: %3 Export tiles - + Exportera brickor Portable Network Graphics (*.png) - + Portable Network Graphics (*.png) Export tile - + Exportera bricka Tiles - + Brickor Export Selected - + Exportera markerade Export All - + Exportera alla 256 colors - + 256 färger Palette - + Palett Magnification - + Förstoring Tiles per row - + Brickor per rad Fit to window - + Aktivera till fönster Displayed tiles - + Visade brickor Only BG tiles - + Endast BG-brickor Only OBJ tiles - + Endast OBJ-brickor Both - + Båda Copy Selected - + Kopiera markerade Copy All - + Kopiera alla @@ -5820,132 +5820,132 @@ Nedladdningsstorlek: %3 Failed to open output video file: %1 - + Misslyckades med att öppna utdatafil för video: %1 Native (%0x%1) - + Inbyggd (%0x%1) Select output file - + Välj utdatafil Record Video - + Spela in video Start - + Starta Stop - + Stoppa Select File - + Select File Presets - + Förval High &Quality - + Hög &kvalitet &YouTube - + &YouTube WebM - + WebM MP4 - + MP4 &Lossless - + &Förlustfri 4K - + 4K &1080p - + &1080p &720p - + &720p &480p - + &480p &Native - + &Inbyggd Format - + Format Bitrate (kbps) - + Bitfrekvens (kbps) ABR - + ABR VBR - + VBR CRF - + CRF Dimensions - + Dimensioner Lock aspect ratio - + Lås bildförhållandet Show advanced - Visa avancerat + Visa avancerat @@ -5953,35 +5953,35 @@ Nedladdningsstorlek: %3 Archives (%1) - + Arkiv (%1) Select ROM - + Välj ROM Select folder - + Välj mapp Select save - + Välj sparning Select patch - + Välj patch Patches (*.ips *.ups *.bps) - + Patchar (*.ips *.ups *.bps) @@ -5991,54 +5991,56 @@ Nedladdningsstorlek: %3 e-Reader card (*.raw *.bin *.bmp) - + e-Reader-kort (*.raw *.bin *.bmp) Select image - + Välj bild Image file (*.png *.gif *.jpg *.jpeg);;All files (*) - + Bildfil (*.png *.gif *.jpg *.jpeg);;Alla filer (*) GameShark saves (*.sps *.xps) - + GameShark-sparningar (*.sps *.xps) Select video log - + Välj videologg Video logs (*.mvl) - + Videologgar (*.mvl) Crash - + Krasch The game has crashed with the following error: %1 - + Spelet har kraschat med följande fel: + +%1 Couldn't Start - + Kunde inte starta Could not start game. - + Kunde inte starta spel. @@ -6058,254 +6060,254 @@ Nedladdningsstorlek: %3 Really make portable? - + Verkligen göra portabel? This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? - + Detta komma att göra att emulatorn läser in sin konfiguration från samma katalog som den körbara filen. Vill du fortsätta? Restart needed - + Omstart krävs Some changes will not take effect until the emulator is restarted. - + Vissa ändringar kommer inte ge effekt tills emulatorn har startats om. - Player %1 of %2 - + - Spelare %1 av %2 %1 - %2 - + %1 - %2 %1 - %2 - %3 - + %1 - %2 - %3 %1 - %2 (%3 fps) - %4 - + %1 - %2 (%3 bilder/s) - %4 &File - + &Arkiv Load &ROM... - + Läs in &ROM... Load ROM in archive... - + Läs in ROM i arkiv... Add folder to library... - + Lägg till mapp till bibliotek... Save games (%1) - + Sparade spel (%1) Select save game - + Välj sparat spel mGBA save state files (%1) - + mGBA-filer för sparat tillstånd (%1) Select save state - + Välj sparat tillstånd Select e-Reader card images - + Välj e-Reader-kortavbilder Image file (*.png *.jpg *.jpeg) - + Bildfil (*.png *.jpg *.jpeg) Conversion finished - + Konvertering färdig %1 of %2 e-Reader cards converted successfully. - + %1 av %2 e-Reader-kort konverterades. Load alternate save game... - + Läs in alternativt sparat spel... Load temporary save game... - + Läs in temporärt sparat spel... Load &patch... - + Läs in &patch... Boot BIOS - + Starta upp BIOS Replace ROM... - + Ersätt ROM... Scan e-Reader dotcodes... - + Skanna e-Reader-punktkoder... Convert e-Reader card image to raw... - + Konvertera e-Reader-kortavbilder till raw... ROM &info... - + ROM-&info... Recent - + Senaste Make portable - + Gör den portabel &Load state - + &Läs in tillstånd Load state file... - + Läs in tillståndsfil... &Save state - + &Spara tillstånd Save state file... - + Spara tillståndsfil... Quick load - + Snabbinläsning Quick save - + Snabbsparning Load recent - + Läs in senaste Save recent - + Spara senaste Undo load state - + Ångra inläst tillstånd Undo save state - + Ångra sparat tillstånd State &%1 - + Tillstånd &%1 Load camera image... - + Läs in kamerabild... Convert save game... - + Konvertera sparat spel... GameShark saves (*.gsv *.sps *.xps) - + GameShark-sparningar (*.gsv *.sps *.xps) Reset needed - + Omstart krävs Some changes will not take effect until the game is reset. - + Vissa ändringar kommer inte bli effektiva tills spelet har startats om. Save games - + Sparade spel Import GameShark Save... - + Importera GameShark-sparning... Export GameShark Save... - + Exportera GameShark-sparning... Automatically determine - + Bestäm automatiskt @@ -6315,42 +6317,42 @@ Nedladdningsstorlek: %3 New multiplayer window - + Nytt flerspelarfönster Connect to Dolphin... - + Anslut till Dolphin... Report bug... - + Rapportera fel... About... - + Om... E&xit - + A&vsluta &Emulation - + &Emulering &Reset - + Starta &om Sh&utdown - + Stäng &ner @@ -6360,97 +6362,97 @@ Nedladdningsstorlek: %3 &Pause - + &Paus &Next frame - + &Nästa bildruta Fast forward (held) - + Snabbspolning (håll) &Fast forward - + Snabbs&pola Fast forward speed - + Snabbspolningshastighet Unbounded - + Obunden %0x - + %0x Increase fast forward speed - + Öka snabbspolningshastighet Decrease fast forward speed - + Sänk snabbspolningshastighet Rewind (held) - + Spola tillbaka (håll) Re&wind - + Spola ti&llbaka Step backwards - + Stega bakåt Solar sensor - + Solsensor Increase solar level - + Öka solnivå Decrease solar level - + Sänk solnivå Brightest solar level - + Starkaste solnivån Darkest solar level - + Mörkaste solnivån Brightness %1 - + Ljusstyrka %1 Game Boy Printer... - + Game Boy-skrivare... @@ -6460,37 +6462,37 @@ Nedladdningsstorlek: %3 Audio/&Video - + Ljud/&Video Frame size - + Bildrutestorlek %1× - + %1× Toggle fullscreen - + Växla helskärm &Lock frame size - + &Lås bildrutestorlek Lock aspect ratio - + Lås bildförhållande Force integer scaling - + Tvinga heltalsskalning @@ -6500,7 +6502,7 @@ Nedladdningsstorlek: %3 Bilinear filtering - + Bilinjär filtrering @@ -6510,7 +6512,7 @@ Nedladdningsstorlek: %3 Mute - + Tyst @@ -6520,52 +6522,52 @@ Nedladdningsstorlek: %3 Native (59.7275) - + Inbyggd (59.7275) Take &screenshot - + Ta s&kärmbild F12 - + F12 Record A/V... - + Spela in ljud/bild... Record GIF/WebP/APNG... - + Spela in GIF/WebP/APNG... Video layers - + Videolager Audio channels - + Ljudkanaler Adjust layer placement... - + Justera lagerplacering... &Tools - + Ver&ktyg View &logs... - + Visa &loggar... @@ -6575,12 +6577,12 @@ Nedladdningsstorlek: %3 Game Pak sensors... - + Game Pak-sensorer... &Cheats... - + &Fusk... @@ -6590,22 +6592,22 @@ Nedladdningsstorlek: %3 Settings... - + Inställningar... Open debugger console... - + Öppna felsökningskonsoll... Start &GDB server... - + Starta &GDB-server... Scripting... - + Skriptning... @@ -6615,62 +6617,62 @@ Nedladdningsstorlek: %3 View &palette... - + Visa &palett... View &sprites... - + Visa &sprites... View &tiles... - + Visa &brickor... View &map... - + Visa &karta... &Frame inspector... - + Inspektera bi&ldrutor... View memory... - + Visa minne... Search memory... - + Sök i minnet... View &I/O registers... - + Visa I/&O-register... Log memory &accesses... - + Logga minneså&tkomster... Record debug video log... - + Spela in felsökningsvideologg... Stop debug video log - + Stoppa felsökningsvideologg Exit fullscreen - + Avsluta helskärm @@ -6735,7 +6737,7 @@ Nedladdningsstorlek: %3 Clear - + Töm @@ -6743,47 +6745,47 @@ Nedladdningsstorlek: %3 %1 byte - + %1 byte %1 kiB - + %1 kiB %1 MiB - + %1 MiB GBA - + GBA GB - + GB ? - + ? Super (L) - + Super (L) Super (R) - + Super (R) Menu - + Menu @@ -6791,22 +6793,22 @@ Nedladdningsstorlek: %3 Shift - + Skift Control - + Control Alt - + Alt Meta - + Meta From fe5e0c555d744968adf80772fb34714fdaf5b44d Mon Sep 17 00:00:00 2001 From: Roll8ack Date: Mon, 9 Dec 2024 06:33:52 +0000 Subject: [PATCH 111/114] Qt: Update translation (Chinese (Simplified Han script)) Translation: mGBA/Qt Translate-URL: https://hosted.weblate.org/projects/mgba/mgba-qt/zh_Hans/ --- src/platform/qt/ts/mgba-zh_CN.ts | 42 ++++++++++++++++---------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/platform/qt/ts/mgba-zh_CN.ts b/src/platform/qt/ts/mgba-zh_CN.ts index 0a0dfef75..be19fdd14 100644 --- a/src/platform/qt/ts/mgba-zh_CN.ts +++ b/src/platform/qt/ts/mgba-zh_CN.ts @@ -482,7 +482,7 @@ Download size: %3 Failed to create an OpenGL 3 context, trying old-style... - + 创建OpenGL3环境失败,请尝试旧形式... @@ -1058,7 +1058,7 @@ Download size: %3 MBC7 (Tilt) - + MBC7 (Tilt) @@ -2714,7 +2714,7 @@ Download size: %3 Transfer active - 传输处于活动状态 + 传输处于活跃状态 @@ -3408,63 +3408,63 @@ Download size: %3 Memory access logging - + 内存访问日志 Log file - + 日志文件 Browse - 浏览 + 浏览 Log additional information (uses 3× space) - + 记录额外信息(使用 3x 空间) Load existing file if present - + 存在的情况下载入已有文件 Regions - + 区域 Export ROM snapshot - + 导出ROM快照 Start - + 启动 Stop - 停止 + 停止 Failed to open memory log file - + 打开内存日志文件失败 Select access log file - + 选择访问日志文件 Memory access logs (*.mal) - + 内存访问日志(*.mal) @@ -4372,12 +4372,12 @@ Download size: %3 Maker Code: - + 创建者代码: Revision: - + 修订: @@ -4559,7 +4559,7 @@ Download size: %3 + RTC - + + RTC @@ -4660,7 +4660,7 @@ Download size: %3 &Load most recent - + 载入最近的(&L) @@ -6480,7 +6480,7 @@ Download size: %3 &Lock frame size - + 锁定帧大小(&L) @@ -6655,7 +6655,7 @@ Download size: %3 Log memory &accesses... - + 记录内存访问(&a)... From c61347185edce8e28bfadbded6bb1571d625659b Mon Sep 17 00:00:00 2001 From: Momo cao Date: Mon, 9 Dec 2024 03:26:08 +0000 Subject: [PATCH 112/114] Qt: Update translation (Spanish (Latin America)) Translation: mGBA/Qt Translate-URL: https://hosted.weblate.org/projects/mgba/mgba-qt/es_419/ --- src/platform/qt/ts/mgba-es_419.ts | 96 +++++++++++++++---------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/src/platform/qt/ts/mgba-es_419.ts b/src/platform/qt/ts/mgba-es_419.ts index cc40f84f7..9311b210d 100644 --- a/src/platform/qt/ts/mgba-es_419.ts +++ b/src/platform/qt/ts/mgba-es_419.ts @@ -6,12 +6,12 @@ Game Boy Advance ROMs (%1) - ROMs para Game Boy Advance (%1) + ROMs de Game Boy Advance (%1) Game Boy ROMs (%1) - ROMs para Game Boy Advance (%1) + ROMs de Game Boy (%1) @@ -78,7 +78,7 @@ Game Boy Advance es una marca registrada de Nintendo Co., Ltd. Do you want to download and install it now? You will need to restart the emulator when the download is complete. -¿Quieres descargarlo e instalarlo ahora? Deberás reiniciar el emulador cuando se complete la descarga. +¿Quieres descargarla e instalarla ahora? Deberás reiniciar el emulador cuando se complete la descarga. @@ -109,7 +109,7 @@ Tamaño de descarga: %3 Downloading done. Press OK to restart %1 and install the update. - Descarga finalizada. Presiona OK para reiniciar %1 e instalar la actualización. + Descarga finalizada. Presiona Aceptar para reiniciar %1 e instalar la actualización. @@ -140,7 +140,7 @@ Tamaño de descarga: %3 Open in archive... - Abrir desde archivo comprimido... + Abrir archivo comprimido... @@ -272,7 +272,7 @@ Tamaño de descarga: %3 Chip ID - ID de chip + Identificador del chip @@ -466,7 +466,7 @@ Tamaño de descarga: %3 Break - Entrar a depuración + Interrumpir @@ -482,7 +482,7 @@ Tamaño de descarga: %3 Failed to create an OpenGL 3 context, trying old-style... - + Fallo al crear contexto OpenGL, intentando estilo antiguo… @@ -495,7 +495,7 @@ Tamaño de descarga: %3 Local computer - Ordenador local + Equipo local @@ -1615,7 +1615,7 @@ Tamaño de descarga: %3 Backdrop target 1 - Backdrop target 1 + Objetivo del cuadro 1 @@ -1670,7 +1670,7 @@ Tamaño de descarga: %3 Backdrop target 2 - Backdrop target 2 + Objetivo del cuadro 2 @@ -2568,7 +2568,7 @@ Tamaño de descarga: %3 SRAM wait - SRAM wait + Espera SRAM @@ -2664,7 +2664,7 @@ Tamaño de descarga: %3 Gamepak prefetch - Gamepak prefetch + Precargar Gamepak @@ -2778,7 +2778,7 @@ Tamaño de descarga: %3 Enable sprites - Habilitar sprites + Activar sprites @@ -2788,7 +2788,7 @@ Tamaño de descarga: %3 Background tile map - Mapa de tesela de fondo + Fondo de tile map @@ -3176,12 +3176,12 @@ Tamaño de descarga: %3 Debug - Depuración (Debug) + Depuración Stub - Stub + Borrador @@ -3252,12 +3252,12 @@ Tamaño de descarga: %3 Debug - Debug + Depuración Stub - Stub + Borrador @@ -3267,7 +3267,7 @@ Tamaño de descarga: %3 Warning - Warning + Advertencia @@ -3408,63 +3408,63 @@ Tamaño de descarga: %3 Memory access logging - + Registro de acceso a la memoria Log file - + Archivo de registro Browse - + Explorar Log additional information (uses 3× space) - + Registrar información adicional (usa x3 más espacio) Load existing file if present - + Cargar archivo existente si está presente Regions - + Regiones Export ROM snapshot - + Exportar captura de la ROM Start - + Comenzar Stop - Detener + Detener Failed to open memory log file - + Error al abrir el archivo de registro de memoria Select access log file - + Seleccionar archivo de registro de acceso Memory access logs (*.mal) - + Archivos de registro de memoria (*.mal) @@ -4020,7 +4020,7 @@ Tamaño de descarga: %3 Game Overrides - Ajustes específicos por juego + Ajustes de juego @@ -4113,7 +4113,7 @@ Tamaño de descarga: %3 VBA bug compatibility mode - Compatibilidad con bugs de VBA + Compatibilidad con errores de VBA @@ -4372,12 +4372,12 @@ Tamaño de descarga: %3 Maker Code: - + Código de fabricante: Revision: - + Revisión: @@ -4400,7 +4400,7 @@ Tamaño de descarga: %3 Bug report archive - Archivo del informe de errores + Archivo del reporte de errores @@ -4410,17 +4410,17 @@ Tamaño de descarga: %3 Generate Bug Report - Generar informe de error + Generar reporte de error <html><head/><body><p>To file a bug report, please first generate a report file to attach to the bug report you're about to file. It is recommended that you include the save files, as these often help with debugging issues. This will collect some information about the version of {projectName} you're running, your configuration, your computer, and the game you currently have open (if any). Once this collection is completed you can review all of the information gathered below and save it to a zip file. The collection will automatically attempt to redact any personal information, such as your username if it's in any of the paths gathered, but just in case you can edit it afterwards. After you have generated and saved it, please click the button below or go to <a href="https://mgba.io/i/"><span style=" text-decoration: underline; color:#2980b9;">mgba.io/i</span></a> to file the bug report on GitHub. Make sure to attach the report you generated!</p></body></html> - <html><head/><body><p>Antes de enviar un informe de errores, por favor primero genera un archivo de reporte para enviarlo como adjunto. Recomendamos adjuntar los archivos de guardado ya que puede ayudar con la investigación de reportes. Esto recopilará información sobre la versión de {projectName} que estás ejecutando y su configuración, tu ordenador, y el juego que tienes abierto (si hay alguno). Cuando esto termine, puedes ver toda la información y guardarla en un archivo ZIP. Este proceso intentará eliminar automáticamente tus datos personales (como tu usuario, si se encuentra en algunas de las rutas de directorio generadas), pero siempre se puede modificiar luego. Tras generar y guardar el reporte, pulsa el botón inferior o visita <a href="https://mgba.io/i/"><span style=" text-decoration: underline; color:#2980b9;">mgba.io/i</span></a> para subirlo a GitHub. ¡Asegúrate de adjuntar el informe que has generado!</p></body></html> + <html><head/><body><p>Antes de enviar un reporte de errores, por favor primero genera el archivo de reporte para enviarlo como adjunto. Recomendamos adjuntar los archivos de guardado ya que pueden ayudar con la investigación del error. Esto recopilará información sobre la versión de {projectName} que estás ejecutando y su configuración, tu equipo, y el juego que tienes abierto (si hay alguno). Cuando esto termine, podrás ver el reporte y guardarlo en un archivo comprimido (ZIP). Este proceso intentará eliminar automáticamente tus datos personales (por ejemplo, tu usuario, si se encuentra en algunas de las rutas de archivo), pero siempre podrás editarlo manualmente antes de enviarlo. Tras generar y guardar el reporte, pulsa el botón inferior o visita <a href="https://mgba.io/i/"><span style=" text-decoration: underline; color:#2980b9;">mgba.io/i</span></a> para crear un reporte de error en GitHub. ¡Asegúrate de adjuntar tu archivo!</p></body></html> Generate report - Generar informe + Generar reporte @@ -4430,7 +4430,7 @@ Tamaño de descarga: %3 Open issue list in browser - Abrir lista de informes en navegador + Abrir reporte de errores en el navegador @@ -4660,7 +4660,7 @@ Tamaño de descarga: %3 &Load most recent - + &Cargar más reciente @@ -5986,7 +5986,7 @@ Tamaño de descarga: %3 Select e-Reader dotcode - Seleccionar dotcode del e-Reader + Seleccionar código del e-Reader @@ -6271,12 +6271,12 @@ Tamaño de descarga: %3 Undo load state - Deshacer cargar estado + Deshacer estado cargado Undo save state - Deshacer guardar estado + Deshacer estado guardado @@ -6452,7 +6452,7 @@ Tamaño de descarga: %3 &Lock frame size - + &Bloquear tamaño de cuadro @@ -6567,7 +6567,7 @@ Tamaño de descarga: %3 Scan e-Reader dotcodes... - Escanear dotcodes del e-Reader... + Escanear código del e-Reader... @@ -6662,7 +6662,7 @@ Tamaño de descarga: %3 Log memory &accesses... - + Registrar &accesoa la memoria… From 0f3b8989cb9934d0727b89a68a50c69d7023f110 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 12 Dec 2024 03:59:21 -0800 Subject: [PATCH 113/114] Qt: Rename LatAm Spanish to just "Spanish" --- src/platform/qt/ts/{mgba-es_419.ts => mgba-es.ts} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/platform/qt/ts/{mgba-es_419.ts => mgba-es.ts} (99%) diff --git a/src/platform/qt/ts/mgba-es_419.ts b/src/platform/qt/ts/mgba-es.ts similarity index 99% rename from src/platform/qt/ts/mgba-es_419.ts rename to src/platform/qt/ts/mgba-es.ts index 9311b210d..57defd38a 100644 --- a/src/platform/qt/ts/mgba-es_419.ts +++ b/src/platform/qt/ts/mgba-es.ts @@ -1,6 +1,6 @@ - + QGBA From 2b2c393e85f4649bd345e45a9a34616c91e56f11 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 12 Dec 2024 04:02:56 -0800 Subject: [PATCH 114/114] Qt: Fix up how the language names get displayed --- src/platform/qt/SettingsView.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index a7be40269..a72bf0d7b 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -357,7 +357,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC }); QLocale englishLocale("en"); - m_ui.languages->addItem(englishLocale.nativeLanguageName(), englishLocale); + m_ui.languages->addItem("English", englishLocale); QDir ts(":/translations/"); for (auto& name : ts.entryList()) { if (!name.endsWith(".qm") || !name.startsWith(binaryName)) { @@ -367,7 +367,17 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC if (locale.language() == QLocale::English) { continue; } - m_ui.languages->addItem(locale.nativeLanguageName(), locale); + QString endonym = locale.nativeLanguageName(); + // Manualy handle some cases that Qt can't seem to do properly + if (locale.language() == QLocale::Spanish) { + // Qt insists this is called español de España, regardless of if we set the country + endonym = u8"Español"; + } else if (locale.language() == QLocale::Portuguese && locale.country() == QLocale::Brazil) { + // Qt insists that Brazilian Portuguese is just called Português + endonym = u8"Português brasileiro"; + } + endonym[0] = endonym[0].toUpper(); + m_ui.languages->addItem(endonym, locale); if (locale.bcp47Name() == QLocale().bcp47Name()) { m_ui.languages->setCurrentIndex(m_ui.languages->count() - 1); }