mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' (early part) into medusa
This commit is contained in:
commit
73cad3248d
|
@ -1,6 +1,17 @@
|
||||||
/build
|
|
||||||
/build-*
|
|
||||||
*.user*
|
*.user*
|
||||||
*~
|
*~
|
||||||
*.swp
|
*.swp
|
||||||
*.pyc
|
*.pyc
|
||||||
|
|
||||||
|
/build
|
||||||
|
/build-*
|
||||||
|
|
||||||
|
*.a
|
||||||
|
*.dylib
|
||||||
|
*.dll
|
||||||
|
*.exe
|
||||||
|
*.o
|
||||||
|
*.so
|
||||||
|
CMakeCache.txt
|
||||||
|
CMakeFiles
|
||||||
|
version.c
|
||||||
|
|
28
CHANGES
28
CHANGES
|
@ -21,6 +21,12 @@ Misc:
|
||||||
0.8.0: (Future)
|
0.8.0: (Future)
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
- GBA: All IRQs have 7 cycle delay (fixes mgba.io/i/539, mgba.io/i/1208)
|
- GBA: All IRQs have 7 cycle delay (fixes mgba.io/i/539, mgba.io/i/1208)
|
||||||
|
- GBA: Reset now reloads multiboot ROMs
|
||||||
|
- GBA BIOS: Fix multiboot entry point (fixes Magic Floor)
|
||||||
|
Misc:
|
||||||
|
- GBA Savedata: EEPROM performance fixes
|
||||||
|
- GBA Savedata: Automatically map 1Mbit Flash files as 1Mbit Flash
|
||||||
|
- GB Memory: Support running from blocked memory
|
||||||
|
|
||||||
0.7.0: (Future)
|
0.7.0: (Future)
|
||||||
Features:
|
Features:
|
||||||
|
@ -87,6 +93,8 @@ Bugfixes:
|
||||||
- GB, GBA Video: Don't call finishFrame twice in thread proxy
|
- GB, GBA Video: Don't call finishFrame twice in thread proxy
|
||||||
- GB Audio: Fix channel 1, 2 and 4 reset timing
|
- GB Audio: Fix channel 1, 2 and 4 reset timing
|
||||||
- Util: Fix wrapping edge cases in RingFIFO
|
- Util: Fix wrapping edge cases in RingFIFO
|
||||||
|
- GBA Hardware: Fix RTC handshake transition (fixes mgba.io/i/1134)
|
||||||
|
- GBA BIOS: Fix BitUnPack narrowing
|
||||||
Misc:
|
Misc:
|
||||||
- GBA Timer: Use global cycles for timers
|
- GBA Timer: Use global cycles for timers
|
||||||
- GBA: Extend oddly-sized ROMs to full address space (fixes mgba.io/i/722)
|
- GBA: Extend oddly-sized ROMs to full address space (fixes mgba.io/i/722)
|
||||||
|
@ -128,9 +136,11 @@ Features:
|
||||||
- Qt: Separate fast forward volume control (fixes mgba.io/i/846, mgba.io/i/1143)
|
- Qt: Separate fast forward volume control (fixes mgba.io/i/846, mgba.io/i/1143)
|
||||||
- Switch: Rumble support
|
- Switch: Rumble support
|
||||||
- Switch: Rotation support
|
- Switch: Rotation support
|
||||||
|
- Switch: Screen stretching options
|
||||||
- Qt: State file load/save menu options
|
- Qt: State file load/save menu options
|
||||||
- Windows installer
|
- Windows installer
|
||||||
- Tile viewer now has adjustable width
|
- Tile viewer now has adjustable width
|
||||||
|
- Python: Experimental audio API
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
- PSP2: Fix audio crackling after fast forward
|
- PSP2: Fix audio crackling after fast forward
|
||||||
- PSP2: Fix audio crackling when buffer is full
|
- PSP2: Fix audio crackling when buffer is full
|
||||||
|
@ -144,6 +154,18 @@ Bugfixes:
|
||||||
- GB, GBA Savedata: Fix leaks when loading masked save (fixes mgba.io/i/1197)
|
- GB, GBA Savedata: Fix leaks when loading masked save (fixes mgba.io/i/1197)
|
||||||
- Qt: Fix focus issues with load/save state overlay
|
- Qt: Fix focus issues with load/save state overlay
|
||||||
- GB Video: Fix SGB border hole size
|
- GB Video: Fix SGB border hole size
|
||||||
|
- PSP2: Fix tearing issues (fixes mgba.io/i/1211)
|
||||||
|
- Qt: Fix mapping analog triggers (fixes mgba.io/i/495)
|
||||||
|
- Qt: Grab focus when game starts (fixes mgba.io/i/804)
|
||||||
|
- Core: Remember to deinit proxy ring FIFO
|
||||||
|
- GBA Savedata: Fix EEPROM writing codepath when savetype is not EEPROM
|
||||||
|
- Core: Reroot timing list when (de)scheduling
|
||||||
|
- GB Video: Changing LYC while LCDC off doesn't affect STAT (fixes mgba.io/i/1224)
|
||||||
|
- GBA I/O: SOUNDCNT_HI is readable when sound is off
|
||||||
|
- SDL: Fix handling of invalid gamepads (fixes mgba.io/i/1239)
|
||||||
|
- Libretro: Fix adding codes with hooks
|
||||||
|
- GBA: Fix GB Player features
|
||||||
|
- Qt: Ensure FATAL logs reach log view
|
||||||
Misc:
|
Misc:
|
||||||
- mGUI: Add SGB border configuration option
|
- mGUI: Add SGB border configuration option
|
||||||
- mGUI: Add support for different settings types
|
- mGUI: Add support for different settings types
|
||||||
|
@ -156,6 +178,12 @@ Misc:
|
||||||
- Debugger: Minor text fixes
|
- Debugger: Minor text fixes
|
||||||
- Qt: Debugger console history
|
- Qt: Debugger console history
|
||||||
- Qt: Detect presence of GL_ARB_framebuffer_object
|
- Qt: Detect presence of GL_ARB_framebuffer_object
|
||||||
|
- Python: Minor API improvements
|
||||||
|
- Qt: Minor memory view tweaks
|
||||||
|
- CMake: Fix libswresample version dependencies (fixes mgba.io/i/1229)
|
||||||
|
- Debugger: Readability improvements (fixes mgba.io/i/1238)
|
||||||
|
- GB Audio: Skip frame if enabled when clock is high
|
||||||
|
- Res: Improve modeling of AGB/AGS screen in shaders
|
||||||
|
|
||||||
0.7 beta 1: (2018-09-24)
|
0.7 beta 1: (2018-09-24)
|
||||||
- Initial beta for 0.7
|
- Initial beta for 0.7
|
||||||
|
|
|
@ -553,12 +553,7 @@ if(USE_FFMPEG)
|
||||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavformat${LIBAVFORMAT_VERSION_MAJOR}|libavformat-ffmpeg${LIBAVFORMAT_VERSION_MAJOR}")
|
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavformat${LIBAVFORMAT_VERSION_MAJOR}|libavformat-ffmpeg${LIBAVFORMAT_VERSION_MAJOR}")
|
||||||
if(USE_LIBSWRESAMPLE)
|
if(USE_LIBSWRESAMPLE)
|
||||||
string(REGEX MATCH "^[0-9]+" LIBSWRESAMPLE_VERSION_MAJOR ${libswresample_VERSION})
|
string(REGEX MATCH "^[0-9]+" LIBSWRESAMPLE_VERSION_MAJOR ${libswresample_VERSION})
|
||||||
if(${libswresample_VERSION} EQUAL "3.1.100")
|
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libswresample${LIBSWRESAMPLE_VERSION_MAJOR}|libswresample-ffmpeg${LIBSWRESAMPLE_VERSION_MAJOR}")
|
||||||
set(LIBSWRESAMPLE_VERSION_DEBIAN 3)
|
|
||||||
else()
|
|
||||||
math(EXPR LIBSWRESAMPLE_VERSION_DEBIAN "${LIBSWRESAMPLE_VERSION_MAJOR} - 1")
|
|
||||||
endif()
|
|
||||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libswresample${LIBSWRESAMPLE_VERSION_DEBIAN}|libswresample-ffmpeg${LIBSWRESAMPLE_VERSION_DEBIAN}")
|
|
||||||
else()
|
else()
|
||||||
string(REGEX MATCH "^[0-9]+" LIBAVRESAMPLE_VERSION_MAJOR ${libavresample_VERSION})
|
string(REGEX MATCH "^[0-9]+" LIBAVRESAMPLE_VERSION_MAJOR ${libavresample_VERSION})
|
||||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavresample${LIBAVRESAMPLE_VERSION_MAJOR}|libavresample-ffmpeg${LIBAVRESAMPLE_VERSION_MAJOR}")
|
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libavresample${LIBAVRESAMPLE_VERSION_MAJOR}|libavresample-ffmpeg${LIBAVRESAMPLE_VERSION_MAJOR}")
|
||||||
|
|
46
README.md
46
README.md
|
@ -125,7 +125,31 @@ DS default controls are slightly different:
|
||||||
Compiling
|
Compiling
|
||||||
---------
|
---------
|
||||||
|
|
||||||
Compiling requires using CMake 2.8.11 or newer. GCC and Clang are both known to work to compile medusa, but Visual Studio 2013 and older are known not to work. Support for Visual Studio 2015 and newer is coming soon. To use CMake to build on a Unix-based system, the recommended commands are as follows:
|
Compiling requires using CMake 3.1 or newer. GCC and Clang are both known to work to compile medusa, but Visual Studio 2013 and older are known not to work. Support for Visual Studio 2015 and newer is coming soon.
|
||||||
|
|
||||||
|
#### Docker building
|
||||||
|
|
||||||
|
The recommended way to build for most platforms is to use Docker. Several Docker images are provided that contain the requisite toolchain and dependencies for building mGBA across several platforms.
|
||||||
|
|
||||||
|
To use a Docker image to build mGBA, simply run the following command while in the root of an mGBA checkout:
|
||||||
|
|
||||||
|
docker run --rm -t -v $PWD:/home/mgba/src mgba/windows:w32
|
||||||
|
|
||||||
|
This will produce a `build-win32` directory with the build products. Replace `mgba/windows:w32` with another Docker image for other platforms, which will produce a corresponding other directory. The following Docker images available on Docker Hub:
|
||||||
|
|
||||||
|
- mgba/3ds
|
||||||
|
- mgba/switch
|
||||||
|
- mgba/ubuntu:xenial
|
||||||
|
- mgba/ubuntu:bionic
|
||||||
|
- mgba/ubuntu:cosmic
|
||||||
|
- mgba/vita
|
||||||
|
- mgba/wii
|
||||||
|
- mgba/windows:w32
|
||||||
|
- mgba/windows:w64
|
||||||
|
|
||||||
|
#### *nix building
|
||||||
|
|
||||||
|
To use CMake to build on a Unix-based system, the recommended commands are as follows:
|
||||||
|
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
|
@ -151,11 +175,11 @@ To build on Windows for development, using MSYS2 is recommended. Follow the inst
|
||||||
|
|
||||||
For x86 (32 bit) builds:
|
For x86 (32 bit) builds:
|
||||||
|
|
||||||
pacman -Sy base-devel git mingw-w64-i686-{cmake,ffmpeg,gcc,gdb,imagemagick,libelf,libepoxy,libzip,pkg-config,qt5,SDL2,ntldd-git}
|
pacman -Sy --needed base-devel git mingw-w64-i686-{cmake,ffmpeg,gcc,gdb,imagemagick,libelf,libepoxy,libzip,pkg-config,qt5,SDL2,ntldd-git}
|
||||||
|
|
||||||
For x86_64 (64 bit) builds:
|
For x86_64 (64 bit) builds:
|
||||||
|
|
||||||
pacman -Sy base-devel git mingw-w64-x86_64-{cmake,ffmpeg,gcc,gdb,imagemagick,libelf,libepoxy,libzip,pkg-config,qt5,SDL2,ntldd-git}
|
pacman -Sy --needed base-devel git mingw-w64-x86_64-{cmake,ffmpeg,gcc,gdb,imagemagick,libelf,libepoxy,libzip,pkg-config,qt5,SDL2,ntldd-git}
|
||||||
|
|
||||||
Check out the source code by running this command:
|
Check out the source code by running this command:
|
||||||
|
|
||||||
|
@ -171,6 +195,22 @@ Then finally build it by running these commands:
|
||||||
|
|
||||||
Please note that this build of medusa for Windows is not suitable for distribution, due to the scattering of DLLs it needs to run, but is perfect for development. However, if distributing such a build is desired (e.g. for testing on machines that don't have the MSYS2 environment installed), running `cpack -G ZIP` will prepare a zip file with all of the necessary DLLs.
|
Please note that this build of medusa for Windows is not suitable for distribution, due to the scattering of DLLs it needs to run, but is perfect for development. However, if distributing such a build is desired (e.g. for testing on machines that don't have the MSYS2 environment installed), running `cpack -G ZIP` will prepare a zip file with all of the necessary DLLs.
|
||||||
|
|
||||||
|
#### Toolchain building
|
||||||
|
|
||||||
|
If you have devkitARM (for 3DS), devkitPPC (for Wii), devkitA64 (for Switch), or vitasdk (for PS Vita), you can use the following commands for building:
|
||||||
|
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -DCMAKE_TOOLCHAIN_FILE=../src/platform/3ds/CMakeToolchain.txt ..
|
||||||
|
make
|
||||||
|
|
||||||
|
Replace the `-DCMAKE_TOOLCHAIN_FILE` parameter for the following platforms:
|
||||||
|
|
||||||
|
- 3DS: `../src/platform/3ds/CMakeToolchain.txt`
|
||||||
|
- Switch: `../src/platform/switch/CMakeToolchain.txt`
|
||||||
|
- Vita: `../src/platform/psp2/CMakeToolchain.vitasdk`
|
||||||
|
- Wii: `../src/platform/wii/CMakeToolchain.txt`
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
medusa has no hard dependencies, however, the following optional dependencies are required for specific features. The features will be disabled if the dependencies can't be found.
|
medusa has no hard dependencies, however, the following optional dependencies are required for specific features. The features will be disabled if the dependencies can't be found.
|
||||||
|
|
65
README_DE.md
65
README_DE.md
|
@ -52,21 +52,18 @@ Die folgenden Mapper werden vollständig unterstützt:
|
||||||
|
|
||||||
Die folgenden Mapper werden teilweise unterstützt:
|
Die folgenden Mapper werden teilweise unterstützt:
|
||||||
|
|
||||||
|
- MBC6
|
||||||
|
- MMM01
|
||||||
- Pocket Cam
|
- Pocket Cam
|
||||||
- TAMA5
|
- TAMA5
|
||||||
- HuC-3
|
|
||||||
|
|
||||||
Die folgenden Mapper werden derzeit nicht unterstützt:
|
|
||||||
|
|
||||||
- MBC6
|
|
||||||
- HuC-1
|
- HuC-1
|
||||||
- MMM01
|
- HuC-3
|
||||||
|
|
||||||
### Geplante Features
|
### Geplante Features
|
||||||
|
|
||||||
- Unterstützung für Link-Kabel-Multiplayer über ein Netzwerk.
|
- Unterstützung für Link-Kabel-Multiplayer über ein Netzwerk.
|
||||||
- Unterstützung für Link-Kabel über Dolphin/JOY-Bus.
|
- Unterstützung für Link-Kabel über Dolphin/JOY-Bus.
|
||||||
- M4A-Audio-Abmischung für höhere Audio-Qualität.
|
- M4A-Audio-Abmischung für höhere Audio-Qualität als echte Hardware.
|
||||||
- Unterstützung für Tool-Assisted Speedruns.
|
- Unterstützung für Tool-Assisted Speedruns.
|
||||||
- Lua-Unterstützung für Scripting.
|
- Lua-Unterstützung für Scripting.
|
||||||
- Eine umfangreiche Debugging-Suite.
|
- Eine umfangreiche Debugging-Suite.
|
||||||
|
@ -93,7 +90,7 @@ Die Systemvoraussetzungen sind minimal. Jeder Computer, der mit Windows Vista od
|
||||||
Downloads
|
Downloads
|
||||||
---------
|
---------
|
||||||
|
|
||||||
Download-Links befinden sich in der [Downloads][downloads]-Sektion auf der offizielle Website. Der Quellcode befindet sich auf [GitHub][source].
|
Download-Links befinden sich in der [Downloads][downloads]-Sektion auf der offiziellen Website. Der Quellcode befindet sich auf [GitHub][source].
|
||||||
|
|
||||||
Steuerung
|
Steuerung
|
||||||
---------
|
---------
|
||||||
|
@ -110,7 +107,31 @@ Die Steuerung kann im Einstellungs-Menü konfiguriert werden. Viele Spiele-Contr
|
||||||
Kompilieren
|
Kompilieren
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
Um mGBA kompilieren zu können, wird CMake 2.8.11 oder neuer benötigt. GCC und Clang sind beide dafür bekannt, mGBA kompilieren zu können. Visual Studio 2013 und älter funktionieren nicht. Unterstützung für Visual Studio 2015 und neuer wird bald hinzugefügt. Um CMake auf einem Unix-basierten System zu verwenden, werden folgende Kommandos empfohlen:
|
Um mGBA kompilieren zu können, wird CMake 3.1 oder neuer benötigt. GCC und Clang sind beide dafür bekannt, mGBA kompilieren zu können. Visual Studio 2013 und älter funktionieren nicht. Unterstützung für Visual Studio 2015 und neuer wird bald hinzugefügt.
|
||||||
|
|
||||||
|
#### Kompilieren mit Docker
|
||||||
|
|
||||||
|
Der empfohlene Weg, um mGBA für die meisten Plattformen zu kompilieren, ist Docker. Mehrere Docker-Images sind verfügbar, welche die benötigte Compiler-Umgebung und alle benötigten Abhängigkeiten beinhaltet, um mGBA für verschiedene Plattformen zu bauen.
|
||||||
|
|
||||||
|
Um ein Docker-Image zum Bau von mGBA zu verwenden, führe einfach folgenden Befehl in dem Verzeichnis aus, in welches Du den mGBA-Quellcode ausgecheckt hast:
|
||||||
|
|
||||||
|
docker run --rm -t -v $PWD:/home/mgba/src mgba/windows:w32
|
||||||
|
|
||||||
|
Dieser Befehl erzeugt ein Verzeichnis `build-win32` mit den erzeugten Programmdateien. Ersetze `mgba/windows:32` durch ein Docker-Image für eine andere Plattform, wodurch dann das entsprechende Verzeichnis erzeugt wird. Die folgenden Docker-Images sind im Docker Hub verfügbar:
|
||||||
|
|
||||||
|
- mgba/3ds
|
||||||
|
- mgba/switch
|
||||||
|
- mgba/ubuntu:xenial
|
||||||
|
- mgba/ubuntu:bionic
|
||||||
|
- mgba/ubuntu:cosmic
|
||||||
|
- mgba/vita
|
||||||
|
- mgba/wii
|
||||||
|
- mgba/windows:w32
|
||||||
|
- mgba/windows:w64
|
||||||
|
|
||||||
|
#### Unter *nix kompilieren
|
||||||
|
|
||||||
|
Verwende folgende Befehle, um mGBA mithilfe von CMake auf einem Unix-basierten System zu bauen:
|
||||||
|
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
|
@ -118,9 +139,9 @@ Um mGBA kompilieren zu können, wird CMake 2.8.11 oder neuer benötigt. GCC und
|
||||||
make
|
make
|
||||||
sudo make install
|
sudo make install
|
||||||
|
|
||||||
Damit wird mGBA gebaut und in `/usr/bin` und `/usr/lib` installiert. Installierte Abhängigkeiten werden automatisch erkannt. Features, die aufgrund fehlender Abhängigkeiten deaktiviert werden, werden nach dem `cmake`-Kommando aufgelistet.
|
Damit wird mGBA gebaut und in `/usr/bin` und `/usr/lib` installiert. Installierte Abhängigkeiten werden automatisch erkannt. Features, die aufgrund fehlender Abhängigkeiten deaktiviert wurden, werden nach dem `cmake`-Kommando angezeigt.
|
||||||
|
|
||||||
Wenn Du macOS verwendest, sind die einzelnen Schritte etwas anders. Angenommen, dass Du eine Homebrew-Paketverwaltung verwendest, werden folgende Schritte zum installieren der Abhängigkeiten und anschließenden bauen von mGBA empfohlen:
|
Wenn Du macOS verwendest, sind die einzelnen Schritte etwas anders. Angenommen, dass Du den Homebrew-Paketmanager verwendest, werden folgende Schritte zum installieren der Abhängigkeiten und anschließenden bauen von mGBA empfohlen:
|
||||||
|
|
||||||
brew install cmake ffmpeg imagemagick libzip qt5 sdl2 libedit pkg-config
|
brew install cmake ffmpeg imagemagick libzip qt5 sdl2 libedit pkg-config
|
||||||
mkdir build
|
mkdir build
|
||||||
|
@ -136,11 +157,11 @@ Um mGBA auf Windows zu kompilieren, wird MSYS2 empfohlen. Befolge die Installati
|
||||||
|
|
||||||
Für x86 (32 Bit):
|
Für x86 (32 Bit):
|
||||||
|
|
||||||
pacman -Sy base-devel git mingw-w64-i686-{cmake,ffmpeg,gcc,gdb,imagemagick,libelf,libepoxy,libzip,pkg-config,qt5,SDL2,ntldd-git}
|
pacman -Sy --needed base-devel git mingw-w64-i686-{cmake,ffmpeg,gcc,gdb,imagemagick,libelf,libepoxy,libzip,pkg-config,qt5,SDL2,ntldd-git}
|
||||||
|
|
||||||
Für x86_64 (64 Bit):
|
Für x86_64 (64 Bit):
|
||||||
|
|
||||||
pacman -Sy base-devel git mingw-w64-x86_64-{cmake,ffmpeg,gcc,gdb,imagemagick,libelf,libepoxy,libzip,pkg-config,qt5,SDL2,ntldd-git}
|
pacman -Sy --needed base-devel git mingw-w64-x86_64-{cmake,ffmpeg,gcc,gdb,imagemagick,libelf,libepoxy,libzip,pkg-config,qt5,SDL2,ntldd-git}
|
||||||
|
|
||||||
Lade den aktuellen mGBA-Quellcode mithilfe des folgenden Kommandos herunter:
|
Lade den aktuellen mGBA-Quellcode mithilfe des folgenden Kommandos herunter:
|
||||||
|
|
||||||
|
@ -154,7 +175,23 @@ Abschließend wird mGBA über folgende Kommandos kompiliert:
|
||||||
cmake .. -G "MSYS Makefiles"
|
cmake .. -G "MSYS Makefiles"
|
||||||
make
|
make
|
||||||
|
|
||||||
Bitte beachte, dass mGBA für Windows aufgrund der Vielzahl an benötigten DLLs nicht für die weitere Verteilung geeignet ist, wenn es auf diese Weise gebaut wurde. Es ist jedoch perfekt für Entwickler geeignet. Soll mGBA dennoch weiter verteilt werden (beispielsweise zu Testzwecken auf Systemen, auf denen keine MSYS2-Umgebung installiert ist), kann mithilfe des Befehls 'cpack -G ZIP' ein ZIP-Archiv mit allen benötigten DLLs erstellt werden.
|
Bitte beachte, dass mGBA für Windows aufgrund der Vielzahl an benötigten DLLs nicht für die weitere Verteilung geeignet ist, wenn es auf diese Weise gebaut wurde. Es ist jedoch perfekt für Entwickler geeignet. Soll mGBA dennoch weiter verteilt werden (beispielsweise zu Testzwecken auf Systemen, auf denen keine MSYS2-Umgebung installiert ist), kann mithilfe des Befehls `cpack -G ZIP` ein ZIP-Archiv mit allen benötigten DLLs erstellt werden.
|
||||||
|
|
||||||
|
#### Kompilieren mithilfe einer Toolchain
|
||||||
|
|
||||||
|
Wenn Du devkitARM (für 3DS), devkitPPC (für Wii), devkitA64 (für Switch) oder vitasdk (für PS Vita) installiert hast, kannst Du die folgenden Befehle zum Kompilieren verwenden:
|
||||||
|
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -DCMAKE_TOOLCHAIN_FILE=../src/platform/3ds/CMakeToolchain.txt ..
|
||||||
|
make
|
||||||
|
|
||||||
|
Ersetze den Parameter `-DCMAKE_TOOLCHAIN_FILE` dabei folgendermaßen:
|
||||||
|
|
||||||
|
- 3DS: `../src/platform/3ds/CMakeToolchain.txt`
|
||||||
|
- Switch: `../src/platform/switch/CMakeToolchain.txt`
|
||||||
|
- Vita: `../src/platform/psp2/CMakeToolchain.vitasdk`
|
||||||
|
- Wii: `../src/platform/wii/CMakeToolchain.txt`
|
||||||
|
|
||||||
### Abhängigkeiten
|
### Abhängigkeiten
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ struct mVideoLogContext;
|
||||||
struct mCore {
|
struct mCore {
|
||||||
void* cpu;
|
void* cpu;
|
||||||
void* board;
|
void* board;
|
||||||
|
struct mTiming* timing;
|
||||||
struct mDebugger* debugger;
|
struct mDebugger* debugger;
|
||||||
struct mDebuggerSymbols* symbolTable;
|
struct mDebuggerSymbols* symbolTable;
|
||||||
|
|
||||||
|
|
|
@ -55,21 +55,23 @@
|
||||||
#define ARM_STUB cpu->irqh.hitStub(cpu, opcode)
|
#define ARM_STUB cpu->irqh.hitStub(cpu, opcode)
|
||||||
#define ARM_ILL cpu->irqh.hitIllegal(cpu, opcode)
|
#define ARM_ILL cpu->irqh.hitIllegal(cpu, opcode)
|
||||||
|
|
||||||
#define ARM_WRITE_PC \
|
static inline int32_t ARMWritePC(struct ARMCore* cpu) {
|
||||||
cpu->gprs[ARM_PC] = (cpu->gprs[ARM_PC] & -WORD_SIZE_ARM); \
|
cpu->gprs[ARM_PC] = (cpu->gprs[ARM_PC] & -WORD_SIZE_ARM);
|
||||||
cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC]); \
|
cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC]);
|
||||||
LOAD_32(cpu->prefetch[0], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion); \
|
LOAD_32(cpu->prefetch[0], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion);
|
||||||
cpu->gprs[ARM_PC] += WORD_SIZE_ARM; \
|
cpu->gprs[ARM_PC] += WORD_SIZE_ARM;
|
||||||
LOAD_32(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion); \
|
LOAD_32(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion);
|
||||||
currentCycles += 2 + cpu->memory.activeNonseqCycles32 + cpu->memory.activeSeqCycles32;
|
return 2 + cpu->memory.activeNonseqCycles32 + cpu->memory.activeSeqCycles32;
|
||||||
|
}
|
||||||
|
|
||||||
#define THUMB_WRITE_PC \
|
static inline int32_t ThumbWritePC(struct ARMCore* cpu) {
|
||||||
cpu->gprs[ARM_PC] = (cpu->gprs[ARM_PC] & -WORD_SIZE_THUMB); \
|
cpu->gprs[ARM_PC] = (cpu->gprs[ARM_PC] & -WORD_SIZE_THUMB);
|
||||||
cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC]); \
|
cpu->memory.setActiveRegion(cpu, cpu->gprs[ARM_PC]);
|
||||||
LOAD_16(cpu->prefetch[0], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion); \
|
LOAD_16(cpu->prefetch[0], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion);
|
||||||
cpu->gprs[ARM_PC] += WORD_SIZE_THUMB; \
|
cpu->gprs[ARM_PC] += WORD_SIZE_THUMB;
|
||||||
LOAD_16(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion); \
|
LOAD_16(cpu->prefetch[1], cpu->gprs[ARM_PC] & cpu->memory.activeMask, cpu->memory.activeRegion);
|
||||||
currentCycles += 2 + cpu->memory.activeNonseqCycles16 + cpu->memory.activeSeqCycles16;
|
return 2 + cpu->memory.activeNonseqCycles16 + cpu->memory.activeSeqCycles16;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int _ARMModeHasSPSR(enum PrivilegeMode mode) {
|
static inline int _ARMModeHasSPSR(enum PrivilegeMode mode) {
|
||||||
return mode != MODE_SYSTEM && mode != MODE_USER;
|
return mode != MODE_SYSTEM && mode != MODE_USER;
|
||||||
|
|
|
@ -187,6 +187,7 @@ struct GBAudio {
|
||||||
uint8_t* nr52;
|
uint8_t* nr52;
|
||||||
|
|
||||||
int frame;
|
int frame;
|
||||||
|
bool skipFrame;
|
||||||
|
|
||||||
int32_t sampleInterval;
|
int32_t sampleInterval;
|
||||||
enum GBAudioStyle style;
|
enum GBAudioStyle style;
|
||||||
|
|
|
@ -195,6 +195,7 @@ DECL_BITS(GBSerializedAudioFlags, Frame, 22, 3);
|
||||||
DECL_BIT(GBSerializedAudioFlags, Ch1SweepEnabled, 25);
|
DECL_BIT(GBSerializedAudioFlags, Ch1SweepEnabled, 25);
|
||||||
DECL_BIT(GBSerializedAudioFlags, Ch1SweepOccurred, 26);
|
DECL_BIT(GBSerializedAudioFlags, Ch1SweepOccurred, 26);
|
||||||
DECL_BIT(GBSerializedAudioFlags, Ch3Readable, 27);
|
DECL_BIT(GBSerializedAudioFlags, Ch3Readable, 27);
|
||||||
|
DECL_BIT(GBSerializedAudioFlags, SkipFrame, 28);
|
||||||
|
|
||||||
DECL_BITFIELD(GBSerializedAudioEnvelope, uint32_t);
|
DECL_BITFIELD(GBSerializedAudioEnvelope, uint32_t);
|
||||||
DECL_BITS(GBSerializedAudioEnvelope, Length, 0, 7);
|
DECL_BITS(GBSerializedAudioEnvelope, Length, 0, 7);
|
||||||
|
|
|
@ -23,7 +23,8 @@ enum SavedataType {
|
||||||
SAVEDATA_SRAM = 1,
|
SAVEDATA_SRAM = 1,
|
||||||
SAVEDATA_FLASH512 = 2,
|
SAVEDATA_FLASH512 = 2,
|
||||||
SAVEDATA_FLASH1M = 3,
|
SAVEDATA_FLASH1M = 3,
|
||||||
SAVEDATA_EEPROM = 4
|
SAVEDATA_EEPROM = 4,
|
||||||
|
SAVEDATA_EEPROM512 = 5
|
||||||
};
|
};
|
||||||
|
|
||||||
enum SavedataCommand {
|
enum SavedataCommand {
|
||||||
|
|
|
@ -4,18 +4,17 @@ uniform vec2 texSize;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 color = texture2D(tex, texCoord);
|
vec4 color = texture2D(tex, texCoord);
|
||||||
vec3 arrayX[4];
|
vec3 arrayX[3];
|
||||||
arrayX[0] = vec3(1.0, 0.2, 0.2);
|
arrayX[0] = vec3(0.2, 0.2, 1.0);
|
||||||
arrayX[1] = vec3(0.2, 1.0, 0.2);
|
arrayX[1] = vec3(0.2, 1.0, 0.2);
|
||||||
arrayX[2] = vec3(0.2, 0.2, 1.0);
|
arrayX[2] = vec3(1.0, 0.2, 0.2);
|
||||||
arrayX[3] = vec3(0.4, 0.4, 0.4);
|
|
||||||
vec3 arrayY[4];
|
vec3 arrayY[4];
|
||||||
arrayY[0] = vec3(1.0, 1.0, 1.0);
|
arrayY[0] = vec3(1.0, 1.0, 1.0);
|
||||||
arrayY[1] = vec3(1.0, 1.0, 1.0);
|
arrayY[1] = vec3(1.0, 1.0, 1.0);
|
||||||
arrayY[2] = vec3(1.0, 1.0, 1.0);
|
arrayY[2] = vec3(1.0, 1.0, 1.0);
|
||||||
arrayY[3] = vec3(0.8, 0.8, 0.8);
|
arrayY[3] = vec3(0.8, 0.8, 0.8);
|
||||||
color.rgb = pow(color.rgb * vec3(0.8, 0.8, 0.8), vec3(1.8, 1.8, 1.8)) + vec3(0.16, 0.16, 0.16);
|
color.rgb = pow(color.rgb * vec3(0.8, 0.8, 0.8), vec3(1.8, 1.8, 1.8)) + vec3(0.16, 0.16, 0.16);
|
||||||
color.rgb *= arrayX[int(mod(texCoord.s * texSize.x * 4.0, 4.0))];
|
color.rgb *= arrayX[int(mod(texCoord.s * texSize.x * 3.0, 3.0))];
|
||||||
color.rgb *= arrayY[int(mod(texCoord.t * texSize.y * 4.0, 4.0))];
|
color.rgb *= arrayY[int(mod(texCoord.t * texSize.y * 4.0, 4.0))];
|
||||||
color.a = 0.5;
|
color.a = 0.5;
|
||||||
gl_FragColor = color;
|
gl_FragColor = color;
|
||||||
|
|
|
@ -7,5 +7,5 @@ passes=1
|
||||||
[pass.0]
|
[pass.0]
|
||||||
fragmentShader=agb001.fs
|
fragmentShader=agb001.fs
|
||||||
blend=1
|
blend=1
|
||||||
width=-4
|
width=-3
|
||||||
height=-4
|
height=-4
|
||||||
|
|
|
@ -4,18 +4,17 @@ uniform vec2 texSize;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 color = texture2D(tex, texCoord);
|
vec4 color = texture2D(tex, texCoord);
|
||||||
vec3 arrayX[4];
|
vec3 arrayX[3];
|
||||||
arrayX[0] = vec3(1.0, 0.2, 0.2);
|
arrayX[0] = vec3(0.2, 0.2, 1.0);
|
||||||
arrayX[1] = vec3(0.2, 1.0, 0.2);
|
arrayX[1] = vec3(0.2, 1.0, 0.2);
|
||||||
arrayX[2] = vec3(0.2, 0.2, 1.0);
|
arrayX[2] = vec3(1.0, 0.2, 0.2);
|
||||||
arrayX[3] = vec3(0.4, 0.4, 0.4);
|
|
||||||
vec3 arrayY[4];
|
vec3 arrayY[4];
|
||||||
arrayY[0] = vec3(1.0, 1.0, 1.0);
|
arrayY[0] = vec3(1.0, 1.0, 1.0);
|
||||||
arrayY[1] = vec3(1.0, 1.0, 1.0);
|
arrayY[1] = vec3(1.0, 1.0, 1.0);
|
||||||
arrayY[2] = vec3(1.0, 1.0, 1.0);
|
arrayY[2] = vec3(1.0, 1.0, 1.0);
|
||||||
arrayY[3] = vec3(0.9, 0.9, 0.9);
|
arrayY[3] = vec3(0.9, 0.9, 0.9);
|
||||||
color.rgb = pow(color.rgb, vec3(1.6, 1.6, 1.6));
|
color.rgb = pow(color.rgb, vec3(1.6, 1.6, 1.6));
|
||||||
color.rgb *= arrayX[int(mod(texCoord.s * texSize.x * 4.0, 4.0))];
|
color.rgb *= arrayX[int(mod(texCoord.s * texSize.x * 3.0, 3.0))];
|
||||||
color.rgb *= arrayY[int(mod(texCoord.t * texSize.y * 4.0, 4.0))];
|
color.rgb *= arrayY[int(mod(texCoord.t * texSize.y * 4.0, 4.0))];
|
||||||
color.a = 0.8;
|
color.a = 0.8;
|
||||||
gl_FragColor = color;
|
gl_FragColor = color;
|
||||||
|
|
|
@ -7,12 +7,12 @@ passes=2
|
||||||
[pass.0]
|
[pass.0]
|
||||||
fragmentShader=ags001.fs
|
fragmentShader=ags001.fs
|
||||||
blend=1
|
blend=1
|
||||||
width=-4
|
width=-3
|
||||||
height=-4
|
height=-4
|
||||||
|
|
||||||
[pass.1]
|
[pass.1]
|
||||||
fragmentShader=ags001-light.fs
|
fragmentShader=ags001-light.fs
|
||||||
width=-4
|
width=-3
|
||||||
height=-4
|
height=-4
|
||||||
|
|
||||||
[pass.1.uniform.lightBrightness]
|
[pass.1.uniform.lightBrightness]
|
||||||
|
|
|
@ -140,9 +140,7 @@ void ARMReset(struct ARMCore* cpu) {
|
||||||
|
|
||||||
cpu->executionMode = MODE_THUMB;
|
cpu->executionMode = MODE_THUMB;
|
||||||
_ARMSetMode(cpu, MODE_ARM);
|
_ARMSetMode(cpu, MODE_ARM);
|
||||||
|
ARMWritePC(cpu);
|
||||||
int currentCycles = 0;
|
|
||||||
ARM_WRITE_PC;
|
|
||||||
|
|
||||||
cpu->cycles = 0;
|
cpu->cycles = 0;
|
||||||
cpu->nextEvent = 0;
|
cpu->nextEvent = 0;
|
||||||
|
@ -169,12 +167,10 @@ void ARMRaiseIRQ(struct ARMCore* cpu) {
|
||||||
if (ARMControlRegIsVE(cpu->cp15.r1.c0)) {
|
if (ARMControlRegIsVE(cpu->cp15.r1.c0)) {
|
||||||
cpu->gprs[ARM_PC] |= 0xFFFF0000;
|
cpu->gprs[ARM_PC] |= 0xFFFF0000;
|
||||||
}
|
}
|
||||||
int currentCycles = 0;
|
|
||||||
ARM_WRITE_PC;
|
|
||||||
_ARMSetMode(cpu, MODE_ARM);
|
_ARMSetMode(cpu, MODE_ARM);
|
||||||
|
cpu->cycles += ARMWritePC(cpu);
|
||||||
cpu->spsr = cpsr;
|
cpu->spsr = cpsr;
|
||||||
cpu->cpsr.i = 1;
|
cpu->cpsr.i = 1;
|
||||||
cpu->cycles += currentCycles;
|
|
||||||
cpu->halted = 0;
|
cpu->halted = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,12 +189,10 @@ void ARMRaiseSWI(struct ARMCore* cpu) {
|
||||||
if (ARMControlRegIsVE(cpu->cp15.r1.c0)) {
|
if (ARMControlRegIsVE(cpu->cp15.r1.c0)) {
|
||||||
cpu->gprs[ARM_PC] |= 0xFFFF0000;
|
cpu->gprs[ARM_PC] |= 0xFFFF0000;
|
||||||
}
|
}
|
||||||
int currentCycles = 0;
|
|
||||||
ARM_WRITE_PC;
|
|
||||||
_ARMSetMode(cpu, MODE_ARM);
|
_ARMSetMode(cpu, MODE_ARM);
|
||||||
|
cpu->cycles += ARMWritePC(cpu);
|
||||||
cpu->spsr = cpsr;
|
cpu->spsr = cpsr;
|
||||||
cpu->cpsr.i = 1;
|
cpu->cpsr.i = 1;
|
||||||
cpu->cycles += currentCycles;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARMRaiseUndefined(struct ARMCore* cpu) {
|
void ARMRaiseUndefined(struct ARMCore* cpu) {
|
||||||
|
@ -216,12 +210,10 @@ void ARMRaiseUndefined(struct ARMCore* cpu) {
|
||||||
if (ARMControlRegIsVE(cpu->cp15.r1.c0)) {
|
if (ARMControlRegIsVE(cpu->cp15.r1.c0)) {
|
||||||
cpu->gprs[ARM_PC] |= 0xFFFF0000;
|
cpu->gprs[ARM_PC] |= 0xFFFF0000;
|
||||||
}
|
}
|
||||||
int currentCycles = 0;
|
|
||||||
ARM_WRITE_PC;
|
|
||||||
_ARMSetMode(cpu, MODE_ARM);
|
_ARMSetMode(cpu, MODE_ARM);
|
||||||
|
cpu->cycles += ARMWritePC(cpu);
|
||||||
cpu->spsr = cpsr;
|
cpu->spsr = cpsr;
|
||||||
cpu->cpsr.i = 1;
|
cpu->cpsr.i = 1;
|
||||||
cpu->cycles += currentCycles;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARMHalt(struct ARMCore* cpu) {
|
void ARMHalt(struct ARMCore* cpu) {
|
||||||
|
|
|
@ -125,13 +125,14 @@ static void _printStatus(struct CLIDebuggerSystem* debugger) {
|
||||||
struct CLIDebuggerBackend* be = debugger->p->backend;
|
struct CLIDebuggerBackend* be = debugger->p->backend;
|
||||||
struct ARMCore* cpu = debugger->p->d.core->cpu;
|
struct ARMCore* cpu = debugger->p->d.core->cpu;
|
||||||
int r;
|
int r;
|
||||||
for (r = 0; r < 4; ++r) {
|
for (r = 0; r < 16; r += 4) {
|
||||||
be->printf(be, "%08X %08X %08X %08X\n",
|
be->printf(be, "%sr%i: %08X %sr%i: %08X %sr%i: %08X %sr%i: %08X\n",
|
||||||
cpu->gprs[r << 2],
|
r < 10 ? " " : "", r, cpu->gprs[r],
|
||||||
cpu->gprs[(r << 2) + 1],
|
r < 9 ? " " : "", r + 1, cpu->gprs[r + 1],
|
||||||
cpu->gprs[(r << 2) + 2],
|
r < 8 ? " " : "", r + 2, cpu->gprs[r + 2],
|
||||||
cpu->gprs[(r << 2) + 3]);
|
r < 7 ? " " : "", r + 3, cpu->gprs[r + 3]);
|
||||||
}
|
}
|
||||||
|
be->printf(be, "cpsr: ");
|
||||||
_printPSR(be, cpu->cpsr);
|
_printPSR(be, cpu->cpsr);
|
||||||
int instructionLength;
|
int instructionLength;
|
||||||
enum ExecutionMode mode = cpu->cpsr.t;
|
enum ExecutionMode mode = cpu->cpsr.t;
|
||||||
|
|
|
@ -347,11 +347,10 @@ bool ARMDebuggerSetRegister(struct mDebuggerPlatform* d, const char* name, int32
|
||||||
}
|
}
|
||||||
if (strcmp(name, "pc") == 0) {
|
if (strcmp(name, "pc") == 0) {
|
||||||
cpu->gprs[ARM_PC] = value;
|
cpu->gprs[ARM_PC] = value;
|
||||||
int32_t currentCycles = 0;
|
|
||||||
if (cpu->executionMode == MODE_ARM) {
|
if (cpu->executionMode == MODE_ARM) {
|
||||||
ARM_WRITE_PC;
|
ARMWritePC(cpu);
|
||||||
} else {
|
} else {
|
||||||
THUMB_WRITE_PC;
|
ThumbWritePC(cpu);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -363,11 +362,10 @@ bool ARMDebuggerSetRegister(struct mDebuggerPlatform* d, const char* name, int32
|
||||||
}
|
}
|
||||||
cpu->gprs[reg] = value;
|
cpu->gprs[reg] = value;
|
||||||
if (reg == ARM_PC) {
|
if (reg == ARM_PC) {
|
||||||
int32_t currentCycles = 0;
|
|
||||||
if (cpu->executionMode == MODE_ARM) {
|
if (cpu->executionMode == MODE_ARM) {
|
||||||
ARM_WRITE_PC;
|
ARMWritePC(cpu);
|
||||||
} else {
|
} else {
|
||||||
THUMB_WRITE_PC;
|
ThumbWritePC(cpu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -254,7 +254,7 @@ ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) {
|
||||||
#define ADDR_MODE_2_WRITEBACK(ADDR) \
|
#define ADDR_MODE_2_WRITEBACK(ADDR) \
|
||||||
cpu->gprs[rn] = ADDR; \
|
cpu->gprs[rn] = ADDR; \
|
||||||
if (UNLIKELY(rn == ARM_PC)) { \
|
if (UNLIKELY(rn == ARM_PC)) { \
|
||||||
ARM_WRITE_PC; \
|
currentCycles += ARMWritePC(cpu); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ADDR_MODE_2_LSL (cpu->gprs[rm] << ADDR_MODE_2_I)
|
#define ADDR_MODE_2_LSL (cpu->gprs[rm] << ADDR_MODE_2_I)
|
||||||
|
@ -285,7 +285,7 @@ ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) {
|
||||||
#define ARM_LOAD_POST_BODY \
|
#define ARM_LOAD_POST_BODY \
|
||||||
currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32; \
|
currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32; \
|
||||||
if (rd == ARM_PC) { \
|
if (rd == ARM_PC) { \
|
||||||
ARM_WRITE_PC; \
|
currentCycles += ARMWritePC(cpu); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ARM_LOAD_POST_BODY_v5 \
|
#define ARM_LOAD_POST_BODY_v5 \
|
||||||
|
@ -294,9 +294,9 @@ ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) {
|
||||||
_ARMSetMode(cpu, cpu->gprs[ARM_PC] & 0x00000001); \
|
_ARMSetMode(cpu, cpu->gprs[ARM_PC] & 0x00000001); \
|
||||||
cpu->gprs[ARM_PC] &= 0xFFFFFFFE; \
|
cpu->gprs[ARM_PC] &= 0xFFFFFFFE; \
|
||||||
if (cpu->executionMode == MODE_THUMB) { \
|
if (cpu->executionMode == MODE_THUMB) { \
|
||||||
THUMB_WRITE_PC; \
|
currentCycles += ThumbWritePC(cpu); \
|
||||||
} else { \
|
} else { \
|
||||||
ARM_WRITE_PC; \
|
currentCycles += ARMWritePC(cpu); \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,9 +320,9 @@ ATTRIBUTE_NOINLINE static void _neutralS(struct ARMCore* cpu, int32_t d) {
|
||||||
S_BODY; \
|
S_BODY; \
|
||||||
if (rd == ARM_PC) { \
|
if (rd == ARM_PC) { \
|
||||||
if (cpu->executionMode == MODE_ARM) { \
|
if (cpu->executionMode == MODE_ARM) { \
|
||||||
ARM_WRITE_PC; \
|
currentCycles += ARMWritePC(cpu); \
|
||||||
} else { \
|
} else { \
|
||||||
THUMB_WRITE_PC; \
|
currentCycles += ThumbWritePC(cpu); \
|
||||||
} \
|
} \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -682,7 +682,7 @@ DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM(LDM,
|
||||||
load,
|
load,
|
||||||
currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32;
|
currentCycles += cpu->memory.activeNonseqCycles32 - cpu->memory.activeSeqCycles32;
|
||||||
if (rs & 0x8000) {
|
if (rs & 0x8000) {
|
||||||
ARM_WRITE_PC;
|
currentCycles += ARMWritePC(cpu);
|
||||||
})
|
})
|
||||||
|
|
||||||
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM_NO_S(LDMv5,
|
DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM_NO_S(LDMv5,
|
||||||
|
@ -692,9 +692,9 @@ DEFINE_LOAD_STORE_MULTIPLE_INSTRUCTION_ARM_NO_S(LDMv5,
|
||||||
_ARMSetMode(cpu, cpu->gprs[ARM_PC] & 0x00000001);
|
_ARMSetMode(cpu, cpu->gprs[ARM_PC] & 0x00000001);
|
||||||
cpu->gprs[ARM_PC] &= 0xFFFFFFFE;
|
cpu->gprs[ARM_PC] &= 0xFFFFFFFE;
|
||||||
if (cpu->executionMode == MODE_THUMB) {
|
if (cpu->executionMode == MODE_THUMB) {
|
||||||
THUMB_WRITE_PC;
|
currentCycles += ThumbWritePC(cpu);
|
||||||
} else {
|
} else {
|
||||||
ARM_WRITE_PC;
|
currentCycles += ARMWritePC(cpu);
|
||||||
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -727,22 +727,22 @@ DEFINE_INSTRUCTION_ARM(B,
|
||||||
int32_t offset = opcode << 8;
|
int32_t offset = opcode << 8;
|
||||||
offset >>= 6;
|
offset >>= 6;
|
||||||
cpu->gprs[ARM_PC] += offset;
|
cpu->gprs[ARM_PC] += offset;
|
||||||
ARM_WRITE_PC;)
|
currentCycles += ARMWritePC(cpu);)
|
||||||
|
|
||||||
DEFINE_INSTRUCTION_ARM(BL,
|
DEFINE_INSTRUCTION_ARM(BL,
|
||||||
int32_t immediate = (opcode & 0x00FFFFFF) << 8;
|
int32_t immediate = (opcode & 0x00FFFFFF) << 8;
|
||||||
cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - WORD_SIZE_ARM;
|
cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - WORD_SIZE_ARM;
|
||||||
cpu->gprs[ARM_PC] += immediate >> 6;
|
cpu->gprs[ARM_PC] += immediate >> 6;
|
||||||
ARM_WRITE_PC;)
|
currentCycles += ARMWritePC(cpu);)
|
||||||
|
|
||||||
DEFINE_INSTRUCTION_ARM(BX,
|
DEFINE_INSTRUCTION_ARM(BX,
|
||||||
int rm = opcode & 0x0000000F;
|
int rm = opcode & 0x0000000F;
|
||||||
_ARMSetMode(cpu, cpu->gprs[rm] & 0x00000001);
|
_ARMSetMode(cpu, cpu->gprs[rm] & 0x00000001);
|
||||||
cpu->gprs[ARM_PC] = cpu->gprs[rm] & 0xFFFFFFFE;
|
cpu->gprs[ARM_PC] = cpu->gprs[rm] & 0xFFFFFFFE;
|
||||||
if (cpu->executionMode == MODE_THUMB) {
|
if (cpu->executionMode == MODE_THUMB) {
|
||||||
THUMB_WRITE_PC;
|
currentCycles += ThumbWritePC(cpu);
|
||||||
} else {
|
} else {
|
||||||
ARM_WRITE_PC;
|
currentCycles += ARMWritePC(cpu);
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -751,7 +751,7 @@ DEFINE_INSTRUCTION_ARM(BLX,
|
||||||
cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - WORD_SIZE_ARM;
|
cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - WORD_SIZE_ARM;
|
||||||
cpu->gprs[ARM_PC] += (immediate >> 6) + ((opcode >> 23) & 2);
|
cpu->gprs[ARM_PC] += (immediate >> 6) + ((opcode >> 23) & 2);
|
||||||
_ARMSetMode(cpu, MODE_THUMB);
|
_ARMSetMode(cpu, MODE_THUMB);
|
||||||
THUMB_WRITE_PC;)
|
currentCycles += ThumbWritePC(cpu);)
|
||||||
|
|
||||||
DEFINE_INSTRUCTION_ARM(BLX2,
|
DEFINE_INSTRUCTION_ARM(BLX2,
|
||||||
int rm = opcode & 0x0000000F;
|
int rm = opcode & 0x0000000F;
|
||||||
|
@ -760,9 +760,9 @@ DEFINE_INSTRUCTION_ARM(BLX2,
|
||||||
_ARMSetMode(cpu, address & 0x00000001);
|
_ARMSetMode(cpu, address & 0x00000001);
|
||||||
cpu->gprs[ARM_PC] = address & 0xFFFFFFFE;
|
cpu->gprs[ARM_PC] = address & 0xFFFFFFFE;
|
||||||
if (cpu->executionMode == MODE_THUMB) {
|
if (cpu->executionMode == MODE_THUMB) {
|
||||||
THUMB_WRITE_PC;
|
currentCycles += ThumbWritePC(cpu);
|
||||||
} else {
|
} else {
|
||||||
ARM_WRITE_PC;
|
currentCycles += ARMWritePC(cpu);
|
||||||
})
|
})
|
||||||
|
|
||||||
// End branch definitions
|
// End branch definitions
|
||||||
|
|
|
@ -238,14 +238,14 @@ DEFINE_DATA_FORM_5_INSTRUCTION_THUMB(MVN, cpu->gprs[rd] = ~cpu->gprs[rn]; THUMB_
|
||||||
DEFINE_INSTRUCTION_WITH_HIGH_THUMB(ADD4,
|
DEFINE_INSTRUCTION_WITH_HIGH_THUMB(ADD4,
|
||||||
cpu->gprs[rd] += cpu->gprs[rm];
|
cpu->gprs[rd] += cpu->gprs[rm];
|
||||||
if (rd == ARM_PC) {
|
if (rd == ARM_PC) {
|
||||||
THUMB_WRITE_PC;
|
currentCycles += ThumbWritePC(cpu);
|
||||||
})
|
})
|
||||||
|
|
||||||
DEFINE_INSTRUCTION_WITH_HIGH_THUMB(CMP3, int32_t aluOut = cpu->gprs[rd] - cpu->gprs[rm]; THUMB_SUBTRACTION_S(cpu->gprs[rd], cpu->gprs[rm], aluOut))
|
DEFINE_INSTRUCTION_WITH_HIGH_THUMB(CMP3, int32_t aluOut = cpu->gprs[rd] - cpu->gprs[rm]; THUMB_SUBTRACTION_S(cpu->gprs[rd], cpu->gprs[rm], aluOut))
|
||||||
DEFINE_INSTRUCTION_WITH_HIGH_THUMB(MOV3,
|
DEFINE_INSTRUCTION_WITH_HIGH_THUMB(MOV3,
|
||||||
cpu->gprs[rd] = cpu->gprs[rm];
|
cpu->gprs[rd] = cpu->gprs[rm];
|
||||||
if (rd == ARM_PC) {
|
if (rd == ARM_PC) {
|
||||||
THUMB_WRITE_PC;
|
currentCycles += ThumbWritePC(cpu);
|
||||||
})
|
})
|
||||||
|
|
||||||
#define DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(NAME, BODY) \
|
#define DEFINE_IMMEDIATE_WITH_REGISTER_THUMB(NAME, BODY) \
|
||||||
|
@ -310,7 +310,7 @@ DEFINE_LOAD_STORE_MULTIPLE_THUMB(STMIA,
|
||||||
if (ARM_COND_ ## COND) { \
|
if (ARM_COND_ ## COND) { \
|
||||||
int8_t immediate = opcode; \
|
int8_t immediate = opcode; \
|
||||||
cpu->gprs[ARM_PC] += (int32_t) immediate << 1; \
|
cpu->gprs[ARM_PC] += (int32_t) immediate << 1; \
|
||||||
THUMB_WRITE_PC; \
|
currentCycles += ThumbWritePC(cpu); \
|
||||||
})
|
})
|
||||||
|
|
||||||
DEFINE_CONDITIONAL_BRANCH_THUMB(EQ)
|
DEFINE_CONDITIONAL_BRANCH_THUMB(EQ)
|
||||||
|
@ -346,7 +346,7 @@ DEFINE_LOAD_STORE_MULTIPLE_THUMB(POPRv4,
|
||||||
rs |= 1 << ARM_PC,
|
rs |= 1 << ARM_PC,
|
||||||
THUMB_LOAD_POST_BODY;
|
THUMB_LOAD_POST_BODY;
|
||||||
cpu->gprs[ARM_SP] = address;
|
cpu->gprs[ARM_SP] = address;
|
||||||
THUMB_WRITE_PC;)
|
currentCycles += ThumbWritePC(cpu);)
|
||||||
|
|
||||||
DEFINE_LOAD_STORE_MULTIPLE_THUMB(POPR,
|
DEFINE_LOAD_STORE_MULTIPLE_THUMB(POPR,
|
||||||
ARM_SP,
|
ARM_SP,
|
||||||
|
@ -358,9 +358,9 @@ DEFINE_LOAD_STORE_MULTIPLE_THUMB(POPR,
|
||||||
_ARMSetMode(cpu, cpu->gprs[ARM_PC] & 0x00000001);
|
_ARMSetMode(cpu, cpu->gprs[ARM_PC] & 0x00000001);
|
||||||
cpu->gprs[ARM_PC] &= 0xFFFFFFFE;
|
cpu->gprs[ARM_PC] &= 0xFFFFFFFE;
|
||||||
if (cpu->executionMode == MODE_THUMB) {
|
if (cpu->executionMode == MODE_THUMB) {
|
||||||
THUMB_WRITE_PC;
|
currentCycles += ThumbWritePC(cpu);
|
||||||
} else {
|
} else {
|
||||||
ARM_WRITE_PC;
|
currentCycles += ARMWritePC(cpu);
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -385,7 +385,7 @@ DEFINE_INSTRUCTION_THUMB(BKPT, cpu->irqh.bkpt16(cpu, opcode & 0xFF);)
|
||||||
DEFINE_INSTRUCTION_THUMB(B,
|
DEFINE_INSTRUCTION_THUMB(B,
|
||||||
int16_t immediate = (opcode & 0x07FF) << 5;
|
int16_t immediate = (opcode & 0x07FF) << 5;
|
||||||
cpu->gprs[ARM_PC] += (((int32_t) immediate) >> 4);
|
cpu->gprs[ARM_PC] += (((int32_t) immediate) >> 4);
|
||||||
THUMB_WRITE_PC;)
|
currentCycles += ThumbWritePC(cpu);)
|
||||||
|
|
||||||
DEFINE_INSTRUCTION_THUMB(BL1,
|
DEFINE_INSTRUCTION_THUMB(BL1,
|
||||||
int16_t immediate = (opcode & 0x07FF) << 5;
|
int16_t immediate = (opcode & 0x07FF) << 5;
|
||||||
|
@ -396,7 +396,7 @@ DEFINE_INSTRUCTION_THUMB(BL2,
|
||||||
uint32_t pc = cpu->gprs[ARM_PC];
|
uint32_t pc = cpu->gprs[ARM_PC];
|
||||||
cpu->gprs[ARM_PC] = cpu->gprs[ARM_LR] + immediate;
|
cpu->gprs[ARM_PC] = cpu->gprs[ARM_LR] + immediate;
|
||||||
cpu->gprs[ARM_LR] = pc - 1;
|
cpu->gprs[ARM_LR] = pc - 1;
|
||||||
THUMB_WRITE_PC;)
|
currentCycles += ThumbWritePC(cpu);)
|
||||||
|
|
||||||
DEFINE_INSTRUCTION_THUMB(BLX1,
|
DEFINE_INSTRUCTION_THUMB(BLX1,
|
||||||
uint16_t immediate = (opcode & 0x07FF) << 1;
|
uint16_t immediate = (opcode & 0x07FF) << 1;
|
||||||
|
@ -404,7 +404,7 @@ DEFINE_INSTRUCTION_THUMB(BLX1,
|
||||||
cpu->gprs[ARM_PC] = (cpu->gprs[ARM_LR] + immediate) & 0xFFFFFFFC;
|
cpu->gprs[ARM_PC] = (cpu->gprs[ARM_LR] + immediate) & 0xFFFFFFFC;
|
||||||
cpu->gprs[ARM_LR] = pc - 1;
|
cpu->gprs[ARM_LR] = pc - 1;
|
||||||
_ARMSetMode(cpu, MODE_ARM);
|
_ARMSetMode(cpu, MODE_ARM);
|
||||||
ARM_WRITE_PC;)
|
currentCycles += ARMWritePC(cpu);)
|
||||||
|
|
||||||
DEFINE_INSTRUCTION_THUMB(BX,
|
DEFINE_INSTRUCTION_THUMB(BX,
|
||||||
int rm = (opcode >> 3) & 0xF;
|
int rm = (opcode >> 3) & 0xF;
|
||||||
|
@ -415,9 +415,9 @@ DEFINE_INSTRUCTION_THUMB(BX,
|
||||||
}
|
}
|
||||||
cpu->gprs[ARM_PC] = (cpu->gprs[rm] & 0xFFFFFFFE) - misalign;
|
cpu->gprs[ARM_PC] = (cpu->gprs[rm] & 0xFFFFFFFE) - misalign;
|
||||||
if (cpu->executionMode == MODE_THUMB) {
|
if (cpu->executionMode == MODE_THUMB) {
|
||||||
THUMB_WRITE_PC;
|
currentCycles += ThumbWritePC(cpu);
|
||||||
} else {
|
} else {
|
||||||
ARM_WRITE_PC;
|
currentCycles += ARMWritePC(cpu);
|
||||||
})
|
})
|
||||||
|
|
||||||
DEFINE_INSTRUCTION_THUMB(BLX2,
|
DEFINE_INSTRUCTION_THUMB(BLX2,
|
||||||
|
@ -431,9 +431,9 @@ DEFINE_INSTRUCTION_THUMB(BLX2,
|
||||||
cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - 1;
|
cpu->gprs[ARM_LR] = cpu->gprs[ARM_PC] - 1;
|
||||||
cpu->gprs[ARM_PC] = (address & 0xFFFFFFFE) - misalign;
|
cpu->gprs[ARM_PC] = (address & 0xFFFFFFFE) - misalign;
|
||||||
if (cpu->executionMode == MODE_THUMB) {
|
if (cpu->executionMode == MODE_THUMB) {
|
||||||
THUMB_WRITE_PC;
|
currentCycles += ThumbWritePC(cpu);
|
||||||
} else {
|
} else {
|
||||||
ARM_WRITE_PC;
|
currentCycles += ARMWritePC(cpu);
|
||||||
})
|
})
|
||||||
|
|
||||||
DEFINE_INSTRUCTION_THUMB(SWI, cpu->irqh.swi16(cpu, opcode & 0xFF))
|
DEFINE_INSTRUCTION_THUMB(SWI, cpu->irqh.swi16(cpu, opcode & 0xFF))
|
||||||
|
|
|
@ -128,13 +128,13 @@ void mLogFilterSet(struct mLogFilter* filter, const char* category, int levels)
|
||||||
|
|
||||||
}
|
}
|
||||||
bool mLogFilterTest(struct mLogFilter* filter, int category, enum mLogLevel level) {
|
bool mLogFilterTest(struct mLogFilter* filter, int category, enum mLogLevel level) {
|
||||||
int value = (int) TableLookup(&filter->levels, category);
|
int value = (intptr_t) TableLookup(&filter->levels, category);
|
||||||
if (value) {
|
if (value) {
|
||||||
return value & level;
|
return value & level;
|
||||||
}
|
}
|
||||||
const char* cat = mLogCategoryId(category);
|
const char* cat = mLogCategoryId(category);
|
||||||
if (cat) {
|
if (cat) {
|
||||||
value = (int) HashTableLookup(&filter->categories, cat);
|
value = (intptr_t) HashTableLookup(&filter->categories, cat);
|
||||||
if (value) {
|
if (value) {
|
||||||
TableInsert(&filter->levels, category, (void*)(intptr_t) value);
|
TableInsert(&filter->levels, category, (void*)(intptr_t) value);
|
||||||
return value & level;
|
return value & level;
|
||||||
|
|
|
@ -198,7 +198,7 @@ static inline color_t* _tileLookup(struct mTileCache* cache, unsigned tileId, un
|
||||||
if (tileId >= tiles) {
|
if (tileId >= tiles) {
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
if (paletteId >= 1 << mTileCacheSystemInfoGetPaletteCount(cache->sysConfig)) {
|
if (paletteId >= 1U << mTileCacheSystemInfoGetPaletteCount(cache->sysConfig)) {
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -28,6 +28,10 @@ void mTimingSchedule(struct mTiming* timing, struct mTimingEvent* event, int32_t
|
||||||
if (nextEvent < *timing->nextEvent) {
|
if (nextEvent < *timing->nextEvent) {
|
||||||
*timing->nextEvent = nextEvent;
|
*timing->nextEvent = nextEvent;
|
||||||
}
|
}
|
||||||
|
if (timing->reroot) {
|
||||||
|
timing->root = timing->reroot;
|
||||||
|
timing->reroot = NULL;
|
||||||
|
}
|
||||||
struct mTimingEvent** previous = &timing->root;
|
struct mTimingEvent** previous = &timing->root;
|
||||||
struct mTimingEvent* next = timing->root;
|
struct mTimingEvent* next = timing->root;
|
||||||
unsigned priority = event->priority;
|
unsigned priority = event->priority;
|
||||||
|
@ -44,6 +48,10 @@ void mTimingSchedule(struct mTiming* timing, struct mTimingEvent* event, int32_t
|
||||||
}
|
}
|
||||||
|
|
||||||
void mTimingDeschedule(struct mTiming* timing, struct mTimingEvent* event) {
|
void mTimingDeschedule(struct mTiming* timing, struct mTimingEvent* event) {
|
||||||
|
if (timing->reroot) {
|
||||||
|
timing->root = timing->reroot;
|
||||||
|
timing->reroot = NULL;
|
||||||
|
}
|
||||||
struct mTimingEvent** previous = &timing->root;
|
struct mTimingEvent** previous = &timing->root;
|
||||||
struct mTimingEvent* next = timing->root;
|
struct mTimingEvent* next = timing->root;
|
||||||
while (next) {
|
while (next) {
|
||||||
|
|
|
@ -305,11 +305,10 @@ static void _writeGPRs(struct GDBStub* stub, const char* message) {
|
||||||
cpu->gprs[r] = _hex2int(readAddress, 8);
|
cpu->gprs[r] = _hex2int(readAddress, 8);
|
||||||
readAddress += 8;
|
readAddress += 8;
|
||||||
}
|
}
|
||||||
int32_t currentCycles = 0;
|
|
||||||
if (cpu->executionMode == MODE_ARM) {
|
if (cpu->executionMode == MODE_ARM) {
|
||||||
ARM_WRITE_PC;
|
ARMWritePC(cpu);
|
||||||
} else {
|
} else {
|
||||||
THUMB_WRITE_PC;
|
ThumbWritePC(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
|
strncpy(stub->outgoing, "OK", GDB_STUB_MAX_LINE - 4);
|
||||||
|
@ -369,11 +368,10 @@ static void _writeRegister(struct GDBStub* stub, const char* message) {
|
||||||
if (reg <= ARM_PC) {
|
if (reg <= ARM_PC) {
|
||||||
cpu->gprs[reg] = value;
|
cpu->gprs[reg] = value;
|
||||||
if (reg == ARM_PC) {
|
if (reg == ARM_PC) {
|
||||||
int32_t currentCycles = 0;
|
|
||||||
if (cpu->executionMode == MODE_ARM) {
|
if (cpu->executionMode == MODE_ARM) {
|
||||||
ARM_WRITE_PC;
|
ARMWritePC(cpu);
|
||||||
} else {
|
} else {
|
||||||
THUMB_WRITE_PC;
|
ThumbWritePC(cpu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (reg == 0x19) {
|
} else if (reg == 0x19) {
|
||||||
|
|
10
src/ds/ds.c
10
src/ds/ds.c
|
@ -329,8 +329,7 @@ void DS7Reset(struct ARMCore* cpu) {
|
||||||
cpu->gprs[12] = header->arm7Entry;
|
cpu->gprs[12] = header->arm7Entry;
|
||||||
cpu->gprs[ARM_LR] = header->arm7Entry;
|
cpu->gprs[ARM_LR] = header->arm7Entry;
|
||||||
cpu->gprs[ARM_PC] = header->arm7Entry;
|
cpu->gprs[ARM_PC] = header->arm7Entry;
|
||||||
int currentCycles = 0;
|
ARMWritePC(cpu);
|
||||||
ARM_WRITE_PC;
|
|
||||||
|
|
||||||
ds->romVf->unmap(ds->romVf, header, sizeof(*header));
|
ds->romVf->unmap(ds->romVf, header, sizeof(*header));
|
||||||
}
|
}
|
||||||
|
@ -369,8 +368,7 @@ void DS9Reset(struct ARMCore* cpu) {
|
||||||
cpu->gprs[12] = header->arm9Entry;
|
cpu->gprs[12] = header->arm9Entry;
|
||||||
cpu->gprs[ARM_LR] = header->arm9Entry;
|
cpu->gprs[ARM_LR] = header->arm9Entry;
|
||||||
cpu->gprs[ARM_PC] = header->arm9Entry;
|
cpu->gprs[ARM_PC] = header->arm9Entry;
|
||||||
int currentCycles = 0;
|
ARMWritePC(cpu);
|
||||||
ARM_WRITE_PC;
|
|
||||||
|
|
||||||
ds->romVf->unmap(ds->romVf, header, sizeof(*header));
|
ds->romVf->unmap(ds->romVf, header, sizeof(*header));
|
||||||
}
|
}
|
||||||
|
@ -632,10 +630,10 @@ void DSIllegal(struct ARMCore* cpu, uint32_t opcode) {
|
||||||
int currentCycles = 0;
|
int currentCycles = 0;
|
||||||
if (cpu->executionMode == MODE_THUMB) {
|
if (cpu->executionMode == MODE_THUMB) {
|
||||||
cpu->gprs[ARM_PC] -= WORD_SIZE_THUMB * 2;
|
cpu->gprs[ARM_PC] -= WORD_SIZE_THUMB * 2;
|
||||||
THUMB_WRITE_PC;
|
ThumbWritePC(cpu);
|
||||||
} else {
|
} else {
|
||||||
cpu->gprs[ARM_PC] -= WORD_SIZE_ARM * 2;
|
cpu->gprs[ARM_PC] -= WORD_SIZE_ARM * 2;
|
||||||
ARM_WRITE_PC;
|
ARMWritePC(cpu);
|
||||||
}
|
}
|
||||||
#ifdef USE_DEBUGGERS
|
#ifdef USE_DEBUGGERS
|
||||||
} else if (ds->debugger) {
|
} else if (ds->debugger) {
|
||||||
|
|
|
@ -78,6 +78,7 @@ void mVideoThreadProxyDeinit(struct mVideoLogger* logger) {
|
||||||
if (waiting) {
|
if (waiting) {
|
||||||
ThreadJoin(proxyRenderer->thread);
|
ThreadJoin(proxyRenderer->thread);
|
||||||
}
|
}
|
||||||
|
RingFIFODeinit(&proxyRenderer->dirtyQueue);
|
||||||
ConditionDeinit(&proxyRenderer->fromThreadCond);
|
ConditionDeinit(&proxyRenderer->fromThreadCond);
|
||||||
ConditionDeinit(&proxyRenderer->toThreadCond);
|
ConditionDeinit(&proxyRenderer->toThreadCond);
|
||||||
MutexDeinit(&proxyRenderer->mutex);
|
MutexDeinit(&proxyRenderer->mutex);
|
||||||
|
|
|
@ -470,7 +470,15 @@ void GBAudioWriteNR52(struct GBAudio* audio, uint8_t value) {
|
||||||
}
|
}
|
||||||
*audio->nr52 &= ~0x000F;
|
*audio->nr52 &= ~0x000F;
|
||||||
} else if (!wasEnable) {
|
} else if (!wasEnable) {
|
||||||
|
audio->skipFrame = false;
|
||||||
audio->frame = 7;
|
audio->frame = 7;
|
||||||
|
|
||||||
|
if (audio->p) {
|
||||||
|
unsigned timingFactor = 0x400 >> !audio->p->doubleSpeed;
|
||||||
|
if (audio->p->timer.internalDiv & timingFactor) {
|
||||||
|
audio->skipFrame = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -483,6 +491,13 @@ void _updateFrame(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAudioUpdateFrame(struct GBAudio* audio, struct mTiming* timing) {
|
void GBAudioUpdateFrame(struct GBAudio* audio, struct mTiming* timing) {
|
||||||
|
if (!audio->enable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (audio->skipFrame) {
|
||||||
|
audio->skipFrame = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
int frame = (audio->frame + 1) & 7;
|
int frame = (audio->frame + 1) & 7;
|
||||||
audio->frame = frame;
|
audio->frame = frame;
|
||||||
|
|
||||||
|
@ -929,6 +944,7 @@ void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGStat
|
||||||
uint32_t ch4Flags = 0;
|
uint32_t ch4Flags = 0;
|
||||||
|
|
||||||
flags = GBSerializedAudioFlagsSetFrame(flags, audio->frame);
|
flags = GBSerializedAudioFlagsSetFrame(flags, audio->frame);
|
||||||
|
flags = GBSerializedAudioFlagsSetSkipFrame(flags, audio->skipFrame);
|
||||||
STORE_32LE(audio->frameEvent.when - mTimingCurrentTime(audio->timing), 0, &state->ch1.nextFrame);
|
STORE_32LE(audio->frameEvent.when - mTimingCurrentTime(audio->timing), 0, &state->ch1.nextFrame);
|
||||||
|
|
||||||
flags = GBSerializedAudioFlagsSetCh1Volume(flags, audio->ch1.envelope.currentVolume);
|
flags = GBSerializedAudioFlagsSetCh1Volume(flags, audio->ch1.envelope.currentVolume);
|
||||||
|
@ -987,6 +1003,7 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt
|
||||||
|
|
||||||
LOAD_32LE(flags, 0, flagsIn);
|
LOAD_32LE(flags, 0, flagsIn);
|
||||||
audio->frame = GBSerializedAudioFlagsGetFrame(flags);
|
audio->frame = GBSerializedAudioFlagsGetFrame(flags);
|
||||||
|
audio->skipFrame = GBSerializedAudioFlagsGetSkipFrame(flags);
|
||||||
|
|
||||||
LOAD_32LE(ch1Flags, 0, &state->ch1.envelope);
|
LOAD_32LE(ch1Flags, 0, &state->ch1.envelope);
|
||||||
audio->ch1.envelope.currentVolume = GBSerializedAudioFlagsGetCh1Volume(flags);
|
audio->ch1.envelope.currentVolume = GBSerializedAudioFlagsGetCh1Volume(flags);
|
||||||
|
|
|
@ -104,6 +104,7 @@ static bool _GBCoreInit(struct mCore* core) {
|
||||||
}
|
}
|
||||||
core->cpu = cpu;
|
core->cpu = cpu;
|
||||||
core->board = gb;
|
core->board = gb;
|
||||||
|
core->timing = &gb->timing;
|
||||||
gbcore->overrides = NULL;
|
gbcore->overrides = NULL;
|
||||||
gbcore->debuggerPlatform = NULL;
|
gbcore->debuggerPlatform = NULL;
|
||||||
gbcore->cheatDevice = NULL;
|
gbcore->cheatDevice = NULL;
|
||||||
|
|
10
src/gb/mbc.c
10
src/gb/mbc.c
|
@ -188,11 +188,6 @@ void GBMBCInit(struct GB* gb) {
|
||||||
case 2:
|
case 2:
|
||||||
case 3:
|
case 3:
|
||||||
gb->memory.mbcType = GB_MBC1;
|
gb->memory.mbcType = GB_MBC1;
|
||||||
if (gb->memory.romSize >= GB_SIZE_CART_BANK0 * 0x31 && _isMulticart(gb->memory.rom)) {
|
|
||||||
gb->memory.mbcState.mbc1.multicartStride = 4;
|
|
||||||
} else {
|
|
||||||
gb->memory.mbcState.mbc1.multicartStride = 5;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
case 6:
|
case 6:
|
||||||
|
@ -255,6 +250,11 @@ void GBMBCInit(struct GB* gb) {
|
||||||
break;
|
break;
|
||||||
case GB_MBC1:
|
case GB_MBC1:
|
||||||
gb->memory.mbcWrite = _GBMBC1;
|
gb->memory.mbcWrite = _GBMBC1;
|
||||||
|
if (gb->memory.romSize >= GB_SIZE_CART_BANK0 * 0x31 && _isMulticart(gb->memory.rom)) {
|
||||||
|
gb->memory.mbcState.mbc1.multicartStride = 4;
|
||||||
|
} else {
|
||||||
|
gb->memory.mbcState.mbc1.multicartStride = 5;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case GB_MBC2:
|
case GB_MBC2:
|
||||||
gb->memory.mbcWrite = _GBMBC2;
|
gb->memory.mbcWrite = _GBMBC2;
|
||||||
|
|
|
@ -45,6 +45,8 @@ static const enum GBBus _oamBlockCGB[] = {
|
||||||
GB_BUS_CPU // 0xE000
|
GB_BUS_CPU // 0xE000
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const uint8_t _blockedRegion[1] = { 0xFF };
|
||||||
|
|
||||||
static void _pristineCow(struct GB* gba);
|
static void _pristineCow(struct GB* gba);
|
||||||
|
|
||||||
static uint8_t GBFastLoad8(struct LR35902Core* cpu, uint16_t address) {
|
static uint8_t GBFastLoad8(struct LR35902Core* cpu, uint16_t address) {
|
||||||
|
@ -92,6 +94,15 @@ static void GBSetActiveRegion(struct LR35902Core* cpu, uint16_t address) {
|
||||||
cpu->memory.cpuLoad8 = GBLoad8;
|
cpu->memory.cpuLoad8 = GBLoad8;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (gb->memory.dmaRemaining) {
|
||||||
|
const enum GBBus* block = gb->model < GB_MODEL_CGB ? _oamBlockDMG : _oamBlockCGB;
|
||||||
|
enum GBBus dmaBus = block[memory->dmaSource >> 13];
|
||||||
|
enum GBBus accessBus = block[address >> 13];
|
||||||
|
if ((dmaBus != GB_BUS_CPU && dmaBus == accessBus) || (address >= GB_BASE_OAM && address < GB_BASE_UNUSABLE)) {
|
||||||
|
cpu->memory.activeRegion = _blockedRegion;
|
||||||
|
cpu->memory.activeMask = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _GBMemoryDMAService(struct mTiming* timing, void* context, uint32_t cyclesLate);
|
static void _GBMemoryDMAService(struct mTiming* timing, void* context, uint32_t cyclesLate);
|
||||||
|
@ -177,6 +188,7 @@ void GBMemoryReset(struct GB* gb) {
|
||||||
|
|
||||||
memset(&gb->memory.hram, 0, sizeof(gb->memory.hram));
|
memset(&gb->memory.hram, 0, sizeof(gb->memory.hram));
|
||||||
|
|
||||||
|
memset(&gb->memory.mbcState, 0, sizeof(gb->memory.mbcState));
|
||||||
GBMBCInit(gb);
|
GBMBCInit(gb);
|
||||||
switch (gb->memory.mbcType) {
|
switch (gb->memory.mbcType) {
|
||||||
case GB_MBC1:
|
case GB_MBC1:
|
||||||
|
@ -192,8 +204,9 @@ void GBMemoryReset(struct GB* gb) {
|
||||||
case GB_MMM01:
|
case GB_MMM01:
|
||||||
GBMBCSwitchBank0(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 2);
|
GBMBCSwitchBank0(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 2);
|
||||||
GBMBCSwitchBank(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 1);
|
GBMBCSwitchBank(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 1);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
memset(&gb->memory.mbcState, 0, sizeof(gb->memory.mbcState));
|
break;
|
||||||
}
|
}
|
||||||
gb->memory.sramBank = gb->memory.sram;
|
gb->memory.sramBank = gb->memory.sram;
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ static void _GBTimerDivIncrement(struct GBTimer* timer, uint32_t cyclesLate) {
|
||||||
mTimingSchedule(&timer->p->timing, &timer->irq, 7 - ((timer->p->cpu->executionState - cyclesLate) & 3));
|
mTimingSchedule(&timer->p->timing, &timer->irq, 7 - ((timer->p->cpu->executionState - cyclesLate) & 3));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int timingFactor = 0x3FF >> !timer->p->doubleSpeed;
|
unsigned timingFactor = 0x3FF >> !timer->p->doubleSpeed;
|
||||||
if ((timer->internalDiv & timingFactor) == timingFactor) {
|
if ((timer->internalDiv & timingFactor) == timingFactor) {
|
||||||
GBAudioUpdateFrame(&timer->p->audio, &timer->p->timing);
|
GBAudioUpdateFrame(&timer->p->audio, &timer->p->timing);
|
||||||
}
|
}
|
||||||
|
@ -80,8 +80,8 @@ void GBTimerDivReset(struct GBTimer* timer) {
|
||||||
mTimingSchedule(&timer->p->timing, &timer->irq, 7 - (timer->p->cpu->executionState & 3));
|
mTimingSchedule(&timer->p->timing, &timer->irq, 7 - (timer->p->cpu->executionState & 3));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int timingFactor = 0x200 >> !timer->p->doubleSpeed;
|
unsigned timingFactor = 0x400 >> !timer->p->doubleSpeed;
|
||||||
if (timer->internalDiv & 0x200) {
|
if (timer->internalDiv & timingFactor) {
|
||||||
GBAudioUpdateFrame(&timer->p->audio, &timer->p->timing);
|
GBAudioUpdateFrame(&timer->p->audio, &timer->p->timing);
|
||||||
}
|
}
|
||||||
timer->p->memory.io[REG_DIV] = 0;
|
timer->p->memory.io[REG_DIV] = 0;
|
||||||
|
|
|
@ -449,10 +449,12 @@ void GBVideoWriteSTAT(struct GBVideo* video, GBRegisterSTAT value) {
|
||||||
|
|
||||||
void GBVideoWriteLYC(struct GBVideo* video, uint8_t value) {
|
void GBVideoWriteLYC(struct GBVideo* video, uint8_t value) {
|
||||||
GBRegisterSTAT oldStat = video->stat;
|
GBRegisterSTAT oldStat = video->stat;
|
||||||
video->stat = GBRegisterSTATSetLYC(video->stat, value == video->ly);
|
if (GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC])) {
|
||||||
if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
|
video->stat = GBRegisterSTATSetLYC(video->stat, value == video->ly);
|
||||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
|
||||||
GBUpdateIRQs(video->p);
|
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||||
|
GBUpdateIRQs(video->p);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
video->p->memory.io[REG_STAT] = video->stat;
|
video->p->memory.io[REG_STAT] = video->stat;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,8 +43,7 @@ static void _SoftReset(struct GBA* gba) {
|
||||||
cpu->gprs[ARM_PC] = BASE_CART0;
|
cpu->gprs[ARM_PC] = BASE_CART0;
|
||||||
}
|
}
|
||||||
_ARMSetMode(cpu, MODE_ARM);
|
_ARMSetMode(cpu, MODE_ARM);
|
||||||
int currentCycles = 0;
|
ARMWritePC(cpu);
|
||||||
ARM_WRITE_PC;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _RegisterRamReset(struct GBA* gba) {
|
static void _RegisterRamReset(struct GBA* gba) {
|
||||||
|
@ -820,7 +819,6 @@ static void _unBitPack(struct GBA* gba) {
|
||||||
in >>= sourceWidth;
|
in >>= sourceWidth;
|
||||||
if (scaled || bias & 0x80000000) {
|
if (scaled || bias & 0x80000000) {
|
||||||
scaled += bias & 0x7FFFFFFF;
|
scaled += bias & 0x7FFFFFFF;
|
||||||
scaled &= (1 << destWidth) - 1;
|
|
||||||
}
|
}
|
||||||
bitsRemaining -= sourceWidth;
|
bitsRemaining -= sourceWidth;
|
||||||
out |= scaled << bitsEaten;
|
out |= scaled << bitsEaten;
|
||||||
|
@ -832,4 +830,6 @@ static void _unBitPack(struct GBA* gba) {
|
||||||
dest += 4;
|
dest += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cpu->gprs[0] = source;
|
||||||
|
cpu->gprs[1] = dest;
|
||||||
}
|
}
|
||||||
|
|
|
@ -276,6 +276,9 @@ static void GBACheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* devic
|
||||||
struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
|
struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats;
|
||||||
if (cheats->enabled) {
|
if (cheats->enabled) {
|
||||||
_patchROM(device, gbaset);
|
_patchROM(device, gbaset);
|
||||||
|
if (gbaset->hook && !gbaset->hook->reentries) {
|
||||||
|
_addBreakpoint(device, gbaset);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
_unpatchROM(device, gbaset);
|
_unpatchROM(device, gbaset);
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,6 +152,7 @@ static bool _GBACoreInit(struct mCore* core) {
|
||||||
}
|
}
|
||||||
core->cpu = cpu;
|
core->cpu = cpu;
|
||||||
core->board = gba;
|
core->board = gba;
|
||||||
|
core->timing = &gba->timing;
|
||||||
core->debugger = NULL;
|
core->debugger = NULL;
|
||||||
core->symbolTable = NULL;
|
core->symbolTable = NULL;
|
||||||
gbacore->overrides = NULL;
|
gbacore->overrides = NULL;
|
||||||
|
|
|
@ -266,11 +266,7 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
|
||||||
memory->dmaTransferRegister &= 0xFFFF0000;
|
memory->dmaTransferRegister &= 0xFFFF0000;
|
||||||
memory->dmaTransferRegister |= memory->dmaTransferRegister >> 16;
|
memory->dmaTransferRegister |= memory->dmaTransferRegister >> 16;
|
||||||
} else {
|
} else {
|
||||||
if (sourceRegion == REGION_CART2_EX && memory->savedata.type == SAVEDATA_EEPROM) {
|
if (sourceRegion == REGION_CART2_EX && (memory->savedata.type == SAVEDATA_EEPROM || memory->savedata.type == SAVEDATA_EEPROM512)) {
|
||||||
if (memory->savedata.type == SAVEDATA_AUTODETECT) {
|
|
||||||
mLOG(GBA_MEM, INFO, "Detected EEPROM savegame");
|
|
||||||
GBASavedataInitEEPROM(&memory->savedata);
|
|
||||||
}
|
|
||||||
memory->dmaTransferRegister = GBASavedataReadEEPROM(&memory->savedata);
|
memory->dmaTransferRegister = GBASavedataReadEEPROM(&memory->savedata);
|
||||||
} else {
|
} else {
|
||||||
if (source) {
|
if (source) {
|
||||||
|
@ -282,7 +278,9 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
|
||||||
mLOG(GBA_MEM, INFO, "Detected EEPROM savegame");
|
mLOG(GBA_MEM, INFO, "Detected EEPROM savegame");
|
||||||
GBASavedataInitEEPROM(&memory->savedata);
|
GBASavedataInitEEPROM(&memory->savedata);
|
||||||
}
|
}
|
||||||
GBASavedataWriteEEPROM(&memory->savedata, memory->dmaTransferRegister, wordsRemaining);
|
if (memory->savedata.type == SAVEDATA_EEPROM512 || memory->savedata.type == SAVEDATA_EEPROM) {
|
||||||
|
GBASavedataWriteEEPROM(&memory->savedata, memory->dmaTransferRegister, wordsRemaining);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
cpu->memory.store16(cpu, dest, memory->dmaTransferRegister, 0);
|
cpu->memory.store16(cpu, dest, memory->dmaTransferRegister, 0);
|
||||||
|
|
||||||
|
|
|
@ -216,6 +216,20 @@ void GBAReset(struct ARMCore* cpu) {
|
||||||
|
|
||||||
GBASIOReset(&gba->sio);
|
GBASIOReset(&gba->sio);
|
||||||
|
|
||||||
|
bool isELF = false;
|
||||||
|
#ifdef USE_ELF
|
||||||
|
struct ELF* elf = ELFOpen(gba->romVf);
|
||||||
|
if (elf) {
|
||||||
|
isELF = true;
|
||||||
|
ELFClose(elf);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (GBAIsMB(gba->romVf) && !isELF) {
|
||||||
|
gba->romVf->seek(gba->romVf, 0, SEEK_SET);
|
||||||
|
gba->romVf->read(gba->romVf, gba->memory.wram, gba->pristineRomSize);
|
||||||
|
}
|
||||||
|
|
||||||
gba->lastJump = 0;
|
gba->lastJump = 0;
|
||||||
gba->haltPending = false;
|
gba->haltPending = false;
|
||||||
gba->idleDetectionStep = 0;
|
gba->idleDetectionStep = 0;
|
||||||
|
@ -242,8 +256,7 @@ void GBASkipBIOS(struct GBA* gba) {
|
||||||
}
|
}
|
||||||
gba->memory.io[REG_VCOUNT >> 1] = 0x7E;
|
gba->memory.io[REG_VCOUNT >> 1] = 0x7E;
|
||||||
gba->memory.io[REG_POSTFLG >> 1] = 1;
|
gba->memory.io[REG_POSTFLG >> 1] = 1;
|
||||||
int currentCycles = 0;
|
ARMWritePC(cpu);
|
||||||
ARM_WRITE_PC;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,11 +357,6 @@ bool GBALoadMB(struct GBA* gba, struct VFile* vf) {
|
||||||
}
|
}
|
||||||
gba->isPristine = true;
|
gba->isPristine = true;
|
||||||
memset(gba->memory.wram, 0, SIZE_WORKING_RAM);
|
memset(gba->memory.wram, 0, SIZE_WORKING_RAM);
|
||||||
vf->read(vf, gba->memory.wram, gba->pristineRomSize);
|
|
||||||
if (!gba->memory.wram) {
|
|
||||||
mLOG(GBA, WARN, "Couldn't map ROM");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
gba->yankedRomSize = 0;
|
gba->yankedRomSize = 0;
|
||||||
gba->memory.romSize = 0;
|
gba->memory.romSize = 0;
|
||||||
gba->memory.romMask = 0;
|
gba->memory.romMask = 0;
|
||||||
|
@ -512,7 +520,7 @@ void GBADebug(struct GBA* gba, uint16_t flags) {
|
||||||
int level = 1 << GBADebugFlagsGetLevel(gba->debugFlags);
|
int level = 1 << GBADebugFlagsGetLevel(gba->debugFlags);
|
||||||
level &= 0x1F;
|
level &= 0x1F;
|
||||||
char oolBuf[0x101];
|
char oolBuf[0x101];
|
||||||
strncpy(oolBuf, gba->debugString, sizeof(gba->debugString));
|
strncpy(oolBuf, gba->debugString, sizeof(oolBuf) - 1);
|
||||||
memset(gba->debugString, 0, sizeof(gba->debugString));
|
memset(gba->debugString, 0, sizeof(gba->debugString));
|
||||||
oolBuf[0x100] = '\0';
|
oolBuf[0x100] = '\0';
|
||||||
mLog(_mLOG_CAT_GBA_DEBUG(), level, "%s", oolBuf);
|
mLog(_mLOG_CAT_GBA_DEBUG(), level, "%s", oolBuf);
|
||||||
|
|
|
@ -169,7 +169,7 @@ void _rtcReadPins(struct GBACartridgeHardware* hw) {
|
||||||
case 1:
|
case 1:
|
||||||
if ((hw->pinState & 5) == 5) {
|
if ((hw->pinState & 5) == 5) {
|
||||||
hw->rtc.transferStep = 2;
|
hw->rtc.transferStep = 2;
|
||||||
} else {
|
} else if ((hw->pinState & 5) != 1) {
|
||||||
hw->rtc.transferStep = 0;
|
hw->rtc.transferStep = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -532,9 +532,9 @@ void GBAHardwarePlayerUpdate(struct GBA* gba) {
|
||||||
uint16_t _gbpRead(struct mKeyCallback* callback) {
|
uint16_t _gbpRead(struct mKeyCallback* callback) {
|
||||||
struct GBAGBPKeyCallback* gbpCallback = (struct GBAGBPKeyCallback*) callback;
|
struct GBAGBPKeyCallback* gbpCallback = (struct GBAGBPKeyCallback*) callback;
|
||||||
if (gbpCallback->p->gbpInputsPosted == 2) {
|
if (gbpCallback->p->gbpInputsPosted == 2) {
|
||||||
return 0x30F;
|
return 0xF0;
|
||||||
}
|
}
|
||||||
return 0x3FF;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) {
|
uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) {
|
||||||
|
|
|
@ -6,7 +6,7 @@ const uint8_t hleBios[SIZE_BIOS] = {
|
||||||
0x06, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x0b, 0x00, 0x00, 0xea,
|
0x06, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x0b, 0x00, 0x00, 0xea,
|
||||||
0xfe, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe1,
|
0xfe, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe1,
|
||||||
0x2c, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x02, 0x03, 0xa0, 0xe3,
|
0x2c, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x02, 0x03, 0xa0, 0xe3,
|
||||||
0x03, 0x10, 0xd0, 0xe5, 0xea, 0x00, 0x51, 0xe3, 0x02, 0x04, 0xa0, 0x13,
|
0x03, 0x10, 0xd0, 0xe5, 0xea, 0x00, 0x51, 0xe3, 0xec, 0x01, 0x9f, 0x15,
|
||||||
0x10, 0xff, 0x2f, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x29, 0xe1,
|
0x10, 0xff, 0x2f, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x29, 0xe1,
|
||||||
0x00, 0x00, 0x5d, 0xe3, 0x01, 0xd3, 0xa0, 0x03, 0x20, 0xd0, 0x4d, 0x02,
|
0x00, 0x00, 0x5d, 0xe3, 0x01, 0xd3, 0xa0, 0x03, 0x20, 0xd0, 0x4d, 0x02,
|
||||||
0x00, 0x58, 0x2d, 0xe9, 0x02, 0xb0, 0x5e, 0xe5, 0x9c, 0xc0, 0xa0, 0xe3,
|
0x00, 0x58, 0x2d, 0xe9, 0x02, 0xb0, 0x5e, 0xe5, 0x9c, 0xc0, 0xa0, 0xe3,
|
||||||
|
@ -48,5 +48,5 @@ const uint8_t hleBios[SIZE_BIOS] = {
|
||||||
0x03, 0xa0, 0xa0, 0xe1, 0x02, 0x00, 0x51, 0xe1, 0xf8, 0x07, 0xa1, 0xb8,
|
0x03, 0xa0, 0xa0, 0xe1, 0x02, 0x00, 0x51, 0xe1, 0xf8, 0x07, 0xa1, 0xb8,
|
||||||
0xfc, 0xff, 0xff, 0xba, 0x03, 0x00, 0x00, 0xea, 0x02, 0x00, 0x51, 0xe1,
|
0xfc, 0xff, 0xff, 0xba, 0x03, 0x00, 0x00, 0xea, 0x02, 0x00, 0x51, 0xe1,
|
||||||
0xf8, 0x07, 0xb0, 0xb8, 0xf8, 0x07, 0xa1, 0xb8, 0xfb, 0xff, 0xff, 0xba,
|
0xf8, 0x07, 0xb0, 0xb8, 0xf8, 0x07, 0xa1, 0xb8, 0xfb, 0xff, 0xff, 0xba,
|
||||||
0xf0, 0x87, 0xbd, 0xe8
|
0xf0, 0x87, 0xbd, 0xe8, 0xc0, 0x00, 0x00, 0x02
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,7 +20,7 @@ resetBase:
|
||||||
mov r0, #0x8000000
|
mov r0, #0x8000000
|
||||||
ldrb r1, [r0, #3]
|
ldrb r1, [r0, #3]
|
||||||
cmp r1, #0xEA
|
cmp r1, #0xEA
|
||||||
movne r0, #0x2000000
|
ldrne r0, =0x20000C0
|
||||||
bx r0
|
bx r0
|
||||||
.word 0
|
.word 0
|
||||||
.word 0xE129F000
|
.word 0xE129F000
|
||||||
|
@ -183,3 +183,5 @@ stmltia r1!, {r3-r10}
|
||||||
blt 0b
|
blt 0b
|
||||||
2:
|
2:
|
||||||
ldmfd sp!, {r4-r10, pc}
|
ldmfd sp!, {r4-r10, pc}
|
||||||
|
|
||||||
|
.ltorg
|
||||||
|
|
24
src/gba/io.c
24
src/gba/io.c
|
@ -728,7 +728,7 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
|
||||||
if (gba->rr && gba->rr->isPlaying(gba->rr)) {
|
if (gba->rr && gba->rr->isPlaying(gba->rr)) {
|
||||||
return 0x3FF ^ gba->rr->queryInput(gba->rr);
|
return 0x3FF ^ gba->rr->queryInput(gba->rr);
|
||||||
} else {
|
} else {
|
||||||
uint16_t input = 0x3FF;
|
uint16_t input = 0;
|
||||||
if (gba->keyCallback) {
|
if (gba->keyCallback) {
|
||||||
input = gba->keyCallback->readKeys(gba->keyCallback);
|
input = gba->keyCallback->readKeys(gba->keyCallback);
|
||||||
if (gba->keySource) {
|
if (gba->keySource) {
|
||||||
|
@ -736,16 +736,16 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
|
||||||
}
|
}
|
||||||
} else if (gba->keySource) {
|
} else if (gba->keySource) {
|
||||||
input = *gba->keySource;
|
input = *gba->keySource;
|
||||||
}
|
if (!gba->allowOpposingDirections) {
|
||||||
if (!gba->allowOpposingDirections) {
|
unsigned rl = input & 0x030;
|
||||||
unsigned rl = input & 0x030;
|
unsigned ud = input & 0x0C0;
|
||||||
unsigned ud = input & 0x0C0;
|
input &= 0x30F;
|
||||||
input &= 0x30F;
|
if (rl != 0x030) {
|
||||||
if (rl != 0x030) {
|
input |= rl;
|
||||||
input |= rl;
|
}
|
||||||
}
|
if (ud != 0x0C0) {
|
||||||
if (ud != 0x0C0) {
|
input |= ud;
|
||||||
input |= ud;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (gba->rr && gba->rr->isRecording(gba->rr)) {
|
if (gba->rr && gba->rr->isRecording(gba->rr)) {
|
||||||
|
@ -841,7 +841,6 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
|
||||||
case REG_SOUND4CNT_LO:
|
case REG_SOUND4CNT_LO:
|
||||||
case REG_SOUND4CNT_HI:
|
case REG_SOUND4CNT_HI:
|
||||||
case REG_SOUNDCNT_LO:
|
case REG_SOUNDCNT_LO:
|
||||||
case REG_SOUNDCNT_HI:
|
|
||||||
if (!GBAudioEnableIsEnable(gba->memory.io[REG_SOUNDCNT_X >> 1])) {
|
if (!GBAudioEnableIsEnable(gba->memory.io[REG_SOUNDCNT_X >> 1])) {
|
||||||
// TODO: Is writing allowed when the circuit is disabled?
|
// TODO: Is writing allowed when the circuit is disabled?
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -858,6 +857,7 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
|
||||||
case REG_WINOUT:
|
case REG_WINOUT:
|
||||||
case REG_BLDCNT:
|
case REG_BLDCNT:
|
||||||
case REG_BLDALPHA:
|
case REG_BLDALPHA:
|
||||||
|
case REG_SOUNDCNT_HI:
|
||||||
case REG_SOUNDCNT_X:
|
case REG_SOUNDCNT_X:
|
||||||
case REG_WAVE_RAM0_LO:
|
case REG_WAVE_RAM0_LO:
|
||||||
case REG_WAVE_RAM0_HI:
|
case REG_WAVE_RAM0_HI:
|
||||||
|
|
|
@ -558,7 +558,7 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
||||||
break;
|
break;
|
||||||
case REGION_CART2_EX:
|
case REGION_CART2_EX:
|
||||||
wait = memory->waitstatesNonseq16[address >> BASE_OFFSET];
|
wait = memory->waitstatesNonseq16[address >> BASE_OFFSET];
|
||||||
if (memory->savedata.type == SAVEDATA_EEPROM) {
|
if (memory->savedata.type == SAVEDATA_EEPROM || memory->savedata.type == SAVEDATA_EEPROM512) {
|
||||||
value = GBASavedataReadEEPROM(&memory->savedata);
|
value = GBASavedataReadEEPROM(&memory->savedata);
|
||||||
} else if ((address & (SIZE_CART0 - 1)) < memory->romSize) {
|
} else if ((address & (SIZE_CART0 - 1)) < memory->romSize) {
|
||||||
LOAD_16(value, address & (SIZE_CART0 - 2), memory->rom);
|
LOAD_16(value, address & (SIZE_CART0 - 2), memory->rom);
|
||||||
|
@ -892,7 +892,11 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
|
||||||
mLOG(GBA_MEM, INFO, "Detected EEPROM savegame");
|
mLOG(GBA_MEM, INFO, "Detected EEPROM savegame");
|
||||||
GBASavedataInitEEPROM(&memory->savedata);
|
GBASavedataInitEEPROM(&memory->savedata);
|
||||||
}
|
}
|
||||||
GBASavedataWriteEEPROM(&memory->savedata, value, 1);
|
if (memory->savedata.type == SAVEDATA_EEPROM512 || memory->savedata.type == SAVEDATA_EEPROM) {
|
||||||
|
GBASavedataWriteEEPROM(&memory->savedata, value, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mLOG(GBA_MEM, GAME_ERROR, "Bad memory Store16: 0x%08X", address);
|
||||||
break;
|
break;
|
||||||
case REGION_CART_SRAM:
|
case REGION_CART_SRAM:
|
||||||
case REGION_CART_SRAM_MIRROR:
|
case REGION_CART_SRAM_MIRROR:
|
||||||
|
|
|
@ -223,6 +223,9 @@ bool GBAOverrideFind(const struct Configuration* config, struct GBACartridgeOver
|
||||||
} else if (strcasecmp(savetype, "EEPROM") == 0) {
|
} else if (strcasecmp(savetype, "EEPROM") == 0) {
|
||||||
found = true;
|
found = true;
|
||||||
override->savetype = SAVEDATA_EEPROM;
|
override->savetype = SAVEDATA_EEPROM;
|
||||||
|
} else if (strcasecmp(savetype, "EEPROM512") == 0) {
|
||||||
|
found = true;
|
||||||
|
override->savetype = SAVEDATA_EEPROM512;
|
||||||
} else if (strcasecmp(savetype, "FLASH512") == 0) {
|
} else if (strcasecmp(savetype, "FLASH512") == 0) {
|
||||||
found = true;
|
found = true;
|
||||||
override->savetype = SAVEDATA_FLASH512;
|
override->savetype = SAVEDATA_FLASH512;
|
||||||
|
@ -267,6 +270,9 @@ void GBAOverrideSave(struct Configuration* config, const struct GBACartridgeOver
|
||||||
case SAVEDATA_EEPROM:
|
case SAVEDATA_EEPROM:
|
||||||
savetype = "EEPROM";
|
savetype = "EEPROM";
|
||||||
break;
|
break;
|
||||||
|
case SAVEDATA_EEPROM512:
|
||||||
|
savetype = "EEPROM512";
|
||||||
|
break;
|
||||||
case SAVEDATA_FLASH512:
|
case SAVEDATA_FLASH512:
|
||||||
savetype = "FLASH512";
|
savetype = "FLASH512";
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -158,10 +158,10 @@ void GBAVideoCacheWriteVideoRegister(struct mCacheSet* cache, uint32_t address,
|
||||||
switch (address) {
|
switch (address) {
|
||||||
case REG_DISPCNT:
|
case REG_DISPCNT:
|
||||||
GBAVideoCacheWriteDISPCNT(cache, value);
|
GBAVideoCacheWriteDISPCNT(cache, value);
|
||||||
GBAVideoCacheWriteBGCNT(cache, 0, (uint16_t) mMapCacheSetGetPointer(&cache->maps, 0)->context);
|
GBAVideoCacheWriteBGCNT(cache, 0, (uintptr_t) mMapCacheSetGetPointer(&cache->maps, 0)->context);
|
||||||
GBAVideoCacheWriteBGCNT(cache, 1, (uint16_t) mMapCacheSetGetPointer(&cache->maps, 1)->context);
|
GBAVideoCacheWriteBGCNT(cache, 1, (uintptr_t) mMapCacheSetGetPointer(&cache->maps, 1)->context);
|
||||||
GBAVideoCacheWriteBGCNT(cache, 2, (uint16_t) mMapCacheSetGetPointer(&cache->maps, 2)->context);
|
GBAVideoCacheWriteBGCNT(cache, 2, (uintptr_t) mMapCacheSetGetPointer(&cache->maps, 2)->context);
|
||||||
GBAVideoCacheWriteBGCNT(cache, 3, (uint16_t) mMapCacheSetGetPointer(&cache->maps, 3)->context);
|
GBAVideoCacheWriteBGCNT(cache, 3, (uintptr_t) mMapCacheSetGetPointer(&cache->maps, 3)->context);
|
||||||
break;
|
break;
|
||||||
case REG_BG0CNT:
|
case REG_BG0CNT:
|
||||||
GBAVideoCacheWriteBGCNT(cache, 0, value);
|
GBAVideoCacheWriteBGCNT(cache, 0, value);
|
||||||
|
|
|
@ -77,6 +77,9 @@ void GBASavedataDeinit(struct GBASavedata* savedata) {
|
||||||
case SAVEDATA_EEPROM:
|
case SAVEDATA_EEPROM:
|
||||||
mappedMemoryFree(savedata->data, SIZE_CART_EEPROM);
|
mappedMemoryFree(savedata->data, SIZE_CART_EEPROM);
|
||||||
break;
|
break;
|
||||||
|
case SAVEDATA_EEPROM512:
|
||||||
|
mappedMemoryFree(savedata->data, SIZE_CART_EEPROM512);
|
||||||
|
break;
|
||||||
case SAVEDATA_FORCE_NONE:
|
case SAVEDATA_FORCE_NONE:
|
||||||
case SAVEDATA_AUTODETECT:
|
case SAVEDATA_AUTODETECT:
|
||||||
break;
|
break;
|
||||||
|
@ -127,6 +130,8 @@ bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) {
|
||||||
return out->write(out, savedata->data, SIZE_CART_FLASH1M) == SIZE_CART_FLASH1M;
|
return out->write(out, savedata->data, SIZE_CART_FLASH1M) == SIZE_CART_FLASH1M;
|
||||||
case SAVEDATA_EEPROM:
|
case SAVEDATA_EEPROM:
|
||||||
return out->write(out, savedata->data, SIZE_CART_EEPROM) == SIZE_CART_EEPROM;
|
return out->write(out, savedata->data, SIZE_CART_EEPROM) == SIZE_CART_EEPROM;
|
||||||
|
case SAVEDATA_EEPROM512:
|
||||||
|
return out->write(out, savedata->data, SIZE_CART_EEPROM512) == SIZE_CART_EEPROM512;
|
||||||
case SAVEDATA_AUTODETECT:
|
case SAVEDATA_AUTODETECT:
|
||||||
case SAVEDATA_FORCE_NONE:
|
case SAVEDATA_FORCE_NONE:
|
||||||
return true;
|
return true;
|
||||||
|
@ -152,7 +157,9 @@ size_t GBASavedataSize(const struct GBASavedata* savedata) {
|
||||||
case SAVEDATA_FLASH1M:
|
case SAVEDATA_FLASH1M:
|
||||||
return SIZE_CART_FLASH1M;
|
return SIZE_CART_FLASH1M;
|
||||||
case SAVEDATA_EEPROM:
|
case SAVEDATA_EEPROM:
|
||||||
return (savedata->vf && savedata->vf->size(savedata->vf) == SIZE_CART_EEPROM512) ? SIZE_CART_EEPROM512 : SIZE_CART_EEPROM;
|
return SIZE_CART_EEPROM;
|
||||||
|
case SAVEDATA_EEPROM512:
|
||||||
|
return SIZE_CART_EEPROM512;
|
||||||
case SAVEDATA_FORCE_NONE:
|
case SAVEDATA_FORCE_NONE:
|
||||||
return 0;
|
return 0;
|
||||||
case SAVEDATA_AUTODETECT:
|
case SAVEDATA_AUTODETECT:
|
||||||
|
@ -218,6 +225,8 @@ void GBASavedataForceType(struct GBASavedata* savedata, enum SavedataType type)
|
||||||
GBASavedataInitFlash(savedata);
|
GBASavedataInitFlash(savedata);
|
||||||
break;
|
break;
|
||||||
case SAVEDATA_EEPROM:
|
case SAVEDATA_EEPROM:
|
||||||
|
case SAVEDATA_EEPROM512:
|
||||||
|
savedata->type = type;
|
||||||
GBASavedataInitEEPROM(savedata);
|
GBASavedataInitEEPROM(savedata);
|
||||||
break;
|
break;
|
||||||
case SAVEDATA_SRAM:
|
case SAVEDATA_SRAM:
|
||||||
|
@ -263,22 +272,23 @@ void GBASavedataInitFlash(struct GBASavedata* savedata) {
|
||||||
|
|
||||||
void GBASavedataInitEEPROM(struct GBASavedata* savedata) {
|
void GBASavedataInitEEPROM(struct GBASavedata* savedata) {
|
||||||
if (savedata->type == SAVEDATA_AUTODETECT) {
|
if (savedata->type == SAVEDATA_AUTODETECT) {
|
||||||
savedata->type = SAVEDATA_EEPROM;
|
savedata->type = SAVEDATA_EEPROM512;
|
||||||
} else {
|
} else if (savedata->type != SAVEDATA_EEPROM512 && savedata->type != SAVEDATA_EEPROM) {
|
||||||
mLOG(GBA_SAVE, WARN, "Can't re-initialize savedata");
|
mLOG(GBA_SAVE, WARN, "Can't re-initialize savedata");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int32_t eepromSize = SIZE_CART_EEPROM512;
|
int32_t eepromSize = SIZE_CART_EEPROM512;
|
||||||
|
if (savedata->type == SAVEDATA_EEPROM) {
|
||||||
|
eepromSize = SIZE_CART_EEPROM;
|
||||||
|
}
|
||||||
off_t end;
|
off_t end;
|
||||||
if (!savedata->vf) {
|
if (!savedata->vf) {
|
||||||
end = 0;
|
end = 0;
|
||||||
savedata->data = anonymousMemoryMap(SIZE_CART_EEPROM);
|
savedata->data = anonymousMemoryMap(SIZE_CART_EEPROM);
|
||||||
} else {
|
} else {
|
||||||
end = savedata->vf->size(savedata->vf);
|
end = savedata->vf->size(savedata->vf);
|
||||||
if (end < SIZE_CART_EEPROM512) {
|
if (end < eepromSize) {
|
||||||
savedata->vf->truncate(savedata->vf, SIZE_CART_EEPROM512);
|
savedata->vf->truncate(savedata->vf, eepromSize);
|
||||||
} else if (end > SIZE_CART_EEPROM512) {
|
|
||||||
eepromSize = SIZE_CART_EEPROM;
|
|
||||||
}
|
}
|
||||||
savedata->data = savedata->vf->map(savedata->vf, eepromSize, savedata->mapMode);
|
savedata->data = savedata->vf->map(savedata->vf, eepromSize, savedata->mapMode);
|
||||||
}
|
}
|
||||||
|
@ -420,13 +430,21 @@ static void _ensureEeprom(struct GBASavedata* savedata, uint32_t size) {
|
||||||
if (size < SIZE_CART_EEPROM512) {
|
if (size < SIZE_CART_EEPROM512) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!savedata->vf || savedata->vf->size(savedata->vf) > SIZE_CART_EEPROM512) {
|
if (savedata->type == SAVEDATA_EEPROM) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
savedata->type = SAVEDATA_EEPROM;
|
||||||
|
if (!savedata->vf) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_EEPROM512);
|
savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_EEPROM512);
|
||||||
savedata->vf->truncate(savedata->vf, SIZE_CART_EEPROM);
|
if (savedata->vf->size(savedata->vf) < SIZE_CART_EEPROM) {
|
||||||
savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_EEPROM, savedata->mapMode);
|
savedata->vf->truncate(savedata->vf, SIZE_CART_EEPROM);
|
||||||
memset(&savedata->data[SIZE_CART_EEPROM512], 0xFF, SIZE_CART_EEPROM - SIZE_CART_EEPROM512);
|
savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_EEPROM, savedata->mapMode);
|
||||||
|
memset(&savedata->data[SIZE_CART_EEPROM512], 0xFF, SIZE_CART_EEPROM - SIZE_CART_EEPROM512);
|
||||||
|
} else {
|
||||||
|
savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_EEPROM, savedata->mapMode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32_t writeSize) {
|
void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32_t writeSize) {
|
||||||
|
@ -586,7 +604,7 @@ void _flashSwitchBank(struct GBASavedata* savedata, int bank) {
|
||||||
savedata->type = SAVEDATA_FLASH1M;
|
savedata->type = SAVEDATA_FLASH1M;
|
||||||
if (savedata->vf) {
|
if (savedata->vf) {
|
||||||
savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_FLASH512);
|
savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_FLASH512);
|
||||||
if (savedata->vf->size(savedata->vf) == SIZE_CART_FLASH512) {
|
if (savedata->vf->size(savedata->vf) < SIZE_CART_FLASH1M) {
|
||||||
savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M);
|
savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M);
|
||||||
savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_FLASH1M, MAP_WRITE);
|
savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_FLASH1M, MAP_WRITE);
|
||||||
memset(&savedata->data[SIZE_CART_FLASH512], 0xFF, SIZE_CART_FLASH512);
|
memset(&savedata->data[SIZE_CART_FLASH512], 0xFF, SIZE_CART_FLASH512);
|
||||||
|
|
|
@ -21,7 +21,7 @@ static struct CLIDebuggerCommandSummary _lr35902Commands[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void _printFlags(struct CLIDebuggerBackend* be, union FlagRegister f) {
|
static inline void _printFlags(struct CLIDebuggerBackend* be, union FlagRegister f) {
|
||||||
be->printf(be, "[%c%c%c%c]\n",
|
be->printf(be, "F: [%c%c%c%c]\n",
|
||||||
f.z ? 'Z' : '-',
|
f.z ? 'Z' : '-',
|
||||||
f.n ? 'N' : '-',
|
f.n ? 'N' : '-',
|
||||||
f.h ? 'H' : '-',
|
f.h ? 'H' : '-',
|
||||||
|
@ -82,16 +82,16 @@ static inline uint16_t _printLine(struct CLIDebugger* debugger, uint16_t address
|
||||||
static void _printStatus(struct CLIDebuggerSystem* debugger) {
|
static void _printStatus(struct CLIDebuggerSystem* debugger) {
|
||||||
struct CLIDebuggerBackend* be = debugger->p->backend;
|
struct CLIDebuggerBackend* be = debugger->p->backend;
|
||||||
struct LR35902Core* cpu = debugger->p->d.core->cpu;
|
struct LR35902Core* cpu = debugger->p->d.core->cpu;
|
||||||
be->printf(be, "A: %02X F: %02X (AF: %04X)\n", cpu->a, cpu->f.packed, cpu->af);
|
be->printf(be, "A: %02X F: %02X (AF: %04X)\n", cpu->a, cpu->f.packed, cpu->af);
|
||||||
be->printf(be, "B: %02X C: %02X (BC: %04X)\n", cpu->b, cpu->c, cpu->bc);
|
be->printf(be, "B: %02X C: %02X (BC: %04X)\n", cpu->b, cpu->c, cpu->bc);
|
||||||
be->printf(be, "D: %02X E: %02X (DE: %04X)\n", cpu->d, cpu->e, cpu->de);
|
be->printf(be, "D: %02X E: %02X (DE: %04X)\n", cpu->d, cpu->e, cpu->de);
|
||||||
be->printf(be, "H: %02X L: %02X (HL: %04X)\n", cpu->h, cpu->l, cpu->hl);
|
be->printf(be, "H: %02X L: %02X (HL: %04X)\n", cpu->h, cpu->l, cpu->hl);
|
||||||
be->printf(be, "PC: %04X SP: %04X\n", cpu->pc, cpu->sp);
|
be->printf(be, "PC: %04X SP: %04X\n", cpu->pc, cpu->sp);
|
||||||
|
|
||||||
struct LR35902Debugger* platDebugger = (struct LR35902Debugger*) debugger->p->d.platform;
|
struct LR35902Debugger* platDebugger = (struct LR35902Debugger*) debugger->p->d.platform;
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; platDebugger->segments[i].name; ++i) {
|
for (i = 0; platDebugger->segments[i].name; ++i) {
|
||||||
be->printf(be, "%s%s: %02X", i ? " " : "", platDebugger->segments[i].name, cpu->memory.currentSegment(cpu, platDebugger->segments[i].start));
|
be->printf(be, "%s%s: %02X", i ? " " : "", platDebugger->segments[i].name, cpu->memory.currentSegment(cpu, platDebugger->segments[i].start));
|
||||||
}
|
}
|
||||||
if (i) {
|
if (i) {
|
||||||
be->printf(be, "\n");
|
be->printf(be, "\n");
|
||||||
|
|
|
@ -213,10 +213,10 @@ static void LR35902DebuggerTrace(struct mDebuggerPlatform* d, char* out, size_t*
|
||||||
disPtr += 2;
|
disPtr += 2;
|
||||||
LR35902Disassemble(&info, disPtr, sizeof(disassembly) - (disPtr - disassembly));
|
LR35902Disassemble(&info, disPtr, sizeof(disassembly) - (disPtr - disassembly));
|
||||||
|
|
||||||
*length = snprintf(out, *length, "A: %02X F: %02X B: %02X C: %02X D: %02X E: %02X H: %02X L: %02X SP: %04X PC: %04X | %s",
|
*length = snprintf(out, *length, "A: %02X F: %02X B: %02X C: %02X D: %02X E: %02X H: %02X L: %02X SP: %04X PC: %02X:%04X | %s",
|
||||||
cpu->a, cpu->f.packed, cpu->b, cpu->c,
|
cpu->a, cpu->f.packed, cpu->b, cpu->c,
|
||||||
cpu->d, cpu->e, cpu->h, cpu->l,
|
cpu->d, cpu->e, cpu->h, cpu->l,
|
||||||
cpu->sp, cpu->pc, disassembly);
|
cpu->sp, cpu->memory.currentSegment(cpu, cpu->pc), cpu->pc, disassembly);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LR35902DebuggerGetRegister(struct mDebuggerPlatform* d, const char* name, int32_t* value) {
|
bool LR35902DebuggerGetRegister(struct mDebuggerPlatform* d, const char* name, int32_t* value) {
|
||||||
|
|
|
@ -84,7 +84,8 @@ static enum {
|
||||||
} hasSound;
|
} hasSound;
|
||||||
|
|
||||||
// TODO: Move into context
|
// TODO: Move into context
|
||||||
static void* outputBuffer;
|
static color_t* outputBuffer = NULL;
|
||||||
|
static color_t* screenshotBuffer = NULL;
|
||||||
static struct mAVStream stream;
|
static struct mAVStream stream;
|
||||||
static int16_t* audioLeft = 0;
|
static int16_t* audioLeft = 0;
|
||||||
static size_t audioPos = 0;
|
static size_t audioPos = 0;
|
||||||
|
@ -135,6 +136,11 @@ static void _cleanup(void) {
|
||||||
|
|
||||||
if (outputBuffer) {
|
if (outputBuffer) {
|
||||||
linearFree(outputBuffer);
|
linearFree(outputBuffer);
|
||||||
|
outputBuffer = NULL;
|
||||||
|
}
|
||||||
|
if (screenshotBuffer) {
|
||||||
|
linearFree(screenshotBuffer);
|
||||||
|
screenshotBuffer = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
C3D_RenderTargetDelete(topScreen[0]);
|
C3D_RenderTargetDelete(topScreen[0]);
|
||||||
|
@ -276,7 +282,7 @@ static void _setup(struct mGUIRunner* runner) {
|
||||||
_map3DSKey(&runner->core->inputMap, KEY_L, GBA_KEY_L);
|
_map3DSKey(&runner->core->inputMap, KEY_L, GBA_KEY_L);
|
||||||
_map3DSKey(&runner->core->inputMap, KEY_R, GBA_KEY_R);
|
_map3DSKey(&runner->core->inputMap, KEY_R, GBA_KEY_R);
|
||||||
|
|
||||||
outputBuffer = linearMemAlign(256 * 224 * 2, 0x80);
|
outputBuffer = linearMemAlign(256 * 224 * sizeof(color_t), 0x80);
|
||||||
runner->core->setVideoBuffer(runner->core, outputBuffer, 256);
|
runner->core->setVideoBuffer(runner->core, outputBuffer, 256);
|
||||||
|
|
||||||
unsigned mode;
|
unsigned mode;
|
||||||
|
@ -547,7 +553,7 @@ static void _drawFrame(struct mGUIRunner* runner, bool faded) {
|
||||||
|
|
||||||
GSPGPU_FlushDataCache(outputBuffer, 256 * VIDEO_VERTICAL_PIXELS * 2);
|
GSPGPU_FlushDataCache(outputBuffer, 256 * VIDEO_VERTICAL_PIXELS * 2);
|
||||||
C3D_SyncDisplayTransfer(
|
C3D_SyncDisplayTransfer(
|
||||||
outputBuffer, GX_BUFFER_DIM(256, VIDEO_VERTICAL_PIXELS),
|
(u32*) outputBuffer, GX_BUFFER_DIM(256, VIDEO_VERTICAL_PIXELS),
|
||||||
tex->data, GX_BUFFER_DIM(256, 256),
|
tex->data, GX_BUFFER_DIM(256, 256),
|
||||||
GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB565) |
|
GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB565) |
|
||||||
GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB565) |
|
GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB565) |
|
||||||
|
@ -564,22 +570,22 @@ static void _drawFrame(struct mGUIRunner* runner, bool faded) {
|
||||||
static void _drawScreenshot(struct mGUIRunner* runner, const color_t* pixels, unsigned width, unsigned height, bool faded) {
|
static void _drawScreenshot(struct mGUIRunner* runner, const color_t* pixels, unsigned width, unsigned height, bool faded) {
|
||||||
C3D_Tex* tex = &outputTexture;
|
C3D_Tex* tex = &outputTexture;
|
||||||
|
|
||||||
color_t* newPixels = linearMemAlign(256 * height * sizeof(color_t), 0x100);
|
if (!screenshotBuffer) {
|
||||||
|
screenshotBuffer = linearMemAlign(256 * 224 * sizeof(color_t), 0x80);
|
||||||
|
}
|
||||||
unsigned y;
|
unsigned y;
|
||||||
for (y = 0; y < height; ++y) {
|
for (y = 0; y < height; ++y) {
|
||||||
memcpy(&newPixels[y * 256], &pixels[y * width], width * sizeof(color_t));
|
memcpy(&screenshotBuffer[y * 256], &pixels[y * width], width * sizeof(color_t));
|
||||||
memset(&newPixels[y * 256 + width], 0, (256 - width) * sizeof(color_t));
|
memset(&screenshotBuffer[y * 256 + width], 0, (256 - width) * sizeof(color_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
GSPGPU_FlushDataCache(newPixels, 256 * height * sizeof(u32));
|
GSPGPU_FlushDataCache(screenshotBuffer, 256 * height * sizeof(color_t));
|
||||||
C3D_SyncDisplayTransfer(
|
C3D_SyncDisplayTransfer(
|
||||||
(u32*) newPixels, GX_BUFFER_DIM(256, height),
|
(u32*) screenshotBuffer, GX_BUFFER_DIM(256, height),
|
||||||
tex->data, GX_BUFFER_DIM(256, 256),
|
tex->data, GX_BUFFER_DIM(256, 256),
|
||||||
GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB565) |
|
GX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGB565) |
|
||||||
GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB565) |
|
GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGB565) |
|
||||||
GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_FLIP_VERT(1));
|
GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_FLIP_VERT(1));
|
||||||
linearFree(newPixels);
|
|
||||||
|
|
||||||
_drawTex(runner->core, faded);
|
_drawTex(runner->core, faded);
|
||||||
}
|
}
|
||||||
|
|
|
@ -490,6 +490,7 @@ bool retro_load_game(const struct retro_game_info* game) {
|
||||||
core->setPeripheral(core, mPERIPH_RUMBLE, &rumble);
|
core->setPeripheral(core, mPERIPH_RUMBLE, &rumble);
|
||||||
|
|
||||||
savedata = anonymousMemoryMap(SIZE_CART_FLASH1M);
|
savedata = anonymousMemoryMap(SIZE_CART_FLASH1M);
|
||||||
|
memset(savedata, 0xFF, SIZE_CART_FLASH1M);
|
||||||
struct VFile* save = VFileFromMemory(savedata, SIZE_CART_FLASH1M);
|
struct VFile* save = VFileFromMemory(savedata, SIZE_CART_FLASH1M);
|
||||||
|
|
||||||
_reloadSettings();
|
_reloadSettings();
|
||||||
|
@ -657,6 +658,7 @@ void retro_cheat_set(unsigned index, bool enabled, const char* code) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
cheatSet->refresh(cheatSet, device);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned retro_get_region(void) {
|
unsigned retro_get_region(void) {
|
||||||
|
|
|
@ -25,18 +25,18 @@
|
||||||
#include <vita2d.h>
|
#include <vita2d.h>
|
||||||
|
|
||||||
static void _drawStart(void) {
|
static void _drawStart(void) {
|
||||||
vita2d_set_vblank_wait(false);
|
static int vcount = 0;
|
||||||
|
extern bool frameLimiter;
|
||||||
|
int oldVCount = vcount;
|
||||||
|
vcount = sceDisplayGetVcount();
|
||||||
|
vita2d_set_vblank_wait(frameLimiter && vcount + 1 >= oldVCount);
|
||||||
vita2d_start_drawing();
|
vita2d_start_drawing();
|
||||||
vita2d_clear_screen();
|
vita2d_clear_screen();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _drawEnd(void) {
|
static void _drawEnd(void) {
|
||||||
static int vcount = 0;
|
|
||||||
extern bool frameLimiter;
|
|
||||||
int oldVCount = vcount;
|
|
||||||
vita2d_end_drawing();
|
vita2d_end_drawing();
|
||||||
vcount = sceDisplayGetVcount();
|
vita2d_wait_rendering_done();
|
||||||
vita2d_set_vblank_wait(frameLimiter && vcount + 1 >= oldVCount);
|
|
||||||
vita2d_swap_buffers();
|
vita2d_swap_buffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ void free(void*);
|
||||||
|
|
||||||
#include "flags.h"
|
#include "flags.h"
|
||||||
|
|
||||||
|
#include <mgba/core/blip_buf.h>
|
||||||
#include <mgba/core/cache-set.h>
|
#include <mgba/core/cache-set.h>
|
||||||
#include <mgba/core/core.h>
|
#include <mgba/core/core.h>
|
||||||
#include <mgba/core/map-cache.h>
|
#include <mgba/core/map-cache.h>
|
||||||
|
|
|
@ -20,6 +20,7 @@ ffi.set_source("mgba._pylib", """
|
||||||
#define inline
|
#define inline
|
||||||
#include "flags.h"
|
#include "flags.h"
|
||||||
#define OPAQUE_THREADING
|
#define OPAQUE_THREADING
|
||||||
|
#include <mgba/core/blip_buf.h>
|
||||||
#include <mgba/core/cache-set.h>
|
#include <mgba/core/cache-set.h>
|
||||||
#include <mgba-util/common.h>
|
#include <mgba-util/common.h>
|
||||||
#include <mgba/core/core.h>
|
#include <mgba/core/core.h>
|
||||||
|
|
|
@ -65,7 +65,6 @@ bool mPythonScriptEngineInit(struct mScriptEngine* se, struct mScriptBridge* sb)
|
||||||
}
|
}
|
||||||
|
|
||||||
void mPythonScriptEngineDeinit(struct mScriptEngine* se) {
|
void mPythonScriptEngineDeinit(struct mScriptEngine* se) {
|
||||||
struct mPythonScriptEngine* engine = (struct mPythonScriptEngine*) se;
|
|
||||||
free(se);
|
free(se);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +75,7 @@ bool mPythonScriptEngineIsScript(struct mScriptEngine* se, const char* name, str
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mPythonScriptEngineLoadScript(struct mScriptEngine* se, const char* name, struct VFile* vf) {
|
bool mPythonScriptEngineLoadScript(struct mScriptEngine* se, const char* name, struct VFile* vf) {
|
||||||
struct mPythonScriptEngine* engine = (struct mPythonScriptEngine*) se;
|
UNUSED(se);
|
||||||
return mPythonLoadScript(name, vf);
|
return mPythonLoadScript(name, vf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +93,7 @@ void mPythonScriptEngineRun(struct mScriptEngine* se) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mPythonScriptEngineLookupSymbol(struct mScriptEngine* se, const char* name, int32_t* out) {
|
bool mPythonScriptEngineLookupSymbol(struct mScriptEngine* se, const char* name, int32_t* out) {
|
||||||
struct mPythonScriptEngine* engine = (struct mPythonScriptEngine*) se;
|
UNUSED(se);
|
||||||
return mPythonLookupSymbol(name, out);
|
return mPythonLookupSymbol(name, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
# Copyright (c) 2013-2018 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/.
|
||||||
|
from ._pylib import ffi, lib # pylint: disable=no-name-in-module
|
||||||
|
|
||||||
|
|
||||||
|
class Buffer(object):
|
||||||
|
def __init__(self, native, internal_rate):
|
||||||
|
self._native = native
|
||||||
|
self._internal_rate = internal_rate
|
||||||
|
|
||||||
|
@property
|
||||||
|
def available(self):
|
||||||
|
return lib.blip_samples_avail(self._native)
|
||||||
|
|
||||||
|
def set_rate(self, rate):
|
||||||
|
lib.blip_set_rates(self._native, self._internal_rate, rate)
|
||||||
|
|
||||||
|
def read(self, samples):
|
||||||
|
buffer = ffi.new("short[%i]" % samples)
|
||||||
|
count = self.read_into(buffer, samples, 1, 0)
|
||||||
|
return buffer[:count]
|
||||||
|
|
||||||
|
def read_into(self, buffer, samples, channels=1, interleave=0):
|
||||||
|
return lib.blip_read_samples(self._native, ffi.addressof(buffer, interleave), samples, channels == 2)
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
lib.blip_clear(self._native)
|
||||||
|
|
||||||
|
|
||||||
|
class StereoBuffer(object):
|
||||||
|
def __init__(self, left, right):
|
||||||
|
self._left = left
|
||||||
|
self._right = right
|
||||||
|
|
||||||
|
@property
|
||||||
|
def available(self):
|
||||||
|
return min(self._left.available, self._right.available)
|
||||||
|
|
||||||
|
def set_rate(self, rate):
|
||||||
|
self._left.set_rate(rate)
|
||||||
|
self._right.set_rate(rate)
|
||||||
|
|
||||||
|
def read(self, samples):
|
||||||
|
buffer = ffi.new("short[%i]" % (2 * samples))
|
||||||
|
count = self.read_into(buffer, samples)
|
||||||
|
return buffer[0:2 * count]
|
||||||
|
|
||||||
|
def read_into(self, buffer, samples):
|
||||||
|
samples = self._left.read_into(buffer, samples, 2, 0)
|
||||||
|
return self._right.read_into(buffer, samples, 2, 1)
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self._left.clear()
|
||||||
|
self._right.clear()
|
|
@ -4,7 +4,7 @@
|
||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
# 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/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
from ._pylib import ffi, lib # pylint: disable=no-name-in-module
|
from ._pylib import ffi, lib # pylint: disable=no-name-in-module
|
||||||
from . import tile
|
from . import tile, audio
|
||||||
from cached_property import cached_property
|
from cached_property import cached_property
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
|
@ -173,51 +173,81 @@ class Core(object):
|
||||||
def _load(self):
|
def _load(self):
|
||||||
self._was_reset = True
|
self._was_reset = True
|
||||||
|
|
||||||
|
@protected
|
||||||
def load_file(self, path):
|
def load_file(self, path):
|
||||||
return bool(lib.mCoreLoadFile(self._core, path.encode('UTF-8')))
|
return bool(lib.mCoreLoadFile(self._core, path.encode('UTF-8')))
|
||||||
|
|
||||||
def is_rom(self, vfile):
|
def is_rom(self, vfile):
|
||||||
return bool(self._core.isROM(vfile.handle))
|
return bool(self._core.isROM(vfile.handle))
|
||||||
|
|
||||||
|
@protected
|
||||||
def load_rom(self, vfile):
|
def load_rom(self, vfile):
|
||||||
return bool(self._core.loadROM(self._core, vfile.handle))
|
return bool(self._core.loadROM(self._core, vfile.handle))
|
||||||
|
|
||||||
|
@protected
|
||||||
def load_bios(self, vfile, id=0):
|
def load_bios(self, vfile, id=0):
|
||||||
return bool(self._core.loadBIOS(self._core, vfile.handle, id))
|
return bool(self._core.loadBIOS(self._core, vfile.handle, id))
|
||||||
|
|
||||||
|
@protected
|
||||||
def load_save(self, vfile):
|
def load_save(self, vfile):
|
||||||
return bool(self._core.loadSave(self._core, vfile.handle))
|
return bool(self._core.loadSave(self._core, vfile.handle))
|
||||||
|
|
||||||
|
@protected
|
||||||
def load_temporary_save(self, vfile):
|
def load_temporary_save(self, vfile):
|
||||||
return bool(self._core.loadTemporarySave(self._core, vfile.handle))
|
return bool(self._core.loadTemporarySave(self._core, vfile.handle))
|
||||||
|
|
||||||
|
@protected
|
||||||
def load_patch(self, vfile):
|
def load_patch(self, vfile):
|
||||||
return bool(self._core.loadPatch(self._core, vfile.handle))
|
return bool(self._core.loadPatch(self._core, vfile.handle))
|
||||||
|
|
||||||
|
@protected
|
||||||
def load_config(self, config):
|
def load_config(self, config):
|
||||||
lib.mCoreLoadForeignConfig(self._core, config._native)
|
lib.mCoreLoadForeignConfig(self._core, config._native)
|
||||||
|
|
||||||
|
@protected
|
||||||
def autoload_save(self):
|
def autoload_save(self):
|
||||||
return bool(lib.mCoreAutoloadSave(self._core))
|
return bool(lib.mCoreAutoloadSave(self._core))
|
||||||
|
|
||||||
|
@protected
|
||||||
def autoload_patch(self):
|
def autoload_patch(self):
|
||||||
return bool(lib.mCoreAutoloadPatch(self._core))
|
return bool(lib.mCoreAutoloadPatch(self._core))
|
||||||
|
|
||||||
|
@protected
|
||||||
def autoload_cheats(self):
|
def autoload_cheats(self):
|
||||||
return bool(lib.mCoreAutoloadCheats(self._core))
|
return bool(lib.mCoreAutoloadCheats(self._core))
|
||||||
|
|
||||||
|
@property
|
||||||
def platform(self):
|
def platform(self):
|
||||||
return self._core.platform(self._core)
|
return self._core.platform(self._core)
|
||||||
|
|
||||||
|
@protected
|
||||||
def desired_video_dimensions(self):
|
def desired_video_dimensions(self):
|
||||||
width = ffi.new("unsigned*")
|
width = ffi.new("unsigned*")
|
||||||
height = ffi.new("unsigned*")
|
height = ffi.new("unsigned*")
|
||||||
self._core.desiredVideoDimensions(self._core, width, height)
|
self._core.desiredVideoDimensions(self._core, width, height)
|
||||||
return width[0], height[0]
|
return width[0], height[0]
|
||||||
|
|
||||||
|
@protected
|
||||||
def set_video_buffer(self, image):
|
def set_video_buffer(self, image):
|
||||||
self._core.setVideoBuffer(self._core, image.buffer, image.stride)
|
self._core.setVideoBuffer(self._core, image.buffer, image.stride)
|
||||||
|
|
||||||
|
@protected
|
||||||
|
def set_audio_buffer_size(self, size):
|
||||||
|
self._core.setAudioBufferSize(self._core, size)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def audio_buffer_size(self):
|
||||||
|
return self._core.getAudioBufferSize(self._core)
|
||||||
|
|
||||||
|
@protected
|
||||||
|
def get_audio_channels(self):
|
||||||
|
return audio.StereoBuffer(self.get_audio_channel(0), self.get_audio_channel(1));
|
||||||
|
|
||||||
|
@protected
|
||||||
|
def get_audio_channel(self, channel):
|
||||||
|
return audio.Buffer(self._core.getAudioChannel(self._core, channel), self.frequency)
|
||||||
|
|
||||||
|
@protected
|
||||||
def reset(self):
|
def reset(self):
|
||||||
self._core.reset(self._core)
|
self._core.reset(self._core)
|
||||||
self._load()
|
self._load()
|
||||||
|
@ -233,6 +263,7 @@ class Core(object):
|
||||||
self._core.runLoop(self._core)
|
self._core.runLoop(self._core)
|
||||||
|
|
||||||
@needs_reset
|
@needs_reset
|
||||||
|
@protected
|
||||||
def step(self):
|
def step(self):
|
||||||
self._core.step(self._core)
|
self._core.step(self._core)
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,10 @@ except ImportError:
|
||||||
|
|
||||||
def search(core):
|
def search(core):
|
||||||
crc32 = None
|
crc32 = None
|
||||||
if hasattr(core, 'PLATFORM_GBA') and core.platform() == core.PLATFORM_GBA:
|
if hasattr(core, 'PLATFORM_GBA') and core.platform == core.PLATFORM_GBA:
|
||||||
platform = 'GBA'
|
platform = 'GBA'
|
||||||
crc32 = core.crc32
|
crc32 = core.crc32
|
||||||
if hasattr(core, 'PLATFORM_GB') and core.platform() == core.PLATFORM_GB:
|
if hasattr(core, 'PLATFORM_GB') and core.platform == core.PLATFORM_GB:
|
||||||
platform = 'GB'
|
platform = 'GB'
|
||||||
crc32 = core.crc32
|
crc32 = core.crc32
|
||||||
cls = mgba_gamedata.registry.search(platform, {'crc32': crc32})
|
cls = mgba_gamedata.registry.search(platform, {'crc32': crc32})
|
||||||
|
|
|
@ -185,12 +185,12 @@ CoreController::CoreController(mCore* core, QObject* parent)
|
||||||
message = QString().vsprintf(format, args);
|
message = QString().vsprintf(format, args);
|
||||||
QMetaObject::invokeMethod(controller, "statusPosted", Q_ARG(const QString&, message));
|
QMetaObject::invokeMethod(controller, "statusPosted", Q_ARG(const QString&, message));
|
||||||
}
|
}
|
||||||
|
message = QString().vsprintf(format, args);
|
||||||
|
QMetaObject::invokeMethod(controller, "logPosted", Q_ARG(int, level), Q_ARG(int, category), Q_ARG(const QString&, message));
|
||||||
if (level == mLOG_FATAL) {
|
if (level == mLOG_FATAL) {
|
||||||
mCoreThreadMarkCrashed(controller->thread());
|
mCoreThreadMarkCrashed(controller->thread());
|
||||||
QMetaObject::invokeMethod(controller, "crashed", Q_ARG(const QString&, QString().vsprintf(format, args)));
|
QMetaObject::invokeMethod(controller, "crashed", Q_ARG(const QString&, QString().vsprintf(format, args)));
|
||||||
}
|
}
|
||||||
message = QString().vsprintf(format, args);
|
|
||||||
QMetaObject::invokeMethod(controller, "logPosted", Q_ARG(int, level), Q_ARG(int, category), Q_ARG(const QString&, message));
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,9 +75,11 @@ void GBAApp::cleanup() {
|
||||||
finishJob(m_workerJobs.firstKey());
|
finishJob(m_workerJobs.firstKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_SQLITE3
|
||||||
if (m_db) {
|
if (m_db) {
|
||||||
NoIntroDBDestroy(m_db);
|
NoIntroDBDestroy(m_db);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GBAApp::event(QEvent* event) {
|
bool GBAApp::event(QEvent* event) {
|
||||||
|
|
|
@ -278,13 +278,13 @@ void GBAKeyEditor::lookupAxes(const mInputMap* map) {
|
||||||
if (description->highDirection != GBA_KEY_NONE) {
|
if (description->highDirection != GBA_KEY_NONE) {
|
||||||
KeyEditor* key = self->keyById(static_cast<enum GBAKey>(description->highDirection));
|
KeyEditor* key = self->keyById(static_cast<enum GBAKey>(description->highDirection));
|
||||||
if (key) {
|
if (key) {
|
||||||
key->setValueAxis(axis, description->deadHigh);
|
key->setValueAxis(axis, GamepadAxisEvent::POSITIVE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (description->lowDirection != GBA_KEY_NONE) {
|
if (description->lowDirection != GBA_KEY_NONE) {
|
||||||
KeyEditor* key = self->keyById(static_cast<enum GBAKey>(description->lowDirection));
|
KeyEditor* key = self->keyById(static_cast<enum GBAKey>(description->lowDirection));
|
||||||
if (key) {
|
if (key) {
|
||||||
key->setValueAxis(axis, description->deadLow);
|
key->setValueAxis(axis, GamepadAxisEvent::NEGATIVE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, this);
|
}, this);
|
||||||
|
@ -350,14 +350,6 @@ bool GBAKeyEditor::findFocus(KeyEditor* needle) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BUILD_SDL
|
#ifdef BUILD_SDL
|
||||||
void GBAKeyEditor::setAxisValue(int axis, int32_t value) {
|
|
||||||
if (!findFocus()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
KeyEditor* focused = *m_currentKey;
|
|
||||||
focused->setValueAxis(axis, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBAKeyEditor::selectGamepad(int index) {
|
void GBAKeyEditor::selectGamepad(int index) {
|
||||||
m_controller->setGamepad(m_type, index);
|
m_controller->setGamepad(m_type, index);
|
||||||
m_profile = m_profileSelect->currentText();
|
m_profile = m_profileSelect->currentText();
|
||||||
|
|
|
@ -42,7 +42,6 @@ private slots:
|
||||||
void setNext();
|
void setNext();
|
||||||
void refresh();
|
void refresh();
|
||||||
#ifdef BUILD_SDL
|
#ifdef BUILD_SDL
|
||||||
void setAxisValue(int axis, int32_t value);
|
|
||||||
void selectGamepad(int index);
|
void selectGamepad(int index);
|
||||||
void updateJoysticks();
|
void updateJoysticks();
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -48,10 +48,10 @@ void KeyEditor::setValueButton(int button) {
|
||||||
setValue(button);
|
setValue(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyEditor::setValueAxis(int axis, int32_t value) {
|
void KeyEditor::setValueAxis(int axis, GamepadAxisEvent::Direction direction) {
|
||||||
m_button = true;
|
m_button = true;
|
||||||
m_axis = axis;
|
m_axis = axis;
|
||||||
m_direction = value < 0 ? GamepadAxisEvent::NEGATIVE : GamepadAxisEvent::POSITIVE;
|
m_direction = direction;
|
||||||
updateButtonText();
|
updateButtonText();
|
||||||
emit axisChanged(axis, m_direction);
|
emit axisChanged(axis, m_direction);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ public slots:
|
||||||
void setValue(int key);
|
void setValue(int key);
|
||||||
void setValueKey(int key);
|
void setValueKey(int key);
|
||||||
void setValueButton(int button);
|
void setValueButton(int button);
|
||||||
void setValueAxis(int axis, int32_t value);
|
void setValueAxis(int axis, GamepadAxisEvent::Direction value);
|
||||||
void setValueHat(int hat, GamepadHatEvent::Direction value);
|
void setValueHat(int hat, GamepadHatEvent::Direction value);
|
||||||
void clearButton();
|
void clearButton();
|
||||||
void clearAxis();
|
void clearAxis();
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>565</width>
|
<width>822</width>
|
||||||
<height>658</height>
|
<height>886</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -98,7 +98,7 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="QRadioButton" name="width8">
|
<widget class="QRadioButton" name="width8">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>1 Byte</string>
|
<string>&1 Byte</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="checked">
|
<property name="checked">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
|
@ -121,7 +121,7 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="QRadioButton" name="width16">
|
<widget class="QRadioButton" name="width16">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>2 Bytes</string>
|
<string>&2 Bytes</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -141,7 +141,7 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="QRadioButton" name="width32">
|
<widget class="QRadioButton" name="width32">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>4 Bytes</string>
|
<string>&4 Bytes</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -168,7 +168,6 @@
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<zorder></zorder>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
|
|
@ -765,6 +765,7 @@ void Window::gameStarted() {
|
||||||
attachWidget(m_display.get());
|
attachWidget(m_display.get());
|
||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
m_display->setMinimumSize(size);
|
m_display->setMinimumSize(size);
|
||||||
|
setFocus();
|
||||||
|
|
||||||
#ifndef Q_OS_MAC
|
#ifndef Q_OS_MAC
|
||||||
if (isFullScreen()) {
|
if (isFullScreen()) {
|
||||||
|
|
|
@ -4687,20 +4687,30 @@ wenn vorhanden</translation>
|
||||||
<translation>Tiles</translation>
|
<translation>Tiles</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../TileView.ui" line="140"/>
|
<location filename="../TileView.ui" line="110"/>
|
||||||
<source>256 colors</source>
|
<source>256 colors</source>
|
||||||
<translation>256 Farben</translation>
|
<translation>256 Farben</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../TileView.ui" line="31"/>
|
<location filename="../TileView.ui" line="123"/>
|
||||||
<source>×</source>
|
<source>×</source>
|
||||||
<translation>×</translation>
|
<translation>×</translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<location filename="../TileView.ui" line="44"/>
|
<location filename="../TileView.ui" line="136"/>
|
||||||
<source>Magnification</source>
|
<source>Magnification</source>
|
||||||
<translation>Vergrößerung</translation>
|
<translation>Vergrößerung</translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../TileView.ui" line="159"/>
|
||||||
|
<source>Tiles per row</source>
|
||||||
|
<translation>Tiles pro Zeile</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="../TileView.ui" line="166"/>
|
||||||
|
<source>Fit to window</source>
|
||||||
|
<translation>An Fenster anpassen</translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>VideoView</name>
|
<name>VideoView</name>
|
||||||
|
|
|
@ -15,7 +15,7 @@ endif()
|
||||||
if(SDL_VERSION EQUAL "1.2" OR NOT SDL2_FOUND)
|
if(SDL_VERSION EQUAL "1.2" OR NOT SDL2_FOUND)
|
||||||
find_package(SDL 1.2)
|
find_package(SDL 1.2)
|
||||||
if(SDL_FOUND)
|
if(SDL_FOUND)
|
||||||
set(SDL_VERSION "1.2" PARENT_SCOPE)
|
set(SDL_VERSION "1.2")
|
||||||
set(SDL_VERSION_DEBIAN "1.2debian")
|
set(SDL_VERSION_DEBIAN "1.2debian")
|
||||||
set(USE_PIXMAN ON)
|
set(USE_PIXMAN ON)
|
||||||
endif()
|
endif()
|
||||||
|
@ -26,6 +26,7 @@ if (NOT SDL2_FOUND AND NOT SDL_FOUND)
|
||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
set(SDL_VERSION "${SDL_VERSION}" PARENT_SCOPE)
|
||||||
add_definitions(-DBUILD_SDL)
|
add_definitions(-DBUILD_SDL)
|
||||||
|
|
||||||
find_feature(USE_PIXMAN "pixman-1")
|
find_feature(USE_PIXMAN "pixman-1")
|
||||||
|
@ -85,7 +86,11 @@ else()
|
||||||
include_directories(${OPENGLES2_INCLUDE_DIR})
|
include_directories(${OPENGLES2_INCLUDE_DIR})
|
||||||
endif()
|
endif()
|
||||||
if(NOT BUILD_GL AND NOT BUILD_GLES2)
|
if(NOT BUILD_GL AND NOT BUILD_GLES2)
|
||||||
list(APPEND MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/sw-sdl.c)
|
if(SDL_VERSION EQUAL "2")
|
||||||
|
list(APPEND MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/sw-sdl2.c)
|
||||||
|
else()
|
||||||
|
list(APPEND MAIN_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/sw-sdl1.c)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
|
@ -55,5 +55,6 @@ void mSDLGLCommonInit(struct mSDLRenderer* renderer) {
|
||||||
#else
|
#else
|
||||||
SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 32, SDL_OPENGL | (SDL_FULLSCREEN * renderer->fullscreen));
|
SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 32, SDL_OPENGL | (SDL_FULLSCREEN * renderer->fullscreen));
|
||||||
#endif
|
#endif
|
||||||
|
SDL_WM_SetCaption(projectName, "");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,8 +64,12 @@ bool mSDLInitEvents(struct mSDLEvents* context) {
|
||||||
if (!SDL_JoystickListSize(&context->joysticks)) {
|
if (!SDL_JoystickListSize(&context->joysticks)) {
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < nJoysticks; ++i) {
|
for (i = 0; i < nJoysticks; ++i) {
|
||||||
|
SDL_Joystick* sdlJoystick = SDL_JoystickOpen(i);
|
||||||
|
if (!sdlJoystick) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
struct SDL_JoystickCombo* joystick = SDL_JoystickListAppend(&context->joysticks);
|
struct SDL_JoystickCombo* joystick = SDL_JoystickListAppend(&context->joysticks);
|
||||||
joystick->joystick = SDL_JoystickOpen(i);
|
joystick->joystick = sdlJoystick;
|
||||||
joystick->index = SDL_JoystickListSize(&context->joysticks) - 1;
|
joystick->index = SDL_JoystickListSize(&context->joysticks) - 1;
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
joystick->id = SDL_JoystickInstanceID(joystick->joystick);
|
joystick->id = SDL_JoystickInstanceID(joystick->joystick);
|
||||||
|
@ -200,6 +204,9 @@ bool mSDLAttachPlayer(struct mSDLEvents* events, struct mSDLPlayer* player) {
|
||||||
#else
|
#else
|
||||||
joystickName = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&events->joysticks, i)->joystick));
|
joystickName = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&events->joysticks, i)->joystick));
|
||||||
#endif
|
#endif
|
||||||
|
if (!joystickName) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (events->preferredJoysticks[player->playerId] && strcmp(events->preferredJoysticks[player->playerId], joystickName) == 0) {
|
if (events->preferredJoysticks[player->playerId] && strcmp(events->preferredJoysticks[player->playerId], joystickName) == 0) {
|
||||||
index = i;
|
index = i;
|
||||||
break;
|
break;
|
||||||
|
@ -250,6 +257,9 @@ void mSDLPlayerLoadConfig(struct mSDLPlayer* context, const struct Configuration
|
||||||
#else
|
#else
|
||||||
const char* name = SDL_JoystickName(SDL_JoystickIndex(context->joystick->joystick));
|
const char* name = SDL_JoystickName(SDL_JoystickIndex(context->joystick->joystick));
|
||||||
#endif
|
#endif
|
||||||
|
if (!name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
mInputProfileLoad(context->bindings, SDL_BINDING_BUTTON, config, name);
|
mInputProfileLoad(context->bindings, SDL_BINDING_BUTTON, config, name);
|
||||||
|
|
||||||
const char* value;
|
const char* value;
|
||||||
|
@ -301,7 +311,10 @@ void mSDLPlayerSaveConfig(const struct mSDLPlayer* context, struct Configuration
|
||||||
#else
|
#else
|
||||||
const char* name = SDL_JoystickName(SDL_JoystickIndex(context->joystick->joystick));
|
const char* name = SDL_JoystickName(SDL_JoystickIndex(context->joystick->joystick));
|
||||||
#endif
|
#endif
|
||||||
char value[12];
|
if (!name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
char value[16];
|
||||||
snprintf(value, sizeof(value), "%i", context->rotation.axisX);
|
snprintf(value, sizeof(value), "%i", context->rotation.axisX);
|
||||||
mInputSetCustomValue(config, "gba", SDL_BINDING_BUTTON, "tiltAxisX", value, name);
|
mInputSetCustomValue(config, "gba", SDL_BINDING_BUTTON, "tiltAxisX", value, name);
|
||||||
snprintf(value, sizeof(value), "%i", context->rotation.axisY);
|
snprintf(value, sizeof(value), "%i", context->rotation.axisY);
|
||||||
|
@ -329,8 +342,12 @@ void mSDLUpdateJoysticks(struct mSDLEvents* events, const struct Configuration*
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_JOYDEVICEADDED, SDL_JOYDEVICEREMOVED) > 0) {
|
while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_JOYDEVICEADDED, SDL_JOYDEVICEREMOVED) > 0) {
|
||||||
if (event.type == SDL_JOYDEVICEADDED) {
|
if (event.type == SDL_JOYDEVICEADDED) {
|
||||||
|
SDL_Joystick* sdlJoystick = SDL_JoystickOpen(event.jdevice.which);
|
||||||
|
if (!sdlJoystick) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
struct SDL_JoystickCombo* joystick = SDL_JoystickListAppend(&events->joysticks);
|
struct SDL_JoystickCombo* joystick = SDL_JoystickListAppend(&events->joysticks);
|
||||||
joystick->joystick = SDL_JoystickOpen(event.jdevice.which);
|
joystick->joystick = sdlJoystick;
|
||||||
joystick->id = SDL_JoystickInstanceID(joystick->joystick);
|
joystick->id = SDL_JoystickInstanceID(joystick->joystick);
|
||||||
joystick->index = SDL_JoystickListSize(&events->joysticks) - 1;
|
joystick->index = SDL_JoystickListSize(&events->joysticks) - 1;
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
|
@ -344,16 +361,18 @@ void mSDLUpdateJoysticks(struct mSDLEvents* events, const struct Configuration*
|
||||||
joystickName = SDL_JoystickName(SDL_JoystickIndex(joystick->joystick));
|
joystickName = SDL_JoystickName(SDL_JoystickIndex(joystick->joystick));
|
||||||
#endif
|
#endif
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; (int) i < events->playersAttached; ++i) {
|
if (joystickName) {
|
||||||
if (events->players[i]->joystick) {
|
for (i = 0; (int) i < events->playersAttached; ++i) {
|
||||||
continue;
|
if (events->players[i]->joystick) {
|
||||||
}
|
continue;
|
||||||
if (events->preferredJoysticks[i] && strcmp(events->preferredJoysticks[i], joystickName) == 0) {
|
}
|
||||||
events->players[i]->joystick = joystick;
|
if (events->preferredJoysticks[i] && strcmp(events->preferredJoysticks[i], joystickName) == 0) {
|
||||||
if (config && events->players[i]->bindings) {
|
events->players[i]->joystick = joystick;
|
||||||
mInputProfileLoad(events->players[i]->bindings, SDL_BINDING_BUTTON, config, joystickName);
|
if (config && events->players[i]->bindings) {
|
||||||
|
mInputProfileLoad(events->players[i]->bindings, SDL_BINDING_BUTTON, config, joystickName);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (i = 0; (int) i < events->playersAttached; ++i) {
|
for (i = 0; (int) i < events->playersAttached; ++i) {
|
||||||
|
@ -361,7 +380,7 @@ void mSDLUpdateJoysticks(struct mSDLEvents* events, const struct Configuration*
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
events->players[i]->joystick = joystick;
|
events->players[i]->joystick = joystick;
|
||||||
if (config && events->players[i]->bindings) {
|
if (config && events->players[i]->bindings && joystickName) {
|
||||||
mInputProfileLoad(events->players[i]->bindings, SDL_BINDING_BUTTON, config, joystickName);
|
mInputProfileLoad(events->players[i]->bindings, SDL_BINDING_BUTTON, config, joystickName);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -21,35 +21,15 @@ void mSDLSWCreate(struct mSDLRenderer* renderer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mSDLSWInit(struct mSDLRenderer* renderer) {
|
bool mSDLSWInit(struct mSDLRenderer* renderer) {
|
||||||
#if !SDL_VERSION_ATLEAST(2, 0, 0)
|
|
||||||
#ifdef COLOR_16_BIT
|
#ifdef COLOR_16_BIT
|
||||||
SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 16, SDL_DOUBLEBUF | SDL_HWSURFACE);
|
SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 16, SDL_DOUBLEBUF | SDL_HWSURFACE | (SDL_FULLSCREEN * renderer->fullscreen));
|
||||||
#else
|
#else
|
||||||
SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 32, SDL_DOUBLEBUF | SDL_HWSURFACE);
|
SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 32, SDL_DOUBLEBUF | SDL_HWSURFACE | (SDL_FULLSCREEN * renderer->fullscreen));
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
SDL_WM_SetCaption(projectName, "");
|
||||||
|
|
||||||
unsigned width, height;
|
unsigned width, height;
|
||||||
renderer->core->desiredVideoDimensions(renderer->core, &width, &height);
|
renderer->core->desiredVideoDimensions(renderer->core, &width, &height);
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
|
||||||
renderer->window = SDL_CreateWindow(projectName, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, renderer->viewportWidth, renderer->viewportHeight, SDL_WINDOW_OPENGL | (SDL_WINDOW_FULLSCREEN_DESKTOP * renderer->player.fullscreen));
|
|
||||||
SDL_GetWindowSize(renderer->window, &renderer->viewportWidth, &renderer->viewportHeight);
|
|
||||||
renderer->player.window = renderer->window;
|
|
||||||
renderer->sdlRenderer = SDL_CreateRenderer(renderer->window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
|
||||||
#ifdef COLOR_16_BIT
|
|
||||||
#ifdef COLOR_5_6_5
|
|
||||||
renderer->sdlTex = SDL_CreateTexture(renderer->sdlRenderer, SDL_PIXELFORMAT_RGB565, SDL_TEXTUREACCESS_STREAMING, width, height);
|
|
||||||
#else
|
|
||||||
renderer->sdlTex = SDL_CreateTexture(renderer->sdlRenderer, SDL_PIXELFORMAT_ABGR1555, SDL_TEXTUREACCESS_STREAMING, width, height);
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
renderer->sdlTex = SDL_CreateTexture(renderer->sdlRenderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, width, height);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int stride;
|
|
||||||
SDL_LockTexture(renderer->sdlTex, 0, (void**) &renderer->outputBuffer, &stride);
|
|
||||||
renderer->core->setVideoBuffer(renderer->core, renderer->outputBuffer, stride / BYTES_PER_PIXEL);
|
|
||||||
#else
|
|
||||||
SDL_Surface* surface = SDL_GetVideoSurface();
|
SDL_Surface* surface = SDL_GetVideoSurface();
|
||||||
SDL_LockSurface(surface);
|
SDL_LockSurface(surface);
|
||||||
|
|
||||||
|
@ -81,7 +61,6 @@ bool mSDLSWInit(struct mSDLRenderer* renderer) {
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -89,9 +68,7 @@ bool mSDLSWInit(struct mSDLRenderer* renderer) {
|
||||||
void mSDLSWRunloop(struct mSDLRenderer* renderer, void* user) {
|
void mSDLSWRunloop(struct mSDLRenderer* renderer, void* user) {
|
||||||
struct mCoreThread* context = user;
|
struct mCoreThread* context = user;
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
#if !SDL_VERSION_ATLEAST(2, 0, 0)
|
|
||||||
SDL_Surface* surface = SDL_GetVideoSurface();
|
SDL_Surface* surface = SDL_GetVideoSurface();
|
||||||
#endif
|
|
||||||
|
|
||||||
while (mCoreThreadIsActive(context)) {
|
while (mCoreThreadIsActive(context)) {
|
||||||
while (SDL_PollEvent(&event)) {
|
while (SDL_PollEvent(&event)) {
|
||||||
|
@ -99,14 +76,6 @@ void mSDLSWRunloop(struct mSDLRenderer* renderer, void* user) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mCoreSyncWaitFrameStart(&context->impl->sync)) {
|
if (mCoreSyncWaitFrameStart(&context->impl->sync)) {
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
|
||||||
SDL_UnlockTexture(renderer->sdlTex);
|
|
||||||
SDL_RenderCopy(renderer->sdlRenderer, renderer->sdlTex, 0, 0);
|
|
||||||
SDL_RenderPresent(renderer->sdlRenderer);
|
|
||||||
int stride;
|
|
||||||
SDL_LockTexture(renderer->sdlTex, 0, (void**) &renderer->outputBuffer, &stride);
|
|
||||||
renderer->core->setVideoBuffer(renderer->core, renderer->outputBuffer, stride / BYTES_PER_PIXEL);
|
|
||||||
#else
|
|
||||||
#ifdef USE_PIXMAN
|
#ifdef USE_PIXMAN
|
||||||
if (renderer->ratio > 1) {
|
if (renderer->ratio > 1) {
|
||||||
pixman_image_composite32(PIXMAN_OP_SRC, renderer->pix, 0, renderer->screenpix,
|
pixman_image_composite32(PIXMAN_OP_SRC, renderer->pix, 0, renderer->screenpix,
|
||||||
|
@ -132,7 +101,6 @@ void mSDLSWRunloop(struct mSDLRenderer* renderer, void* user) {
|
||||||
SDL_UnlockSurface(surface);
|
SDL_UnlockSurface(surface);
|
||||||
SDL_Flip(surface);
|
SDL_Flip(surface);
|
||||||
SDL_LockSurface(surface);
|
SDL_LockSurface(surface);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
mCoreSyncWaitFrameEnd(&context->impl->sync);
|
mCoreSyncWaitFrameEnd(&context->impl->sync);
|
||||||
}
|
}
|
||||||
|
@ -141,13 +109,11 @@ void mSDLSWRunloop(struct mSDLRenderer* renderer, void* user) {
|
||||||
void mSDLSWDeinit(struct mSDLRenderer* renderer) {
|
void mSDLSWDeinit(struct mSDLRenderer* renderer) {
|
||||||
if (renderer->ratio > 1) {
|
if (renderer->ratio > 1) {
|
||||||
free(renderer->outputBuffer);
|
free(renderer->outputBuffer);
|
||||||
|
#ifdef USE_PIXMAN
|
||||||
|
pixman_image_unref(renderer->pix);
|
||||||
|
pixman_image_unref(renderer->screenpix);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#if !SDL_VERSION_ATLEAST(2, 0, 0)
|
|
||||||
SDL_Surface* surface = SDL_GetVideoSurface();
|
SDL_Surface* surface = SDL_GetVideoSurface();
|
||||||
SDL_UnlockSurface(surface);
|
SDL_UnlockSurface(surface);
|
||||||
#ifdef USE_PIXMAN
|
|
||||||
pixman_image_unref(renderer->pix);
|
|
||||||
pixman_image_unref(renderer->screenpix);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
}
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
/* Copyright (c) 2013-2015 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 "main.h"
|
||||||
|
|
||||||
|
#include <mgba/core/core.h>
|
||||||
|
#include <mgba/core/thread.h>
|
||||||
|
#include <mgba/core/version.h>
|
||||||
|
#include <mgba-util/arm-algo.h>
|
||||||
|
|
||||||
|
static bool mSDLSWInit(struct mSDLRenderer* renderer);
|
||||||
|
static void mSDLSWRunloop(struct mSDLRenderer* renderer, void* user);
|
||||||
|
static void mSDLSWDeinit(struct mSDLRenderer* renderer);
|
||||||
|
|
||||||
|
void mSDLSWCreate(struct mSDLRenderer* renderer) {
|
||||||
|
renderer->init = mSDLSWInit;
|
||||||
|
renderer->deinit = mSDLSWDeinit;
|
||||||
|
renderer->runloop = mSDLSWRunloop;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mSDLSWInit(struct mSDLRenderer* renderer) {
|
||||||
|
unsigned width, height;
|
||||||
|
renderer->core->desiredVideoDimensions(renderer->core, &width, &height);
|
||||||
|
renderer->window = SDL_CreateWindow(projectName, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, renderer->viewportWidth, renderer->viewportHeight, SDL_WINDOW_OPENGL | (SDL_WINDOW_FULLSCREEN_DESKTOP * renderer->player.fullscreen));
|
||||||
|
SDL_GetWindowSize(renderer->window, &renderer->viewportWidth, &renderer->viewportHeight);
|
||||||
|
renderer->player.window = renderer->window;
|
||||||
|
renderer->sdlRenderer = SDL_CreateRenderer(renderer->window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
||||||
|
#ifdef COLOR_16_BIT
|
||||||
|
#ifdef COLOR_5_6_5
|
||||||
|
renderer->sdlTex = SDL_CreateTexture(renderer->sdlRenderer, SDL_PIXELFORMAT_RGB565, SDL_TEXTUREACCESS_STREAMING, width, height);
|
||||||
|
#else
|
||||||
|
renderer->sdlTex = SDL_CreateTexture(renderer->sdlRenderer, SDL_PIXELFORMAT_ABGR1555, SDL_TEXTUREACCESS_STREAMING, width, height);
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
renderer->sdlTex = SDL_CreateTexture(renderer->sdlRenderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, width, height);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int stride;
|
||||||
|
SDL_LockTexture(renderer->sdlTex, 0, (void**) &renderer->outputBuffer, &stride);
|
||||||
|
renderer->core->setVideoBuffer(renderer->core, renderer->outputBuffer, stride / BYTES_PER_PIXEL);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mSDLSWRunloop(struct mSDLRenderer* renderer, void* user) {
|
||||||
|
struct mCoreThread* context = user;
|
||||||
|
SDL_Event event;
|
||||||
|
|
||||||
|
while (mCoreThreadIsActive(context)) {
|
||||||
|
while (SDL_PollEvent(&event)) {
|
||||||
|
mSDLHandleEvent(context, &renderer->player, &event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mCoreSyncWaitFrameStart(&context->impl->sync)) {
|
||||||
|
SDL_UnlockTexture(renderer->sdlTex);
|
||||||
|
SDL_RenderCopy(renderer->sdlRenderer, renderer->sdlTex, 0, 0);
|
||||||
|
SDL_RenderPresent(renderer->sdlRenderer);
|
||||||
|
int stride;
|
||||||
|
SDL_LockTexture(renderer->sdlTex, 0, (void**) &renderer->outputBuffer, &stride);
|
||||||
|
renderer->core->setVideoBuffer(renderer->core, renderer->outputBuffer, stride / BYTES_PER_PIXEL);
|
||||||
|
}
|
||||||
|
mCoreSyncWaitFrameEnd(&context->impl->sync);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mSDLSWDeinit(struct mSDLRenderer* renderer) {
|
||||||
|
if (renderer->ratio > 1) {
|
||||||
|
free(renderer->outputBuffer);
|
||||||
|
}
|
||||||
|
}
|
|
@ -92,6 +92,13 @@ static unsigned framecap = 10;
|
||||||
static u32 vibrationDeviceHandles[4];
|
static u32 vibrationDeviceHandles[4];
|
||||||
static HidVibrationValue vibrationStop = { .freq_low = 160.f, .freq_high = 320.f };
|
static HidVibrationValue vibrationStop = { .freq_low = 160.f, .freq_high = 320.f };
|
||||||
|
|
||||||
|
static enum ScreenMode {
|
||||||
|
SM_PA,
|
||||||
|
SM_AF,
|
||||||
|
SM_SF,
|
||||||
|
SM_MAX
|
||||||
|
} screenMode = SM_PA;
|
||||||
|
|
||||||
static bool initEgl() {
|
static bool initEgl() {
|
||||||
s_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
s_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||||
if (!s_display) {
|
if (!s_display) {
|
||||||
|
@ -113,7 +120,7 @@ static bool initEgl() {
|
||||||
goto _fail1;
|
goto _fail1;
|
||||||
}
|
}
|
||||||
|
|
||||||
s_surface = eglCreateWindowSurface(s_display, config, "", NULL);
|
s_surface = eglCreateWindowSurface(s_display, config, nwindowGetDefault(), NULL);
|
||||||
if (!s_surface) {
|
if (!s_surface) {
|
||||||
goto _fail1;
|
goto _fail1;
|
||||||
}
|
}
|
||||||
|
@ -238,6 +245,11 @@ static void _setup(struct mGUIRunner* runner) {
|
||||||
runner->core->setPeripheral(runner->core, mPERIPH_RUMBLE, &rumble.d);
|
runner->core->setPeripheral(runner->core, mPERIPH_RUMBLE, &rumble.d);
|
||||||
runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation);
|
runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation);
|
||||||
runner->core->setAVStream(runner->core, &stream);
|
runner->core->setAVStream(runner->core, &stream);
|
||||||
|
|
||||||
|
unsigned mode;
|
||||||
|
if (mCoreConfigGetUIntValue(&runner->config, "screenMode", &mode) && mode < SM_MAX) {
|
||||||
|
screenMode = mode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _gameLoaded(struct mGUIRunner* runner) {
|
static void _gameLoaded(struct mGUIRunner* runner) {
|
||||||
|
@ -249,6 +261,11 @@ static void _gameLoaded(struct mGUIRunner* runner) {
|
||||||
|
|
||||||
mCoreConfigGetUIntValue(&runner->config, "fastForwardCap", &framecap);
|
mCoreConfigGetUIntValue(&runner->config, "fastForwardCap", &framecap);
|
||||||
|
|
||||||
|
unsigned mode;
|
||||||
|
if (mCoreConfigGetUIntValue(&runner->config, "screenMode", &mode) && mode < SM_MAX) {
|
||||||
|
screenMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
rumble.up = 0;
|
rumble.up = 0;
|
||||||
rumble.down = 0;
|
rumble.down = 0;
|
||||||
}
|
}
|
||||||
|
@ -270,11 +287,26 @@ static void _drawTex(struct mGUIRunner* runner, unsigned width, unsigned height,
|
||||||
glBindVertexArray(vao);
|
glBindVertexArray(vao);
|
||||||
float aspectX = width / (float) runner->params.width;
|
float aspectX = width / (float) runner->params.width;
|
||||||
float aspectY = height / (float) runner->params.height;
|
float aspectY = height / (float) runner->params.height;
|
||||||
float max;
|
float max = 1.f;
|
||||||
if (aspectX > aspectY) {
|
switch (screenMode) {
|
||||||
max = floor(1.0 / aspectX);
|
case SM_PA:
|
||||||
} else {
|
if (aspectX > aspectY) {
|
||||||
max = floor(1.0 / aspectY);
|
max = floor(1.0 / aspectX);
|
||||||
|
} else {
|
||||||
|
max = floor(1.0 / aspectY);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SM_AF:
|
||||||
|
if (aspectX > aspectY) {
|
||||||
|
max = 1.0 / aspectX;
|
||||||
|
} else {
|
||||||
|
max = 1.0 / aspectY;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SM_SF:
|
||||||
|
aspectX = 1.0;
|
||||||
|
aspectY = 1.0;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
aspectX *= max;
|
aspectX *= max;
|
||||||
|
@ -354,6 +386,12 @@ static uint16_t _pollGameInput(struct mGUIRunner* runner) {
|
||||||
return _pollInput(&runner->core->inputMap);
|
return _pollInput(&runner->core->inputMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _incrementScreenMode(struct mGUIRunner* runner) {
|
||||||
|
UNUSED(runner);
|
||||||
|
screenMode = (screenMode + 1) % SM_MAX;
|
||||||
|
mCoreConfigSetUIntValue(&runner->config, "screenMode", screenMode);
|
||||||
|
}
|
||||||
|
|
||||||
static void _setFrameLimiter(struct mGUIRunner* runner, bool limit) {
|
static void _setFrameLimiter(struct mGUIRunner* runner, bool limit) {
|
||||||
UNUSED(runner);
|
UNUSED(runner);
|
||||||
if (!frameLimiter && limit) {
|
if (!frameLimiter && limit) {
|
||||||
|
@ -365,6 +403,7 @@ static void _setFrameLimiter(struct mGUIRunner* runner, bool limit) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
frameLimiter = limit;
|
frameLimiter = limit;
|
||||||
|
eglSwapInterval(s_surface, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _running(struct mGUIRunner* runner) {
|
static bool _running(struct mGUIRunner* runner) {
|
||||||
|
@ -607,17 +646,30 @@ int main(int argc, char* argv[]) {
|
||||||
{ .id = 0 }
|
{ .id = 0 }
|
||||||
},
|
},
|
||||||
.configExtra = (struct GUIMenuItem[]) {
|
.configExtra = (struct GUIMenuItem[]) {
|
||||||
|
{
|
||||||
|
.title = "Screen mode",
|
||||||
|
.data = "screenMode",
|
||||||
|
.submenu = 0,
|
||||||
|
.state = SM_PA,
|
||||||
|
.validStates = (const char*[]) {
|
||||||
|
"Pixel-Accurate",
|
||||||
|
"Aspect-Ratio Fit",
|
||||||
|
"Stretched",
|
||||||
|
},
|
||||||
|
.nStates = 3
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.title = "Fast forward cap",
|
.title = "Fast forward cap",
|
||||||
.data = "fastForwardCap",
|
.data = "fastForwardCap",
|
||||||
.submenu = 0,
|
.submenu = 0,
|
||||||
.state = 7,
|
.state = 7,
|
||||||
.validStates = (const char*[]) {
|
.validStates = (const char*[]) {
|
||||||
"3", "4", "5", "6", "7", "8", "9",
|
"2", "3", "4", "5", "6", "7", "8", "9",
|
||||||
"10", "11", "12", "13", "14", "15",
|
"10", "11", "12", "13", "14", "15",
|
||||||
"20", "30"
|
"20", "30"
|
||||||
},
|
},
|
||||||
.stateMappings = (const struct GUIVariant[]) {
|
.stateMappings = (const struct GUIVariant[]) {
|
||||||
|
GUI_V_U(2),
|
||||||
GUI_V_U(3),
|
GUI_V_U(3),
|
||||||
GUI_V_U(4),
|
GUI_V_U(4),
|
||||||
GUI_V_U(5),
|
GUI_V_U(5),
|
||||||
|
@ -634,10 +686,10 @@ int main(int argc, char* argv[]) {
|
||||||
GUI_V_U(20),
|
GUI_V_U(20),
|
||||||
GUI_V_U(30),
|
GUI_V_U(30),
|
||||||
},
|
},
|
||||||
.nStates = 15
|
.nStates = 16
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.nConfigExtra = 1,
|
.nConfigExtra = 2,
|
||||||
.setup = _setup,
|
.setup = _setup,
|
||||||
.teardown = NULL,
|
.teardown = NULL,
|
||||||
.gameLoaded = _gameLoaded,
|
.gameLoaded = _gameLoaded,
|
||||||
|
@ -647,7 +699,7 @@ int main(int argc, char* argv[]) {
|
||||||
.drawScreenshot = _drawScreenshot,
|
.drawScreenshot = _drawScreenshot,
|
||||||
.paused = _gameUnloaded,
|
.paused = _gameUnloaded,
|
||||||
.unpaused = _gameLoaded,
|
.unpaused = _gameLoaded,
|
||||||
.incrementScreenMode = NULL,
|
.incrementScreenMode = _incrementScreenMode,
|
||||||
.setFrameLimiter = _setFrameLimiter,
|
.setFrameLimiter = _setFrameLimiter,
|
||||||
.pollGameInput = _pollGameInput,
|
.pollGameInput = _pollGameInput,
|
||||||
.running = _running
|
.running = _running
|
||||||
|
|
Loading…
Reference in New Issue