diff --git a/.travis-deps.sh b/.travis-deps.sh index fe7c49b23..b249cefee 100755 --- a/.travis-deps.sh +++ b/.travis-deps.sh @@ -2,11 +2,6 @@ if [ $TRAVIS_OS_NAME = "osx" ]; then brew update brew install qt5 ffmpeg imagemagick sdl2 libzip libpng - if [ "$CC" == "gcc" ]; then - brew install gcc@5 - export CC=gcc-5 - export CXX=g++-5 - fi else sudo apt-get clean sudo add-apt-repository -y ppa:george-edison55/cmake-3.x diff --git a/.travis.yml b/.travis.yml index 3b8970eae..3f3e6077c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,8 +7,6 @@ matrix: compiler: gcc - os: osx compiler: clang - - os: osx - compiler: gcc before_install: - source ./.travis-deps.sh diff --git a/CHANGES b/CHANGES index aa28a7c30..e9dc9c04f 100644 --- a/CHANGES +++ b/CHANGES @@ -28,6 +28,13 @@ Features: - Customizable autofire speed - Ability to set default Game Boy model - Map viewer + - Automatic cheat loading and saving + - GameShark and Action Replay button support + - AGBPrint support + - Debugger: Conditional breakpoints and watchpoints + - Ability to select GB/GBC/SGB BIOS on console ports + - Optional automatic state saving/loading + - Access to ur0 and uma0 partitions on the Vita Bugfixes: - GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749) - GB Serialize: Fix audio state loading @@ -36,6 +43,28 @@ Bugfixes: - ARM: Fix MSR when T bit is set - GB Serialize: Fix game title check - GB: Revamp IRQ handling based on new information + - GBA Video: Don't mask out high bits of BLDY (fixes mgba.io/i/899) + - GBA Video: Force align 256-color tiles + - GBA DMA: ROM reads are forced to increment + - GB Video: Fix loading states while in mode 3 + - GB Video: Only trigger STAT write IRQs when screen is on (fixes mgba.io/i/912) + - GBA Cheats: Fix PARv3 slide codes (fixes mgba.io/i/919) + - GBA Video: OBJWIN can change blend params after OBJ is drawn (fixes mgba.io/i/921) + - GBA DMA: Fix invalid DMA reads (fixes mgba.io/i/142) + - GBA Savedata: Fix crash when resizing flash + - GBA Video: Add delay when enabling BGs (fixes mgba.io/i/744, mgba.io/i/752) + - GB Memory: HDMAs should not start when LCD is off (fixes mgba.io/i/310) + - GBA Cheats: Fix slide codes not initializing properly + - Qt: Fix locale being set to English on settings save (fixes mgba.io/i/906) + - LR35902: Fix watchpoints not reporting new value + - GBA Audio: Increase PSG volume (fixes mgba.io/i/932) + - 3DS: Fix opening files in directory names with trailing slashes + - GB MBC: Fix MBC2 saves (fixes mgba.io/i/954) + - GBA Memory: Fix copy-on-write memory leak + - Core: Fix ROM patches not being unloaded when disabled (fixes mgba.io/i/962) + - GBA I/O: Fix writing to DISPCNT CGB flag (fixes mgba.io/i/902) + - GBA Memory: Partially revert prefetch changes (fixes mgba.io/i/840) + - PSP2: Fix issues causing poor audio Misc: - GBA Timer: Use global cycles for timers - GBA: Extend oddly-sized ROMs to full address space (fixes mgba.io/i/722) @@ -44,6 +73,18 @@ Misc: - Test: Restructure test suite into multiple executables - Python: Integrate tests from cinema test suite - Util: Don't build crc32 if the function already exists + - GBA: Implement display start DMAs + - Qt: Prevent window from being created off-screen + - Qt: Add option to disable FPS display + - GBA: Improve multiboot image detection + - GB MBC: Remove erroneous bank 0 wrapping + - GBA Cheats: Allow multiple ROM patches in the same slot + - GB: Skip BIOS option now works + - Libretro: Add frameskip option + - GBA Memory: 64 MiB GBA Video cartridge support + - 3DS: Scale font based on glyph heights (fixes mgba.io/i/961) + - PSP2: Use system enter key by default + - 3DS: Remove deprecated CSND interface 0.6.1: (2017-10-01) Bugfixes: diff --git a/CMakeLists.txt b/CMakeLists.txt index 9dfecec0e..44aa4d99f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,11 @@ cmake_minimum_required(VERSION 3.1) project(medusa) set(BINARY_NAME medusa-emu CACHE INTERNAL "Name of output binaries") if(NOT MSVC) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-missing-field-initializers -std=c99") + set(GCC_STD "c99") + if(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_COMPILER_VERSION VERSION_LESS "4.3") + set(GCC_STD "gnu99") + endif() + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-missing-field-initializers -std=${GCC_STD}") else() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS /wd4003 /wd4244 /wd4146") endif() @@ -40,6 +44,10 @@ set(BUILD_GL ON CACHE STRING "Build with OpenGL") set(BUILD_GLES2 OFF CACHE STRING "Build with OpenGL|ES 2") set(USE_EPOXY ON CACHE STRING "Build with libepoxy") set(DISABLE_DEPS OFF CACHE BOOL "Build without dependencies") +if(WIN32) + set(WIN32_UNIX_PATHS OFF CACHE BOOL "Use Unix-like paths") + mark_as_advanced(WIN32_UNIX_PATHS) +endif() file(GLOB ARM_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/arm/*.c) file(GLOB ARM_TEST_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/arm/test/*.c) file(GLOB LR35902_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/lr35902/*.c) @@ -57,7 +65,7 @@ file(GLOB UTIL_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/*.[cSs]) file(GLOB UTIL_TEST_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/test/*.c) file(GLOB GUI_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/gui/*.c ${CMAKE_CURRENT_SOURCE_DIR}/src/feature/gui/*.c) file(GLOB GBA_RENDERER_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/renderers/*.c) -file(GLOB GBA_SIO_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/sio/lockstep.c) +file(GLOB GBA_SIO_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/sio/*.c) file(GLOB GBA_EXTRA_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/extra/*.c) file(GLOB GB_SIO_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/sio/*.c) file(GLOB GB_RENDERER_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/renderers/*.c) @@ -80,8 +88,16 @@ if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type (e.g. Release or Debug)" FORCE) endif() -include(GNUInstallDirs) -string(REPLACE "${PROJECT_NAME}" "${BINARY_NAME}" CMAKE_INSTALL_DOCDIR "${CMAKE_INSTALL_DOCDIR}") +if(NOT WIN32 OR WIN32_UNIX_PATHS) + include(GNUInstallDirs) + string(REPLACE "${PROJECT_NAME}" "${BINARY_NAME}" CMAKE_INSTALL_DOCDIR "${CMAKE_INSTALL_DOCDIR}") +else() + set(CMAKE_INSTALL_LIBDIR ".") + set(CMAKE_INSTALL_BINDIR ".") + set(CMAKE_INSTALL_DATADIR ".") + set(CMAKE_INSTALL_DOCDIR ".") + set(CMAKE_INSTALL_INCLUDEDIR "include") +endif() set(LIBDIR "${CMAKE_INSTALL_LIBDIR}" CACHE PATH "Installed library directory") mark_as_advanced(LIBDIR) @@ -168,7 +184,7 @@ list(APPEND UTIL_SRC ${CMAKE_CURRENT_BINARY_DIR}/version.c) source_group("Generated sources" FILES ${CMAKE_CURRENT_BINARY_DIR}/version.c) # Advanced settings -if(NOT DEFINED 3DS) +if(NOT DEFINED 3DS AND NOT (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_COMPILER_VERSION VERSION_LESS "4.5")) # LTO appears to make 3DS binary slower set(DEFAULT_LTO ON) else() @@ -214,6 +230,7 @@ elseif(UNIX) endif() if(NOT APPLE AND NOT HAIKU) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") endif() list(APPEND CORE_VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-fd.c ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-dirent.c) @@ -223,7 +240,9 @@ endif() if(APPLE) add_definitions(-D_DARWIN_C_SOURCE) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.6") + if(CMAKE_SYSTEM_VERSION VERSION_GREATER "10.5.8") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.6") + endif() endif() if(NOT HAIKU AND NOT MSVC AND NOT PSP2) @@ -414,6 +433,8 @@ set(DEBUGGER_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/debugger/symbols.c ${CMAKE_CURRENT_SOURCE_DIR}/src/debugger/cli-debugger.c) +file(GLOB DEBUGGER_TEST_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/debugger/test/*.c) + set(FEATURE_SRC) set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6") @@ -520,6 +541,7 @@ if(USE_ZLIB) list(APPEND DEPENDENCY_LIB ${ZLIB_LIBRARIES}) set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},zlib1g") set(HAVE_CRC32 ON) + list(APPEND OS_LIB ${ZLIB_LIBRARIES}) else() # zlib pulls in crc32 check_function_exists(crc32 HAVE_CRC32) @@ -596,7 +618,7 @@ elseif(USE_ZLIB) endif() if (USE_LZMA) - include_directories(AFTER ${CMAKE_CURRENT_SOURCE_DIR}/third-party/lzma) + include_directories(AFTER ${CMAKE_CURRENT_SOURCE_DIR}/src/third-party/lzma) add_definitions(-D_7ZIP_PPMD_SUPPPORT) list(APPEND VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-lzma.c) set(LZMA_SRC @@ -710,6 +732,7 @@ endif() if(USE_DEBUGGERS) list(APPEND FEATURE_SRC ${DEBUGGER_SRC}) + list(APPEND TEST_SRC ${DEBUGGER_TEST_SRC}) list(APPEND FEATURES DEBUGGERS) endif() @@ -850,7 +873,7 @@ if(BUILD_OPENEMU) find_library(FOUNDATION Foundation) find_library(OPENEMUBASE OpenEmuBase) file(GLOB OE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/openemu/*.m) - add_library(${BINARY_NAME}-openemu MODULE ${CORE_SRC} ${OE_SRC}) + add_library(${BINARY_NAME}-openemu MODULE ${CORE_SRC} ${OS_SRC}) set_target_properties(${BINARY_NAME}-openemu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/openemu/Info.plist.in BUNDLE TRUE @@ -951,7 +974,8 @@ SET(CPACK_DEB_COMPONENT_INSTALL ON) set(CPACK_STRIP_FILES ${BINARY_NAME}) -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/README.md ${CMAKE_CURRENT_SOURCE_DIR}/CHANGES DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT lib${BINARY_NAME}) +file(GLOB READMES ${CMAKE_CURRENT_SOURCE_DIR}/README*.md) +install(FILES ${READMES} ${CMAKE_CURRENT_SOURCE_DIR}/CHANGES DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT lib${BINARY_NAME}) include(CPack) diff --git a/README.md b/README.md index f756ebdf7..dc6d2aeb8 100644 --- a/README.md +++ b/README.md @@ -128,11 +128,11 @@ To build on Windows for development, using MSYS2 is recommended. Follow the inst For x86 (32 bit) builds: - pacman -Sy mingw-w64-i686-{cmake,ffmpeg,gcc,gdb,imagemagick,libzip,pkg-config,qt5,SDL2} + pacman -Sy mingw-w64-i686-{cmake,ffmpeg,gcc,gdb,imagemagick,libzip,pkg-config,qt5,SDL2,ntldd-git} For x86_64 (64 bit) builds: - pacman -Sy mingw-w64-x86_64-{cmake,ffmpeg,gcc,gdb,imagemagick,libzip,pkg-config,qt5,SDL2} + pacman -Sy mingw-w64-x86_64-{cmake,ffmpeg,gcc,gdb,imagemagick,libzip,pkg-config,qt5,SDL2,ntldd-git} Check out the source code by running this command: @@ -146,7 +146,7 @@ Then finally build it by running these commands: cmake .. -G "MSYS Makefiles" make -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), a tool called "[Dependency Walker](http://dependencywalker.com)" can be used to see which additional DLL files need to be shipped with the medusa executable. +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. ### Dependencies diff --git a/README_DE.md b/README_DE.md new file mode 100644 index 000000000..dd02c90a3 --- /dev/null +++ b/README_DE.md @@ -0,0 +1,178 @@ +mGBA +==== + +mGBA ist ein Emulator für Game Boy Advance-Spiele. Das Ziel von mGBA ist, schneller und genauer als viele existierende Game Boy Advance-Emulatoren zu sein. Außerdem verfügt mGBA über Funktionen, die anderen Emulatoren fehlen. Zusätzlich werden auch Game Boy- und Game Boy Color-Spiele unterstützt. + +Aktuelle Neuigkeiten und Downloads findest Du auf [mgba.io](https://mgba.io). + +[![Build-Status](https://travis-ci.org/mgba-emu/mgba.svg?branch=master)](https://travis-ci.org/mgba-emu/mgba) + +Features +-------- + +- Nahzu vollständige Unterstützung der Game Boy Advance-Hardware[[1]](#missing). +- Unterstützung der Game Boy-/Game Boy Color-Hardware. +- Schnelle Emulation. mGBA ist dafür bekannt, auch auf schwacher Hardware wie Netbooks mit voller Geschwindigkeit zu laufen. +- Qt- und SDL-Portierungen für eine vollwertige und eine "leichtgewichtige" Benutzeroberfläche. +- Lokale (gleicher Computer) Unterstützung für Link-Kabel. +- Erkennung des Speichertypes, einschließlich der Größe des Flash-Speichers[[2]](#flashdetect). +- Unterstützung für Spielmodule mit Bewegungssensoren und Rüttel-Effekten (nur verwendbar mit Spiele-Controllern). +- Unterstützung für Echtzeituhren, selbst ohne Konfiguration. +- Unterstützung für Game Boy Printer und Game Boy Camera. +- Eingebaute BIOS-Implementierung mit der Möglichkeit, externe BIOS-Dateien zu laden. +- Turbo/Vorlauf-Unterstützung durch drücken der Tab-Taste. +- Rücklauf-Unterstützung durch drücken der Akzent-Taste. +- Frameskip von bis zu 10 Bildern. +- Unterstützung für Screenshots. +- Unterstützung für Cheat-Codes. +- 9 Speicherstände für Savestates/Spielzustände. Savestates können auch als Screenshots dargestellt werden. +- Video- und GIF-Aufzeichnung. +- Frei wählbare Tastenbelegungen für Tastaturen und Controller. +- Unterstützung für ZIP- und 7z-Archive. +- Unterstützung für Patches im IPS-, UPS- und BPS-Format. +- Spiele-Debugging über ein Kommandozeilen-Interface und IDA Pro-kompatible GDB-Unterstützung. +- Einstellbare Rücklauf-Funktion. +- Unterstützung für das Laden und Exportieren von GameShark- und Action Replay-Abbildern. +- Verfügbare Cores für RetroArch/Libretro und OpenEmu. +- Viele, viele kleinere Dinge. + +### Geplante Features + +- Unterstützung für Link-Kabel-Multiplayer über ein Netzwerk. +- Unterstützung für Link-Kabel über Dolphin/JOY-Bus. +- M4A-Audio-Abmischung für höhere Audio-Qualität. +- Unterstützung für Tool-Assisted Speedruns. +- Lua-Unterstützung für Scripting. +- Eine umfangreiche Debugging-Suite. +- e-Reader-Unterstützung. +- Unterstützung für Drahtlosadapter. + +Unterstützte Plattformen +------------------------ + +- Windows Vista oder neuer +- OS X 10.7 (Lion)[[3]](#osxver) oder neuer +- Linux +- FreeBSD +- Nintendo 3DS +- Wii +- PlayStation Vita + +Andere Unix-ähnliche Plattformen wie OpenBSD sind ebenfalls dafür bekannt, mit mGBA kompatibel zu sein. Sie sind jedoch nicht getestet und werden nicht voll unterstützt. + +### Systemvoraussetzungen + +Die Systemvoraussetzungen sind minimal. Jeder Computer, der mit Windows Vista oder neuer läuft, sollte in der Lage sein, die Emulation zu bewältigen. Unterstützung für OpenGL 1.1 oder neuer ist ebenfalls voraussgesetzt. + +Downloads +--------- + +Download-Links befinden sich in der [Downloads][downloads]-Sektion auf der offizielle Website. Der Quellcode befindet sich auf [GitHub][source]. + +Steuerung +--------- + +Die Steuerung kann im Einstellungs-Menü konfiguriert werden. Viele Spiele-Controller werden automatisch erkannt und entsprechend belegt. Für Tastaturen wird standardmäßig folgende Belegung verwendet: + +- **A**: X +- **B**: Z +- **L**: A +- **R**: S +- **Start**: Enter +- **Select**: Rücktaste + +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: + + mkdir build + cd build + cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr .. + make + 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. + +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: + + brew install cmake ffmpeg imagemagick libzip qt5 sdl2 libedit + mkdir build + cd build + cmake -DCMAKE_PREFIX_PATH='brew --prefix qt5' .. + make + +Bitte beachte, dass Du unter macOS nicht 'make install' verwenden solltest, da dies nicht korrekt funktionieren wird. + +### Für Entwickler: Kompilieren unter Windows + +Um mGBA auf Windows zu kompilieren, wird MSYS2 empfohlen. Befolge die Installationsschritte auf der [MSYS2-Website](https://msys2.github.io). Stelle sicher, dass Du die 32-Bit-Version ("MSYS2 MinGW 32-bit") (oder die 64-Bit-Version "MSYS2 MinGW 64-bit", wenn Du mGBA für x86_64 kompilieren willst) verwendest und führe folgendes Kommando (einschließlich der Klammern) aus, um alle benötigten Abhängigkeiten zu installieren. Bitte beachte, dass dafür über 500MiB an Paketen heruntergeladen werden, was eine Weile dauern kann): + +Für x86 (32 Bit): + + pacman -Sy mingw-w64-i686-{cmake,ffmpeg,gcc,gdb,imagemagick,libzip,pkg-config,qt5,SDL2,ntldd-git} + +Für x86_64 (64 Bit): + + pacman -Sy mingw-w64-x86_64-{cmake,ffmpeg,gcc,gdb,imagemagick,libzip,pkg-config,qt5,SDL2,ntldd-git} + +Lade den aktuellen mGBA-Quellcode mithilfe des folgenden Kommandos herunter: + + git clone https://github.com/mgba-emu/mgba.git + +Abschließend wird mGBA über folgende Kommandos kompiliert: + + cd mgba + mkdir build + cd build + cmake .. -G "MSYS Makefiles" + 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. + +### Abhängigkeiten + +mGBA hat keine "harten" Abhängigkeiten. Dennoch werden die folgenden optionalen Abhängigkeiten für einige Features benötigt. Diese Features werden automatisch deaktiviert, wenn die benötigten Abhängigkeiten nicht gefunden werden. + +- Qt 5: Für die Benutzeroberfläche. Qt Multimedia oder SDL werden für Audio-Ausgabe benötigt. +- SDL: Für eine einfachere Benutzeroberfläche und Spiele-Controller-Unterstützung in der Qt-Oberfläche. SDL 2 ist empfohlen, SDL 1.2 wird jedoch auch unterstützt. +- zlib und libpng: Für die Unterstützung von Bildschirmfotos und Savestates-in-PNG-Unterstützung. +- libedit: Für die Unterstützung des Kommandozeilen-Debuggers. +- ffmpeg oder libav: Für Videoaufzeichnungen. +- libzip oder zlib: Um ROMs aus ZIP-Dateien zu laden. +- ImageMagick: Für GIF-Aufzeichnungen. +- SQLite3: Für Spiele-Datenbanken. +- libelf: Für das Laden von ELF-Dateien. + +SQLite3, libpng und zlib werden mit dem Emulator mitgeliefert, sodass sie nicht zuerst kompiliert werden müssen. + +Fußnoten +-------- + +[1] Zurzeit fehlende Features sind + +- OBJ-Fenster für die Modi 3, 4 und 5 ([Bug #5](http://mgba.io/b/5)) +- Mosaik-Effekt für umgewandelte OBJs ([Bug #9](http://mgba.io/b/9)) + +[2] In manchen Fällen ist es nicht möglich, die Größe des Flash-Speichers automatisch zu ermitteln. Diese kann dann zur Laufzeit konfiguriert werden, es wird jedoch empfohlen, den Fehler zu melden. + +[3] 10.7 wird nur für die Qt-Portierung benötigt. Die SDL-Portierung ist dafür bekannt, mit 10.5 und möglicherweise auf älteren Versionen zu funktionieren. + +[downloads]: http://mgba.io/downloads.html +[source]: https://github.com/mgba-emu/mgba/ + +Copyright +--------- + +Copyright für mGBA © 2013 – 2017 Jeffrey Pfau. mGBA wird unter der [Mozilla Public License version 2.0](https://www.mozilla.org/MPL/2.0/) veröffentlicht. Eine Kopie der Lizenz ist in der mitgelieferten Datei LICENSE verfügbar. + +mGBA beinhaltet die folgenden Bibliotheken von Drittanbietern: + +- [inih](https://github.com/benhoyt/inih), Copyright © 2009 Ben Hoyt, verwendet unter einer BSD 3-clause-Lizenz. +- [blip-buf](https://code.google.com/archive/b/blip-buf), Copyright © 2003 - 2009 Shay Green, verwendet unter einer Lesser GNU Public License. +- [LZMA SDK](http://www.7-zip.org/sdk.html), Public Domain. +- [MurmurHash3](https://github.com/aappleby/smhasher), Implementierung von Austin Appleby, Public Domain. +- [getopt fot MSVC](https://github.com/skandhurkat/Getopt-for-Visual-Studio/), Public Domain. +- [SQLite3](https://www.sqlite.org), Public Domain. + +Wenn Du ein Spiele-Publisher bist und mGBA für kommerzielle Verwendung lizenzieren möchtest, schreibe bitte eine e-Mail an [licensing@mgba.io](mailto:licensing@mgba.io) für weitere Informationen. diff --git a/src/platform/python/tests/cinema/.gitignore b/cinema/.gitignore similarity index 100% rename from src/platform/python/tests/cinema/.gitignore rename to cinema/.gitignore diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/LICENSE b/cinema/gb/mooneye-gb/LICENSE similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/LICENSE rename to cinema/gb/mooneye-gb/LICENSE diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/add_sp_e_timing/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/add_sp_e_timing/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/add_sp_e_timing/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/add_sp_e_timing/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/add_sp_e_timing/test.gb b/cinema/gb/mooneye-gb/acceptance/add_sp_e_timing/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/add_sp_e_timing/test.gb rename to cinema/gb/mooneye-gb/acceptance/add_sp_e_timing/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/add_sp_e_timing/test.sym b/cinema/gb/mooneye-gb/acceptance/add_sp_e_timing/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/add_sp_e_timing/test.sym rename to cinema/gb/mooneye-gb/acceptance/add_sp_e_timing/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/bits/mem_oam/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/bits/mem_oam/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/bits/mem_oam/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/bits/mem_oam/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/bits/mem_oam/test.gb b/cinema/gb/mooneye-gb/acceptance/bits/mem_oam/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/bits/mem_oam/test.gb rename to cinema/gb/mooneye-gb/acceptance/bits/mem_oam/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/bits/mem_oam/test.sym b/cinema/gb/mooneye-gb/acceptance/bits/mem_oam/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/bits/mem_oam/test.sym rename to cinema/gb/mooneye-gb/acceptance/bits/mem_oam/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/bits/reg_f/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/bits/reg_f/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/bits/reg_f/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/bits/reg_f/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/bits/reg_f/test.gb b/cinema/gb/mooneye-gb/acceptance/bits/reg_f/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/bits/reg_f/test.gb rename to cinema/gb/mooneye-gb/acceptance/bits/reg_f/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/bits/reg_f/test.sym b/cinema/gb/mooneye-gb/acceptance/bits/reg_f/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/bits/reg_f/test.sym rename to cinema/gb/mooneye-gb/acceptance/bits/reg_f/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/bits/unused_hwio-GS/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/bits/unused_hwio-GS/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/bits/unused_hwio-GS/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/bits/unused_hwio-GS/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/bits/unused_hwio-GS/test.gb b/cinema/gb/mooneye-gb/acceptance/bits/unused_hwio-GS/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/bits/unused_hwio-GS/test.gb rename to cinema/gb/mooneye-gb/acceptance/bits/unused_hwio-GS/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/bits/unused_hwio-GS/test.sym b/cinema/gb/mooneye-gb/acceptance/bits/unused_hwio-GS/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/bits/unused_hwio-GS/test.sym rename to cinema/gb/mooneye-gb/acceptance/bits/unused_hwio-GS/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_hwio-S/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/boot_hwio-S/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_hwio-S/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/boot_hwio-S/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_hwio-S/manifest.yml b/cinema/gb/mooneye-gb/acceptance/boot_hwio-S/manifest.yml similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_hwio-S/manifest.yml rename to cinema/gb/mooneye-gb/acceptance/boot_hwio-S/manifest.yml diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_hwio-S/test.gb b/cinema/gb/mooneye-gb/acceptance/boot_hwio-S/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_hwio-S/test.gb rename to cinema/gb/mooneye-gb/acceptance/boot_hwio-S/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_hwio-S/test.sym b/cinema/gb/mooneye-gb/acceptance/boot_hwio-S/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_hwio-S/test.sym rename to cinema/gb/mooneye-gb/acceptance/boot_hwio-S/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_hwio-dmg0/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/boot_hwio-dmg0/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_hwio-dmg0/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/boot_hwio-dmg0/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_hwio-dmg0/manifest.yml b/cinema/gb/mooneye-gb/acceptance/boot_hwio-dmg0/manifest.yml similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_hwio-dmg0/manifest.yml rename to cinema/gb/mooneye-gb/acceptance/boot_hwio-dmg0/manifest.yml diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_hwio-dmg0/test.gb b/cinema/gb/mooneye-gb/acceptance/boot_hwio-dmg0/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_hwio-dmg0/test.gb rename to cinema/gb/mooneye-gb/acceptance/boot_hwio-dmg0/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_hwio-dmg0/test.sym b/cinema/gb/mooneye-gb/acceptance/boot_hwio-dmg0/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_hwio-dmg0/test.sym rename to cinema/gb/mooneye-gb/acceptance/boot_hwio-dmg0/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_hwio-dmgABCXmgb/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/boot_hwio-dmgABCXmgb/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_hwio-dmgABCXmgb/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/boot_hwio-dmgABCXmgb/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_hwio-dmgABCXmgb/test.gb b/cinema/gb/mooneye-gb/acceptance/boot_hwio-dmgABCXmgb/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_hwio-dmgABCXmgb/test.gb rename to cinema/gb/mooneye-gb/acceptance/boot_hwio-dmgABCXmgb/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_hwio-dmgABCXmgb/test.sav b/cinema/gb/mooneye-gb/acceptance/boot_hwio-dmgABCXmgb/test.sav similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_hwio-dmgABCXmgb/test.sav rename to cinema/gb/mooneye-gb/acceptance/boot_hwio-dmgABCXmgb/test.sav diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_hwio-dmgABCXmgb/test.sym b/cinema/gb/mooneye-gb/acceptance/boot_hwio-dmgABCXmgb/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_hwio-dmgABCXmgb/test.sym rename to cinema/gb/mooneye-gb/acceptance/boot_hwio-dmgABCXmgb/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-dmg/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/boot_regs-dmg/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-dmg/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/boot_regs-dmg/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-dmg/test.gb b/cinema/gb/mooneye-gb/acceptance/boot_regs-dmg/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-dmg/test.gb rename to cinema/gb/mooneye-gb/acceptance/boot_regs-dmg/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-dmg/test.sym b/cinema/gb/mooneye-gb/acceptance/boot_regs-dmg/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-dmg/test.sym rename to cinema/gb/mooneye-gb/acceptance/boot_regs-dmg/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-dmg0/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/boot_regs-dmg0/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-dmg0/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/boot_regs-dmg0/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-dmg0/manifest.yml b/cinema/gb/mooneye-gb/acceptance/boot_regs-dmg0/manifest.yml similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-dmg0/manifest.yml rename to cinema/gb/mooneye-gb/acceptance/boot_regs-dmg0/manifest.yml diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-dmg0/test.gb b/cinema/gb/mooneye-gb/acceptance/boot_regs-dmg0/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-dmg0/test.gb rename to cinema/gb/mooneye-gb/acceptance/boot_regs-dmg0/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-dmg0/test.sym b/cinema/gb/mooneye-gb/acceptance/boot_regs-dmg0/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-dmg0/test.sym rename to cinema/gb/mooneye-gb/acceptance/boot_regs-dmg0/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-dmgABCX/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/boot_regs-dmgABCX/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-dmgABCX/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/boot_regs-dmgABCX/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-dmgABCX/test.gb b/cinema/gb/mooneye-gb/acceptance/boot_regs-dmgABCX/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-dmgABCX/test.gb rename to cinema/gb/mooneye-gb/acceptance/boot_regs-dmgABCX/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-dmgABCX/test.sym b/cinema/gb/mooneye-gb/acceptance/boot_regs-dmgABCX/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-dmgABCX/test.sym rename to cinema/gb/mooneye-gb/acceptance/boot_regs-dmgABCX/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-mgb/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/boot_regs-mgb/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-mgb/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/boot_regs-mgb/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-mgb/manifest.yml b/cinema/gb/mooneye-gb/acceptance/boot_regs-mgb/manifest.yml similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-mgb/manifest.yml rename to cinema/gb/mooneye-gb/acceptance/boot_regs-mgb/manifest.yml diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-mgb/test.gb b/cinema/gb/mooneye-gb/acceptance/boot_regs-mgb/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-mgb/test.gb rename to cinema/gb/mooneye-gb/acceptance/boot_regs-mgb/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-mgb/test.sym b/cinema/gb/mooneye-gb/acceptance/boot_regs-mgb/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-mgb/test.sym rename to cinema/gb/mooneye-gb/acceptance/boot_regs-mgb/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-sgb/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/boot_regs-sgb/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-sgb/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/boot_regs-sgb/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-sgb/manifest.yml b/cinema/gb/mooneye-gb/acceptance/boot_regs-sgb/manifest.yml similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-sgb/manifest.yml rename to cinema/gb/mooneye-gb/acceptance/boot_regs-sgb/manifest.yml diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-sgb/test.gb b/cinema/gb/mooneye-gb/acceptance/boot_regs-sgb/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-sgb/test.gb rename to cinema/gb/mooneye-gb/acceptance/boot_regs-sgb/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-sgb/test.sym b/cinema/gb/mooneye-gb/acceptance/boot_regs-sgb/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-sgb/test.sym rename to cinema/gb/mooneye-gb/acceptance/boot_regs-sgb/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-sgb2/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/boot_regs-sgb2/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-sgb2/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/boot_regs-sgb2/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-sgb2/manifest.yml b/cinema/gb/mooneye-gb/acceptance/boot_regs-sgb2/manifest.yml similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-sgb2/manifest.yml rename to cinema/gb/mooneye-gb/acceptance/boot_regs-sgb2/manifest.yml diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-sgb2/test.gb b/cinema/gb/mooneye-gb/acceptance/boot_regs-sgb2/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-sgb2/test.gb rename to cinema/gb/mooneye-gb/acceptance/boot_regs-sgb2/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-sgb2/test.sym b/cinema/gb/mooneye-gb/acceptance/boot_regs-sgb2/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/boot_regs-sgb2/test.sym rename to cinema/gb/mooneye-gb/acceptance/boot_regs-sgb2/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/call_cc_timing/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/call_cc_timing/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/call_cc_timing/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/call_cc_timing/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/call_cc_timing/test.gb b/cinema/gb/mooneye-gb/acceptance/call_cc_timing/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/call_cc_timing/test.gb rename to cinema/gb/mooneye-gb/acceptance/call_cc_timing/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/call_cc_timing/test.sym b/cinema/gb/mooneye-gb/acceptance/call_cc_timing/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/call_cc_timing/test.sym rename to cinema/gb/mooneye-gb/acceptance/call_cc_timing/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/call_cc_timing2/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/call_cc_timing2/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/call_cc_timing2/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/call_cc_timing2/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/call_cc_timing2/test.gb b/cinema/gb/mooneye-gb/acceptance/call_cc_timing2/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/call_cc_timing2/test.gb rename to cinema/gb/mooneye-gb/acceptance/call_cc_timing2/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/call_cc_timing2/test.sym b/cinema/gb/mooneye-gb/acceptance/call_cc_timing2/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/call_cc_timing2/test.sym rename to cinema/gb/mooneye-gb/acceptance/call_cc_timing2/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/call_timing/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/call_timing/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/call_timing/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/call_timing/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/call_timing/test.gb b/cinema/gb/mooneye-gb/acceptance/call_timing/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/call_timing/test.gb rename to cinema/gb/mooneye-gb/acceptance/call_timing/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/call_timing/test.sym b/cinema/gb/mooneye-gb/acceptance/call_timing/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/call_timing/test.sym rename to cinema/gb/mooneye-gb/acceptance/call_timing/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/call_timing2/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/call_timing2/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/call_timing2/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/call_timing2/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/call_timing2/test.gb b/cinema/gb/mooneye-gb/acceptance/call_timing2/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/call_timing2/test.gb rename to cinema/gb/mooneye-gb/acceptance/call_timing2/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/call_timing2/test.sym b/cinema/gb/mooneye-gb/acceptance/call_timing2/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/call_timing2/test.sym rename to cinema/gb/mooneye-gb/acceptance/call_timing2/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/di_timing-GS/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/di_timing-GS/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/di_timing-GS/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/di_timing-GS/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/di_timing-GS/test.gb b/cinema/gb/mooneye-gb/acceptance/di_timing-GS/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/di_timing-GS/test.gb rename to cinema/gb/mooneye-gb/acceptance/di_timing-GS/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/di_timing-GS/test.sym b/cinema/gb/mooneye-gb/acceptance/di_timing-GS/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/di_timing-GS/test.sym rename to cinema/gb/mooneye-gb/acceptance/di_timing-GS/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/div_timing/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/div_timing/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/div_timing/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/div_timing/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/div_timing/test.gb b/cinema/gb/mooneye-gb/acceptance/div_timing/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/div_timing/test.gb rename to cinema/gb/mooneye-gb/acceptance/div_timing/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/div_timing/test.sym b/cinema/gb/mooneye-gb/acceptance/div_timing/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/div_timing/test.sym rename to cinema/gb/mooneye-gb/acceptance/div_timing/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/ei_timing/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/ei_timing/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/ei_timing/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/ei_timing/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/ei_timing/test.gb b/cinema/gb/mooneye-gb/acceptance/ei_timing/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/ei_timing/test.gb rename to cinema/gb/mooneye-gb/acceptance/ei_timing/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/ei_timing/test.sym b/cinema/gb/mooneye-gb/acceptance/ei_timing/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/ei_timing/test.sym rename to cinema/gb/mooneye-gb/acceptance/ei_timing/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/hblank_ly_scx_timing-GS/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/gpu/hblank_ly_scx_timing-GS/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/hblank_ly_scx_timing-GS/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/gpu/hblank_ly_scx_timing-GS/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/hblank_ly_scx_timing-GS/manifest.yml b/cinema/gb/mooneye-gb/acceptance/gpu/hblank_ly_scx_timing-GS/manifest.yml similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/hblank_ly_scx_timing-GS/manifest.yml rename to cinema/gb/mooneye-gb/acceptance/gpu/hblank_ly_scx_timing-GS/manifest.yml diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/hblank_ly_scx_timing-GS/test.gb b/cinema/gb/mooneye-gb/acceptance/gpu/hblank_ly_scx_timing-GS/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/hblank_ly_scx_timing-GS/test.gb rename to cinema/gb/mooneye-gb/acceptance/gpu/hblank_ly_scx_timing-GS/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/hblank_ly_scx_timing-GS/test.sym b/cinema/gb/mooneye-gb/acceptance/gpu/hblank_ly_scx_timing-GS/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/hblank_ly_scx_timing-GS/test.sym rename to cinema/gb/mooneye-gb/acceptance/gpu/hblank_ly_scx_timing-GS/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_1_2_timing-GS/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/gpu/intr_1_2_timing-GS/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_1_2_timing-GS/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/gpu/intr_1_2_timing-GS/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_1_2_timing-GS/test.gb b/cinema/gb/mooneye-gb/acceptance/gpu/intr_1_2_timing-GS/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_1_2_timing-GS/test.gb rename to cinema/gb/mooneye-gb/acceptance/gpu/intr_1_2_timing-GS/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_1_2_timing-GS/test.sym b/cinema/gb/mooneye-gb/acceptance/gpu/intr_1_2_timing-GS/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_1_2_timing-GS/test.sym rename to cinema/gb/mooneye-gb/acceptance/gpu/intr_1_2_timing-GS/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_0_timing/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_0_timing/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_0_timing/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/gpu/intr_2_0_timing/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_0_timing/test.gb b/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_0_timing/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_0_timing/test.gb rename to cinema/gb/mooneye-gb/acceptance/gpu/intr_2_0_timing/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_0_timing/test.sym b/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_0_timing/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_0_timing/test.sym rename to cinema/gb/mooneye-gb/acceptance/gpu/intr_2_0_timing/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode0_timing/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode0_timing/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode0_timing/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode0_timing/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode0_timing/test.gb b/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode0_timing/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode0_timing/test.gb rename to cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode0_timing/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode0_timing/test.sym b/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode0_timing/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode0_timing/test.sym rename to cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode0_timing/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode0_timing_sprites/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode0_timing_sprites/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode0_timing_sprites/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode0_timing_sprites/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode0_timing_sprites/test.gb b/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode0_timing_sprites/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode0_timing_sprites/test.gb rename to cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode0_timing_sprites/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode0_timing_sprites/test.sym b/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode0_timing_sprites/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode0_timing_sprites/test.sym rename to cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode0_timing_sprites/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode3_timing/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode3_timing/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode3_timing/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode3_timing/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode3_timing/test.gb b/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode3_timing/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode3_timing/test.gb rename to cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode3_timing/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode3_timing/test.sym b/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode3_timing/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode3_timing/test.sym rename to cinema/gb/mooneye-gb/acceptance/gpu/intr_2_mode3_timing/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_oam_ok_timing/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_oam_ok_timing/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_oam_ok_timing/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/gpu/intr_2_oam_ok_timing/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_oam_ok_timing/test.gb b/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_oam_ok_timing/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_oam_ok_timing/test.gb rename to cinema/gb/mooneye-gb/acceptance/gpu/intr_2_oam_ok_timing/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_oam_ok_timing/test.sym b/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_oam_ok_timing/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/intr_2_oam_ok_timing/test.sym rename to cinema/gb/mooneye-gb/acceptance/gpu/intr_2_oam_ok_timing/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/lcdon_timing-dmgABCXmgbS/manifest.yml b/cinema/gb/mooneye-gb/acceptance/gpu/lcdon_timing-dmgABCXmgbS/manifest.yml similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/lcdon_timing-dmgABCXmgbS/manifest.yml rename to cinema/gb/mooneye-gb/acceptance/gpu/lcdon_timing-dmgABCXmgbS/manifest.yml diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/lcdon_timing-dmgABCXmgbS/test.gb b/cinema/gb/mooneye-gb/acceptance/gpu/lcdon_timing-dmgABCXmgbS/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/lcdon_timing-dmgABCXmgbS/test.gb rename to cinema/gb/mooneye-gb/acceptance/gpu/lcdon_timing-dmgABCXmgbS/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/lcdon_timing-dmgABCXmgbS/test.sym b/cinema/gb/mooneye-gb/acceptance/gpu/lcdon_timing-dmgABCXmgbS/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/lcdon_timing-dmgABCXmgbS/test.sym rename to cinema/gb/mooneye-gb/acceptance/gpu/lcdon_timing-dmgABCXmgbS/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/lcdon_write_timing-GS/manifest.yml b/cinema/gb/mooneye-gb/acceptance/gpu/lcdon_write_timing-GS/manifest.yml similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/lcdon_write_timing-GS/manifest.yml rename to cinema/gb/mooneye-gb/acceptance/gpu/lcdon_write_timing-GS/manifest.yml diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/lcdon_write_timing-GS/test.gb b/cinema/gb/mooneye-gb/acceptance/gpu/lcdon_write_timing-GS/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/lcdon_write_timing-GS/test.gb rename to cinema/gb/mooneye-gb/acceptance/gpu/lcdon_write_timing-GS/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/lcdon_write_timing-GS/test.sym b/cinema/gb/mooneye-gb/acceptance/gpu/lcdon_write_timing-GS/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/lcdon_write_timing-GS/test.sym rename to cinema/gb/mooneye-gb/acceptance/gpu/lcdon_write_timing-GS/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/stat_irq_blocking/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/gpu/stat_irq_blocking/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/stat_irq_blocking/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/gpu/stat_irq_blocking/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/stat_irq_blocking/test.gb b/cinema/gb/mooneye-gb/acceptance/gpu/stat_irq_blocking/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/stat_irq_blocking/test.gb rename to cinema/gb/mooneye-gb/acceptance/gpu/stat_irq_blocking/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/stat_irq_blocking/test.sym b/cinema/gb/mooneye-gb/acceptance/gpu/stat_irq_blocking/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/stat_irq_blocking/test.sym rename to cinema/gb/mooneye-gb/acceptance/gpu/stat_irq_blocking/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/vblank_stat_intr-GS/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/gpu/vblank_stat_intr-GS/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/vblank_stat_intr-GS/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/gpu/vblank_stat_intr-GS/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/vblank_stat_intr-GS/test.gb b/cinema/gb/mooneye-gb/acceptance/gpu/vblank_stat_intr-GS/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/vblank_stat_intr-GS/test.gb rename to cinema/gb/mooneye-gb/acceptance/gpu/vblank_stat_intr-GS/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/vblank_stat_intr-GS/test.sym b/cinema/gb/mooneye-gb/acceptance/gpu/vblank_stat_intr-GS/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/gpu/vblank_stat_intr-GS/test.sym rename to cinema/gb/mooneye-gb/acceptance/gpu/vblank_stat_intr-GS/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/halt_ime0_ei/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/halt_ime0_ei/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/halt_ime0_ei/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/halt_ime0_ei/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/halt_ime0_ei/test.gb b/cinema/gb/mooneye-gb/acceptance/halt_ime0_ei/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/halt_ime0_ei/test.gb rename to cinema/gb/mooneye-gb/acceptance/halt_ime0_ei/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/halt_ime0_ei/test.sym b/cinema/gb/mooneye-gb/acceptance/halt_ime0_ei/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/halt_ime0_ei/test.sym rename to cinema/gb/mooneye-gb/acceptance/halt_ime0_ei/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/halt_ime0_nointr_timing/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/halt_ime0_nointr_timing/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/halt_ime0_nointr_timing/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/halt_ime0_nointr_timing/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/halt_ime0_nointr_timing/test.gb b/cinema/gb/mooneye-gb/acceptance/halt_ime0_nointr_timing/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/halt_ime0_nointr_timing/test.gb rename to cinema/gb/mooneye-gb/acceptance/halt_ime0_nointr_timing/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/halt_ime0_nointr_timing/test.sym b/cinema/gb/mooneye-gb/acceptance/halt_ime0_nointr_timing/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/halt_ime0_nointr_timing/test.sym rename to cinema/gb/mooneye-gb/acceptance/halt_ime0_nointr_timing/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/halt_ime1_timing/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/halt_ime1_timing/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/halt_ime1_timing/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/halt_ime1_timing/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/halt_ime1_timing/test.gb b/cinema/gb/mooneye-gb/acceptance/halt_ime1_timing/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/halt_ime1_timing/test.gb rename to cinema/gb/mooneye-gb/acceptance/halt_ime1_timing/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/halt_ime1_timing/test.sym b/cinema/gb/mooneye-gb/acceptance/halt_ime1_timing/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/halt_ime1_timing/test.sym rename to cinema/gb/mooneye-gb/acceptance/halt_ime1_timing/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/halt_ime1_timing2-GS/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/halt_ime1_timing2-GS/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/halt_ime1_timing2-GS/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/halt_ime1_timing2-GS/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/halt_ime1_timing2-GS/test.gb b/cinema/gb/mooneye-gb/acceptance/halt_ime1_timing2-GS/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/halt_ime1_timing2-GS/test.gb rename to cinema/gb/mooneye-gb/acceptance/halt_ime1_timing2-GS/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/halt_ime1_timing2-GS/test.sym b/cinema/gb/mooneye-gb/acceptance/halt_ime1_timing2-GS/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/halt_ime1_timing2-GS/test.sym rename to cinema/gb/mooneye-gb/acceptance/halt_ime1_timing2-GS/test.sym diff --git a/cinema/gb/mooneye-gb/acceptance/hdma_lcdc/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/hdma_lcdc/baseline_0000.png new file mode 100644 index 000000000..c1dac62b3 Binary files /dev/null and b/cinema/gb/mooneye-gb/acceptance/hdma_lcdc/baseline_0000.png differ diff --git a/cinema/gb/mooneye-gb/acceptance/hdma_lcdc/manifest.yml b/cinema/gb/mooneye-gb/acceptance/hdma_lcdc/manifest.yml new file mode 100644 index 000000000..6cd567986 --- /dev/null +++ b/cinema/gb/mooneye-gb/acceptance/hdma_lcdc/manifest.yml @@ -0,0 +1,2 @@ +config: + gb.model: CGB diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/hdma_lcdc/test.gb b/cinema/gb/mooneye-gb/acceptance/hdma_lcdc/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/hdma_lcdc/test.gb rename to cinema/gb/mooneye-gb/acceptance/hdma_lcdc/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/hdma_lcdc/test.sym b/cinema/gb/mooneye-gb/acceptance/hdma_lcdc/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/hdma_lcdc/test.sym rename to cinema/gb/mooneye-gb/acceptance/hdma_lcdc/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/if_ie_registers/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/if_ie_registers/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/if_ie_registers/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/if_ie_registers/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/if_ie_registers/test.gb b/cinema/gb/mooneye-gb/acceptance/if_ie_registers/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/if_ie_registers/test.gb rename to cinema/gb/mooneye-gb/acceptance/if_ie_registers/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/if_ie_registers/test.sym b/cinema/gb/mooneye-gb/acceptance/if_ie_registers/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/if_ie_registers/test.sym rename to cinema/gb/mooneye-gb/acceptance/if_ie_registers/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/interrupts/ie_push/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/interrupts/ie_push/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/interrupts/ie_push/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/interrupts/ie_push/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/interrupts/ie_push/test.gb b/cinema/gb/mooneye-gb/acceptance/interrupts/ie_push/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/interrupts/ie_push/test.gb rename to cinema/gb/mooneye-gb/acceptance/interrupts/ie_push/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/interrupts/ie_push/test.sym b/cinema/gb/mooneye-gb/acceptance/interrupts/ie_push/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/interrupts/ie_push/test.sym rename to cinema/gb/mooneye-gb/acceptance/interrupts/ie_push/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/intr_timing/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/intr_timing/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/intr_timing/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/intr_timing/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/intr_timing/test.gb b/cinema/gb/mooneye-gb/acceptance/intr_timing/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/intr_timing/test.gb rename to cinema/gb/mooneye-gb/acceptance/intr_timing/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/intr_timing/test.sym b/cinema/gb/mooneye-gb/acceptance/intr_timing/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/intr_timing/test.sym rename to cinema/gb/mooneye-gb/acceptance/intr_timing/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/jp_cc_timing/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/jp_cc_timing/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/jp_cc_timing/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/jp_cc_timing/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/jp_cc_timing/test.gb b/cinema/gb/mooneye-gb/acceptance/jp_cc_timing/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/jp_cc_timing/test.gb rename to cinema/gb/mooneye-gb/acceptance/jp_cc_timing/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/jp_cc_timing/test.sym b/cinema/gb/mooneye-gb/acceptance/jp_cc_timing/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/jp_cc_timing/test.sym rename to cinema/gb/mooneye-gb/acceptance/jp_cc_timing/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/jp_timing/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/jp_timing/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/jp_timing/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/jp_timing/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/jp_timing/test.gb b/cinema/gb/mooneye-gb/acceptance/jp_timing/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/jp_timing/test.gb rename to cinema/gb/mooneye-gb/acceptance/jp_timing/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/jp_timing/test.sym b/cinema/gb/mooneye-gb/acceptance/jp_timing/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/jp_timing/test.sym rename to cinema/gb/mooneye-gb/acceptance/jp_timing/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/ld_hl_sp_e_timing/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/ld_hl_sp_e_timing/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/ld_hl_sp_e_timing/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/ld_hl_sp_e_timing/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/ld_hl_sp_e_timing/test.gb b/cinema/gb/mooneye-gb/acceptance/ld_hl_sp_e_timing/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/ld_hl_sp_e_timing/test.gb rename to cinema/gb/mooneye-gb/acceptance/ld_hl_sp_e_timing/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/ld_hl_sp_e_timing/test.sym b/cinema/gb/mooneye-gb/acceptance/ld_hl_sp_e_timing/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/ld_hl_sp_e_timing/test.sym rename to cinema/gb/mooneye-gb/acceptance/ld_hl_sp_e_timing/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/oam_dma_restart/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/oam_dma_restart/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/oam_dma_restart/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/oam_dma_restart/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/oam_dma_restart/test.gb b/cinema/gb/mooneye-gb/acceptance/oam_dma_restart/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/oam_dma_restart/test.gb rename to cinema/gb/mooneye-gb/acceptance/oam_dma_restart/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/oam_dma_restart/test.sym b/cinema/gb/mooneye-gb/acceptance/oam_dma_restart/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/oam_dma_restart/test.sym rename to cinema/gb/mooneye-gb/acceptance/oam_dma_restart/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/oam_dma_start/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/oam_dma_start/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/oam_dma_start/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/oam_dma_start/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/hdma_lcdc/manifest.yml b/cinema/gb/mooneye-gb/acceptance/oam_dma_start/manifest.yml similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/hdma_lcdc/manifest.yml rename to cinema/gb/mooneye-gb/acceptance/oam_dma_start/manifest.yml diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/oam_dma_start/test.gb b/cinema/gb/mooneye-gb/acceptance/oam_dma_start/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/oam_dma_start/test.gb rename to cinema/gb/mooneye-gb/acceptance/oam_dma_start/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/oam_dma_start/test.sym b/cinema/gb/mooneye-gb/acceptance/oam_dma_start/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/oam_dma_start/test.sym rename to cinema/gb/mooneye-gb/acceptance/oam_dma_start/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/oam_dma_timing/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/oam_dma_timing/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/oam_dma_timing/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/oam_dma_timing/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/oam_dma_timing/test.gb b/cinema/gb/mooneye-gb/acceptance/oam_dma_timing/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/oam_dma_timing/test.gb rename to cinema/gb/mooneye-gb/acceptance/oam_dma_timing/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/oam_dma_timing/test.sym b/cinema/gb/mooneye-gb/acceptance/oam_dma_timing/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/oam_dma_timing/test.sym rename to cinema/gb/mooneye-gb/acceptance/oam_dma_timing/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/pop_timing/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/pop_timing/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/pop_timing/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/pop_timing/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/pop_timing/test.gb b/cinema/gb/mooneye-gb/acceptance/pop_timing/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/pop_timing/test.gb rename to cinema/gb/mooneye-gb/acceptance/pop_timing/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/pop_timing/test.sym b/cinema/gb/mooneye-gb/acceptance/pop_timing/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/pop_timing/test.sym rename to cinema/gb/mooneye-gb/acceptance/pop_timing/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/push_timing/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/push_timing/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/push_timing/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/push_timing/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/oam_dma_start/manifest.yml b/cinema/gb/mooneye-gb/acceptance/push_timing/manifest.yml similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/oam_dma_start/manifest.yml rename to cinema/gb/mooneye-gb/acceptance/push_timing/manifest.yml diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/push_timing/test.gb b/cinema/gb/mooneye-gb/acceptance/push_timing/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/push_timing/test.gb rename to cinema/gb/mooneye-gb/acceptance/push_timing/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/push_timing/test.sym b/cinema/gb/mooneye-gb/acceptance/push_timing/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/push_timing/test.sym rename to cinema/gb/mooneye-gb/acceptance/push_timing/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/rapid_di_ei/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/rapid_di_ei/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/rapid_di_ei/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/rapid_di_ei/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/push_timing/manifest.yml b/cinema/gb/mooneye-gb/acceptance/rapid_di_ei/manifest.yml similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/push_timing/manifest.yml rename to cinema/gb/mooneye-gb/acceptance/rapid_di_ei/manifest.yml diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/rapid_di_ei/test.gb b/cinema/gb/mooneye-gb/acceptance/rapid_di_ei/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/rapid_di_ei/test.gb rename to cinema/gb/mooneye-gb/acceptance/rapid_di_ei/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/rapid_di_ei/test.sym b/cinema/gb/mooneye-gb/acceptance/rapid_di_ei/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/rapid_di_ei/test.sym rename to cinema/gb/mooneye-gb/acceptance/rapid_di_ei/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/ret_cc_timing/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/ret_cc_timing/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/ret_cc_timing/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/ret_cc_timing/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/ret_cc_timing/test.gb b/cinema/gb/mooneye-gb/acceptance/ret_cc_timing/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/ret_cc_timing/test.gb rename to cinema/gb/mooneye-gb/acceptance/ret_cc_timing/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/ret_cc_timing/test.sym b/cinema/gb/mooneye-gb/acceptance/ret_cc_timing/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/ret_cc_timing/test.sym rename to cinema/gb/mooneye-gb/acceptance/ret_cc_timing/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/ret_timing/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/ret_timing/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/ret_timing/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/ret_timing/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/ret_timing/test.gb b/cinema/gb/mooneye-gb/acceptance/ret_timing/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/ret_timing/test.gb rename to cinema/gb/mooneye-gb/acceptance/ret_timing/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/ret_timing/test.sym b/cinema/gb/mooneye-gb/acceptance/ret_timing/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/ret_timing/test.sym rename to cinema/gb/mooneye-gb/acceptance/ret_timing/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/reti_intr_timing/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/reti_intr_timing/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/reti_intr_timing/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/reti_intr_timing/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/reti_intr_timing/test.gb b/cinema/gb/mooneye-gb/acceptance/reti_intr_timing/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/reti_intr_timing/test.gb rename to cinema/gb/mooneye-gb/acceptance/reti_intr_timing/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/reti_intr_timing/test.sym b/cinema/gb/mooneye-gb/acceptance/reti_intr_timing/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/reti_intr_timing/test.sym rename to cinema/gb/mooneye-gb/acceptance/reti_intr_timing/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/reti_timing/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/reti_timing/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/reti_timing/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/reti_timing/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/reti_timing/test.gb b/cinema/gb/mooneye-gb/acceptance/reti_timing/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/reti_timing/test.gb rename to cinema/gb/mooneye-gb/acceptance/reti_timing/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/reti_timing/test.sym b/cinema/gb/mooneye-gb/acceptance/reti_timing/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/reti_timing/test.sym rename to cinema/gb/mooneye-gb/acceptance/reti_timing/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/rst_timing/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/rst_timing/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/rst_timing/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/rst_timing/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/rst_timing/test.gb b/cinema/gb/mooneye-gb/acceptance/rst_timing/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/rst_timing/test.gb rename to cinema/gb/mooneye-gb/acceptance/rst_timing/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/rst_timing/test.sym b/cinema/gb/mooneye-gb/acceptance/rst_timing/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/rst_timing/test.sym rename to cinema/gb/mooneye-gb/acceptance/rst_timing/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/rapid_di_ei/manifest.yml b/cinema/gb/mooneye-gb/acceptance/serial/boot_sclk_align-dmgABCXmgb/manifest.yml similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/rapid_di_ei/manifest.yml rename to cinema/gb/mooneye-gb/acceptance/serial/boot_sclk_align-dmgABCXmgb/manifest.yml diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/serial/boot_sclk_align-dmgABCXmgb/test.gb b/cinema/gb/mooneye-gb/acceptance/serial/boot_sclk_align-dmgABCXmgb/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/serial/boot_sclk_align-dmgABCXmgb/test.gb rename to cinema/gb/mooneye-gb/acceptance/serial/boot_sclk_align-dmgABCXmgb/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/serial/boot_sclk_align-dmgABCXmgb/test.sav b/cinema/gb/mooneye-gb/acceptance/serial/boot_sclk_align-dmgABCXmgb/test.sav similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/serial/boot_sclk_align-dmgABCXmgb/test.sav rename to cinema/gb/mooneye-gb/acceptance/serial/boot_sclk_align-dmgABCXmgb/test.sav diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/serial/boot_sclk_align-dmgABCXmgb/test.sym b/cinema/gb/mooneye-gb/acceptance/serial/boot_sclk_align-dmgABCXmgb/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/serial/boot_sclk_align-dmgABCXmgb/test.sym rename to cinema/gb/mooneye-gb/acceptance/serial/boot_sclk_align-dmgABCXmgb/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/div_write/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/timer/div_write/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/div_write/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/timer/div_write/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/div_write/test.gb b/cinema/gb/mooneye-gb/acceptance/timer/div_write/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/div_write/test.gb rename to cinema/gb/mooneye-gb/acceptance/timer/div_write/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/div_write/test.sym b/cinema/gb/mooneye-gb/acceptance/timer/div_write/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/div_write/test.sym rename to cinema/gb/mooneye-gb/acceptance/timer/div_write/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/rapid_toggle/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/timer/rapid_toggle/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/rapid_toggle/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/timer/rapid_toggle/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/serial/boot_sclk_align-dmgABCXmgb/manifest.yml b/cinema/gb/mooneye-gb/acceptance/timer/rapid_toggle/manifest.yml similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/serial/boot_sclk_align-dmgABCXmgb/manifest.yml rename to cinema/gb/mooneye-gb/acceptance/timer/rapid_toggle/manifest.yml diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/rapid_toggle/test.gb b/cinema/gb/mooneye-gb/acceptance/timer/rapid_toggle/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/rapid_toggle/test.gb rename to cinema/gb/mooneye-gb/acceptance/timer/rapid_toggle/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/rapid_toggle/test.sym b/cinema/gb/mooneye-gb/acceptance/timer/rapid_toggle/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/rapid_toggle/test.sym rename to cinema/gb/mooneye-gb/acceptance/timer/rapid_toggle/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim00/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/timer/tim00/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim00/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/timer/tim00/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim00/test.gb b/cinema/gb/mooneye-gb/acceptance/timer/tim00/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim00/test.gb rename to cinema/gb/mooneye-gb/acceptance/timer/tim00/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim00/test.sym b/cinema/gb/mooneye-gb/acceptance/timer/tim00/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim00/test.sym rename to cinema/gb/mooneye-gb/acceptance/timer/tim00/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim00_div_trigger/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/timer/tim00_div_trigger/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim00_div_trigger/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/timer/tim00_div_trigger/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim00_div_trigger/test.gb b/cinema/gb/mooneye-gb/acceptance/timer/tim00_div_trigger/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim00_div_trigger/test.gb rename to cinema/gb/mooneye-gb/acceptance/timer/tim00_div_trigger/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim00_div_trigger/test.sym b/cinema/gb/mooneye-gb/acceptance/timer/tim00_div_trigger/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim00_div_trigger/test.sym rename to cinema/gb/mooneye-gb/acceptance/timer/tim00_div_trigger/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim01/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/timer/tim01/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim01/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/timer/tim01/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim01/test.gb b/cinema/gb/mooneye-gb/acceptance/timer/tim01/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim01/test.gb rename to cinema/gb/mooneye-gb/acceptance/timer/tim01/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim01/test.sym b/cinema/gb/mooneye-gb/acceptance/timer/tim01/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim01/test.sym rename to cinema/gb/mooneye-gb/acceptance/timer/tim01/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim01_div_trigger/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/timer/tim01_div_trigger/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim01_div_trigger/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/timer/tim01_div_trigger/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/rapid_toggle/manifest.yml b/cinema/gb/mooneye-gb/acceptance/timer/tim01_div_trigger/manifest.yml similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/rapid_toggle/manifest.yml rename to cinema/gb/mooneye-gb/acceptance/timer/tim01_div_trigger/manifest.yml diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim01_div_trigger/test.gb b/cinema/gb/mooneye-gb/acceptance/timer/tim01_div_trigger/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim01_div_trigger/test.gb rename to cinema/gb/mooneye-gb/acceptance/timer/tim01_div_trigger/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim01_div_trigger/test.sym b/cinema/gb/mooneye-gb/acceptance/timer/tim01_div_trigger/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim01_div_trigger/test.sym rename to cinema/gb/mooneye-gb/acceptance/timer/tim01_div_trigger/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim10/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/timer/tim10/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim10/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/timer/tim10/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim10/test.gb b/cinema/gb/mooneye-gb/acceptance/timer/tim10/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim10/test.gb rename to cinema/gb/mooneye-gb/acceptance/timer/tim10/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim10/test.sym b/cinema/gb/mooneye-gb/acceptance/timer/tim10/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim10/test.sym rename to cinema/gb/mooneye-gb/acceptance/timer/tim10/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim10_div_trigger/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/timer/tim10_div_trigger/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim10_div_trigger/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/timer/tim10_div_trigger/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim10_div_trigger/test.gb b/cinema/gb/mooneye-gb/acceptance/timer/tim10_div_trigger/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim10_div_trigger/test.gb rename to cinema/gb/mooneye-gb/acceptance/timer/tim10_div_trigger/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim10_div_trigger/test.sym b/cinema/gb/mooneye-gb/acceptance/timer/tim10_div_trigger/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim10_div_trigger/test.sym rename to cinema/gb/mooneye-gb/acceptance/timer/tim10_div_trigger/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim11/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/timer/tim11/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim11/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/timer/tim11/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim11/test.gb b/cinema/gb/mooneye-gb/acceptance/timer/tim11/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim11/test.gb rename to cinema/gb/mooneye-gb/acceptance/timer/tim11/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim11/test.sym b/cinema/gb/mooneye-gb/acceptance/timer/tim11/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim11/test.sym rename to cinema/gb/mooneye-gb/acceptance/timer/tim11/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim11_div_trigger/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/timer/tim11_div_trigger/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim11_div_trigger/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/timer/tim11_div_trigger/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim11_div_trigger/test.gb b/cinema/gb/mooneye-gb/acceptance/timer/tim11_div_trigger/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim11_div_trigger/test.gb rename to cinema/gb/mooneye-gb/acceptance/timer/tim11_div_trigger/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim11_div_trigger/test.sym b/cinema/gb/mooneye-gb/acceptance/timer/tim11_div_trigger/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim11_div_trigger/test.sym rename to cinema/gb/mooneye-gb/acceptance/timer/tim11_div_trigger/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tima_reload/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/timer/tima_reload/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tima_reload/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/timer/tima_reload/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tima_reload/test.gb b/cinema/gb/mooneye-gb/acceptance/timer/tima_reload/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tima_reload/test.gb rename to cinema/gb/mooneye-gb/acceptance/timer/tima_reload/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tima_reload/test.sym b/cinema/gb/mooneye-gb/acceptance/timer/tima_reload/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tima_reload/test.sym rename to cinema/gb/mooneye-gb/acceptance/timer/tima_reload/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tima_write_reloading/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/timer/tima_write_reloading/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tima_write_reloading/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/timer/tima_write_reloading/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim01_div_trigger/manifest.yml b/cinema/gb/mooneye-gb/acceptance/timer/tima_write_reloading/manifest.yml similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tim01_div_trigger/manifest.yml rename to cinema/gb/mooneye-gb/acceptance/timer/tima_write_reloading/manifest.yml diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tima_write_reloading/test.gb b/cinema/gb/mooneye-gb/acceptance/timer/tima_write_reloading/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tima_write_reloading/test.gb rename to cinema/gb/mooneye-gb/acceptance/timer/tima_write_reloading/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tima_write_reloading/test.sym b/cinema/gb/mooneye-gb/acceptance/timer/tima_write_reloading/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tima_write_reloading/test.sym rename to cinema/gb/mooneye-gb/acceptance/timer/tima_write_reloading/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tma_write_reloading/baseline_0000.png b/cinema/gb/mooneye-gb/acceptance/timer/tma_write_reloading/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tma_write_reloading/baseline_0000.png rename to cinema/gb/mooneye-gb/acceptance/timer/tma_write_reloading/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tima_write_reloading/manifest.yml b/cinema/gb/mooneye-gb/acceptance/timer/tma_write_reloading/manifest.yml similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tima_write_reloading/manifest.yml rename to cinema/gb/mooneye-gb/acceptance/timer/tma_write_reloading/manifest.yml diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tma_write_reloading/test.gb b/cinema/gb/mooneye-gb/acceptance/timer/tma_write_reloading/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tma_write_reloading/test.gb rename to cinema/gb/mooneye-gb/acceptance/timer/tma_write_reloading/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tma_write_reloading/test.sym b/cinema/gb/mooneye-gb/acceptance/timer/tma_write_reloading/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tma_write_reloading/test.sym rename to cinema/gb/mooneye-gb/acceptance/timer/tma_write_reloading/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/multicart_rom_8Mb/baseline_0000.png b/cinema/gb/mooneye-gb/emulator-only/mbc1/multicart_rom_8Mb/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/multicart_rom_8Mb/baseline_0000.png rename to cinema/gb/mooneye-gb/emulator-only/mbc1/multicart_rom_8Mb/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/multicart_rom_8Mb/test.gb b/cinema/gb/mooneye-gb/emulator-only/mbc1/multicart_rom_8Mb/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/multicart_rom_8Mb/test.gb rename to cinema/gb/mooneye-gb/emulator-only/mbc1/multicart_rom_8Mb/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/multicart_rom_8Mb/test.sym b/cinema/gb/mooneye-gb/emulator-only/mbc1/multicart_rom_8Mb/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/multicart_rom_8Mb/test.sym rename to cinema/gb/mooneye-gb/emulator-only/mbc1/multicart_rom_8Mb/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/ram_256Kb/baseline_0000.png b/cinema/gb/mooneye-gb/emulator-only/mbc1/ram_256Kb/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/ram_256Kb/baseline_0000.png rename to cinema/gb/mooneye-gb/emulator-only/mbc1/ram_256Kb/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/ram_256Kb/test.gb b/cinema/gb/mooneye-gb/emulator-only/mbc1/ram_256Kb/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/ram_256Kb/test.gb rename to cinema/gb/mooneye-gb/emulator-only/mbc1/ram_256Kb/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/ram_256Kb/test.sym b/cinema/gb/mooneye-gb/emulator-only/mbc1/ram_256Kb/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/ram_256Kb/test.sym rename to cinema/gb/mooneye-gb/emulator-only/mbc1/ram_256Kb/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/ram_64Kb/baseline_0000.png b/cinema/gb/mooneye-gb/emulator-only/mbc1/ram_64Kb/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/ram_64Kb/baseline_0000.png rename to cinema/gb/mooneye-gb/emulator-only/mbc1/ram_64Kb/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/ram_64Kb/test.gb b/cinema/gb/mooneye-gb/emulator-only/mbc1/ram_64Kb/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/ram_64Kb/test.gb rename to cinema/gb/mooneye-gb/emulator-only/mbc1/ram_64Kb/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/ram_64Kb/test.sym b/cinema/gb/mooneye-gb/emulator-only/mbc1/ram_64Kb/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/ram_64Kb/test.sym rename to cinema/gb/mooneye-gb/emulator-only/mbc1/ram_64Kb/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_16Mb/baseline_0000.png b/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_16Mb/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_16Mb/baseline_0000.png rename to cinema/gb/mooneye-gb/emulator-only/mbc1/rom_16Mb/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_16Mb/test.gb b/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_16Mb/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_16Mb/test.gb rename to cinema/gb/mooneye-gb/emulator-only/mbc1/rom_16Mb/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_16Mb/test.sym b/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_16Mb/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_16Mb/test.sym rename to cinema/gb/mooneye-gb/emulator-only/mbc1/rom_16Mb/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_1Mb/baseline_0000.png b/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_1Mb/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_1Mb/baseline_0000.png rename to cinema/gb/mooneye-gb/emulator-only/mbc1/rom_1Mb/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_1Mb/test.gb b/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_1Mb/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_1Mb/test.gb rename to cinema/gb/mooneye-gb/emulator-only/mbc1/rom_1Mb/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_1Mb/test.sym b/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_1Mb/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_1Mb/test.sym rename to cinema/gb/mooneye-gb/emulator-only/mbc1/rom_1Mb/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_2Mb/baseline_0000.png b/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_2Mb/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_2Mb/baseline_0000.png rename to cinema/gb/mooneye-gb/emulator-only/mbc1/rom_2Mb/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_2Mb/test.gb b/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_2Mb/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_2Mb/test.gb rename to cinema/gb/mooneye-gb/emulator-only/mbc1/rom_2Mb/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_2Mb/test.sym b/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_2Mb/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_2Mb/test.sym rename to cinema/gb/mooneye-gb/emulator-only/mbc1/rom_2Mb/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_4Mb/baseline_0000.png b/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_4Mb/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_4Mb/baseline_0000.png rename to cinema/gb/mooneye-gb/emulator-only/mbc1/rom_4Mb/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_4Mb/test.gb b/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_4Mb/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_4Mb/test.gb rename to cinema/gb/mooneye-gb/emulator-only/mbc1/rom_4Mb/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_4Mb/test.sym b/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_4Mb/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_4Mb/test.sym rename to cinema/gb/mooneye-gb/emulator-only/mbc1/rom_4Mb/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_8Mb/baseline_0000.png b/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_512Kb/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_8Mb/baseline_0000.png rename to cinema/gb/mooneye-gb/emulator-only/mbc1/rom_512Kb/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_512Kb/test.gb b/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_512Kb/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_512Kb/test.gb rename to cinema/gb/mooneye-gb/emulator-only/mbc1/rom_512Kb/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_512Kb/test.sym b/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_512Kb/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_512Kb/test.sym rename to cinema/gb/mooneye-gb/emulator-only/mbc1/rom_512Kb/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1_rom_4banks/baseline_0000.png b/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_8Mb/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1_rom_4banks/baseline_0000.png rename to cinema/gb/mooneye-gb/emulator-only/mbc1/rom_8Mb/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_8Mb/test.gb b/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_8Mb/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_8Mb/test.gb rename to cinema/gb/mooneye-gb/emulator-only/mbc1/rom_8Mb/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_8Mb/test.sym b/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_8Mb/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_8Mb/test.sym rename to cinema/gb/mooneye-gb/emulator-only/mbc1/rom_8Mb/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/misc/bits/unused_hwio-C/baseline_0000.png b/cinema/gb/mooneye-gb/emulator-only/mbc1_rom_4banks/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/misc/bits/unused_hwio-C/baseline_0000.png rename to cinema/gb/mooneye-gb/emulator-only/mbc1_rom_4banks/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1_rom_4banks/test.gb b/cinema/gb/mooneye-gb/emulator-only/mbc1_rom_4banks/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1_rom_4banks/test.gb rename to cinema/gb/mooneye-gb/emulator-only/mbc1_rom_4banks/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1_rom_4banks/test.sym b/cinema/gb/mooneye-gb/emulator-only/mbc1_rom_4banks/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1_rom_4banks/test.sym rename to cinema/gb/mooneye-gb/emulator-only/mbc1_rom_4banks/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/madness/mgb_oam_dma_halt_sprites/baseline_0000.png b/cinema/gb/mooneye-gb/madness/mgb_oam_dma_halt_sprites/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/madness/mgb_oam_dma_halt_sprites/baseline_0000.png rename to cinema/gb/mooneye-gb/madness/mgb_oam_dma_halt_sprites/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tma_write_reloading/manifest.yml b/cinema/gb/mooneye-gb/madness/mgb_oam_dma_halt_sprites/manifest.yml similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/acceptance/timer/tma_write_reloading/manifest.yml rename to cinema/gb/mooneye-gb/madness/mgb_oam_dma_halt_sprites/manifest.yml diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/madness/mgb_oam_dma_halt_sprites/test.gb b/cinema/gb/mooneye-gb/madness/mgb_oam_dma_halt_sprites/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/madness/mgb_oam_dma_halt_sprites/test.gb rename to cinema/gb/mooneye-gb/madness/mgb_oam_dma_halt_sprites/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/madness/mgb_oam_dma_halt_sprites/test.sym b/cinema/gb/mooneye-gb/madness/mgb_oam_dma_halt_sprites/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/madness/mgb_oam_dma_halt_sprites/test.sym rename to cinema/gb/mooneye-gb/madness/mgb_oam_dma_halt_sprites/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/manifest.yml b/cinema/gb/mooneye-gb/manifest.yml similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/manifest.yml rename to cinema/gb/mooneye-gb/manifest.yml diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/manual-only/sprite_priority/baseline_0000.png b/cinema/gb/mooneye-gb/manual-only/sprite_priority/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/manual-only/sprite_priority/baseline_0000.png rename to cinema/gb/mooneye-gb/manual-only/sprite_priority/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_1Mb/manifest.yml b/cinema/gb/mooneye-gb/manual-only/sprite_priority/manifest.yml similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_1Mb/manifest.yml rename to cinema/gb/mooneye-gb/manual-only/sprite_priority/manifest.yml diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/manual-only/sprite_priority/test.gb b/cinema/gb/mooneye-gb/manual-only/sprite_priority/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/manual-only/sprite_priority/test.gb rename to cinema/gb/mooneye-gb/manual-only/sprite_priority/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/manual-only/sprite_priority/test.sym b/cinema/gb/mooneye-gb/manual-only/sprite_priority/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/manual-only/sprite_priority/test.sym rename to cinema/gb/mooneye-gb/manual-only/sprite_priority/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_hwio-C/baseline_0000.png b/cinema/gb/mooneye-gb/misc/bits/unused_hwio-C/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_hwio-C/baseline_0000.png rename to cinema/gb/mooneye-gb/misc/bits/unused_hwio-C/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/misc/bits/unused_hwio-C/manifest.yml b/cinema/gb/mooneye-gb/misc/bits/unused_hwio-C/manifest.yml similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/misc/bits/unused_hwio-C/manifest.yml rename to cinema/gb/mooneye-gb/misc/bits/unused_hwio-C/manifest.yml diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/misc/bits/unused_hwio-C/test.gb b/cinema/gb/mooneye-gb/misc/bits/unused_hwio-C/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/misc/bits/unused_hwio-C/test.gb rename to cinema/gb/mooneye-gb/misc/bits/unused_hwio-C/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/misc/bits/unused_hwio-C/test.sym b/cinema/gb/mooneye-gb/misc/bits/unused_hwio-C/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/misc/bits/unused_hwio-C/test.sym rename to cinema/gb/mooneye-gb/misc/bits/unused_hwio-C/test.sym diff --git a/cinema/gb/mooneye-gb/misc/boot_hwio-C/baseline_0000.png b/cinema/gb/mooneye-gb/misc/boot_hwio-C/baseline_0000.png new file mode 100644 index 000000000..30590ffab Binary files /dev/null and b/cinema/gb/mooneye-gb/misc/boot_hwio-C/baseline_0000.png differ diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_hwio-C/manifest.yml b/cinema/gb/mooneye-gb/misc/boot_hwio-C/manifest.yml similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_hwio-C/manifest.yml rename to cinema/gb/mooneye-gb/misc/boot_hwio-C/manifest.yml diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_hwio-C/test.gb b/cinema/gb/mooneye-gb/misc/boot_hwio-C/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_hwio-C/test.gb rename to cinema/gb/mooneye-gb/misc/boot_hwio-C/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_hwio-C/test.sav b/cinema/gb/mooneye-gb/misc/boot_hwio-C/test.sav similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_hwio-C/test.sav rename to cinema/gb/mooneye-gb/misc/boot_hwio-C/test.sav diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_hwio-C/test.sym b/cinema/gb/mooneye-gb/misc/boot_hwio-C/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_hwio-C/test.sym rename to cinema/gb/mooneye-gb/misc/boot_hwio-C/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_regs-A/baseline_0000.png b/cinema/gb/mooneye-gb/misc/boot_regs-A/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_regs-A/baseline_0000.png rename to cinema/gb/mooneye-gb/misc/boot_regs-A/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_regs-A/manifest.yml b/cinema/gb/mooneye-gb/misc/boot_regs-A/manifest.yml similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_regs-A/manifest.yml rename to cinema/gb/mooneye-gb/misc/boot_regs-A/manifest.yml diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_regs-A/test.gb b/cinema/gb/mooneye-gb/misc/boot_regs-A/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_regs-A/test.gb rename to cinema/gb/mooneye-gb/misc/boot_regs-A/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_regs-A/test.sym b/cinema/gb/mooneye-gb/misc/boot_regs-A/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_regs-A/test.sym rename to cinema/gb/mooneye-gb/misc/boot_regs-A/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_regs-cgb/baseline_0000.png b/cinema/gb/mooneye-gb/misc/boot_regs-cgb/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_regs-cgb/baseline_0000.png rename to cinema/gb/mooneye-gb/misc/boot_regs-cgb/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_regs-cgb/manifest.yml b/cinema/gb/mooneye-gb/misc/boot_regs-cgb/manifest.yml similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_regs-cgb/manifest.yml rename to cinema/gb/mooneye-gb/misc/boot_regs-cgb/manifest.yml diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_regs-cgb/test.gb b/cinema/gb/mooneye-gb/misc/boot_regs-cgb/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_regs-cgb/test.gb rename to cinema/gb/mooneye-gb/misc/boot_regs-cgb/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_regs-cgb/test.sym b/cinema/gb/mooneye-gb/misc/boot_regs-cgb/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/misc/boot_regs-cgb/test.sym rename to cinema/gb/mooneye-gb/misc/boot_regs-cgb/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/misc/gpu/vblank_stat_intr-C/baseline_0000.png b/cinema/gb/mooneye-gb/misc/gpu/vblank_stat_intr-C/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/misc/gpu/vblank_stat_intr-C/baseline_0000.png rename to cinema/gb/mooneye-gb/misc/gpu/vblank_stat_intr-C/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/misc/gpu/vblank_stat_intr-C/manifest.yml b/cinema/gb/mooneye-gb/misc/gpu/vblank_stat_intr-C/manifest.yml similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/misc/gpu/vblank_stat_intr-C/manifest.yml rename to cinema/gb/mooneye-gb/misc/gpu/vblank_stat_intr-C/manifest.yml diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/misc/gpu/vblank_stat_intr-C/test.gb b/cinema/gb/mooneye-gb/misc/gpu/vblank_stat_intr-C/test.gb similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/misc/gpu/vblank_stat_intr-C/test.gb rename to cinema/gb/mooneye-gb/misc/gpu/vblank_stat_intr-C/test.gb diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/misc/gpu/vblank_stat_intr-C/test.sym b/cinema/gb/mooneye-gb/misc/gpu/vblank_stat_intr-C/test.sym similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/misc/gpu/vblank_stat_intr-C/test.sym rename to cinema/gb/mooneye-gb/misc/gpu/vblank_stat_intr-C/test.sym diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/update.py b/cinema/gb/mooneye-gb/update.py similarity index 100% rename from src/platform/python/tests/cinema/gb/mooneye-gb/update.py rename to cinema/gb/mooneye-gb/update.py diff --git a/src/platform/python/tests/cinema/gb/window/dk94-split/baseline_0000.png b/cinema/gb/window/dk94-split/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/window/dk94-split/baseline_0000.png rename to cinema/gb/window/dk94-split/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/window/dk94-split/baseline_0001.png b/cinema/gb/window/dk94-split/baseline_0001.png similarity index 100% rename from src/platform/python/tests/cinema/gb/window/dk94-split/baseline_0001.png rename to cinema/gb/window/dk94-split/baseline_0001.png diff --git a/src/platform/python/tests/cinema/gb/window/dk94-split/baseline_0002.png b/cinema/gb/window/dk94-split/baseline_0002.png similarity index 100% rename from src/platform/python/tests/cinema/gb/window/dk94-split/baseline_0002.png rename to cinema/gb/window/dk94-split/baseline_0002.png diff --git a/src/platform/python/tests/cinema/gb/window/dk94-split/baseline_0003.png b/cinema/gb/window/dk94-split/baseline_0003.png similarity index 100% rename from src/platform/python/tests/cinema/gb/window/dk94-split/baseline_0003.png rename to cinema/gb/window/dk94-split/baseline_0003.png diff --git a/src/platform/python/tests/cinema/gb/window/dk94-split/baseline_0004.png b/cinema/gb/window/dk94-split/baseline_0004.png similarity index 100% rename from src/platform/python/tests/cinema/gb/window/dk94-split/baseline_0004.png rename to cinema/gb/window/dk94-split/baseline_0004.png diff --git a/src/platform/python/tests/cinema/gb/window/dk94-split/baseline_0005.png b/cinema/gb/window/dk94-split/baseline_0005.png similarity index 100% rename from src/platform/python/tests/cinema/gb/window/dk94-split/baseline_0005.png rename to cinema/gb/window/dk94-split/baseline_0005.png diff --git a/src/platform/python/tests/cinema/gb/window/dk94-split/baseline_0006.png b/cinema/gb/window/dk94-split/baseline_0006.png similarity index 100% rename from src/platform/python/tests/cinema/gb/window/dk94-split/baseline_0006.png rename to cinema/gb/window/dk94-split/baseline_0006.png diff --git a/src/platform/python/tests/cinema/gb/window/dk94-split/baseline_0007.png b/cinema/gb/window/dk94-split/baseline_0007.png similarity index 100% rename from src/platform/python/tests/cinema/gb/window/dk94-split/baseline_0007.png rename to cinema/gb/window/dk94-split/baseline_0007.png diff --git a/src/platform/python/tests/cinema/gb/window/dk94-split/baseline_0008.png b/cinema/gb/window/dk94-split/baseline_0008.png similarity index 100% rename from src/platform/python/tests/cinema/gb/window/dk94-split/baseline_0008.png rename to cinema/gb/window/dk94-split/baseline_0008.png diff --git a/src/platform/python/tests/cinema/gb/window/dk94-split/test.mvl b/cinema/gb/window/dk94-split/test.mvl similarity index 100% rename from src/platform/python/tests/cinema/gb/window/dk94-split/test.mvl rename to cinema/gb/window/dk94-split/test.mvl diff --git a/src/platform/python/tests/cinema/gb/window/dk94-split/test.sav b/cinema/gb/window/dk94-split/test.sav similarity index 100% rename from src/platform/python/tests/cinema/gb/window/dk94-split/test.sav rename to cinema/gb/window/dk94-split/test.sav diff --git a/src/platform/python/tests/cinema/gb/window/gsc-battle/baseline_0000.png b/cinema/gb/window/gsc-battle/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/window/gsc-battle/baseline_0000.png rename to cinema/gb/window/gsc-battle/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/window/gsc-battle/baseline_0001.png b/cinema/gb/window/gsc-battle/baseline_0001.png similarity index 100% rename from src/platform/python/tests/cinema/gb/window/gsc-battle/baseline_0001.png rename to cinema/gb/window/gsc-battle/baseline_0001.png diff --git a/src/platform/python/tests/cinema/gb/window/gsc-battle/baseline_0002.png b/cinema/gb/window/gsc-battle/baseline_0002.png similarity index 100% rename from src/platform/python/tests/cinema/gb/window/gsc-battle/baseline_0002.png rename to cinema/gb/window/gsc-battle/baseline_0002.png diff --git a/src/platform/python/tests/cinema/gb/window/gsc-battle/baseline_0003.png b/cinema/gb/window/gsc-battle/baseline_0003.png similarity index 100% rename from src/platform/python/tests/cinema/gb/window/gsc-battle/baseline_0003.png rename to cinema/gb/window/gsc-battle/baseline_0003.png diff --git a/src/platform/python/tests/cinema/gb/window/gsc-battle/baseline_0004.png b/cinema/gb/window/gsc-battle/baseline_0004.png similarity index 100% rename from src/platform/python/tests/cinema/gb/window/gsc-battle/baseline_0004.png rename to cinema/gb/window/gsc-battle/baseline_0004.png diff --git a/src/platform/python/tests/cinema/gb/window/gsc-battle/test.mvl b/cinema/gb/window/gsc-battle/test.mvl similarity index 100% rename from src/platform/python/tests/cinema/gb/window/gsc-battle/test.mvl rename to cinema/gb/window/gsc-battle/test.mvl diff --git a/src/platform/python/tests/cinema/gb/window/gsc-battle/test.sav b/cinema/gb/window/gsc-battle/test.sav similarity index 100% rename from src/platform/python/tests/cinema/gb/window/gsc-battle/test.sav rename to cinema/gb/window/gsc-battle/test.sav diff --git a/src/platform/python/tests/cinema/gb/window/zoos-intro/baseline_0000.png b/cinema/gb/window/zoos-intro/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gb/window/zoos-intro/baseline_0000.png rename to cinema/gb/window/zoos-intro/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gb/window/zoos-intro/baseline_0001.png b/cinema/gb/window/zoos-intro/baseline_0001.png similarity index 100% rename from src/platform/python/tests/cinema/gb/window/zoos-intro/baseline_0001.png rename to cinema/gb/window/zoos-intro/baseline_0001.png diff --git a/src/platform/python/tests/cinema/gb/window/zoos-intro/baseline_0002.png b/cinema/gb/window/zoos-intro/baseline_0002.png similarity index 100% rename from src/platform/python/tests/cinema/gb/window/zoos-intro/baseline_0002.png rename to cinema/gb/window/zoos-intro/baseline_0002.png diff --git a/src/platform/python/tests/cinema/gb/window/zoos-intro/baseline_0003.png b/cinema/gb/window/zoos-intro/baseline_0003.png similarity index 100% rename from src/platform/python/tests/cinema/gb/window/zoos-intro/baseline_0003.png rename to cinema/gb/window/zoos-intro/baseline_0003.png diff --git a/src/platform/python/tests/cinema/gb/window/zoos-intro/baseline_0004.png b/cinema/gb/window/zoos-intro/baseline_0004.png similarity index 100% rename from src/platform/python/tests/cinema/gb/window/zoos-intro/baseline_0004.png rename to cinema/gb/window/zoos-intro/baseline_0004.png diff --git a/src/platform/python/tests/cinema/gb/window/zoos-intro/baseline_0005.png b/cinema/gb/window/zoos-intro/baseline_0005.png similarity index 100% rename from src/platform/python/tests/cinema/gb/window/zoos-intro/baseline_0005.png rename to cinema/gb/window/zoos-intro/baseline_0005.png diff --git a/src/platform/python/tests/cinema/gb/window/zoos-intro/baseline_0006.png b/cinema/gb/window/zoos-intro/baseline_0006.png similarity index 100% rename from src/platform/python/tests/cinema/gb/window/zoos-intro/baseline_0006.png rename to cinema/gb/window/zoos-intro/baseline_0006.png diff --git a/src/platform/python/tests/cinema/gb/window/zoos-intro/test.mvl b/cinema/gb/window/zoos-intro/test.mvl similarity index 100% rename from src/platform/python/tests/cinema/gb/window/zoos-intro/test.mvl rename to cinema/gb/window/zoos-intro/test.mvl diff --git a/src/platform/python/tests/cinema/gb/window/zoos-intro/test.sav b/cinema/gb/window/zoos-intro/test.sav similarity index 100% rename from src/platform/python/tests/cinema/gb/window/zoos-intro/test.sav rename to cinema/gb/window/zoos-intro/test.sav diff --git a/cinema/gba/bg/lady-sia/baseline_0000.png b/cinema/gba/bg/lady-sia/baseline_0000.png new file mode 100644 index 000000000..56454bbfa Binary files /dev/null and b/cinema/gba/bg/lady-sia/baseline_0000.png differ diff --git a/cinema/gba/bg/lady-sia/baseline_0001.png b/cinema/gba/bg/lady-sia/baseline_0001.png new file mode 100644 index 000000000..d1eb4323c Binary files /dev/null and b/cinema/gba/bg/lady-sia/baseline_0001.png differ diff --git a/cinema/gba/bg/lady-sia/baseline_0002.png b/cinema/gba/bg/lady-sia/baseline_0002.png new file mode 100644 index 000000000..80f6dd2cb Binary files /dev/null and b/cinema/gba/bg/lady-sia/baseline_0002.png differ diff --git a/cinema/gba/bg/lady-sia/baseline_0003.png b/cinema/gba/bg/lady-sia/baseline_0003.png new file mode 100644 index 000000000..30d9b4446 Binary files /dev/null and b/cinema/gba/bg/lady-sia/baseline_0003.png differ diff --git a/src/platform/python/tests/cinema/gba/bg/lady-sia/test.mvl b/cinema/gba/bg/lady-sia/test.mvl similarity index 100% rename from src/platform/python/tests/cinema/gba/bg/lady-sia/test.mvl rename to cinema/gba/bg/lady-sia/test.mvl diff --git a/src/platform/python/tests/cinema/gba/blend/gs-obj-modes/baseline_0000.png b/cinema/gba/blend/gs-obj-modes/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/gs-obj-modes/baseline_0000.png rename to cinema/gba/blend/gs-obj-modes/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gba/blend/gs-obj-modes/baseline_0001.png b/cinema/gba/blend/gs-obj-modes/baseline_0001.png similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/gs-obj-modes/baseline_0001.png rename to cinema/gba/blend/gs-obj-modes/baseline_0001.png diff --git a/src/platform/python/tests/cinema/gba/blend/gs-obj-modes/baseline_0002.png b/cinema/gba/blend/gs-obj-modes/baseline_0002.png similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/gs-obj-modes/baseline_0002.png rename to cinema/gba/blend/gs-obj-modes/baseline_0002.png diff --git a/src/platform/python/tests/cinema/gba/blend/gs-obj-modes/baseline_0003.png b/cinema/gba/blend/gs-obj-modes/baseline_0003.png similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/gs-obj-modes/baseline_0003.png rename to cinema/gba/blend/gs-obj-modes/baseline_0003.png diff --git a/src/platform/python/tests/cinema/gba/blend/gs-obj-modes/baseline_0004.png b/cinema/gba/blend/gs-obj-modes/baseline_0004.png similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/gs-obj-modes/baseline_0004.png rename to cinema/gba/blend/gs-obj-modes/baseline_0004.png diff --git a/src/platform/python/tests/cinema/gba/blend/gs-obj-modes/baseline_0005.png b/cinema/gba/blend/gs-obj-modes/baseline_0005.png similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/gs-obj-modes/baseline_0005.png rename to cinema/gba/blend/gs-obj-modes/baseline_0005.png diff --git a/src/platform/python/tests/cinema/gba/blend/gs-obj-modes/test.mvl b/cinema/gba/blend/gs-obj-modes/test.mvl similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/gs-obj-modes/test.mvl rename to cinema/gba/blend/gs-obj-modes/test.mvl diff --git a/src/platform/python/tests/cinema/gba/blend/kam-knockout/baseline_0000.png b/cinema/gba/blend/kam-knockout/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/kam-knockout/baseline_0000.png rename to cinema/gba/blend/kam-knockout/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gba/blend/kam-knockout/baseline_0001.png b/cinema/gba/blend/kam-knockout/baseline_0001.png similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/kam-knockout/baseline_0001.png rename to cinema/gba/blend/kam-knockout/baseline_0001.png diff --git a/src/platform/python/tests/cinema/gba/blend/kam-knockout/baseline_0002.png b/cinema/gba/blend/kam-knockout/baseline_0002.png similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/kam-knockout/baseline_0002.png rename to cinema/gba/blend/kam-knockout/baseline_0002.png diff --git a/src/platform/python/tests/cinema/gba/blend/kam-knockout/baseline_0003.png b/cinema/gba/blend/kam-knockout/baseline_0003.png similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/kam-knockout/baseline_0003.png rename to cinema/gba/blend/kam-knockout/baseline_0003.png diff --git a/src/platform/python/tests/cinema/gba/blend/kam-knockout/test.mvl b/cinema/gba/blend/kam-knockout/test.mvl similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/kam-knockout/test.mvl rename to cinema/gba/blend/kam-knockout/test.mvl diff --git a/src/platform/python/tests/cinema/gba/blend/mzm-layering/baseline_0000.png b/cinema/gba/blend/mzm-layering/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/mzm-layering/baseline_0000.png rename to cinema/gba/blend/mzm-layering/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gba/blend/mzm-layering/baseline_0001.png b/cinema/gba/blend/mzm-layering/baseline_0001.png similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/mzm-layering/baseline_0001.png rename to cinema/gba/blend/mzm-layering/baseline_0001.png diff --git a/src/platform/python/tests/cinema/gba/blend/mzm-layering/baseline_0002.png b/cinema/gba/blend/mzm-layering/baseline_0002.png similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/mzm-layering/baseline_0002.png rename to cinema/gba/blend/mzm-layering/baseline_0002.png diff --git a/src/platform/python/tests/cinema/gba/blend/mzm-layering/baseline_0003.png b/cinema/gba/blend/mzm-layering/baseline_0003.png similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/mzm-layering/baseline_0003.png rename to cinema/gba/blend/mzm-layering/baseline_0003.png diff --git a/src/platform/python/tests/cinema/gba/blend/mzm-layering/baseline_0004.png b/cinema/gba/blend/mzm-layering/baseline_0004.png similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/mzm-layering/baseline_0004.png rename to cinema/gba/blend/mzm-layering/baseline_0004.png diff --git a/src/platform/python/tests/cinema/gba/blend/mzm-layering/baseline_0005.png b/cinema/gba/blend/mzm-layering/baseline_0005.png similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/mzm-layering/baseline_0005.png rename to cinema/gba/blend/mzm-layering/baseline_0005.png diff --git a/src/platform/python/tests/cinema/gba/blend/mzm-layering/baseline_0006.png b/cinema/gba/blend/mzm-layering/baseline_0006.png similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/mzm-layering/baseline_0006.png rename to cinema/gba/blend/mzm-layering/baseline_0006.png diff --git a/src/platform/python/tests/cinema/gba/blend/mzm-layering/baseline_0007.png b/cinema/gba/blend/mzm-layering/baseline_0007.png similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/mzm-layering/baseline_0007.png rename to cinema/gba/blend/mzm-layering/baseline_0007.png diff --git a/src/platform/python/tests/cinema/gba/blend/mzm-layering/baseline_0008.png b/cinema/gba/blend/mzm-layering/baseline_0008.png similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/mzm-layering/baseline_0008.png rename to cinema/gba/blend/mzm-layering/baseline_0008.png diff --git a/src/platform/python/tests/cinema/gba/blend/mzm-layering/baseline_0009.png b/cinema/gba/blend/mzm-layering/baseline_0009.png similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/mzm-layering/baseline_0009.png rename to cinema/gba/blend/mzm-layering/baseline_0009.png diff --git a/src/platform/python/tests/cinema/gba/blend/mzm-layering/test.mvl b/cinema/gba/blend/mzm-layering/test.mvl similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/mzm-layering/test.mvl rename to cinema/gba/blend/mzm-layering/test.mvl diff --git a/src/platform/python/tests/cinema/gba/blend/mzm-layering/test.sav b/cinema/gba/blend/mzm-layering/test.sav similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/mzm-layering/test.sav rename to cinema/gba/blend/mzm-layering/test.sav diff --git a/src/platform/python/tests/cinema/gba/blend/sma-knockout/baseline_0000.png b/cinema/gba/blend/sma-knockout/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/sma-knockout/baseline_0000.png rename to cinema/gba/blend/sma-knockout/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gba/blend/sma-knockout/baseline_0001.png b/cinema/gba/blend/sma-knockout/baseline_0001.png similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/sma-knockout/baseline_0001.png rename to cinema/gba/blend/sma-knockout/baseline_0001.png diff --git a/src/platform/python/tests/cinema/gba/blend/sma-knockout/baseline_0002.png b/cinema/gba/blend/sma-knockout/baseline_0002.png similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/sma-knockout/baseline_0002.png rename to cinema/gba/blend/sma-knockout/baseline_0002.png diff --git a/src/platform/python/tests/cinema/gba/blend/sma-knockout/test.mvl b/cinema/gba/blend/sma-knockout/test.mvl similarity index 100% rename from src/platform/python/tests/cinema/gba/blend/sma-knockout/test.mvl rename to cinema/gba/blend/sma-knockout/test.mvl diff --git a/cinema/gba/obj/unaligned-256-linear/baseline_0000.png b/cinema/gba/obj/unaligned-256-linear/baseline_0000.png new file mode 100644 index 000000000..4be2744f7 Binary files /dev/null and b/cinema/gba/obj/unaligned-256-linear/baseline_0000.png differ diff --git a/cinema/gba/obj/unaligned-256-linear/baseline_0001.png b/cinema/gba/obj/unaligned-256-linear/baseline_0001.png new file mode 100644 index 000000000..f658bc32f Binary files /dev/null and b/cinema/gba/obj/unaligned-256-linear/baseline_0001.png differ diff --git a/cinema/gba/obj/unaligned-256-linear/baseline_0002.png b/cinema/gba/obj/unaligned-256-linear/baseline_0002.png new file mode 100644 index 000000000..5f20a6405 Binary files /dev/null and b/cinema/gba/obj/unaligned-256-linear/baseline_0002.png differ diff --git a/cinema/gba/obj/unaligned-256-linear/baseline_0003.png b/cinema/gba/obj/unaligned-256-linear/baseline_0003.png new file mode 100644 index 000000000..04451274a Binary files /dev/null and b/cinema/gba/obj/unaligned-256-linear/baseline_0003.png differ diff --git a/cinema/gba/obj/unaligned-256-linear/baseline_0004.png b/cinema/gba/obj/unaligned-256-linear/baseline_0004.png new file mode 100644 index 000000000..04451274a Binary files /dev/null and b/cinema/gba/obj/unaligned-256-linear/baseline_0004.png differ diff --git a/cinema/gba/obj/unaligned-256-linear/baseline_0005.png b/cinema/gba/obj/unaligned-256-linear/baseline_0005.png new file mode 100644 index 000000000..ab9305096 Binary files /dev/null and b/cinema/gba/obj/unaligned-256-linear/baseline_0005.png differ diff --git a/cinema/gba/obj/unaligned-256-linear/test.mvl b/cinema/gba/obj/unaligned-256-linear/test.mvl new file mode 100644 index 000000000..6256de8fd Binary files /dev/null and b/cinema/gba/obj/unaligned-256-linear/test.mvl differ diff --git a/cinema/gba/obj/unaligned-256/baseline_0000.png b/cinema/gba/obj/unaligned-256/baseline_0000.png new file mode 100644 index 000000000..6cb480936 Binary files /dev/null and b/cinema/gba/obj/unaligned-256/baseline_0000.png differ diff --git a/cinema/gba/obj/unaligned-256/baseline_0001.png b/cinema/gba/obj/unaligned-256/baseline_0001.png new file mode 100644 index 000000000..6cb480936 Binary files /dev/null and b/cinema/gba/obj/unaligned-256/baseline_0001.png differ diff --git a/cinema/gba/obj/unaligned-256/baseline_0002.png b/cinema/gba/obj/unaligned-256/baseline_0002.png new file mode 100644 index 000000000..dc77b1946 Binary files /dev/null and b/cinema/gba/obj/unaligned-256/baseline_0002.png differ diff --git a/cinema/gba/obj/unaligned-256/baseline_0003.png b/cinema/gba/obj/unaligned-256/baseline_0003.png new file mode 100644 index 000000000..f1f194deb Binary files /dev/null and b/cinema/gba/obj/unaligned-256/baseline_0003.png differ diff --git a/cinema/gba/obj/unaligned-256/test.mvl b/cinema/gba/obj/unaligned-256/test.mvl new file mode 100644 index 000000000..2eead4ed3 Binary files /dev/null and b/cinema/gba/obj/unaligned-256/test.mvl differ diff --git a/src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0000.png b/cinema/gba/window/gs-clock-wipe/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0000.png rename to cinema/gba/window/gs-clock-wipe/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0001.png b/cinema/gba/window/gs-clock-wipe/baseline_0001.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0001.png rename to cinema/gba/window/gs-clock-wipe/baseline_0001.png diff --git a/src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0002.png b/cinema/gba/window/gs-clock-wipe/baseline_0002.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0002.png rename to cinema/gba/window/gs-clock-wipe/baseline_0002.png diff --git a/src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0003.png b/cinema/gba/window/gs-clock-wipe/baseline_0003.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0003.png rename to cinema/gba/window/gs-clock-wipe/baseline_0003.png diff --git a/src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0004.png b/cinema/gba/window/gs-clock-wipe/baseline_0004.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0004.png rename to cinema/gba/window/gs-clock-wipe/baseline_0004.png diff --git a/src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0005.png b/cinema/gba/window/gs-clock-wipe/baseline_0005.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0005.png rename to cinema/gba/window/gs-clock-wipe/baseline_0005.png diff --git a/src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0006.png b/cinema/gba/window/gs-clock-wipe/baseline_0006.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0006.png rename to cinema/gba/window/gs-clock-wipe/baseline_0006.png diff --git a/src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0007.png b/cinema/gba/window/gs-clock-wipe/baseline_0007.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0007.png rename to cinema/gba/window/gs-clock-wipe/baseline_0007.png diff --git a/src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0008.png b/cinema/gba/window/gs-clock-wipe/baseline_0008.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0008.png rename to cinema/gba/window/gs-clock-wipe/baseline_0008.png diff --git a/src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0009.png b/cinema/gba/window/gs-clock-wipe/baseline_0009.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0009.png rename to cinema/gba/window/gs-clock-wipe/baseline_0009.png diff --git a/src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0010.png b/cinema/gba/window/gs-clock-wipe/baseline_0010.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0010.png rename to cinema/gba/window/gs-clock-wipe/baseline_0010.png diff --git a/src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0011.png b/cinema/gba/window/gs-clock-wipe/baseline_0011.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0011.png rename to cinema/gba/window/gs-clock-wipe/baseline_0011.png diff --git a/src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0012.png b/cinema/gba/window/gs-clock-wipe/baseline_0012.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0012.png rename to cinema/gba/window/gs-clock-wipe/baseline_0012.png diff --git a/src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0013.png b/cinema/gba/window/gs-clock-wipe/baseline_0013.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0013.png rename to cinema/gba/window/gs-clock-wipe/baseline_0013.png diff --git a/src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0014.png b/cinema/gba/window/gs-clock-wipe/baseline_0014.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0014.png rename to cinema/gba/window/gs-clock-wipe/baseline_0014.png diff --git a/src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0015.png b/cinema/gba/window/gs-clock-wipe/baseline_0015.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0015.png rename to cinema/gba/window/gs-clock-wipe/baseline_0015.png diff --git a/src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0016.png b/cinema/gba/window/gs-clock-wipe/baseline_0016.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0016.png rename to cinema/gba/window/gs-clock-wipe/baseline_0016.png diff --git a/src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0017.png b/cinema/gba/window/gs-clock-wipe/baseline_0017.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/gs-clock-wipe/baseline_0017.png rename to cinema/gba/window/gs-clock-wipe/baseline_0017.png diff --git a/src/platform/python/tests/cinema/gba/window/gs-clock-wipe/test.mvl b/cinema/gba/window/gs-clock-wipe/test.mvl similarity index 100% rename from src/platform/python/tests/cinema/gba/window/gs-clock-wipe/test.mvl rename to cinema/gba/window/gs-clock-wipe/test.mvl diff --git a/cinema/gba/window/sthg-objwin-blend/baseline_0000.png b/cinema/gba/window/sthg-objwin-blend/baseline_0000.png new file mode 100644 index 000000000..cea5ccfff Binary files /dev/null and b/cinema/gba/window/sthg-objwin-blend/baseline_0000.png differ diff --git a/cinema/gba/window/sthg-objwin-blend/baseline_0001.png b/cinema/gba/window/sthg-objwin-blend/baseline_0001.png new file mode 100644 index 000000000..65dc98588 Binary files /dev/null and b/cinema/gba/window/sthg-objwin-blend/baseline_0001.png differ diff --git a/cinema/gba/window/sthg-objwin-blend/baseline_0002.png b/cinema/gba/window/sthg-objwin-blend/baseline_0002.png new file mode 100644 index 000000000..03e0c04dd Binary files /dev/null and b/cinema/gba/window/sthg-objwin-blend/baseline_0002.png differ diff --git a/cinema/gba/window/sthg-objwin-blend/baseline_0003.png b/cinema/gba/window/sthg-objwin-blend/baseline_0003.png new file mode 100644 index 000000000..f7a0f4f63 Binary files /dev/null and b/cinema/gba/window/sthg-objwin-blend/baseline_0003.png differ diff --git a/cinema/gba/window/sthg-objwin-blend/test.mvl b/cinema/gba/window/sthg-objwin-blend/test.mvl new file mode 100644 index 000000000..675314786 Binary files /dev/null and b/cinema/gba/window/sthg-objwin-blend/test.mvl differ diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0000.png b/cinema/gba/window/tgr-objwin-order/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0000.png rename to cinema/gba/window/tgr-objwin-order/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0001.png b/cinema/gba/window/tgr-objwin-order/baseline_0001.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0001.png rename to cinema/gba/window/tgr-objwin-order/baseline_0001.png diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0002.png b/cinema/gba/window/tgr-objwin-order/baseline_0002.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0002.png rename to cinema/gba/window/tgr-objwin-order/baseline_0002.png diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0003.png b/cinema/gba/window/tgr-objwin-order/baseline_0003.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0003.png rename to cinema/gba/window/tgr-objwin-order/baseline_0003.png diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0004.png b/cinema/gba/window/tgr-objwin-order/baseline_0004.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0004.png rename to cinema/gba/window/tgr-objwin-order/baseline_0004.png diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0005.png b/cinema/gba/window/tgr-objwin-order/baseline_0005.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0005.png rename to cinema/gba/window/tgr-objwin-order/baseline_0005.png diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0006.png b/cinema/gba/window/tgr-objwin-order/baseline_0006.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0006.png rename to cinema/gba/window/tgr-objwin-order/baseline_0006.png diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0007.png b/cinema/gba/window/tgr-objwin-order/baseline_0007.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0007.png rename to cinema/gba/window/tgr-objwin-order/baseline_0007.png diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0008.png b/cinema/gba/window/tgr-objwin-order/baseline_0008.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0008.png rename to cinema/gba/window/tgr-objwin-order/baseline_0008.png diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0009.png b/cinema/gba/window/tgr-objwin-order/baseline_0009.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0009.png rename to cinema/gba/window/tgr-objwin-order/baseline_0009.png diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0010.png b/cinema/gba/window/tgr-objwin-order/baseline_0010.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0010.png rename to cinema/gba/window/tgr-objwin-order/baseline_0010.png diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0011.png b/cinema/gba/window/tgr-objwin-order/baseline_0011.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0011.png rename to cinema/gba/window/tgr-objwin-order/baseline_0011.png diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0012.png b/cinema/gba/window/tgr-objwin-order/baseline_0012.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0012.png rename to cinema/gba/window/tgr-objwin-order/baseline_0012.png diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0013.png b/cinema/gba/window/tgr-objwin-order/baseline_0013.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0013.png rename to cinema/gba/window/tgr-objwin-order/baseline_0013.png diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0014.png b/cinema/gba/window/tgr-objwin-order/baseline_0014.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0014.png rename to cinema/gba/window/tgr-objwin-order/baseline_0014.png diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0015.png b/cinema/gba/window/tgr-objwin-order/baseline_0015.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0015.png rename to cinema/gba/window/tgr-objwin-order/baseline_0015.png diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0016.png b/cinema/gba/window/tgr-objwin-order/baseline_0016.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0016.png rename to cinema/gba/window/tgr-objwin-order/baseline_0016.png diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0017.png b/cinema/gba/window/tgr-objwin-order/baseline_0017.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0017.png rename to cinema/gba/window/tgr-objwin-order/baseline_0017.png diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0018.png b/cinema/gba/window/tgr-objwin-order/baseline_0018.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0018.png rename to cinema/gba/window/tgr-objwin-order/baseline_0018.png diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0019.png b/cinema/gba/window/tgr-objwin-order/baseline_0019.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0019.png rename to cinema/gba/window/tgr-objwin-order/baseline_0019.png diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0020.png b/cinema/gba/window/tgr-objwin-order/baseline_0020.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0020.png rename to cinema/gba/window/tgr-objwin-order/baseline_0020.png diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0021.png b/cinema/gba/window/tgr-objwin-order/baseline_0021.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0021.png rename to cinema/gba/window/tgr-objwin-order/baseline_0021.png diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0022.png b/cinema/gba/window/tgr-objwin-order/baseline_0022.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0022.png rename to cinema/gba/window/tgr-objwin-order/baseline_0022.png diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0023.png b/cinema/gba/window/tgr-objwin-order/baseline_0023.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0023.png rename to cinema/gba/window/tgr-objwin-order/baseline_0023.png diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0024.png b/cinema/gba/window/tgr-objwin-order/baseline_0024.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/baseline_0024.png rename to cinema/gba/window/tgr-objwin-order/baseline_0024.png diff --git a/src/platform/python/tests/cinema/gba/window/tgr-objwin-order/test.mvl b/cinema/gba/window/tgr-objwin-order/test.mvl similarity index 100% rename from src/platform/python/tests/cinema/gba/window/tgr-objwin-order/test.mvl rename to cinema/gba/window/tgr-objwin-order/test.mvl diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0000.png b/cinema/gba/window/zmc-window-mosaic/baseline_0000.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0000.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0000.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0001.png b/cinema/gba/window/zmc-window-mosaic/baseline_0001.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0001.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0001.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0002.png b/cinema/gba/window/zmc-window-mosaic/baseline_0002.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0002.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0002.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0003.png b/cinema/gba/window/zmc-window-mosaic/baseline_0003.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0003.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0003.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0004.png b/cinema/gba/window/zmc-window-mosaic/baseline_0004.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0004.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0004.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0005.png b/cinema/gba/window/zmc-window-mosaic/baseline_0005.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0005.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0005.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0006.png b/cinema/gba/window/zmc-window-mosaic/baseline_0006.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0006.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0006.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0007.png b/cinema/gba/window/zmc-window-mosaic/baseline_0007.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0007.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0007.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0008.png b/cinema/gba/window/zmc-window-mosaic/baseline_0008.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0008.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0008.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0009.png b/cinema/gba/window/zmc-window-mosaic/baseline_0009.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0009.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0009.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0010.png b/cinema/gba/window/zmc-window-mosaic/baseline_0010.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0010.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0010.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0011.png b/cinema/gba/window/zmc-window-mosaic/baseline_0011.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0011.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0011.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0012.png b/cinema/gba/window/zmc-window-mosaic/baseline_0012.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0012.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0012.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0013.png b/cinema/gba/window/zmc-window-mosaic/baseline_0013.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0013.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0013.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0014.png b/cinema/gba/window/zmc-window-mosaic/baseline_0014.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0014.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0014.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0015.png b/cinema/gba/window/zmc-window-mosaic/baseline_0015.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0015.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0015.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0016.png b/cinema/gba/window/zmc-window-mosaic/baseline_0016.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0016.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0016.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0017.png b/cinema/gba/window/zmc-window-mosaic/baseline_0017.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0017.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0017.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0018.png b/cinema/gba/window/zmc-window-mosaic/baseline_0018.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0018.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0018.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0019.png b/cinema/gba/window/zmc-window-mosaic/baseline_0019.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0019.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0019.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0020.png b/cinema/gba/window/zmc-window-mosaic/baseline_0020.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0020.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0020.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0021.png b/cinema/gba/window/zmc-window-mosaic/baseline_0021.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0021.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0021.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0022.png b/cinema/gba/window/zmc-window-mosaic/baseline_0022.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0022.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0022.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0023.png b/cinema/gba/window/zmc-window-mosaic/baseline_0023.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0023.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0023.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0024.png b/cinema/gba/window/zmc-window-mosaic/baseline_0024.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0024.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0024.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0025.png b/cinema/gba/window/zmc-window-mosaic/baseline_0025.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0025.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0025.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0026.png b/cinema/gba/window/zmc-window-mosaic/baseline_0026.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0026.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0026.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0027.png b/cinema/gba/window/zmc-window-mosaic/baseline_0027.png similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/baseline_0027.png rename to cinema/gba/window/zmc-window-mosaic/baseline_0027.png diff --git a/src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/test.mvl b/cinema/gba/window/zmc-window-mosaic/test.mvl similarity index 100% rename from src/platform/python/tests/cinema/gba/window/zmc-window-mosaic/test.mvl rename to cinema/gba/window/zmc-window-mosaic/test.mvl diff --git a/include/mgba-util/common.h b/include/mgba-util/common.h index 6abc28b66..d655d402a 100644 --- a/include/mgba-util/common.h +++ b/include/mgba-util/common.h @@ -73,7 +73,7 @@ typedef intptr_t ssize_t; #define M_PI 3.141592654f #endif -#ifndef _MSC_VER +#if !defined(_MSC_VER) && (defined(__llvm__) || (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)) #define ATOMIC_STORE(DST, SRC) __atomic_store_n(&DST, SRC, __ATOMIC_RELEASE) #define ATOMIC_LOAD(DST, SRC) DST = __atomic_load_n(&SRC, __ATOMIC_ACQUIRE) #define ATOMIC_ADD(DST, OP) __atomic_add_fetch(&DST, OP, __ATOMIC_RELEASE) @@ -101,6 +101,8 @@ typedef intptr_t ssize_t; #define PRIz "z" #endif +#if defined __BIG_ENDIAN__ +#define LOAD_32BE(DEST, ADDR, ARR) DEST = *(uint32_t*) ((uintptr_t) (ARR) + (size_t) (ADDR)) #if defined(__PPC__) || defined(__POWERPC__) #define LOAD_32LE(DEST, ADDR, ARR) { \ uint32_t _addr = (ADDR); \ @@ -126,10 +128,39 @@ typedef intptr_t ssize_t; __asm__("sthbrx %0, %1, %2" : : "r"(SRC), "b"(_ptr), "r"(_addr)); \ } -#define LOAD_64LE(DEST, ADDR, ARR) DEST = __builtin_bswap64(((uint64_t*) ARR)[(ADDR) >> 3]) -#define STORE_64LE(SRC, ADDR, ARR) ((uint64_t*) ARR)[(ADDR) >> 3] = __builtin_bswap64(SRC) -#elif defined __BIG_ENDIAN__ -#if defined(__llvm__) || (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) +#define LOAD_64LE(DEST, ADDR, ARR) { \ + uint32_t _addr = (ADDR); \ + union { \ + struct { \ + uint32_t hi; \ + uint32_t lo; \ + }; \ + uint64_t b64; \ + } *bswap = (void*) &DEST; \ + const void* _ptr = (ARR); \ + __asm__( \ + "lwbrx %0, %2, %3 \n" \ + "lwbrx %1, %2, %4 \n" \ + : "=r"(bswap->lo), "=r"(bswap->hi) : "b"(_ptr), "r"(_addr), "r"(_addr + 4)); \ +} + +#define STORE_64LE(SRC, ADDR, ARR) { \ + uint32_t _addr = (ADDR); \ + union { \ + struct { \ + uint32_t hi; \ + uint32_t lo; \ + }; \ + uint64_t b64; \ + } *bswap = (void*) &SRC; \ + const void* _ptr = (ARR); \ + __asm__( \ + "stwbrx %0, %2, %3 \n" \ + "stwbrx %1, %2, %4 \n" \ + : : "r"(bswap->hi), "r"(bswap->lo), "b"(_ptr), "r"(_addr), "r"(_addr + 4)); \ +} + +#elif defined(__llvm__) || (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) #define LOAD_64LE(DEST, ADDR, ARR) DEST = __builtin_bswap64(((uint64_t*) ARR)[(ADDR) >> 3]) #define LOAD_32LE(DEST, ADDR, ARR) DEST = __builtin_bswap32(((uint32_t*) ARR)[(ADDR) >> 2]) #define LOAD_16LE(DEST, ADDR, ARR) DEST = __builtin_bswap16(((uint16_t*) ARR)[(ADDR) >> 1]) @@ -146,6 +177,7 @@ typedef intptr_t ssize_t; #define STORE_64LE(SRC, ADDR, ARR) *(uint64_t*) ((uintptr_t) (ARR) + (size_t) (ADDR)) = SRC #define STORE_32LE(SRC, ADDR, ARR) *(uint32_t*) ((uintptr_t) (ARR) + (size_t) (ADDR)) = SRC #define STORE_16LE(SRC, ADDR, ARR) *(uint16_t*) ((uintptr_t) (ARR) + (size_t) (ADDR)) = SRC +#define LOAD_32BE(DEST, ADDR, ARR) DEST = __builtin_bswap32(((uint32_t*) ARR)[(ADDR) >> 2]) #endif #define MAKE_MASK(START, END) (((1 << ((END) - (START))) - 1) << (START)) diff --git a/include/mgba-util/platform/3ds/threading.h b/include/mgba-util/platform/3ds/threading.h index 0eda3dd43..769039615 100644 --- a/include/mgba-util/platform/3ds/threading.h +++ b/include/mgba-util/platform/3ds/threading.h @@ -11,18 +11,9 @@ #include <3ds.h> #include -#ifdef _3DS -// ctrulib already has a type called Thread -#define Thread CustomThread -#endif - #define THREAD_ENTRY void typedef ThreadFunc ThreadEntry; -typedef struct { - Handle handle; - u8* stack; -} Thread; typedef LightLock Mutex; typedef struct { Mutex mutex; @@ -103,22 +94,12 @@ static inline int ThreadCreate(Thread* thread, ThreadEntry entry, void* context) if (!entry || !thread) { return 1; } - thread->stack = memalign(8, 0x8000); - if (!thread->stack) { - return 1; - } - bool isNew3DS; - APT_CheckNew3DS(&isNew3DS); - if (isNew3DS && svcCreateThread(&thread->handle, entry, (u32) context, (u32*) &thread->stack[0x8000], 0x18, 2) == 0) { - return 0; - } - return svcCreateThread(&thread->handle, entry, (u32) context, (u32*) &thread->stack[0x8000], 0x18, -1); + *thread = threadCreate(entry, context, 0x8000, 0x18, 2, true); + return !*thread; } static inline int ThreadJoin(Thread thread) { - svcWaitSynchronization(thread.handle, U64_MAX); - free(thread.stack); - return 0; + return threadJoin(thread, U64_MAX); } static inline void ThreadSetName(const char* name) { diff --git a/include/mgba-util/platform/posix/threading.h b/include/mgba-util/platform/posix/threading.h index f79d97b10..468e1460c 100644 --- a/include/mgba-util/platform/posix/threading.h +++ b/include/mgba-util/platform/posix/threading.h @@ -86,7 +86,12 @@ static inline int ThreadJoin(Thread thread) { static inline int ThreadSetName(const char* name) { #ifdef __APPLE__ +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060 return pthread_setname_np(name); +#else + UNUSED(name); + return 0; +#endif #elif defined(__FreeBSD__) || defined(__OpenBSD__) pthread_set_name_np(pthread_self(), name); return 0; diff --git a/include/mgba-util/vector.h b/include/mgba-util/vector.h index ae542d41b..c18b6c8b5 100644 --- a/include/mgba-util/vector.h +++ b/include/mgba-util/vector.h @@ -10,6 +10,10 @@ CXX_GUARD_START +#ifdef vector +#undef vector +#endif + #define DECLARE_VECTOR(NAME, TYPE) \ struct NAME { \ TYPE* vector; \ diff --git a/include/mgba-util/vfs.h b/include/mgba-util/vfs.h index 562fa52b5..f10f3c577 100644 --- a/include/mgba-util/vfs.h +++ b/include/mgba-util/vfs.h @@ -87,7 +87,7 @@ struct VDir* VDirOpenZip(const char* path, int flags); struct VDir* VDirOpen7z(const char* path, int flags); #endif -#if defined(__wii__) || defined(_3DS) +#if defined(__wii__) || defined(_3DS) || defined(PSP2) struct VDir* VDeviceList(void); #endif diff --git a/include/mgba/core/cheats.h b/include/mgba/core/cheats.h index 25ad3bbe1..32c7cad42 100644 --- a/include/mgba/core/cheats.h +++ b/include/mgba/core/cheats.h @@ -14,8 +14,6 @@ CXX_GUARD_START #include #include -#define MAX_ROM_PATCHES 4 - enum mCheatType { CHEAT_ASSIGN, CHEAT_ASSIGN_INDIRECT, @@ -30,7 +28,8 @@ enum mCheatType { CHEAT_IF_UGT, CHEAT_IF_AND, CHEAT_IF_LAND, - CHEAT_IF_NAND + CHEAT_IF_NAND, + CHEAT_IF_BUTTON, }; struct mCheat { @@ -79,6 +78,8 @@ struct mCheatDevice { struct mCheatSet* (*createSet)(struct mCheatDevice*, const char* name); struct mCheatSets cheats; + bool autosave; + bool buttonDown; }; struct VFile; @@ -99,7 +100,12 @@ void mCheatRemoveSet(struct mCheatDevice*, struct mCheatSet*); bool mCheatParseFile(struct mCheatDevice*, struct VFile*); bool mCheatSaveFile(struct mCheatDevice*, struct VFile*); +#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 +void mCheatAutosave(struct mCheatDevice*); +#endif + void mCheatRefresh(struct mCheatDevice*, struct mCheatSet*); +void mCheatPressButton(struct mCheatDevice*, bool down); CXX_GUARD_END diff --git a/include/mgba/core/config.h b/include/mgba/core/config.h index 0c6426e7a..a55d19c0d 100644 --- a/include/mgba/core/config.h +++ b/include/mgba/core/config.h @@ -51,6 +51,7 @@ struct mCoreOptions { char* savestatePath; char* screenshotPath; char* patchPath; + char* cheatsPath; int volume; bool mute; diff --git a/include/mgba/core/core.h b/include/mgba/core/core.h index 8f7b75c0c..484b19c71 100644 --- a/include/mgba/core/core.h +++ b/include/mgba/core/core.h @@ -148,6 +148,7 @@ struct mCore { void (*detachDebugger)(struct mCore*); void (*loadSymbols)(struct mCore*, struct VFile*); + bool (*lookupIdentifier)(struct mCore*, const char* name, int32_t* value, int* segment); #endif struct mCheatDevice* (*cheatDevice)(struct mCore*); @@ -175,6 +176,7 @@ bool mCorePreloadFile(struct mCore* core, const char* path); bool mCoreAutoloadSave(struct mCore* core); bool mCoreAutoloadPatch(struct mCore* core); +bool mCoreAutoloadCheats(struct mCore* core); bool mCoreSaveState(struct mCore* core, int slot, int flags); bool mCoreLoadState(struct mCore* core, int slot, int flags); diff --git a/include/mgba/core/directories.h b/include/mgba/core/directories.h index 8715c1025..d421e7f51 100644 --- a/include/mgba/core/directories.h +++ b/include/mgba/core/directories.h @@ -21,6 +21,7 @@ struct mDirectorySet { struct VDir* patch; struct VDir* state; struct VDir* screenshot; + struct VDir* cheats; }; void mDirectorySetInit(struct mDirectorySet* dirs); diff --git a/include/mgba/core/mem-search.h b/include/mgba/core/mem-search.h index 57084b4ae..c18c20922 100644 --- a/include/mgba/core/mem-search.h +++ b/include/mgba/core/mem-search.h @@ -13,29 +13,38 @@ CXX_GUARD_START #include enum mCoreMemorySearchType { - mCORE_MEMORY_SEARCH_32, - mCORE_MEMORY_SEARCH_16, - mCORE_MEMORY_SEARCH_8, + mCORE_MEMORY_SEARCH_INT, mCORE_MEMORY_SEARCH_STRING, mCORE_MEMORY_SEARCH_GUESS, }; +enum mCoreMemorySearchOp { + mCORE_MEMORY_SEARCH_EQUAL, + mCORE_MEMORY_SEARCH_GREATER, + mCORE_MEMORY_SEARCH_LESS, + mCORE_MEMORY_SEARCH_DELTA, +}; + struct mCoreMemorySearchParams { int memoryFlags; enum mCoreMemorySearchType type; + enum mCoreMemorySearchOp op; + int align; + int width; union { const char* valueStr; - uint32_t value32; - uint32_t value16; - uint32_t value8; + int32_t valueInt; }; }; struct mCoreMemorySearchResult { uint32_t address; int segment; - uint64_t guessDivisor; + uint32_t guessDivisor; + uint32_t guessMultiplier; enum mCoreMemorySearchType type; + int width; + int32_t oldValue; }; DECLARE_VECTOR(mCoreMemorySearchResults, struct mCoreMemorySearchResult); diff --git a/include/mgba/debugger/debugger.h b/include/mgba/debugger/debugger.h index 213236425..8de7d4c00 100644 --- a/include/mgba/debugger/debugger.h +++ b/include/mgba/debugger/debugger.h @@ -60,16 +60,17 @@ struct mDebuggerEntryInfo { uint32_t newValue; enum mWatchpointType watchType; enum mWatchpointType accessType; - }; + } wp; struct { uint32_t opcode; enum mBreakpointType breakType; - }; - }; + } bp; + } type; }; struct mDebugger; +struct ParseTree; struct mDebuggerPlatform { struct mDebugger* p; @@ -79,14 +80,19 @@ struct mDebuggerPlatform { bool (*hasBreakpoints)(struct mDebuggerPlatform*); void (*setBreakpoint)(struct mDebuggerPlatform*, uint32_t address, int segment); + void (*setConditionalBreakpoint)(struct mDebuggerPlatform*, uint32_t address, int segment, struct ParseTree* condition); void (*clearBreakpoint)(struct mDebuggerPlatform*, uint32_t address, int segment); void (*setWatchpoint)(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type); + void (*setConditionalWatchpoint)(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type, struct ParseTree* condition); void (*clearWatchpoint)(struct mDebuggerPlatform*, uint32_t address, int segment); void (*checkBreakpoints)(struct mDebuggerPlatform*); void (*trace)(struct mDebuggerPlatform*, char* out, size_t* length); + + bool (*getRegister)(struct mDebuggerPlatform*, const char* name, int32_t* value); + bool (*setRegister)(struct mDebuggerPlatform*, const char* name, int32_t value); + bool (*lookupIdentifier)(struct mDebuggerPlatform*, const char* name, int32_t* value, int* segment); }; -struct mDebuggerSymbols; struct mDebugger { struct mCPUComponent d; struct mDebuggerPlatform* platform; @@ -109,6 +115,8 @@ void mDebuggerRun(struct mDebugger*); void mDebuggerRunFrame(struct mDebugger*); void mDebuggerEnter(struct mDebugger*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*); +bool mDebuggerLookupIdentifier(struct mDebugger* debugger, const char* name, int32_t* value, int* segment); + CXX_GUARD_END #endif diff --git a/include/mgba/gba/interface.h b/include/mgba/gba/interface.h index 90f5e0697..0d85982f5 100644 --- a/include/mgba/gba/interface.h +++ b/include/mgba/gba/interface.h @@ -21,6 +21,13 @@ enum GBASIOMode { SIO_JOYBUS = 12 }; +enum GBASIOJOYCommand { + JOY_RESET = 0xFF, + JOY_POLL = 0x00, + JOY_TRANS = 0x14, + JOY_RECV = 0x15 +}; + struct GBA; struct GBAAudio; struct GBASIO; @@ -48,6 +55,10 @@ struct GBASIODriver { uint16_t (*writeRegister)(struct GBASIODriver* driver, uint32_t address, uint16_t value); }; +void GBASIOJOYCreate(struct GBASIODriver* sio); +int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command, uint8_t* data); + + CXX_GUARD_END #endif diff --git a/include/mgba/internal/arm/debugger/debugger.h b/include/mgba/internal/arm/debugger/debugger.h index d35dbed13..5c1852243 100644 --- a/include/mgba/internal/arm/debugger/debugger.h +++ b/include/mgba/internal/arm/debugger/debugger.h @@ -15,8 +15,10 @@ CXX_GUARD_START #include #include +struct ParseTree; struct ARMDebugBreakpoint { uint32_t address; + struct ParseTree* condition; bool isSw; struct { uint32_t opcode; @@ -27,6 +29,7 @@ struct ARMDebugBreakpoint { struct ARMDebugWatchpoint { uint32_t address; enum mWatchpointType type; + struct ParseTree* condition; }; DECLARE_VECTOR(ARMDebugBreakpointList, struct ARMDebugBreakpoint); diff --git a/include/mgba/internal/debugger/cli-debugger.h b/include/mgba/internal/debugger/cli-debugger.h index 6eb28b1dc..7a03d1df0 100644 --- a/include/mgba/internal/debugger/cli-debugger.h +++ b/include/mgba/internal/debugger/cli-debugger.h @@ -14,6 +14,7 @@ CXX_GUARD_START extern const char* ERROR_MISSING_ARGS; extern const char* ERROR_OVERFLOW; +extern const char* ERROR_INVALID_ARGS; struct CLIDebugger; @@ -24,22 +25,17 @@ struct CLIDebugVector { CLIDV_INT_TYPE, CLIDV_CHAR_TYPE, } type; - union { - char* charValue; - struct { - int32_t intValue; - int segmentValue; - }; - }; + char* charValue; + int32_t intValue; + int segmentValue; }; typedef void (*CLIDebuggerCommand)(struct CLIDebugger*, struct CLIDebugVector*); -typedef struct CLIDebugVector* (*CLIDVParser)(struct CLIDebugger* debugger, const char* string, size_t length); struct CLIDebuggerCommandSummary { const char* name; CLIDebuggerCommand command; - CLIDVParser parser; + const char* format; const char* summary; }; @@ -51,8 +47,6 @@ struct CLIDebuggerSystem { bool (*custom)(struct CLIDebuggerSystem*); void (*disassemble)(struct CLIDebuggerSystem*, struct CLIDebugVector* dv); - uint32_t (*lookupIdentifier)(struct CLIDebuggerSystem*, const char* name, struct CLIDebugVector* dv); - uint32_t (*lookupPlatformIdentifier)(struct CLIDebuggerSystem*, const char* name, struct CLIDebugVector* dv); void (*printStatus)(struct CLIDebuggerSystem*); struct CLIDebuggerCommandSummary* commands; @@ -82,9 +76,6 @@ struct CLIDebugger { struct CLIDebuggerBackend* backend; }; -struct CLIDebugVector* CLIDVParse(struct CLIDebugger* debugger, const char* string, size_t length); -struct CLIDebugVector* CLIDVStringParse(struct CLIDebugger* debugger, const char* string, size_t length); - void CLIDebuggerCreate(struct CLIDebugger*); void CLIDebuggerAttachSystem(struct CLIDebugger*, struct CLIDebuggerSystem*); void CLIDebuggerAttachBackend(struct CLIDebugger*, struct CLIDebuggerBackend*); diff --git a/include/mgba/internal/debugger/parser.h b/include/mgba/internal/debugger/parser.h index 178254b69..6092c1f8c 100644 --- a/include/mgba/internal/debugger/parser.h +++ b/include/mgba/internal/debugger/parser.h @@ -8,27 +8,36 @@ #include +#include + CXX_GUARD_START -#include - -enum LexState { - LEX_ERROR = -1, - LEX_ROOT = 0, - LEX_EXPECT_IDENTIFIER, - LEX_EXPECT_BINARY, - LEX_EXPECT_DECIMAL, - LEX_EXPECT_HEX, - LEX_EXPECT_PREFIX, - LEX_EXPECT_OPERATOR -}; +struct Token; +DECLARE_VECTOR(LexVector, struct Token); enum Operation { OP_ASSIGN, OP_ADD, OP_SUBTRACT, OP_MULTIPLY, - OP_DIVIDE + OP_DIVIDE, + OP_MODULO, + OP_AND, + OP_OR, + OP_XOR, + OP_LESS, + OP_GREATER, + OP_EQUAL, + OP_NOT_EQUAL, + OP_LOGICAL_AND, + OP_LOGICAL_OR, + OP_LE, + OP_GE, + OP_NEGATE, + OP_FLIP, + OP_NOT, + OP_SHIFT_L, + OP_SHIFT_R, }; struct Token { @@ -48,23 +57,21 @@ struct Token { }; }; -struct LexVector { - struct LexVector* next; - struct Token token; -}; - struct ParseTree { struct Token token; struct ParseTree* lhs; struct ParseTree* rhs; }; -size_t lexExpression(struct LexVector* lv, const char* string, size_t length); +size_t lexExpression(struct LexVector* lv, const char* string, size_t length, const char* eol); void parseLexedExpression(struct ParseTree* tree, struct LexVector* lv); void lexFree(struct LexVector* lv); void parseFree(struct ParseTree* tree); +struct mDebugger; +bool mDebuggerEvaluateParseTree(struct mDebugger* debugger, struct ParseTree* tree, int32_t* value, int* segment); + CXX_GUARD_END #endif diff --git a/include/mgba/internal/gb/gb.h b/include/mgba/internal/gb/gb.h index 1d222211a..65ffb88f7 100644 --- a/include/mgba/internal/gb/gb.h +++ b/include/mgba/internal/gb/gb.h @@ -145,6 +145,8 @@ void GBCreate(struct GB* gb); void GBDestroy(struct GB* gb); void GBReset(struct LR35902Core* cpu); +void GBSkipBIOS(struct GB* gb); +void GBUnmapBIOS(struct GB* gb); void GBDetectModel(struct GB* gb); void GBUpdateIRQs(struct GB* gb); diff --git a/include/mgba/internal/gb/io.h b/include/mgba/internal/gb/io.h index 5ed4cb0fe..95afebe0f 100644 --- a/include/mgba/internal/gb/io.h +++ b/include/mgba/internal/gb/io.h @@ -84,6 +84,7 @@ enum GBIORegisters { REG_WX = 0x4B, // CGB + REG_UNK4C = 0x4C, REG_KEY1 = 0x4D, REG_VBK = 0x4F, REG_HDMA1 = 0x51, @@ -101,8 +102,8 @@ enum GBIORegisters { REG_UNK72 = 0x72, REG_UNK73 = 0x73, REG_UNK74 = 0x74, - REG_UNK75 = 0x75, - REG_UNK76 = 0x76, + REG_PCM12 = 0x75, + REG_PCM34 = 0x76, REG_UNK77 = 0x77, REG_MAX = 0x100 }; diff --git a/include/mgba/internal/gb/memory.h b/include/mgba/internal/gb/memory.h index f971c353e..ba4a155db 100644 --- a/include/mgba/internal/gb/memory.h +++ b/include/mgba/internal/gb/memory.h @@ -207,7 +207,7 @@ int GBCurrentSegment(struct LR35902Core* cpu, uint16_t address); uint8_t GBView8(struct LR35902Core* cpu, uint16_t address, int segment); void GBMemoryDMA(struct GB* gb, uint16_t base); -void GBMemoryWriteHDMA5(struct GB* gb, uint8_t value); +uint8_t GBMemoryWriteHDMA5(struct GB* gb, uint8_t value); void GBPatch8(struct LR35902Core* cpu, uint16_t address, int8_t value, int8_t* old, int segment); diff --git a/include/mgba/internal/gb/renderers/software.h b/include/mgba/internal/gb/renderers/software.h index a5bbcb80f..91f7421f4 100644 --- a/include/mgba/internal/gb/renderers/software.h +++ b/include/mgba/internal/gb/renderers/software.h @@ -32,6 +32,8 @@ struct GBVideoSoftwareRenderer { uint8_t wy; uint8_t wx; uint8_t currentWy; + int lastY; + bool hasWindow; GBRegisterLCDC lcdc; enum GBModel model; @@ -43,6 +45,9 @@ struct GBVideoSoftwareRenderer { int sgbDataSets; uint8_t sgbPartialDataSet[15]; bool sgbBorders; + int sgbAttrX; + int sgbAttrY; + int sgbAttrDirection; }; void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer*); diff --git a/include/mgba/internal/gb/serialize.h b/include/mgba/internal/gb/serialize.h index 2acf592d4..22c4ccb3f 100644 --- a/include/mgba/internal/gb/serialize.h +++ b/include/mgba/internal/gb/serialize.h @@ -169,7 +169,9 @@ mLOG_DECLARE_CATEGORY(GB_STATE); * | 0x0C7DC - 0x0C7DF: Flags * | bits 0 - 1: Current P1 bits * | bits 2 - 3: Current render mode - * | bits 4 - 31: Reserved (leave 0) + * | bit 4: Is a mode event not scheduled? + * | bit 5: Is a frame event not scheduled? + * | bits 6 - 31: Reserved (leave 0) * | 0x0C7E0 - 0x0C7EF: Current packet * | 0x0C7F0 - 0x0C7FF: Reserved * | 0x0C800 - 0x0E7FF: Character VRAM diff --git a/include/mgba/internal/gb/video.h b/include/mgba/internal/gb/video.h index a19fa928e..90cfd7bbb 100644 --- a/include/mgba/internal/gb/video.h +++ b/include/mgba/internal/gb/video.h @@ -159,7 +159,7 @@ void GBVideoInit(struct GBVideo* video); void GBVideoReset(struct GBVideo* video); void GBVideoDeinit(struct GBVideo* video); void GBVideoAssociateRenderer(struct GBVideo* video, struct GBVideoRenderer* renderer); -void GBVideoProcessDots(struct GBVideo* video); +void GBVideoProcessDots(struct GBVideo* video, uint32_t cyclesLate); void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value); void GBVideoWriteSTAT(struct GBVideo* video, GBRegisterSTAT value); diff --git a/include/mgba/internal/gba/cheats.h b/include/mgba/internal/gba/cheats.h index 83912f660..fe17af1a6 100644 --- a/include/mgba/internal/gba/cheats.h +++ b/include/mgba/internal/gba/cheats.h @@ -13,7 +13,7 @@ CXX_GUARD_START #include #include -#define MAX_ROM_PATCHES 4 +#define MAX_ROM_PATCHES 10 #define COMPLETE ((size_t) -1) enum GBACheatType { @@ -63,7 +63,7 @@ enum GBAActionReplay3Condition { PAR3_COND_GT = 0x20000000, PAR3_COND_ULT = 0x28000000, PAR3_COND_UGT = 0x30000000, - PAR3_COND_LAND = 0x38000000, + PAR3_COND_AND = 0x38000000, }; enum GBAActionReplay3Width { diff --git a/include/mgba/internal/gba/dma.h b/include/mgba/internal/gba/dma.h index 31d6986e1..6f880c971 100644 --- a/include/mgba/internal/gba/dma.h +++ b/include/mgba/internal/gba/dma.h @@ -10,18 +10,18 @@ CXX_GUARD_START -enum DMAControl { - DMA_INCREMENT = 0, - DMA_DECREMENT = 1, - DMA_FIXED = 2, - DMA_INCREMENT_RELOAD = 3 +enum GBADMAControl { + GBA_DMA_INCREMENT = 0, + GBA_DMA_DECREMENT = 1, + GBA_DMA_FIXED = 2, + GBA_DMA_INCREMENT_RELOAD = 3 }; -enum DMATiming { - DMA_TIMING_NOW = 0, - DMA_TIMING_VBLANK = 1, - DMA_TIMING_HBLANK = 2, - DMA_TIMING_CUSTOM = 3 +enum GBADMATiming { + GBA_DMA_TIMING_NOW = 0, + GBA_DMA_TIMING_VBLANK = 1, + GBA_DMA_TIMING_HBLANK = 2, + GBA_DMA_TIMING_CUSTOM = 3 }; DECL_BITFIELD(GBADMARegister, uint16_t); @@ -59,6 +59,7 @@ struct GBADMA; void GBADMASchedule(struct GBA* gba, int number, struct GBADMA* info); void GBADMARunHblank(struct GBA* gba, int32_t cycles); void GBADMARunVblank(struct GBA* gba, int32_t cycles); +void GBADMARunDisplayStart(struct GBA* gba, int32_t cycles); void GBADMAUpdate(struct GBA* gba); CXX_GUARD_END diff --git a/include/mgba/internal/gba/matrix.h b/include/mgba/internal/gba/matrix.h new file mode 100644 index 000000000..cc4191c8b --- /dev/null +++ b/include/mgba/internal/gba/matrix.h @@ -0,0 +1,28 @@ +/* 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/. */ +#ifndef GBA_MATRIX_H +#define GBA_MATRIX_H + +#include + +CXX_GUARD_START + +struct GBAMatrix { + uint32_t cmd; + uint32_t paddr; + uint32_t vaddr; + uint32_t size; +}; + +struct GBA; +struct GBAMemory; +void GBAMatrixReset(struct GBA*); +void GBAMatrixWrite(struct GBA*, uint32_t address, uint32_t value); +void GBAMatrixWrite16(struct GBA*, uint32_t address, uint16_t value); + +CXX_GUARD_END + +#endif diff --git a/include/mgba/internal/gba/memory.h b/include/mgba/internal/gba/memory.h index 08e9ef673..ea27aefe7 100644 --- a/include/mgba/internal/gba/memory.h +++ b/include/mgba/internal/gba/memory.h @@ -17,6 +17,7 @@ CXX_GUARD_START #include #include #include +#include mLOG_DECLARE_CATEGORY(GBA_MEM); @@ -71,7 +72,9 @@ enum { SIZE_CART_FLASH512 = 0x00010000, SIZE_CART_FLASH1M = 0x00020000, SIZE_CART_EEPROM = 0x00002000, - SIZE_CART_EEPROM512 = 0x00000200 + SIZE_CART_EEPROM512 = 0x00000200, + + SIZE_AGB_PRINT = 0x10000 }; enum { @@ -79,6 +82,21 @@ enum { BASE_OFFSET = 24 }; +enum { + AGB_PRINT_BASE = 0x00FD0000, + AGB_PRINT_TOP = 0x00FE0000, + AGB_PRINT_PROTECT = 0x00FE2FFE, + AGB_PRINT_STRUCT = 0x01FE20F8, + AGB_PRINT_FLUSH_ADDR = 0x01FE209C, +}; + +struct GBAPrintContext { + uint16_t request; + uint16_t bank; + uint16_t get; + uint16_t put; +}; + struct GBAMemory { uint32_t* bios; uint32_t* wram; @@ -89,6 +107,7 @@ struct GBAMemory { struct GBACartridgeHardware hw; struct GBASavedata savedata; struct GBAVFameCart vfame; + struct GBAMatrix matrix; size_t romSize; uint32_t romMask; uint16_t romID; @@ -106,6 +125,11 @@ struct GBAMemory { struct GBADMA dma[4]; struct mTimingEvent dmaEvent; int activeDMA; + uint32_t dmaTransferRegister; + + uint16_t agbPrint; + struct GBAPrintContext agbPrintCtx; + uint16_t* agbPrintBuffer; bool mirroring; }; @@ -145,6 +169,8 @@ struct GBASerializedState; void GBAMemorySerialize(const struct GBAMemory* memory, struct GBASerializedState* state); void GBAMemoryDeserialize(struct GBAMemory* memory, const struct GBASerializedState* state); +void GBAPrintFlush(struct GBA* gba); + CXX_GUARD_END #endif diff --git a/include/mgba/internal/gba/renderers/video-software.h b/include/mgba/internal/gba/renderers/video-software.h index 63893c0fe..bd3c252ca 100644 --- a/include/mgba/internal/gba/renderers/video-software.h +++ b/include/mgba/internal/gba/renderers/video-software.h @@ -43,6 +43,8 @@ struct GBAVideoSoftwareBackground { int16_t dmy; int32_t sx; int32_t sy; + int yCache; + uint16_t mapCache[64]; color_t* extPalette; color_t* variantPalette; }; diff --git a/include/mgba/internal/gba/serialize.h b/include/mgba/internal/gba/serialize.h index 8b4e4a84b..26b9daf31 100644 --- a/include/mgba/internal/gba/serialize.h +++ b/include/mgba/internal/gba/serialize.h @@ -169,7 +169,8 @@ mLOG_DECLARE_CATEGORY(GBA_STATE); * | bits 4 - 8: GB Player transmit position * | bits 9 - 23: Reserved * 0x002C4 - 0x002C7: Game Boy Player next event - * 0x002C8 - 0x002DF: Reserved (leave zero) + * 0x002C8 - 0x002CB: Current DMA transfer word + * 0x002CC - 0x002DF: Reserved (leave zero) * 0x002E0 - 0x002EF: Savedata state * | 0x002E0 - 0x002E0: Savedata type * | 0x002E1 - 0x002E1: Savedata command (see savedata.h) @@ -293,7 +294,9 @@ struct GBASerializedState { uint32_t gbpNextEvent; } hw; - uint32_t reservedHardware[6]; + uint32_t dmaTransferRegister; + + uint32_t reservedHardware[5]; struct { uint8_t type; diff --git a/include/mgba/internal/lr35902/debugger/debugger.h b/include/mgba/internal/lr35902/debugger/debugger.h index 340e13aac..6b66c715a 100644 --- a/include/mgba/internal/lr35902/debugger/debugger.h +++ b/include/mgba/internal/lr35902/debugger/debugger.h @@ -15,16 +15,18 @@ CXX_GUARD_START #include #include - +struct ParseTree; struct LR35902DebugBreakpoint { uint16_t address; int segment; + struct ParseTree* condition; }; struct LR35902DebugWatchpoint { uint16_t address; int segment; enum mWatchpointType type; + struct ParseTree* condition; }; struct LR35902Segment { diff --git a/src/arm/debugger/cli-debugger.c b/src/arm/debugger/cli-debugger.c index 87a1655c5..a4f37e5ef 100644 --- a/src/arm/debugger/cli-debugger.c +++ b/src/arm/debugger/cli-debugger.c @@ -17,23 +17,21 @@ static void _disassembleArm(struct CLIDebugger*, struct CLIDebugVector*); static void _disassembleThumb(struct CLIDebugger*, struct CLIDebugVector*); static void _setBreakpointARM(struct CLIDebugger*, struct CLIDebugVector*); static void _setBreakpointThumb(struct CLIDebugger*, struct CLIDebugVector*); -static void _writeRegister(struct CLIDebugger*, struct CLIDebugVector*); static void _disassembleMode(struct CLIDebugger*, struct CLIDebugVector*, enum ExecutionMode mode); static uint32_t _printLine(struct CLIDebugger* debugger, uint32_t address, enum ExecutionMode mode); static struct CLIDebuggerCommandSummary _armCommands[] = { - { "b/a", _setBreakpointARM, CLIDVParse, "Set a software breakpoint as ARM" }, - { "b/t", _setBreakpointThumb, CLIDVParse, "Set a software breakpoint as Thumb" }, - { "break/a", _setBreakpointARM, CLIDVParse, "Set a software breakpoint as ARM" }, - { "break/t", _setBreakpointThumb, CLIDVParse, "Set a software breakpoint as Thumb" }, - { "dis/a", _disassembleArm, CLIDVParse, "Disassemble instructions as ARM" }, - { "dis/t", _disassembleThumb, CLIDVParse, "Disassemble instructions as Thumb" }, - { "disasm/a", _disassembleArm, CLIDVParse, "Disassemble instructions as ARM" }, - { "disasm/t", _disassembleThumb, CLIDVParse, "Disassemble instructions as Thumb" }, - { "disassemble/a", _disassembleArm, CLIDVParse, "Disassemble instructions as ARM" }, - { "disassemble/t", _disassembleThumb, CLIDVParse, "Disassemble instructions as Thumb" }, - { "w/r", _writeRegister, CLIDVParse, "Write a register" }, + { "b/a", _setBreakpointARM, "I", "Set a software breakpoint as ARM" }, + { "b/t", _setBreakpointThumb, "I", "Set a software breakpoint as Thumb" }, + { "break/a", _setBreakpointARM, "I", "Set a software breakpoint as ARM" }, + { "break/t", _setBreakpointThumb, "I", "Set a software breakpoint as Thumb" }, + { "dis/a", _disassembleArm, "Ii", "Disassemble instructions as ARM" }, + { "dis/t", _disassembleThumb, "Ii", "Disassemble instructions as Thumb" }, + { "disasm/a", _disassembleArm, "Ii", "Disassemble instructions as ARM" }, + { "disasm/t", _disassembleThumb, "Ii", "Disassemble instructions as Thumb" }, + { "disassemble/a", _disassembleArm, "Ii", "Disassemble instructions as ARM" }, + { "disassemble/t", _disassembleThumb, "Ii", "Disassemble instructions as Thumb" }, { 0, 0, 0, 0 } }; @@ -145,25 +143,6 @@ static void _printStatus(struct CLIDebuggerSystem* debugger) { _printLine(debugger->p, cpu->gprs[ARM_PC] - instructionLength, mode); } -static void _writeRegister(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { - struct CLIDebuggerBackend* be = debugger->backend; - struct ARMCore* cpu = debugger->d.core->cpu; - if (!dv || dv->type != CLIDV_INT_TYPE) { - be->printf(be, "%s\n", ERROR_MISSING_ARGS); - return; - } - if (!dv->next || dv->next->type != CLIDV_INT_TYPE) { - be->printf(be, "%s\n", ERROR_MISSING_ARGS); - return; - } - uint32_t regid = dv->intValue; - uint32_t value = dv->next->intValue; - if (regid >= ARM_PC) { - return; - } - cpu->gprs[regid] = value; -} - static void _setBreakpointARM(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { struct CLIDebuggerBackend* be = debugger->backend; if (!dv || dv->type != CLIDV_INT_TYPE) { @@ -184,38 +163,9 @@ static void _setBreakpointThumb(struct CLIDebugger* debugger, struct CLIDebugVec ARMDebuggerSetSoftwareBreakpoint(debugger->d.platform, address, MODE_THUMB); } -static uint32_t _lookupPlatformIdentifier(struct CLIDebuggerSystem* debugger, const char* name, struct CLIDebugVector* dv) { - struct ARMCore* cpu = debugger->p->d.core->cpu; - if (strcmp(name, "sp") == 0) { - return cpu->gprs[ARM_SP]; - } - if (strcmp(name, "lr") == 0) { - return cpu->gprs[ARM_LR]; - } - if (strcmp(name, "pc") == 0) { - return cpu->gprs[ARM_PC]; - } - if (strcmp(name, "cpsr") == 0) { - return cpu->cpsr.packed; - } - // TODO: test if mode has SPSR - if (strcmp(name, "spsr") == 0) { - return cpu->spsr.packed; - } - if (name[0] == 'r' && name[1] >= '0' && name[1] <= '9') { - int reg = atoi(&name[1]); - if (reg < 16) { - return cpu->gprs[reg]; - } - } - dv->type = CLIDV_ERROR_TYPE; - return 0; -} - void ARMCLIDebuggerCreate(struct CLIDebuggerSystem* debugger) { debugger->printStatus = _printStatus; debugger->disassemble = _disassemble; - debugger->lookupPlatformIdentifier = _lookupPlatformIdentifier; debugger->platformName = "ARM"; debugger->platformCommands = _armCommands; } diff --git a/src/arm/debugger/debugger.c b/src/arm/debugger/debugger.c index fde61a8a8..55105a17d 100644 --- a/src/arm/debugger/debugger.c +++ b/src/arm/debugger/debugger.c @@ -10,6 +10,7 @@ #include #include #include +#include DEFINE_VECTOR(ARMDebugBreakpointList, struct ARMDebugBreakpoint); DEFINE_VECTOR(ARMDebugWatchpointList, struct ARMDebugWatchpoint); @@ -24,6 +25,20 @@ static struct ARMDebugBreakpoint* _lookupBreakpoint(struct ARMDebugBreakpointLis return 0; } +static void _destroyBreakpoint(struct ARMDebugBreakpoint* breakpoint) { + if (breakpoint->condition) { + parseFree(breakpoint->condition); + free(breakpoint->condition); + } +} + +static void _destroyWatchpoint(struct ARMDebugWatchpoint* watchpoint) { + if (watchpoint->condition) { + parseFree(watchpoint->condition); + free(watchpoint->condition); + } +} + static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform* d) { struct ARMDebugger* debugger = (struct ARMDebugger*) d; int instructionLength; @@ -37,9 +52,16 @@ static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform* d) { if (!breakpoint) { return; } + if (breakpoint->condition) { + int32_t value; + int segment; + if (!mDebuggerEvaluateParseTree(d->p, breakpoint->condition, &value, &segment) || !(value || segment >= 0)) { + return; + } + } struct mDebuggerEntryInfo info = { .address = breakpoint->address, - .breakType = BREAKPOINT_HARDWARE + .type.bp.breakType = BREAKPOINT_HARDWARE }; mDebuggerEnter(d->p, DEBUGGER_ENTER_BREAKPOINT, &info); } @@ -50,12 +72,16 @@ static void ARMDebuggerDeinit(struct mDebuggerPlatform* platform); static void ARMDebuggerEnter(struct mDebuggerPlatform* d, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info); static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment); +static void ARMDebuggerSetConditionalBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment, struct ParseTree* condition); static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment); static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type); +static void ARMDebuggerSetConditionalWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type, struct ParseTree* condition); static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment); static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform*); static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform*); static void ARMDebuggerTrace(struct mDebuggerPlatform*, char* out, size_t* length); +static bool ARMDebuggerGetRegister(struct mDebuggerPlatform*, const char* name, int32_t* value); +static bool ARMDebuggerSetRegister(struct mDebuggerPlatform*, const char* name, int32_t value); struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) { struct mDebuggerPlatform* platform = (struct mDebuggerPlatform*) malloc(sizeof(struct ARMDebugger)); @@ -63,12 +89,16 @@ struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) { platform->init = ARMDebuggerInit; platform->deinit = ARMDebuggerDeinit; platform->setBreakpoint = ARMDebuggerSetBreakpoint; + platform->setConditionalBreakpoint = ARMDebuggerSetConditionalBreakpoint; platform->clearBreakpoint = ARMDebuggerClearBreakpoint; platform->setWatchpoint = ARMDebuggerSetWatchpoint; + platform->setConditionalWatchpoint = ARMDebuggerSetConditionalWatchpoint; platform->clearWatchpoint = ARMDebuggerClearWatchpoint; platform->checkBreakpoints = ARMDebuggerCheckBreakpoints; platform->hasBreakpoints = ARMDebuggerHasBreakpoints; platform->trace = ARMDebuggerTrace; + platform->getRegister = ARMDebuggerGetRegister; + platform->setRegister = ARMDebuggerSetRegister; return platform; } @@ -93,7 +123,15 @@ void ARMDebuggerDeinit(struct mDebuggerPlatform* platform) { } ARMDebuggerRemoveMemoryShim(debugger); + size_t i; + for (i = 0; i < ARMDebugBreakpointListSize(&debugger->breakpoints); ++i) { + _destroyBreakpoint(ARMDebugBreakpointListGetPointer(&debugger->breakpoints, i)); + } ARMDebugBreakpointListDeinit(&debugger->breakpoints); + + for (i = 0; i < ARMDebugWatchpointListSize(&debugger->watchpoints); ++i) { + _destroyWatchpoint(ARMDebugWatchpointListGetPointer(&debugger->watchpoints, i)); + } ARMDebugBreakpointListDeinit(&debugger->swBreakpoints); ARMDebugWatchpointListDeinit(&debugger->watchpoints); } @@ -161,9 +199,14 @@ void ARMDebuggerClearSoftwareBreakpoint(struct mDebuggerPlatform* d, uint32_t ad } static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) { + ARMDebuggerSetConditionalBreakpoint(d, address, segment, NULL); +} + +static void ARMDebuggerSetConditionalBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, struct ParseTree* condition) { UNUSED(segment); struct ARMDebugger* debugger = (struct ARMDebugger*) d; struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->breakpoints); + breakpoint->condition = condition; breakpoint->address = address; breakpoint->isSw = false; } @@ -175,6 +218,7 @@ static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform* d, uint32_t add size_t i; for (i = 0; i < ARMDebugBreakpointListSize(breakpoints); ++i) { if (ARMDebugBreakpointListGetPointer(breakpoints, i)->address == address) { + _destroyBreakpoint(ARMDebugBreakpointListGetPointer(breakpoints, i)); ARMDebugBreakpointListShift(breakpoints, i, 1); } } @@ -186,6 +230,10 @@ static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform* d) { } static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type) { + ARMDebuggerSetConditionalWatchpoint(d, address, segment, type, NULL); +} + +static void ARMDebuggerSetConditionalWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type, struct ParseTree* condition) { UNUSED(segment); struct ARMDebugger* debugger = (struct ARMDebugger*) d; if (!ARMDebugWatchpointListSize(&debugger->watchpoints)) { @@ -194,6 +242,7 @@ static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t addre struct ARMDebugWatchpoint* watchpoint = ARMDebugWatchpointListAppend(&debugger->watchpoints); watchpoint->address = address; watchpoint->type = type; + watchpoint->condition = condition; } static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) { @@ -203,6 +252,7 @@ static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t add size_t i; for (i = 0; i < ARMDebugWatchpointListSize(watchpoints); ++i) { if (ARMDebugWatchpointListGetPointer(watchpoints, i)->address == address) { + _destroyWatchpoint(ARMDebugWatchpointListGetPointer(watchpoints, i)); ARMDebugWatchpointListShift(watchpoints, i, 1); } } @@ -246,3 +296,81 @@ static void ARMDebuggerTrace(struct mDebuggerPlatform* d, char* out, size_t* len cpu->gprs[12], cpu->gprs[13], cpu->gprs[14], cpu->gprs[15], cpu->cpsr.packed, disassembly); } + +bool ARMDebuggerGetRegister(struct mDebuggerPlatform* d, const char* name, int32_t* value) { + struct ARMDebugger* debugger = (struct ARMDebugger*) d; + struct ARMCore* cpu = debugger->cpu; + + if (strcmp(name, "sp") == 0) { + *value = cpu->gprs[ARM_SP]; + return true; + } + if (strcmp(name, "lr") == 0) { + *value = cpu->gprs[ARM_LR]; + return true; + } + if (strcmp(name, "pc") == 0) { + *value = cpu->gprs[ARM_PC]; + return true; + } + if (strcmp(name, "cpsr") == 0) { + *value = cpu->cpsr.packed; + return true; + } + // TODO: test if mode has SPSR + if (strcmp(name, "spsr") == 0) { + *value = cpu->spsr.packed; + return true; + } + if (name[0] == 'r') { + char* end; + uint32_t reg = strtoul(&name[1], &end, 10); + if (reg <= ARM_PC) { + *value = cpu->gprs[reg]; + return true; + } + } + return false; +} + +bool ARMDebuggerSetRegister(struct mDebuggerPlatform* d, const char* name, int32_t value) { + struct ARMDebugger* debugger = (struct ARMDebugger*) d; + struct ARMCore* cpu = debugger->cpu; + + if (strcmp(name, "sp") == 0) { + cpu->gprs[ARM_SP] = value; + return true; + } + if (strcmp(name, "lr") == 0) { + cpu->gprs[ARM_LR] = value; + return true; + } + if (strcmp(name, "pc") == 0) { + cpu->gprs[ARM_PC] = value; + int32_t currentCycles = 0; + if (cpu->executionMode == MODE_ARM) { + ARM_WRITE_PC; + } else { + THUMB_WRITE_PC; + } + return true; + } + if (name[0] == 'r') { + char* end; + uint32_t reg = strtoul(&name[1], &end, 10); + if (reg > ARM_PC) { + return false; + } + cpu->gprs[reg] = value; + if (reg == ARM_PC) { + int32_t currentCycles = 0; + if (cpu->executionMode == MODE_ARM) { + ARM_WRITE_PC; + } else { + THUMB_WRITE_PC; + } + } + return true; + } + return false; +} diff --git a/src/arm/debugger/memory-debugger.c b/src/arm/debugger/memory-debugger.c index faa8651f4..2faca99a6 100644 --- a/src/arm/debugger/memory-debugger.c +++ b/src/arm/debugger/memory-debugger.c @@ -6,6 +6,7 @@ #include #include +#include #include @@ -97,21 +98,29 @@ static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, st for (i = 0; i < ARMDebugWatchpointListSize(&debugger->watchpoints); ++i) { watchpoint = ARMDebugWatchpointListGetPointer(&debugger->watchpoints, i); if (!((watchpoint->address ^ address) & ~width) && watchpoint->type & type) { + if (watchpoint->condition) { + int32_t value; + int segment; + if (!mDebuggerEvaluateParseTree(debugger->d.p, watchpoint->condition, &value, &segment) || !(value || segment >= 0)) { + return false; + } + } + switch (width + 1) { case 1: - info->oldValue = debugger->originalMemory.load8(debugger->cpu, address, 0); + info->type.wp.oldValue = debugger->originalMemory.load8(debugger->cpu, address, 0); break; case 2: - info->oldValue = debugger->originalMemory.load16(debugger->cpu, address, 0); + info->type.wp.oldValue = debugger->originalMemory.load16(debugger->cpu, address, 0); break; case 4: - info->oldValue = debugger->originalMemory.load32(debugger->cpu, address, 0); + info->type.wp.oldValue = debugger->originalMemory.load32(debugger->cpu, address, 0); break; } - info->newValue = newValue; + info->type.wp.newValue = newValue; info->address = address; - info->watchType = watchpoint->type; - info->accessType = type; + info->type.wp.watchType = watchpoint->type; + info->type.wp.accessType = type; return true; } } diff --git a/src/core/cheats.c b/src/core/cheats.c index 21a7b8a7b..d0ed72508 100644 --- a/src/core/cheats.c +++ b/src/core/cheats.c @@ -51,6 +51,8 @@ void mCheatDeviceCreate(struct mCheatDevice* device) { device->d.id = M_CHEAT_DEVICE_ID; device->d.init = mCheatDeviceInit; device->d.deinit = mCheatDeviceDeinit; + device->autosave = false; + device->buttonDown = false; mCheatSetsInit(&device->cheats, 4); } @@ -252,11 +254,25 @@ bool mCheatSaveFile(struct mCheatDevice* device, struct VFile* vf) { return true; } +#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 +void mCheatAutosave(struct mCheatDevice* device) { + if (!device->autosave) { + return; + } + struct VFile* vf = mDirectorySetOpenSuffix(&device->p->dirs, device->p->dirs.cheats, ".cheats", O_WRONLY | O_CREAT | O_TRUNC); + if (!vf) { + return; + } + mCheatSaveFile(device, vf); + vf->close(vf); +} +#endif + void mCheatRefresh(struct mCheatDevice* device, struct mCheatSet* cheats) { + cheats->refresh(cheats, device); if (!cheats->enabled) { return; } - cheats->refresh(cheats, device); size_t elseLoc = 0; size_t endLoc = 0; @@ -350,6 +366,12 @@ void mCheatRefresh(struct mCheatDevice* device, struct mCheatSet* cheats) { negativeConditionRemaining = cheat->negativeRepeat; operationsRemaining = 1; break; + case CHEAT_IF_BUTTON: + condition = device->buttonDown; + conditionRemaining = cheat->repeat; + negativeConditionRemaining = cheat->negativeRepeat; + operationsRemaining = 1; + break; } if (performAssignment) { @@ -374,6 +396,10 @@ void mCheatRefresh(struct mCheatDevice* device, struct mCheatSet* cheats) { } } +void mCheatPressButton(struct mCheatDevice* device, bool down) { + device->buttonDown = down; +} + void mCheatDeviceInit(void* cpu, struct mCPUComponent* component) { UNUSED(cpu); struct mCheatDevice* device = (struct mCheatDevice*) component; diff --git a/src/core/config.c b/src/core/config.c index 950607469..44861e3c2 100644 --- a/src/core/config.c +++ b/src/core/config.c @@ -379,6 +379,7 @@ void mCoreConfigMap(const struct mCoreConfig* config, struct mCoreOptions* opts) _lookupCharValue(config, "savestatePath", &opts->savestatePath); _lookupCharValue(config, "screenshotPath", &opts->screenshotPath); _lookupCharValue(config, "patchPath", &opts->patchPath); + _lookupCharValue(config, "cheatsPath", &opts->cheatsPath); } void mCoreConfigLoadDefaults(struct mCoreConfig* config, const struct mCoreOptions* opts) { @@ -443,10 +444,12 @@ void mCoreConfigFreeOpts(struct mCoreOptions* opts) { free(opts->savestatePath); free(opts->screenshotPath); free(opts->patchPath); + free(opts->cheatsPath); opts->bios = 0; opts->shader = 0; opts->savegamePath = 0; opts->savestatePath = 0; opts->screenshotPath = 0; opts->patchPath = 0; + opts->cheatsPath = 0; } diff --git a/src/core/core.c b/src/core/core.c index 526b63e7e..18775af32 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#include #include #include #include @@ -172,6 +173,30 @@ bool mCoreAutoloadPatch(struct mCore* core) { core->loadPatch(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".bps", O_RDONLY)); } +bool mCoreAutoloadCheats(struct mCore* core) { + bool success = true; + int cheatAuto; + if (!mCoreConfigGetIntValue(&core->config, "cheatAutoload", &cheatAuto) || cheatAuto) { + struct VFile* vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.cheats, ".cheats", O_RDONLY); + if (vf) { + struct mCheatDevice* device = core->cheatDevice(core); + if (!device) { + success = false; + } else { + success = mCheatParseFile(device, vf); + } + vf->close(vf); + } + } + if (!mCoreConfigGetIntValue(&core->config, "cheatAutosave", &cheatAuto) || cheatAuto) { + struct mCheatDevice* device = core->cheatDevice(core); + if (device) { + device->autosave = true; + } + } + return success; +} + bool mCoreSaveState(struct mCore* core, int slot, int flags) { struct VFile* vf = mCoreGetState(core, slot, true); if (!vf) { @@ -264,7 +289,7 @@ void mCoreInitConfig(struct mCore* core, const char* port) { } void mCoreLoadConfig(struct mCore* core) { -#ifndef MINIMAL_CORE +#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 mCoreConfigLoad(&core->config); #endif mCoreLoadForeignConfig(core, &core->config); @@ -272,12 +297,16 @@ void mCoreLoadConfig(struct mCore* core) { void mCoreLoadForeignConfig(struct mCore* core, const struct mCoreConfig* config) { mCoreConfigMap(config, &core->opts); -#ifndef MINIMAL_CORE +#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 mDirectorySetMapOptions(&core->dirs, &core->opts); #endif if (core->opts.audioBuffers) { core->setAudioBufferSize(core, core->opts.audioBuffers); } + + mCoreConfigCopyValue(&core->config, config, "cheatAutosave"); + mCoreConfigCopyValue(&core->config, config, "cheatAutoload"); + core->loadConfig(core, config); } diff --git a/src/core/directories.c b/src/core/directories.c index 4d52bec43..295614a12 100644 --- a/src/core/directories.c +++ b/src/core/directories.c @@ -16,6 +16,7 @@ void mDirectorySetInit(struct mDirectorySet* dirs) { dirs->patch = 0; dirs->state = 0; dirs->screenshot = 0; + dirs->cheats = 0; } void mDirectorySetDeinit(struct mDirectorySet* dirs) { @@ -34,6 +35,9 @@ void mDirectorySetDeinit(struct mDirectorySet* dirs) { if (dirs->archive == dirs->screenshot) { dirs->screenshot = NULL; } + if (dirs->archive == dirs->cheats) { + dirs->cheats = NULL; + } dirs->archive->close(dirs->archive); dirs->archive = NULL; } @@ -48,6 +52,9 @@ void mDirectorySetDeinit(struct mDirectorySet* dirs) { if (dirs->save == dirs->screenshot) { dirs->screenshot = NULL; } + if (dirs->save == dirs->cheats) { + dirs->cheats = NULL; + } dirs->save->close(dirs->save); dirs->save = NULL; } @@ -59,6 +66,9 @@ void mDirectorySetDeinit(struct mDirectorySet* dirs) { if (dirs->patch == dirs->screenshot) { dirs->screenshot = NULL; } + if (dirs->patch == dirs->cheats) { + dirs->cheats = NULL; + } dirs->patch->close(dirs->patch); dirs->patch = NULL; } @@ -67,14 +77,25 @@ void mDirectorySetDeinit(struct mDirectorySet* dirs) { if (dirs->state == dirs->screenshot) { dirs->state = NULL; } + if (dirs->state == dirs->cheats) { + dirs->cheats = NULL; + } dirs->state->close(dirs->state); dirs->state = NULL; } if (dirs->screenshot) { + if (dirs->screenshot == dirs->cheats) { + dirs->cheats = NULL; + } dirs->screenshot->close(dirs->screenshot); dirs->screenshot = NULL; } + + if (dirs->cheats) { + dirs->cheats->close(dirs->cheats); + dirs->cheats = NULL; + } } void mDirectorySetAttachBase(struct mDirectorySet* dirs, struct VDir* base) { @@ -91,6 +112,9 @@ void mDirectorySetAttachBase(struct mDirectorySet* dirs, struct VDir* base) { if (!dirs->screenshot) { dirs->screenshot = dirs->base; } + if (!dirs->cheats) { + dirs->cheats = dirs->base; + } } void mDirectorySetDetachBase(struct mDirectorySet* dirs) { @@ -106,6 +130,9 @@ void mDirectorySetDetachBase(struct mDirectorySet* dirs) { if (dirs->screenshot == dirs->base) { dirs->screenshot = NULL; } + if (dirs->cheats == dirs->base) { + dirs->cheats = NULL; + } if (dirs->base) { dirs->base->close(dirs->base); @@ -183,5 +210,15 @@ void mDirectorySetMapOptions(struct mDirectorySet* dirs, const struct mCoreOptio dirs->patch = dir; } } + + if (opts->cheatsPath) { + struct VDir* dir = VDirOpen(opts->cheatsPath); + if (dir) { + if (dirs->cheats && dirs->cheats != dirs->base) { + dirs->cheats->close(dirs->cheats); + } + dirs->cheats = dir; + } + } } #endif diff --git a/src/core/input.c b/src/core/input.c index 6d391ee3b..fe24d0822 100644 --- a/src/core/input.c +++ b/src/core/input.c @@ -11,7 +11,7 @@ #include -#define SECTION_NAME_MAX 50 +#define SECTION_NAME_MAX 128 #define KEY_NAME_MAX 32 #define KEY_VALUE_MAX 16 #define AXIS_INFO_MAX 12 diff --git a/src/core/mem-search.c b/src/core/mem-search.c index 5cf74140f..4f007d9ef 100644 --- a/src/core/mem-search.c +++ b/src/core/mem-search.c @@ -10,248 +10,112 @@ DEFINE_VECTOR(mCoreMemorySearchResults, struct mCoreMemorySearchResult); -static size_t _search32(const void* mem, size_t size, const struct mCoreMemoryBlock* block, uint32_t value32, struct mCoreMemorySearchResults* out, size_t limit) { +static bool _op(int32_t value, int32_t match, enum mCoreMemorySearchOp op) { + switch (op) { + case mCORE_MEMORY_SEARCH_GREATER: + return value > match; + case mCORE_MEMORY_SEARCH_LESS: + return value < match; + case mCORE_MEMORY_SEARCH_EQUAL: + case mCORE_MEMORY_SEARCH_DELTA: + return value == match; + } +} + +static size_t _search32(const void* mem, size_t size, const struct mCoreMemoryBlock* block, uint32_t value32, enum mCoreMemorySearchOp op, struct mCoreMemorySearchResults* out, size_t limit) { const uint32_t* mem32 = mem; size_t found = 0; uint32_t start = block->start; uint32_t end = size; // TODO: Segments size_t i; // TODO: Big endian - for (i = 0; (!limit || found < limit) && i < end; i += 16) { - int mask = 0; - mask |= (mem32[(i >> 2) + 0] == value32) << 0; - mask |= (mem32[(i >> 2) + 1] == value32) << 1; - mask |= (mem32[(i >> 2) + 2] == value32) << 2; - mask |= (mem32[(i >> 2) + 3] == value32) << 3; - if (!mask) { - continue; - } - if ((mask & 1) && (!limit || found < limit)) { + for (i = 0; (!limit || found < limit) && i < end; i += 4) { + if (_op(mem32[i >> 2], value32, op)) { struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); res->address = start + i; - res->type = mCORE_MEMORY_SEARCH_32; - res->segment = -1; // TODO - res->guessDivisor = 1; - ++found; - } - if ((mask & 2) && (!limit || found < limit)) { - struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); - res->address = start + i + 4; - res->type = mCORE_MEMORY_SEARCH_32; - res->segment = -1; // TODO - res->guessDivisor = 1; - ++found; - } - if ((mask & 4) && (!limit || found < limit)) { - struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); - res->address = start + i + 8; - res->type = mCORE_MEMORY_SEARCH_32; - res->segment = -1; // TODO - res->guessDivisor = 1; - ++found; - } - if ((mask & 8) && (!limit || found < limit)) { - struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); - res->address = start + i + 12; - res->type = mCORE_MEMORY_SEARCH_32; + res->type = mCORE_MEMORY_SEARCH_INT; + res->width = 4; res->segment = -1; // TODO res->guessDivisor = 1; + res->guessMultiplier = 1; + res->oldValue = value32; ++found; } } - // TODO: last 12 bytes return found; } -static size_t _search16(const void* mem, size_t size, const struct mCoreMemoryBlock* block, uint16_t value16, struct mCoreMemorySearchResults* out, size_t limit) { +static size_t _search16(const void* mem, size_t size, const struct mCoreMemoryBlock* block, uint16_t value16, enum mCoreMemorySearchOp op, struct mCoreMemorySearchResults* out, size_t limit) { const uint16_t* mem16 = mem; size_t found = 0; uint32_t start = block->start; uint32_t end = size; // TODO: Segments size_t i; // TODO: Big endian - for (i = 0; (!limit || found < limit) && i < end; i += 16) { - int mask = 0; - mask |= (mem16[(i >> 1) + 0] == value16) << 0; - mask |= (mem16[(i >> 1) + 1] == value16) << 1; - mask |= (mem16[(i >> 1) + 2] == value16) << 2; - mask |= (mem16[(i >> 1) + 3] == value16) << 3; - mask |= (mem16[(i >> 1) + 4] == value16) << 4; - mask |= (mem16[(i >> 1) + 5] == value16) << 5; - mask |= (mem16[(i >> 1) + 6] == value16) << 6; - mask |= (mem16[(i >> 1) + 7] == value16) << 7; - if (!mask) { - continue; - } - if ((mask & 1) && (!limit || found < limit)) { + for (i = 0; (!limit || found < limit) && i < end; i += 2) { + if (_op(mem16[i >> 1], value16, op)) { struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); res->address = start + i; - res->type = mCORE_MEMORY_SEARCH_16; - res->segment = -1; // TODO - res->guessDivisor = 1; - ++found; - } - if ((mask & 2) && (!limit || found < limit)) { - struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); - res->address = start + i + 2; - res->type = mCORE_MEMORY_SEARCH_16; - res->segment = -1; // TODO - res->guessDivisor = 1; - ++found; - } - if ((mask & 4) && (!limit || found < limit)) { - struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); - res->address = start + i + 4; - res->type = mCORE_MEMORY_SEARCH_16; - res->segment = -1; // TODO - res->guessDivisor = 1; - ++found; - } - if ((mask & 8) && (!limit || found < limit)) { - struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); - res->address = start + i + 6; - res->type = mCORE_MEMORY_SEARCH_16; - res->segment = -1; // TODO - res->guessDivisor = 1; - ++found; - } - if ((mask & 16) && (!limit || found < limit)) { - struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); - res->address = start + i + 8; - res->type = mCORE_MEMORY_SEARCH_16; - res->segment = -1; // TODO - res->guessDivisor = 1; - ++found; - } - if ((mask & 32) && (!limit || found < limit)) { - struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); - res->address = start + i + 10; - res->type = mCORE_MEMORY_SEARCH_16; - res->segment = -1; // TODO - res->guessDivisor = 1; - ++found; - } - if ((mask & 64) && (!limit || found < limit)) { - struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); - res->address = start + i + 12; - res->type = mCORE_MEMORY_SEARCH_16; - res->segment = -1; // TODO - res->guessDivisor = 1; - ++found; - } - if ((mask & 128) && (!limit || found < limit)) { - struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); - res->address = start + i + 14; - res->type = mCORE_MEMORY_SEARCH_16; + res->type = mCORE_MEMORY_SEARCH_INT; + res->width = 2; res->segment = -1; // TODO res->guessDivisor = 1; + res->guessMultiplier = 1; + res->oldValue = value16; ++found; } } - // TODO: last 14 bytes return found; } -static size_t _search8(const void* mem, size_t size, const struct mCoreMemoryBlock* block, uint8_t value8, struct mCoreMemorySearchResults* out, size_t limit) { +static size_t _search8(const void* mem, size_t size, const struct mCoreMemoryBlock* block, uint8_t value8, enum mCoreMemorySearchOp op, struct mCoreMemorySearchResults* out, size_t limit) { const uint8_t* mem8 = mem; size_t found = 0; uint32_t start = block->start; uint32_t end = size; // TODO: Segments size_t i; - for (i = 0; (!limit || found < limit) && i < end; i += 8) { - int mask = 0; - mask |= (mem8[i + 0] == value8) << 0; - mask |= (mem8[i + 1] == value8) << 1; - mask |= (mem8[i + 2] == value8) << 2; - mask |= (mem8[i + 3] == value8) << 3; - mask |= (mem8[i + 4] == value8) << 4; - mask |= (mem8[i + 5] == value8) << 5; - mask |= (mem8[i + 6] == value8) << 6; - mask |= (mem8[i + 7] == value8) << 7; - if (!mask) { - continue; - } - if ((mask & 1) && (!limit || found < limit)) { + for (i = 0; (!limit || found < limit) && i < end; ++i) { + if (_op(mem8[i], value8, op)) { struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); res->address = start + i; - res->type = mCORE_MEMORY_SEARCH_8; - res->segment = -1; // TODO - res->guessDivisor = 1; - ++found; - } - if ((mask & 2) && (!limit || found < limit)) { - struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); - res->address = start + i + 1; - res->type = mCORE_MEMORY_SEARCH_8; - res->segment = -1; // TODO - res->guessDivisor = 1; - ++found; - } - if ((mask & 4) && (!limit || found < limit)) { - struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); - res->address = start + i + 2; - res->type = mCORE_MEMORY_SEARCH_8; - res->segment = -1; // TODO - res->guessDivisor = 1; - ++found; - } - if ((mask & 8) && (!limit || found < limit)) { - struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); - res->address = start + i + 3; - res->type = mCORE_MEMORY_SEARCH_8; - res->segment = -1; // TODO - res->guessDivisor = 1; - ++found; - } - if ((mask & 16) && (!limit || found < limit)) { - struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); - res->address = start + i + 4; - res->type = mCORE_MEMORY_SEARCH_8; - res->segment = -1; // TODO - res->guessDivisor = 1; - ++found; - } - if ((mask & 32) && (!limit || found < limit)) { - struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); - res->address = start + i + 5; - res->type = mCORE_MEMORY_SEARCH_8; - res->segment = -1; // TODO - res->guessDivisor = 1; - ++found; - } - if ((mask & 64) && (!limit || found < limit)) { - struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); - res->address = start + i + 6; - res->type = mCORE_MEMORY_SEARCH_8; - res->segment = -1; // TODO - res->guessDivisor = 1; - ++found; - } - if ((mask & 128) && (!limit || found < limit)) { - struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); - res->address = start + i + 7; - res->type = mCORE_MEMORY_SEARCH_8; + res->type = mCORE_MEMORY_SEARCH_INT; + res->width = 1; res->segment = -1; // TODO res->guessDivisor = 1; + res->guessMultiplier = 1; + res->oldValue = value8; ++found; } } - // TODO: last 7 bytes return found; } -static size_t _searchStr(const void* mem, size_t size, const struct mCoreMemoryBlock* block, const char* valueStr, struct mCoreMemorySearchResults* out, size_t limit) { +static size_t _searchInt(const void* mem, size_t size, const struct mCoreMemoryBlock* block, const struct mCoreMemorySearchParams* params, struct mCoreMemorySearchResults* out, size_t limit) { + if (params->align == params->width || params->align == -1) { + switch (params->width) { + case 4: + return _search32(mem, size, block, params->valueInt, params->op, out, limit); + case 2: + return _search16(mem, size, block, params->valueInt, params->op, out, limit); + case 1: + return _search8(mem, size, block, params->valueInt, params->op, out, limit); + } + } + return 0; +} + +static size_t _searchStr(const void* mem, size_t size, const struct mCoreMemoryBlock* block, const char* valueStr, int len, struct mCoreMemorySearchResults* out, size_t limit) { const char* memStr = mem; size_t found = 0; - size_t len = strlen(valueStr); uint32_t start = block->start; uint32_t end = size; // TODO: Segments size_t i; for (i = 0; (!limit || found < limit) && i < end - len; ++i) { - if (!strncmp(valueStr, &memStr[i], len)) { + if (!memcmp(valueStr, &memStr[i], len)) { struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out); res->address = start + i; res->type = mCORE_MEMORY_SEARCH_STRING; + res->width = len; res->segment = -1; // TODO ++found; } @@ -259,11 +123,11 @@ static size_t _searchStr(const void* mem, size_t size, const struct mCoreMemoryB return found; } -static size_t _searchGuess(const void* mem, size_t size, const struct mCoreMemoryBlock* block, const char* valueStr, struct mCoreMemorySearchResults* out, size_t limit) { +static size_t _searchGuess(const void* mem, size_t size, const struct mCoreMemoryBlock* block, const struct mCoreMemorySearchParams* params, struct mCoreMemorySearchResults* out, size_t limit) { // TODO: As str char* end; - uint64_t value; + int64_t value; size_t found = 0; @@ -271,14 +135,14 @@ static size_t _searchGuess(const void* mem, size_t size, const struct mCoreMemor mCoreMemorySearchResultsInit(&tmp, 0); // Decimal: - value = strtoull(valueStr, &end, 10); + value = strtoll(params->valueStr, &end, 10); if (end && !end[0]) { - if (value > 0x10000) { - found += _search32(mem, size, block, value, out, limit ? limit - found : 0); - } else if (value > 0x100) { - found += _search16(mem, size, block, value, out, limit ? limit - found : 0); + if ((params->width == -1 && value > 0x10000) || params->width == 4) { + found += _search32(mem, size, block, value, params->op, out, limit ? limit - found : 0); + } else if ((params->width == -1 && value > 0x100) || params->width == 2) { + found += _search16(mem, size, block, value, params->op, out, limit ? limit - found : 0); } else { - found += _search8(mem, size, block, value, out, limit ? limit - found : 0); + found += _search8(mem, size, block, value, params->op, out, limit ? limit - found : 0); } uint32_t divisor = 1; @@ -287,12 +151,12 @@ static size_t _searchGuess(const void* mem, size_t size, const struct mCoreMemor value /= 10; divisor *= 10; - if (value > 0x10000) { - found += _search32(mem, size, block, value, &tmp, limit ? limit - found : 0); - } else if (value > 0x100) { - found += _search16(mem, size, block, value, &tmp, limit ? limit - found : 0); + if ((params->width == -1 && value > 0x10000) || params->width == 4) { + found += _search32(mem, size, block, value, params->op, &tmp, limit ? limit - found : 0); + } else if ((params->width == -1 && value > 0x100) || params->width == 2) { + found += _search16(mem, size, block, value, params->op, &tmp, limit ? limit - found : 0); } else { - found += _search8(mem, size, block, value, &tmp, limit ? limit - found : 0); + found += _search8(mem, size, block, value, params->op, &tmp, limit ? limit - found : 0); } size_t i; for (i = 0; i < mCoreMemorySearchResultsSize(&tmp); ++i) { @@ -304,14 +168,14 @@ static size_t _searchGuess(const void* mem, size_t size, const struct mCoreMemor } // Hex: - value = strtoull(valueStr, &end, 16); + value = strtoll(params->valueStr, &end, 16); if (end && !end[0]) { - if (value > 0x10000) { - found += _search32(mem, size, block, value, out, limit ? limit - found : 0); - } else if (value > 0x100) { - found += _search16(mem, size, block, value, out, limit ? limit - found : 0); + if ((params->width == -1 && value > 0x10000) || params->width == 4) { + found += _search32(mem, size, block, value, params->op, out, limit ? limit - found : 0); + } else if ((params->width == -1 && value > 0x100) || params->width == 2) { + found += _search16(mem, size, block, value, params->op, out, limit ? limit - found : 0); } else { - found += _search8(mem, size, block, value, out, limit ? limit - found : 0); + found += _search8(mem, size, block, value, params->op, out, limit ? limit - found : 0); } uint32_t divisor = 1; @@ -320,12 +184,12 @@ static size_t _searchGuess(const void* mem, size_t size, const struct mCoreMemor value >>= 4; divisor <<= 4; - if (value > 0x10000) { - found += _search32(mem, size, block, value, &tmp, limit ? limit - found : 0); - } else if (value > 0x100) { - found += _search16(mem, size, block, value, &tmp, limit ? limit - found : 0); + if ((params->width == -1 && value > 0x10000) || params->width == 4) { + found += _search32(mem, size, block, value, params->op, &tmp, limit ? limit - found : 0); + } else if ((params->width == -1 && value > 0x100) || params->width == 2) { + found += _search16(mem, size, block, value, params->op, &tmp, limit ? limit - found : 0); } else { - found += _search8(mem, size, block, value, &tmp, limit ? limit - found : 0); + found += _search8(mem, size, block, value, params->op, &tmp, limit ? limit - found : 0); } size_t i; for (i = 0; i < mCoreMemorySearchResultsSize(&tmp); ++i) { @@ -342,16 +206,12 @@ static size_t _searchGuess(const void* mem, size_t size, const struct mCoreMemor static size_t _search(const void* mem, size_t size, const struct mCoreMemoryBlock* block, const struct mCoreMemorySearchParams* params, struct mCoreMemorySearchResults* out, size_t limit) { switch (params->type) { - case mCORE_MEMORY_SEARCH_32: - return _search32(mem, size, block, params->value32, out, limit); - case mCORE_MEMORY_SEARCH_16: - return _search16(mem, size, block, params->value16, out, limit); - case mCORE_MEMORY_SEARCH_8: - return _search8(mem, size, block, params->value8, out, limit); + case mCORE_MEMORY_SEARCH_INT: + return _searchInt(mem, size, block, params, out, limit); case mCORE_MEMORY_SEARCH_STRING: - return _searchStr(mem, size, block, params->valueStr, out, limit); + return _searchStr(mem, size, block, params->valueStr, params->width, out, limit); case mCORE_MEMORY_SEARCH_GUESS: - return _searchGuess(mem, size, block, params->valueStr, out, limit); + return _searchGuess(mem, size, block, params, out, limit); } } @@ -378,34 +238,42 @@ void mCoreMemorySearch(struct mCore* core, const struct mCoreMemorySearchParams* } } -bool _testGuess(struct mCore* core, const struct mCoreMemorySearchResult* res, const struct mCoreMemorySearchParams* params) { - uint64_t value; +bool _testGuess(struct mCore* core, struct mCoreMemorySearchResult* res, const struct mCoreMemorySearchParams* params) { + int64_t value; + int32_t offset = 0; char* end; - - value = strtoull(params->valueStr, &end, 10); - if (end) { - if (core->rawRead8(core, res->address, res->segment) * res->guessDivisor == value) { - return true; - } - if (!(res->address & 1) && core->rawRead16(core, res->address, res->segment) * res->guessDivisor == value) { - return true; - } - if (!(res->address & 3) && core->rawRead32(core, res->address, res->segment) * res->guessDivisor == value) { - return true; - } + if (params->op == mCORE_MEMORY_SEARCH_DELTA) { + offset = res->oldValue; } - value = strtoull(params->valueStr, &end, 16); + value = strtoll(params->valueStr, &end, 10); if (end) { - if (core->rawRead8(core, res->address, res->segment) * res->guessDivisor == value) { + res->oldValue += value; + if (_op(core->rawRead8(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier, value + offset, params->op)) { return true; } - if (!(res->address & 1) && core->rawRead16(core, res->address, res->segment) * res->guessDivisor == value) { + if (!(res->address & 1) && (res->width >= 2 || res->width == -1) && _op(core->rawRead16(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier, value + offset, params->op)) { return true; } - if (!(res->address & 3) && core->rawRead32(core, res->address, res->segment) * res->guessDivisor == value) { + if (!(res->address & 3) && (res->width >= 4 || res->width == -1) && _op(core->rawRead32(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier, value + offset, params->op)) { return true; } + res->oldValue -= value; + } + + value = strtoll(params->valueStr, &end, 16); + if (end) { + res->oldValue += value; + if (_op(core->rawRead8(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier, value + offset, params->op)) { + return true; + } + if (!(res->address & 1) && (res->width >= 2 || res->width == -1) && _op(core->rawRead16(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier, value + offset, params->op)) { + return true; + } + if (!(res->address & 3) && (res->width >= 4 || res->width == -1) && _op(core->rawRead32(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier, value + offset, params->op)) { + return true; + } + res->oldValue -= value; } return false; } @@ -415,76 +283,39 @@ void mCoreMemorySearchRepeat(struct mCore* core, const struct mCoreMemorySearchP for (i = 0; i < mCoreMemorySearchResultsSize(inout); ++i) { struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsGetPointer(inout, i); switch (res->type) { - case mCORE_MEMORY_SEARCH_8: - switch (params->type) { - case mCORE_MEMORY_SEARCH_8: - if (core->rawRead8(core, res->address, res->segment) != params->value8) { - mCoreMemorySearchResultsShift(inout, i, 1); - --i; - } - break; - case mCORE_MEMORY_SEARCH_16: - if (core->rawRead8(core, res->address, res->segment) != params->value16) { - mCoreMemorySearchResultsShift(inout, i, 1); - --i; - } - break; - case mCORE_MEMORY_SEARCH_32: - if (core->rawRead32(core, res->address, res->segment) != params->value32) { - mCoreMemorySearchResultsShift(inout, i, 1); - --i; - } - break; - case mCORE_MEMORY_SEARCH_GUESS: + case mCORE_MEMORY_SEARCH_INT: + if (params->type == mCORE_MEMORY_SEARCH_GUESS) { if (!_testGuess(core, res, params)) { - mCoreMemorySearchResultsShift(inout, i, 1); + *res = *mCoreMemorySearchResultsGetPointer(inout, mCoreMemorySearchResultsSize(inout) - 1); + mCoreMemorySearchResultsResize(inout, -1); --i; } - break; - default: - break; - } - break; - case mCORE_MEMORY_SEARCH_16: - switch (params->type) { - case mCORE_MEMORY_SEARCH_16: - if (core->rawRead16(core, res->address, res->segment) != params->value16) { - mCoreMemorySearchResultsShift(inout, i, 1); - --i; + } else if (params->type == mCORE_MEMORY_SEARCH_INT) { + int32_t oldValue = params->valueInt; + if (params->op == mCORE_MEMORY_SEARCH_DELTA) { + oldValue += res->oldValue; } - break; - case mCORE_MEMORY_SEARCH_32: - if (core->rawRead32(core, res->address, res->segment) != params->value32) { - mCoreMemorySearchResultsShift(inout, i, 1); - --i; + int32_t value = 0; + switch (params->width) { + case 1: + value = core->rawRead8(core, res->address, res->segment); + break; + case 2: + value = core->rawRead16(core, res->address, res->segment); + break; + case 4: + value = core->rawRead32(core, res->address, res->segment); + break; + default: + break; } - break; - case mCORE_MEMORY_SEARCH_GUESS: - if (!_testGuess(core, res, params)) { - mCoreMemorySearchResultsShift(inout, i, 1); + if (!_op(value, oldValue, params->op)) { + *res = *mCoreMemorySearchResultsGetPointer(inout, mCoreMemorySearchResultsSize(inout) - 1); + mCoreMemorySearchResultsResize(inout, -1); --i; + } else { + res->oldValue = value; } - break; - default: - break; - } - break; - case mCORE_MEMORY_SEARCH_32: - switch (params->type) { - case mCORE_MEMORY_SEARCH_32: - if (core->rawRead32(core, res->address, res->segment) != params->value32) { - mCoreMemorySearchResultsShift(inout, i, 1); - --i; - } - break; - case mCORE_MEMORY_SEARCH_GUESS: - if (!_testGuess(core, res, params)) { - mCoreMemorySearchResultsShift(inout, i, 1); - --i; - } - break; - default: - break; } break; case mCORE_MEMORY_SEARCH_STRING: diff --git a/src/core/thread.c b/src/core/thread.c index 3a0c802ea..c08a1ab7e 100644 --- a/src/core/thread.c +++ b/src/core/thread.c @@ -623,6 +623,12 @@ static void _mCoreLog(struct mLogger* logger, int category, enum mLogLevel level printf("%s: ", mLogCategoryName(category)); vprintf(format, args); printf("\n"); + struct mCoreThread* thread = mCoreThreadGet(); + if (thread && level == mLOG_FATAL) { +#ifndef DISABLE_THREADING + mCoreThreadMarkCrashed(thread); +#endif + } } struct mLogger* mCoreThreadLogger(void) { diff --git a/src/debugger/cli-debugger.c b/src/debugger/cli-debugger.c index 871143436..d0ed6b672 100644 --- a/src/debugger/cli-debugger.c +++ b/src/debugger/cli-debugger.c @@ -26,6 +26,7 @@ const char* ERROR_MISSING_ARGS = "Arguments missing"; // TODO: share const char* ERROR_OVERFLOW = "Arguments overflow"; +const char* ERROR_INVALID_ARGS = "Invalid arguments"; #if !defined(NDEBUG) && !defined(_WIN32) static void _breakInto(struct CLIDebugger*, struct CLIDebugVector*); @@ -51,6 +52,7 @@ static void _setWriteWatchpoint(struct CLIDebugger*, struct CLIDebugVector*); static void _trace(struct CLIDebugger*, struct CLIDebugVector*); static void _writeByte(struct CLIDebugger*, struct CLIDebugVector*); static void _writeHalfword(struct CLIDebugger*, struct CLIDebugVector*); +static void _writeRegister(struct CLIDebugger*, struct CLIDebugVector*); static void _writeWord(struct CLIDebugger*, struct CLIDebugVector*); static void _dumpByte(struct CLIDebugger*, struct CLIDebugVector*); static void _dumpHalfword(struct CLIDebugger*, struct CLIDebugVector*); @@ -60,50 +62,51 @@ static void _source(struct CLIDebugger*, struct CLIDebugVector*); #endif static struct CLIDebuggerCommandSummary _debuggerCommands[] = { - { "b", _setBreakpoint, CLIDVParse, "Set a breakpoint" }, - { "break", _setBreakpoint, CLIDVParse, "Set a breakpoint" }, - { "c", _continue, 0, "Continue execution" }, - { "continue", _continue, 0, "Continue execution" }, - { "d", _clearBreakpoint, CLIDVParse, "Delete a breakpoint" }, - { "delete", _clearBreakpoint, CLIDVParse, "Delete a breakpoint" }, - { "dis", _disassemble, CLIDVParse, "Disassemble instructions" }, - { "disasm", _disassemble, CLIDVParse, "Disassemble instructions" }, - { "disassemble", _disassemble, CLIDVParse, "Disassemble instructions" }, - { "h", _printHelp, CLIDVStringParse, "Print help" }, - { "help", _printHelp, CLIDVStringParse, "Print help" }, - { "i", _printStatus, 0, "Print the current status" }, - { "info", _printStatus, 0, "Print the current status" }, - { "n", _next, 0, "Execute next instruction" }, - { "next", _next, 0, "Execute next instruction" }, - { "p", _print, CLIDVParse, "Print a value" }, - { "p/t", _printBin, CLIDVParse, "Print a value as binary" }, - { "p/x", _printHex, CLIDVParse, "Print a value as hexadecimal" }, - { "print", _print, CLIDVParse, "Print a value" }, - { "print/t", _printBin, CLIDVParse, "Print a value as binary" }, - { "print/x", _printHex, CLIDVParse, "Print a value as hexadecimal" }, - { "q", _quit, 0, "Quit the emulator" }, - { "quit", _quit, 0, "Quit the emulator" }, - { "reset", _reset, 0, "Reset the emulation" }, - { "r/1", _readByte, CLIDVParse, "Read a byte from a specified offset" }, - { "r/2", _readHalfword, CLIDVParse, "Read a halfword from a specified offset" }, - { "r/4", _readWord, CLIDVParse, "Read a word from a specified offset" }, - { "status", _printStatus, 0, "Print the current status" }, - { "trace", _trace, CLIDVParse, "Trace a fixed number of instructions" }, - { "w", _setWatchpoint, CLIDVParse, "Set a watchpoint" }, - { "w/1", _writeByte, CLIDVParse, "Write a byte at a specified offset" }, - { "w/2", _writeHalfword, CLIDVParse, "Write a halfword at a specified offset" }, - { "w/4", _writeWord, CLIDVParse, "Write a word at a specified offset" }, - { "watch", _setWatchpoint, CLIDVParse, "Set a watchpoint" }, - { "watch/r", _setReadWatchpoint, CLIDVParse, "Set a read watchpoint" }, - { "watch/w", _setWriteWatchpoint, CLIDVParse, "Set a write watchpoint" }, - { "x/1", _dumpByte, CLIDVParse, "Examine bytes at a specified offset" }, - { "x/2", _dumpHalfword, CLIDVParse, "Examine halfwords at a specified offset" }, - { "x/4", _dumpWord, CLIDVParse, "Examine words at a specified offset" }, + { "b", _setBreakpoint, "Is", "Set a breakpoint" }, + { "break", _setBreakpoint, "Is", "Set a breakpoint" }, + { "c", _continue, "", "Continue execution" }, + { "continue", _continue, "", "Continue execution" }, + { "d", _clearBreakpoint, "I", "Delete a breakpoint" }, + { "delete", _clearBreakpoint, "I", "Delete a breakpoint" }, + { "dis", _disassemble, "Ii", "Disassemble instructions" }, + { "disasm", _disassemble, "Ii", "Disassemble instructions" }, + { "disassemble", _disassemble, "Ii", "Disassemble instructions" }, + { "h", _printHelp, "S", "Print help" }, + { "help", _printHelp, "S", "Print help" }, + { "i", _printStatus, "", "Print the current status" }, + { "info", _printStatus, "", "Print the current status" }, + { "n", _next, "", "Execute next instruction" }, + { "next", _next, "", "Execute next instruction" }, + { "p", _print, "I", "Print a value" }, + { "p/t", _printBin, "I", "Print a value as binary" }, + { "p/x", _printHex, "I", "Print a value as hexadecimal" }, + { "print", _print, "I", "Print a value" }, + { "print/t", _printBin, "I", "Print a value as binary" }, + { "print/x", _printHex, "I", "Print a value as hexadecimal" }, + { "q", _quit, "", "Quit the emulator" }, + { "quit", _quit, "", "Quit the emulator" }, + { "reset", _reset, "", "Reset the emulation" }, + { "r/1", _readByte, "I", "Read a byte from a specified offset" }, + { "r/2", _readHalfword, "I", "Read a halfword from a specified offset" }, + { "r/4", _readWord, "I", "Read a word from a specified offset" }, + { "status", _printStatus, "", "Print the current status" }, + { "trace", _trace, "I", "Trace a fixed number of instructions" }, + { "w", _setWatchpoint, "Is", "Set a watchpoint" }, + { "w/1", _writeByte, "II", "Write a byte at a specified offset" }, + { "w/2", _writeHalfword, "II", "Write a halfword at a specified offset" }, + { "w/r", _writeRegister, "SI", "Write a register" }, + { "w/4", _writeWord, "II", "Write a word at a specified offset" }, + { "watch", _setWatchpoint, "Is", "Set a watchpoint" }, + { "watch/r", _setReadWatchpoint, "Is", "Set a read watchpoint" }, + { "watch/w", _setWriteWatchpoint, "Is", "Set a write watchpoint" }, + { "x/1", _dumpByte, "Ii", "Examine bytes at a specified offset" }, + { "x/2", _dumpHalfword, "Ii", "Examine halfwords at a specified offset" }, + { "x/4", _dumpWord, "Ii", "Examine words at a specified offset" }, #ifdef ENABLE_SCRIPTING - { "source", _source, CLIDVStringParse, "Load a script" }, + { "source", _source, "S", "Load a script" }, #endif #if !defined(NDEBUG) && !defined(_WIN32) - { "!", _breakInto, 0, "Break into attached debugger (for developers)" }, + { "!", _breakInto, "", "Break into attached debugger (for developers)" }, #endif { 0, 0, 0, 0 } }; @@ -273,12 +276,12 @@ static void _readWord(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { } static void _writeByte(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { - if (!dv || dv->type != CLIDV_INT_TYPE) { + if (!dv || !dv->next) { debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS); return; } - if (!dv->next || dv->next->type != CLIDV_INT_TYPE) { - debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS); + if (dv->type != CLIDV_INT_TYPE || dv->next->type != CLIDV_INT_TYPE) { + debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS); return; } uint32_t address = dv->intValue; @@ -295,12 +298,12 @@ static void _writeByte(struct CLIDebugger* debugger, struct CLIDebugVector* dv) } static void _writeHalfword(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { - if (!dv || dv->type != CLIDV_INT_TYPE) { + if (!dv || !dv->next) { debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS); return; } - if (!dv->next || dv->next->type != CLIDV_INT_TYPE) { - debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS); + if (dv->type != CLIDV_INT_TYPE || dv->next->type != CLIDV_INT_TYPE) { + debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS); return; } uint32_t address = dv->intValue; @@ -316,15 +319,29 @@ static void _writeHalfword(struct CLIDebugger* debugger, struct CLIDebugVector* } } -static void _writeWord(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { - if (!dv || dv->type != CLIDV_INT_TYPE) { +static void _writeRegister(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { + if (!dv || !dv->next) { debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS); return; } - if (!dv->next || dv->next->type != CLIDV_INT_TYPE) { + if (dv->type != CLIDV_CHAR_TYPE || dv->next->type != CLIDV_INT_TYPE) { + debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS); + return; + } + if (!debugger->d.platform->setRegister(debugger->d.platform, dv->charValue, dv->next->intValue)) { + debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS); + } +} + +static void _writeWord(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { + if (!dv || !dv->next) { debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS); return; } + if (dv->type != CLIDV_INT_TYPE || dv->next->type != CLIDV_INT_TYPE) { + debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS); + return; + } uint32_t address = dv->intValue; uint32_t value = dv->next->intValue; if (dv->segmentValue >= 0) { @@ -435,13 +452,53 @@ static void _source(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { } #endif +static struct ParseTree* _parseTree(const char* string) { + struct LexVector lv; + bool error = false; + LexVectorInit(&lv, 0); + size_t length = strlen(string); + size_t adjusted = lexExpression(&lv, string, length, NULL); + struct ParseTree* tree = malloc(sizeof(*tree)); + if (!adjusted) { + error = true; + } else { + parseLexedExpression(tree, &lv); + + if (adjusted > length) { + error = true; + } else { + length -= adjusted; + string += adjusted; + } + } + lexFree(&lv); + LexVectorClear(&lv); + LexVectorDeinit(&lv); + if (error) { + parseFree(tree); + free(tree); + return NULL; + } else { + return tree; + } +} + static void _setBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { if (!dv || dv->type != CLIDV_INT_TYPE) { debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS); return; } uint32_t address = dv->intValue; - debugger->d.platform->setBreakpoint(debugger->d.platform, address, dv->segmentValue); + if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) { + struct ParseTree* tree = _parseTree(dv->next->charValue); + if (tree) { + debugger->d.platform->setConditionalBreakpoint(debugger->d.platform, address, dv->segmentValue, tree); + } else { + debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS); + } + } else { + debugger->d.platform->setBreakpoint(debugger->d.platform, address, dv->segmentValue); + } } static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { @@ -454,7 +511,16 @@ static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* return; } uint32_t address = dv->intValue; - debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_RW); + if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) { + struct ParseTree* tree = _parseTree(dv->next->charValue); + if (tree) { + debugger->d.platform->setConditionalWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_RW, tree); + } else { + debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS); + } + } else { + debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_RW); + } } static void _setReadWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { @@ -467,7 +533,16 @@ static void _setReadWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVect return; } uint32_t address = dv->intValue; - debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_READ); + if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) { + struct ParseTree* tree = _parseTree(dv->next->charValue); + if (tree) { + debugger->d.platform->setConditionalWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_READ, tree); + } else { + debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS); + } + } else { + debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_READ); + } } static void _setWriteWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { @@ -480,8 +555,16 @@ static void _setWriteWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVec return; } uint32_t address = dv->intValue; - debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_WRITE); -} + if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) { + struct ParseTree* tree = _parseTree(dv->next->charValue); + if (tree) { + debugger->d.platform->setConditionalWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_WRITE, tree); + } else { + debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS); + } + } else { + debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_WRITE); + }} static void _clearBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { if (!dv || dv->type != CLIDV_INT_TYPE) { @@ -521,86 +604,6 @@ static void _printStatus(struct CLIDebugger* debugger, struct CLIDebugVector* dv debugger->system->printStatus(debugger->system); } -static uint32_t _performOperation(enum Operation operation, uint32_t current, uint32_t next, struct CLIDebugVector* dv) { - switch (operation) { - case OP_ASSIGN: - current = next; - break; - case OP_ADD: - current += next; - break; - case OP_SUBTRACT: - current -= next; - break; - case OP_MULTIPLY: - current *= next; - break; - case OP_DIVIDE: - if (next != 0) { - current /= next; - } else { - dv->type = CLIDV_ERROR_TYPE; - return 0; - } - break; - } - return current; -} - -static void _lookupIdentifier(struct mDebugger* debugger, const char* name, struct CLIDebugVector* dv) { - struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger; - if (cliDebugger->system) { - uint32_t value; -#ifdef ENABLE_SCRIPTING - if (debugger->bridge && mScriptBridgeLookupSymbol(debugger->bridge, name, &dv->intValue)) { - return; - } -#endif - if (debugger->core->symbolTable && mDebuggerSymbolLookup(debugger->core->symbolTable, name, &dv->intValue, &dv->segmentValue)) { - return; - } - value = cliDebugger->system->lookupPlatformIdentifier(cliDebugger->system, name, dv); - if (dv->type != CLIDV_ERROR_TYPE) { - dv->intValue = value; - return; - } - dv->type = CLIDV_INT_TYPE; - value = cliDebugger->system->lookupIdentifier(cliDebugger->system, name, dv); - if (dv->type != CLIDV_ERROR_TYPE) { - dv->intValue = value; - return; - } - } - dv->type = CLIDV_ERROR_TYPE; -} - -static void _evaluateParseTree(struct mDebugger* debugger, struct ParseTree* tree, struct CLIDebugVector* dv) { - int32_t lhs, rhs; - switch (tree->token.type) { - case TOKEN_UINT_TYPE: - dv->intValue = tree->token.uintValue; - break; - case TOKEN_SEGMENT_TYPE: - _evaluateParseTree(debugger, tree->lhs, dv); - dv->segmentValue = dv->intValue; - _evaluateParseTree(debugger, tree->rhs, dv); - break; - case TOKEN_OPERATOR_TYPE: - _evaluateParseTree(debugger, tree->lhs, dv); - lhs = dv->intValue; - _evaluateParseTree(debugger, tree->rhs, dv); - rhs = dv->intValue; - dv->intValue = _performOperation(tree->token.operatorValue, lhs, rhs, dv); - break; - case TOKEN_IDENTIFIER_TYPE: - _lookupIdentifier(debugger, tree->token.identifierValue, dv); - break; - case TOKEN_ERROR_TYPE: - default: - dv->type = CLIDV_ERROR_TYPE; - } -} - struct CLIDebugVector* CLIDVParse(struct CLIDebugger* debugger, const char* string, size_t length) { if (!string || length < 1) { return 0; @@ -608,11 +611,11 @@ struct CLIDebugVector* CLIDVParse(struct CLIDebugger* debugger, const char* stri struct CLIDebugVector dvTemp = { .type = CLIDV_INT_TYPE, .segmentValue = -1 }; - struct LexVector lv = { .next = 0 }; - size_t adjusted = lexExpression(&lv, string, length); + struct LexVector lv; + LexVectorInit(&lv, 0); + size_t adjusted = lexExpression(&lv, string, length, " "); if (adjusted > length) { dvTemp.type = CLIDV_ERROR_TYPE; - lexFree(lv.next); } struct ParseTree tree; @@ -620,14 +623,15 @@ struct CLIDebugVector* CLIDVParse(struct CLIDebugger* debugger, const char* stri if (tree.token.type == TOKEN_ERROR_TYPE) { dvTemp.type = CLIDV_ERROR_TYPE; } else { - _evaluateParseTree(&debugger->d, &tree, &dvTemp); + if (!mDebuggerEvaluateParseTree(&debugger->d, &tree, &dvTemp.intValue, &dvTemp.segmentValue)) { + dvTemp.type = CLIDV_ERROR_TYPE; + } } - parseFree(tree.lhs); - parseFree(tree.rhs); + parseFree(&tree); - length -= adjusted; - string += adjusted; + lexFree(&lv); + LexVectorDeinit(&lv); struct CLIDebugVector* dv = malloc(sizeof(struct CLIDebugVector)); if (dvTemp.type == CLIDV_ERROR_TYPE) { @@ -635,43 +639,22 @@ struct CLIDebugVector* CLIDVParse(struct CLIDebugger* debugger, const char* stri dv->next = 0; } else { *dv = dvTemp; - if (string[0] == ' ') { - dv->next = CLIDVParse(debugger, string + 1, length - 1); - if (dv->next && dv->next->type == CLIDV_ERROR_TYPE) { - dv->type = CLIDV_ERROR_TYPE; - } - } } return dv; } struct CLIDebugVector* CLIDVStringParse(struct CLIDebugger* debugger, const char* string, size_t length) { + UNUSED(debugger); if (!string || length < 1) { return 0; } struct CLIDebugVector dvTemp = { .type = CLIDV_CHAR_TYPE }; - size_t adjusted; - const char* next = strchr(string, ' '); - if (next) { - adjusted = next - string; - } else { - adjusted = length; - } - dvTemp.charValue = strndup(string, adjusted); - - length -= adjusted; - string += adjusted; + dvTemp.charValue = strndup(string, length); struct CLIDebugVector* dv = malloc(sizeof(struct CLIDebugVector)); *dv = dvTemp; - if (string[0] == ' ') { - dv->next = CLIDVStringParse(debugger, string + 1, length - 1); - if (dv->next && dv->next->type == CLIDV_ERROR_TYPE) { - dv->type = CLIDV_ERROR_TYPE; - } - } return dv; } @@ -687,8 +670,28 @@ static void _DVFree(struct CLIDebugVector* dv) { } } +static struct CLIDebugVector* _parseArg(struct CLIDebugger* debugger, const char* args, size_t argsLen, char type) { + struct CLIDebugVector* dv = NULL; + switch (type) { + case 'I': + case 'i': + return CLIDVParse(debugger, args, argsLen); + case 'S': + case 's': + return CLIDVStringParse(debugger, args, argsLen); + case '*': + dv = _parseArg(debugger, args, argsLen, 'I'); + if (!dv) { + dv = _parseArg(debugger, args, argsLen, 'S'); + } + break; + } + return dv; +} + static int _tryCommands(struct CLIDebugger* debugger, struct CLIDebuggerCommandSummary* commands, const char* command, size_t commandLen, const char* args, size_t argsLen) { - struct CLIDebugVector* dv = 0; + struct CLIDebugVector* dv = NULL; + struct CLIDebugVector* dvLast = NULL; int i; const char* name; for (i = 0; (name = commands[i].name); ++i) { @@ -696,22 +699,78 @@ static int _tryCommands(struct CLIDebugger* debugger, struct CLIDebuggerCommandS continue; } if (strncasecmp(name, command, commandLen) == 0) { - if (commands[i].parser) { - if (args) { - dv = commands[i].parser(debugger, args, argsLen); - if (dv && dv->type == CLIDV_ERROR_TYPE) { + if (commands[i].format && args) { + char lastArg = '\0'; + int arg; + for (arg = 0; commands[i].format[arg] && argsLen; ++arg) { + while (isspace(args[0]) && argsLen) { + ++args; + --argsLen; + } + if (!args[0] || !argsLen) { + debugger->backend->printf(debugger->backend, "Wrong number of arguments\n"); + _DVFree(dv); + return 0; + } + + size_t adjusted; + const char* next = strchr(args, ' '); + if (next) { + adjusted = next - args; + } else { + adjusted = argsLen; + } + + struct CLIDebugVector* dvNext = NULL; + bool nextArgMandatory = false; + + if (commands[i].format[arg] == '+') { + dvNext = _parseArg(debugger, args, adjusted, lastArg); + --args; + } else { + nextArgMandatory = isupper(commands[i].format[arg]) || (commands[i].format[arg] == '*'); + dvNext = _parseArg(debugger, args, adjusted, commands[i].format[arg]); + } + + args += adjusted; + argsLen -= adjusted; + + if (!dvNext) { + if (!nextArgMandatory) { + args = NULL; + } + break; + } + if (dvNext->type == CLIDV_ERROR_TYPE) { debugger->backend->printf(debugger->backend, "Parse error\n"); _DVFree(dv); - return false; + return 0; + } + + if (dvLast) { + dvLast->next = dvNext; + dvLast = dvNext; + } else { + dv = dvNext; + dvLast = dv; } } - } else if (args) { + } + + if (args) { + while (isspace(args[0]) && argsLen) { + ++args; + --argsLen; + } + } + if (args && argsLen) { debugger->backend->printf(debugger->backend, "Wrong number of arguments\n"); - return false; + _DVFree(dv); + return 0; } commands[i].command(debugger, dv); _DVFree(dv); - return true; + return 1; } } return -1; @@ -781,10 +840,10 @@ static void _reportEntry(struct mDebugger* debugger, enum mDebuggerEntryReason r break; case DEBUGGER_ENTER_WATCHPOINT: if (info) { - if (info->accessType & WATCHPOINT_WRITE) { - cliDebugger->backend->printf(cliDebugger->backend, "Hit watchpoint at 0x%08X: (new value = 0x%08x, old value = 0x%08X)\n", info->address, info->newValue, info->oldValue); + if (info->type.wp.accessType & WATCHPOINT_WRITE) { + cliDebugger->backend->printf(cliDebugger->backend, "Hit watchpoint at 0x%08X: (new value = 0x%08x, old value = 0x%08X)\n", info->address, info->type.wp.newValue, info->type.wp.oldValue); } else { - cliDebugger->backend->printf(cliDebugger->backend, "Hit watchpoint at 0x%08X: (value = 0x%08x)\n", info->address, info->oldValue); + cliDebugger->backend->printf(cliDebugger->backend, "Hit watchpoint at 0x%08X: (value = 0x%08x)\n", info->address, info->type.wp.oldValue); } } else { cliDebugger->backend->printf(cliDebugger->backend, "Hit watchpoint\n"); @@ -792,7 +851,7 @@ static void _reportEntry(struct mDebugger* debugger, enum mDebuggerEntryReason r break; case DEBUGGER_ENTER_ILLEGAL_OP: if (info) { - cliDebugger->backend->printf(cliDebugger->backend, "Hit illegal opcode at 0x%08X: 0x%08X\n", info->address, info->opcode); + cliDebugger->backend->printf(cliDebugger->backend, "Hit illegal opcode at 0x%08X: 0x%08X\n", info->address, info->type.bp.opcode); } else { cliDebugger->backend->printf(cliDebugger->backend, "Hit illegal opcode\n"); } diff --git a/src/debugger/debugger.c b/src/debugger/debugger.c index 79b06076a..14e4d101c 100644 --- a/src/debugger/debugger.c +++ b/src/debugger/debugger.c @@ -8,6 +8,7 @@ #include #include +#include #ifdef USE_GDB_STUB #include @@ -137,3 +138,22 @@ static void mDebuggerDeinit(struct mCPUComponent* component) { } debugger->platform->deinit(debugger->platform); } + +bool mDebuggerLookupIdentifier(struct mDebugger* debugger, const char* name, int32_t* value, int* segment) { + *segment = -1; +#ifdef ENABLE_SCRIPTING + if (debugger->bridge && mScriptBridgeLookupSymbol(debugger->bridge, name, value)) { + return true; + } +#endif + if (debugger->core->symbolTable && mDebuggerSymbolLookup(debugger->core->symbolTable, name, value, segment)) { + return true; + } + if (debugger->core->lookupIdentifier(debugger->core, name, value, segment)) { + return true; + } + if (debugger->platform && debugger->platform->getRegister(debugger->platform, name, value)) { + return true; + } + return false; +} diff --git a/src/debugger/gdb-stub.c b/src/debugger/gdb-stub.c index 6e2d287fa..73dd978d6 100644 --- a/src/debugger/gdb-stub.c +++ b/src/debugger/gdb-stub.c @@ -46,7 +46,7 @@ static void _gdbStubEntered(struct mDebugger* debugger, enum mDebuggerEntryReaso break; case DEBUGGER_ENTER_BREAKPOINT: if (stub->supportsHwbreak && stub->supportsSwbreak && info) { - snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "T%02x%cwbreak:;", SIGTRAP, info->breakType == BREAKPOINT_SOFTWARE ? 's' : 'h'); + snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "T%02x%cwbreak:;", SIGTRAP, info->type.bp.breakType == BREAKPOINT_SOFTWARE ? 's' : 'h'); } else { snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02xk", SIGTRAP); } @@ -54,9 +54,9 @@ static void _gdbStubEntered(struct mDebugger* debugger, enum mDebuggerEntryReaso case DEBUGGER_ENTER_WATCHPOINT: if (info) { const char* type = 0; - switch (info->watchType) { + switch (info->type.wp.watchType) { case WATCHPOINT_WRITE: - if (info->newValue == info->oldValue) { + if (info->type.wp.newValue == info->type.wp.oldValue) { if (stub->d.state == DEBUGGER_PAUSED) { stub->d.state = DEBUGGER_RUNNING; } @@ -363,7 +363,7 @@ static void _writeRegister(struct GDBStub* stub, const char* message) { #ifdef _MSC_VER value = _byteswap_ulong(value); #else - value = __builtin_bswap32(value); + LOAD_32BE(value, 0, &value); #endif if (reg <= ARM_PC) { diff --git a/src/debugger/parser.c b/src/debugger/parser.c index a755af505..41eafc6d4 100644 --- a/src/debugger/parser.c +++ b/src/debugger/parser.c @@ -5,39 +5,181 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#include #include -static struct LexVector* _lexOperator(struct LexVector* lv, char operator) { - struct LexVector* lvNext = malloc(sizeof(struct LexVector)); - lvNext->token.type = TOKEN_OPERATOR_TYPE; +DEFINE_VECTOR(LexVector, struct Token); + +enum LexState { + LEX_ERROR = -1, + LEX_ROOT = 0, + LEX_EXPECT_IDENTIFIER, + LEX_EXPECT_BINARY_FIRST, + LEX_EXPECT_BINARY, + LEX_EXPECT_DECIMAL, + LEX_EXPECT_HEX_FIRST, + LEX_EXPECT_HEX, + LEX_EXPECT_PREFIX, + LEX_EXPECT_OPERATOR, + LEX_EXPECT_OPERATOR2, +}; + +static void _lexOperator(struct LexVector* lv, char operator, enum LexState* state) { + if (*state == LEX_EXPECT_OPERATOR2) { + struct Token* lvNext = LexVectorGetPointer(lv, LexVectorSize(lv) - 1); + if (lvNext->type != TOKEN_OPERATOR_TYPE) { + lvNext->type = TOKEN_ERROR_TYPE; + *state = LEX_ERROR; + return; + } + switch (lvNext->operatorValue) { + case OP_AND: + if (operator == '&') { + lvNext->operatorValue = OP_LOGICAL_AND; + *state = LEX_ROOT; + return; + } + break; + case OP_OR: + if (operator == '|') { + lvNext->operatorValue = OP_LOGICAL_OR; + *state = LEX_ROOT; + return; + } + break; + case OP_LESS: + if (operator == '=') { + lvNext->operatorValue = OP_LE; + *state = LEX_ROOT; + return; + } + if (operator == '<') { + lvNext->operatorValue = OP_SHIFT_L; + *state = LEX_ROOT; + return; + } + break; + case OP_GREATER: + if (operator == '=') { + lvNext->operatorValue = OP_GE; + *state = LEX_ROOT; + return; + } + if (operator == '>') { + lvNext->operatorValue = OP_SHIFT_R; + *state = LEX_ROOT; + return; + } + break; + case OP_ASSIGN: + if (operator == '=') { + lvNext->operatorValue = OP_EQUAL; + *state = LEX_ROOT; + return; + } + break; + case OP_NOT: + if (operator == '=') { + lvNext->operatorValue = OP_NOT_EQUAL; + *state = LEX_ROOT; + return; + } + break; + default: + break; + } + *state = LEX_ERROR; + return; + } + struct Token* lvNext = LexVectorAppend(lv); + lvNext->type = TOKEN_OPERATOR_TYPE; + *state = LEX_EXPECT_OPERATOR2; switch (operator) { + case '=': + lvNext->operatorValue = OP_ASSIGN; + break; case '+': - lvNext->token.operatorValue = OP_ADD; + lvNext->operatorValue = OP_ADD; break; case '-': - lvNext->token.operatorValue = OP_SUBTRACT; + lvNext->operatorValue = OP_SUBTRACT; break; case '*': - lvNext->token.operatorValue = OP_MULTIPLY; + lvNext->operatorValue = OP_MULTIPLY; break; case '/': - lvNext->token.operatorValue = OP_DIVIDE; + lvNext->operatorValue = OP_DIVIDE; + break; + case '%': + lvNext->operatorValue = OP_MODULO; + break; + case '&': + lvNext->operatorValue = OP_AND; + break; + case '|': + lvNext->operatorValue = OP_OR; + break; + case '^': + lvNext->operatorValue = OP_XOR; + break; + case '<': + lvNext->operatorValue = OP_LESS; + break; + case '>': + lvNext->operatorValue = OP_GREATER; + break; + case '!': + lvNext->operatorValue = OP_NOT; break; default: - lvNext->token.type = TOKEN_ERROR_TYPE; + lvNext->type = TOKEN_ERROR_TYPE; break; } - lvNext->next = lv->next; - lv->next = lvNext; - lv = lvNext; - lvNext = malloc(sizeof(struct LexVector)); - lvNext->next = lv->next; - lvNext->token.type = TOKEN_ERROR_TYPE; - lv->next = lvNext; - return lvNext; } -size_t lexExpression(struct LexVector* lv, const char* string, size_t length) { +static void _lexValue(struct LexVector* lv, char token, uint32_t next, enum LexState* state) { + struct Token* lvNext; + + switch (token) { + case '=': + case '+': + case '-': + case '*': + case '/': + case '%': + case '&': + case '|': + case '^': + case '<': + case '>': + case '!': + lvNext = LexVectorAppend(lv); + lvNext->type = TOKEN_UINT_TYPE; + lvNext->uintValue = next; + _lexOperator(lv, token, state); + break; + case ')': + lvNext = LexVectorAppend(lv); + lvNext->type = TOKEN_UINT_TYPE; + lvNext->uintValue = next; + lvNext = LexVectorAppend(lv); + lvNext->type = TOKEN_CLOSE_PAREN_TYPE; + *state = LEX_EXPECT_OPERATOR; + break; + case ' ': + case '\t': + lvNext = LexVectorAppend(lv); + lvNext->type = TOKEN_UINT_TYPE; + lvNext->uintValue = next; + *state = LEX_EXPECT_OPERATOR; + break; + default: + *state = LEX_ERROR; + break; + } +} + +size_t lexExpression(struct LexVector* lv, const char* string, size_t length, const char* eol) { if (!string || length < 1) { return 0; } @@ -47,14 +189,32 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length) { enum LexState state = LEX_ROOT; const char* tokenStart = 0; - struct LexVector* lvNext; + struct Token* lvNext; - while (length > 0 && string[0] && string[0] != ' ' && state != LEX_ERROR) { + if (!eol) { + eol = " \r\n"; + } + + while (length > 0 && string[0] && !strchr(eol, string[0]) && state != LEX_ERROR) { char token = string[0]; ++string; ++adjusted; --length; switch (state) { + case LEX_EXPECT_OPERATOR2: + switch (token) { + case '&': + case '|': + case '=': + case '<': + case '>': + _lexOperator(lv, token, &state); + break; + } + if (state != LEX_EXPECT_OPERATOR2) { + break; + } + // Fall through case LEX_ROOT: tokenStart = string - 1; switch (token) { @@ -75,17 +235,20 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length) { next = 0; break; case '$': - state = LEX_EXPECT_HEX; + state = LEX_EXPECT_HEX_FIRST; + next = 0; + break; + case '%': + state = LEX_EXPECT_BINARY_FIRST; next = 0; break; case '(': state = LEX_ROOT; - lv->token.type = TOKEN_OPEN_PAREN_TYPE; - lvNext = malloc(sizeof(struct LexVector)); - lvNext->next = lv->next; - lvNext->token.type = TOKEN_ERROR_TYPE; - lv->next = lvNext; - lv = lvNext; + lvNext = LexVectorAppend(lv); + lvNext->type = TOKEN_OPEN_PAREN_TYPE; + break; + case ' ': + case '\t': break; default: if (tolower(token) >= 'a' && tolower(token <= 'z')) { @@ -98,24 +261,45 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length) { break; case LEX_EXPECT_IDENTIFIER: switch (token) { + case '=': case '+': case '-': case '*': case '/': - lv->token.type = TOKEN_IDENTIFIER_TYPE; - lv->token.identifierValue = strndup(tokenStart, string - tokenStart - 1); - lv = _lexOperator(lv, token); - state = LEX_ROOT; + case '%': + case '&': + case '|': + case '^': + case '<': + case '>': + case '!': + lvNext = LexVectorAppend(lv); + lvNext->type = TOKEN_IDENTIFIER_TYPE; + lvNext->identifierValue = strndup(tokenStart, string - tokenStart - 1); + _lexOperator(lv, token, &state); break; case ')': - lv->token.type = TOKEN_IDENTIFIER_TYPE; - lv->token.identifierValue = strndup(tokenStart, string - tokenStart - 1); + lvNext = LexVectorAppend(lv); + lvNext->type = TOKEN_IDENTIFIER_TYPE; + lvNext->identifierValue = strndup(tokenStart, string - tokenStart - 1); + lvNext = LexVectorAppend(lv); + lvNext->type = TOKEN_CLOSE_PAREN_TYPE; + state = LEX_EXPECT_OPERATOR; + break; + case ' ': + case '\t': + lvNext = LexVectorAppend(lv); + lvNext->type = TOKEN_IDENTIFIER_TYPE; + lvNext->identifierValue = strndup(tokenStart, string - tokenStart - 1); state = LEX_EXPECT_OPERATOR; break; default: break; } break; + case LEX_EXPECT_BINARY_FIRST: + state = LEX_EXPECT_BINARY; + // Fall through case LEX_EXPECT_BINARY: switch (token) { case '0': @@ -124,22 +308,8 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length) { next <<= 1; next += token - '0'; break; - case '+': - case '-': - case '*': - case '/': - lv->token.type = TOKEN_UINT_TYPE; - lv->token.uintValue = next; - lv = _lexOperator(lv, token); - state = LEX_ROOT; - break; - case ')': - lv->token.type = TOKEN_UINT_TYPE; - lv->token.uintValue = next; - state = LEX_EXPECT_OPERATOR; - break; default: - state = LEX_ERROR; + _lexValue(lv, token, next, &state); break; } break; @@ -159,24 +329,14 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length) { next *= 10; next += token - '0'; break; - case '+': - case '-': - case '*': - case '/': - lv->token.type = TOKEN_UINT_TYPE; - lv->token.uintValue = next; - lv = _lexOperator(lv, token); - state = LEX_ROOT; - break; - case ')': - lv->token.type = TOKEN_UINT_TYPE; - lv->token.uintValue = next; - state = LEX_EXPECT_OPERATOR; - break; default: - state = LEX_ERROR; + _lexValue(lv, token, next, &state); + break; } break; + case LEX_EXPECT_HEX_FIRST: + state = LEX_EXPECT_HEX; + // Fall through case LEX_EXPECT_HEX: switch (token) { case '0': @@ -213,33 +373,14 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length) { next *= 16; next += token - 'a' + 10; break; - case '+': - case '-': - case '*': - case '/': - lv->token.type = TOKEN_UINT_TYPE; - lv->token.uintValue = next; - lv = _lexOperator(lv, token); - state = LEX_ROOT; - break; case ':': - lv->token.type = TOKEN_SEGMENT_TYPE; - lv->token.uintValue = next; - lvNext = malloc(sizeof(struct LexVector)); - lvNext->next = lv->next; - lvNext->token.type = TOKEN_UINT_TYPE; - lv->next = lvNext; - lv = lvNext; + lvNext = LexVectorAppend(lv); + lvNext->type = TOKEN_SEGMENT_TYPE; + lvNext->uintValue = next; next = 0; - state = LEX_EXPECT_HEX; - break; - case ')': - lv->token.type = TOKEN_UINT_TYPE; - lv->token.uintValue = next; - state = LEX_EXPECT_OPERATOR; break; default: - state = LEX_ERROR; + _lexValue(lv, token, next, &state); break; } break; @@ -248,26 +389,12 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length) { case 'X': case 'x': next = 0; - state = LEX_EXPECT_HEX; + state = LEX_EXPECT_HEX_FIRST; break; case 'B': case 'b': next = 0; - state = LEX_EXPECT_BINARY; - break; - case '+': - case '-': - case '*': - case '/': - lv->token.type = TOKEN_UINT_TYPE; - lv->token.uintValue = next; - lv = _lexOperator(lv, token); - state = LEX_ROOT; - break; - case ')': - lv->token.type = TOKEN_UINT_TYPE; - lv->token.uintValue = next; - state = LEX_EXPECT_OPERATOR; + state = LEX_EXPECT_BINARY_FIRST; break; case '0': case '1': @@ -283,21 +410,32 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length) { state = LEX_EXPECT_DECIMAL; break; default: - state = LEX_ERROR; + _lexValue(lv, token, next, &state); + break; } break; case LEX_EXPECT_OPERATOR: switch (token) { + case '=': case '+': case '-': case '*': case '/': - lvNext = malloc(sizeof(struct LexVector)); - lvNext->next = lv->next; - lvNext->token.type = TOKEN_CLOSE_PAREN_TYPE; - lv->next = lvNext; - lv = _lexOperator(lv->next, token); - state = LEX_ROOT; + case '%': + case '&': + case '|': + case '^': + case '<': + case '>': + case '!': + _lexOperator(lv, token, &state); + break; + case ')': + lvNext = LexVectorAppend(lv); + lvNext->type = TOKEN_CLOSE_PAREN_TYPE; + break; + case ' ': + case '\t': break; default: state = LEX_ERROR; @@ -314,33 +452,53 @@ size_t lexExpression(struct LexVector* lv, const char* string, size_t length) { case LEX_EXPECT_DECIMAL: case LEX_EXPECT_HEX: case LEX_EXPECT_PREFIX: - lv->token.type = TOKEN_UINT_TYPE; - lv->token.uintValue = next; + lvNext = LexVectorAppend(lv); + lvNext->type = TOKEN_UINT_TYPE; + lvNext->uintValue = next; break; case LEX_EXPECT_IDENTIFIER: - lv->token.type = TOKEN_IDENTIFIER_TYPE; - lv->token.identifierValue = strndup(tokenStart, string - tokenStart); + lvNext = LexVectorAppend(lv); + lvNext->type = TOKEN_IDENTIFIER_TYPE; + lvNext->identifierValue = strndup(tokenStart, string - tokenStart); break; + case LEX_ROOT: case LEX_EXPECT_OPERATOR: - lvNext = malloc(sizeof(struct LexVector)); - lvNext->next = lv->next; - lvNext->token.type = TOKEN_CLOSE_PAREN_TYPE; - lv->next = lvNext; + case LEX_EXPECT_OPERATOR2: break; + case LEX_EXPECT_BINARY_FIRST: + case LEX_EXPECT_HEX_FIRST: case LEX_ERROR: default: - lv->token.type = TOKEN_ERROR_TYPE; + lvNext = LexVectorAppend(lv); + lvNext->type = TOKEN_ERROR_TYPE; break; } return adjusted; } static const int _operatorPrecedence[] = { - 2, - 1, - 1, - 0, - 0 + [OP_ASSIGN] = 14, + [OP_ADD] = 4, + [OP_SUBTRACT] = 4, + [OP_MULTIPLY] = 3, + [OP_DIVIDE] = 3, + [OP_MODULO] = 3, + [OP_AND] = 8, + [OP_OR] = 10, + [OP_XOR] = 9, + [OP_LESS] = 6, + [OP_GREATER] = 6, + [OP_EQUAL] = 7, + [OP_NOT_EQUAL] = 7, + [OP_LE] = 6, + [OP_GE] = 6, + [OP_LOGICAL_AND] = 11, + [OP_LOGICAL_OR] = 12, + [OP_NEGATE] = 2, + [OP_FLIP] = 2, + [OP_NOT] = 2, + [OP_SHIFT_L] = 5, + [OP_SHIFT_R] = 5, }; static struct ParseTree* _parseTreeCreate() { @@ -351,65 +509,69 @@ static struct ParseTree* _parseTreeCreate() { return tree; } -static struct LexVector* _parseExpression(struct ParseTree* tree, struct LexVector* lv, int precedence, int openParens) { +static size_t _parseExpression(struct ParseTree* tree, struct LexVector* lv, size_t i, int precedence, int* openParens) { struct ParseTree* newTree = 0; - while (lv) { + while (i < LexVectorSize(lv)) { + struct Token* token = LexVectorGetPointer(lv, i); int newPrecedence; - switch (lv->token.type) { + switch (token->type) { case TOKEN_IDENTIFIER_TYPE: case TOKEN_UINT_TYPE: if (tree->token.type == TOKEN_ERROR_TYPE) { - tree->token = lv->token; - lv = lv->next; + tree->token = *token; + if (token->type == TOKEN_IDENTIFIER_TYPE) { + tree->token.identifierValue = strdup(token->identifierValue); + } + ++i; } else { tree->token.type = TOKEN_ERROR_TYPE; - return 0; + return i + 1; } break; case TOKEN_SEGMENT_TYPE: tree->lhs = _parseTreeCreate(); tree->lhs->token.type = TOKEN_UINT_TYPE; - tree->lhs->token.uintValue = lv->token.uintValue; + tree->lhs->token.uintValue = token->uintValue; tree->rhs = _parseTreeCreate(); tree->token.type = TOKEN_SEGMENT_TYPE; - lv = _parseExpression(tree->rhs, lv->next, precedence, openParens); + i = _parseExpression(tree->rhs, lv, i + 1, precedence, openParens); if (tree->token.type == TOKEN_ERROR_TYPE) { tree->token.type = TOKEN_ERROR_TYPE; } break; case TOKEN_OPEN_PAREN_TYPE: - lv = _parseExpression(tree, lv->next, INT_MAX, openParens + 1); + ++*openParens; + i = _parseExpression(tree, lv, i + 1, INT_MAX, openParens); break; case TOKEN_CLOSE_PAREN_TYPE: - if (openParens <= 0) { + if (*openParens <= 0) { tree->token.type = TOKEN_ERROR_TYPE; - return 0; } - return lv->next; - break; + --*openParens; + return i + 1; case TOKEN_OPERATOR_TYPE: - newPrecedence = _operatorPrecedence[lv->token.operatorValue]; + newPrecedence = _operatorPrecedence[token->operatorValue]; if (newPrecedence < precedence) { newTree = _parseTreeCreate(); *newTree = *tree; tree->lhs = newTree; tree->rhs = _parseTreeCreate(); - tree->token = lv->token; - lv = _parseExpression(tree->rhs, lv->next, newPrecedence, openParens); + tree->token = *token; + i = _parseExpression(tree->rhs, lv, i + 1, newPrecedence, openParens); if (tree->token.type == TOKEN_ERROR_TYPE) { tree->token.type = TOKEN_ERROR_TYPE; } } else { - return lv; + return i; } break; case TOKEN_ERROR_TYPE: tree->token.type = TOKEN_ERROR_TYPE; - return 0; + return i + 1; } } - return 0; + return i; } void parseLexedExpression(struct ParseTree* tree, struct LexVector* lv) { @@ -421,14 +583,23 @@ void parseLexedExpression(struct ParseTree* tree, struct LexVector* lv) { tree->lhs = 0; tree->rhs = 0; - _parseExpression(tree, lv, _operatorPrecedence[OP_ASSIGN], 0); + int openParens = 0; + _parseExpression(tree, lv, 0, INT_MAX, &openParens); + if (openParens) { + if (tree->token.type == TOKEN_IDENTIFIER_TYPE) { + free(tree->token.identifierValue); + } + tree->token.type = TOKEN_ERROR_TYPE; + } } void lexFree(struct LexVector* lv) { - while (lv) { - struct LexVector* lvNext = lv->next; - free(lv); - lv = lvNext; + size_t i; + for (i = 0; i < LexVectorSize(lv); ++i) { + struct Token* token = LexVectorGetPointer(lv, i); + if (token->type == TOKEN_IDENTIFIER_TYPE) { + free(token->identifierValue); + } } } @@ -437,11 +608,124 @@ void parseFree(struct ParseTree* tree) { return; } - parseFree(tree->lhs); - parseFree(tree->rhs); + if (tree->lhs) { + parseFree(tree->lhs); + free(tree->lhs); + } + if (tree->rhs) { + parseFree(tree->rhs); + free(tree->rhs); + } if (tree->token.type == TOKEN_IDENTIFIER_TYPE) { free(tree->token.identifierValue); } - free(tree); +} + +static bool _performOperation(enum Operation operation, int32_t current, int32_t next, int32_t* value) { + switch (operation) { + case OP_ASSIGN: + current = next; + break; + case OP_ADD: + current += next; + break; + case OP_SUBTRACT: + current -= next; + break; + case OP_MULTIPLY: + current *= next; + break; + case OP_DIVIDE: + if (next != 0) { + current /= next; + } else { + return false; + } + break; + case OP_MODULO: + if (next != 0) { + current %= next; + } else { + return false; + } + break; + case OP_AND: + current &= next; + break; + case OP_OR: + current |= next; + break; + case OP_XOR: + current ^= next; + break; + case OP_LESS: + current = current < next; + break; + case OP_GREATER: + current = current > next; + break; + case OP_EQUAL: + current = current == next; + break; + case OP_NOT_EQUAL: + current = current != next; + break; + case OP_LOGICAL_AND: + current = current && next; + break; + case OP_LOGICAL_OR: + current = current || next; + break; + case OP_LE: + current = current <= next; + break; + case OP_GE: + current = current >= next; + break; + case OP_SHIFT_L: + current <<= next; + break; + case OP_SHIFT_R: + current >>= next; + break; + default: + return false; + } + *value = current; + return true; +} + +bool mDebuggerEvaluateParseTree(struct mDebugger* debugger, struct ParseTree* tree, int32_t* value, int* segment) { + if (!value) { + return false; + } + int32_t lhs, rhs; + switch (tree->token.type) { + case TOKEN_UINT_TYPE: + if (segment) { + *segment = -1; + } + *value = tree->token.uintValue; + return true; + case TOKEN_SEGMENT_TYPE: + if (!mDebuggerEvaluateParseTree(debugger, tree->rhs, value, segment)) { + return false; + } + return mDebuggerEvaluateParseTree(debugger, tree->lhs, segment, NULL); + case TOKEN_OPERATOR_TYPE: + if (!mDebuggerEvaluateParseTree(debugger, tree->lhs, &lhs, segment)) { + return false; + } + if (!mDebuggerEvaluateParseTree(debugger, tree->rhs, &rhs, segment)) { + return false; + } + return _performOperation(tree->token.operatorValue, lhs, rhs, value); + case TOKEN_IDENTIFIER_TYPE: + return mDebuggerLookupIdentifier(debugger, tree->token.identifierValue, value, segment); + case TOKEN_ERROR_TYPE: + default: + break; + } + return false; } diff --git a/src/debugger/test/lexer.c b/src/debugger/test/lexer.c new file mode 100644 index 000000000..89e1024df --- /dev/null +++ b/src/debugger/test/lexer.c @@ -0,0 +1,855 @@ +/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "util/test/suite.h" + +#include + +#define LEX(STR) \ + struct LexVector* lv = *state; \ + lexFree(lv); \ + LexVectorClear(lv); \ + size_t adjusted = lexExpression(lv, STR, strlen(STR), ""); \ + assert_false(adjusted > strlen(STR)) + +M_TEST_SUITE_SETUP(Lexer) { + struct LexVector* lv = malloc(sizeof(struct LexVector)); + LexVectorInit(lv, 0); + *state = lv; + return 0; +} + +M_TEST_SUITE_TEARDOWN(Lexer) { + struct LexVector* lv = *state; + lexFree(lv); + LexVectorDeinit(lv); + free(lv); + return 0; +} + +M_TEST_DEFINE(lexEmpty) { + LEX(""); + + assert_int_equal(LexVectorSize(lv), 0); +} + +M_TEST_DEFINE(lexInt) { + LEX("0"); + + assert_int_equal(LexVectorSize(lv), 1); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 0); +} + +M_TEST_DEFINE(lexDecimal) { + LEX("10"); + + assert_int_equal(LexVectorSize(lv), 1); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 10); +} + +M_TEST_DEFINE(lexBinary) { + LEX("0b10"); + + assert_int_equal(LexVectorSize(lv), 1); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 2); +} + +M_TEST_DEFINE(lexSigilBinary) { + LEX("%10"); + + assert_int_equal(LexVectorSize(lv), 1); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 2); +} + +M_TEST_DEFINE(lexHex) { + LEX("0x10"); + + assert_int_equal(LexVectorSize(lv), 1); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 0x10); +} + +M_TEST_DEFINE(lexSigilHex) { + LEX("$10"); + + assert_int_equal(LexVectorSize(lv), 1); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 0x10); +} + +M_TEST_DEFINE(lexInvalidDecimal) { + LEX("1a"); + + assert_int_equal(LexVectorSize(lv), 1); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_ERROR_TYPE); +} + +M_TEST_DEFINE(lexInvalidBinary) { + LEX("0b12"); + + assert_int_equal(LexVectorSize(lv), 1); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_ERROR_TYPE); +} + +M_TEST_DEFINE(lexInvalidHex) { + LEX("0x1g"); + + assert_int_equal(LexVectorSize(lv), 1); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_ERROR_TYPE); +} + +M_TEST_DEFINE(lexTruncatedBinary) { + LEX("0b"); + + assert_int_equal(LexVectorSize(lv), 1); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_ERROR_TYPE); +} + +M_TEST_DEFINE(lexTruncatedSigilBinary) { + LEX("%"); + + assert_int_equal(LexVectorSize(lv), 1); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_ERROR_TYPE); +} + +M_TEST_DEFINE(lexTruncatedSigilHex) { + LEX("$"); + + assert_int_equal(LexVectorSize(lv), 1); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_ERROR_TYPE); +} + +M_TEST_DEFINE(lexTruncatedHex) { + LEX("0x"); + + assert_int_equal(LexVectorSize(lv), 1); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_ERROR_TYPE); +} + +M_TEST_DEFINE(lexSigilSegmentHex) { + LEX("$01:0010"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_SEGMENT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->uintValue, 0x10); +} + +M_TEST_DEFINE(lexIdentifier) { + LEX("x"); + + assert_int_equal(LexVectorSize(lv), 1); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); +} + +M_TEST_DEFINE(lexAddOperator) { + LEX("1+"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_ADD); +} + +M_TEST_DEFINE(lexIdentifierAddOperator) { + LEX("x+"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_ADD); +} + +M_TEST_DEFINE(lexSubOperator) { + LEX("1-"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_SUBTRACT); +} + +M_TEST_DEFINE(lexIdentifierSubOperator) { + LEX("x-"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_SUBTRACT); +} + +M_TEST_DEFINE(lexMulOperator) { + LEX("1*"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_MULTIPLY); +} + +M_TEST_DEFINE(lexIdentifierMulOperator) { + LEX("x*"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_MULTIPLY); +} + +M_TEST_DEFINE(lexDivOperator) { + LEX("1/"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_DIVIDE); +} + +M_TEST_DEFINE(lexIdentifierDivOperator) { + LEX("x/"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_DIVIDE); +} + +M_TEST_DEFINE(lexModOperator) { + LEX("1%"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_MODULO); +} + +M_TEST_DEFINE(lexIdentifierModOperator) { + LEX("x%"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_MODULO); +} + +M_TEST_DEFINE(lexAndOperator) { + LEX("1&"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_AND); +} + +M_TEST_DEFINE(lexIdentifierAndOperator) { + LEX("x&"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_AND); +} + +M_TEST_DEFINE(lexOrOperator) { + LEX("1|"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_OR); +} + +M_TEST_DEFINE(lexIdentifierOrOperator) { + LEX("x|"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_OR); +} + +M_TEST_DEFINE(lexXorOperator) { + LEX("1^"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_XOR); +} + +M_TEST_DEFINE(lexIdentifierXorOperator) { + LEX("x^"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_XOR); +} + +M_TEST_DEFINE(lexLessOperator) { + LEX("1<"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_LESS); +} + +M_TEST_DEFINE(lexIdentifierLessOperator) { + LEX("x<"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_LESS); +} + +M_TEST_DEFINE(lexGreaterOperator) { + LEX("1>"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_GREATER); +} + +M_TEST_DEFINE(lexIdentifierGreaterOperator) { + LEX("x>"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_GREATER); +} + +M_TEST_DEFINE(lexEqualsOperator) { + LEX("1=="); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_EQUAL); +} + +M_TEST_DEFINE(lexIdentifierEqualsOperator) { + LEX("x=="); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_EQUAL); +} + +M_TEST_DEFINE(lexNotEqualsOperator) { + LEX("1!="); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_NOT_EQUAL); +} + +M_TEST_DEFINE(lexIdentifierNotEqualsOperator) { + LEX("x!="); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_NOT_EQUAL); +} + +M_TEST_DEFINE(lexLEOperator) { + LEX("1<="); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_LE); +} + +M_TEST_DEFINE(lexIdentifierLEOperator) { + LEX("x<="); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_LE); +} + +M_TEST_DEFINE(lexGEOperator) { + LEX("1>="); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_GE); +} + +M_TEST_DEFINE(lexIdentifierGEOperator) { + LEX("x>="); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_GE); +} + +M_TEST_DEFINE(lexLAndOperator) { + LEX("1&&"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_LOGICAL_AND); +} + +M_TEST_DEFINE(lexIdentifierLAndOperator) { + LEX("x&&"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_LOGICAL_AND); +} + +M_TEST_DEFINE(lexLOrOperator) { + LEX("1||"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_LOGICAL_OR); +} + +M_TEST_DEFINE(lexIdentifierLOrOperator) { + LEX("x||"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_LOGICAL_OR); +} + +M_TEST_DEFINE(lexShiftLOperator) { + LEX("1<<"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_SHIFT_L); +} + +M_TEST_DEFINE(lexIdentifierShiftLOperator) { + LEX("x<<"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_SHIFT_L); +} + +M_TEST_DEFINE(lexShiftROperator) { + LEX("1>>"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_SHIFT_R); +} + +M_TEST_DEFINE(lexIdentifierShiftROperator) { + LEX("x>>"); + + assert_int_equal(LexVectorSize(lv), 2); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_SHIFT_R); +} + +M_TEST_DEFINE(lexEqualsInvalidOperator) { + LEX("1=|"); + + assert_int_equal(LexVectorSize(lv), 3); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_ASSIGN); + assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE); +} + +M_TEST_DEFINE(lexIdentifierEqualsInvalidOperator) { + LEX("x=|"); + + assert_int_equal(LexVectorSize(lv), 3); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_ASSIGN); + assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE); +} + +M_TEST_DEFINE(lexNotInvalidOperator) { + LEX("1!|"); + + assert_int_equal(LexVectorSize(lv), 3); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_NOT); + assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE); +} + +M_TEST_DEFINE(lexIdentifierNotInvalidOperator) { + LEX("x!|"); + + assert_int_equal(LexVectorSize(lv), 3); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_NOT); + assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE); +} + +M_TEST_DEFINE(lexLessInvalidOperator) { + LEX("1<|"); + + assert_int_equal(LexVectorSize(lv), 3); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_LESS); + assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE); +} + +M_TEST_DEFINE(lexIdentifierLessInvalidOperator) { + LEX("x<|"); + + assert_int_equal(LexVectorSize(lv), 3); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_LESS); + assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE); +} + +M_TEST_DEFINE(lexGreaterInvalidOperator) { + LEX("1>|"); + + assert_int_equal(LexVectorSize(lv), 3); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_GREATER); + assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE); +} + +M_TEST_DEFINE(lexIdentifierGreaterInvalidOperator) { + LEX("x>|"); + + assert_int_equal(LexVectorSize(lv), 3); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_GREATER); + assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE); +} + +M_TEST_DEFINE(lexAndInvalidOperator) { + LEX("1&|"); + + assert_int_equal(LexVectorSize(lv), 3); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_AND); + assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE); +} + +M_TEST_DEFINE(lexIdentifierAndInvalidOperator) { + LEX("x&|"); + + assert_int_equal(LexVectorSize(lv), 3); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_AND); + assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE); +} + +M_TEST_DEFINE(lexOrInvalidOperator) { + LEX("1|>"); + + assert_int_equal(LexVectorSize(lv), 3); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_OR); + assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE); +} + +M_TEST_DEFINE(lexIdentifierOrInvalidOperator) { + LEX("x|>"); + + assert_int_equal(LexVectorSize(lv), 3); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_OR); + assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_ERROR_TYPE); +} + +M_TEST_DEFINE(lexSimpleExpression) { + LEX("1+1"); + + assert_int_equal(LexVectorSize(lv), 3); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_ADD); + assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 2)->uintValue, 1); +} + +M_TEST_DEFINE(lexOpenParen) { + LEX("("); + + assert_int_equal(LexVectorSize(lv), 1); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_OPEN_PAREN_TYPE); +} + +M_TEST_DEFINE(lexCloseParen) { + LEX("(0)"); + + assert_int_equal(LexVectorSize(lv), 3); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_OPEN_PAREN_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->uintValue, 0); + assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_CLOSE_PAREN_TYPE); +} + +M_TEST_DEFINE(lexIdentifierCloseParen) { + LEX("(x)"); + + assert_int_equal(LexVectorSize(lv), 3); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_OPEN_PAREN_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 1)->identifierValue, "x"); + assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_CLOSE_PAREN_TYPE); +} + +M_TEST_DEFINE(lexParentheticalExpression) { + LEX("(1+1)"); + + assert_int_equal(LexVectorSize(lv), 5); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_OPEN_PAREN_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 2)->operatorValue, OP_ADD); + assert_int_equal(LexVectorGetPointer(lv, 3)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 3)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 4)->type, TOKEN_CLOSE_PAREN_TYPE); +} + +M_TEST_DEFINE(lexNestedParentheticalExpression) { + LEX("(1+(2+3))"); + + assert_int_equal(LexVectorSize(lv), 9); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_OPEN_PAREN_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 2)->operatorValue, OP_ADD); + assert_int_equal(LexVectorGetPointer(lv, 3)->type, TOKEN_OPEN_PAREN_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 4)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 4)->uintValue, 2); + assert_int_equal(LexVectorGetPointer(lv, 5)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 5)->operatorValue, OP_ADD); + assert_int_equal(LexVectorGetPointer(lv, 6)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 6)->uintValue, 3); + assert_int_equal(LexVectorGetPointer(lv, 7)->type, TOKEN_CLOSE_PAREN_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 8)->type, TOKEN_CLOSE_PAREN_TYPE); +} + +M_TEST_DEFINE(lexSpaceSimple) { + LEX(" 1 "); + + assert_int_equal(LexVectorSize(lv), 1); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); +} + +M_TEST_DEFINE(lexSpaceIdentifier) { + LEX(" x "); + + assert_int_equal(LexVectorSize(lv), 1); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_IDENTIFIER_TYPE); + assert_string_equal(LexVectorGetPointer(lv, 0)->identifierValue, "x"); +} + +M_TEST_DEFINE(lexSpaceOperator) { + LEX("1 + 2"); + + assert_int_equal(LexVectorSize(lv), 3); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 0)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->operatorValue, OP_ADD); + assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 2)->uintValue, 2); +} + +M_TEST_DEFINE(lexSpaceParen) { + LEX(" ( 1 + 2 ) "); + + assert_int_equal(LexVectorSize(lv), 5); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_OPEN_PAREN_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 2)->operatorValue, OP_ADD); + assert_int_equal(LexVectorGetPointer(lv, 3)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 3)->uintValue, 2); + assert_int_equal(LexVectorGetPointer(lv, 4)->type, TOKEN_CLOSE_PAREN_TYPE); +} + +M_TEST_DEFINE(lexSpaceParens) { + LEX(" ( 1 + ( 2 + 3 ) ) "); + + assert_int_equal(LexVectorSize(lv), 9); + assert_int_equal(LexVectorGetPointer(lv, 0)->type, TOKEN_OPEN_PAREN_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 1)->uintValue, 1); + assert_int_equal(LexVectorGetPointer(lv, 2)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 2)->operatorValue, OP_ADD); + assert_int_equal(LexVectorGetPointer(lv, 3)->type, TOKEN_OPEN_PAREN_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 4)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 4)->uintValue, 2); + assert_int_equal(LexVectorGetPointer(lv, 5)->type, TOKEN_OPERATOR_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 5)->operatorValue, OP_ADD); + assert_int_equal(LexVectorGetPointer(lv, 6)->type, TOKEN_UINT_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 6)->uintValue, 3); + assert_int_equal(LexVectorGetPointer(lv, 7)->type, TOKEN_CLOSE_PAREN_TYPE); + assert_int_equal(LexVectorGetPointer(lv, 8)->type, TOKEN_CLOSE_PAREN_TYPE); +} + +M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(Lexer, + cmocka_unit_test(lexEmpty), + cmocka_unit_test(lexInt), + cmocka_unit_test(lexDecimal), + cmocka_unit_test(lexBinary), + cmocka_unit_test(lexSigilBinary), + cmocka_unit_test(lexHex), + cmocka_unit_test(lexSigilHex), + cmocka_unit_test(lexSigilSegmentHex), + cmocka_unit_test(lexInvalidDecimal), + cmocka_unit_test(lexInvalidHex), + cmocka_unit_test(lexInvalidBinary), + cmocka_unit_test(lexTruncatedHex), + cmocka_unit_test(lexTruncatedSigilHex), + cmocka_unit_test(lexTruncatedBinary), + cmocka_unit_test(lexTruncatedSigilBinary), + cmocka_unit_test(lexIdentifier), + cmocka_unit_test(lexAddOperator), + cmocka_unit_test(lexIdentifierAddOperator), + cmocka_unit_test(lexSubOperator), + cmocka_unit_test(lexIdentifierSubOperator), + cmocka_unit_test(lexMulOperator), + cmocka_unit_test(lexIdentifierMulOperator), + cmocka_unit_test(lexDivOperator), + cmocka_unit_test(lexIdentifierDivOperator), + cmocka_unit_test(lexModOperator), + cmocka_unit_test(lexIdentifierModOperator), + cmocka_unit_test(lexAndOperator), + cmocka_unit_test(lexIdentifierAndOperator), + cmocka_unit_test(lexOrOperator), + cmocka_unit_test(lexIdentifierOrOperator), + cmocka_unit_test(lexXorOperator), + cmocka_unit_test(lexIdentifierXorOperator), + cmocka_unit_test(lexLessOperator), + cmocka_unit_test(lexIdentifierLessOperator), + cmocka_unit_test(lexGreaterOperator), + cmocka_unit_test(lexIdentifierGreaterOperator), + cmocka_unit_test(lexEqualsOperator), + cmocka_unit_test(lexIdentifierEqualsOperator), + cmocka_unit_test(lexNotEqualsOperator), + cmocka_unit_test(lexIdentifierNotEqualsOperator), + cmocka_unit_test(lexLEOperator), + cmocka_unit_test(lexIdentifierLEOperator), + cmocka_unit_test(lexGEOperator), + cmocka_unit_test(lexIdentifierGEOperator), + cmocka_unit_test(lexLAndOperator), + cmocka_unit_test(lexIdentifierLAndOperator), + cmocka_unit_test(lexLOrOperator), + cmocka_unit_test(lexIdentifierLOrOperator), + cmocka_unit_test(lexShiftLOperator), + cmocka_unit_test(lexIdentifierShiftLOperator), + cmocka_unit_test(lexShiftROperator), + cmocka_unit_test(lexIdentifierShiftROperator), + cmocka_unit_test(lexEqualsInvalidOperator), + cmocka_unit_test(lexIdentifierEqualsInvalidOperator), + cmocka_unit_test(lexNotInvalidOperator), + cmocka_unit_test(lexIdentifierNotInvalidOperator), + cmocka_unit_test(lexLessInvalidOperator), + cmocka_unit_test(lexIdentifierLessInvalidOperator), + cmocka_unit_test(lexGreaterInvalidOperator), + cmocka_unit_test(lexIdentifierGreaterInvalidOperator), + cmocka_unit_test(lexAndInvalidOperator), + cmocka_unit_test(lexIdentifierAndInvalidOperator), + cmocka_unit_test(lexOrInvalidOperator), + cmocka_unit_test(lexIdentifierOrInvalidOperator), + cmocka_unit_test(lexSimpleExpression), + cmocka_unit_test(lexOpenParen), + cmocka_unit_test(lexCloseParen), + cmocka_unit_test(lexIdentifierCloseParen), + cmocka_unit_test(lexParentheticalExpression), + cmocka_unit_test(lexNestedParentheticalExpression), + cmocka_unit_test(lexSpaceSimple), + cmocka_unit_test(lexSpaceIdentifier), + cmocka_unit_test(lexSpaceOperator), + cmocka_unit_test(lexSpaceParen), + cmocka_unit_test(lexSpaceParens)) diff --git a/src/debugger/test/parser.c b/src/debugger/test/parser.c new file mode 100644 index 000000000..412fc65c9 --- /dev/null +++ b/src/debugger/test/parser.c @@ -0,0 +1,118 @@ +/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "util/test/suite.h" + +#include + +struct LPTest { + struct LexVector lv; + struct ParseTree tree; +}; + +#define PARSE(STR) \ + struct LPTest* lp = *state; \ + lexFree(&lp->lv); \ + LexVectorClear(&lp->lv); \ + size_t adjusted = lexExpression(&lp->lv, STR, strlen(STR), ""); \ + assert_false(adjusted > strlen(STR)); \ + struct ParseTree* tree = &lp->tree; \ + parseLexedExpression(tree, &lp->lv) + +M_TEST_SUITE_SETUP(Parser) { + struct LPTest* lp = malloc(sizeof(struct LPTest)); + LexVectorInit(&lp->lv, 0); + *state = lp; + return 0; +} + +M_TEST_SUITE_TEARDOWN(Parser) { + struct LPTest* lp = *state; + parseFree(&lp->tree); \ + lexFree(&lp->lv); + LexVectorDeinit(&lp->lv); + free(lp); + return 0; +} + +M_TEST_DEFINE(parseEmpty) { + PARSE(""); + + assert_int_equal(tree->token.type, TOKEN_ERROR_TYPE); +} + +M_TEST_DEFINE(parseInt) { + PARSE("0"); + + assert_int_equal(tree->token.type, TOKEN_UINT_TYPE); + assert_int_equal(tree->token.uintValue, 0); +} + +M_TEST_DEFINE(parseLexError) { + PARSE("@"); + + assert_int_equal(tree->token.type, TOKEN_ERROR_TYPE); +} + +M_TEST_DEFINE(parseSimpleExpression) { + PARSE("1+2"); + + assert_int_equal(tree->token.type, TOKEN_OPERATOR_TYPE); + assert_int_equal(tree->token.operatorValue, OP_ADD); + assert_int_equal(tree->lhs->token.type, TOKEN_UINT_TYPE); + assert_int_equal(tree->lhs->token.uintValue, 1); + assert_int_equal(tree->rhs->token.type, TOKEN_UINT_TYPE); + assert_int_equal(tree->rhs->token.uintValue, 2); +} + +M_TEST_DEFINE(parseAddMultplyExpression) { + PARSE("1+2*3"); + + assert_int_equal(tree->token.type, TOKEN_OPERATOR_TYPE); + assert_int_equal(tree->token.operatorValue, OP_ADD); + assert_int_equal(tree->lhs->token.type, TOKEN_UINT_TYPE); + assert_int_equal(tree->lhs->token.uintValue, 1); + assert_int_equal(tree->rhs->token.type, TOKEN_OPERATOR_TYPE); + assert_int_equal(tree->rhs->token.uintValue, OP_MULTIPLY); + assert_int_equal(tree->rhs->lhs->token.type, TOKEN_UINT_TYPE); + assert_int_equal(tree->rhs->lhs->token.uintValue, 2); + assert_int_equal(tree->rhs->rhs->token.type, TOKEN_UINT_TYPE); + assert_int_equal(tree->rhs->rhs->token.uintValue, 3); +} + +M_TEST_DEFINE(parseParentheticalExpression) { + PARSE("(1+2)"); + + assert_int_equal(tree->token.type, TOKEN_OPERATOR_TYPE); + assert_int_equal(tree->token.operatorValue, OP_ADD); + assert_int_equal(tree->lhs->token.type, TOKEN_UINT_TYPE); + assert_int_equal(tree->lhs->token.uintValue, 1); + assert_int_equal(tree->rhs->token.type, TOKEN_UINT_TYPE); + assert_int_equal(tree->rhs->token.uintValue, 2); +} + +M_TEST_DEFINE(parseParentheticalAddMultplyExpression) { + PARSE("(1+2)*3"); + + assert_int_equal(tree->token.type, TOKEN_OPERATOR_TYPE); + assert_int_equal(tree->token.operatorValue, OP_MULTIPLY); + assert_int_equal(tree->lhs->token.type, TOKEN_OPERATOR_TYPE); + assert_int_equal(tree->lhs->token.uintValue, OP_ADD); + assert_int_equal(tree->lhs->lhs->token.type, TOKEN_UINT_TYPE); + assert_int_equal(tree->lhs->lhs->token.uintValue, 1); + assert_int_equal(tree->lhs->lhs->token.type, TOKEN_UINT_TYPE); + assert_int_equal(tree->lhs->rhs->token.uintValue, 2); + assert_int_equal(tree->rhs->token.type, TOKEN_UINT_TYPE); + assert_int_equal(tree->rhs->token.uintValue, 3); +} + +M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(Parser, + cmocka_unit_test(parseEmpty), + cmocka_unit_test(parseInt), + cmocka_unit_test(parseLexError), + cmocka_unit_test(parseSimpleExpression), + cmocka_unit_test(parseAddMultplyExpression), + cmocka_unit_test(parseParentheticalExpression), + cmocka_unit_test(parseParentheticalAddMultplyExpression)) diff --git a/src/ds/dma.c b/src/ds/dma.c index 4c14c83ea..355931ddf 100644 --- a/src/ds/dma.c +++ b/src/ds/dma.c @@ -133,13 +133,13 @@ void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) { } } else { dma->nextCount = 0; - if (!GBADMARegisterIsRepeat(dma->reg) || GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_NOW) { + if (!GBADMARegisterIsRepeat(dma->reg) || GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_NOW) { dma->reg = GBADMARegisterClearEnable(dma->reg); // Clear the enable bit in memory memory->io[(DS_REG_DMA0CNT_HI + memory->activeDMA * (DS_REG_DMA1CNT_HI - DS_REG_DMA0CNT_HI)) >> 1] &= 0x7FFF; } - if (GBADMARegisterGetDestControl(dma->reg) == DMA_INCREMENT_RELOAD) { + if (GBADMARegisterGetDestControl(dma->reg) == GBA_DMA_INCREMENT_RELOAD) { dma->nextDest = dma->dest; } if (GBADMARegisterIsDoIRQ(dma->reg)) { diff --git a/src/ds/ds.c b/src/ds/ds.c index da4b15651..1e9d96ef5 100644 --- a/src/ds/ds.c +++ b/src/ds/ds.c @@ -617,7 +617,7 @@ void DSHitStub(struct ARMCore* cpu, uint32_t opcode) { if (ds->debugger) { struct mDebuggerEntryInfo info = { .address = _ARMPCAddress(cpu), - .opcode = opcode + .type.bp.opcode = opcode }; mDebuggerEnter(ds->debugger->d.p, DEBUGGER_ENTER_ILLEGAL_OP, &info); } @@ -641,7 +641,7 @@ void DSIllegal(struct ARMCore* cpu, uint32_t opcode) { } else if (ds->debugger) { struct mDebuggerEntryInfo info = { .address = _ARMPCAddress(cpu), - .opcode = opcode + .type.bp.opcode = opcode }; mDebuggerEnter(ds->debugger->d.p, DEBUGGER_ENTER_ILLEGAL_OP, &info); #endif diff --git a/src/ds/extra/cli.c b/src/ds/extra/cli.c index 7c467b6ee..a516a009f 100644 --- a/src/ds/extra/cli.c +++ b/src/ds/extra/cli.c @@ -12,7 +12,6 @@ static void _DSCLIDebuggerInit(struct CLIDebuggerSystem*); static bool _DSCLIDebuggerCustom(struct CLIDebuggerSystem*); -static uint32_t _DSCLIDebuggerLookupIdentifier(struct CLIDebuggerSystem*, const char* name, struct CLIDebugVector* dv); static void _frame(struct CLIDebugger*, struct CLIDebugVector*); static void _switchCpu(struct CLIDebugger*, struct CLIDebugVector*); @@ -29,7 +28,6 @@ struct DSCLIDebugger* DSCLIDebuggerCreate(struct mCore* core) { debugger->d.init = _DSCLIDebuggerInit; debugger->d.deinit = NULL; debugger->d.custom = _DSCLIDebuggerCustom; - debugger->d.lookupIdentifier = _DSCLIDebuggerLookupIdentifier; debugger->d.name = "DS"; debugger->d.commands = _DSCLIDebuggerCommands; @@ -49,12 +47,6 @@ static bool _DSCLIDebuggerCustom(struct CLIDebuggerSystem* debugger) { return false; } -static uint32_t _DSCLIDebuggerLookupIdentifier(struct CLIDebuggerSystem* debugger, const char* name, struct CLIDebugVector* dv) { - UNUSED(debugger); - dv->type = CLIDV_ERROR_TYPE; - return 0; -} - static void _frame(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { UNUSED(dv); debugger->d.state = DEBUGGER_CUSTOM; diff --git a/src/feature/ffmpeg/ffmpeg-encoder.c b/src/feature/ffmpeg/ffmpeg-encoder.c index e35f276ed..b97d9e1c1 100644 --- a/src/feature/ffmpeg/ffmpeg-encoder.c +++ b/src/feature/ffmpeg/ffmpeg-encoder.c @@ -235,7 +235,11 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) { AVDictionary* opts = 0; av_dict_set(&opts, "strict", "-2", 0); if (encoder->context->oformat->flags & AVFMT_GLOBALHEADER) { +#ifdef AV_CODEC_FLAG_GLOBAL_HEADER + encoder->audio->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; +#else encoder->audio->flags |= CODEC_FLAG_GLOBAL_HEADER; +#endif } avcodec_open2(encoder->audio, acodec, &opts); av_dict_free(&opts); @@ -298,7 +302,11 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) { encoder->video->gop_size = 60; encoder->video->max_b_frames = 3; if (encoder->context->oformat->flags & AVFMT_GLOBALHEADER) { +#ifdef AV_CODEC_FLAG_GLOBAL_HEADER + encoder->video->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; +#else encoder->video->flags |= CODEC_FLAG_GLOBAL_HEADER; +#endif } if (strcmp(vcodec->name, "libx264") == 0) { // Try to adaptively figure out when you can use a slower encoder diff --git a/src/feature/gui/gui-config.c b/src/feature/gui/gui-config.c index 9d4e39fb2..a0572903a 100644 --- a/src/feature/gui/gui-config.c +++ b/src/feature/gui/gui-config.c @@ -10,6 +10,9 @@ #include "feature/gui/gui-runner.h" #include "feature/gui/remap.h" #include +#ifdef M_CORE_GB +#include +#endif #include #include @@ -44,6 +47,26 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t }, .nStates = 2 }; + *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) { + .title = "Autosave state", + .data = "autosave", + .submenu = 0, + .state = true, + .validStates = (const char*[]) { + "Off", "On" + }, + .nStates = 2 + }; + *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) { + .title = "Autoload state", + .data = "autoload", + .submenu = 0, + .state = true, + .validStates = (const char*[]) { + "Off", "On" + }, + .nStates = 2 + }; *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) { .title = "Use BIOS if found", .data = "useBios", @@ -55,9 +78,23 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t .nStates = 2 }; *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) { - .title = "Select BIOS path", - .data = "bios", + .title = "Select GBA BIOS path", + .data = "gba.bios", }; +#ifdef M_CORE_GB + *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) { + .title = "Select GB BIOS path", + .data = "gb.bios", + }; + *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) { + .title = "Select GBC BIOS path", + .data = "gbc.bios", + }; + *GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) { + .title = "Select SGB BIOS path", + .data = "sgb.bios", + }; +#endif size_t i; const char* mapNames[GUI_MAX_INPUTS + 1]; if (runner->keySources) { @@ -88,7 +125,12 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t .data = 0, }; enum GUIMenuExitReason reason; - char biosPath[256] = ""; + char gbaBiosPath[256] = ""; +#ifdef M_CORE_GB + char gbBiosPath[256] = ""; + char gbcBiosPath[256] = ""; + char sgbBiosPath[256] = ""; +#endif struct GUIMenuItem* item; for (i = 0; i < GUIMenuItemListSize(&menu.items); ++i) { @@ -105,8 +147,17 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t break; } if (!strcmp(item->data, "*SAVE")) { - if (biosPath[0]) { - mCoreConfigSetValue(&runner->config, "bios", biosPath); + if (gbaBiosPath[0]) { + mCoreConfigSetValue(&runner->config, "gba.bios", gbaBiosPath); + } + if (gbBiosPath[0]) { + mCoreConfigSetValue(&runner->config, "gb.bios", gbBiosPath); + } + if (gbcBiosPath[0]) { + mCoreConfigSetValue(&runner->config, "gbc.bios", gbcBiosPath); + } + if (sgbBiosPath[0]) { + mCoreConfigSetValue(&runner->config, "sgb.bios", sgbBiosPath); } for (i = 0; i < GUIMenuItemListSize(&menu.items); ++i) { item = GUIMenuItemListGetPointer(&menu.items, i); @@ -130,13 +181,36 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t mGUIRemapKeys(&runner->params, &runner->core->inputMap, &runner->keySources[item->state]); continue; } - if (!strcmp(item->data, "bios")) { + if (!strcmp(item->data, "gba.bios")) { // TODO: show box if failed - if (!GUISelectFile(&runner->params, biosPath, sizeof(biosPath), GBAIsBIOS)) { - biosPath[0] = '\0'; + if (!GUISelectFile(&runner->params, gbaBiosPath, sizeof(gbaBiosPath), GBAIsBIOS)) { + gbaBiosPath[0] = '\0'; } continue; } +#ifdef M_CORE_GB + if (!strcmp(item->data, "gb.bios")) { + // TODO: show box if failed + if (!GUISelectFile(&runner->params, gbBiosPath, sizeof(gbBiosPath), GBIsBIOS)) { + gbBiosPath[0] = '\0'; + } + continue; + } + if (!strcmp(item->data, "gbc.bios")) { + // TODO: show box if failed + if (!GUISelectFile(&runner->params, gbcBiosPath, sizeof(gbcBiosPath), GBIsBIOS)) { + gbcBiosPath[0] = '\0'; + } + continue; + } + if (!strcmp(item->data, "sgb.bios")) { + // TODO: show box if failed + if (!GUISelectFile(&runner->params, sgbBiosPath, sizeof(sgbBiosPath), GBIsBIOS)) { + sgbBiosPath[0] = '\0'; + } + continue; + } +#endif if (item->validStates) { ++item->state; if (item->state >= item->nStates) { diff --git a/src/feature/gui/gui-runner.c b/src/feature/gui/gui-runner.c index 0f997f395..e0ad1a155 100644 --- a/src/feature/gui/gui-runner.c +++ b/src/feature/gui/gui-runner.c @@ -18,15 +18,12 @@ #include #include -#ifdef _3DS -#include <3ds.h> -#endif - #include mLOG_DECLARE_CATEGORY(GUI_RUNNER); mLOG_DEFINE_CATEGORY(GUI_RUNNER, "GUI Runner", "gui.runner"); +#define AUTOSAVE_GRANULARITY 600 #define FPS_GRANULARITY 120 #define FPS_BUFFER_SIZE 3 @@ -38,19 +35,11 @@ enum { RUNNER_SCREENSHOT, RUNNER_CONFIG, RUNNER_RESET, - RUNNER_COMMAND_MASK = 0xFFFF, - - RUNNER_STATE_1 = 0x10000, - RUNNER_STATE_2 = 0x20000, - RUNNER_STATE_3 = 0x30000, - RUNNER_STATE_4 = 0x40000, - RUNNER_STATE_5 = 0x50000, - RUNNER_STATE_6 = 0x60000, - RUNNER_STATE_7 = 0x70000, - RUNNER_STATE_8 = 0x80000, - RUNNER_STATE_9 = 0x90000, + RUNNER_COMMAND_MASK = 0xFFFF }; +#define RUNNER_STATE(X) ((X) << 16) + static const struct mInputPlatformInfo _mGUIKeyInfo = { .platformName = "gui", .keyId = (const char*[GUI_INPUT_MAX]) { @@ -145,6 +134,27 @@ static uint8_t _readLux(struct GBALuminanceSource* lux) { return 0xFF - value; } +static void _tryAutosave(struct mGUIRunner* runner) { + int autosave = false; + mCoreConfigGetIntValue(&runner->config, "autosave", &autosave); + if (!autosave) { + return; + } + +#ifdef DISABLE_THREADING + mCoreSaveState(runner->core, 0, SAVESTATE_SAVEDATA | SAVESTATE_RTC | SAVESTATE_METADATA); +#else + if (!runner->autosave.buffer) { + runner->autosave.buffer = VFileMemChunk(NULL, 0); + } + MutexLock(&runner->autosave.mutex); + runner->autosave.core = runner->core; + mCoreSaveStateNamed(runner->core, runner->autosave.buffer, SAVESTATE_SAVEDATA | SAVESTATE_RTC | SAVESTATE_METADATA); + ConditionWake(&runner->autosave.cond); + MutexUnlock(&runner->autosave.mutex); +#endif +} + void mGUIInit(struct mGUIRunner* runner, const char* port) { GUIInit(&runner->params); runner->port = port; @@ -164,7 +174,14 @@ void mGUIInit(struct mGUIRunner* runner, const char* port) { // TODO: Do we need to load more defaults? mCoreConfigSetDefaultIntValue(&runner->config, "volume", 0x100); mCoreConfigSetDefaultValue(&runner->config, "idleOptimization", "detect"); + mCoreConfigSetDefaultIntValue(&runner->config, "autoload", true); +#ifdef DISABLE_THREADING + mCoreConfigSetDefaultIntValue(&runner->config, "autosave", false); +#else + mCoreConfigSetDefaultIntValue(&runner->config, "autosave", true); +#endif mCoreConfigLoad(&runner->config); + mCoreConfigGetIntValue(&runner->config, "logLevel", &logger.logLevel); char path[PATH_MAX]; mCoreConfigDirectory(path, PATH_MAX); @@ -177,9 +194,34 @@ void mGUIInit(struct mGUIRunner* runner, const char* port) { strncpy(runner->params.currentPath, lastPath, PATH_MAX - 1); runner->params.currentPath[PATH_MAX - 1] = '\0'; } + +#ifndef DISABLE_THREADING + if (!runner->autosave.running) { + runner->autosave.running = true; + MutexInit(&runner->autosave.mutex); + ConditionInit(&runner->autosave.cond); + ThreadCreate(&runner->autosave.thread, mGUIAutosaveThread, &runner->autosave); + } +#endif } void mGUIDeinit(struct mGUIRunner* runner) { +#ifndef DISABLE_THREADING + MutexLock(&runner->autosave.mutex); + runner->autosave.running = false; + ConditionWake(&runner->autosave.cond); + MutexUnlock(&runner->autosave.mutex); + + ThreadJoin(runner->autosave.thread); + + ConditionDeinit(&runner->autosave.cond); + MutexDeinit(&runner->autosave.mutex); + + if (runner->autosave.buffer) { + runner->autosave.buffer->close(runner->autosave.buffer); + } +#endif + if (runner->teardown) { runner->teardown(runner); } @@ -242,25 +284,26 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) { *GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Save state", .submenu = &stateSaveMenu }; *GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Load state", .submenu = &stateLoadMenu }; - *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 1", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_1) }; - *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 2", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_2) }; - *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 3", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_3) }; - *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 4", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_4) }; - *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 5", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_5) }; - *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 6", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_6) }; - *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 7", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_7) }; - *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 8", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_8) }; - *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 9", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE_9) }; + *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 1", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(1)) }; + *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 2", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(2)) }; + *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 3", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(3)) }; + *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 4", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(4)) }; + *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 5", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(5)) }; + *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 6", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(6)) }; + *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 7", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(7)) }; + *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 8", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(8)) }; + *GUIMenuItemListAppend(&stateSaveMenu.items) = (struct GUIMenuItem) { .title = "State 9", .data = (void*) (RUNNER_SAVE_STATE | RUNNER_STATE(9)) }; - *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 1", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_1) }; - *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 2", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_2) }; - *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 3", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_3) }; - *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 4", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_4) }; - *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 5", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_5) }; - *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 6", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_6) }; - *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 7", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_7) }; - *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 8", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_8) }; - *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 9", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE_9) }; + *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "Autosave", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(0)) }; + *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 1", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(1)) }; + *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 2", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(2)) }; + *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 3", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(3)) }; + *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 4", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(4)) }; + *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 5", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(5)) }; + *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 6", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(6)) }; + *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 7", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(7)) }; + *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 8", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(8)) }; + *GUIMenuItemListAppend(&stateLoadMenu.items) = (struct GUIMenuItem) { .title = "State 9", .data = (void*) (RUNNER_LOAD_STATE | RUNNER_STATE(9)) }; *GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Take screenshot", .data = (void*) RUNNER_SCREENSHOT }; *GUIMenuItemListAppend(&pauseMenu.items) = (struct GUIMenuItem) { .title = "Configure", .data = (void*) RUNNER_CONFIG }; @@ -283,7 +326,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) { if (runner->core) { mLOG(GUI_RUNNER, INFO, "Found core"); runner->core->init(runner->core); - mCoreConfigInit(&runner->core->config, runner->port); + mCoreInitConfig(runner->core, runner->port); mInputMapInit(&runner->core->inputMap, &GBAInputInfo); found = mCoreLoadFile(runner->core, path); if (!found) { @@ -302,10 +345,10 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) { } mLOG(GUI_RUNNER, DEBUG, "Loading config..."); mCoreLoadForeignConfig(runner->core, &runner->config); - logger.logLevel = runner->core->opts.logLevel; mLOG(GUI_RUNNER, DEBUG, "Loading save..."); mCoreAutoloadSave(runner->core); + mCoreAutoloadCheats(runner->core); if (runner->setup) { mLOG(GUI_RUNNER, DEBUG, "Setting up runner..."); runner->setup(runner); @@ -320,7 +363,22 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) { mLOG(GUI_RUNNER, DEBUG, "Reseting..."); runner->core->reset(runner->core); mLOG(GUI_RUNNER, DEBUG, "Reset!"); + + + int autoload = false; + mCoreConfigGetIntValue(&runner->config, "autoload", &autoload); + if (autoload) { + mCoreLoadState(runner->core, 0, SAVESTATE_SCREENSHOT | SAVESTATE_RTC); + } + bool running = true; + +#ifndef DISABLE_THREADING + MutexLock(&runner->autosave.mutex); + runner->autosave.core = runner->core; + MutexUnlock(&runner->autosave.mutex); +#endif + if (runner->gameLoaded) { runner->gameLoaded(runner); } @@ -333,13 +391,14 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) { gettimeofday(&tv, 0); runner->lastFpsCheck = 1000000LL * tv.tv_sec + tv.tv_usec; - while (true) { -#ifdef _3DS - running = aptMainLoop(); - if (!running) { - break; + int frame = 0; + while (running) { + if (runner->running) { + running = runner->running(runner); + if (!running) { + break; + } } -#endif uint32_t guiKeys; uint32_t heldKeys; GUIPollInput(&runner->params, &guiKeys, &heldKeys); @@ -411,6 +470,11 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) { runner->fps = (CircleBufferSize(&runner->fpsBuffer) * FPS_GRANULARITY * 1000000.0f) / (runner->totalDelta * sizeof(uint32_t)); } } + if (frame == AUTOSAVE_GRANULARITY) { + frame = 0; + _tryAutosave(runner); + } + ++frame; } } @@ -463,6 +527,18 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) { if (runner->gameUnloaded) { runner->gameUnloaded(runner); } +#ifndef DISABLE_THREADING + MutexLock(&runner->autosave.mutex); + runner->autosave.core = NULL; + MutexUnlock(&runner->autosave.mutex); +#endif + + int autosave = false; + mCoreConfigGetIntValue(&runner->config, "autosave", &autosave); + if (autosave) { + mCoreSaveState(runner->core, 0, SAVESTATE_SAVEDATA | SAVESTATE_RTC | SAVESTATE_METADATA); + } + mLOG(GUI_RUNNER, DEBUG, "Unloading game..."); runner->core->unloadROM(runner->core); drawState.screenshotId = 0; @@ -486,6 +562,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) { mInputMapDeinit(&runner->core->inputMap); mLOG(GUI_RUNNER, DEBUG, "Deinitializing core..."); runner->core->deinit(runner->core); + runner->core = NULL; GUIMenuItemListDeinit(&pauseMenu.items); GUIMenuItemListDeinit(&stateSaveMenu.items); @@ -511,3 +588,21 @@ void mGUIRunloop(struct mGUIRunner* runner) { mGUIRun(runner, path); } } + +#ifndef DISABLE_THREADING +THREAD_ENTRY mGUIAutosaveThread(void* context) { + struct mGUIAutosaveContext* autosave = context; + MutexLock(&autosave->mutex); + while (autosave->running) { + ConditionWait(&autosave->cond, &autosave->mutex); + if (autosave->running && autosave->core) { + struct VFile* vf = mCoreGetState(autosave->core, 0, true); + void* mem = autosave->buffer->map(autosave->buffer, autosave->buffer->size(autosave->buffer), MAP_READ); + vf->write(vf, mem, autosave->buffer->size(autosave->buffer)); + autosave->buffer->unmap(autosave->buffer, mem, autosave->buffer->size(autosave->buffer)); + vf->close(vf); + } + } + MutexUnlock(&autosave->mutex); +} +#endif diff --git a/src/feature/gui/gui-runner.h b/src/feature/gui/gui-runner.h index 0a288951f..6c91d1347 100644 --- a/src/feature/gui/gui-runner.h +++ b/src/feature/gui/gui-runner.h @@ -15,6 +15,7 @@ CXX_GUARD_START #include #include #include +#include enum mGUIInput { mGUI_INPUT_INCREASE_BRIGHTNESS = GUI_INPUT_USER_START, @@ -38,12 +39,27 @@ struct mGUIRunnerLux { int luxLevel; }; +#ifndef DISABLE_THREADING +struct VFile; +struct mGUIAutosaveContext { + struct VFile* buffer; + struct mCore* core; + Thread thread; + Mutex mutex; + Condition cond; + bool running; +}; +#endif + struct mGUIRunner { struct mCore* core; struct GUIParams params; struct mGUIBackground background; struct mGUIRunnerLux luminanceSource; +#ifndef DISABLE_THREADING + struct mGUIAutosaveContext autosave; +#endif struct mInputMap guiKeys; struct mCoreConfig config; @@ -70,6 +86,7 @@ struct mGUIRunner { void (*incrementScreenMode)(struct mGUIRunner*); void (*setFrameLimiter)(struct mGUIRunner*, bool limit); uint16_t (*pollGameInput)(struct mGUIRunner*); + bool (*running)(struct mGUIRunner*); }; void mGUIInit(struct mGUIRunner*, const char* port); @@ -77,6 +94,10 @@ void mGUIDeinit(struct mGUIRunner*); void mGUIRun(struct mGUIRunner*, const char* path); void mGUIRunloop(struct mGUIRunner*); +#ifndef DISABLE_THREADING +THREAD_ENTRY mGUIAutosaveThread(void* context); +#endif + CXX_GUARD_END #endif diff --git a/src/gb/cheats.c b/src/gb/cheats.c index 118ddde20..5c202782c 100644 --- a/src/gb/cheats.c +++ b/src/gb/cheats.c @@ -244,7 +244,11 @@ bool GBCheatAddLine(struct mCheatSet* set, const char* line, int type) { static void GBCheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* device) { struct GBCheatSet* gbset = (struct GBCheatSet*) cheats; - _patchROM(device, gbset); + if (cheats->enabled) { + _patchROM(device, gbset); + } else { + _unpatchROM(device, gbset); + } } static void GBCheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet) { diff --git a/src/gb/core.c b/src/gb/core.c index 8adffa633..bc891d923 100644 --- a/src/gb/core.c +++ b/src/gb/core.c @@ -463,6 +463,10 @@ static void _GBCoreReset(struct mCore* core) { #endif LR35902Reset(core->cpu); + + if (core->opts.skipBios) { + GBSkipBIOS(core->board); + } } static void _GBCoreRunFrame(struct mCore* core) { @@ -746,6 +750,20 @@ static void _GBCoreLoadSymbols(struct mCore* core, struct VFile* vf) { } GBLoadSymbols(core->symbolTable, vf); } + +static bool _GBCoreLookupIdentifier(struct mCore* core, const char* name, int32_t* value, int* segment) { + UNUSED(core); + *segment = -1; + int i; + for (i = 0; i < REG_MAX; ++i) { + const char* reg = GBIORegisterNames[i]; + if (reg && strcasecmp(reg, name) == 0) { + *value = GB_BASE_IO | i; + return true; + } + } + return false; +} #endif static struct mCheatDevice* _GBCoreCheatDevice(struct mCore* core) { @@ -835,6 +853,7 @@ static void _GBCoreEnableAudioChannel(struct mCore* core, size_t id, bool enable } } +#ifndef MINIMAL_CORE static void _GBCoreStartVideoLog(struct mCore* core, struct mVideoLogContext* context) { struct GBCore* gbcore = (struct GBCore*) core; struct GB* gb = core->board; @@ -857,6 +876,7 @@ static void _GBCoreEndVideoLog(struct mCore* core) { free(gbcore->proxyRenderer.logger); gbcore->proxyRenderer.logger = NULL; } +#endif struct mCore* GBCoreCreate(void) { struct GBCore* gbcore = malloc(sizeof(*gbcore)); @@ -928,6 +948,7 @@ struct mCore* GBCoreCreate(void) { core->attachDebugger = _GBCoreAttachDebugger; core->detachDebugger = _GBCoreDetachDebugger; core->loadSymbols = _GBCoreLoadSymbols; + core->lookupIdentifier = _GBCoreLookupIdentifier; #endif core->cheatDevice = _GBCoreCheatDevice; core->savedataClone = _GBCoreSavedataClone; diff --git a/src/gb/debugger/cli.c b/src/gb/debugger/cli.c index 2f59293d1..a051ea577 100644 --- a/src/gb/debugger/cli.c +++ b/src/gb/debugger/cli.c @@ -14,16 +14,15 @@ static void _GBCLIDebuggerInit(struct CLIDebuggerSystem*); static bool _GBCLIDebuggerCustom(struct CLIDebuggerSystem*); -static uint32_t _GBCLIDebuggerLookupIdentifier(struct CLIDebuggerSystem*, const char* name, struct CLIDebugVector* dv); static void _frame(struct CLIDebugger*, struct CLIDebugVector*); static void _load(struct CLIDebugger*, struct CLIDebugVector*); static void _save(struct CLIDebugger*, struct CLIDebugVector*); struct CLIDebuggerCommandSummary _GBCLIDebuggerCommands[] = { - { "frame", _frame, 0, "Frame advance" }, - { "load", _load, CLIDVParse, "Load a savestate" }, - { "save", _save, CLIDVParse, "Save a savestate" }, + { "frame", _frame, "", "Frame advance" }, + { "load", _load, "*", "Load a savestate" }, + { "save", _save, "*", "Save a savestate" }, { 0, 0, 0, 0 } }; @@ -34,7 +33,6 @@ struct CLIDebuggerSystem* GBCLIDebuggerCreate(struct mCore* core) { debugger->d.init = _GBCLIDebuggerInit; debugger->d.deinit = NULL; debugger->d.custom = _GBCLIDebuggerCustom; - debugger->d.lookupIdentifier = _GBCLIDebuggerLookupIdentifier; debugger->d.name = "Game Boy"; debugger->d.commands = _GBCLIDebuggerCommands; @@ -65,19 +63,6 @@ static bool _GBCLIDebuggerCustom(struct CLIDebuggerSystem* debugger) { return false; } -static uint32_t _GBCLIDebuggerLookupIdentifier(struct CLIDebuggerSystem* debugger, const char* name, struct CLIDebugVector* dv) { - UNUSED(debugger); - int i; - for (i = 0; i < REG_MAX; ++i) { - const char* reg = GBIORegisterNames[i]; - if (reg && strcasecmp(reg, name) == 0) { - return GB_BASE_IO | i; - } - } - dv->type = CLIDV_ERROR_TYPE; - return 0; -} - static void _frame(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { UNUSED(dv); debugger->d.state = DEBUGGER_CUSTOM; diff --git a/src/gb/gb.c b/src/gb/gb.c index c75992461..dd99b2bac 100644 --- a/src/gb/gb.c +++ b/src/gb/gb.c @@ -435,84 +435,11 @@ void GBReset(struct LR35902Core* cpu) { cpu->d = 0; gb->timer.internalDiv = 0; - int nextDiv = 0; - if (!gb->biosVf) { - switch (gb->model) { - case GB_MODEL_AUTODETECT: // Silence warnings - gb->model = GB_MODEL_DMG; - case GB_MODEL_DMG: - cpu->a = 1; - cpu->f.packed = 0xB0; - cpu->c = 0x13; - cpu->e = 0xD8; - cpu->h = 1; - cpu->l = 0x4D; - gb->timer.internalDiv = 0xABC; - nextDiv = 4; - break; - case GB_MODEL_SGB: - cpu->a = 1; - cpu->f.packed = 0x00; - cpu->c = 0x14; - cpu->e = 0x00; - cpu->h = 0xC0; - cpu->l = 0x60; - gb->timer.internalDiv = 0xABC; - nextDiv = 4; - break; - case GB_MODEL_MGB: - cpu->a = 0xFF; - cpu->f.packed = 0xB0; - cpu->c = 0x13; - cpu->e = 0xD8; - cpu->h = 1; - cpu->l = 0x4D; - gb->timer.internalDiv = 0xABC; - nextDiv = 4; - break; - case GB_MODEL_SGB2: - cpu->a = 0xFF; - cpu->f.packed = 0x00; - cpu->c = 0x14; - cpu->e = 0x00; - cpu->h = 0xC0; - cpu->l = 0x60; - gb->timer.internalDiv = 0xABC; - nextDiv = 4; - break; - case GB_MODEL_AGB: - cpu->a = 0x11; - cpu->b = 1; - cpu->f.packed = 0x00; - cpu->c = 0; - cpu->e = 0x08; - cpu->h = 0; - cpu->l = 0x7C; - gb->timer.internalDiv = 0x1EA; - nextDiv = 0xC; - break; - case GB_MODEL_CGB: - cpu->a = 0x11; - cpu->f.packed = 0x80; - cpu->c = 0; - cpu->e = 0x08; - cpu->h = 0; - cpu->l = 0x7C; - gb->timer.internalDiv = 0x1EA; - nextDiv = 0xC; - break; - } - - cpu->sp = 0xFFFE; - cpu->pc = 0x100; - } gb->cpuBlocked = false; gb->earlyExit = false; gb->doubleSpeed = 0; - cpu->memory.setActiveRegion(cpu, cpu->pc); - if (gb->yankedRomSize) { gb->memory.romSize = gb->yankedRomSize; gb->yankedRomSize = 0; @@ -527,15 +454,109 @@ void GBReset(struct LR35902Core* cpu) { GBMemoryReset(gb); GBVideoReset(&gb->video); GBTimerReset(&gb->timer); - mTimingSchedule(&gb->timing, &gb->timer.event, nextDiv); + if (!gb->biosVf) { + GBSkipBIOS(gb); + } else { + mTimingSchedule(&gb->timing, &gb->timer.event, 0); + } GBIOReset(gb); GBAudioReset(&gb->audio); GBSIOReset(&gb->sio); + cpu->memory.setActiveRegion(cpu, cpu->pc); + GBSavedataUnmask(gb); } +void GBSkipBIOS(struct GB* gb) { + struct LR35902Core* cpu = gb->cpu; + int nextDiv = 0; + + switch (gb->model) { + case GB_MODEL_AUTODETECT: // Silence warnings + gb->model = GB_MODEL_DMG; + case GB_MODEL_DMG: + cpu->a = 1; + cpu->f.packed = 0xB0; + cpu->c = 0x13; + cpu->e = 0xD8; + cpu->h = 1; + cpu->l = 0x4D; + gb->timer.internalDiv = 0xABC; + nextDiv = 4; + break; + case GB_MODEL_SGB: + cpu->a = 1; + cpu->f.packed = 0x00; + cpu->c = 0x14; + cpu->e = 0x00; + cpu->h = 0xC0; + cpu->l = 0x60; + gb->timer.internalDiv = 0xABC; + nextDiv = 4; + break; + case GB_MODEL_MGB: + cpu->a = 0xFF; + cpu->f.packed = 0xB0; + cpu->c = 0x13; + cpu->e = 0xD8; + cpu->h = 1; + cpu->l = 0x4D; + gb->timer.internalDiv = 0xABC; + nextDiv = 4; + break; + case GB_MODEL_SGB2: + cpu->a = 0xFF; + cpu->f.packed = 0x00; + cpu->c = 0x14; + cpu->e = 0x00; + cpu->h = 0xC0; + cpu->l = 0x60; + gb->timer.internalDiv = 0xABC; + nextDiv = 4; + break; + case GB_MODEL_AGB: + cpu->a = 0x11; + cpu->b = 1; + cpu->f.packed = 0x00; + cpu->c = 0; + cpu->e = 0x08; + cpu->h = 0; + cpu->l = 0x7C; + gb->timer.internalDiv = 0x1EA; + nextDiv = 0xC; + break; + case GB_MODEL_CGB: + cpu->a = 0x11; + cpu->f.packed = 0x80; + cpu->c = 0; + cpu->e = 0x08; + cpu->h = 0; + cpu->l = 0x7C; + gb->timer.internalDiv = 0x1EA; + nextDiv = 0xC; + break; + } + + cpu->sp = 0xFFFE; + cpu->pc = 0x100; + + mTimingDeschedule(&gb->timing, &gb->timer.event); + mTimingSchedule(&gb->timing, &gb->timer.event, 0); + + if (gb->biosVf) { + GBUnmapBIOS(gb); + } +} + +void GBUnmapBIOS(struct GB* gb) { + if (gb->memory.romBase < gb->memory.rom || gb->memory.romBase > &gb->memory.rom[gb->memory.romSize - 1]) { + free(gb->memory.romBase); + gb->memory.romBase = gb->memory.rom; + } +} + void GBDetectModel(struct GB* gb) { if (gb->model != GB_MODEL_AUTODETECT) { return; @@ -677,9 +698,12 @@ static void _enableInterrupts(struct mTiming* timing, void* user, uint32_t cycle } void GBHalt(struct LR35902Core* cpu) { - if (!cpu->irqPending) { + struct GB* gb = (struct GB*) cpu->master; + if (!(gb->memory.ie & gb->memory.io[REG_IF])) { cpu->cycles = cpu->nextEvent; cpu->halted = true; + } else if (gb->model < GB_MODEL_CGB) { + mLOG(GB, STUB, "Unimplemented HALT bug"); } } @@ -698,7 +722,7 @@ void GBStop(struct LR35902Core* cpu) { if (cpu->components && cpu->components[CPU_COMPONENT_DEBUGGER]) { struct mDebuggerEntryInfo info = { .address = cpu->pc - 1, - .opcode = 0x1000 | cpu->bus + .type.bp.opcode = 0x1000 | cpu->bus }; mDebuggerEnter((struct mDebugger*) cpu->components[CPU_COMPONENT_DEBUGGER], DEBUGGER_ENTER_ILLEGAL_OP, &info); } @@ -717,7 +741,7 @@ void GBIllegal(struct LR35902Core* cpu) { if (cpu->components && cpu->components[CPU_COMPONENT_DEBUGGER]) { struct mDebuggerEntryInfo info = { .address = cpu->pc, - .opcode = cpu->bus + .type.bp.opcode = cpu->bus }; mDebuggerEnter((struct mDebugger*) cpu->components[CPU_COMPONENT_DEBUGGER], DEBUGGER_ENTER_ILLEGAL_OP, &info); } diff --git a/src/gb/io.c b/src/gb/io.c index 2db5d8c6e..c6e065b95 100644 --- a/src/gb/io.c +++ b/src/gb/io.c @@ -101,7 +101,6 @@ static const uint8_t _registerMask[] = { [REG_BCPS] = 0x40, [REG_UNK6C] = 0xFE, [REG_SVBK] = 0xF8, - [REG_UNK75] = 0x8F, [REG_IE] = 0xE0, }; @@ -174,6 +173,7 @@ void GBIOReset(struct GB* gb) { GBIOWrite(gb, REG_WY, 0x00); GBIOWrite(gb, REG_WX, 0x00); if (gb->model >= GB_MODEL_CGB) { + GBIOWrite(gb, REG_UNK4C, 0); GBIOWrite(gb, REG_JOYP, 0xFF); GBIOWrite(gb, REG_VBK, 0); GBIOWrite(gb, REG_BCPS, 0); @@ -392,7 +392,7 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) { return; case REG_LCDC: // TODO: handle GBC differences - GBVideoProcessDots(&gb->video); + GBVideoProcessDots(&gb->video, 0); value = gb->video.renderer->writeVideoRegister(gb->video.renderer, address, value); GBVideoWriteLCDC(&gb->video, value); break; @@ -406,13 +406,13 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) { case REG_SCX: case REG_WY: case REG_WX: - GBVideoProcessDots(&gb->video); + GBVideoProcessDots(&gb->video, 0); value = gb->video.renderer->writeVideoRegister(gb->video.renderer, address, value); break; case REG_BGP: case REG_OBP0: case REG_OBP1: - GBVideoProcessDots(&gb->video); + GBVideoProcessDots(&gb->video, 0); GBVideoWritePalette(&gb->video, address, value); break; case REG_STAT: @@ -420,11 +420,8 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) { value = gb->video.stat; break; case 0x50: - if (gb->memory.romBase < gb->memory.rom || gb->memory.romBase > &gb->memory.rom[gb->memory.romSize - 1]) { - free(gb->memory.romBase); - gb->memory.romBase = gb->memory.rom; - } - if (gb->model >= GB_MODEL_CGB && gb->memory.io[0x6C]) { + GBUnmapBIOS(gb); + if (gb->model >= GB_MODEL_CGB && gb->memory.io[REG_UNK4C] < 0x80) { gb->model = GB_MODEL_DMG; GBVideoDisableCGB(&gb->video); } @@ -436,6 +433,8 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) { default: if (gb->model >= GB_MODEL_CGB) { switch (address) { + case REG_UNK4C: + break; case REG_KEY1: value &= 0x1; value |= gb->memory.io[address] & 0x80; @@ -450,8 +449,7 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) { // Handled transparently by the registers break; case REG_HDMA5: - GBMemoryWriteHDMA5(gb, value); - value &= 0x7F; + value = GBMemoryWriteHDMA5(gb, value); break; case REG_BCPS: gb->video.bcpIndex = value & 0x3F; @@ -459,7 +457,7 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) { gb->memory.io[REG_BCPD] = gb->video.palette[gb->video.bcpIndex >> 1] >> (8 * (gb->video.bcpIndex & 1)); break; case REG_BCPD: - GBVideoProcessDots(&gb->video); + GBVideoProcessDots(&gb->video, 0); GBVideoWritePalette(&gb->video, address, value); return; case REG_OCPS: @@ -468,7 +466,7 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) { gb->memory.io[REG_OCPD] = gb->video.palette[8 * 4 + (gb->video.ocpIndex >> 1)] >> (8 * (gb->video.ocpIndex & 1)); break; case REG_OCPD: - GBVideoProcessDots(&gb->video); + GBVideoProcessDots(&gb->video, 0); GBVideoWritePalette(&gb->video, address, value); return; case REG_SVBK: diff --git a/src/gb/mbc.c b/src/gb/mbc.c index 83ac2f2fc..16d3e01f8 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -31,6 +31,7 @@ static void _GBHuC3(struct GB*, uint16_t address, uint8_t value); static void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value); static void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value); +static uint8_t _GBMBC2Read(struct GBMemory*, uint16_t address); static uint8_t _GBMBC7Read(struct GBMemory*, uint16_t address); static void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value); @@ -45,9 +46,6 @@ void GBMBCSwitchBank(struct GB* gb, int bank) { mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank); bankStart &= (gb->memory.romSize - 1); bank = bankStart / GB_SIZE_CART_BANK0; - if (!bank) { - ++bank; - } } gb->memory.romBank = &gb->memory.rom[bankStart]; gb->memory.currentBank = bank; @@ -217,7 +215,8 @@ void GBMBCInit(struct GB* gb) { break; case GB_MBC2: gb->memory.mbcWrite = _GBMBC2; - gb->sramSize = 0x200; + gb->memory.mbcRead = _GBMBC2Read; + gb->sramSize = 0x100; break; case GB_MBC3: gb->memory.mbcWrite = _GBMBC3; @@ -399,6 +398,7 @@ void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) { void _GBMBC2(struct GB* gb, uint16_t address, uint8_t value) { struct GBMemory* memory = &gb->memory; + int shift = (address & 1) * 4; int bank = value & 0xF; switch (address >> 13) { case 0x0: @@ -408,7 +408,6 @@ void _GBMBC2(struct GB* gb, uint16_t address, uint8_t value) { break; case 0xA: memory->sramAccess = true; - GBMBCSwitchSramBank(gb, memory->sramCurrentBank); break; default: // TODO @@ -422,6 +421,13 @@ void _GBMBC2(struct GB* gb, uint16_t address, uint8_t value) { } GBMBCSwitchBank(gb, bank); break; + case 0x5: + if (!memory->sramAccess) { + return; + } + address &= 0x1FF; + memory->sramBank[(address >> 1)] &= 0xF0 >> shift; + memory->sramBank[(address >> 1)] |= (value & 0xF) << shift; default: // TODO mLOG(GB_MBC, STUB, "MBC2 unknown address: %04X:%02X", address, value); @@ -429,6 +435,12 @@ void _GBMBC2(struct GB* gb, uint16_t address, uint8_t value) { } } +static uint8_t _GBMBC2Read(struct GBMemory* memory, uint16_t address) { + address &= 0x1FF; + int shift = (address & 1) * 4; + return (memory->sramBank[(address >> 1)] >> shift) | 0xF0; +} + void _GBMBC3(struct GB* gb, uint16_t address, uint8_t value) { struct GBMemory* memory = &gb->memory; int bank = value & 0x7F; diff --git a/src/gb/memory.c b/src/gb/memory.c index 25b562146..1dae0b988 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -294,7 +294,7 @@ void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) { case GB_REGION_EXTERNAL_RAM + 1: if (memory->rtcAccess) { memory->rtcRegs[memory->activeRtcReg] = value; - } else if (memory->sramAccess && memory->sram) { + } else if (memory->sramAccess && memory->sram && memory->mbcType != GB_MBC2) { memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)] = value; } else { memory->mbcWrite(gb, address, value); @@ -454,7 +454,7 @@ void GBMemoryDMA(struct GB* gb, uint16_t base) { gb->memory.dmaRemaining = 0xA0; } -void GBMemoryWriteHDMA5(struct GB* gb, uint8_t value) { +uint8_t GBMemoryWriteHDMA5(struct GB* gb, uint8_t value) { gb->memory.hdmaSource = gb->memory.io[REG_HDMA1] << 8; gb->memory.hdmaSource |= gb->memory.io[REG_HDMA2]; gb->memory.hdmaDest = gb->memory.io[REG_HDMA3] << 8; @@ -462,7 +462,7 @@ void GBMemoryWriteHDMA5(struct GB* gb, uint8_t value) { gb->memory.hdmaSource &= 0xFFF0; if (gb->memory.hdmaSource >= 0x8000 && gb->memory.hdmaSource < 0xA000) { mLOG(GB_MEM, GAME_ERROR, "Invalid HDMA source: %04X", gb->memory.hdmaSource); - return; + return value | 0x80; } gb->memory.hdmaDest &= 0x1FF0; gb->memory.hdmaDest |= 0x8000; @@ -476,7 +476,10 @@ void GBMemoryWriteHDMA5(struct GB* gb, uint8_t value) { } gb->cpuBlocked = true; mTimingSchedule(&gb->timing, &gb->memory.hdmaEvent, 0); + } else if (gb->memory.isHdma && !GBRegisterLCDCIsEnable(gb->memory.io[REG_LCDC])) { + return 0x80 | ((value + 1) & 0x7F); } + return value & 0x7F; } void _GBMemoryDMAService(struct mTiming* timing, void* context, uint32_t cyclesLate) { diff --git a/src/gb/renderers/software.c b/src/gb/renderers/software.c index 0c3526960..5214b3d7f 100644 --- a/src/gb/renderers/software.c +++ b/src/gb/renderers/software.c @@ -146,6 +146,10 @@ static void _parseAttrBlock(struct GBVideoSoftwareRenderer* renderer, int start) } } +static bool _inWindow(struct GBVideoSoftwareRenderer* renderer) { + return GBRegisterLCDCIsWindow(renderer->lcdc) && GB_VIDEO_HORIZONTAL_PIXELS + 7 > renderer->wx; +} + void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer* renderer) { renderer->d.init = GBVideoSoftwareRendererInit; renderer->d.deinit = GBVideoSoftwareRendererDeinit; @@ -174,6 +178,8 @@ static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum G softwareRenderer->scx = 0; softwareRenderer->wy = 0; softwareRenderer->currentWy = 0; + softwareRenderer->lastY = 0; + softwareRenderer->hasWindow = false; softwareRenderer->wx = 0; softwareRenderer->model = model; softwareRenderer->sgbTransfer = 0; @@ -193,14 +199,34 @@ static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer) { UNUSED(softwareRenderer); } +static void GBVideoSoftwareRendererUpdateWindow(struct GBVideoSoftwareRenderer* renderer, bool before, bool after) { + if (renderer->lastY >= GB_VIDEO_VERTICAL_PIXELS || after == before) { + return; + } + if (renderer->lastY >= renderer->wy) { + if (!after) { + renderer->currentWy -= renderer->lastY; + renderer->hasWindow = true; + } else { + if (!renderer->hasWindow) { + renderer->currentWy = renderer->lastY + 1 - renderer->wy; + } else { + renderer->currentWy += renderer->lastY; + } + } + } +} + static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value) { struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer; if (renderer->cache) { GBVideoCacheWriteVideoRegister(renderer->cache, address, value); } + bool wasWindow = _inWindow(softwareRenderer); switch (address) { case REG_LCDC: softwareRenderer->lcdc = value; + GBVideoSoftwareRendererUpdateWindow(softwareRenderer, wasWindow, _inWindow(softwareRenderer)); break; case REG_SCY: softwareRenderer->scy = value; @@ -210,9 +236,11 @@ static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* break; case REG_WY: softwareRenderer->wy = value; + GBVideoSoftwareRendererUpdateWindow(softwareRenderer, wasWindow, _inWindow(softwareRenderer)); break; case REG_WX: softwareRenderer->wx = value; + GBVideoSoftwareRendererUpdateWindow(softwareRenderer, wasWindow, _inWindow(softwareRenderer)); break; case REG_BGP: softwareRenderer->lookup[0] = value & 3; @@ -274,6 +302,51 @@ static void GBVideoSoftwareRendererWriteSGBPacket(struct GBVideoRenderer* render if (i < 16 && softwareRenderer->sgbDataSets) { memcpy(softwareRenderer->sgbPartialDataSet, &softwareRenderer->sgbPacket[i], 16 - i); } + break; + case SGB_ATTR_CHR: + if (softwareRenderer->sgbPacketId == 1) { + softwareRenderer->sgbAttrX = softwareRenderer->sgbPacket[1]; + softwareRenderer->sgbAttrY = softwareRenderer->sgbPacket[2]; + if (softwareRenderer->sgbAttrX >= GB_VIDEO_HORIZONTAL_PIXELS / 8) { + softwareRenderer->sgbAttrX = 0; + } + if (softwareRenderer->sgbAttrY >= GB_VIDEO_VERTICAL_PIXELS / 8) { + softwareRenderer->sgbAttrY = 0; + } + softwareRenderer->sgbDataSets = softwareRenderer->sgbPacket[3]; + softwareRenderer->sgbDataSets |= softwareRenderer->sgbPacket[4] << 8; + softwareRenderer->sgbAttrDirection = softwareRenderer->sgbPacket[5]; + i = 6; + } else { + i = 0; + } + for (; i < 16 && softwareRenderer->sgbDataSets; ++i) { + int j; + for (j = 0; j < 4 && softwareRenderer->sgbDataSets; ++j, --softwareRenderer->sgbDataSets) { + uint8_t p = softwareRenderer->sgbPacket[i] >> (6 - j * 2); + _setAttribute(renderer->sgbAttributes, softwareRenderer->sgbAttrX, softwareRenderer->sgbAttrY, p & 3); + if (softwareRenderer->sgbAttrDirection) { + ++softwareRenderer->sgbAttrY; + if (softwareRenderer->sgbAttrY >= GB_VIDEO_VERTICAL_PIXELS / 8) { + softwareRenderer->sgbAttrY = 0; + ++softwareRenderer->sgbAttrX; + } + if (softwareRenderer->sgbAttrX >= GB_VIDEO_HORIZONTAL_PIXELS / 8) { + softwareRenderer->sgbAttrX = 0; + } + } else { + ++softwareRenderer->sgbAttrX; + if (softwareRenderer->sgbAttrX >= GB_VIDEO_HORIZONTAL_PIXELS / 8) { + softwareRenderer->sgbAttrX = 0; + ++softwareRenderer->sgbAttrY; + } + if (softwareRenderer->sgbAttrY >= GB_VIDEO_VERTICAL_PIXELS / 8) { + softwareRenderer->sgbAttrY = 0; + } + } + } + } + break; case SGB_ATRC_EN: if (softwareRenderer->sgbBorders) { @@ -306,6 +379,7 @@ static void GBVideoSoftwareRendererWriteOAM(struct GBVideoRenderer* renderer, ui static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax) { struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer; + softwareRenderer->lastY = y; uint8_t* maps = &softwareRenderer->d.vram[GB_BASE_MAP]; if (GBRegisterLCDCIsTileMap(softwareRenderer->lcdc)) { maps += GB_SIZE_MAP; @@ -314,7 +388,8 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i memset(&softwareRenderer->row[startX], 0, endX - startX); } if (GBRegisterLCDCIsBgEnable(softwareRenderer->lcdc) || softwareRenderer->model >= GB_MODEL_CGB) { - if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && softwareRenderer->wy <= y && endX >= softwareRenderer->wx - 7) { + int wy = softwareRenderer->wy + softwareRenderer->currentWy; + if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && wy <= y && endX >= softwareRenderer->wx - 7) { if (softwareRenderer->wx - 7 > 0 && !softwareRenderer->d.disableBG) { GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, softwareRenderer->wx - 7, softwareRenderer->scx, softwareRenderer->scy + y); } @@ -324,7 +399,7 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i maps += GB_SIZE_MAP; } if (!softwareRenderer->d.disableWIN) { - GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, softwareRenderer->wx - 7, endX, 7 - softwareRenderer->wx, softwareRenderer->currentWy); + GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, softwareRenderer->wx - 7, endX, 7 - softwareRenderer->wx, y - wy); } } else if (!softwareRenderer->d.disableBG) { GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, endX, softwareRenderer->scx, softwareRenderer->scy + y); @@ -428,9 +503,6 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i static void GBVideoSoftwareRendererFinishScanline(struct GBVideoRenderer* renderer, int y) { struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer; - if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && softwareRenderer->wy <= y && softwareRenderer->wx - 7 < GB_VIDEO_HORIZONTAL_PIXELS) { - ++softwareRenderer->currentWy; - } if (softwareRenderer->sgbTransfer == 1) { size_t offset = 2 * ((y & 7) + (y >> 3) * GB_VIDEO_HORIZONTAL_PIXELS); if (offset >= 0x1000) { @@ -520,7 +592,9 @@ static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer) break; } } + softwareRenderer->lastY = GB_VIDEO_VERTICAL_PIXELS; softwareRenderer->currentWy = 0; + softwareRenderer->hasWindow = false; } static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer* renderer, uint8_t* maps, int startX, int endX, int sx, int sy) { diff --git a/src/gb/serialize.c b/src/gb/serialize.c index c37b3872b..4c54c3e59 100644 --- a/src/gb/serialize.c +++ b/src/gb/serialize.c @@ -9,6 +9,8 @@ #include #include +#include + mLOG_DEFINE_CATEGORY(GB_STATE, "GB Savestate", "gb.serialize"); const uint32_t GB_SAVESTATE_MAGIC = 0x00400000; @@ -171,6 +173,7 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) { mTimingSchedule(&gb->timing, &gb->eiPending, when); } + enum GBModel oldModel = gb->model; gb->model = state->model; if (gb->model < GB_MODEL_CGB) { @@ -179,6 +182,10 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) { gb->audio.style = GB_AUDIO_CGB; } + if (gb->model != GB_MODEL_SGB || oldModel != GB_MODEL_SGB) { + gb->video.sgbBorders = false; + } + GBMemoryDeserialize(gb, state); GBVideoDeserialize(&gb->video, state); GBIODeserialize(gb, state); @@ -237,22 +244,28 @@ void GBSGBDeserialize(struct GB* gb, const struct GBSerializedState* state) { memcpy(gb->sgbPacket, state->sgb.packet, sizeof(state->sgb.packet)); - if (gb->video.renderer->sgbCharRam) { - memcpy(gb->video.renderer->sgbCharRam, state->sgb.charRam, sizeof(state->sgb.charRam)); + if (!gb->video.renderer->sgbCharRam) { + gb->video.renderer->sgbCharRam = anonymousMemoryMap(SGB_SIZE_CHAR_RAM); } - if (gb->video.renderer->sgbMapRam) { - memcpy(gb->video.renderer->sgbMapRam, state->sgb.mapRam, sizeof(state->sgb.mapRam)); + if (!gb->video.renderer->sgbMapRam) { + gb->video.renderer->sgbMapRam = anonymousMemoryMap(SGB_SIZE_MAP_RAM); } - if (gb->video.renderer->sgbPalRam) { - memcpy(gb->video.renderer->sgbPalRam, state->sgb.palRam, sizeof(state->sgb.palRam)); + if (!gb->video.renderer->sgbPalRam) { + gb->video.renderer->sgbPalRam = anonymousMemoryMap(SGB_SIZE_PAL_RAM); } - if (gb->video.renderer->sgbAttributeFiles) { - memcpy(gb->video.renderer->sgbAttributeFiles, state->sgb.atfRam, sizeof(state->sgb.atfRam)); + if (!gb->video.renderer->sgbAttributeFiles) { + gb->video.renderer->sgbAttributeFiles = anonymousMemoryMap(SGB_SIZE_ATF_RAM); } - if (gb->video.renderer->sgbAttributes) { - memcpy(gb->video.renderer->sgbAttributes, state->sgb.attributes, sizeof(state->sgb.attributes)); + if (!gb->video.renderer->sgbAttributes) { + gb->video.renderer->sgbAttributes = malloc(90 * 45); } + memcpy(gb->video.renderer->sgbCharRam, state->sgb.charRam, sizeof(state->sgb.charRam)); + memcpy(gb->video.renderer->sgbMapRam, state->sgb.mapRam, sizeof(state->sgb.mapRam)); + memcpy(gb->video.renderer->sgbPalRam, state->sgb.palRam, sizeof(state->sgb.palRam)); + memcpy(gb->video.renderer->sgbAttributeFiles, state->sgb.atfRam, sizeof(state->sgb.atfRam)); + memcpy(gb->video.renderer->sgbAttributes, state->sgb.attributes, sizeof(state->sgb.attributes)); + GBVideoWriteSGBPacket(&gb->video, (uint8_t[16]) { (SGB_ATRC_EN << 3) | 1, 0 }); GBVideoWriteSGBPacket(&gb->video, gb->sgbPacket); } diff --git a/src/gb/sio/printer.c b/src/gb/sio/printer.c index 4bd1bf2ea..dd2f4ecc9 100644 --- a/src/gb/sio/printer.c +++ b/src/gb/sio/printer.c @@ -69,8 +69,8 @@ static void _processByte(struct GBPrinter* printer) { static uint8_t GBPrinterWriteSC(struct GBSIODriver* driver, uint8_t value) { struct GBPrinter* printer = (struct GBPrinter*) driver; if ((value & 0x81) == 0x81) { - switch (printer->next) { driver->p->pendingSB = 0; + switch (printer->next) { case GB_PRINTER_BYTE_MAGIC_0: if (printer->byte == 0x88) { printer->next = GB_PRINTER_BYTE_MAGIC_1; @@ -183,51 +183,54 @@ static uint8_t GBPrinterWriteSC(struct GBSIODriver* driver, uint8_t value) { } break; case GB_PRINTER_COMMAND_STATUS: - if (!printer->printWait) { - printer->status &= ~GB_PRINTER_STATUS_READY; - printer->status |= GB_PRINTER_STATUS_PRINTING | GB_PRINTER_STATUS_PRINT_REQ; - if (printer->print) { - size_t y; - for (y = 0; y < printer->currentIndex / (2 * GB_VIDEO_HORIZONTAL_PIXELS); ++y) { - uint8_t lineBuffer[GB_VIDEO_HORIZONTAL_PIXELS * 2]; - uint8_t* buffer = &printer->buffer[sizeof(lineBuffer) * y]; - size_t i; - for (i = 0; i < sizeof(lineBuffer); i += 2) { - uint8_t ilo = buffer[i + 0x0]; - uint8_t ihi = buffer[i + 0x1]; - uint8_t olo = 0; - uint8_t ohi = 0; - olo |= ((ihi & 0x80) >> 0) | ((ilo & 0x80) >> 1); - olo |= ((ihi & 0x40) >> 1) | ((ilo & 0x40) >> 2); - olo |= ((ihi & 0x20) >> 2) | ((ilo & 0x20) >> 3); - olo |= ((ihi & 0x10) >> 3) | ((ilo & 0x10) >> 4); - ohi |= ((ihi & 0x08) << 4) | ((ilo & 0x08) << 3); - ohi |= ((ihi & 0x04) << 3) | ((ilo & 0x04) << 2); - ohi |= ((ihi & 0x02) << 2) | ((ilo & 0x02) << 1); - ohi |= ((ihi & 0x01) << 1) | ((ilo & 0x01) << 0); - lineBuffer[(((i >> 1) & 0x7) * GB_VIDEO_HORIZONTAL_PIXELS / 4) + ((i >> 3) & ~1)] = olo; - lineBuffer[(((i >> 1) & 0x7) * GB_VIDEO_HORIZONTAL_PIXELS / 4) + ((i >> 3) | 1)] = ohi; - } - memcpy(buffer, lineBuffer, sizeof(lineBuffer)); - } - printer->print(printer, printer->currentIndex * 4 / GB_VIDEO_HORIZONTAL_PIXELS, printer->buffer); - } - } - if (printer->printWait >= 0) { - --printer->printWait; - } default: break; } + driver->p->pendingSB = printer->status; printer->next = GB_PRINTER_BYTE_MAGIC_0; break; } + + if (!printer->printWait) { + printer->status &= ~GB_PRINTER_STATUS_READY; + printer->status |= GB_PRINTER_STATUS_PRINTING | GB_PRINTER_STATUS_PRINT_REQ; + if (printer->print) { + size_t y; + for (y = 0; y < printer->currentIndex / (2 * GB_VIDEO_HORIZONTAL_PIXELS); ++y) { + uint8_t lineBuffer[GB_VIDEO_HORIZONTAL_PIXELS * 2]; + uint8_t* buffer = &printer->buffer[sizeof(lineBuffer) * y]; + size_t i; + for (i = 0; i < sizeof(lineBuffer); i += 2) { + uint8_t ilo = buffer[i + 0x0]; + uint8_t ihi = buffer[i + 0x1]; + uint8_t olo = 0; + uint8_t ohi = 0; + olo |= ((ihi & 0x80) >> 0) | ((ilo & 0x80) >> 1); + olo |= ((ihi & 0x40) >> 1) | ((ilo & 0x40) >> 2); + olo |= ((ihi & 0x20) >> 2) | ((ilo & 0x20) >> 3); + olo |= ((ihi & 0x10) >> 3) | ((ilo & 0x10) >> 4); + ohi |= ((ihi & 0x08) << 4) | ((ilo & 0x08) << 3); + ohi |= ((ihi & 0x04) << 3) | ((ilo & 0x04) << 2); + ohi |= ((ihi & 0x02) << 2) | ((ilo & 0x02) << 1); + ohi |= ((ihi & 0x01) << 1) | ((ilo & 0x01) << 0); + lineBuffer[(((i >> 1) & 0x7) * GB_VIDEO_HORIZONTAL_PIXELS / 4) + ((i >> 3) & ~1)] = olo; + lineBuffer[(((i >> 1) & 0x7) * GB_VIDEO_HORIZONTAL_PIXELS / 4) + ((i >> 3) | 1)] = ohi; + } + memcpy(buffer, lineBuffer, sizeof(lineBuffer)); + } + printer->print(printer, printer->currentIndex * 4 / GB_VIDEO_HORIZONTAL_PIXELS, printer->buffer); + } + printer->printWait = -1; + } else if (printer->printWait > 0) { + --printer->printWait; + } + printer->byte = 0; } return value; } void GBPrinterDonePrinting(struct GBPrinter* printer) { - printer->status &= ~GB_PRINTER_STATUS_PRINTING; + printer->status &= ~(GB_PRINTER_STATUS_PRINTING | GB_PRINTER_STATUS_PRINT_REQ); } diff --git a/src/gb/video.c b/src/gb/video.c index 4478b079e..aafff0714 100644 --- a/src/gb/video.c +++ b/src/gb/video.c @@ -56,7 +56,7 @@ void GBVideoInit(struct GBVideo* video) { video->renderer = &dummyRenderer; video->renderer->cache = NULL; video->renderer->sgbRenderMode = 0; - video->vram = 0; + video->vram = anonymousMemoryMap(GB_SIZE_VRAM); video->frameskip = 0; video->modeEvent.context = video; @@ -99,10 +99,6 @@ void GBVideoReset(struct GBVideo* video) { video->frameCounter = 0; video->frameskipCounter = 0; - if (video->vram) { - mappedMemoryFree(video->vram, GB_SIZE_VRAM); - } - video->vram = anonymousMemoryMap(GB_SIZE_VRAM); GBVideoSwitchBank(video, 0); video->renderer->vram = video->vram; memset(&video->oam, 0, sizeof(video->oam)); @@ -310,7 +306,7 @@ void _endMode2(struct mTiming* timing, void* context, uint32_t cyclesLate) { void _endMode3(struct mTiming* timing, void* context, uint32_t cyclesLate) { struct GBVideo* video = context; - GBVideoProcessDots(video); + GBVideoProcessDots(video, cyclesLate); if (video->ly < GB_VIDEO_VERTICAL_PIXELS && video->p->memory.isHdma && video->p->memory.io[REG_HDMA5] != 0xFF) { video->p->memory.hdmaRemaining = 0x10; video->p->cpuBlocked = true; @@ -400,12 +396,12 @@ static void _cleanOAM(struct GBVideo* video, int y) { video->objMax = o; } -void GBVideoProcessDots(struct GBVideo* video) { +void GBVideoProcessDots(struct GBVideo* video, uint32_t cyclesLate) { if (video->mode != 3) { return; } int oldX = video->x; - video->x = (video->p->timing.masterCycles - video->dotClock + video->p->cpu->cycles) >> video->p->doubleSpeed; + video->x = (mTimingCurrentTime(&video->p->timing) - video->dotClock - cyclesLate) >> video->p->doubleSpeed; if (video->x > GB_VIDEO_HORIZONTAL_PIXELS) { video->x = GB_VIDEO_HORIZONTAL_PIXELS; } else if (video->x < 0) { @@ -455,7 +451,10 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) { void GBVideoWriteSTAT(struct GBVideo* video, GBRegisterSTAT value) { GBRegisterSTAT oldStat = video->stat; video->stat = (video->stat & 0x7) | (value & 0x78); - if (video->p->model < GB_MODEL_CGB && !_statIRQAsserted(video, oldStat) && video->mode < 3) { + if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) || video->p->model >= GB_MODEL_CGB) { + return; + } + if (!_statIRQAsserted(video, oldStat) && video->mode < 3) { // TODO: variable for the IRQ line value? video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); GBUpdateIRQs(video->p); @@ -595,39 +594,39 @@ void GBVideoWriteSGBPacket(struct GBVideo* video, uint8_t* data) { video->palette[2] = data[5] | (data[6] << 8); video->palette[3] = data[7] | (data[8] << 8); - video->palette[16] = data[1] | (data[2] << 8); - video->palette[17] = data[9] | (data[10] << 8); - video->palette[18] = data[11] | (data[12] << 8); - video->palette[19] = data[13] | (data[14] << 8); + video->palette[4] = data[1] | (data[2] << 8); + video->palette[5] = data[9] | (data[10] << 8); + video->palette[6] = data[11] | (data[12] << 8); + video->palette[7] = data[13] | (data[14] << 8); - video->palette[32] = data[1] | (data[2] << 8); - video->palette[48] = data[1] | (data[2] << 8); + video->palette[8] = data[1] | (data[2] << 8); + video->palette[12] = data[1] | (data[2] << 8); video->renderer->writePalette(video->renderer, 0, video->palette[0]); video->renderer->writePalette(video->renderer, 1, video->palette[1]); video->renderer->writePalette(video->renderer, 2, video->palette[2]); video->renderer->writePalette(video->renderer, 3, video->palette[3]); - video->renderer->writePalette(video->renderer, 16, video->palette[0]); - video->renderer->writePalette(video->renderer, 17, video->palette[17]); - video->renderer->writePalette(video->renderer, 18, video->palette[18]); - video->renderer->writePalette(video->renderer, 19, video->palette[19]); - video->renderer->writePalette(video->renderer, 32, video->palette[0]); - video->renderer->writePalette(video->renderer, 48, video->palette[0]); + video->renderer->writePalette(video->renderer, 4, video->palette[0]); + video->renderer->writePalette(video->renderer, 5, video->palette[5]); + video->renderer->writePalette(video->renderer, 6, video->palette[6]); + video->renderer->writePalette(video->renderer, 7, video->palette[7]); + video->renderer->writePalette(video->renderer, 8, video->palette[0]); + video->renderer->writePalette(video->renderer, 12, video->palette[0]); break; case SGB_PAL23: - video->palette[33] = data[3] | (data[4] << 8); - video->palette[34] = data[5] | (data[6] << 8); - video->palette[35] = data[7] | (data[8] << 8); + video->palette[9] = data[3] | (data[4] << 8); + video->palette[10] = data[5] | (data[6] << 8); + video->palette[11] = data[7] | (data[8] << 8); - video->palette[49] = data[9] | (data[10] << 8); - video->palette[50] = data[11] | (data[12] << 8); - video->palette[51] = data[13] | (data[14] << 8); - video->renderer->writePalette(video->renderer, 33, video->palette[33]); - video->renderer->writePalette(video->renderer, 34, video->palette[34]); - video->renderer->writePalette(video->renderer, 35, video->palette[35]); - video->renderer->writePalette(video->renderer, 49, video->palette[49]); - video->renderer->writePalette(video->renderer, 50, video->palette[50]); - video->renderer->writePalette(video->renderer, 51, video->palette[51]); + video->palette[13] = data[9] | (data[10] << 8); + video->palette[14] = data[11] | (data[12] << 8); + video->palette[15] = data[13] | (data[14] << 8); + video->renderer->writePalette(video->renderer, 9, video->palette[9]); + video->renderer->writePalette(video->renderer, 10, video->palette[10]); + video->renderer->writePalette(video->renderer, 11, video->palette[11]); + video->renderer->writePalette(video->renderer, 13, video->palette[13]); + video->renderer->writePalette(video->renderer, 14, video->palette[14]); + video->renderer->writePalette(video->renderer, 15, video->palette[15]); break; case SGB_PAL03: video->palette[0] = data[1] | (data[2] << 8); @@ -635,38 +634,38 @@ void GBVideoWriteSGBPacket(struct GBVideo* video, uint8_t* data) { video->palette[2] = data[5] | (data[6] << 8); video->palette[3] = data[7] | (data[8] << 8); - video->palette[16] = data[1] | (data[2] << 8); - video->palette[32] = data[1] | (data[2] << 8); + video->palette[4] = data[1] | (data[2] << 8); + video->palette[8] = data[1] | (data[2] << 8); - video->palette[48] = data[1] | (data[2] << 8); - video->palette[49] = data[9] | (data[10] << 8); - video->palette[50] = data[11] | (data[12] << 8); - video->palette[51] = data[13] | (data[14] << 8); + video->palette[12] = data[1] | (data[2] << 8); + video->palette[13] = data[9] | (data[10] << 8); + video->palette[14] = data[11] | (data[12] << 8); + video->palette[15] = data[13] | (data[14] << 8); video->renderer->writePalette(video->renderer, 0, video->palette[0]); video->renderer->writePalette(video->renderer, 1, video->palette[1]); video->renderer->writePalette(video->renderer, 2, video->palette[2]); video->renderer->writePalette(video->renderer, 3, video->palette[3]); - video->renderer->writePalette(video->renderer, 16, video->palette[0]); - video->renderer->writePalette(video->renderer, 32, video->palette[0]); - video->renderer->writePalette(video->renderer, 48, video->palette[0]); - video->renderer->writePalette(video->renderer, 49, video->palette[49]); - video->renderer->writePalette(video->renderer, 50, video->palette[50]); - video->renderer->writePalette(video->renderer, 51, video->palette[51]); + video->renderer->writePalette(video->renderer, 4, video->palette[0]); + video->renderer->writePalette(video->renderer, 8, video->palette[0]); + video->renderer->writePalette(video->renderer, 12, video->palette[0]); + video->renderer->writePalette(video->renderer, 13, video->palette[13]); + video->renderer->writePalette(video->renderer, 14, video->palette[14]); + video->renderer->writePalette(video->renderer, 15, video->palette[15]); break; case SGB_PAL12: - video->palette[17] = data[3] | (data[4] << 8); - video->palette[18] = data[5] | (data[6] << 8); - video->palette[19] = data[7] | (data[8] << 8); + video->palette[5] = data[3] | (data[4] << 8); + video->palette[6] = data[5] | (data[6] << 8); + video->palette[7] = data[7] | (data[8] << 8); - video->palette[33] = data[9] | (data[10] << 8); - video->palette[34] = data[11] | (data[12] << 8); - video->palette[35] = data[13] | (data[14] << 8); - video->renderer->writePalette(video->renderer, 17, video->palette[17]); - video->renderer->writePalette(video->renderer, 18, video->palette[18]); - video->renderer->writePalette(video->renderer, 19, video->palette[19]); - video->renderer->writePalette(video->renderer, 33, video->palette[33]); - video->renderer->writePalette(video->renderer, 34, video->palette[34]); - video->renderer->writePalette(video->renderer, 35, video->palette[35]); + video->palette[9] = data[9] | (data[10] << 8); + video->palette[10] = data[11] | (data[12] << 8); + video->palette[11] = data[13] | (data[14] << 8); + video->renderer->writePalette(video->renderer, 5, video->palette[5]); + video->renderer->writePalette(video->renderer, 6, video->palette[6]); + video->renderer->writePalette(video->renderer, 7, video->palette[7]); + video->renderer->writePalette(video->renderer, 9, video->palette[9]); + video->renderer->writePalette(video->renderer, 10, video->palette[10]); + video->renderer->writePalette(video->renderer, 11, video->palette[11]); break; case SGB_PAL_SET: for (i = 0; i < 4; ++i) { @@ -686,6 +685,7 @@ void GBVideoWriteSGBPacket(struct GBVideo* video, uint8_t* data) { } break; case SGB_ATTR_BLK: + case SGB_ATTR_CHR: case SGB_PAL_TRN: case SGB_ATRC_EN: case SGB_CHR_TRN: @@ -786,6 +786,7 @@ void GBVideoSerialize(const struct GBVideo* video, struct GBSerializedState* sta STORE_16LE(video->x, 0, &state->video.x); STORE_16LE(video->ly, 0, &state->video.ly); STORE_32LE(video->frameCounter, 0, &state->video.frameCounter); + STORE_32LE(video->dotClock, 0, &state->video.dotCounter); state->video.vramCurrentBank = video->vramCurrentBank; GBSerializedVideoFlags flags = 0; @@ -814,6 +815,7 @@ void GBVideoDeserialize(struct GBVideo* video, const struct GBSerializedState* s LOAD_16LE(video->x, 0, &state->video.x); LOAD_16LE(video->ly, 0, &state->video.ly); LOAD_32LE(video->frameCounter, 0, &state->video.frameCounter); + LOAD_32LE(video->dotClock, 0, &state->video.dotCounter); video->vramCurrentBank = state->video.vramCurrentBank; GBSerializedVideoFlags flags = state->video.flags; diff --git a/src/gba/audio.c b/src/gba/audio.c index be736e78b..630f6fe0c 100644 --- a/src/gba/audio.c +++ b/src/gba/audio.c @@ -111,7 +111,7 @@ void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA* mLOG(GBA_AUDIO, GAME_ERROR, "Invalid FIFO destination: 0x%08X", info->dest); return; } - info->reg = GBADMARegisterSetDestControl(info->reg, DMA_FIXED); + info->reg = GBADMARegisterSetDestControl(info->reg, GBA_DMA_FIXED); info->reg = GBADMARegisterSetWidth(info->reg, 1); } @@ -235,7 +235,7 @@ void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles) { } if (CircleBufferSize(&channel->fifo) <= 4 * sizeof(int32_t) && channel->dmaSource > 0) { struct GBADMA* dma = &audio->p->memory.dma[channel->dmaSource]; - if (GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_CUSTOM) { + if (GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_CUSTOM) { dma->when = mTimingCurrentTime(&audio->p->timing) - cycles; dma->nextCount = 4; GBADMASchedule(audio->p, channel->dmaSource, dma); @@ -260,7 +260,7 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) { struct GBAAudio* audio = user; int16_t sampleLeft = 0; int16_t sampleRight = 0; - int psgShift = 5 - audio->volume; + int psgShift = 4 - audio->volume; GBAudioSamplePSG(&audio->psg, &sampleLeft, &sampleRight); sampleLeft >>= psgShift; sampleRight >>= psgShift; diff --git a/src/gba/bios.c b/src/gba/bios.c index 65014898e..e14050184 100644 --- a/src/gba/bios.c +++ b/src/gba/bios.c @@ -333,6 +333,12 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { mLOG(GBA_BIOS, DEBUG, "SWI: %02X r0: %08X r1: %08X r2: %08X r3: %08X", immediate, cpu->gprs[0], cpu->gprs[1], cpu->gprs[2], cpu->gprs[3]); + switch (immediate) { + case 0xFA: + GBAPrintFlush(gba); + return; + } + if (gba->memory.fullBios) { ARMRaiseSWI(cpu); return; @@ -529,8 +535,10 @@ static void _unLz77(struct GBA* gba, int width) { source += 2; disp = dest - (block & 0x0FFF) - 1; bytes = (block >> 12) + 3; - while (bytes-- && remaining) { - --remaining; + while (bytes--) { + if (remaining) { + --remaining; + } if (width == 2) { byte = (int16_t) cpu->memory.load16(cpu, disp & ~1, 0); if (dest & 1) { diff --git a/src/gba/cheats.c b/src/gba/cheats.c index b17244c76..ec32924f3 100644 --- a/src/gba/cheats.c +++ b/src/gba/cheats.c @@ -274,7 +274,11 @@ bool GBACheatAddLine(struct mCheatSet* set, const char* line, int type) { static void GBACheatRefresh(struct mCheatSet* cheats, struct mCheatDevice* device) { struct GBACheatSet* gbaset = (struct GBACheatSet*) cheats; - _patchROM(device, gbaset); + if (cheats->enabled) { + _patchROM(device, gbaset); + } else { + _unpatchROM(device, gbaset); + } } static void GBACheatSetCopyProperties(struct mCheatSet* set, struct mCheatSet* oldSet) { diff --git a/src/gba/cheats/gameshark.c b/src/gba/cheats/gameshark.c index e95b23b54..d61ffbe6a 100644 --- a/src/gba/cheats/gameshark.c +++ b/src/gba/cheats/gameshark.c @@ -154,9 +154,32 @@ bool GBACheatAddGameSharkRaw(struct GBACheatSet* cheats, uint32_t op1, uint32_t cheats->romPatches[0].exists = true; return true; case GSA_BUTTON: - // TODO: Implement button - mLOG(CHEATS, STUB, "GameShark button unimplemented"); - return false; + switch (op1 & 0x00F00000) { + case 0x00100000: + cheat = mCheatListAppend(&cheats->d.list); + cheat->type = CHEAT_IF_BUTTON; + cheat->repeat = 1; + cheat->negativeRepeat = 0; + cheat = mCheatListAppend(&cheats->d.list); + cheat->type = CHEAT_ASSIGN; + cheat->width = 1; + cheat->address = op1 & 0x0F0FFFFF; + break; + case 0x00200000: + cheat = mCheatListAppend(&cheats->d.list); + cheat->type = CHEAT_IF_BUTTON; + cheat->repeat = 1; + cheat->negativeRepeat = 0; + cheat = mCheatListAppend(&cheats->d.list); + cheat->type = CHEAT_ASSIGN; + cheat->width = 2; + cheat->address = op1 & 0x0F0FFFFF; + break; + default: + mLOG(CHEATS, STUB, "GameShark button type unimplemented"); + return false; + } + break; case GSA_IF_EQ: if (op1 == 0xDEADFACE) { GBACheatReseedGameShark(cheats->gsaSeeds, op2, _gsa1T1, _gsa1T2); @@ -174,6 +197,7 @@ bool GBACheatAddGameSharkRaw(struct GBACheatSet* cheats, uint32_t op1, uint32_t cheat->address = op2 & 0x0FFFFFFF; cheat->operand = op1 & 0xFFFF; cheat->repeat = (op1 >> 16) & 0xFF; + cheat->negativeRepeat = 0; return true; case GSA_HOOK: if (cheats->hook) { @@ -190,6 +214,7 @@ bool GBACheatAddGameSharkRaw(struct GBACheatSet* cheats, uint32_t op1, uint32_t } cheat->operand = op2; cheat->repeat = 1; + cheat->negativeRepeat = 0; return true; } diff --git a/src/gba/cheats/parv3.c b/src/gba/cheats/parv3.c index 087294d0f..a49333568 100644 --- a/src/gba/cheats/parv3.c +++ b/src/gba/cheats/parv3.c @@ -132,49 +132,65 @@ static bool _addPAR3Cond(struct GBACheatSet* cheats, uint32_t op1, uint32_t op2) case PAR3_COND_UGT: cheat->type = CHEAT_IF_UGT; break; - case PAR3_COND_LAND: - cheat->type = CHEAT_IF_LAND; + case PAR3_COND_AND: + cheat->type = CHEAT_IF_AND; break; } return true; } static bool _addPAR3Special(struct GBACheatSet* cheats, uint32_t op2) { + int romPatch = -1; struct mCheat* cheat; switch (op2 & 0xFF000000) { case PAR3_OTHER_SLOWDOWN: // TODO: Slowdown + mLOG(CHEATS, STUB, "Unimplemented PARv3 slowdown"); return false; case PAR3_OTHER_BUTTON_1: + cheat = mCheatListAppend(&cheats->d.list); + cheat->type = CHEAT_IF_BUTTON; + cheat->repeat = 1; + cheat->negativeRepeat = 0; + cheat = mCheatListAppend(&cheats->d.list); + cheat->type = CHEAT_ASSIGN; + cheat->width = 1; + cheat->address = _parAddr(op2); + cheats->incompleteCheat = mCheatListIndex(&cheats->d.list, cheat); + break; case PAR3_OTHER_BUTTON_2: + cheat = mCheatListAppend(&cheats->d.list); + cheat->type = CHEAT_IF_BUTTON; + cheat->repeat = 1; + cheat->negativeRepeat = 0; + cheat = mCheatListAppend(&cheats->d.list); + cheat->type = CHEAT_ASSIGN; + cheat->width = 2; + cheat->address = _parAddr(op2); + cheats->incompleteCheat = mCheatListIndex(&cheats->d.list, cheat); + break; case PAR3_OTHER_BUTTON_4: - // TODO: Button - mLOG(CHEATS, STUB, "GameShark button unimplemented"); - return false; - // TODO: Fix overriding existing patches + cheat = mCheatListAppend(&cheats->d.list); + cheat->type = CHEAT_IF_BUTTON; + cheat->repeat = 1; + cheat->negativeRepeat = 0; + cheat = mCheatListAppend(&cheats->d.list); + cheat->type = CHEAT_ASSIGN; + cheat->width = 4; + cheat->address = _parAddr(op2); + cheats->incompleteCheat = mCheatListIndex(&cheats->d.list, cheat); + break; case PAR3_OTHER_PATCH_1: - cheats->romPatches[0].address = BASE_CART0 | ((op2 & 0xFFFFFF) << 1); - cheats->romPatches[0].applied = false; - cheats->romPatches[0].exists = true; - cheats->incompletePatch = &cheats->romPatches[0]; + romPatch = 0; break; case PAR3_OTHER_PATCH_2: - cheats->romPatches[1].address = BASE_CART0 | ((op2 & 0xFFFFFF) << 1); - cheats->romPatches[1].applied = false; - cheats->romPatches[1].exists = true; - cheats->incompletePatch = &cheats->romPatches[1]; + romPatch = 1; break; case PAR3_OTHER_PATCH_3: - cheats->romPatches[2].address = BASE_CART0 | ((op2 & 0xFFFFFF) << 1); - cheats->romPatches[2].applied = false; - cheats->romPatches[2].exists = true; - cheats->incompletePatch = &cheats->romPatches[2]; + romPatch = 2; break; case PAR3_OTHER_PATCH_4: - cheats->romPatches[3].address = BASE_CART0 | ((op2 & 0xFFFFFF) << 1); - cheats->romPatches[3].applied = false; - cheats->romPatches[3].exists = true; - cheats->incompletePatch = &cheats->romPatches[3]; + romPatch = 3; break; case PAR3_OTHER_ENDIF: if (cheats->currentBlock != COMPLETE) { @@ -190,23 +206,38 @@ static bool _addPAR3Special(struct GBACheatSet* cheats, uint32_t op2) { return false; case PAR3_OTHER_FILL_1: cheat = mCheatListAppend(&cheats->d.list); + cheat->type = CHEAT_ASSIGN; cheat->address = _parAddr(op2); cheat->width = 1; cheats->incompleteCheat = mCheatListIndex(&cheats->d.list, cheat); break; case PAR3_OTHER_FILL_2: cheat = mCheatListAppend(&cheats->d.list); + cheat->type = CHEAT_ASSIGN; cheat->address = _parAddr(op2); cheat->width = 2; cheats->incompleteCheat = mCheatListIndex(&cheats->d.list, cheat); break; case PAR3_OTHER_FILL_4: cheat = mCheatListAppend(&cheats->d.list); + cheat->type = CHEAT_ASSIGN; cheat->address = _parAddr(op2); - cheat->width = 3; + cheat->width = 4; cheats->incompleteCheat = mCheatListIndex(&cheats->d.list, cheat); break; } + if (romPatch >= 0) { + while (cheats->romPatches[romPatch].exists) { + ++romPatch; + if (romPatch >= MAX_ROM_PATCHES) { + break; + } + } + cheats->romPatches[romPatch].address = BASE_CART0 | ((op2 & 0xFFFFFF) << 1); + cheats->romPatches[romPatch].applied = false; + cheats->romPatches[romPatch].exists = true; + cheats->incompletePatch = &cheats->romPatches[romPatch]; + } return true; } @@ -219,7 +250,14 @@ bool GBACheatAddProActionReplayRaw(struct GBACheatSet* cheats, uint32_t op1, uin if (cheats->incompleteCheat != COMPLETE) { struct mCheat* incompleteCheat = mCheatListGetPointer(&cheats->d.list, cheats->incompleteCheat); incompleteCheat->operand = op1 & (0xFFFFFFFFU >> ((4 - incompleteCheat->width) * 8)); - incompleteCheat->addressOffset = op2 >> 24; + if (cheats->incompleteCheat > 0) { + struct mCheat* lastCheat = mCheatListGetPointer(&cheats->d.list, cheats->incompleteCheat - 1); + if (lastCheat->type == CHEAT_IF_BUTTON) { + cheats->incompleteCheat = COMPLETE; + return true; + } + } + incompleteCheat->operandOffset = op2 >> 24; incompleteCheat->repeat = (op2 >> 16) & 0xFF; incompleteCheat->addressOffset = (op2 & 0xFFFF) * incompleteCheat->width; cheats->incompleteCheat = COMPLETE; @@ -339,7 +377,7 @@ int GBACheatProActionReplayProbability(uint32_t op1, uint32_t op2) { return 0x100; } if (!op1) { - probability += 0x20; + probability += 0x40; uint32_t address = _parAddr(op2); switch (op2 & 0xFE000000) { case PAR3_OTHER_FILL_1: @@ -360,8 +398,8 @@ int GBACheatProActionReplayProbability(uint32_t op1, uint32_t op2) { case PAR3_OTHER_BUTTON_4: case PAR3_OTHER_ENDIF: case PAR3_OTHER_ELSE: - if (op2 & 0x01FFFFFF) { - probability -= 0x20; + if (op2 & 0x01000000) { + probability -= 0x40; } break; default: diff --git a/src/gba/core.c b/src/gba/core.c index 6f493fe5b..d2fdfe9ae 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -746,6 +746,20 @@ static void _GBACoreLoadSymbols(struct mCore* core, struct VFile* vf) { } #endif } + +static bool _GBACoreLookupIdentifier(struct mCore* core, const char* name, int32_t* value, int* segment) { + UNUSED(core); + *segment = -1; + int i; + for (i = 0; i < REG_MAX; i += 2) { + const char* reg = GBAIORegisterNames[i >> 1]; + if (reg && strcasecmp(reg, name) == 0) { + *value = BASE_IO | i; + return true; + } + } + return false; +} #endif static struct mCheatDevice* _GBACoreCheatDevice(struct mCore* core) { @@ -847,6 +861,7 @@ static void _GBACoreEnableAudioChannel(struct mCore* core, size_t id, bool enabl } } +#ifndef MINIMAL_CORE static void _GBACoreStartVideoLog(struct mCore* core, struct mVideoLogContext* context) { struct GBACore* gbacore = (struct GBACore*) core; struct GBA* gba = core->board; @@ -873,6 +888,7 @@ static void _GBACoreEndVideoLog(struct mCore* core) { free(gbacore->proxyRenderer.logger); gbacore->proxyRenderer.logger = NULL; } +#endif struct mCore* GBACoreCreate(void) { struct GBACore* gbacore = malloc(sizeof(*gbacore)); @@ -943,6 +959,7 @@ struct mCore* GBACoreCreate(void) { core->attachDebugger = _GBACoreAttachDebugger; core->detachDebugger = _GBACoreDetachDebugger; core->loadSymbols = _GBACoreLoadSymbols; + core->lookupIdentifier = _GBACoreLookupIdentifier; #endif core->cheatDevice = _GBACoreCheatDevice; core->savedataClone = _GBACoreSavedataClone; diff --git a/src/gba/debugger/cli.c b/src/gba/debugger/cli.c index 4208d9021..4eb13b0f3 100644 --- a/src/gba/debugger/cli.c +++ b/src/gba/debugger/cli.c @@ -14,16 +14,15 @@ static void _GBACLIDebuggerInit(struct CLIDebuggerSystem*); static bool _GBACLIDebuggerCustom(struct CLIDebuggerSystem*); -static uint32_t _GBACLIDebuggerLookupIdentifier(struct CLIDebuggerSystem*, const char* name, struct CLIDebugVector* dv); static void _frame(struct CLIDebugger*, struct CLIDebugVector*); static void _load(struct CLIDebugger*, struct CLIDebugVector*); static void _save(struct CLIDebugger*, struct CLIDebugVector*); struct CLIDebuggerCommandSummary _GBACLIDebuggerCommands[] = { - { "frame", _frame, 0, "Frame advance" }, - { "load", _load, CLIDVParse, "Load a savestate" }, - { "save", _save, CLIDVParse, "Save a savestate" }, + { "frame", _frame, "", "Frame advance" }, + { "load", _load, "*", "Load a savestate" }, + { "save", _save, "*", "Save a savestate" }, { 0, 0, 0, 0 } }; @@ -33,7 +32,6 @@ struct GBACLIDebugger* GBACLIDebuggerCreate(struct mCore* core) { debugger->d.init = _GBACLIDebuggerInit; debugger->d.deinit = NULL; debugger->d.custom = _GBACLIDebuggerCustom; - debugger->d.lookupIdentifier = _GBACLIDebuggerLookupIdentifier; debugger->d.name = "Game Boy Advance"; debugger->d.commands = _GBACLIDebuggerCommands; @@ -64,19 +62,6 @@ static bool _GBACLIDebuggerCustom(struct CLIDebuggerSystem* debugger) { return false; } -static uint32_t _GBACLIDebuggerLookupIdentifier(struct CLIDebuggerSystem* debugger, const char* name, struct CLIDebugVector* dv) { - UNUSED(debugger); - int i; - for (i = 0; i < REG_MAX; i += 2) { - const char* reg = GBAIORegisterNames[i >> 1]; - if (reg && strcasecmp(reg, name) == 0) { - return BASE_IO | i; - } - } - dv->type = CLIDV_ERROR_TYPE; - return 0; -} - static void _frame(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { UNUSED(dv); debugger->d.state = DEBUGGER_CUSTOM; diff --git a/src/gba/dma.c b/src/gba/dma.c index 5c5499ebf..4516b5224 100644 --- a/src/gba/dma.c +++ b/src/gba/dma.c @@ -46,6 +46,8 @@ uint32_t GBADMAWriteSAD(struct GBA* gba, int dma, uint32_t address) { address &= 0x0FFFFFFE; if (_isValidDMASAD(dma, address)) { memory->dma[dma].source = address; + } else { + memory->dma[dma].source = 0; } return memory->dma[dma].source; } @@ -81,7 +83,19 @@ uint16_t GBADMAWriteCNT_HI(struct GBA* gba, int dma, uint16_t control) { if (!wasEnabled && GBADMARegisterIsEnable(currentDma->reg)) { currentDma->nextSource = currentDma->source; + if (currentDma->nextSource >= BASE_CART0 && currentDma->nextSource < BASE_CART_SRAM && GBADMARegisterGetSrcControl(currentDma->reg) < 3) { + currentDma->reg = GBADMARegisterClearSrcControl(currentDma->reg); + } currentDma->nextDest = currentDma->dest; + + uint32_t width = 2 << GBADMARegisterGetWidth(currentDma->reg); + if (currentDma->nextSource & (width - 1)) { + mLOG(GBA_MEM, GAME_ERROR, "Misaligned DMA source address: 0x%08X", currentDma->nextSource); + } + if (currentDma->nextDest & (width - 1)) { + mLOG(GBA_MEM, GAME_ERROR, "Misaligned DMA destination address: 0x%08X", currentDma->nextDest); + } + GBADMASchedule(gba, dma, currentDma); } // If the DMA has already occurred, this value might have changed since the function started @@ -90,15 +104,15 @@ uint16_t GBADMAWriteCNT_HI(struct GBA* gba, int dma, uint16_t control) { void GBADMASchedule(struct GBA* gba, int number, struct GBADMA* info) { switch (GBADMARegisterGetTiming(info->reg)) { - case DMA_TIMING_NOW: + case GBA_DMA_TIMING_NOW: info->when = mTimingCurrentTime(&gba->timing) + 3; // DMAs take 3 cycles to start info->nextCount = info->count; break; - case DMA_TIMING_HBLANK: - case DMA_TIMING_VBLANK: + case GBA_DMA_TIMING_HBLANK: + case GBA_DMA_TIMING_VBLANK: // Handled implicitly return; - case DMA_TIMING_CUSTOM: + case GBA_DMA_TIMING_CUSTOM: switch (number) { case 0: mLOG(GBA_MEM, WARN, "Discarding invalid DMA0 scheduling"); @@ -108,7 +122,7 @@ void GBADMASchedule(struct GBA* gba, int number, struct GBADMA* info) { GBAAudioScheduleFifoDma(&gba->audio, number, info); break; case 3: - // GBAVideoScheduleVCaptureDma(dma, info); + // Handled implicitly break; } } @@ -121,7 +135,7 @@ void GBADMARunHblank(struct GBA* gba, int32_t cycles) { int i; for (i = 0; i < 4; ++i) { dma = &memory->dma[i]; - if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_HBLANK && !dma->nextCount) { + if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_HBLANK && !dma->nextCount) { dma->when = mTimingCurrentTime(&gba->timing) + 3 + cycles; dma->nextCount = dma->count; } @@ -135,7 +149,7 @@ void GBADMARunVblank(struct GBA* gba, int32_t cycles) { int i; for (i = 0; i < 4; ++i) { dma = &memory->dma[i]; - if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_VBLANK && !dma->nextCount) { + if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_VBLANK && !dma->nextCount) { dma->when = mTimingCurrentTime(&gba->timing) + 3 + cycles; dma->nextCount = dma->count; } @@ -143,6 +157,16 @@ void GBADMARunVblank(struct GBA* gba, int32_t cycles) { GBADMAUpdate(gba); } +void GBADMARunDisplayStart(struct GBA* gba, int32_t cycles) { + struct GBAMemory* memory = &gba->memory; + struct GBADMA* dma = &memory->dma[3]; + if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_CUSTOM && !dma->nextCount) { + dma->when = mTimingCurrentTime(&gba->timing) + 3 + cycles; + dma->nextCount = dma->count; + GBADMAUpdate(gba); + } +} + void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) { UNUSED(timing); UNUSED(cyclesLate); @@ -156,13 +180,16 @@ void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) { GBADMAService(gba, memory->activeDMA, dma); } else { dma->nextCount = 0; - if (!GBADMARegisterIsRepeat(dma->reg) || GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_NOW) { + bool noRepeat = !GBADMARegisterIsRepeat(dma->reg); + noRepeat |= GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_NOW; + noRepeat |= memory->activeDMA == 3 && GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_CUSTOM; + if (noRepeat) { dma->reg = GBADMARegisterClearEnable(dma->reg); // Clear the enable bit in memory memory->io[(REG_DMA0CNT_HI + memory->activeDMA * (REG_DMA1CNT_HI - REG_DMA0CNT_HI)) >> 1] &= 0x7FE0; } - if (GBADMARegisterGetDestControl(dma->reg) == DMA_INCREMENT_RELOAD) { + if (GBADMARegisterGetDestControl(dma->reg) == GBA_DMA_INCREMENT_RELOAD) { dma->nextDest = dma->dest; } if (GBADMARegisterIsDoIRQ(dma->reg)) { @@ -226,31 +253,44 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { info->when += cycles; gba->performingDMA = 1 | (number << 1); - uint32_t word; if (width == 4) { - word = cpu->memory.load32(cpu, source, 0); - gba->bus = word; - cpu->memory.store32(cpu, dest, word, 0); + if (source) { + memory->dmaTransferRegister = cpu->memory.load32(cpu, source, 0); + } + gba->bus = memory->dmaTransferRegister; + cpu->memory.store32(cpu, dest, memory->dmaTransferRegister, 0); + memory->dmaTransferRegister &= 0xFFFF0000; + memory->dmaTransferRegister |= memory->dmaTransferRegister >> 16; } else { if (sourceRegion == REGION_CART2_EX && memory->savedata.type == SAVEDATA_EEPROM) { - word = GBASavedataReadEEPROM(&memory->savedata); - cpu->memory.store16(cpu, dest, word, 0); - } else if (destRegion == REGION_CART2_EX) { if (memory->savedata.type == SAVEDATA_AUTODETECT) { mLOG(GBA_MEM, INFO, "Detected EEPROM savegame"); GBASavedataInitEEPROM(&memory->savedata, gba->realisticTiming); } - word = cpu->memory.load16(cpu, source, 0); - GBASavedataWriteEEPROM(&memory->savedata, word, wordsRemaining); + memory->dmaTransferRegister = GBASavedataReadEEPROM(&memory->savedata); } else { - word = cpu->memory.load16(cpu, source, 0); - cpu->memory.store16(cpu, dest, word, 0); + if (source) { + memory->dmaTransferRegister = cpu->memory.load16(cpu, source, 0); + } } - gba->bus = word | (word << 16); + if (destRegion == REGION_CART2_EX) { + if (memory->savedata.type == SAVEDATA_AUTODETECT) { + mLOG(GBA_MEM, INFO, "Detected EEPROM savegame"); + GBASavedataInitEEPROM(&memory->savedata, gba->realisticTiming); + } + GBASavedataWriteEEPROM(&memory->savedata, memory->dmaTransferRegister, wordsRemaining); + } else { + cpu->memory.store16(cpu, dest, memory->dmaTransferRegister, 0); + + } + memory->dmaTransferRegister |= memory->dmaTransferRegister << 16; + gba->bus = memory->dmaTransferRegister; } int sourceOffset = DMA_OFFSET[GBADMARegisterGetSrcControl(info->reg)] * width; int destOffset = DMA_OFFSET[GBADMARegisterGetDestControl(info->reg)] * width; - source += sourceOffset; + if (source) { + source += sourceOffset; + } dest += destOffset; --wordsRemaining; gba->performingDMA = 0; diff --git a/src/gba/extra/proxy.c b/src/gba/extra/proxy.c index a54f68a8a..aa8e231ba 100644 --- a/src/gba/extra/proxy.c +++ b/src/gba/extra/proxy.c @@ -189,11 +189,16 @@ static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address) { uint16_t GBAVideoProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; switch (address) { + case REG_DISPCNT: + value &= 0xFFF7; + break; case REG_BG0CNT: case REG_BG1CNT: + value &= 0xDFFF; + break; case REG_BG2CNT: case REG_BG3CNT: - value &= 0xFFCF; + value &= 0xFFFF; break; case REG_BG0HOFS: case REG_BG0VOFS: diff --git a/src/gba/gba.c b/src/gba/gba.c index c5c83ada0..743c46374 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -125,7 +125,9 @@ void GBAUnloadROM(struct GBA* gba) { if (gba->romVf) { #ifndef FIXED_ROM_BUFFER - gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->pristineRomSize); + if (gba->isPristine) { + gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->pristineRomSize); + } #endif gba->romVf->close(gba->romVf); gba->romVf = NULL; @@ -207,6 +209,9 @@ void GBAReset(struct ARMCore* cpu) { gba->debug = false; memset(gba->debugString, 0, sizeof(gba->debugString)); + if (gba->pristineRomSize > SIZE_CART0) { + GBAMatrixReset(gba); + } if (!gba->romVf && gba->memory.rom) { GBASkipBIOS(gba); @@ -243,19 +248,17 @@ static void GBAProcessEvents(struct ARMCore* cpu) { int32_t nextEvent = cpu->nextEvent; while (cpu->cycles >= nextEvent) { - int32_t cycles = cpu->cycles; - - cpu->cycles = 0; cpu->nextEvent = INT_MAX; - -#ifndef NDEBUG - if (cycles < 0) { - mLOG(GBA, FATAL, "Negative cycles passed: %i", cycles); - } -#endif - nextEvent = cycles; + nextEvent = 0; do { - nextEvent = mTimingTick(&gba->timing, nextEvent); + int32_t cycles = cpu->cycles; + cpu->cycles = 0; +#ifndef NDEBUG + if (cycles < 0) { + mLOG(GBA, FATAL, "Negative cycles passed: %i", cycles); + } +#endif + nextEvent = mTimingTick(&gba->timing, nextEvent + cycles); } while (gba->cpuBlocked); cpu->nextEvent = nextEvent; @@ -276,6 +279,11 @@ static void GBAProcessEvents(struct ARMCore* cpu) { } #endif } +#ifndef NDEBUG + if (gba->cpuBlocked) { + mLOG(GBA, FATAL, "CPU is blocked!"); + } +#endif } #ifdef USE_DEBUGGERS @@ -300,7 +308,6 @@ bool GBALoadNull(struct GBA* gba) { GBAUnloadROM(gba); gba->romVf = NULL; gba->pristineRomSize = 0; - gba->memory.wram = anonymousMemoryMap(SIZE_WORKING_RAM); #ifndef FIXED_ROM_BUFFER gba->memory.rom = anonymousMemoryMap(SIZE_CART0); #else @@ -328,7 +335,6 @@ bool GBALoadMB(struct GBA* gba, struct VFile* vf) { gba->pristineRomSize = SIZE_WORKING_RAM; } gba->isPristine = true; - gba->memory.wram = anonymousMemoryMap(SIZE_WORKING_RAM); memset(gba->memory.wram, 0, SIZE_WORKING_RAM); vf->read(vf, gba->memory.wram, gba->pristineRomSize); if (!gba->memory.wram) { @@ -354,23 +360,30 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) { gba->pristineRomSize = vf->size(vf); vf->seek(vf, 0, SEEK_SET); if (gba->pristineRomSize > SIZE_CART0) { - gba->pristineRomSize = SIZE_CART0; - } - gba->isPristine = true; + gba->isPristine = false; + gba->memory.romSize = 0x01000000; #ifdef FIXED_ROM_BUFFER - if (gba->pristineRomSize <= romBufferSize) { gba->memory.rom = romBuffer; - vf->read(vf, romBuffer, gba->pristineRomSize); - } #else - gba->memory.rom = vf->map(vf, gba->pristineRomSize, MAP_READ); + gba->memory.rom = anonymousMemoryMap(SIZE_CART0); #endif + } else { + gba->isPristine = true; +#ifdef FIXED_ROM_BUFFER + if (gba->pristineRomSize <= romBufferSize) { + gba->memory.rom = romBuffer; + vf->read(vf, romBuffer, gba->pristineRomSize); + } +#else + gba->memory.rom = vf->map(vf, gba->pristineRomSize, MAP_READ); +#endif + gba->memory.romSize = gba->pristineRomSize; + } if (!gba->memory.rom) { mLOG(GBA, WARN, "Couldn't map ROM"); return false; } gba->yankedRomSize = 0; - gba->memory.romSize = gba->pristineRomSize; gba->memory.romMask = toPow2(gba->memory.romSize) - 1; gba->memory.mirroring = false; gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize); @@ -568,16 +581,49 @@ bool GBAIsMB(struct VFile* vf) { LOAD_32(opcode, 0, &signature); struct ARMInstructionInfo info; ARMDecodeARM(opcode, &info); - if (info.branchType != ARM_BRANCH) { - return false; + if (info.branchType == ARM_BRANCH) { + if (info.op1.immediate <= 0) { + return false; + } else if (info.op1.immediate == 28) { + // Ancient toolchain that is known to throw MB detection for a loop + return false; + } else if (info.op1.immediate != 24) { + return true; + } } - if (info.op1.immediate <= 0) { - return false; - } else if (info.op1.immediate == 28) { - // Ancient toolchain that is known to throw MB detection for a loop - return false; - } else if (info.op1.immediate != 24) { - return true; + + uint32_t pc = GBA_MB_MAGIC_OFFSET; + int i; + for (i = 0; i < 80; ++i) { + if (vf->read(vf, &signature, sizeof(signature)) != sizeof(signature)) { + break; + } + pc += 4; + LOAD_32(opcode, 0, &signature); + ARMDecodeARM(opcode, &info); + if (info.mnemonic != ARM_MN_LDR) { + continue; + } + if ((info.operandFormat & ARM_OPERAND_MEMORY) && info.memory.baseReg == ARM_PC && info.memory.format & ARM_MEMORY_IMMEDIATE_OFFSET) { + uint32_t immediate = info.memory.offset.immediate; + if (info.memory.format & ARM_MEMORY_OFFSET_SUBTRACT) { + immediate = -immediate; + } + immediate += pc + 8; + if (vf->seek(vf, immediate, SEEK_SET) < 0) { + break; + } + if (vf->read(vf, &signature, sizeof(signature)) != sizeof(signature)) { + break; + } + LOAD_32(immediate, 0, &signature); + if (vf->seek(vf, pc, SEEK_SET) < 0) { + break; + } + if ((immediate & ~0x7FF) == BASE_WORKING_RAM) { + return true; + } + } } // Found a libgba-linked cart...these are a bit harder to detect. return false; @@ -628,7 +674,7 @@ void GBAHitStub(struct ARMCore* cpu, uint32_t opcode) { if (gba->debugger) { struct mDebuggerEntryInfo info = { .address = _ARMPCAddress(cpu), - .opcode = opcode + .type.bp.opcode = opcode }; mDebuggerEnter(gba->debugger->d.p, DEBUGGER_ENTER_ILLEGAL_OP, &info); } @@ -651,7 +697,7 @@ void GBAIllegal(struct ARMCore* cpu, uint32_t opcode) { if (gba->debugger) { struct mDebuggerEntryInfo info = { .address = _ARMPCAddress(cpu), - .opcode = opcode + .type.bp.opcode = opcode }; mDebuggerEnter(gba->debugger->d.p, DEBUGGER_ENTER_ILLEGAL_OP, &info); } else @@ -672,7 +718,7 @@ void GBABreakpoint(struct ARMCore* cpu, int immediate) { if (gba->debugger) { struct mDebuggerEntryInfo info = { .address = _ARMPCAddress(cpu), - .breakType = BREAKPOINT_SOFTWARE + .type.bp.breakType = BREAKPOINT_SOFTWARE }; mDebuggerEnter(gba->debugger->d.p, DEBUGGER_ENTER_BREAKPOINT, &info); } diff --git a/src/gba/io.c b/src/gba/io.c index 629c4b687..6b9e21eba 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -939,6 +939,8 @@ void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) { STORE_32(gba->memory.dma[i].when, 0, &state->dma[i].when); } + state->dmaTransferRegister = gba->memory.dmaTransferRegister; + GBAHardwareSerialize(&gba->memory.hw, state); } @@ -979,11 +981,12 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) { LOAD_32(gba->memory.dma[i].nextDest, 0, &state->dma[i].nextDest); LOAD_32(gba->memory.dma[i].nextCount, 0, &state->dma[i].nextCount); LOAD_32(gba->memory.dma[i].when, 0, &state->dma[i].when); - if (GBADMARegisterGetTiming(gba->memory.dma[i].reg) != DMA_TIMING_NOW) { + if (GBADMARegisterGetTiming(gba->memory.dma[i].reg) != GBA_DMA_TIMING_NOW) { GBADMASchedule(gba, i, &gba->memory.dma[i]); } } GBAAudioWriteSOUNDCNT_X(&gba->audio, gba->memory.io[REG_SOUNDCNT_X >> 1]); + gba->memory.dmaTransferRegister = state->dmaTransferRegister; GBADMAUpdate(gba); GBAHardwareDeserialize(&gba->memory.hw, state); } diff --git a/src/gba/matrix.c b/src/gba/matrix.c new file mode 100644 index 000000000..5c8fef48a --- /dev/null +++ b/src/gba/matrix.c @@ -0,0 +1,75 @@ +/* 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/. */ +#include + +#include +#include +#include +#include + +static void _remapMatrix(struct GBA* gba) { + gba->romVf->seek(gba->romVf, gba->memory.matrix.paddr, SEEK_SET); + gba->romVf->read(gba->romVf, &gba->memory.rom[gba->memory.matrix.vaddr >> 2], gba->memory.matrix.size); +} + +void GBAMatrixReset(struct GBA* gba) { + gba->memory.matrix.paddr = 0x200; + gba->memory.matrix.size = 0x1000; + + gba->memory.matrix.vaddr = 0; + _remapMatrix(gba); + gba->memory.matrix.vaddr = 0x1000; + _remapMatrix(gba); + + gba->memory.matrix.paddr = 0; + gba->memory.matrix.vaddr = 0; + gba->memory.matrix.size = 0x100; + _remapMatrix(gba); +} + +void GBAMatrixWrite(struct GBA* gba, uint32_t address, uint32_t value) { + switch (address) { + case 0x0: + gba->memory.matrix.cmd = value; + switch (value) { + case 0x01: + case 0x11: + _remapMatrix(gba); + break; + default: + mLOG(GBA_MEM, STUB, "Unknown Matrix command: %08X", value); + break; + } + return; + case 0x4: + gba->memory.matrix.paddr = value & 0x03FFFFFF; + return; + case 0x8: + gba->memory.matrix.vaddr = value & 0x007FFFFF; + return; + case 0xC: + gba->memory.matrix.size = value << 9; + return; + } + mLOG(GBA_MEM, STUB, "Unknown Matrix write: %08X:%04X", address, value); +} + +void GBAMatrixWrite16(struct GBA* gba, uint32_t address, uint16_t value) { + switch (address) { + case 0x0: + GBAMatrixWrite(gba, address, value | (gba->memory.matrix.cmd & 0xFFFF0000)); + break; + case 0x4: + GBAMatrixWrite(gba, address, value | (gba->memory.matrix.paddr & 0xFFFF0000)); + break; + case 0x8: + GBAMatrixWrite(gba, address, value | (gba->memory.matrix.vaddr & 0xFFFF0000)); + break; + case 0xC: + GBAMatrixWrite(gba, address, value | (gba->memory.matrix.size & 0xFFFF0000)); + break; + } +} diff --git a/src/gba/memory.c b/src/gba/memory.c index d51354704..299b3a15d 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -22,7 +22,10 @@ mLOG_DEFINE_CATEGORY(GBA_MEM, "GBA Memory", "gba.memory"); static void _pristineCow(struct GBA* gba); -static uint32_t _deadbeef[1] = { 0xE710B710 }; // Illegal instruction on both ARM and Thumb +static void _agbPrintStore(struct GBA* gba, uint32_t address, int16_t value); +static int16_t _agbPrintLoad(struct GBA* gba, uint32_t address); +static uint8_t _deadbeef[4] = { 0x10, 0xB7, 0x10, 0xE7 }; // Illegal instruction on both ARM and Thumb +static uint8_t _agbPrintFunc[4] = { 0xFA, 0xDF /* swi 0xFF */, 0x70, 0x47 /* bx lr */ }; static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t region); static int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait); @@ -80,15 +83,19 @@ void GBAMemoryInit(struct GBA* gba) { gba->memory.biosPrefetch = 0; gba->memory.mirroring = false; - gba->memory.iwram = anonymousMemoryMap(SIZE_WORKING_IRAM); + gba->memory.agbPrint = 0; + memset(&gba->memory.agbPrintCtx, 0, sizeof(gba->memory.agbPrintCtx)); + gba->memory.agbPrintBuffer = NULL; + + gba->memory.wram = anonymousMemoryMap(SIZE_WORKING_RAM + SIZE_WORKING_IRAM); + gba->memory.iwram = &gba->memory.wram[SIZE_WORKING_RAM >> 2]; GBADMAInit(gba); GBAVFameInit(&gba->memory.vfame); } void GBAMemoryDeinit(struct GBA* gba) { - mappedMemoryFree(gba->memory.wram, SIZE_WORKING_RAM); - mappedMemoryFree(gba->memory.iwram, SIZE_WORKING_IRAM); + mappedMemoryFree(gba->memory.wram, SIZE_WORKING_RAM + SIZE_WORKING_IRAM); if (gba->memory.rom) { mappedMemoryFree(gba->memory.rom, gba->memory.romSize); } @@ -98,15 +105,15 @@ void GBAMemoryDeinit(struct GBA* gba) { if (gba->memory.savedata.realVf) { gba->memory.savedata.realVf->close(gba->memory.savedata.realVf); } + + if (gba->memory.agbPrintBuffer) { + mappedMemoryFree(gba->memory.agbPrintBuffer, SIZE_AGB_PRINT); + } } void GBAMemoryReset(struct GBA* gba) { - if (gba->memory.rom || gba->memory.fullBios || !gba->memory.wram) { - // Not multiboot - if (gba->memory.wram) { - mappedMemoryFree(gba->memory.wram, SIZE_WORKING_RAM); - } - gba->memory.wram = anonymousMemoryMap(SIZE_WORKING_RAM); + if (gba->memory.wram && gba->memory.rom) { + memset(gba->memory.wram, 0, SIZE_WORKING_RAM); } if (gba->memory.iwram) { @@ -115,6 +122,12 @@ void GBAMemoryReset(struct GBA* gba) { memset(gba->memory.io, 0, sizeof(gba->memory.io)); + gba->memory.agbPrint = 0; + memset(&gba->memory.agbPrintCtx, 0, sizeof(gba->memory.agbPrintCtx)); + if (gba->memory.agbPrintBuffer) { + gba->memory.agbPrintBuffer = NULL; + } + gba->memory.prefetch = false; gba->memory.lastPrefetchedPc = 0; @@ -124,6 +137,7 @@ void GBAMemoryReset(struct GBA* gba) { } GBADMAReset(gba); + memset(&gba->memory.matrix, 0, sizeof(gba->memory.matrix)); } static void _analyzeForIdleLoop(struct GBA* gba, struct ARMCore* cpu, uint32_t address) { @@ -294,10 +308,15 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { if ((address & (SIZE_CART0 - 1)) < memory->romSize) { break; } + if ((address & (SIZE_CART0 - 1)) == AGB_PRINT_FLUSH_ADDR && memory->agbPrint == 0x20) { + cpu->memory.activeRegion = (uint32_t*) _agbPrintFunc; + cpu->memory.activeMask = sizeof(_agbPrintFunc) - 1; + break; + } // Fall through default: memory->activeRegion = -1; - cpu->memory.activeRegion = _deadbeef; + cpu->memory.activeRegion = (uint32_t*) _deadbeef; cpu->memory.activeMask = 0; if (!gba->yankedRomSize && mCoreCallbacksListSize(&gba->coreCallbacks)) { @@ -527,6 +546,16 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { LOAD_16(value, address & memory->romMask, memory->rom); } else if (memory->vfame.cartType) { value = GBAVFameGetPatternValue(address, 16); + } else if ((address & (SIZE_CART0 - 1)) >= AGB_PRINT_BASE) { + uint32_t agbPrintAddr = address & 0x00FFFFFF; + if (agbPrintAddr == AGB_PRINT_PROTECT) { + value = memory->agbPrint; + } else if (agbPrintAddr < AGB_PRINT_TOP || (agbPrintAddr & 0x00FFFFF8) == (AGB_PRINT_STRUCT & 0x00FFFFF8)) { + value = _agbPrintLoad(gba, address); + } else { + mLOG(GBA_MEM, GAME_ERROR, "Out of bounds ROM Load16: 0x%08X", address); + value = (address >> 1) & 0xFFFF; + } } else { mLOG(GBA_MEM, GAME_ERROR, "Out of bounds ROM Load16: 0x%08X", address); value = (address >> 1) & 0xFFFF; @@ -720,6 +749,10 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { #define STORE_CART \ wait += waitstatesRegion[address >> BASE_OFFSET]; \ + if (memory->matrix.size && (address & 0x01FFFF00) == 0x00800100) { \ + GBAMatrixWrite(gba, address & 0x3C, value); \ + break; \ + } \ mLOG(GBA_MEM, STUB, "Unimplemented memory Store32: 0x%08X", address); #define STORE_SRAM \ @@ -837,9 +870,27 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle if (memory->hw.devices != HW_NONE && IS_GPIO_REGISTER(address & 0xFFFFFE)) { uint32_t reg = address & 0xFFFFFE; GBAHardwareGPIOWrite(&memory->hw, reg, value); - } else { - mLOG(GBA_MEM, GAME_ERROR, "Bad cartridge Store16: 0x%08X", address); + break; } + if (memory->matrix.size && (address & 0x01FFFF00) == 0x00800100) { + GBAMatrixWrite16(gba, address & 0x3C, value); + break; + } + // Fall through + case REGION_CART0_EX: + if ((address & 0x00FFFFFF) >= AGB_PRINT_BASE) { + uint32_t agbPrintAddr = address & 0x00FFFFFF; + if (agbPrintAddr == AGB_PRINT_PROTECT) { + memory->agbPrint = value; + _agbPrintStore(gba, address, value); + break; + } + if (memory->agbPrint == 0x20 && (agbPrintAddr < AGB_PRINT_TOP || (agbPrintAddr & 0x00FFFFF8) == (AGB_PRINT_STRUCT & 0x00FFFFF8))) { + _agbPrintStore(gba, address, value); + break; + } + } + mLOG(GBA_MEM, GAME_ERROR, "Bad cartridge Store16: 0x%08X", address); break; case REGION_CART2_EX: if (memory->savedata.type == SAVEDATA_AUTODETECT) { @@ -1531,24 +1582,23 @@ int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) { int32_t previousLoads = 0; // Don't prefetch too much if we're overlapping with a previous prefetch - uint32_t dist = (memory->lastPrefetchedPc - cpu->gprs[ARM_PC]) >> 1; - if (dist < 8) { - previousLoads = dist; + uint32_t dist = (memory->lastPrefetchedPc - cpu->gprs[ARM_PC]); + int32_t maxLoads = 8; + if (dist < 16) { + previousLoads = dist >> 1; + maxLoads -= previousLoads; } - int32_t s = cpu->memory.activeSeqCycles16; - int32_t n2s = cpu->memory.activeNonseqCycles16 - cpu->memory.activeSeqCycles16; + int32_t s = cpu->memory.activeSeqCycles16 + 1; + int32_t n2s = cpu->memory.activeNonseqCycles16 - cpu->memory.activeSeqCycles16 + 1; // Figure out how many sequential loads we can jam in int32_t stall = s; int32_t loads = 1; - if (stall < wait) { - int32_t maxLoads = 8 - previousLoads; - while (stall < wait && loads < maxLoads) { - stall += s; - ++loads; - } + while (stall < wait && loads < maxLoads) { + stall += s; + ++loads; } if (stall > wait) { // The wait cannot take less time than the prefetch stalls @@ -1561,7 +1611,7 @@ int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) { memory->lastPrefetchedPc = cpu->gprs[ARM_PC] + WORD_SIZE_THUMB * (loads + previousLoads - 1); // The next |loads|S waitstates disappear entirely, so long as they're all in a row - cpu->cycles -= stall; + cpu->cycles -= (s - 1) * loads; return wait; } @@ -1579,6 +1629,7 @@ void _pristineCow(struct GBA* gba) { if (!gba->isPristine) { return; } +#ifndef FIXED_ROM_BUFFER void* newRom = anonymousMemoryMap(SIZE_CART0); memcpy(newRom, gba->memory.rom, gba->memory.romSize); memset(((uint8_t*) newRom) + gba->memory.romSize, 0xFF, SIZE_CART0 - gba->memory.romSize); @@ -1586,12 +1637,63 @@ void _pristineCow(struct GBA* gba) { gba->cpu->memory.activeRegion = newRom; } if (gba->romVf) { -#ifndef FIXED_ROM_BUFFER gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->memory.romSize); -#endif gba->romVf->close(gba->romVf); gba->romVf = NULL; } gba->memory.rom = newRom; gba->memory.hw.gpioBase = &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1]; +#endif + gba->isPristine = false; +} + +void GBAPrintFlush(struct GBA* gba) { + char oolBuf[0x101]; + size_t i; + for (i = 0; gba->memory.agbPrintCtx.get != gba->memory.agbPrintCtx.put && i < 0x100; ++i) { + int16_t value; + LOAD_16(value, gba->memory.agbPrintCtx.get & -2, gba->memory.agbPrintBuffer); + if (gba->memory.agbPrintCtx.get & 1) { + value >>= 8; + } else { + value &= 0xFF; + } + oolBuf[i] = value; + oolBuf[i + 1] = 0; + ++gba->memory.agbPrintCtx.get; + } + _agbPrintStore(gba, AGB_PRINT_STRUCT + 4, gba->memory.agbPrintCtx.get); + + mLOG(GBA_DEBUG, INFO, "%s", oolBuf); +} + +static void _agbPrintStore(struct GBA* gba, uint32_t address, int16_t value) { + struct GBAMemory* memory = &gba->memory; + if ((address & 0x00FFFFFF) < AGB_PRINT_TOP) { + if (!memory->agbPrintBuffer) { + memory->agbPrintBuffer = anonymousMemoryMap(SIZE_AGB_PRINT); + } + STORE_16(value, address & (SIZE_AGB_PRINT - 2), memory->agbPrintBuffer); + } else if ((address & 0x00FFFFF8) == (AGB_PRINT_STRUCT & 0x00FFFFF8)) { + (&memory->agbPrintCtx.request)[(address & 7) >> 1] = value; + } + if (memory->romSize == SIZE_CART0) { + _pristineCow(gba); + memcpy(&memory->rom[AGB_PRINT_FLUSH_ADDR >> 2], _agbPrintFunc, sizeof(_agbPrintFunc)); + STORE_16(value, address & (SIZE_CART0 - 2), memory->rom); + } else if (memory->agbPrintCtx.bank == 0xFD && memory->romSize >= SIZE_CART0 / 2) { + _pristineCow(gba); + STORE_16(value, address & (SIZE_CART0 / 2 - 2), memory->rom); + } +} + +static int16_t _agbPrintLoad(struct GBA* gba, uint32_t address) { + struct GBAMemory* memory = &gba->memory; + int16_t value = 0xFFFF; + if (address < AGB_PRINT_TOP) { + LOAD_16(value, address & (SIZE_AGB_PRINT - 1), memory->agbPrintBuffer); + } else if ((address & 0x00FFFFF8) == (AGB_PRINT_STRUCT & 0x00FFFFF8)) { + value = (&memory->agbPrintCtx.request)[(address & 7) >> 1]; + } + return value; } diff --git a/src/gba/overrides.c b/src/gba/overrides.c index 915d220e3..1a1e9c9ec 100644 --- a/src/gba/overrides.c +++ b/src/gba/overrides.c @@ -180,6 +180,9 @@ static const struct GBACartridgeOverride _overrides[] = { { "KYGE", SAVEDATA_EEPROM, HW_TILT, IDLE_LOOP_NONE, false }, { "KYGP", SAVEDATA_EEPROM, HW_TILT, IDLE_LOOP_NONE, false }, + // Aging cartridge + { "TCHK", SAVEDATA_EEPROM, HW_NONE, IDLE_LOOP_NONE, false }, + { { 0, 0, 0, 0 }, 0, 0, IDLE_LOOP_NONE, false } }; diff --git a/src/gba/renderers/cache-set.c b/src/gba/renderers/cache-set.c index ce28d7e20..343cdcb8e 100644 --- a/src/gba/renderers/cache-set.c +++ b/src/gba/renderers/cache-set.c @@ -57,12 +57,15 @@ void GBAVideoCacheAssociate(struct mCacheSet* cache, struct GBAVideo* video) { } static void mapParser0(struct mMapCache* cache, struct mMapCacheEntry* entry, void* vram) { - UNUSED(cache); uint16_t map = *(uint16_t*) vram; entry->tileId = GBA_TEXT_MAP_TILE(map); entry->flags = mMapCacheEntryFlagsSetHMirror(entry->flags, !!GBA_TEXT_MAP_HFLIP(map)); entry->flags = mMapCacheEntryFlagsSetVMirror(entry->flags, !!GBA_TEXT_MAP_VFLIP(map)); - entry->flags = mMapCacheEntryFlagsSetPaletteId(entry->flags, GBA_TEXT_MAP_PALETTE(map)); + if (mMapCacheSystemInfoGetPaletteBPP(cache->sysConfig) == 3) { + entry->flags = mMapCacheEntryFlagsClearPaletteId(entry->flags); + } else { + entry->flags = mMapCacheEntryFlagsSetPaletteId(entry->flags, GBA_TEXT_MAP_PALETTE(map)); + } } static void mapParser2(struct mMapCache* cache, struct mMapCacheEntry* entry, void* vram) { @@ -82,10 +85,14 @@ static void GBAVideoCacheWriteDISPCNT(struct mCacheSet* cache, uint16_t value) { mMapCacheSetGetPointer(&cache->maps, 2)->mapParser = mapParser0; mMapCacheSetGetPointer(&cache->maps, 3)->mapParser = mapParser0; - mMapCacheSetGetPointer(&cache->maps, 0)->tileCache = mTileCacheSetGetPointer(&cache->tiles, 0); - mMapCacheSetGetPointer(&cache->maps, 1)->tileCache = mTileCacheSetGetPointer(&cache->tiles, 0); - mMapCacheSetGetPointer(&cache->maps, 2)->tileCache = mTileCacheSetGetPointer(&cache->tiles, 0); - mMapCacheSetGetPointer(&cache->maps, 3)->tileCache = mTileCacheSetGetPointer(&cache->tiles, 0); + mMapCacheSetGetPointer(&cache->maps, 0)->tileCache = mTileCacheSetGetPointer(&cache->tiles, + mMapCacheSystemInfoGetPaletteBPP(mMapCacheSetGetPointer(&cache->maps, 0)->sysConfig) == 3); + mMapCacheSetGetPointer(&cache->maps, 1)->tileCache = mTileCacheSetGetPointer(&cache->tiles, + mMapCacheSystemInfoGetPaletteBPP(mMapCacheSetGetPointer(&cache->maps, 1)->sysConfig) == 3); + mMapCacheSetGetPointer(&cache->maps, 2)->tileCache = mTileCacheSetGetPointer(&cache->tiles, + mMapCacheSystemInfoGetPaletteBPP(mMapCacheSetGetPointer(&cache->maps, 2)->sysConfig) == 3); + mMapCacheSetGetPointer(&cache->maps, 3)->tileCache = mTileCacheSetGetPointer(&cache->tiles, + mMapCacheSystemInfoGetPaletteBPP(mMapCacheSetGetPointer(&cache->maps, 3)->sysConfig) == 3); break; case 1: case 2: @@ -94,8 +101,11 @@ static void GBAVideoCacheWriteDISPCNT(struct mCacheSet* cache, uint16_t value) { mMapCacheSetGetPointer(&cache->maps, 2)->mapParser = mapParser2; mMapCacheSetGetPointer(&cache->maps, 3)->mapParser = mapParser2; - mMapCacheSetGetPointer(&cache->maps, 0)->tileCache = mTileCacheSetGetPointer(&cache->tiles, 0); - mMapCacheSetGetPointer(&cache->maps, 1)->tileCache = mTileCacheSetGetPointer(&cache->tiles, 0); + mMapCacheSetGetPointer(&cache->maps, 0)->tileCache = mTileCacheSetGetPointer(&cache->tiles, + mMapCacheSystemInfoGetPaletteBPP(mMapCacheSetGetPointer(&cache->maps, 0)->sysConfig) == 3); + mMapCacheSetGetPointer(&cache->maps, 1)->tileCache = mTileCacheSetGetPointer(&cache->tiles, + mMapCacheSystemInfoGetPaletteBPP(mMapCacheSetGetPointer(&cache->maps, 1)->sysConfig) == 3); + mMapCacheSetGetPointer(&cache->maps, 2)->tileCache = mTileCacheSetGetPointer(&cache->tiles, 1); mMapCacheSetGetPointer(&cache->maps, 3)->tileCache = mTileCacheSetGetPointer(&cache->tiles, 1); break; @@ -113,6 +123,7 @@ static void GBAVideoCacheWriteBGCNT(struct mCacheSet* cache, size_t bg, uint16_t int tilesHigh = 0; mMapCacheSystemInfo sysconfig = 0; if (map->mapParser == mapParser0) { + map->tileCache = mTileCacheSetGetPointer(&cache->tiles, p); sysconfig = mMapCacheSystemInfoSetPaletteBPP(sysconfig, 2 + p); sysconfig = mMapCacheSystemInfoSetPaletteCount(sysconfig, 4 * !p); sysconfig = mMapCacheSystemInfoSetMacroTileSize(sysconfig, 5); @@ -125,8 +136,9 @@ static void GBAVideoCacheWriteBGCNT(struct mCacheSet* cache, size_t bg, uint16_t if (size & 2) { ++tilesHigh; } - map->tileStart = tileStart * 2; + map->tileStart = tileStart * (2 - p); } else if (map->mapParser == mapParser2) { + map->tileCache = mTileCacheSetGetPointer(&cache->tiles, 1); sysconfig = mMapCacheSystemInfoSetPaletteBPP(sysconfig, 3); sysconfig = mMapCacheSystemInfoSetPaletteCount(sysconfig, 0); sysconfig = mMapCacheSystemInfoSetMacroTileSize(sysconfig, 4 + size); diff --git a/src/gba/renderers/software-mode0.c b/src/gba/renderers/software-mode0.c index a35cbd2be..1cfe02d6a 100644 --- a/src/gba/renderers/software-mode0.c +++ b/src/gba/renderers/software-mode0.c @@ -8,7 +8,6 @@ #include #define BACKGROUND_TEXT_SELECT_CHARACTER \ - localX = tileX * 8 + inX; \ xBase = localX & 0xF8; \ if (background->size & 1) { \ xBase += (localX & 0x100) << 5; \ @@ -16,10 +15,6 @@ screenBase = background->screenBase + yBase + (xBase >> 2); \ uint16_t* screenBlock = renderer->d.vramBG[screenBase >> VRAM_BLOCK_OFFSET]; \ LOAD_16(mapData, screenBase & VRAM_BLOCK_MASK, screenBlock); \ - localY = inY & 0x7; \ - if (GBA_TEXT_MAP_VFLIP(mapData)) { \ - localY = 7 - localY; \ - } #define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_16(BLEND, OBJWIN) \ paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \ @@ -75,11 +70,21 @@ if (baseX < 0) { \ int disturbX = (16 + baseX) >> 3; \ inX -= disturbX << 3; \ + localX = tileX * 8 + inX; \ BACKGROUND_TEXT_SELECT_CHARACTER; \ + localY = inY & 0x7; \ + if (GBA_TEXT_MAP_VFLIP(mapData)) { \ + localY = 7 - localY; \ + } \ baseX -= disturbX << 3; \ inX += disturbX << 3; \ } else { \ + localX = tileX * 8 + inX; \ BACKGROUND_TEXT_SELECT_CHARACTER; \ + localY = inY & 0x7; \ + if (GBA_TEXT_MAP_VFLIP(mapData)) { \ + localY = 7 - localY; \ + } \ } \ charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \ vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \ @@ -97,8 +102,14 @@ tileData |= tileData << 16; \ carryData = tileData; \ } \ + localX = tileX * 8 + inX; \ for (; length; ++tileX) { \ - BACKGROUND_TEXT_SELECT_CHARACTER; \ + mapData = background->mapCache[(localX >> 3) & 0x3F]; \ + localX += 8; \ + localY = inY & 0x7; \ + if (GBA_TEXT_MAP_VFLIP(mapData)) { \ + localY = 7 - localY; \ + } \ charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \ vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \ tileData = carryData; \ @@ -128,7 +139,12 @@ #define DRAW_BACKGROUND_MODE_0_TILES_16(BLEND, OBJWIN) \ for (; tileX < tileEnd; ++tileX) { \ - BACKGROUND_TEXT_SELECT_CHARACTER; \ + mapData = background->mapCache[(localX >> 3) & 0x3F]; \ + localX += 8; \ + localY = inY & 0x7; \ + if (GBA_TEXT_MAP_VFLIP(mapData)) { \ + localY = 7 - localY; \ + } \ paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \ palette = &mainPalette[paletteData]; \ charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \ @@ -238,7 +254,12 @@ #define DRAW_BACKGROUND_MODE_0_TILES_256(BLEND, OBJWIN) \ for (; tileX < tileEnd; ++tileX) { \ - BACKGROUND_TEXT_SELECT_CHARACTER; \ + mapData = background->mapCache[(localX >> 3) & 0x3F]; \ + localX += 8; \ + localY = inY & 0x7; \ + if (GBA_TEXT_MAP_VFLIP(mapData)) { \ + localY = 7 - localY; \ + } \ charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \ vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \ if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ @@ -279,8 +300,14 @@ } #define DRAW_BACKGROUND_MODE_0_MOSAIC_256(BLEND, OBJWIN) \ + localX = tileX * 8 + inX; \ for (; tileX < tileEnd; ++tileX) { \ - BACKGROUND_TEXT_SELECT_CHARACTER; \ + mapData = background->mapCache[(localX >> 3) & 0x3F]; \ + localX += 8; \ + localY = inY & 0x7; \ + if (GBA_TEXT_MAP_VFLIP(mapData)) { \ + localY = 7 - localY; \ + } \ charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \ vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \ tileData = carryData; \ @@ -398,9 +425,14 @@ #define DRAW_BACKGROUND_MODE_0_TILES_256EXT(BLEND, OBJWIN) \ for (; tileX < tileEnd; ++tileX) { \ - BACKGROUND_TEXT_SELECT_CHARACTER; \ + mapData = background->mapCache[(localX >> 3) & 0x3F]; \ paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 8; \ palette = &mainPalette[paletteData]; \ + localX += 8; \ + localY = inY & 0x7; \ + if (GBA_TEXT_MAP_VFLIP(mapData)) { \ + localY = 7 - localY; \ + } \ charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \ vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \ if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ @@ -441,8 +473,14 @@ } #define DRAW_BACKGROUND_MODE_0_MOSAIC_256EXT(BLEND, OBJWIN) \ + localX = tileX * 8 + inX; \ for (; tileX < tileEnd; ++tileX) { \ - BACKGROUND_TEXT_SELECT_CHARACTER; \ + mapData = background->mapCache[(localX >> 3) & 0x3F]; \ + localX += 8; \ + localY = inY & 0x7; \ + if (GBA_TEXT_MAP_VFLIP(mapData)) { \ + localY = 7 - localY; \ + } \ charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) + (localY << 3); \ vram = renderer->d.vramBG[charBase >> VRAM_BLOCK_OFFSET]; \ tileData = carryData; \ @@ -490,8 +528,12 @@ } \ \ if (inX & 0x7) { \ + localX = tileX * 8 + inX; \ BACKGROUND_TEXT_SELECT_CHARACTER; \ - \ + localY = inY & 0x7; \ + if (GBA_TEXT_MAP_VFLIP(mapData)) { \ + localY = 7 - localY; \ + } \ int mod8 = inX & 0x7; \ int end = outX + 0x8 - mod8; \ if (end > renderer->end) { \ @@ -514,10 +556,15 @@ } \ length -= end - renderer->start; \ } \ + localX = (tileX * 8 + inX) & 0x1FF; \ DRAW_BACKGROUND_MODE_0_TILES_ ## BPP (BLEND, OBJWIN) \ if (length & 0x7) { \ + localX = tileX * 8 + inX; \ BACKGROUND_TEXT_SELECT_CHARACTER; \ - \ + localY = inY & 0x7; \ + if (GBA_TEXT_MAP_VFLIP(mapData)) { \ + localY = 7 - localY; \ + } \ int mod8 = length & 0x7; \ if (VIDEO_CHECKS && UNLIKELY(outX + mod8 != renderer->end)) { \ mLOG(GBA_VIDEO, FATAL, "Invariant doesn't hold in background draw!"); \ @@ -531,7 +578,7 @@ } void GBAVideoSoftwareRendererDrawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) { - int inX = renderer->start + background->x; + int inX = (renderer->start + background->x) & 0x1FF; int length = renderer->end - renderer->start; if (background->mosaic) { int mosaicV = GBAMosaicControlGetBgV(renderer->mosaic) + 1; @@ -587,10 +634,20 @@ void GBAVideoSoftwareRendererDrawBackgroundMode0(struct GBAVideoSoftwareRenderer uint32_t current; int pixelData; int paletteData; - int tileX = 0; + int tileX; int tileEnd = ((length + inX) >> 3) - (inX >> 3); uint16_t* vram = NULL; + if (background->yCache != inY >> 3) { + localX = 0; + for (tileX = 0; tileX < 64; ++tileX, localX += 8) { + BACKGROUND_TEXT_SELECT_CHARACTER; + background->mapCache[tileX] = mapData; + } + background->yCache = inY >> 3; + } + + tileX = 0; if (!objwinSlowPath) { if (!(flags & FLAG_TARGET_2)) { if (!background->multipalette) { diff --git a/src/gba/renderers/software-obj.c b/src/gba/renderers/software-obj.c index da0128ca0..450470528 100644 --- a/src/gba/renderers/software-obj.c +++ b/src/gba/renderers/software-obj.c @@ -245,26 +245,26 @@ int GBAVideoSoftwareRendererPreprocessSprite(struct GBAVideoSoftwareRenderer* re return 0; } + int objwinSlowPath = GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt) && GBAWindowControlGetBlendEnable(renderer->objwin.packed) != GBAWindowControlIsBlendEnable(renderer->currentWindow.packed); int variant = renderer->target1Obj && GBAWindowControlIsBlendEnable(renderer->currentWindow.packed) && (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); - if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT) { - int target2 = renderer->target2Bd << 4; - target2 |= renderer->bg[0].target2 << (renderer->bg[0].priority); - target2 |= renderer->bg[1].target2 << (renderer->bg[1].priority); - target2 |= renderer->bg[2].target2 << (renderer->bg[2].priority); - target2 |= renderer->bg[3].target2 << (renderer->bg[3].priority); - if ((1 << GBAObjAttributesCGetPriority(sprite->c)) <= target2) { + if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT || objwinSlowPath) { + int target2 = renderer->target2Bd; + target2 |= renderer->bg[0].target2; + target2 |= renderer->bg[1].target2; + target2 |= renderer->bg[2].target2; + target2 |= renderer->bg[3].target2; + if (target2) { flags |= FLAG_REBLEND; variant = 0; - } else if (!target2) { + } else { flags &= ~FLAG_TARGET_1; } } color_t* palette = &renderer->normalPalette[0x100]; color_t* objwinPalette = palette; - int objwinSlowPath = GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt) && GBAWindowControlGetBlendEnable(renderer->objwin.packed) != GBAWindowControlIsBlendEnable(renderer->currentWindow.packed); if (GBAObjAttributesAIs256Color(sprite->a) && renderer->objExtPalette) { if (!variant) { diff --git a/src/gba/renderers/software-private.h b/src/gba/renderers/software-private.h index 2119d3c76..e4f5b62c0 100644 --- a/src/gba/renderers/software-private.h +++ b/src/gba/renderers/software-private.h @@ -47,7 +47,7 @@ static inline void _compositeBlendObjwin(struct GBAVideoSoftwareRenderer* render if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) { color = _mix(renderer->alphaA[x], current, renderer->alphaB[x], color); } else { - color = (current & 0x00FFFFFF) | (current & FLAG_REBLEND); + color = (current & 0x00FFFFFF) | (current & (FLAG_REBLEND | FLAG_OBJWIN)); } } else { color = (color & ~FLAG_TARGET_2) | (current & FLAG_OBJWIN); @@ -63,7 +63,7 @@ static inline void _compositeBlendNoObjwin(struct GBAVideoSoftwareRenderer* rend if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) { color = _mix(renderer->alphaA[x], current, renderer->alphaB[x], color); } else { - color = (current & 0x00FFFFFF) | (current & FLAG_REBLEND); + color = (current & 0x00FFFFFF) | (current & (FLAG_REBLEND | FLAG_OBJWIN)); } } else { color = color & ~FLAG_TARGET_2; @@ -77,7 +77,7 @@ static inline void _compositeNoBlendObjwin(struct GBAVideoSoftwareRenderer* rend if (color < current) { color |= (current & FLAG_OBJWIN); } else { - color = (current & 0x00FFFFFF) | (current & FLAG_REBLEND); + color = (current & 0x00FFFFFF) | (current & (FLAG_REBLEND | FLAG_OBJWIN)); } renderer->row[x] = color; } @@ -86,7 +86,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re uint32_t current) { UNUSED(renderer); if (color >= current) { - color = (current & 0x00FFFFFF) | (current & FLAG_REBLEND); + color = (current & 0x00FFFFFF) | (current & (FLAG_REBLEND | FLAG_OBJWIN)); } renderer->row[x] = color; } diff --git a/src/gba/renderers/video-software.c b/src/gba/renderers/video-software.c index b8d49ba80..10bf0b994 100644 --- a/src/gba/renderers/video-software.c +++ b/src/gba/renderers/video-software.c @@ -149,6 +149,7 @@ static void GBAVideoSoftwareRendererReset(struct GBAVideoRenderer* renderer) { bg->dmy = 256; bg->sx = 0; bg->sy = 0; + bg->yCache = -1; bg->extPalette = NULL; bg->variantPalette = NULL; } @@ -167,6 +168,7 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender switch (address) { case REG_DISPCNT: + value &= 0xFFF7; softwareRenderer->dispcnt = value; GBAVideoSoftwareRendererUpdateDISPCNT(softwareRenderer); break; @@ -407,6 +409,10 @@ static void GBAVideoSoftwareRendererWriteVRAM(struct GBAVideoRenderer* renderer, mCacheSetWriteVRAM(renderer->cache, address); } memset(softwareRenderer->scanlineDirty, 0xFFFFFFFF, sizeof(softwareRenderer->scanlineDirty)); + softwareRenderer->bg[0].yCache = -1; + softwareRenderer->bg[1].yCache = -1; + softwareRenderer->bg[2].yCache = -1; + softwareRenderer->bg[3].yCache = -1; } static void GBAVideoSoftwareRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) { @@ -586,6 +592,10 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render GBAVideoSoftwareRendererPreprocessBuffer(softwareRenderer, y); int spriteLayers = GBAVideoSoftwareRendererPreprocessSpriteLayer(softwareRenderer, y); softwareRenderer->d.vramOBJ[0] = objVramBase; + if (softwareRenderer->blendDirty) { + _updatePalettes(softwareRenderer); + softwareRenderer->blendDirty = false; + } int w; unsigned priority; @@ -636,6 +646,38 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render } } } + if (softwareRenderer->target1Obj && (softwareRenderer->blendEffect == BLEND_DARKEN || softwareRenderer->blendEffect == BLEND_BRIGHTEN)) { + int x = 0; + uint32_t mask = 0xFF000000 & ~FLAG_OBJWIN; + uint32_t match = FLAG_REBLEND; + if (GBARegisterDISPCNTIsObjwinEnable(softwareRenderer->dispcnt)) { + mask |= FLAG_OBJWIN; + if (GBAWindowControlIsBlendEnable(softwareRenderer->objwin.packed)) { + match |= FLAG_OBJWIN; + } + } + for (w = 0; w < softwareRenderer->nWindows; ++w) { + if (!GBAWindowControlIsBlendEnable(softwareRenderer->windows[w].control.packed)) { + continue; + } + int end = softwareRenderer->windows[w].endX; + if (softwareRenderer->blendEffect == BLEND_DARKEN) { + for (; x < end; ++x) { + uint32_t color = softwareRenderer->row[x]; + if ((color & mask) == match) { + softwareRenderer->row[x] = _darken(color, softwareRenderer->bldy); + } + } + } else if (softwareRenderer->blendEffect == BLEND_BRIGHTEN) { + for (; x < end; ++x) { + uint32_t color = softwareRenderer->row[x]; + if ((color & mask) == match) { + softwareRenderer->row[x] = _brighten(color, softwareRenderer->bldy); + } + } + } + } + } if (GBARegisterDISPCNTGetMode(softwareRenderer->dispcnt) != 0) { softwareRenderer->bg[2].sx += softwareRenderer->bg[2].dmx; softwareRenderer->bg[2].sy += softwareRenderer->bg[2].dmy; @@ -643,6 +685,19 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render softwareRenderer->bg[3].sy += softwareRenderer->bg[3].dmy; } + if (softwareRenderer->bg[0].enabled > 0 && softwareRenderer->bg[0].enabled < 4) { + ++softwareRenderer->bg[0].enabled; + } + if (softwareRenderer->bg[1].enabled > 0 && softwareRenderer->bg[1].enabled < 4) { + ++softwareRenderer->bg[1].enabled; + } + if (softwareRenderer->bg[2].enabled > 0 && softwareRenderer->bg[2].enabled < 4) { + ++softwareRenderer->bg[2].enabled; + } + if (softwareRenderer->bg[3].enabled > 0 && softwareRenderer->bg[3].enabled < 4) { + ++softwareRenderer->bg[3].enabled; + } + GBAVideoSoftwareRendererPostprocessBuffer(softwareRenderer); #ifdef COLOR_16_BIT @@ -671,6 +726,19 @@ static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* rendere softwareRenderer->bg[2].sy = softwareRenderer->bg[2].refy; softwareRenderer->bg[3].sx = softwareRenderer->bg[3].refx; softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy; + + if (softwareRenderer->bg[0].enabled > 0) { + softwareRenderer->bg[0].enabled = 4; + } + if (softwareRenderer->bg[1].enabled > 0) { + softwareRenderer->bg[1].enabled = 4; + } + if (softwareRenderer->bg[2].enabled > 0) { + softwareRenderer->bg[2].enabled = 4; + } + if (softwareRenderer->bg[3].enabled > 0) { + softwareRenderer->bg[3].enabled = 4; + } } static void GBAVideoSoftwareRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) { @@ -689,11 +757,23 @@ static void GBAVideoSoftwareRendererPutPixels(struct GBAVideoRenderer* renderer, } } +static void _enableBg(struct GBAVideoSoftwareRenderer* renderer, int bg, bool active) { + if (renderer->d.disableBG[bg] || !active) { + renderer->bg[bg].enabled = 0; + } else if (!renderer->bg[bg].enabled && active) { + if (renderer->nextY == 0) { + renderer->bg[bg].enabled = 4; + } else { + renderer->bg[bg].enabled = 1; + } + } +} + static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) { - renderer->bg[0].enabled = GBARegisterDISPCNTGetBg0Enable(renderer->dispcnt) && !renderer->d.disableBG[0]; - renderer->bg[1].enabled = GBARegisterDISPCNTGetBg1Enable(renderer->dispcnt) && !renderer->d.disableBG[1]; - renderer->bg[2].enabled = GBARegisterDISPCNTGetBg2Enable(renderer->dispcnt) && !renderer->d.disableBG[2]; - renderer->bg[3].enabled = GBARegisterDISPCNTGetBg3Enable(renderer->dispcnt) && !renderer->d.disableBG[3]; + _enableBg(renderer, 0, GBARegisterDISPCNTGetBg0Enable(renderer->dispcnt)); + _enableBg(renderer, 1, GBARegisterDISPCNTGetBg1Enable(renderer->dispcnt)); + _enableBg(renderer, 2, GBARegisterDISPCNTGetBg2Enable(renderer->dispcnt)); + _enableBg(renderer, 3, GBARegisterDISPCNTGetBg3Enable(renderer->dispcnt)); } static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value) { diff --git a/src/gba/savedata.c b/src/gba/savedata.c index 3008b9428..c8c6c8be7 100644 --- a/src/gba/savedata.c +++ b/src/gba/savedata.c @@ -576,9 +576,15 @@ void _flashSwitchBank(struct GBASavedata* savedata, int bank) { if (bank > 0 && savedata->type == SAVEDATA_FLASH512) { mLOG(GBA_SAVE, INFO, "Updating flash chip from 512kb to 1Mb"); savedata->type = SAVEDATA_FLASH1M; - if (savedata->vf && savedata->vf->size(savedata->vf) == SIZE_CART_FLASH512) { - savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M); - memset(&savedata->data[SIZE_CART_FLASH512], 0xFF, SIZE_CART_FLASH512); + if (savedata->vf) { + savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_FLASH512); + if (savedata->vf->size(savedata->vf) == SIZE_CART_FLASH512) { + savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M); + savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_FLASH1M, MAP_WRITE); + memset(&savedata->data[SIZE_CART_FLASH512], 0xFF, SIZE_CART_FLASH512); + } else { + savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_FLASH1M, MAP_WRITE); + } } } } diff --git a/src/gba/sio/joybus.c b/src/gba/sio/joybus.c new file mode 100644 index 000000000..e7aa934e6 --- /dev/null +++ b/src/gba/sio/joybus.c @@ -0,0 +1,76 @@ +/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include + +#include +#include + +static uint16_t GBASIOJOYWriteRegister(struct GBASIODriver* sio, uint32_t address, uint16_t value); + +void GBASIOJOYCreate(struct GBASIODriver* sio) { + sio->init = NULL; + sio->deinit = NULL; + sio->load = NULL; + sio->unload = NULL; + sio->writeRegister = GBASIOJOYWriteRegister; +} + +uint16_t GBASIOJOYWriteRegister(struct GBASIODriver* sio, uint32_t address, uint16_t value) { + switch (address) { + case REG_JOYCNT: + return (value & 0x0040) | (sio->p->p->memory.io[REG_JOYCNT >> 1] & ~(value & 0x7) & ~0x0040); + case REG_JOYSTAT: + return (value & 0x0030) | (sio->p->p->memory.io[REG_JOYSTAT >> 1] & ~0x30); + case REG_JOY_TRANS_LO: + case REG_JOY_TRANS_HI: + sio->p->p->memory.io[REG_JOYSTAT >> 1] |= 8; + break; + } + return value; +} + +int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command, uint8_t* data) { + switch (command) { + case JOY_RESET: + sio->p->p->memory.io[REG_JOYCNT >> 1] |= 1; + if (sio->p->p->memory.io[REG_JOYCNT >> 1] & 0x40) { + GBARaiseIRQ(sio->p->p, IRQ_SIO); + } + // Fall through + case JOY_POLL: + data[0] = 0x00; + data[1] = 0x04; + data[2] = sio->p->p->memory.io[REG_JOYSTAT >> 1]; + return 3; + case JOY_RECV: + sio->p->p->memory.io[REG_JOYCNT >> 1] |= 2; + sio->p->p->memory.io[REG_JOYSTAT >> 1] |= 2; + + sio->p->p->memory.io[REG_JOY_RECV_LO >> 1] = data[0] | (data[1] << 8); + sio->p->p->memory.io[REG_JOY_RECV_HI >> 1] = data[2] | (data[3] << 8); + + data[0] = sio->p->p->memory.io[REG_JOYSTAT >> 1]; + + if (sio->p->p->memory.io[REG_JOYCNT >> 1] & 0x40) { + GBARaiseIRQ(sio->p->p, IRQ_SIO); + } + return 1; + case JOY_TRANS: + sio->p->p->memory.io[REG_JOYCNT >> 1] |= 4; + sio->p->p->memory.io[REG_JOYSTAT >> 1] &= ~8; + data[0] = sio->p->p->memory.io[REG_JOY_TRANS_LO >> 1]; + data[1] = sio->p->p->memory.io[REG_JOY_TRANS_LO >> 1] >> 8; + data[2] = sio->p->p->memory.io[REG_JOY_TRANS_HI >> 1]; + data[3] = sio->p->p->memory.io[REG_JOY_TRANS_HI >> 1] >> 8; + data[4] = sio->p->p->memory.io[REG_JOYSTAT >> 1]; + + if (sio->p->p->memory.io[REG_JOYCNT >> 1] & 0x40) { + GBARaiseIRQ(sio->p->p, IRQ_SIO); + } + return 5; + } + return 0; +} diff --git a/src/gba/test/cheats.c b/src/gba/test/cheats.c index f3fb80b00..a18e77c5e 100644 --- a/src/gba/test/cheats.c +++ b/src/gba/test/cheats.c @@ -75,6 +75,93 @@ M_TEST_DEFINE(doPARv3Assign) { set->deinit(set); } +M_TEST_DEFINE(doPARv3Slide1) { + struct mCore* core = *state; + struct mCheatDevice* device = core->cheatDevice(core); + assert_non_null(device); + struct mCheatSet* set = device->createSet(device, NULL); + assert_non_null(set); + GBACheatSetGameSharkVersion((struct GBACheatSet*) set, GBA_GS_PARV3_RAW); + assert_true(set->addLine(set, "00000000 80300000", GBA_CHEAT_PRO_ACTION_REPLAY)); + assert_true(set->addLine(set, "00000001 01020002", GBA_CHEAT_PRO_ACTION_REPLAY)); + + core->reset(core); + assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0); + assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0); + assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0); + assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0); + assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0); + assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0); + + mCheatRefresh(device, set); + assert_int_equal(core->rawRead8(core, 0x03000000, -1), 1); + assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0); + assert_int_equal(core->rawRead8(core, 0x03000002, -1), 2); + assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0); + assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0); + assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0); + + set->deinit(set); +} + +M_TEST_DEFINE(doPARv3Slide2) { + struct mCore* core = *state; + struct mCheatDevice* device = core->cheatDevice(core); + assert_non_null(device); + struct mCheatSet* set = device->createSet(device, NULL); + assert_non_null(set); + GBACheatSetGameSharkVersion((struct GBACheatSet*) set, GBA_GS_PARV3_RAW); + assert_true(set->addLine(set, "00000000 82300000", GBA_CHEAT_PRO_ACTION_REPLAY)); + assert_true(set->addLine(set, "00000001 01020002", GBA_CHEAT_PRO_ACTION_REPLAY)); + + core->reset(core); + assert_int_equal(core->rawRead16(core, 0x03000000, -1), 0); + assert_int_equal(core->rawRead16(core, 0x03000002, -1), 0); + assert_int_equal(core->rawRead16(core, 0x03000004, -1), 0); + assert_int_equal(core->rawRead16(core, 0x03000006, -1), 0); + assert_int_equal(core->rawRead16(core, 0x03000008, -1), 0); + assert_int_equal(core->rawRead16(core, 0x0300000A, -1), 0); + + mCheatRefresh(device, set); + assert_int_equal(core->rawRead16(core, 0x03000000, -1), 1); + assert_int_equal(core->rawRead16(core, 0x03000002, -1), 0); + assert_int_equal(core->rawRead16(core, 0x03000004, -1), 2); + assert_int_equal(core->rawRead16(core, 0x03000006, -1), 0); + assert_int_equal(core->rawRead16(core, 0x03000008, -1), 0); + assert_int_equal(core->rawRead16(core, 0x0300000A, -1), 0); + + set->deinit(set); +} + +M_TEST_DEFINE(doPARv3Slide4) { + struct mCore* core = *state; + struct mCheatDevice* device = core->cheatDevice(core); + assert_non_null(device); + struct mCheatSet* set = device->createSet(device, NULL); + assert_non_null(set); + GBACheatSetGameSharkVersion((struct GBACheatSet*) set, GBA_GS_PARV3_RAW); + assert_true(set->addLine(set, "00000000 84300000", GBA_CHEAT_PRO_ACTION_REPLAY)); + assert_true(set->addLine(set, "00000001 01020002", GBA_CHEAT_PRO_ACTION_REPLAY)); + + core->reset(core); + assert_int_equal(core->rawRead32(core, 0x03000000, -1), 0); + assert_int_equal(core->rawRead32(core, 0x03000004, -1), 0); + assert_int_equal(core->rawRead32(core, 0x03000008, -1), 0); + assert_int_equal(core->rawRead32(core, 0x0300000C, -1), 0); + assert_int_equal(core->rawRead32(core, 0x03000010, -1), 0); + assert_int_equal(core->rawRead32(core, 0x03000014, -1), 0); + + mCheatRefresh(device, set); + assert_int_equal(core->rawRead16(core, 0x03000000, -1), 1); + assert_int_equal(core->rawRead16(core, 0x03000004, -1), 0); + assert_int_equal(core->rawRead16(core, 0x03000008, -1), 2); + assert_int_equal(core->rawRead16(core, 0x0300000C, -1), 0); + assert_int_equal(core->rawRead16(core, 0x03000010, -1), 0); + assert_int_equal(core->rawRead16(core, 0x03000014, -1), 0); + + set->deinit(set); +} + M_TEST_DEFINE(doPARv3If1) { struct mCore* core = *state; struct mCheatDevice* device = core->cheatDevice(core); @@ -334,6 +421,7 @@ M_TEST_DEFINE(doPARv3IfX) { mCheatRefresh(device, set); assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1); assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x11); + set->deinit(set); } M_TEST_DEFINE(doPARv3IfXxX) { @@ -396,6 +484,7 @@ M_TEST_DEFINE(doPARv3IfXxX) { assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x21); assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x32); assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x41); + set->deinit(set); } M_TEST_DEFINE(doPARv3IfXElse) { @@ -430,6 +519,7 @@ M_TEST_DEFINE(doPARv3IfXElse) { assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1); assert_int_equal(core->rawRead8(core, 0x03000001, -1), 0x11); assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x22); + set->deinit(set); } M_TEST_DEFINE(doPARv3IfXElsexX) { @@ -500,6 +590,7 @@ M_TEST_DEFINE(doPARv3IfXElsexX) { assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x32); assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x42); assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x51); + set->deinit(set); } M_TEST_DEFINE(doPARv3IfXElsexXElse) { @@ -577,6 +668,7 @@ M_TEST_DEFINE(doPARv3IfXElsexXElse) { assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x42); assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x51); assert_int_equal(core->rawRead8(core, 0x03000006, -1), 0x62); + set->deinit(set); } M_TEST_DEFINE(doPARv3IfXContain1) { @@ -638,6 +730,7 @@ M_TEST_DEFINE(doPARv3IfXContain1) { assert_int_equal(core->rawRead8(core, 0x03000002, -1), 0x21); assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x31); assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x41); + set->deinit(set); } M_TEST_DEFINE(doPARv3IfXContain1Else) { @@ -707,6 +800,7 @@ M_TEST_DEFINE(doPARv3IfXContain1Else) { assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x31); assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x41); assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x52); + set->deinit(set); } M_TEST_DEFINE(doPARv3IfXElseContain1) { @@ -776,6 +870,7 @@ M_TEST_DEFINE(doPARv3IfXElseContain1) { assert_int_equal(core->rawRead8(core, 0x03000003, -1), 0x32); assert_int_equal(core->rawRead8(core, 0x03000004, -1), 0x41); assert_int_equal(core->rawRead8(core, 0x03000005, -1), 0x52); + set->deinit(set); } M_TEST_DEFINE(doPARv3IfXContain1ElseContain1) { @@ -921,12 +1016,46 @@ M_TEST_DEFINE(doPARv3IfXContain1ElseContain1) { assert_int_equal(core->rawRead8(core, 0x03000006, -1), 0x62); assert_int_equal(core->rawRead8(core, 0x03000007, -1), 0x71); assert_int_equal(core->rawRead8(core, 0x03000008, -1), 0x82); + set->deinit(set); +} + +M_TEST_DEFINE(doPARv3IfButton) { + struct mCore* core = *state; + struct mCheatDevice* device = core->cheatDevice(core); + assert_non_null(device); + struct mCheatSet* set = device->createSet(device, NULL); + assert_non_null(set); + GBACheatSetGameSharkVersion((struct GBACheatSet*) set, GBA_GS_PARV3_RAW); + assert_true(set->addLine(set, "00000000 10300000", GBA_CHEAT_PRO_ACTION_REPLAY)); + assert_true(set->addLine(set, "00000001 00000000", GBA_CHEAT_PRO_ACTION_REPLAY)); + + core->reset(core); + assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0); + + mCheatRefresh(device, set); + assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0); + + mCheatPressButton(device, true); + mCheatRefresh(device, set); + assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1); + + mCheatPressButton(device, false); + mCheatRefresh(device, set); + assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0x1); + + core->rawWrite8(core, 0x03000000, -1, 0); + mCheatRefresh(device, set); + assert_int_equal(core->rawRead8(core, 0x03000000, -1), 0); + set->deinit(set); } M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(GBACheats, cmocka_unit_test(createSet), cmocka_unit_test(addRawPARv3), cmocka_unit_test(doPARv3Assign), + cmocka_unit_test(doPARv3Slide1), + cmocka_unit_test(doPARv3Slide2), + cmocka_unit_test(doPARv3Slide4), cmocka_unit_test(doPARv3If1), cmocka_unit_test(doPARv3If1x1), cmocka_unit_test(doPARv3If2), @@ -940,4 +1069,5 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(GBACheats, cmocka_unit_test(doPARv3IfXContain1), cmocka_unit_test(doPARv3IfXContain1Else), cmocka_unit_test(doPARv3IfXElseContain1), - cmocka_unit_test(doPARv3IfXContain1ElseContain1)) + cmocka_unit_test(doPARv3IfXContain1ElseContain1), + cmocka_unit_test(doPARv3IfButton)) diff --git a/src/gba/video.c b/src/gba/video.c index 377aafb15..c89796585 100644 --- a/src/gba/video.c +++ b/src/gba/video.c @@ -71,7 +71,7 @@ static struct GBAVideoRenderer dummyRenderer = { void GBAVideoInit(struct GBAVideo* video) { video->renderer = &dummyRenderer; video->renderer->cache = NULL; - video->vram = 0; + video->vram = anonymousMemoryMap(SIZE_VRAM); video->frameskip = 0; video->event.name = "GBA Video"; video->event.callback = NULL; @@ -94,10 +94,6 @@ void GBAVideoReset(struct GBAVideo* video) { video->frameCounter = 0; video->frameskipCounter = 0; - if (video->vram) { - mappedMemoryFree(video->vram, SIZE_VRAM); - } - video->vram = anonymousMemoryMap(SIZE_VRAM); memset(video->renderer->vramBG, 0, sizeof(video->renderer->vramBG)); video->renderer->vramBG[0] = &video->vram[0x0000]; video->renderer->vramBG[1] = &video->vram[0x2000]; @@ -207,6 +203,9 @@ void _startHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) { if (video->vcount < VIDEO_VERTICAL_PIXELS) { GBADMARunHblank(video->p, -cyclesLate); } + if (video->vcount >= 2 && video->vcount < VIDEO_VERTICAL_PIXELS + 2) { + GBADMARunDisplayStart(video->p, -cyclesLate); + } if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) { GBARaiseIRQ(video->p, IRQ_HBLANK); } @@ -239,6 +238,9 @@ static uint16_t GBAVideoDummyRendererWriteVideoRegister(struct GBAVideoRenderer* GBAVideoCacheWriteVideoRegister(renderer->cache, address, value); } switch (address) { + case REG_DISPCNT: + value &= 0xFFF7; + break; case REG_BG0CNT: case REG_BG1CNT: value &= 0xDFFF; diff --git a/src/lr35902/debugger/cli-debugger.c b/src/lr35902/debugger/cli-debugger.c index ac01343df..7df1ea726 100644 --- a/src/lr35902/debugger/cli-debugger.c +++ b/src/lr35902/debugger/cli-debugger.c @@ -100,58 +100,9 @@ static void _printStatus(struct CLIDebuggerSystem* debugger) { _printLine(debugger->p, cpu->pc, cpu->memory.currentSegment(cpu, cpu->pc)); } -static uint32_t _lookupPlatformIdentifier(struct CLIDebuggerSystem* debugger, const char* name, struct CLIDebugVector* dv) { - struct LR35902Core* cpu = debugger->p->d.core->cpu; - if (strcmp(name, "a") == 0) { - return cpu->a; - } - if (strcmp(name, "b") == 0) { - return cpu->b; - } - if (strcmp(name, "c") == 0) { - return cpu->c; - } - if (strcmp(name, "d") == 0) { - return cpu->d; - } - if (strcmp(name, "e") == 0) { - return cpu->e; - } - if (strcmp(name, "h") == 0) { - return cpu->h; - } - if (strcmp(name, "l") == 0) { - return cpu->l; - } - if (strcmp(name, "bc") == 0) { - return cpu->bc; - } - if (strcmp(name, "de") == 0) { - return cpu->de; - } - if (strcmp(name, "hl") == 0) { - return cpu->hl; - } - if (strcmp(name, "af") == 0) { - return cpu->af; - } - if (strcmp(name, "pc") == 0) { - return cpu->pc; - } - if (strcmp(name, "sp") == 0) { - return cpu->sp; - } - if (strcmp(name, "f") == 0) { - return cpu->f.packed; - } - dv->type = CLIDV_ERROR_TYPE; - return 0; -} - void LR35902CLIDebuggerCreate(struct CLIDebuggerSystem* debugger) { debugger->printStatus = _printStatus; debugger->disassemble = _disassemble; - debugger->lookupPlatformIdentifier = _lookupPlatformIdentifier; debugger->platformName = "GB-Z80"; debugger->platformCommands = _lr35902Commands; } diff --git a/src/lr35902/debugger/debugger.c b/src/lr35902/debugger/debugger.c index ae6345b79..4f899ae1e 100644 --- a/src/lr35902/debugger/debugger.c +++ b/src/lr35902/debugger/debugger.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -23,6 +24,20 @@ static struct LR35902DebugBreakpoint* _lookupBreakpoint(struct LR35902DebugBreak return 0; } +static void _destroyBreakpoint(struct LR35902DebugBreakpoint* breakpoint) { + if (breakpoint->condition) { + parseFree(breakpoint->condition); + free(breakpoint->condition); + } +} + +static void _destroyWatchpoint(struct LR35902DebugWatchpoint* watchpoint) { + if (watchpoint->condition) { + parseFree(watchpoint->condition); + free(watchpoint->condition); + } +} + static void LR35902DebuggerCheckBreakpoints(struct mDebuggerPlatform* d) { struct LR35902Debugger* debugger = (struct LR35902Debugger*) d; struct LR35902DebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->breakpoints, debugger->cpu->pc); @@ -32,6 +47,13 @@ static void LR35902DebuggerCheckBreakpoints(struct mDebuggerPlatform* d) { if (breakpoint->segment >= 0 && debugger->cpu->memory.currentSegment(debugger->cpu, breakpoint->address) != breakpoint->segment) { return; } + if (breakpoint->condition) { + int32_t value; + int segment; + if (!mDebuggerEvaluateParseTree(d->p, breakpoint->condition, &value, &segment) || !(value || segment >= 0)) { + return; + } + } struct mDebuggerEntryInfo info = { .address = breakpoint->address }; @@ -44,12 +66,16 @@ static void LR35902DebuggerDeinit(struct mDebuggerPlatform* platform); static void LR35902DebuggerEnter(struct mDebuggerPlatform* d, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info); static void LR35902DebuggerSetBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment); +static void LR35902DebuggerSetConditionalBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment, struct ParseTree* condition); static void LR35902DebuggerClearBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment); static void LR35902DebuggerSetWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type); +static void LR35902DebuggerSetConditionalWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type, struct ParseTree* condition); static void LR35902DebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment); static void LR35902DebuggerCheckBreakpoints(struct mDebuggerPlatform*); static bool LR35902DebuggerHasBreakpoints(struct mDebuggerPlatform*); static void LR35902DebuggerTrace(struct mDebuggerPlatform*, char* out, size_t* length); +static bool LR35902DebuggerGetRegister(struct mDebuggerPlatform*, const char* name, int32_t* value); +static bool LR35902DebuggerSetRegister(struct mDebuggerPlatform*, const char* name, int32_t value); struct mDebuggerPlatform* LR35902DebuggerPlatformCreate(void) { struct mDebuggerPlatform* platform = (struct mDebuggerPlatform*) malloc(sizeof(struct LR35902Debugger)); @@ -57,12 +83,16 @@ struct mDebuggerPlatform* LR35902DebuggerPlatformCreate(void) { platform->init = LR35902DebuggerInit; platform->deinit = LR35902DebuggerDeinit; platform->setBreakpoint = LR35902DebuggerSetBreakpoint; + platform->setConditionalBreakpoint = LR35902DebuggerSetConditionalBreakpoint; platform->clearBreakpoint = LR35902DebuggerClearBreakpoint; platform->setWatchpoint = LR35902DebuggerSetWatchpoint; + platform->setConditionalWatchpoint = LR35902DebuggerSetConditionalWatchpoint; platform->clearWatchpoint = LR35902DebuggerClearWatchpoint; platform->checkBreakpoints = LR35902DebuggerCheckBreakpoints; platform->hasBreakpoints = LR35902DebuggerHasBreakpoints; platform->trace = LR35902DebuggerTrace; + platform->getRegister = LR35902DebuggerGetRegister; + platform->setRegister = LR35902DebuggerSetRegister; return platform; } @@ -75,7 +105,15 @@ void LR35902DebuggerInit(void* cpu, struct mDebuggerPlatform* platform) { void LR35902DebuggerDeinit(struct mDebuggerPlatform* platform) { struct LR35902Debugger* debugger = (struct LR35902Debugger*) platform; + size_t i; + for (i = 0; i < LR35902DebugBreakpointListSize(&debugger->breakpoints); ++i) { + _destroyBreakpoint(LR35902DebugBreakpointListGetPointer(&debugger->breakpoints, i)); + } LR35902DebugBreakpointListDeinit(&debugger->breakpoints); + + for (i = 0; i < LR35902DebugWatchpointListSize(&debugger->watchpoints); ++i) { + _destroyWatchpoint(LR35902DebugWatchpointListGetPointer(&debugger->watchpoints, i)); + } LR35902DebugWatchpointListDeinit(&debugger->watchpoints); } @@ -92,10 +130,15 @@ static void LR35902DebuggerEnter(struct mDebuggerPlatform* platform, enum mDebug } static void LR35902DebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) { + LR35902DebuggerSetConditionalBreakpoint(d, address, segment, NULL); +} + +static void LR35902DebuggerSetConditionalBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, struct ParseTree* condition) { struct LR35902Debugger* debugger = (struct LR35902Debugger*) d; struct LR35902DebugBreakpoint* breakpoint = LR35902DebugBreakpointListAppend(&debugger->breakpoints); breakpoint->address = address; breakpoint->segment = segment; + breakpoint->condition = condition; } static void LR35902DebuggerClearBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) { @@ -105,6 +148,7 @@ static void LR35902DebuggerClearBreakpoint(struct mDebuggerPlatform* d, uint32_t for (i = 0; i < LR35902DebugBreakpointListSize(breakpoints); ++i) { struct LR35902DebugBreakpoint* breakpoint = LR35902DebugBreakpointListGetPointer(breakpoints, i); if (breakpoint->address == address && breakpoint->segment == segment) { + _destroyBreakpoint(LR35902DebugBreakpointListGetPointer(breakpoints, i)); LR35902DebugBreakpointListShift(breakpoints, i, 1); } } @@ -116,6 +160,10 @@ static bool LR35902DebuggerHasBreakpoints(struct mDebuggerPlatform* d) { } static void LR35902DebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type) { + LR35902DebuggerSetConditionalWatchpoint(d, address, segment, type, NULL); +} + +static void LR35902DebuggerSetConditionalWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type, struct ParseTree* condition) { struct LR35902Debugger* debugger = (struct LR35902Debugger*) d; if (!LR35902DebugWatchpointListSize(&debugger->watchpoints)) { LR35902DebuggerInstallMemoryShim(debugger); @@ -124,6 +172,7 @@ static void LR35902DebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t a watchpoint->address = address; watchpoint->type = type; watchpoint->segment = segment; + watchpoint->condition = condition; } static void LR35902DebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) { @@ -168,3 +217,131 @@ static void LR35902DebuggerTrace(struct mDebuggerPlatform* d, char* out, size_t* cpu->d, cpu->e, cpu->h, cpu->l, cpu->sp, cpu->pc, disassembly); } + +bool LR35902DebuggerGetRegister(struct mDebuggerPlatform* d, const char* name, int32_t* value) { + struct LR35902Debugger* debugger = (struct LR35902Debugger*) d; + struct LR35902Core* cpu = debugger->cpu; + + if (strcmp(name, "a") == 0) { + *value = cpu->a; + return true; + } + if (strcmp(name, "b") == 0) { + *value = cpu->b; + return true; + } + if (strcmp(name, "c") == 0) { + *value = cpu->c; + return true; + } + if (strcmp(name, "d") == 0) { + *value = cpu->d; + return true; + } + if (strcmp(name, "e") == 0) { + *value = cpu->e; + return true; + } + if (strcmp(name, "h") == 0) { + *value = cpu->h; + return true; + } + if (strcmp(name, "l") == 0) { + *value = cpu->l; + return true; + } + if (strcmp(name, "bc") == 0) { + *value = cpu->bc; + return true; + } + if (strcmp(name, "de") == 0) { + *value = cpu->de; + return true; + } + if (strcmp(name, "hl") == 0) { + *value = cpu->hl; + return true; + } + if (strcmp(name, "af") == 0) { + *value = cpu->af; + return true; + } + if (strcmp(name, "pc") == 0) { + *value = cpu->pc; + return true; + } + if (strcmp(name, "sp") == 0) { + *value = cpu->sp; + return true; + } + if (strcmp(name, "f") == 0) { + *value = cpu->f.packed; + return true; + } + return false; +} + +bool LR35902DebuggerSetRegister(struct mDebuggerPlatform* d, const char* name, int32_t value) { + struct LR35902Debugger* debugger = (struct LR35902Debugger*) d; + struct LR35902Core* cpu = debugger->cpu; + + if (strcmp(name, "a") == 0) { + cpu->a = value; + return true; + } + if (strcmp(name, "b") == 0) { + cpu->b = value; + return true; + } + if (strcmp(name, "c") == 0) { + cpu->c = value; + return true; + } + if (strcmp(name, "d") == 0) { + cpu->d = value; + return true; + } + if (strcmp(name, "e") == 0) { + cpu->e = value; + return true; + } + if (strcmp(name, "h") == 0) { + cpu->h = value; + return true; + } + if (strcmp(name, "l") == 0) { + cpu->l = value; + return true; + } + if (strcmp(name, "bc") == 0) { + cpu->bc = value; + return true; + } + if (strcmp(name, "de") == 0) { + cpu->de = value; + return true; + } + if (strcmp(name, "hl") == 0) { + cpu->hl = value; + return true; + } + if (strcmp(name, "af") == 0) { + cpu->af = value; + cpu->f.packed &= 0xF0; + return true; + } + if (strcmp(name, "pc") == 0) { + cpu->pc = value; + cpu->memory.setActiveRegion(cpu, cpu->pc); + return true; + } + if (strcmp(name, "sp") == 0) { + cpu->sp = value; + return true; + } + if (strcmp(name, "f") == 0) { + cpu->f.packed = value & 0xF0; + return true; + } + return false; +} diff --git a/src/lr35902/debugger/memory-debugger.c b/src/lr35902/debugger/memory-debugger.c index 3b9f66343..b5c36a81e 100644 --- a/src/lr35902/debugger/memory-debugger.c +++ b/src/lr35902/debugger/memory-debugger.c @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#include #include #include @@ -27,19 +28,19 @@ static bool _checkWatchpoints(struct LR35902Debugger* debugger, uint16_t address debuggerFound: break; \ } while(0) -#define CREATE_WATCHPOINT_SHIM(NAME, RW, RETURN, TYPES, ...) \ +#define CREATE_WATCHPOINT_SHIM(NAME, RW, VALUE, RETURN, TYPES, ...) \ static RETURN DebuggerShim_ ## NAME TYPES { \ struct LR35902Debugger* debugger; \ FIND_DEBUGGER(debugger, cpu); \ struct mDebuggerEntryInfo info; \ - if (_checkWatchpoints(debugger, address, &info, WATCHPOINT_ ## RW, 0)) { \ + if (_checkWatchpoints(debugger, address, &info, WATCHPOINT_ ## RW, VALUE)) { \ mDebuggerEnter(debugger->d.p, DEBUGGER_ENTER_WATCHPOINT, &info); \ } \ return debugger->originalMemory.NAME(cpu, __VA_ARGS__); \ } -CREATE_WATCHPOINT_SHIM(load8, READ, uint8_t, (struct LR35902Core* cpu, uint16_t address), address) -CREATE_WATCHPOINT_SHIM(store8, WRITE, void, (struct LR35902Core* cpu, uint16_t address, int8_t value), address, value) +CREATE_WATCHPOINT_SHIM(load8, READ, 0, uint8_t, (struct LR35902Core* cpu, uint16_t address), address) +CREATE_WATCHPOINT_SHIM(store8, WRITE, value, void, (struct LR35902Core* cpu, uint16_t address, int8_t value), address, value) static bool _checkWatchpoints(struct LR35902Debugger* debugger, uint16_t address, struct mDebuggerEntryInfo* info, enum mWatchpointType type, uint8_t newValue) { struct LR35902DebugWatchpoint* watchpoint; @@ -47,11 +48,18 @@ static bool _checkWatchpoints(struct LR35902Debugger* debugger, uint16_t address for (i = 0; i < LR35902DebugWatchpointListSize(&debugger->watchpoints); ++i) { watchpoint = LR35902DebugWatchpointListGetPointer(&debugger->watchpoints, i); if (watchpoint->address == address && (watchpoint->segment < 0 || watchpoint->segment == debugger->originalMemory.currentSegment(debugger->cpu, address)) && watchpoint->type & type) { - info->oldValue = debugger->originalMemory.load8(debugger->cpu, address); - info->newValue = newValue; + if (watchpoint->condition) { + int32_t value; + int segment; + if (!mDebuggerEvaluateParseTree(debugger->d.p, watchpoint->condition, &value, &segment) || !(value || segment >= 0)) { + return false; + } + } + info->type.wp.oldValue = debugger->originalMemory.load8(debugger->cpu, address); + info->type.wp.newValue = newValue; info->address = address; - info->watchType = watchpoint->type; - info->accessType = type; + info->type.wp.watchType = watchpoint->type; + info->type.wp.accessType = type; return true; } } diff --git a/src/lr35902/lr35902.c b/src/lr35902/lr35902.c index 6dd9db9ef..23878c310 100644 --- a/src/lr35902/lr35902.c +++ b/src/lr35902/lr35902.c @@ -137,22 +137,22 @@ static void _LR35902Step(struct LR35902Core* cpu) { } void LR35902Tick(struct LR35902Core* cpu) { + if (cpu->cycles >= cpu->nextEvent) { + cpu->irqh.processEvents(cpu); + } _LR35902Step(cpu); if (cpu->cycles + 2 >= cpu->nextEvent) { int32_t diff = cpu->nextEvent - cpu->cycles; cpu->cycles = cpu->nextEvent; cpu->executionState += diff; cpu->irqh.processEvents(cpu); - cpu->cycles += 2 - diff; + cpu->cycles += LR35902_CORE_EXECUTE - cpu->executionState; } else { cpu->cycles += 2; } cpu->executionState = LR35902_CORE_FETCH; cpu->instruction(cpu); ++cpu->cycles; - if (cpu->cycles >= cpu->nextEvent) { - cpu->irqh.processEvents(cpu); - } } void LR35902Run(struct LR35902Core* cpu) { @@ -168,7 +168,7 @@ void LR35902Run(struct LR35902Core* cpu) { cpu->cycles = cpu->nextEvent; cpu->executionState += diff; cpu->irqh.processEvents(cpu); - cpu->cycles += 2 - diff; + cpu->cycles += LR35902_CORE_EXECUTE - cpu->executionState; running = false; } else { cpu->cycles += 2; diff --git a/src/platform/3ds/3ds-vfs.c b/src/platform/3ds/3ds-vfs.c index 9de4b1ab3..975d63fdf 100644 --- a/src/platform/3ds/3ds-vfs.c +++ b/src/platform/3ds/3ds-vfs.c @@ -241,7 +241,11 @@ static struct VFile* _vd3dOpenFile(struct VDir* vd, const char* path, int mode) } const char* dir = vd3d->path; char* combined = malloc(sizeof(char) * (strlen(path) + strlen(dir) + 2)); - sprintf(combined, "%s/%s", dir, path); + if (dir[strlen(dir) - 1] == '/') { + sprintf(combined, "%s%s", dir, path); + } else { + sprintf(combined, "%s/%s", dir, path); + } struct VFile* file = VFileOpen(combined, mode); free(combined); @@ -255,7 +259,11 @@ static struct VDir* _vd3dOpenDir(struct VDir* vd, const char* path) { } const char* dir = vd3d->path; char* combined = malloc(sizeof(char) * (strlen(path) + strlen(dir) + 2)); - sprintf(combined, "%s/%s", dir, path); + if (dir[strlen(dir) - 1] == '/') { + sprintf(combined, "%s%s", dir, path); + } else { + sprintf(combined, "%s/%s", dir, path); + } struct VDir* vd2 = VDirOpen(combined); if (!vd2) { @@ -272,7 +280,11 @@ static bool _vd3dDeleteFile(struct VDir* vd, const char* path) { } const char* dir = vd3d->path; char* combined = malloc(sizeof(char) * (strlen(path) + strlen(dir) + 2)); - sprintf(combined, "%s/%s", dir, path); + if (dir[strlen(dir) - 1] == '/') { + sprintf(combined, "%s%s", dir, path); + } else { + sprintf(combined, "%s/%s", dir, path); + } uint16_t utf16Path[PATH_MAX + 1]; ssize_t units = utf8_to_utf16(utf16Path, (const uint8_t*) combined, PATH_MAX); diff --git a/src/platform/3ds/CMakeLists.txt b/src/platform/3ds/CMakeLists.txt index bea542412..9218d5438 100644 --- a/src/platform/3ds/CMakeLists.txt +++ b/src/platform/3ds/CMakeLists.txt @@ -57,6 +57,10 @@ add_custom_command(OUTPUT ${BINARY_NAME}.smdh COMMAND ${BANNERTOOL} makesmdh -s "${PROJECT_NAME}" -l "${SUMMARY}" -p "endrift" -i ${CMAKE_SOURCE_DIR}/res/mgba-48.png -o ${BINARY_NAME}.smdh DEPENDS ${CMAKE_SOURCE_DIR}/res/${BINARY_NAME}-48.png) +add_custom_command(OUTPUT ${BINARY_NAME}.xml + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/hbl.xml ${BINARY_NAME}.xml + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/hbl.xml) + add_custom_command(OUTPUT ${BINARY_NAME}.bnr COMMAND ${BANNERTOOL} makebanner -ci ${CMAKE_CURRENT_SOURCE_DIR}/banner.cgfx -a ${CMAKE_CURRENT_SOURCE_DIR}/bios.wav -o ${BINARY_NAME}.bnr DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/banner.cgfx ${CMAKE_CURRENT_SOURCE_DIR}/bios.wav) @@ -84,7 +88,7 @@ add_custom_command( add_custom_command(OUTPUT ${BINARY_NAME}.3dsx COMMAND ${3DSXTOOL} ${BINARY_NAME}.elf ${BINARY_NAME}.3dsx --smdh=${BINARY_NAME}.smdh - DEPENDS ${BINARY_NAME}.elf ${BINARY_NAME}.smdh) + DEPENDS ${BINARY_NAME}.elf ${BINARY_NAME}.smdh ${BINARY_NAME}.xml) add_custom_target(${BINARY_NAME}.3dsx ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${BINARY_NAME}.3dsx) add_custom_command(OUTPUT ${BINARY_NAME}.cia @@ -112,5 +116,8 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cia.rsf.in ${CMAKE_CURRENT_BINARY_DIR install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${BINARY_NAME}.3dsx ${CMAKE_CURRENT_BINARY_DIR}/${BINARY_NAME}.smdh + ${CMAKE_CURRENT_BINARY_DIR}/${BINARY_NAME}.xml + DESTINATION 3dsx COMPONENT ${BINARY_NAME}-3ds) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${BINARY_NAME}.cia - DESTINATION . COMPONENT ${BINARY_NAME}-3ds) + DESTINATION cia COMPONENT ${BINARY_NAME}-3ds) diff --git a/src/platform/3ds/CMakeToolchain.txt b/src/platform/3ds/CMakeToolchain.txt index 9b67d3013..32ef76c98 100644 --- a/src/platform/3ds/CMakeToolchain.txt +++ b/src/platform/3ds/CMakeToolchain.txt @@ -23,9 +23,9 @@ endif() set(CMAKE_PROGRAM_PATH ${DEVKITARM}/bin) set(cross_prefix arm-none-eabi-) -set(arch_flags "-march=armv6k -mtune=mpcore -mfpu=vfp -mfloat-abi=hard") +set(arch_flags "-march=armv6k -mtune=mpcore -mfloat-abi=hard -ffunction-sections") set(inc_flags "-I${CTRULIB}/include ${arch_flags} -mword-relocations") -set(link_flags "-L${CTRULIB}/lib -lctru -specs=3dsx.specs ${arch_flags}") +set(link_flags "-L${CTRULIB}/lib -lctru -specs=3dsx.specs ${arch_flags} -Wl,--gc-sections") set(CMAKE_SYSTEM_NAME Generic CACHE INTERNAL "system name") set(CMAKE_SYSTEM_PROCESSOR arm CACHE INTERNAL "processor") diff --git a/src/platform/3ds/cia.rsf.in b/src/platform/3ds/cia.rsf.in index a5dda0b83..507da46e0 100644 --- a/src/platform/3ds/cia.rsf.in +++ b/src/platform/3ds/cia.rsf.in @@ -174,7 +174,6 @@ AccessControlInfo: - ldr:ro - ir:USER - ir:u - - csnd:SND SystemControlInfo: @@ -195,7 +194,6 @@ SystemControlInfo: cecd: 0x0004013000002602L cfg: 0x0004013000001702L codec: 0x0004013000001802L - csnd: 0x0004013000002702L dlp: 0x0004013000002802L dsp: 0x0004013000001a02L friends: 0x0004013000003202L diff --git a/src/platform/3ds/gui-font.c b/src/platform/3ds/gui-font.c index 3c1abc0a0..67d44f4c9 100644 --- a/src/platform/3ds/gui-font.c +++ b/src/platform/3ds/gui-font.c @@ -11,13 +11,12 @@ #include "ctr-gpu.h" -#define CELL_HEIGHT 16 -#define CELL_WIDTH 16 -#define FONT_SIZE 0.52f +#define FONT_SIZE 15.6f struct GUIFont { C3D_Tex* sheets; C3D_Tex icons; + float size; }; struct GUIFont* GUIFontCreate(void) { @@ -29,6 +28,7 @@ struct GUIFont* GUIFontCreate(void) { C3D_Tex* tex; TGLP_s* glyphInfo = fontGetGlyphInfo(); + guiFont->size = FONT_SIZE / glyphInfo->cellHeight; guiFont->sheets = malloc(sizeof(*guiFont->sheets) * glyphInfo->nSheets); int i; @@ -59,16 +59,14 @@ void GUIFontDestroy(struct GUIFont* font) { } unsigned GUIFontHeight(const struct GUIFont* font) { - UNUSED(font); - return fontGetInfo()->lineFeed * FONT_SIZE; + return fontGetInfo()->lineFeed * font->size; } unsigned GUIFontGlyphWidth(const struct GUIFont* font, uint32_t glyph) { - UNUSED(font); int index = fontGlyphIndexFromCodePoint(glyph); charWidthInfo_s* info = fontGetCharWidthInfo(index); if (info) { - return info->charWidth * FONT_SIZE; + return info->charWidth * font->size; } return 0; } @@ -108,7 +106,7 @@ void GUIFontDrawGlyph(const struct GUIFont* font, int glyph_x, int glyph_y, uint u16 v = tex->height * data.texcoord.bottom; ctrAddRectEx(color, x, y, - tex->width * width * FONT_SIZE, tex->height * height * -FONT_SIZE, + tex->width * width * font->size, tex->height * height * -font->size, u, v, tex->width * width, tex->height * height, 0); } diff --git a/src/platform/3ds/hbl.xml b/src/platform/3ds/hbl.xml new file mode 100644 index 000000000..a004677ce --- /dev/null +++ b/src/platform/3ds/hbl.xml @@ -0,0 +1,8 @@ + + 0004001000020e00 + 0004001000021e00 + 0004001000022e00 + 0004001000026e00 + 0004001000027e00 + 0004001000028e00 + diff --git a/src/platform/3ds/main.c b/src/platform/3ds/main.c index 7c54d1bae..71b77f1a1 100644 --- a/src/platform/3ds/main.c +++ b/src/platform/3ds/main.c @@ -6,6 +6,7 @@ #include #include +#include #ifdef M_CORE_GBA #include #include @@ -22,6 +23,7 @@ #include #include +#include #include "ctr-gpu.h" #include <3ds.h> @@ -78,8 +80,7 @@ static struct m3DSImageSource { static enum { NO_SOUND, - DSP_SUPPORTED, - CSND_SUPPORTED + DSP_SUPPORTED } hasSound; // TODO: Move into context @@ -154,37 +155,19 @@ static void _cleanup(void) { linearFree(audioLeft); } - if (hasSound == CSND_SUPPORTED) { - linearFree(audioRight); - csndExit(); - } - if (hasSound == DSP_SUPPORTED) { ndspExit(); } camExit(); - csndExit(); + ndspExit(); ptmuExit(); } static void _aptHook(APT_HookType hook, void* user) { UNUSED(user); switch (hook) { - case APTHOOK_ONSUSPEND: - case APTHOOK_ONSLEEP: - if (hasSound == CSND_SUPPORTED) { - CSND_SetPlayState(8, 0); - CSND_SetPlayState(9, 0); - csndExecCmds(false); - } - break; case APTHOOK_ONEXIT: - if (hasSound == CSND_SUPPORTED) { - CSND_SetPlayState(8, 0); - CSND_SetPlayState(9, 0); - csndExecCmds(false); - } _cleanup(); exit(0); break; @@ -197,33 +180,6 @@ static void _map3DSKey(struct mInputMap* map, int ctrKey, enum GBAKey key) { mInputBindKey(map, _3DS_INPUT, __builtin_ctz(ctrKey), key); } -static void _csndPlaySound(u32 flags, u32 sampleRate, float vol, void* left, void* right, u32 size) { - u32 pleft = 0, pright = 0; - - int loopMode = (flags >> 10) & 3; - if (!loopMode) { - flags |= SOUND_ONE_SHOT; - } - - pleft = osConvertVirtToPhys(left); - pright = osConvertVirtToPhys(right); - - u32 timer = CSND_TIMER(sampleRate); - if (timer < 0x0042) { - timer = 0x0042; - } - else if (timer > 0xFFFF) { - timer = 0xFFFF; - } - flags &= ~0xFFFF001F; - flags |= SOUND_ENABLE | (timer << 16); - - u32 volumes = CSND_VOL(vol, -1.0); - CSND_SetChnRegs(flags | SOUND_CHANNEL(8), pleft, pleft, size, volumes, volumes); - volumes = CSND_VOL(vol, 1.0); - CSND_SetChnRegs(flags | SOUND_CHANNEL(9), pright, pright, size, volumes, volumes); -} - static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right); static void _drawStart(void) { @@ -381,12 +337,7 @@ static void _gameLoaded(struct mGUIRunner* runner) { if (hasSound != NO_SOUND) { audioPos = 0; } - if (hasSound == CSND_SUPPORTED) { - memset(audioLeft, 0, AUDIO_SAMPLE_BUFFER * sizeof(int16_t)); - memset(audioRight, 0, AUDIO_SAMPLE_BUFFER * sizeof(int16_t)); - _csndPlaySound(SOUND_REPEAT | SOUND_FORMAT_16BIT, 32768, 1.0, audioLeft, audioRight, AUDIO_SAMPLE_BUFFER * sizeof(int16_t)); - csndExecCmds(false); - } else if (hasSound == DSP_SUPPORTED) { + if (hasSound == DSP_SUPPORTED) { memset(audioLeft, 0, AUDIO_SAMPLE_BUFFER * 2 * sizeof(int16_t)); } unsigned mode; @@ -428,11 +379,6 @@ static void _gameLoaded(struct mGUIRunner* runner) { } static void _gameUnloaded(struct mGUIRunner* runner) { - if (hasSound == CSND_SUPPORTED) { - CSND_SetPlayState(8, 0); - CSND_SetPlayState(9, 0); - csndExecCmds(false); - } osSetSpeedupEnable(false); frameLimiter = true; @@ -678,6 +624,11 @@ static void _setFrameLimiter(struct mGUIRunner* runner, bool limit) { tickCounter = svcGetSystemTick(); } +static bool _running(struct mGUIRunner* runner) { + UNUSED(runner); + return aptMainLoop(); +} + static uint32_t _pollInput(const struct mInputMap* map) { hidScanInput(); int activeKeys = hidKeysHeld(); @@ -789,22 +740,7 @@ static void _requestImage(struct mImageSource* source, const void** buffer, size static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right) { UNUSED(stream); - if (hasSound == CSND_SUPPORTED) { - blip_read_samples(left, &audioLeft[audioPos], AUDIO_SAMPLES, false); - blip_read_samples(right, &audioRight[audioPos], AUDIO_SAMPLES, false); - GSPGPU_FlushDataCache(&audioLeft[audioPos], AUDIO_SAMPLES * sizeof(int16_t)); - GSPGPU_FlushDataCache(&audioRight[audioPos], AUDIO_SAMPLES * sizeof(int16_t)); - audioPos = (audioPos + AUDIO_SAMPLES) % AUDIO_SAMPLE_BUFFER; - if (audioPos == AUDIO_SAMPLES * 3) { - u8 playing = 0; - csndIsPlaying(0x8, &playing); - if (!playing) { - CSND_SetPlayState(0x8, 1); - CSND_SetPlayState(0x9, 1); - csndExecCmds(false); - } - } - } else if (hasSound == DSP_SUPPORTED) { + if (hasSound == DSP_SUPPORTED) { int startId = bufferId; while (dspBuffer[bufferId].status == NDSP_WBUF_QUEUED || dspBuffer[bufferId].status == NDSP_WBUF_PLAYING) { bufferId = (bufferId + 1) & (DSP_BUFFERS - 1); @@ -872,12 +808,6 @@ int main() { } } - if (hasSound == NO_SOUND && !csndInit()) { - hasSound = CSND_SUPPORTED; - audioLeft = linearMemAlign(AUDIO_SAMPLE_BUFFER * sizeof(int16_t), 0x80); - audioRight = linearMemAlign(AUDIO_SAMPLE_BUFFER * sizeof(int16_t), 0x80); - } - gfxInit(GSP_BGR8_OES, GSP_BGR8_OES, true); if (!_initGpu()) { @@ -1021,9 +951,17 @@ int main() { .unpaused = _gameLoaded, .incrementScreenMode = _incrementScreenMode, .setFrameLimiter = _setFrameLimiter, - .pollGameInput = _pollGameInput + .pollGameInput = _pollGameInput, + .running = _running }; + runner.autosave.running = true; + MutexInit(&runner.autosave.mutex); + ConditionInit(&runner.autosave.cond); + + APT_SetAppCpuTimeLimit(20); + runner.autosave.thread = threadCreate(mGUIAutosaveThread, &runner.autosave, 0x4000, 0x1F, 1, true); + mGUIInit(&runner, "3ds"); _map3DSKey(&runner.params.keyMap, KEY_X, GUI_INPUT_CANCEL); diff --git a/src/platform/example/client-server/client.c b/src/platform/example/client-server/client.c index 86d8fdc6f..3c20144ff 100644 --- a/src/platform/example/client-server/client.c +++ b/src/platform/example/client-server/client.c @@ -29,19 +29,19 @@ int main() { SocketRecv(server, &bpp, sizeof(bpp)); width = ntohl(width); height = ntohl(height); - if (ntohl(bpp) != BYTES_PER_PIXEL) { + bpp = ntohl(bpp); + ssize_t bufferSize = width * height * bpp; + +#if !SDL_VERSION_ATLEAST(2, 0, 0) + if (bpp == 2) { + SDL_SetVideoMode(width, height, 16, SDL_DOUBLEBUF | SDL_HWSURFACE); + } else if (bpp == 4) { + SDL_SetVideoMode(width, height, 32, SDL_DOUBLEBUF | SDL_HWSURFACE); + } else { SocketClose(server); SocketSubsystemDeinit(); return 1; } - ssize_t bufferSize = width * height * BYTES_PER_PIXEL; - -#if !SDL_VERSION_ATLEAST(2, 0, 0) -#ifdef COLOR_16_BIT - SDL_SetVideoMode(width, height, 16, SDL_DOUBLEBUF | SDL_HWSURFACE); -#else - SDL_SetVideoMode(width, height, 32, SDL_DOUBLEBUF | SDL_HWSURFACE); -#endif #endif #if SDL_VERSION_ATLEAST(2, 0, 0) @@ -49,15 +49,16 @@ int main() { SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); Uint32 pixfmt; -#ifdef COLOR_16_BIT -#ifdef COLOR_5_6_5 - pixfmt = SDL_PIXELFORMAT_RGB565; -#else - pixfmt = SDL_PIXELFORMAT_ABGR1555; -#endif -#else - pixfmt = SDL_PIXELFORMAT_ABGR8888; -#endif + if (bpp == 2) { + pixfmt = SDL_PIXELFORMAT_RGB565; + } else if (bpp == 4) { + pixfmt = SDL_PIXELFORMAT_ABGR8888; + } else { + SocketClose(server); + SocketSubsystemDeinit(); + return 1; + } + SDL_Texture* sdlTex = SDL_CreateTexture(renderer, pixfmt, SDL_TEXTUREACCESS_STREAMING, width, height); #endif diff --git a/src/platform/libretro/libretro.c b/src/platform/libretro/libretro.c index bafc88b9c..707f3eaf6 100644 --- a/src/platform/libretro/libretro.c +++ b/src/platform/libretro/libretro.c @@ -88,6 +88,13 @@ static void _reloadSettings(void) { } } + var.key = "mgba_frameskip"; + var.value = 0; + if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { + opts.frameskip = strtol(var.value, NULL, 10); + + } + mCoreConfigLoadDefaults(&core->config, &opts); mCoreLoadConfig(core); } @@ -105,6 +112,7 @@ void retro_set_environment(retro_environment_t env) { { "mgba_use_bios", "Use BIOS file if found (requires restart); ON|OFF" }, { "mgba_skip_bios", "Skip BIOS intro (requires restart); OFF|ON" }, { "mgba_idle_optimization", "Idle loop removal; Remove Known|Detect and Remove|Don't Remove" }, + { "mgba_frameskip", "Frameskip; 0|1|2|3|4|5|6|7|8|9|10" }, { 0, 0 } }; @@ -223,16 +231,22 @@ void retro_run(void) { uint16_t keys; inputPollCallback(); - struct retro_variable var = { - .key = "mgba_allow_opposing_directions", - .value = 0 - }; - bool updated = false; if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated) { + struct retro_variable var = { + .key = "mgba_allow_opposing_directions", + .value = 0 + }; if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { ((struct GBA*) core->board)->allowOpposingDirections = strcmp(var.value, "yes") == 0; } + + var.key = "mgba_frameskip"; + var.value = 0; + if (environCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { + mCoreConfigSetUIntValue(&core->config, "frameskip", strtol(var.value, NULL, 10)); + mCoreLoadConfig(core); + } } keys = 0; diff --git a/src/platform/opengl/gl.c b/src/platform/opengl/gl.c index cb9bdd106..8d8c2b7ef 100644 --- a/src/platform/opengl/gl.c +++ b/src/platform/opengl/gl.c @@ -38,6 +38,8 @@ static void mGLContextSetDimensions(struct VideoBackend* v, unsigned width, unsi #else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toPow2(width), toPow2(height), 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 0); #endif +#elif defined(__BIG_ENDIAN__) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toPow2(width), toPow2(height), 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, 0); #else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, toPow2(width), toPow2(height), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); #endif @@ -116,6 +118,8 @@ void mGLContextPostFrame(struct VideoBackend* v, const void* frame) { #else glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, v->width, v->height, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, frame); #endif +#elif defined(__BIG_ENDIAN__) + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, v->width, v->height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, frame); #else glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, v->width, v->height, GL_RGBA, GL_UNSIGNED_BYTE, frame); #endif diff --git a/src/platform/opengl/gles2.c b/src/platform/opengl/gles2.c index 84343e35f..94d0856ca 100644 --- a/src/platform/opengl/gles2.c +++ b/src/platform/opengl/gles2.c @@ -149,6 +149,8 @@ static void mGLES2ContextSetDimensions(struct VideoBackend* v, unsigned width, u #else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, 0); #endif +#elif defined(__BIG_ENDIAN__) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, 0); #else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); #endif @@ -324,6 +326,8 @@ void mGLES2ContextPostFrame(struct VideoBackend* v, const void* frame) { #else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, v->width, v->height, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, frame); #endif +#elif defined(__BIG_ENDIAN__) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, v->width, v->height, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, frame); #else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, v->width, v->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, frame); #endif diff --git a/src/platform/psp2/CMakeLists.txt b/src/platform/psp2/CMakeLists.txt index eb8f31d75..34ea754e7 100644 --- a/src/platform/psp2/CMakeLists.txt +++ b/src/platform/psp2/CMakeLists.txt @@ -13,7 +13,21 @@ source_group("PS Vita-specific code" FILES ${OS_SRC}) list(APPEND CORE_VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/sce-vfs.c) set(CORE_VFS_SRC ${CORE_VFS_SRC} PARENT_SCOPE) -set(OS_LIB -lvita2d -lSceAppMgr_stub -lSceCtrl_stub -lSceAudio_stub -lSceCamera_stub -lSceCommonDialog_stub -lSceDisplay_stub -lSceGxm_stub -lSceMotion_stub -lScePgf_stub -lScePhotoExport_stub -lScePower_stub -lSceSysmodule_stub -lSceTouch_stub -l${M_LIBRARY}) +set(OS_LIB -lvita2d -l${M_LIBRARY} + -lSceAppMgr_stub + -lSceAppUtil_stub + -lSceAudio_stub + -lSceCamera_stub + -lSceCommonDialog_stub + -lSceCtrl_stub + -lSceDisplay_stub + -lSceGxm_stub + -lSceMotion_stub + -lScePgf_stub + -lScePhotoExport_stub + -lScePower_stub + -lSceSysmodule_stub + -lSceTouch_stub) set(OBJCOPY_CMD ${OBJCOPY} -I binary -O elf32-littlearm -B arm) list(APPEND GUI_SRC ${CMAKE_CURRENT_SOURCE_DIR}/gui-font.c) diff --git a/src/platform/psp2/main.c b/src/platform/psp2/main.c index 40068e700..c33c7228c 100644 --- a/src/platform/psp2/main.c +++ b/src/platform/psp2/main.c @@ -12,12 +12,14 @@ #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -87,7 +89,8 @@ int main() { struct mGUIRunner runner = { .params = { PSP2_HORIZONTAL_PIXELS, PSP2_VERTICAL_PIXELS, - font, "ux0:data", _drawStart, _drawEnd, + font, "", + _drawStart, _drawEnd, _pollInput, _pollCursor, _batteryState, 0, 0, @@ -150,7 +153,7 @@ int main() { .teardown = mPSP2Teardown, .gameLoaded = mPSP2LoadROM, .gameUnloaded = mPSP2UnloadROM, - .prepareForFrame = mPSP2PrepareForFrame, + .prepareForFrame = NULL, .drawFrame = mPSP2Draw, .drawScreenshot = mPSP2DrawScreenshot, .paused = mPSP2Paused, @@ -163,11 +166,26 @@ int main() { sceTouchSetSamplingState(SCE_TOUCH_PORT_FRONT, SCE_TOUCH_SAMPLING_STATE_START); sceCtrlSetSamplingMode(SCE_CTRL_MODE_ANALOG_WIDE); sceSysmoduleLoadModule(SCE_SYSMODULE_PHOTO_EXPORT); + sceSysmoduleLoadModule(SCE_SYSMODULE_APPUTIL); mGUIInit(&runner, "psvita"); - mPSP2MapKey(&runner.params.keyMap, SCE_CTRL_CROSS, GUI_INPUT_SELECT); - mPSP2MapKey(&runner.params.keyMap, SCE_CTRL_CIRCLE, GUI_INPUT_BACK); + int enterButton; + SceAppUtilInitParam initParam; + SceAppUtilBootParam bootParam; + memset(&initParam, 0, sizeof(SceAppUtilInitParam)); + memset(&bootParam, 0, sizeof(SceAppUtilBootParam)); + sceAppUtilInit(&initParam, &bootParam); + sceAppUtilSystemParamGetInt(SCE_SYSTEM_PARAM_ID_ENTER_BUTTON, &enterButton); + sceAppUtilShutdown(); + + if (enterButton == SCE_SYSTEM_PARAM_ENTER_BUTTON_CIRCLE) { + mPSP2MapKey(&runner.params.keyMap, SCE_CTRL_CROSS, GUI_INPUT_BACK); + mPSP2MapKey(&runner.params.keyMap, SCE_CTRL_CIRCLE, GUI_INPUT_SELECT); + } else { + mPSP2MapKey(&runner.params.keyMap, SCE_CTRL_CROSS, GUI_INPUT_SELECT); + mPSP2MapKey(&runner.params.keyMap, SCE_CTRL_CIRCLE, GUI_INPUT_BACK); + } mPSP2MapKey(&runner.params.keyMap, SCE_CTRL_TRIANGLE, GUI_INPUT_CANCEL); mPSP2MapKey(&runner.params.keyMap, SCE_CTRL_UP, GUI_INPUT_UP); mPSP2MapKey(&runner.params.keyMap, SCE_CTRL_DOWN, GUI_INPUT_DOWN); diff --git a/src/platform/psp2/psp2-context.c b/src/platform/psp2/psp2-context.c index 278c7419d..93d0d98e3 100644 --- a/src/platform/psp2/psp2-context.c +++ b/src/platform/psp2/psp2-context.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -71,16 +70,20 @@ static struct mSceImageSource { size_t bufferOffset; } camera; +static struct mAVStream stream; + bool frameLimiter = true; extern const uint8_t _binary_backdrop_png_start[]; static vita2d_texture* backdrop = 0; -#define PSP2_SAMPLES 256 -#define PSP2_AUDIO_BUFFER_SIZE (PSP2_SAMPLES * 20) +#define PSP2_SAMPLES 512 +#define PSP2_AUDIO_BUFFER_SIZE (PSP2_SAMPLES * 16) static struct mPSP2AudioContext { - struct RingFIFO buffer; + struct GBAStereoSample buffer[PSP2_AUDIO_BUFFER_SIZE]; + size_t writeOffset; + size_t readOffset; size_t samples; Mutex mutex; Condition cond; @@ -93,26 +96,25 @@ void mPSP2MapKey(struct mInputMap* map, int pspKey, int key) { static THREAD_ENTRY _audioThread(void* context) { struct mPSP2AudioContext* audio = (struct mPSP2AudioContext*) context; + uint32_t zeroBuffer[PSP2_SAMPLES] = {0}; int audioPort = sceAudioOutOpenPort(SCE_AUDIO_OUT_PORT_TYPE_MAIN, PSP2_SAMPLES, 48000, SCE_AUDIO_OUT_MODE_STEREO); while (audio->running) { MutexLock(&audio->mutex); - int len = audio->samples; - if (len > PSP2_SAMPLES) { - len = PSP2_SAMPLES; + void* buffer; + if (audio->samples >= PSP2_SAMPLES) { + buffer = &audio->buffer[audio->readOffset]; + audio->samples -= PSP2_SAMPLES; + audio->readOffset += PSP2_SAMPLES; + if (audio->readOffset >= PSP2_AUDIO_BUFFER_SIZE) { + audio->readOffset = 0; + } + ConditionWake(&audio->cond); + } else { + buffer = zeroBuffer; } - struct GBAStereoSample* buffer = audio->buffer.readPtr; - RingFIFORead(&audio->buffer, NULL, len * 4); - audio->samples -= len; - ConditionWake(&audio->cond); - MutexUnlock(&audio->mutex); + sceAudioOutOutput(audioPort, buffer); - MutexLock(&audio->mutex); - - if (audio->samples < PSP2_SAMPLES) { - ConditionWait(&audio->cond, &audio->mutex); - } - MutexUnlock(&audio->mutex); } sceAudioOutReleasePort(audioPort); return 0; @@ -229,6 +231,29 @@ static void _requestImage(struct mImageSource* source, const void** buffer, size sceCameraRead(imageSource->cam - 1, &read); } +static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* right) { + UNUSED(stream); + MutexLock(&audioContext.mutex); + struct GBAStereoSample* samples = &audioContext.buffer[audioContext.writeOffset]; + while (audioContext.samples == PSP2_AUDIO_BUFFER_SIZE) { + if (!frameLimiter) { + blip_clear(left); + blip_clear(right); + MutexUnlock(&audioContext.mutex); + return; + } + ConditionWait(&audioContext.cond, &audioContext.mutex); + } + blip_read_samples(left, &samples[0].left, PSP2_SAMPLES, true); + blip_read_samples(right, &samples[0].right, PSP2_SAMPLES, true); + audioContext.samples += PSP2_SAMPLES; + audioContext.writeOffset += PSP2_SAMPLES; + if (audioContext.writeOffset >= PSP2_AUDIO_BUFFER_SIZE) { + audioContext.writeOffset = 0; + } + MutexUnlock(&audioContext.mutex); +} + uint16_t mPSP2PollInput(struct mGUIRunner* runner) { SceCtrlData pad; sceCtrlPeekBufferPositive(0, &pad, 1); @@ -285,6 +310,7 @@ void mPSP2Setup(struct mGUIRunner* runner) { outputBuffer = vita2d_texture_get_datap(tex); runner->core->setVideoBuffer(runner->core, outputBuffer, 256); + runner->core->setAudioBufferSize(runner->core, PSP2_SAMPLES); rotation.d.sample = _sampleRotation; rotation.d.readTiltX = _readTiltX; @@ -303,6 +329,13 @@ void mPSP2Setup(struct mGUIRunner* runner) { camera.cam = 1; runner->core->setPeripheral(runner->core, mPERIPH_IMAGE_SOURCE, &camera.d); + + stream.videoDimensionsChanged = NULL; + stream.postAudioFrame = NULL; + stream.postAudioBuffer = _postAudioBuffer; + stream.postVideoFrame = NULL; + runner->core->setAVStream(runner->core, &stream); + frameLimiter = true; backdrop = vita2d_load_PNG_buffer(_binary_backdrop_png_start); @@ -341,37 +374,15 @@ void mPSP2LoadROM(struct mGUIRunner* runner) { break; } - RingFIFOInit(&audioContext.buffer, PSP2_AUDIO_BUFFER_SIZE * sizeof(struct GBAStereoSample)); MutexInit(&audioContext.mutex); ConditionInit(&audioContext.cond); + memset(audioContext.buffer, 0, sizeof(audioContext.buffer)); + audioContext.readOffset = 0; + audioContext.writeOffset = 0; audioContext.running = true; ThreadCreate(&audioThread, _audioThread, &audioContext); } -void mPSP2PrepareForFrame(struct mGUIRunner* runner) { - int nSamples = 0; - while (blip_samples_avail(runner->core->getAudioChannel(runner->core, 0)) >= PSP2_SAMPLES) { - struct GBAStereoSample* samples = audioContext.buffer.writePtr; - if (nSamples > (PSP2_AUDIO_BUFFER_SIZE >> 2) + (PSP2_AUDIO_BUFFER_SIZE >> 1)) { // * 0.75 - if (!frameLimiter) { - blip_clear(runner->core->getAudioChannel(runner->core, 0)); - blip_clear(runner->core->getAudioChannel(runner->core, 1)); - break; - } - } - blip_read_samples(runner->core->getAudioChannel(runner->core, 0), &samples[0].left, PSP2_SAMPLES, true); - blip_read_samples(runner->core->getAudioChannel(runner->core, 1), &samples[0].right, PSP2_SAMPLES, true); - while (!RingFIFOWrite(&audioContext.buffer, NULL, PSP2_SAMPLES * 4)) { - ConditionWake(&audioContext.cond); - // Spinloooooooop! - } - MutexLock(&audioContext.mutex); - audioContext.samples += PSP2_SAMPLES; - nSamples = audioContext.samples; - ConditionWake(&audioContext.cond); - MutexUnlock(&audioContext.mutex); - } -} void mPSP2UnloadROM(struct mGUIRunner* runner) { switch (runner->core->platform(runner->core)) { diff --git a/src/platform/psp2/sce-vfs.c b/src/platform/psp2/sce-vfs.c index 284479660..383847291 100644 --- a/src/platform/psp2/sce-vfs.c +++ b/src/platform/psp2/sce-vfs.c @@ -10,6 +10,10 @@ #include #include +#ifndef SCE_CST_SIZE +#define SCE_CST_SIZE 0x0004 +#endif + struct VFileSce { struct VFile d; @@ -48,6 +52,16 @@ static bool _vdsceDeleteFile(struct VDir* vd, const char* path); static const char* _vdesceName(struct VDirEntry* vde); static enum VFSType _vdesceType(struct VDirEntry* vde); +static bool _vdlsceClose(struct VDir* vd); +static void _vdlsceRewind(struct VDir* vd); +static struct VDirEntry* _vdlsceListNext(struct VDir* vd); +static struct VFile* _vdlsceOpenFile(struct VDir* vd, const char* path, int mode); +static struct VDir* _vdlsceOpenDir(struct VDir* vd, const char* path); +static bool _vdlsceDeleteFile(struct VDir* vd, const char* path); + +static const char* _vdlesceName(struct VDirEntry* vde); +static enum VFSType _vdlesceType(struct VDirEntry* vde); + struct VFile* VFileOpenSce(const char* path, int flags, SceMode mode) { struct VFileSce* vfsce = malloc(sizeof(struct VFileSce)); if (!vfsce) { @@ -120,20 +134,8 @@ static void _vfsceUnmap(struct VFile* vf, void* memory, size_t size) { static void _vfsceTruncate(struct VFile* vf, size_t size) { struct VFileSce* vfsce = (struct VFileSce*) vf; - SceOff cur = sceIoLseek(vfsce->fd, 0, SEEK_CUR); - SceOff end = sceIoLseek(vfsce->fd, 0, SEEK_END); - if (end < size) { - uint8_t buffer[2048] = {}; - size_t write = size - end; - while (write >= sizeof(buffer)) { - sceIoWrite(vfsce->fd, buffer, sizeof(buffer)); - write -= sizeof(buffer); - } - if (write) { - sceIoWrite(vfsce->fd, buffer, write); - } - } // TODO: Else - sceIoLseek(vfsce->fd, cur, SEEK_SET); + SceIoStat stat = { .st_size = size }; + sceIoChstatByFd(vfsce->fd, &stat, SCE_CST_SIZE); } ssize_t _vfsceSize(struct VFile* vf) { @@ -156,6 +158,10 @@ bool _vfsceSync(struct VFile* vf, const void* buffer, size_t size) { } struct VDir* VDirOpen(const char* path) { + if (!path || !path[0]) { + return VDeviceList(); + } + SceUID dir = sceIoDopen(path); if (dir < 0) { return 0; @@ -258,3 +264,96 @@ static enum VFSType _vdesceType(struct VDirEntry* vde) { } return VFS_FILE; } + +struct VDirEntrySceDevList { + struct VDirEntry d; + ssize_t index; + const char* name; +}; + +struct VDirSceDevList { + struct VDir d; + struct VDirEntrySceDevList vde; +}; + +static const char* _devs[] = { + "ux0:", + "ur0:", + "uma0:" +}; + +struct VDir* VDeviceList() { + struct VDirSceDevList* vd = malloc(sizeof(struct VDirSceDevList)); + if (!vd) { + return 0; + } + + vd->d.close = _vdlsceClose; + vd->d.rewind = _vdlsceRewind; + vd->d.listNext = _vdlsceListNext; + vd->d.openFile = _vdlsceOpenFile; + vd->d.openDir = _vdlsceOpenDir; + vd->d.deleteFile = _vdlsceDeleteFile; + + vd->vde.d.name = _vdlesceName; + vd->vde.d.type = _vdlesceType; + vd->vde.index = -1; + vd->vde.name = 0; + + return &vd->d; +} + +static bool _vdlsceClose(struct VDir* vd) { + struct VDirSceDevList* vdl = (struct VDirSceDevList*) vd; + free(vdl); + return true; +} + +static void _vdlsceRewind(struct VDir* vd) { + struct VDirSceDevList* vdl = (struct VDirSceDevList*) vd; + vdl->vde.name = NULL; + vdl->vde.index = -1; +} + +static struct VDirEntry* _vdlsceListNext(struct VDir* vd) { + struct VDirSceDevList* vdl = (struct VDirSceDevList*) vd; + while (vdl->vde.index < 3) { + ++vdl->vde.index; + vdl->vde.name = _devs[vdl->vde.index]; + SceUID dir = sceIoDopen(vdl->vde.name); + if (dir < 0) { + continue; + } + sceIoDclose(dir); + return &vdl->vde.d; + } + return 0; +} + +static struct VFile* _vdlsceOpenFile(struct VDir* vd, const char* path, int mode) { + UNUSED(vd); + UNUSED(path); + UNUSED(mode); + return NULL; +} + +static struct VDir* _vdlsceOpenDir(struct VDir* vd, const char* path) { + UNUSED(vd); + return VDirOpen(path); +} + +static bool _vdlsceDeleteFile(struct VDir* vd, const char* path) { + UNUSED(vd); + UNUSED(path); + return false; +} + +static const char* _vdlesceName(struct VDirEntry* vde) { + struct VDirEntrySceDevList* vdle = (struct VDirEntrySceDevList*) vde; + return vdle->name; +} + +static enum VFSType _vdlesceType(struct VDirEntry* vde) { + UNUSED(vde); + return VFS_DIRECTORY; +} diff --git a/src/platform/python/CMakeLists.txt b/src/platform/python/CMakeLists.txt index a2c1a3568..db17129a5 100644 --- a/src/platform/python/CMakeLists.txt +++ b/src/platform/python/CMakeLists.txt @@ -13,9 +13,9 @@ file(GLOB PYTHON_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/*.h) if(NOT GIT_TAG) if(GIT_BRANCH STREQUAL "master" OR NOT GIT_BRANCH) - set(PYLIB_VERSION -b -${GIT_REV}-${GIT_COMMIT_SHORT}) + set(PYLIB_VERSION -b .dev${GIT_REV}+g${GIT_COMMIT_SHORT}) else() - set(PYLIB_VERSION -b -${GIT_BRANCH}-${GIT_REV}-${GIT_COMMIT_SHORT}) + set(PYLIB_VERSION -b .dev${GIT_REV}+${GIT_BRANCH}.g${GIT_COMMIT_SHORT}) endif() endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in ${CMAKE_CURRENT_BINARY_DIR}/setup.py) diff --git a/src/platform/python/_builder.h b/src/platform/python/_builder.h index 1c12bbf5c..2e65d3dd0 100644 --- a/src/platform/python/_builder.h +++ b/src/platform/python/_builder.h @@ -54,6 +54,7 @@ void free(void*); #include #endif #ifdef M_CORE_GBA +#include #include #include #include diff --git a/src/platform/python/_builder.py b/src/platform/python/_builder.py index 2d44e3769..cba4b303c 100644 --- a/src/platform/python/_builder.py +++ b/src/platform/python/_builder.py @@ -30,6 +30,7 @@ ffi.set_source("mgba._pylib", """ #include #include #include +#include #include #include #include diff --git a/src/platform/python/mgba/core.py b/src/platform/python/mgba/core.py index 1b9b0c113..95b79f166 100644 --- a/src/platform/python/mgba/core.py +++ b/src/platform/python/mgba/core.py @@ -142,9 +142,6 @@ class Core(object): raise RuntimeError("Failed to initialize core") return cls._detect(core) - def _deinit(self): - self._core.deinit(self._core) - @classmethod def _detect(cls, core): if hasattr(cls, 'PLATFORM_GBA') and core.platform(core) == cls.PLATFORM_GBA: @@ -158,6 +155,9 @@ class Core(object): return DS(core) return Core(core) + def _load(self): + self._wasReset = True + def loadFile(self, path): return bool(lib.mCoreLoadFile(self._core, path.encode('UTF-8'))) @@ -167,6 +167,9 @@ class Core(object): def loadROM(self, vf): return bool(self._core.loadROM(self._core, vf.handle)) + def loadBIOS(self, vf, id=0): + return bool(self._core.loadBIOS(self._core, vf.handle, id)) + def loadSave(self, vf): return bool(self._core.loadSave(self._core, vf.handle)) @@ -185,6 +188,9 @@ class Core(object): def autoloadPatch(self): return bool(lib.mCoreAutoloadPatch(self._core)) + def autoloadCheats(self): + return bool(lib.mCoreAutoloadCheats(self._core)) + def platform(self): return self._core.platform(self._core) @@ -199,7 +205,7 @@ class Core(object): def reset(self): self._core.reset(self._core) - self._wasReset = True + self._load() @needsReset @protected @@ -261,6 +267,10 @@ class Core(object): def addFrameCallback(self, cb): self._callbacks.videoFrameEnded.append(cb) + @property + def crc32(self): + return self._native.romCrc32 + class ICoreOwner(object): def claim(self): raise NotImplementedError diff --git a/src/platform/python/mgba/debugger.py b/src/platform/python/mgba/debugger.py index 2d597491c..17f4d5017 100644 --- a/src/platform/python/mgba/debugger.py +++ b/src/platform/python/mgba/debugger.py @@ -41,7 +41,7 @@ class NativeDebugger(IRunner): self._native = native self._cbs = [] self._core = Core._detect(native.core) - self._core._wasReset = True + self._core._load() def pause(self): lib.mDebuggerEnter(self._native, lib.DEBUGGER_ENTER_MANUAL, ffi.NULL) diff --git a/src/platform/python/mgba/gamedata.py b/src/platform/python/mgba/gamedata.py new file mode 100644 index 000000000..40eb16c04 --- /dev/null +++ b/src/platform/python/mgba/gamedata.py @@ -0,0 +1,22 @@ +# Copyright (c) 2013-2017 Jeffrey Pfau +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +try: + import mgba_gamedata +except ImportError: + pass + +def search(core): + crc32 = None + if hasattr(core, 'PLATFORM_GBA') and core.platform() == core.PLATFORM_GBA: + platform = 'GBA' + crc32 = core.crc32 + if hasattr(core, 'PLATFORM_GB') and core.platform() == core.PLATFORM_GB: + platform = 'GB' + crc32 = core.crc32 + cls = mgba_gamedata.registry.search(platform, {'crc32': crc32}) + if not cls: + return None + return cls(core.memory.u8) diff --git a/src/platform/python/mgba/gb.py b/src/platform/python/mgba/gb.py index f70f9173f..01baeb29d 100644 --- a/src/platform/python/mgba/gb.py +++ b/src/platform/python/mgba/gb.py @@ -36,13 +36,16 @@ class GB(Core): if self._wasReset: self._native.video.renderer.cache = ffi.NULL - def reset(self): - super(GB, self).reset() + def _load(self): + super(GB, self)._load() self.memory = GBMemory(self._core) def attachSIO(self, link): lib.GBSIOSetDriver(ffi.addressof(self._native.sio), link._native) + def __del__(self): + lib.GBSIOSetDriver(ffi.addressof(self._native.sio), ffi.NULL) + createCallback("GBSIOPythonDriver", "init") createCallback("GBSIOPythonDriver", "deinit") createCallback("GBSIOPythonDriver", "writeSB") diff --git a/src/platform/python/mgba/gba.py b/src/platform/python/mgba/gba.py index bbe544683..3074c39e4 100644 --- a/src/platform/python/mgba/gba.py +++ b/src/platform/python/mgba/gba.py @@ -26,6 +26,7 @@ class GBA(Core): SIO_NORMAL_32 = lib.SIO_NORMAL_32 SIO_MULTI = lib.SIO_MULTI SIO_UART = lib.SIO_UART + SIO_JOYBUS = lib.SIO_JOYBUS SIO_GPIO = lib.SIO_GPIO def __init__(self, native): @@ -33,6 +34,7 @@ class GBA(Core): self._native = ffi.cast("struct GBA*", native.board) self.sprites = GBAObjs(self) self.cpu = ARMCore(self._core.cpu) + self._sio = set() @needsReset def _initCache(self, cache): @@ -44,13 +46,18 @@ class GBA(Core): if self._wasReset: self._native.video.renderer.cache = ffi.NULL - def reset(self): - super(GBA, self).reset() + def _load(self): + super(GBA, self)._load() self.memory = GBAMemory(self._core, self._native.memory.romSize) def attachSIO(self, link, mode=lib.SIO_MULTI): + self._sio.add(mode) lib.GBASIOSetDriver(ffi.addressof(self._native.sio), link._native, mode) + def __del__(self): + for mode in self._sio: + lib.GBASIOSetDriver(ffi.addressof(self._native.sio), ffi.NULL, mode) + createCallback("GBASIOPythonDriver", "init") createCallback("GBASIOPythonDriver", "deinit") createCallback("GBASIOPythonDriver", "load") @@ -77,6 +84,32 @@ class GBASIODriver(object): def writeRegister(self, address, value): return value +class GBASIOJOYDriver(GBASIODriver): + RESET = lib.JOY_RESET + POLL = lib.JOY_POLL + TRANS = lib.JOY_TRANS + RECV = lib.JOY_RECV + + def __init__(self): + self._handle = ffi.new_handle(self) + self._native = ffi.gc(lib.GBASIOJOYPythonDriverCreate(self._handle), lib.free) + + def sendCommand(self, cmd, data): + buffer = ffi.new('uint8_t[5]') + try: + buffer[0] = data[0] + buffer[1] = data[1] + buffer[2] = data[2] + buffer[3] = data[3] + buffer[4] = data[4] + except IndexError: + pass + + outlen = lib.GBASIOJOYSendCommand(self._native, cmd, buffer) + if outlen > 0 and outlen <= 5: + return bytes(buffer[0:outlen]) + return None + class GBAMemory(Memory): def __init__(self, core, romSize=lib.SIZE_CART0): super(GBAMemory, self).__init__(core, 0x100000000) diff --git a/src/platform/python/mgba/memory.py b/src/platform/python/mgba/memory.py index c59f5a792..89f8f9098 100644 --- a/src/platform/python/mgba/memory.py +++ b/src/platform/python/mgba/memory.py @@ -100,12 +100,12 @@ class MemorySearchResult(object): class Memory(object): - SEARCH_32 = lib.mCORE_MEMORY_SEARCH_32 - SEARCH_16 = lib.mCORE_MEMORY_SEARCH_16 - SEARCH_8 = lib.mCORE_MEMORY_SEARCH_8 + SEARCH_INT = lib.mCORE_MEMORY_SEARCH_INT SEARCH_STRING = lib.mCORE_MEMORY_SEARCH_STRING SEARCH_GUESS = lib.mCORE_MEMORY_SEARCH_GUESS + SEARCH_EQUAL = lib.mCORE_MEMORY_SEARCH_EQUAL + READ = lib.mCORE_MEMORY_READ WRITE = lib.mCORE_MEMORY_READ RW = lib.mCORE_MEMORY_RW @@ -131,12 +131,9 @@ class Memory(object): params = ffi.new("struct mCoreMemorySearchParams*") params.memoryFlags = flags params.type = type - if type == self.SEARCH_8: - params.value8 = int(value) - elif type == self.SEARCH_16: - params.value16 = int(value) - elif type == self.SEARCH_32: - params.value32 = int(value) + params.op = self.SEARCH_EQUAL + if type == self.SEARCH_INT: + params.valueInt = int(value) else: params.valueStr = ffi.new("char[]", str(value).encode("ascii")) @@ -153,3 +150,9 @@ class Memory(object): new_results = [MemorySearchResult(self, lib.mCoreMemorySearchResultsGetPointer(results, i)) for i in range(lib.mCoreMemorySearchResultsSize(results))] lib.mCoreMemorySearchResultsDeinit(results) return new_results + + def __getitem__(self, address): + if isinstance(address, slice): + return bytearray(self.u8[address]) + else: + return self.u8[address] diff --git a/src/platform/python/setup.py.in b/src/platform/python/setup.py.in index 040be3fdc..0a6fa8b52 100644 --- a/src/platform/python/setup.py.in +++ b/src/platform/python/setup.py.in @@ -5,7 +5,6 @@ import sys os.environ["BINDIR"] = "${CMAKE_BINARY_DIR}" os.environ["CPPFLAGS"] = " ".join([d for d in "${INCLUDE_FLAGS}".split(";") if d]) -os.chdir("${CMAKE_CURRENT_SOURCE_DIR}") classifiers = [ "Programming Language :: C", @@ -22,11 +21,14 @@ setup(name="${BINARY_NAME}", author_email="jeffrey@endrift.com", url="http://github.com/mgba-emu/mgba/", packages=["mgba"], + package_dir={ + "mgba": "${CMAKE_CURRENT_SOURCE_DIR}" + }, setup_requires=['cffi>=1.6', 'pytest-runner'], install_requires=['cffi>=1.6', 'cached-property'], extras_require={'pil': ['Pillow>=2.3'], 'cinema': ['pyyaml', 'pytest']}, tests_require=['pytest'], - cffi_modules=["_builder.py:ffi"], + cffi_modules=["${CMAKE_CURRENT_SOURCE_DIR}/_builder.py:ffi"], license="MPL 2.0", classifiers=classifiers ) diff --git a/src/platform/python/sio.c b/src/platform/python/sio.c index a7cba8973..ab8dd47bd 100644 --- a/src/platform/python/sio.c +++ b/src/platform/python/sio.c @@ -46,6 +46,18 @@ struct GBASIODriver* GBASIOPythonDriverCreate(void* pyobj) { return &driver->d; } +struct GBASIODriver* GBASIOJOYPythonDriverCreate(void* pyobj) { + struct GBASIOPythonDriver* driver = malloc(sizeof(*driver)); + GBASIOJOYCreate(&driver->d); + driver->d.init = _pyGBASIOPythonDriverInitShim; + driver->d.deinit = _pyGBASIOPythonDriverDeinitShim; + driver->d.load = _pyGBASIOPythonDriverLoadShim; + driver->d.unload = _pyGBASIOPythonDriverUnloadShim; + + driver->pyobj = pyobj; + return &driver->d; +} + #endif #ifdef M_CORE_GB diff --git a/src/platform/python/sio.h b/src/platform/python/sio.h index 0403fdfd9..23b059427 100644 --- a/src/platform/python/sio.h +++ b/src/platform/python/sio.h @@ -13,6 +13,7 @@ struct GBASIOPythonDriver { }; struct GBASIODriver* GBASIOPythonDriverCreate(void* pyobj); +struct GBASIODriver* GBASIOJOYPythonDriverCreate(void* pyobj); PYEXPORT bool _pyGBASIOPythonDriverInit(void* driver); PYEXPORT void _pyGBASIOPythonDriverDeinit(void* driver); diff --git a/src/platform/python/test_cinema.py b/src/platform/python/test_cinema.py index 61c27bf7d..addb38094 100644 --- a/src/platform/python/test_cinema.py +++ b/src/platform/python/test_cinema.py @@ -18,7 +18,7 @@ def flatten(d): def pytest_generate_tests(metafunc): if 'vtest' in metafunc.fixturenames: - tests = cinema.test.gatherTests(os.path.join(os.path.dirname(__file__), 'tests/cinema')) + tests = cinema.test.gatherTests(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'cinema')) testList = flatten(tests) params = [] for test in testList: diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_2Mb/manifest.yml b/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_2Mb/manifest.yml deleted file mode 100644 index a697ada66..000000000 --- a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_2Mb/manifest.yml +++ /dev/null @@ -1 +0,0 @@ -fail: true diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_512Kb/baseline_0000.png b/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_512Kb/baseline_0000.png deleted file mode 100644 index 282b639d3..000000000 Binary files a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1/rom_512Kb/baseline_0000.png and /dev/null differ diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1_rom_4banks/manifest.yml b/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1_rom_4banks/manifest.yml deleted file mode 100644 index a697ada66..000000000 --- a/src/platform/python/tests/cinema/gb/mooneye-gb/emulator-only/mbc1_rom_4banks/manifest.yml +++ /dev/null @@ -1 +0,0 @@ -fail: true diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/madness/mgb_oam_dma_halt_sprites/manifest.yml b/src/platform/python/tests/cinema/gb/mooneye-gb/madness/mgb_oam_dma_halt_sprites/manifest.yml deleted file mode 100644 index a697ada66..000000000 --- a/src/platform/python/tests/cinema/gb/mooneye-gb/madness/mgb_oam_dma_halt_sprites/manifest.yml +++ /dev/null @@ -1 +0,0 @@ -fail: true diff --git a/src/platform/python/tests/cinema/gb/mooneye-gb/manual-only/sprite_priority/manifest.yml b/src/platform/python/tests/cinema/gb/mooneye-gb/manual-only/sprite_priority/manifest.yml deleted file mode 100644 index a697ada66..000000000 --- a/src/platform/python/tests/cinema/gb/mooneye-gb/manual-only/sprite_priority/manifest.yml +++ /dev/null @@ -1 +0,0 @@ -fail: true diff --git a/src/platform/python/tests/cinema/gb/window/gsc-battle/manifest.yml b/src/platform/python/tests/cinema/gb/window/gsc-battle/manifest.yml deleted file mode 100644 index a697ada66..000000000 --- a/src/platform/python/tests/cinema/gb/window/gsc-battle/manifest.yml +++ /dev/null @@ -1 +0,0 @@ -fail: true diff --git a/src/platform/python/tests/cinema/gba/bg/lady-sia/baseline_0000.png b/src/platform/python/tests/cinema/gba/bg/lady-sia/baseline_0000.png deleted file mode 100644 index 53ffb604e..000000000 Binary files a/src/platform/python/tests/cinema/gba/bg/lady-sia/baseline_0000.png and /dev/null differ diff --git a/src/platform/python/tests/cinema/gba/bg/lady-sia/baseline_0001.png b/src/platform/python/tests/cinema/gba/bg/lady-sia/baseline_0001.png deleted file mode 100644 index f7cc51299..000000000 Binary files a/src/platform/python/tests/cinema/gba/bg/lady-sia/baseline_0001.png and /dev/null differ diff --git a/src/platform/python/tests/cinema/gba/bg/lady-sia/baseline_0002.png b/src/platform/python/tests/cinema/gba/bg/lady-sia/baseline_0002.png deleted file mode 100644 index df4d6402a..000000000 Binary files a/src/platform/python/tests/cinema/gba/bg/lady-sia/baseline_0002.png and /dev/null differ diff --git a/src/platform/python/tests/cinema/gba/bg/lady-sia/baseline_0003.png b/src/platform/python/tests/cinema/gba/bg/lady-sia/baseline_0003.png deleted file mode 100644 index 9bd30f5fd..000000000 Binary files a/src/platform/python/tests/cinema/gba/bg/lady-sia/baseline_0003.png and /dev/null differ diff --git a/src/platform/python/tests/mgba/test_vfs.py b/src/platform/python/tests/mgba/test_vfs.py index 2315e2a32..38cbacb23 100644 --- a/src/platform/python/tests/mgba/test_vfs.py +++ b/src/platform/python/tests/mgba/test_vfs.py @@ -19,7 +19,7 @@ def test_vfs_read(): vf = vfs.openPath(__file__) buffer = ffi.new('char[13]') assert vf.read(buffer, 13) == 13 - assert ffi.string(buffer) == 'import pytest' + assert ffi.string(buffer) == b'import pytest' vf.close() def test_vfs_readline(): @@ -28,9 +28,9 @@ def test_vfs_readline(): linelen = vf.readline(buffer, 16) assert linelen in (14, 15) if linelen == 14: - assert ffi.string(buffer) == 'import pytest\n' + assert ffi.string(buffer) == b'import pytest\n' elif linelen == 15: - assert ffi.string(buffer) == 'import pytest\r\n' + assert ffi.string(buffer) == b'import pytest\r\n' vf.close() def test_vfs_readAllSize(): diff --git a/src/platform/qt/AssetTile.ui b/src/platform/qt/AssetTile.ui index 92a8623e1..e5557506b 100644 --- a/src/platform/qt/AssetTile.ui +++ b/src/platform/qt/AssetTile.ui @@ -50,6 +50,9 @@ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + @@ -92,6 +95,9 @@ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 76b43a03a..22327829c 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -2,13 +2,6 @@ set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -if(APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.7") - if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") - endif() -endif() - set(PLATFORM_SRC) set(QT_STATIC OFF) @@ -42,6 +35,17 @@ if(NOT Qt5Widgets_FOUND) return() endif() +if(APPLE) + if(Qt5Widgets_VERSION MATCHES "^5.1[0-9]") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.8") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.7") + endif() + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") + endif() +endif() + if(BUILD_GL) list(APPEND PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/opengl/gl.c) if(NOT WIN32 OR USE_EPOXY) @@ -93,6 +97,7 @@ set(SOURCE_FILES OverrideView.cpp PaletteView.cpp PrinterView.cpp + RegisterView.cpp ROMInfo.cpp SavestateButton.cpp SensorView.cpp @@ -219,6 +224,8 @@ endif() if(NOT DEFINED DATADIR) if(APPLE) set(DATADIR Applications/${PROJECT_NAME}.app/Contents/Resources) + elseif(WIN32 AND NOT WIN32_UNIX_PATHS) + set(DATADIR ".") else() set(DATADIR ${CMAKE_INSTALL_DATADIR}/${BINARY_NAME}) endif() @@ -316,8 +323,13 @@ if(APPLE) endif() install(CODE "execute_process(COMMAND \"${CMAKE_SOURCE_DIR}/tools/deploy-mac.py\" -v ${DEPLOY_OPTIONS} \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/Applications/${PROJECT_NAME}.app\")") endif() -endif() -if(WIN32 AND CMAKE_MAJOR_VERSION GREATER 2 AND CMAKE_MINOR_VERSION GREATER 7) - # Work around CMake issue #16907 - set_target_properties(${BINARY_NAME}-qt PROPERTIES AUTORCC ON SKIP_AUTORCC ON) +elseif(WIN32) + if(CMAKE_MAJOR_VERSION EQUAL 3 AND CMAKE_MINOR_VERSION EQUAL 8) + # Work around CMake issue #16907 + set_target_properties(${BINARY_NAME}-qt PROPERTIES AUTORCC ON SKIP_AUTORCC ON) + endif() + if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + find_program(BASH bash) + install(CODE "execute_process(COMMAND \"${BASH}\" \"${CMAKE_SOURCE_DIR}/tools/deploy-win.sh\" \"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.exe\" \"\${CMAKE_INSTALL_PREFIX}\" \"\$ENV{PWD}\" WORKING_DIRECTORY \"${CMAKE_BINARY_DIR}\")" COMPONENT ${BINARY_NAME}-qt) + endif() endif() diff --git a/src/platform/qt/CheatsModel.cpp b/src/platform/qt/CheatsModel.cpp index 79041ecea..a2c535f0c 100644 --- a/src/platform/qt/CheatsModel.cpp +++ b/src/platform/qt/CheatsModel.cpp @@ -70,10 +70,12 @@ bool CheatsModel::setData(const QModelIndex& index, const QVariant& value, int r case Qt::DisplayRole: case Qt::EditRole: mCheatSetRename(cheats, value.toString().toUtf8().constData()); + mCheatAutosave(m_device); emit dataChanged(index, index); return true; case Qt::CheckStateRole: cheats->enabled = value == Qt::Checked; + mCheatAutosave(m_device); emit dataChanged(index, index); return true; default: @@ -154,7 +156,8 @@ void CheatsModel::removeAt(const QModelIndex& index) { beginRemoveRows(QModelIndex(), row, row); mCheatRemoveSet(m_device, set); mCheatSetDeinit(set); - endInsertRows(); + endRemoveRows(); + mCheatAutosave(m_device); } QString CheatsModel::toString(const QModelIndexList& indices) const { @@ -201,6 +204,7 @@ void CheatsModel::beginAppendRow(const QModelIndex& index) { void CheatsModel::endAppendRow() { endInsertRows(); + mCheatAutosave(m_device); } void CheatsModel::loadFile(const QString& path) { @@ -232,6 +236,7 @@ void CheatsModel::addSet(mCheatSet* set) { } mCheatAddSet(m_device, set); endInsertRows(); + mCheatAutosave(m_device); } void CheatsModel::invalidated() { diff --git a/src/platform/qt/CheatsView.cpp b/src/platform/qt/CheatsView.cpp index 80e398b4b..315edcae1 100644 --- a/src/platform/qt/CheatsView.cpp +++ b/src/platform/qt/CheatsView.cpp @@ -109,14 +109,14 @@ bool CheatsView::eventFilter(QObject* object, QEvent* event) { } void CheatsView::load() { - QString filename = GBAApp::app()->getOpenFileName(this, tr("Select cheats file")); + QString filename = GBAApp::app()->getOpenFileName(this, tr("Select cheats file"), tr(("Cheats file (*.cheats *.cht *.clt)"))); if (!filename.isEmpty()) { m_model.loadFile(filename); } } void CheatsView::save() { - QString filename = GBAApp::app()->getSaveFileName(this, tr("Select cheats file")); + QString filename = GBAApp::app()->getSaveFileName(this, tr("Select cheats file"), tr(("Cheats file (*.cheats *.cht *.clt)"))); if (!filename.isEmpty()) { m_model.saveFile(filename); } diff --git a/src/platform/qt/CoreController.cpp b/src/platform/qt/CoreController.cpp index 2fb08aceb..80736e180 100644 --- a/src/platform/qt/CoreController.cpp +++ b/src/platform/qt/CoreController.cpp @@ -28,8 +28,9 @@ #include #include -using namespace QGBA; +#define AUTOSAVE_GRANULARITY 600 +using namespace QGBA; CoreController::CoreController(mCore* core, QObject* parent) : QObject(parent) @@ -48,6 +49,12 @@ CoreController::CoreController(mCore* core, QObject* parent) m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast(m_activeBuffer->data()), size.width()); + m_resetActions.append([this]() { + if (m_autoload) { + mCoreLoadState(m_threadContext.core, 0, m_loadStateFlags); + } + }); + m_threadContext.startCallback = [](mCoreThread* context) { CoreController* controller = static_cast(context->userData); @@ -61,6 +68,8 @@ CoreController::CoreController(mCore* core, QObject* parent) break; } + controller->updateFastForward(); + if (controller->m_multiplayer) { controller->m_multiplayer->attachGame(controller); } @@ -79,10 +88,6 @@ CoreController::CoreController(mCore* core, QObject* parent) controller->m_override->apply(context->core); } - if (mCoreLoadState(context->core, 0, controller->m_loadStateFlags)) { - mCoreDeleteState(context->core, 0); - } - controller->m_resetActions.clear(); QSize size = controller->screenDimensions(); @@ -100,12 +105,24 @@ CoreController::CoreController(mCore* core, QObject* parent) m_threadContext.frameCallback = [](mCoreThread* context) { CoreController* controller = static_cast(context->userData); + if (controller->m_autosaveCounter == AUTOSAVE_GRANULARITY) { + if (controller->m_autosave) { + mCoreSaveState(context->core, 0, controller->m_saveStateFlags); + } + controller->m_autosaveCounter = 0; + } + ++controller->m_autosaveCounter; + controller->finishFrame(); }; m_threadContext.cleanCallback = [](mCoreThread* context) { CoreController* controller = static_cast(context->userData); + if (controller->m_autosave) { + mCoreSaveState(context->core, 0, controller->m_saveStateFlags); + } + controller->clearMultiplayerController(); QMetaObject::invokeMethod(controller, "stopping"); }; @@ -126,7 +143,8 @@ CoreController::CoreController(mCore* core, QObject* parent) mThreadLogger* logContext = reinterpret_cast(logger); mCoreThread* context = logContext->p; - static const char* savestateMessage = "State %i loaded"; + static const char* savestateMessage = "State %i saved"; + static const char* loadstateMessage = "State %i loaded"; static const char* savestateFailedMessage = "State %i failed to load"; static int biosCat = -1; static int statusCat = -1; @@ -152,7 +170,7 @@ CoreController::CoreController(mCore* core, QObject* parent) #endif if (category == statusCat) { // Slot 0 is reserved for suspend points - if (strncmp(savestateMessage, format, strlen(savestateMessage)) == 0) { + if (strncmp(loadstateMessage, format, strlen(loadstateMessage)) == 0) { va_list argc; va_copy(argc, args); int slot = va_arg(argc, int); @@ -160,7 +178,7 @@ CoreController::CoreController(mCore* core, QObject* parent) if (slot == 0) { format = "Loaded suspend state"; } - } else if (strncmp(savestateFailedMessage, format, strlen(savestateFailedMessage)) == 0) { + } else if (strncmp(savestateFailedMessage, format, strlen(savestateFailedMessage)) == 0 || strncmp(savestateMessage, format, strlen(savestateMessage)) == 0) { va_list argc; va_copy(argc, args); int slot = va_arg(argc, int); @@ -209,6 +227,10 @@ bool CoreController::isPaused() { return mCoreThreadIsPaused(&m_threadContext); } +bool CoreController::hasStarted() { + return mCoreThreadHasStarted(&m_threadContext); +} + mPlatform CoreController::platform() const { return m_threadContext.core->platform(m_threadContext.core); } @@ -232,10 +254,14 @@ void CoreController::loadConfig(ConfigController* config) { m_videoSync = config->getOption("videoSync", m_videoSync).toInt(); m_audioSync = config->getOption("audioSync", m_audioSync).toInt(); m_fpsTarget = config->getOption("fpsTarget").toFloat(); + m_autosave = config->getOption("autosave", false).toInt(); + m_autoload = config->getOption("autoload", true).toInt(); m_autofireThreshold = config->getOption("autofireThreshold", m_autofireThreshold).toInt(); - updateFastForward(); mCoreLoadForeignConfig(m_threadContext.core, config->config()); - mCoreThreadRewindParamsChanged(&m_threadContext); + if (hasStarted()) { + updateFastForward(); + mCoreThreadRewindParamsChanged(&m_threadContext); + } } #ifdef USE_DEBUGGERS @@ -628,6 +654,7 @@ void CoreController::attachPrinter() { } QMetaObject::invokeMethod(qPrinter->parent, "imagePrinted", Q_ARG(const QImage&, image)); }; + Interrupter interrupter(this); GBSIOSetDriver(&gb->sio, &m_printer.d.d); #endif } @@ -637,6 +664,7 @@ void CoreController::detachPrinter() { if (platform() != PLATFORM_GB) { return; } + Interrupter interrupter(this); GB* gb = static_cast(m_threadContext.core->board); GBPrinterDonePrinting(&m_printer.d); GBSIOSetDriver(&gb->sio, nullptr); @@ -648,6 +676,7 @@ void CoreController::endPrint() { if (platform() != PLATFORM_GB) { return; } + Interrupter interrupter(this); GBPrinterDonePrinting(&m_printer.d); #endif } diff --git a/src/platform/qt/CoreController.h b/src/platform/qt/CoreController.h index 1dd4b6bca..fa3a1f771 100644 --- a/src/platform/qt/CoreController.h +++ b/src/platform/qt/CoreController.h @@ -61,6 +61,7 @@ public: color_t* drawContext(); bool isPaused(); + bool hasStarted(); mPlatform platform() const; QSize screenDimensions() const; @@ -190,6 +191,10 @@ private: bool m_audioSync = AUDIO_SYNC; bool m_videoSync = VIDEO_SYNC; + bool m_autosave; + bool m_autoload; + int m_autosaveCounter; + int m_fastForward = false; int m_fastForwardForced = false; float m_fastForwardRatio = -1.f; diff --git a/src/platform/qt/CoreManager.cpp b/src/platform/qt/CoreManager.cpp index 50991026b..adb002c18 100644 --- a/src/platform/qt/CoreManager.cpp +++ b/src/platform/qt/CoreManager.cpp @@ -109,6 +109,7 @@ CoreController* CoreManager::loadGame(VFile* vf, const QString& path, const QStr bytes = info.dir().canonicalPath().toUtf8(); mDirectorySetAttachBase(&core->dirs, VDirOpen(bytes.constData())); mCoreAutoloadSave(core); + mCoreAutoloadCheats(core); CoreController* cc = new CoreController(core); if (m_multiplayer) { diff --git a/src/platform/qt/GBAApp.cpp b/src/platform/qt/GBAApp.cpp index 9b994047a..39f85723b 100644 --- a/src/platform/qt/GBAApp.cpp +++ b/src/platform/qt/GBAApp.cpp @@ -67,10 +67,7 @@ GBAApp::GBAApp(int& argc, char* argv[], ConfigController* config) } GBAApp::~GBAApp() { -#ifdef USE_SQLITE3 - m_parseThread.quit(); - m_parseThread.wait(); -#endif + m_workerThreads.waitForDone(); } bool GBAApp::event(QEvent* event) { @@ -180,14 +177,8 @@ bool GBAApp::reloadGameDB() { NoIntroDBDestroy(m_db); } if (db) { - if (m_parseThread.isRunning()) { - m_parseThread.quit(); - m_parseThread.wait(); - } GameDBParser* parser = new GameDBParser(db); - m_parseThread.start(); - parser->moveToThread(&m_parseThread); - QMetaObject::invokeMethod(parser, "parseNoIntroDB"); + submitWorkerJob(std::bind(&GameDBParser::parseNoIntroDB, parser)); m_db = db; return true; } @@ -199,6 +190,77 @@ bool GBAApp::reloadGameDB() { } #endif +qint64 GBAApp::submitWorkerJob(std::function job, std::function callback) { + return submitWorkerJob(job, nullptr, callback); +} + +qint64 GBAApp::submitWorkerJob(std::function job, QObject* context, std::function callback) { + qint64 jobId = m_nextJob; + ++m_nextJob; + WorkerJob* jobRunnable = new WorkerJob(jobId, job, this); + m_workerJobs.insert(jobId, jobRunnable); + if (callback) { + waitOnJob(jobId, context, callback); + } + m_workerThreads.start(jobRunnable); + return jobId; +} + +bool GBAApp::removeWorkerJob(qint64 jobId) { + for (auto& job : m_workerJobCallbacks.values(jobId)) { + disconnect(job); + } + m_workerJobCallbacks.remove(jobId); + if (!m_workerJobs.contains(jobId)) { + return true; + } + bool success = false; +#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)) + success = m_workerThreads.tryTake(m_workerJobs[jobId]); +#endif + if (success) { + m_workerJobs.remove(jobId); + } + return success; +} + + +bool GBAApp::waitOnJob(qint64 jobId, QObject* context, std::function callback) { + if (!m_workerJobs.contains(jobId)) { + return false; + } + if (!context) { + context = this; + } + QMetaObject::Connection connection = connect(this, &GBAApp::jobFinished, context, [jobId, callback](qint64 testedJobId) { + if (jobId != testedJobId) { + return; + } + callback(); + }); + m_workerJobCallbacks.insert(m_nextJob, connection); + return true; +} + +void GBAApp::finishJob(qint64 jobId) { + m_workerJobs.remove(jobId); + emit jobFinished(jobId); + m_workerJobCallbacks.remove(jobId); +} + +GBAApp::WorkerJob::WorkerJob(qint64 id, std::function job, GBAApp* owner) + : m_id(id) + , m_job(job) + , m_owner(owner) +{ + setAutoDelete(true); +} + +void GBAApp::WorkerJob::run() { + m_job(); + QMetaObject::invokeMethod(m_owner, "finishJob", Q_ARG(qint64, m_id)); +} + #ifdef USE_SQLITE3 GameDBParser::GameDBParser(NoIntroDB* db, QObject* parent) : QObject(parent) diff --git a/src/platform/qt/GBAApp.h b/src/platform/qt/GBAApp.h index 8530a70b3..2e2c02af2 100644 --- a/src/platform/qt/GBAApp.h +++ b/src/platform/qt/GBAApp.h @@ -8,9 +8,14 @@ #include #include #include +#include +#include #include +#include #include -#include +#include + +#include #include "CoreManager.h" #include "MultiplayerController.h" @@ -61,10 +66,34 @@ public: const NoIntroDB* gameDB() const { return m_db; } bool reloadGameDB(); + qint64 submitWorkerJob(std::function job, std::function callback = {}); + qint64 submitWorkerJob(std::function job, QObject* context, std::function callback); + bool removeWorkerJob(qint64 jobId); + bool waitOnJob(qint64 jobId, QObject* context, std::function callback); + +signals: + void jobFinished(qint64 jobId); + protected: bool event(QEvent*); +private slots: + void finishJob(qint64 jobId); + private: + class WorkerJob : public QRunnable { + public: + WorkerJob(qint64 id, std::function job, GBAApp* owner); + + public: + void run() override; + + private: + qint64 m_id; + std::function m_job; + GBAApp* m_owner; + }; + Window* newWindowInternal(); void pauseAll(QList* paused); @@ -75,10 +104,12 @@ private: MultiplayerController m_multiplayer; CoreManager m_manager; + QMap m_workerJobs; + QMultiMap m_workerJobCallbacks; + QThreadPool m_workerThreads; + qint64 m_nextJob = 1; + NoIntroDB* m_db = nullptr; -#ifdef USE_SQLITE3 - QThread m_parseThread; -#endif }; } diff --git a/src/platform/qt/LogController.cpp b/src/platform/qt/LogController.cpp index d8870d335..32cf7a917 100644 --- a/src/platform/qt/LogController.cpp +++ b/src/platform/qt/LogController.cpp @@ -13,7 +13,7 @@ LogController::LogController(int levels, QObject* parent) : QObject(parent) { mLogFilterInit(&m_filter); - mLogFilterSet(&m_filter, "gba.bios", mLOG_STUB); + mLogFilterSet(&m_filter, "gba.bios", mLOG_STUB | mLOG_FATAL); mLogFilterSet(&m_filter, "core.status", mLOG_ALL & ~mLOG_DEBUG); m_filter.defaultLevels = levels; diff --git a/src/platform/qt/MemoryModel.cpp b/src/platform/qt/MemoryModel.cpp index 92fb2aa98..e2ad0700a 100644 --- a/src/platform/qt/MemoryModel.cpp +++ b/src/platform/qt/MemoryModel.cpp @@ -99,7 +99,7 @@ void MemoryModel::setRegion(uint32_t base, uint32_t size, const QString& name, i m_top = 0; m_base = base; m_size = size; - m_regionName = name; + m_regionName = QStaticText(name); m_regionName.prepare(QTransform(), m_font); m_currentBank = segment; verticalScrollBar()->setRange(0, (size >> 4) + 1 - viewport()->size().height() / m_cellHeight); diff --git a/src/platform/qt/MemorySearch.cpp b/src/platform/qt/MemorySearch.cpp index 2230f00aa..4ff967da5 100644 --- a/src/platform/qt/MemorySearch.cpp +++ b/src/platform/qt/MemorySearch.cpp @@ -41,49 +41,60 @@ bool MemorySearch::createParams(mCoreMemorySearchParams* params) { QByteArray string; bool ok = false; if (m_ui.typeNum->isChecked()) { + params->type = mCORE_MEMORY_SEARCH_INT; + if (m_ui.opDelta->isChecked()) { + params->op = mCORE_MEMORY_SEARCH_DELTA; + } else if (m_ui.opGreater->isChecked()) { + params->op = mCORE_MEMORY_SEARCH_GREATER; + } else if (m_ui.opLess->isChecked()) { + params->op = mCORE_MEMORY_SEARCH_LESS; + } else { + params->op = mCORE_MEMORY_SEARCH_EQUAL; + } + params->align = -1; if (m_ui.bits8->isChecked()) { - params->type = mCORE_MEMORY_SEARCH_8; + params->width = 1; } if (m_ui.bits16->isChecked()) { - params->type = mCORE_MEMORY_SEARCH_16; + params->width = 2; } if (m_ui.bits32->isChecked()) { - params->type = mCORE_MEMORY_SEARCH_32; + params->width = 4; + } + if (m_ui.bitsGuess->isChecked()) { + params->width = -1; } if (m_ui.numHex->isChecked()) { uint32_t v = m_ui.value->text().toUInt(&ok, 16); if (ok) { - switch (params->type) { - case mCORE_MEMORY_SEARCH_8: + params->valueInt = v; + switch (params->width) { + case 1: ok = v < 0x100; - params->value8 = v; break; - case mCORE_MEMORY_SEARCH_16: + case 2: ok = v < 0x10000; - params->value16 = v; break; - case mCORE_MEMORY_SEARCH_32: - params->value32 = v; + case 4: break; default: ok = false; + break; } } } if (m_ui.numDec->isChecked()) { uint32_t v = m_ui.value->text().toUInt(&ok, 10); if (ok) { - switch (params->type) { - case mCORE_MEMORY_SEARCH_8: + params->valueInt = v; + switch (params->width) { + case 1: ok = v < 0x100; - params->value8 = v; break; - case mCORE_MEMORY_SEARCH_16: + case 2: ok = v < 0x10000; - params->value16 = v; break; - case mCORE_MEMORY_SEARCH_32: - params->value32 = v; + case 4: break; default: ok = false; @@ -101,6 +112,7 @@ bool MemorySearch::createParams(mCoreMemorySearchParams* params) { params->type = mCORE_MEMORY_SEARCH_STRING; m_string = m_ui.value->text().toLocal8Bit(); params->valueStr = m_string.constData(); + params->width = m_ui.value->text().size(); ok = true; } return ok; @@ -140,66 +152,73 @@ void MemorySearch::refresh() { m_ui.results->clearContents(); m_ui.results->setRowCount(mCoreMemorySearchResultsSize(&m_results)); + m_ui.opDelta->setEnabled(false); for (size_t i = 0; i < mCoreMemorySearchResultsSize(&m_results); ++i) { mCoreMemorySearchResult* result = mCoreMemorySearchResultsGetPointer(&m_results, i); QTableWidgetItem* item = new QTableWidgetItem(QString("%1").arg(result->address, 8, 16, QChar('0'))); m_ui.results->setItem(i, 0, item); QTableWidgetItem* type; - if (m_ui.numHex->isChecked()) { - switch (result->type) { - case mCORE_MEMORY_SEARCH_8: + QByteArray string; + if (result->type == mCORE_MEMORY_SEARCH_INT && m_ui.numHex->isChecked()) { + switch (result->width) { + case 1: item = new QTableWidgetItem(QString("%1").arg(core->rawRead8(core, result->address, result->segment), 2, 16, QChar('0'))); break; - case mCORE_MEMORY_SEARCH_16: + case 2: item = new QTableWidgetItem(QString("%1").arg(core->rawRead16(core, result->address, result->segment), 4, 16, QChar('0'))); break; - case mCORE_MEMORY_SEARCH_GUESS: - case mCORE_MEMORY_SEARCH_32: + case 4: item = new QTableWidgetItem(QString("%1").arg(core->rawRead32(core, result->address, result->segment), 8, 16, QChar('0'))); break; - case mCORE_MEMORY_SEARCH_STRING: - item = new QTableWidgetItem("?"); // TODO } } else { switch (result->type) { - case mCORE_MEMORY_SEARCH_8: - item = new QTableWidgetItem(QString::number(core->rawRead8(core, result->address, result->segment))); - break; - case mCORE_MEMORY_SEARCH_16: - item = new QTableWidgetItem(QString::number(core->rawRead16(core, result->address, result->segment))); - break; - case mCORE_MEMORY_SEARCH_GUESS: - case mCORE_MEMORY_SEARCH_32: - item = new QTableWidgetItem(QString::number(core->rawRead32(core, result->address, result->segment))); + case mCORE_MEMORY_SEARCH_INT: + switch (result->width) { + case 1: + item = new QTableWidgetItem(QString::number(core->rawRead8(core, result->address, result->segment))); + break; + case 2: + item = new QTableWidgetItem(QString::number(core->rawRead16(core, result->address, result->segment))); + break; + case 4: + item = new QTableWidgetItem(QString::number(core->rawRead32(core, result->address, result->segment))); + break; + } break; case mCORE_MEMORY_SEARCH_STRING: - item = new QTableWidgetItem("?"); // TODO + string.reserve(result->width); + for (int i = 0; i < result->width; ++i) { + string.append(core->rawRead8(core, result->address + i, result->segment)); + } + item = new QTableWidgetItem(QLatin1String(string)); // TODO } } QString divisor; if (result->guessDivisor > 1) { - divisor = tr(" (⅟%0×)").arg(result->guessDivisor); + if (result->guessMultiplier > 1) { + divisor = tr(" (%0/%1×)").arg(result->guessMultiplier).arg(result->guessMultiplier); + } else { + divisor = tr(" (⅟%0×)").arg(result->guessDivisor); + } + } else if (result->guessMultiplier > 1) { + divisor = tr(" (%0×)").arg(result->guessMultiplier); } switch (result->type) { - case mCORE_MEMORY_SEARCH_8: - type = new QTableWidgetItem(tr("1 byte%0").arg(divisor)); - break; - case mCORE_MEMORY_SEARCH_16: - type = new QTableWidgetItem(tr("2 bytes%0").arg(divisor)); - break; - case mCORE_MEMORY_SEARCH_GUESS: - case mCORE_MEMORY_SEARCH_32: - type = new QTableWidgetItem(tr("4 bytes%0").arg(divisor)); + case mCORE_MEMORY_SEARCH_INT: + type = new QTableWidgetItem(tr("%1 byte%2").arg(result->width).arg(divisor)); break; case mCORE_MEMORY_SEARCH_STRING: - item = new QTableWidgetItem("?"); // TODO + type = new QTableWidgetItem("string"); } m_ui.results->setItem(i, 1, item); m_ui.results->setItem(i, 2, type); + m_ui.opDelta->setEnabled(true); + } + if (m_ui.opDelta->isChecked() && !m_ui.opDelta->isEnabled()) { + m_ui.opEqual->setChecked(true); } m_ui.results->sortItems(0); - m_ui.results->resizeColumnsToContents(); - m_ui.results->resizeRowsToContents(); } void MemorySearch::openMemory() { diff --git a/src/platform/qt/MemorySearch.ui b/src/platform/qt/MemorySearch.ui index ccbe07076..4e8649e42 100644 --- a/src/platform/qt/MemorySearch.ui +++ b/src/platform/qt/MemorySearch.ui @@ -6,8 +6,8 @@ 0 0 - 631 - 378 + 540 + 491 @@ -99,14 +99,34 @@ - + + + + Qt::Horizontal + + + + Width - + + + + Guess + + + true + + + width + + + + 1 Byte (8-bit) @@ -116,7 +136,7 @@ - + 2 Bytes (16-bit) @@ -126,50 +146,117 @@ - + 4 Bytes (32-bit) - true + false width - + + + + Qt::Horizontal + + + + Number type - - + + - Hexadecimal + Guess true - + Decimal - - + + - Guess + Hexadecimal + + + + Qt::Horizontal + + + + + + + Compare + + + + + + + Equal + + + true + + + op + + + + + + + Greater + + + op + + + + + + + Less + + + op + + + + + + + false + + + Delta + + + op + + + @@ -235,5 +322,6 @@ + diff --git a/src/platform/qt/ObjView.ui b/src/platform/qt/ObjView.ui index 37dcf9533..7f33eaeb0 100644 --- a/src/platform/qt/ObjView.ui +++ b/src/platform/qt/ObjView.ui @@ -561,6 +561,9 @@ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + diff --git a/src/platform/qt/OverrideView.cpp b/src/platform/qt/OverrideView.cpp index c9e68c68c..1259761ee 100644 --- a/src/platform/qt/OverrideView.cpp +++ b/src/platform/qt/OverrideView.cpp @@ -93,13 +93,16 @@ OverrideView::OverrideView(ConfigController* config, QWidget* parent) connect(m_ui.buttonBox, &QDialogButtonBox::accepted, this, &OverrideView::saveOverride); connect(m_ui.buttonBox, &QDialogButtonBox::rejected, this, &QWidget::close); + + m_recheck.setInterval(200); + connect(&m_recheck, &QTimer::timeout, this, &OverrideView::recheck); } void OverrideView::setController(std::shared_ptr controller) { m_controller = controller; connect(controller.get(), &CoreController::started, this, &OverrideView::gameStarted); connect(controller.get(), &CoreController::stopping, this, &OverrideView::gameStopped); - updateOverrides(); + recheck(); } void OverrideView::saveOverride() { @@ -115,6 +118,17 @@ void OverrideView::saveOverride() { m_config->saveOverride(*override); } +void OverrideView::recheck() { + if (!m_controller) { + return; + } + if (m_controller->hasStarted()) { + gameStarted(); + } else { + updateOverrides(); + } +} + void OverrideView::updateOverrides() { if (!m_controller) { return; @@ -190,6 +204,7 @@ void OverrideView::gameStarted() { mCoreThread* thread = m_controller->thread(); m_ui.tabWidget->setEnabled(false); + m_recheck.start(); switch (thread->core->platform(thread->core)) { #ifdef M_CORE_GBA @@ -242,6 +257,7 @@ void OverrideView::gameStarted() { } void OverrideView::gameStopped() { + m_recheck.stop(); m_controller.reset(); m_ui.tabWidget->setEnabled(true); m_ui.savetype->setCurrentIndex(0); diff --git a/src/platform/qt/OverrideView.h b/src/platform/qt/OverrideView.h index 5a877b174..cbface71c 100644 --- a/src/platform/qt/OverrideView.h +++ b/src/platform/qt/OverrideView.h @@ -6,6 +6,7 @@ #pragma once #include +#include #include @@ -36,6 +37,7 @@ public: public slots: void saveOverride(); + void recheck(); private slots: void updateOverrides(); @@ -48,6 +50,7 @@ private: std::shared_ptr m_controller; ConfigController* m_config; bool m_savePending = false; + QTimer m_recheck; #ifdef M_CORE_GB uint32_t m_gbColors[12]{}; diff --git a/src/platform/qt/RegisterView.cpp b/src/platform/qt/RegisterView.cpp new file mode 100644 index 000000000..0211f13cb --- /dev/null +++ b/src/platform/qt/RegisterView.cpp @@ -0,0 +1,144 @@ +/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "RegisterView.h" + +#include "CoreController.h" + +#ifdef M_CORE_GBA +#include +#endif +#ifdef M_CORE_GB +#include +#endif + +#include +#include +#include + +using namespace QGBA; + +RegisterView::RegisterView(std::shared_ptr controller, QWidget* parent) + : QWidget(parent) + , m_controller(controller) +{ + QFormLayout* layout = new QFormLayout; + setLayout(layout); + + switch (controller->platform()) { +#ifdef M_CORE_GBA + case PLATFORM_GBA: + addRegisters({ + "r0", + "r1", + "r2", + "r3", + "r4", + "r5", + "r6", + "r7", + "r8", + "r9", + "r10", + "r11", + "r12", + "sp", + "lr", + "pc", + "cpsr", + }); + break; +#endif +#ifdef M_CORE_GB + case PLATFORM_GB: + addRegisters({ + "a", + "f", + "b", + "c", + "d", + "e", + "h", + "l", + "sp", + "pc" + }); + break; +#endif + default: + break; + } +} + +void RegisterView::addRegisters(const QStringList& names) { + QFormLayout* form = static_cast(layout()); + const QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont); + for (const auto& reg : names) { + QLabel* value = new QLabel; + value->setTextInteractionFlags(Qt::TextSelectableByMouse); + value->setFont(font); + form->addWidget(value); + m_registers[reg] = value; + form->addRow(reg, value); + } +} + +void RegisterView::updateRegisters() { + switch (m_controller->platform()) { +#ifdef M_CORE_GBA + case PLATFORM_GBA: + updateRegistersARM(); + break; +#endif +#ifdef M_CORE_GB + case PLATFORM_GB: + updateRegistersLR35902(); + break; +#endif + default: + break; + } +} + +#ifdef M_CORE_GBA +void RegisterView::updateRegistersARM() { + CoreController::Interrupter interrupter(m_controller); + struct ARMCore* core = static_cast(m_controller->thread()->core->cpu); + m_registers["r0"]->setText(QString("%1").arg((uint32_t) core->gprs[0], 8, 16, QChar('0')).toUpper()); + m_registers["r1"]->setText(QString("%1").arg((uint32_t) core->gprs[1], 8, 16, QChar('0')).toUpper()); + m_registers["r2"]->setText(QString("%1").arg((uint32_t) core->gprs[2], 8, 16, QChar('0')).toUpper()); + m_registers["r3"]->setText(QString("%1").arg((uint32_t) core->gprs[3], 8, 16, QChar('0')).toUpper()); + m_registers["r4"]->setText(QString("%1").arg((uint32_t) core->gprs[4], 8, 16, QChar('0')).toUpper()); + m_registers["r5"]->setText(QString("%1").arg((uint32_t) core->gprs[5], 8, 16, QChar('0')).toUpper()); + m_registers["r6"]->setText(QString("%1").arg((uint32_t) core->gprs[6], 8, 16, QChar('0')).toUpper()); + m_registers["r7"]->setText(QString("%1").arg((uint32_t) core->gprs[7], 8, 16, QChar('0')).toUpper()); + m_registers["r8"]->setText(QString("%1").arg((uint32_t) core->gprs[8], 8, 16, QChar('0')).toUpper()); + m_registers["r9"]->setText(QString("%1").arg((uint32_t) core->gprs[9], 8, 16, QChar('0')).toUpper()); + m_registers["r10"]->setText(QString("%1").arg((uint32_t) core->gprs[10], 8, 16, QChar('0')).toUpper()); + m_registers["r11"]->setText(QString("%1").arg((uint32_t) core->gprs[11], 8, 16, QChar('0')).toUpper()); + m_registers["r12"]->setText(QString("%1").arg((uint32_t) core->gprs[12], 8, 16, QChar('0')).toUpper()); + m_registers["sp"]->setText(QString("%1").arg((uint32_t) core->gprs[ARM_SP], 8, 16, QChar('0')).toUpper()); + m_registers["lr"]->setText(QString("%1").arg((uint32_t) core->gprs[ARM_LR], 8, 16, QChar('0')).toUpper()); + m_registers["pc"]->setText(QString("%1").arg((uint32_t) core->gprs[ARM_PC], 8, 16, QChar('0')).toUpper()); + m_registers["cpsr"]->setText(QString("%1").arg((uint32_t) core->cpsr.packed, 8, 16, QChar('0')).toUpper()); +} +#endif + +#ifdef M_CORE_GB +void RegisterView::updateRegistersLR35902() { + CoreController::Interrupter interrupter(m_controller); + struct LR35902Core* core = static_cast(m_controller->thread()->core->cpu); + m_registers["a"]->setText(QString("%1").arg((uint8_t) core->a, 2, 16, QChar('0')).toUpper()); + m_registers["f"]->setText(QString("%1").arg((uint8_t) core->f.packed, 2, 16, QChar('0')).toUpper()); + m_registers["b"]->setText(QString("%1").arg((uint8_t) core->b, 2, 16, QChar('0')).toUpper()); + m_registers["c"]->setText(QString("%1").arg((uint8_t) core->c, 2, 16, QChar('0')).toUpper()); + m_registers["d"]->setText(QString("%1").arg((uint8_t) core->d, 2, 16, QChar('0')).toUpper()); + m_registers["e"]->setText(QString("%1").arg((uint8_t) core->e, 2, 16, QChar('0')).toUpper()); + m_registers["h"]->setText(QString("%1").arg((uint8_t) core->h, 2, 16, QChar('0')).toUpper()); + m_registers["l"]->setText(QString("%1").arg((uint8_t) core->l, 2, 16, QChar('0')).toUpper()); + m_registers["sp"]->setText(QString("%1").arg((uint8_t) core->sp, 4, 16, QChar('0')).toUpper()); + m_registers["pc"]->setText(QString("%1").arg((uint8_t) core->pc, 4, 16, QChar('0')).toUpper()); +} +#endif diff --git a/src/platform/qt/RegisterView.h b/src/platform/qt/RegisterView.h new file mode 100644 index 000000000..1769de445 --- /dev/null +++ b/src/platform/qt/RegisterView.h @@ -0,0 +1,42 @@ +/* Copyright (c) 2013-2017 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#pragma once + +#include +#include + +#include + +class QLabel; + +namespace QGBA { + +class CoreController; + +class RegisterView : public QWidget { +Q_OBJECT + +public: + RegisterView(std::shared_ptr controller, QWidget* parent = nullptr); + +public slots: + void updateRegisters(); + +private: + void addRegisters(const QStringList& names); +#ifdef M_CORE_GBA + void updateRegistersARM(); +#endif +#ifdef M_CORE_GB + void updateRegistersLR35902(); +#endif + + QMap m_registers; + + std::shared_ptr m_controller; +}; + +} diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index e2d0adb19..4f310a022 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -106,6 +106,22 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC m_ui.patchPath->setText(path); } }); + + if (m_ui.cheatsPath->text().isEmpty()) { + m_ui.cheatsSameDir->setChecked(true); + } + connect(m_ui.cheatsSameDir, &QAbstractButton::toggled, [this] (bool e) { + if (e) { + m_ui.cheatsPath->clear(); + } + }); + connect(m_ui.cheatsBrowse, &QAbstractButton::pressed, [this] () { + QString path = GBAApp::app()->getOpenDirectoryName(this, "Select directory"); + if (!path.isNull()) { + m_ui.cheatsSameDir->setChecked(false); + m_ui.cheatsPath->setText(path); + } + }); connect(m_ui.clearCache, &QAbstractButton::pressed, this, &SettingsView::libraryCleared); // TODO: Move to reloadConfig() @@ -257,7 +273,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC } QLocale locale(name.remove(QString("%0-").arg(binaryName)).remove(".qm")); m_ui.languages->addItem(locale.nativeLanguageName(), locale); - if (locale == QLocale()) { + if (locale.bcp47Name() == QLocale().bcp47Name()) { m_ui.languages->setCurrentIndex(m_ui.languages->count() - 1); } } @@ -339,9 +355,15 @@ void SettingsView::updateConfig() { saveSetting("savestatePath", m_ui.savestatePath); saveSetting("screenshotPath", m_ui.screenshotPath); saveSetting("patchPath", m_ui.patchPath); + saveSetting("cheatsPath", m_ui.cheatsPath); saveSetting("libraryStyle", m_ui.libraryStyle->currentIndex()); saveSetting("showLibrary", m_ui.showLibrary); saveSetting("preload", m_ui.preload); + saveSetting("showFps", m_ui.showFps); + saveSetting("cheatAutoload", m_ui.cheatAutoload); + saveSetting("cheatAutosave", m_ui.cheatAutosave); + saveSetting("autoload", m_ui.autoload); + saveSetting("autosave", m_ui.autosave); if (m_ui.fastForwardUnbounded->isChecked()) { saveSetting("fastForwardRatio", "-1"); @@ -464,8 +486,14 @@ void SettingsView::reloadConfig() { loadSetting("savestatePath", m_ui.savestatePath); loadSetting("screenshotPath", m_ui.screenshotPath); loadSetting("patchPath", m_ui.patchPath); + loadSetting("cheatsPath", m_ui.cheatsPath); loadSetting("showLibrary", m_ui.showLibrary); loadSetting("preload", m_ui.preload); + loadSetting("showFps", m_ui.showFps, true); + loadSetting("cheatAutoload", m_ui.cheatAutoload, true); + loadSetting("cheatAutosave", m_ui.cheatAutosave, true); + loadSetting("autoload", m_ui.autoload, true); + loadSetting("autosave", m_ui.autosave, false); m_ui.libraryStyle->setCurrentIndex(loadSetting("libraryStyle").toInt()); diff --git a/src/platform/qt/SettingsView.ui b/src/platform/qt/SettingsView.ui index 6e5d16440..fb5acc11d 100644 --- a/src/platform/qt/SettingsView.ui +++ b/src/platform/qt/SettingsView.ui @@ -419,6 +419,13 @@ + + + + Qt::Horizontal + + + @@ -488,13 +495,70 @@ - - + + + + Show FPS in title bar + + + true + + + + + Qt::Horizontal + + + + Qt::Horizontal + + + + + + + Automatically save cheats + + + true + + + + + + + Automatically load cheats + + + true + + + + + + + Automatically save state + + + true + + + + + + + Automatically load state + + + true + + + @@ -1138,6 +1202,54 @@ + + + + Qt::Horizontal + + + + + + + Cheats + + + + + + + + + + 0 + 0 + + + + + 170 + 0 + + + + + + + + Browse + + + + + + + + + Same directory as the ROM + + + @@ -1669,5 +1781,37 @@ + + cheatsSameDir + toggled(bool) + cheatsPath + setDisabled(bool) + + + 351 + 407 + + + 343 + 372 + + + + + fastForwardUnbounded + toggled(bool) + fastForwardRatio + setDisabled(bool) + + + 445 + 38 + + + 349 + 38 + + + diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index d57656d69..69fe909af 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -52,6 +52,7 @@ #include "VideoView.h" #include +#include #ifdef M_CORE_GB #include #include @@ -275,6 +276,9 @@ QString Window::getFilters() const { #endif #ifdef USE_LZMA "*.7z", +#endif +#ifdef USE_ELF + "*.elf", #endif "*.agb", "*.mb", @@ -564,11 +568,12 @@ void Window::showEvent(QShowEvent* event) { m_wasOpened = true; resizeFrame(m_screenWidget->sizeHint()); QVariant windowPos = m_config->getQtOption("windowPos"); - if (!windowPos.isNull()) { + QRect geom = QApplication::desktop()->availableGeometry(this); + if (!windowPos.isNull() && geom.contains(windowPos.toPoint())) { move(windowPos.toPoint()); } else { QRect rect = frameGeometry(); - rect.moveCenter(QApplication::desktop()->availableGeometry().center()); + rect.moveCenter(geom.center()); move(rect.topLeft()); } if (m_fullscreenOnStart) { @@ -734,7 +739,9 @@ void Window::gameStarted() { #endif m_hitUnimplementedBiosCall = false; - m_fpsTimer.start(); + if (m_config->getOption("showFps", "1").toInt()) { + m_fpsTimer.start(); + } m_focusCheck.start(); if (m_display->underMouse()) { m_screenWidget->setCursor(Qt::BlankCursor); @@ -1499,6 +1506,7 @@ void Window::setupMenu(QMenuBar* menubar) { connect(this, &Window::shutdown, m_overrideView.get(), &QWidget::close); } m_overrideView->show(); + m_overrideView->recheck(); }); addControlledAction(toolsMenu, overrides, "overrideWindow"); @@ -1644,11 +1652,31 @@ void Window::setupMenu(QMenuBar* menubar) { }, this); m_config->updateOption("preload"); + ConfigOption* showFps = m_config->addOption("showFps"); + showFps->connect([this](const QVariant& value) { + if (!value.toInt()) { + m_fpsTimer.stop(); + updateTitle(); + } else if (m_controller) { + m_fpsTimer.start(); + } + }, this); + QAction* exitFullScreen = new QAction(tr("Exit fullscreen"), frameMenu); connect(exitFullScreen, &QAction::triggered, this, &Window::exitFullScreen); exitFullScreen->setShortcut(QKeySequence("Esc")); addHiddenAction(frameMenu, exitFullScreen, "exitFullScreen"); + m_inputController.inputIndex()->addItem(qMakePair([this]() { + if (m_controller) { + mCheatPressButton(m_controller->cheatDevice(), true); + } + }, [this]() { + if (m_controller) { + mCheatPressButton(m_controller->cheatDevice(), false); + } + }), tr("GameShark Button (held)"), "holdGSButton", toolsMenu)->setShortcut(QKeySequence(Qt::Key_Apostrophe)[0]); + for (QAction* action : m_gameActions) { action->setDisabled(true); } @@ -1834,8 +1862,8 @@ void Window::setController(CoreController* controller, const QString& fname) { m_pendingPatch = QString(); } - m_controller->start(); m_controller->loadConfig(m_config); + m_controller->start(); } WindowBackground::WindowBackground(QWidget* parent) diff --git a/src/platform/qt/library/LibraryController.cpp b/src/platform/qt/library/LibraryController.cpp index 1a7fc8bf4..9bfad5d95 100644 --- a/src/platform/qt/library/LibraryController.cpp +++ b/src/platform/qt/library/LibraryController.cpp @@ -29,16 +29,6 @@ void AbstractGameList::removeEntries(QList items) { } } -LibraryLoaderThread::LibraryLoaderThread(QObject* parent) - : QThread(parent) -{ -} - -void LibraryLoaderThread::run() { - mLibraryLoadDirectory(m_library, m_directory.toUtf8().constData()); - m_directory = QString(); -} - LibraryController::LibraryController(QWidget* parent, const QString& path, ConfigController* config) : QStackedWidget(parent) , m_config(config) @@ -47,13 +37,13 @@ LibraryController::LibraryController(QWidget* parent, const QString& path, Confi if (!path.isNull()) { // This can return NULL if the library is already open - m_library = mLibraryLoad(path.toUtf8().constData()); + m_library = std::shared_ptr(mLibraryLoad(path.toUtf8().constData()), mLibraryDestroy); } if (!m_library) { - m_library = mLibraryCreateEmpty(); + m_library = std::shared_ptr(mLibraryCreateEmpty(), mLibraryDestroy); } - mLibraryAttachGameDB(m_library, GBAApp::app()->gameDB()); + mLibraryAttachGameDB(m_library.get(), GBAApp::app()->gameDB()); m_libraryTree = new LibraryTree(this); addWidget(m_libraryTree->widget()); @@ -61,25 +51,12 @@ LibraryController::LibraryController(QWidget* parent, const QString& path, Confi m_libraryGrid = new LibraryGrid(this); addWidget(m_libraryGrid->widget()); - connect(&m_loaderThread, &QThread::finished, this, &LibraryController::refresh, Qt::QueuedConnection); - setViewStyle(LibraryStyle::STYLE_LIST); refresh(); } LibraryController::~LibraryController() { mLibraryListingDeinit(&m_listing); - - if (m_loaderThread.isRunning()) { - m_loaderThread.wait(); - } - if (!m_loaderThread.isRunning() && m_loaderThread.m_library) { - m_library = m_loaderThread.m_library; - m_loaderThread.m_library = nullptr; - } - if (m_library) { - mLibraryDestroy(m_library); - } } void LibraryController::setViewStyle(LibraryStyle newStyle) { @@ -117,7 +94,7 @@ LibraryEntryRef LibraryController::selectedEntry() { VFile* LibraryController::selectedVFile() { LibraryEntryRef entry = selectedEntry(); if (entry) { - return mLibraryOpenVFile(m_library, entry->entry); + return mLibraryOpenVFile(m_library.get(), entry->entry); } else { return nullptr; } @@ -129,35 +106,26 @@ QPair LibraryController::selectedPath() { } void LibraryController::addDirectory(const QString& dir) { - m_loaderThread.m_directory = dir; - m_loaderThread.m_library = m_library; - // The m_loaderThread temporarily owns the library - m_library = nullptr; - m_loaderThread.start(); + // The worker thread temporarily owns the library + std::shared_ptr library = m_library; + m_libraryJob = GBAApp::app()->submitWorkerJob(std::bind(&LibraryController::loadDirectory, this, dir), this, [this, library]() { + m_libraryJob = -1; + refresh(); + }); } void LibraryController::clear() { - if (!m_library) { - if (!m_loaderThread.isRunning() && m_loaderThread.m_library) { - m_library = m_loaderThread.m_library; - m_loaderThread.m_library = nullptr; - } else { - return; - } + if (m_libraryJob > 0) { + return; } - mLibraryClear(m_library); + mLibraryClear(m_library.get()); refresh(); } void LibraryController::refresh() { - if (!m_library) { - if (!m_loaderThread.isRunning() && m_loaderThread.m_library) { - m_library = m_loaderThread.m_library; - m_loaderThread.m_library = nullptr; - } else { - return; - } + if (m_libraryJob > 0) { + return; } setDisabled(true); @@ -166,7 +134,7 @@ void LibraryController::refresh() { QList newEntries; mLibraryListingClear(&m_listing); - mLibraryGetEntries(m_library, &m_listing, 0, 0, nullptr); + mLibraryGetEntries(m_library.get(), &m_listing, 0, 0, nullptr); for (size_t i = 0; i < mLibraryListingSize(&m_listing); i++) { mLibraryEntry* entry = mLibraryListingGetPointer(&m_listing, i); QString fullpath = QString("%1/%2").arg(entry->base, entry->filename); @@ -210,4 +178,10 @@ void LibraryController::selectLastBootedGame() { } } +void LibraryController::loadDirectory(const QString& dir) { + // This class can get delted during this function (sigh) so we need to hold onto this + std::shared_ptr library = m_library; + mLibraryLoadDirectory(library.get(), dir.toUtf8().constData()); +} + } diff --git a/src/platform/qt/library/LibraryController.h b/src/platform/qt/library/LibraryController.h index 027b83f31..789550919 100644 --- a/src/platform/qt/library/LibraryController.h +++ b/src/platform/qt/library/LibraryController.h @@ -9,7 +9,6 @@ #include #include -#include #include #include @@ -66,19 +65,6 @@ public: virtual QWidget* widget() = 0; }; -class LibraryLoaderThread final : public QThread { -Q_OBJECT - -public: - LibraryLoaderThread(QObject* parent = nullptr); - - mLibrary* m_library = nullptr; - QString m_directory; - -protected: - virtual void run() override; -}; - class LibraryController final : public QStackedWidget { Q_OBJECT @@ -110,9 +96,11 @@ private slots: void refresh(); private: + void loadDirectory(const QString&); // Called on separate thread + ConfigController* m_config = nullptr; - LibraryLoaderThread m_loaderThread; - mLibrary* m_library = nullptr; + std::shared_ptr m_library; + qint64 m_libraryJob = -1; mLibraryListing m_listing; QMap m_entries; diff --git a/src/platform/qt/ts/medusa-emu-de.ts b/src/platform/qt/ts/medusa-emu-de.ts index b5eada7d3..dd0c77b0b 100644 --- a/src/platform/qt/ts/medusa-emu-de.ts +++ b/src/platform/qt/ts/medusa-emu-de.ts @@ -83,44 +83,44 @@ Game Boy und Game Boy Advance sind eingetragene Warenzeichen von Nintendo Co., L - + 0 0 - + Palette # Palette # - + Address Adresse - + 0x06000000 0x06000000 - + Red Rot - + Green Grün - + Blue Blau - - - + + + 0x00 (00) 0x00 (00) @@ -522,62 +522,88 @@ Game Boy und Game Boy Advance sind eingetragene Warenzeichen von Nintendo Co., L Text - + Width Breite - + 1 Byte (8-bit) 1 Byte (8-bit) - + 2 Bytes (16-bit) 2 Bytes (16-bit) - + 4 Bytes (32-bit) 4 Bytes (32-bit) - + Number type Zahlensystem - + Hexadecimal Hexadezimal - + Decimal Dezimal - + + Guess automatisch - + + Compare + Vergleichen + + + + Equal + Gleichwertig + + + + Greater + Größer + + + + Less + Kleiner + + + + Delta + Differenz + + + Search Suchen - + Search Within Suchen innerhalb - + Open in Memory Viewer Im Speicher-Monitor öffnen - + Refresh Aktualisieren @@ -1119,7 +1145,7 @@ Game Boy und Game Boy Advance sind eingetragene Warenzeichen von Nintendo Co., L (unbenannt) - + Failed to open cheats file: %1 Fehler beim Öffnen der Cheat-Datei: %1 @@ -1157,22 +1183,22 @@ Game Boy und Game Boy Advance sind eingetragene Warenzeichen von Nintendo Co., L QGBA::CoreController - + Failed to open save file: %1 Fehler beim Öffnen der Speicherdatei: %1 - + Failed to open game file: %1 Fehler beim Öffnen der Spieldatei: %1 - + Failed to open snapshot file for reading: %1 Konnte Snapshot-Datei %1 nicht zum Lesen öffnen - + Failed to open snapshot file for writing: %1 Konnte Snapshot-Datei %1 nicht zum Schreiben öffnen @@ -2858,24 +2884,24 @@ Game Boy und Game Boy Advance sind eingetragene Warenzeichen von Nintendo Co., L QGBA::MemorySearch - + + (%0/%1×) + (%0/%1×) + + + (⅟%0×) (⅟%0×) - - 1 byte%0 - 1 Byte%0 + + (%0×) + (%0×) - - 2 bytes%0 - 2 Bytes%0 - - - - 4 bytes%0 - 4 Bytes%0 + + %1 byte%2 + %1 byte%2 @@ -3012,59 +3038,59 @@ Game Boy und Game Boy Advance sind eingetragene Warenzeichen von Nintendo Co., L QGBA::SettingsView - - + + Qt Multimedia Qt Multimedia - + SDL SDL - + Software (Qt) Software (Qt) - + OpenGL OpenGL - + OpenGL (force version 1.x) OpenGL (erzwinge Version 1.x) - + None (Still Image) Keiner (Standbild) - + Keyboard Tastatur - + Controllers Gamepads - + Shortcuts Tastenkürzel - - + + Shaders Shader - + Select BIOS BIOS auswählen @@ -3123,7 +3149,7 @@ Game Boy und Game Boy Advance sind eingetragene Warenzeichen von Nintendo Co., L QGBA::Window - + Game Boy Advance ROMs (%1) Game Boy Advance-ROMs (%1) @@ -3138,62 +3164,62 @@ Game Boy und Game Boy Advance sind eingetragene Warenzeichen von Nintendo Co., L Game Boy-ROMs (%1) - + All ROMs (%1) Alle ROMs (%1) - + %1 Video Logs (*.mvl) %1 Video-Logs (*.mvl) - + Archives (%1) Archive (%1) - - - + + + Select ROM ROM auswählen - + Game Boy Advance save files (%1) Game Boy Advance-Speicherdateien (%1) - - - + + + Select save Speicherdatei wählen - + Select patch Patch wählen - + Patches (*.ips *.ups *.bps) Patches (*.ips *.ups *.bps) - + Select image Bild auswählen - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) Bild-Datei (*.png *.gif *.jpg *.jpeg);;Alle Dateien (*) - - + + GameShark saves (*.sps *.xps) GameShark-Speicherdaten (*.sps *.xps) @@ -3213,17 +3239,17 @@ Game Boy und Game Boy Advance sind eingetragene Warenzeichen von Nintendo Co., L Video-Log auswählen - + Video logs (*.mvl) Video-Logs (*.mvl) - + Crash Absturz - + The game has crashed with the following error: %1 @@ -3232,528 +3258,528 @@ Game Boy und Game Boy Advance sind eingetragene Warenzeichen von Nintendo Co., L %1 - + Couldn't Load Konnte nicht geladen werden - + Could not load game. Are you sure it's in the correct format? Konnte das Spiel nicht laden. Sind Sie sicher, dass es im korrekten Format vorliegt? - + Unimplemented BIOS call Nicht implementierter BIOS-Aufruf - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. Dieses Spiel verwendet einen BIOS-Aufruf, der nicht implementiert ist. Bitte verwenden Sie für die beste Spielerfahrung das offizielle BIOS. - + Really make portable? Portablen Modus wirklich aktivieren? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? Diese Einstellung wird den Emulator so konfigurieren, dass er seine Konfiguration aus dem gleichen Verzeichnis wie die Programmdatei lädt. Möchten Sie fortfahren? - + Restart needed Neustart benötigt - + Some changes will not take effect until the emulator is restarted. Einige Änderungen werden erst übernommen, wenn der Emulator neu gestartet wurde. - + - Player %1 of %2 - Spieler %1 von %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 Bilder/Sekunde) - %4 - + &File &Datei - + Load &ROM... &ROM laden... - + Load ROM in archive... ROM aus Archiv laden... - + Load temporary save... Temporäre Speicherdatei laden... - + Load &patch... &Patch laden... - + Boot BIOS BIOS booten - + Replace ROM... ROM ersetzen... - + ROM &info... ROM-&Informationen... - + Recent Zuletzt verwendet - + Make portable Portablen Modus aktivieren - + &Load state - Savestate &laden + Savestate (aktueller Zustand) &laden - + F10 F10 - + &Save state - Savestate &speichern + Savestate (aktueller Zustand) &speichern - + Shift+F10 Umschalt+F10 - + Quick load Schnell laden - + Quick save Schnell speichern - + Load recent Lade zuletzt gespeicherten Savestate - + Save recent - Speichere aktuellen Stand + Speichere aktuellen Zustand - + Undo load state Laden des Savestate rückgängig machen - + F11 F11 - + Undo save state Speichern des Savestate rückgängig machen - + Shift+F11 Umschalt+F11 - - + + State &%1 Savestate &%1 - + F%1 F%1 - + Shift+F%1 Umschalt+F%1 - + Load camera image... Lade Kamerabild... - + Import GameShark Save Importiere GameShark-Speicherstand - + Export GameShark Save Exportiere GameShark-Speicherstand - + New multiplayer window Neues Multiplayer-Fenster - + About Über - + E&xit &Beenden - + &Emulation &Emulation - + &Reset Zu&rücksetzen - + Ctrl+R Strg+R - + Sh&utdown Schli&eßen - + Yank game pak Spielmodul herausziehen - + &Pause &Pause - + Ctrl+P Strg+P - + &Next frame &Nächstes Bild - + Ctrl+N Strg+N - + Fast forward (held) Schneller Vorlauf (gehalten) - + &Fast forward Schneller &Vorlauf - + Shift+Tab Umschalt+Tab - + Fast forward speed Vorlauf-Geschwindigkeit - + Unbounded Unbegrenzt - + %0x %0x - + Rewind (held) Zurückspulen (gehalten) - + Re&wind Zur&ückspulen - + ~ ~ - + Step backwards Schrittweiser Rücklauf - + Ctrl+B Strg+B - + Sync to &video Mit &Video synchronisieren - + Sync to &audio Mit &Audio synchronisieren - + Solar sensor Solar-Sensor - + Increase solar level Sonnen-Level erhöhen - + Decrease solar level Sonnen-Level verringern - + Brightest solar level Hellster Sonnen-Level - + Darkest solar level Dunkelster Sonnen-Level - + Brightness %1 Helligkeit %1 - + Audio/&Video Audio/&Video - + Frame size Bildgröße - + %1x %1x - + Toggle fullscreen Vollbildmodus umschalten - + Lock aspect ratio Seitenverhältnis korrigieren - + Force integer scaling Pixelgenaue Skalierung (Integer scaling) - + Frame&skip Frame&skip - + Mute Stummschalten - + FPS target Bildwiederholrate - + 15 15 - + 30 30 - + 45 45 - + Native (59.7) Nativ (59.7) - + 60 60 - + 90 90 - + 120 120 - + 240 240 - + Take &screenshot &Screenshot erstellen - + F12 F12 - + Record output... Ausgabe aufzeichen... - + Record GIF... GIF aufzeichen... - + Record video log... Video-Log aufzeichnen... - + Stop video log Video-Log beenden - + Game Boy Printer... Game Boy Printer... - + Video layers Video-Ebenen - + Audio channels Audio-Kanäle - + &Tools &Werkzeuge - + View &logs... &Logs ansehen... - + Game &overrides... Spiel-&Überschreibungen... - + Game &Pak sensors... Game &Pak-Sensoren... - + &Cheats... &Cheats... - + Open debugger console... Debugger-Konsole äffnen... - + Start &GDB server... &GDB-Server starten... - + Settings... Einstellungen... @@ -3778,37 +3804,37 @@ Game Boy und Game Boy Advance sind eingetragene Warenzeichen von Nintendo Co., L Ordner auswählen - + Add folder to library... Ordner zur Bibliothek hinzufügen... - + Bilinear filtering Bilineare Filterung - + View &palette... &Palette betrachten... - + View &sprites... &Sprites betrachten... - + View &tiles... &Tiles betrachten... - + View &map... &Map betrachten... - + View memory... Speicher betrachten... @@ -3823,67 +3849,72 @@ Game Boy und Game Boy Advance sind eingetragene Warenzeichen von Nintendo Co., L Speicher durchsuchen... - + View &I/O registers... &I/O-Register betrachten... - + Exit fullscreen Vollbildmodus beenden - + + GameShark Button (held) + GameShark-Taste (gehalten) + + + Autofire Autofeuer - + Autofire A Autofeuer A - + Autofire B Autofeuer B - + Autofire L Autofeuer L - + Autofire R Autofeuer R - + Autofire Start Autofeuer Start - + Autofire Select Autofeuer Select - + Autofire Up Autofeuer nach oben - + Autofire Right Autofeuer rechts - + Autofire Down Autofeuer nach unten - + Autofire Left Autofeuer links @@ -4176,13 +4207,13 @@ Game Boy und Game Boy Advance sind eingetragene Warenzeichen von Nintendo Co., L Skip every - Alle + Überspringe - + frames - Bilder + Bild(er) @@ -4231,131 +4262,162 @@ Game Boy und Game Boy Advance sind eingetragene Warenzeichen von Nintendo Co., L Englisch - + List view Listenansicht - + Tree view Baumansicht - + + Show FPS in title bar + Bildwiederholrate in der Titelleiste anzeigen + + + + Automatically save cheats + Cheats automatisch speichern + + + + Automatically load cheats + Cheats automatisch laden + + + + Automatically save state + Zustand (Savestate) automatisch speichern + + + + Automatically load state + Zustand (Savestate) automatisch laden + + + + Cheats + Cheats + + + Game Boy model Game Boy-Modell - - - + + + Autodetect Automatisch erkennen - - - + + + Game Boy (DMG) Game Boy (DMG) - - - + + + Super Game Boy (SGB) Super Game Boy (SGB) - - - + + + Game Boy Color (CGB) Game Boy Color (CGB) - - - + + + Game Boy Advance (AGB) Game Boy Advance (AGB) - + Super Game Boy model Super Game Boy-Modell - + Game Boy Color model Game Boy Color-Modell - + Default BG colors: Standard-Hintergrundfarben: - + Default sprite colors 1: Standard-Sprite-Farben 1: - + Default sprite colors 2: Standard-Sprite-Farben 2: - + Super Game Boy borders Super Game Boy-Rahmen - + Camera driver: Kamera-Treiber: - + Library: Bibliothek: - + Show when no game open Anzeigen, wenn kein Spiel geöffnet ist - + Clear cache Cache leeren - + Fast forward speed: Vorlauf-Geschwindigkeit: - + Rewind affects save data Rücklauf beeinflusst Speicherdaten - + Preload entire ROM into memory ROM-Datei vollständig in Arbeitsspeicher vorladen - - - - - - - - + + + + + + + + + Browse Durchsuchen @@ -4376,22 +4438,22 @@ in Arbeitsspeicher vorladen wenn vorhanden - + Skip BIOS intro BIOS-Intro überspringen - + × × - + Unbounded unbegrenzt - + Suspend screensaver Bildschirmschoner deaktivieren @@ -4401,50 +4463,50 @@ wenn vorhanden BIOS - + Pause when inactive Pause, wenn inaktiv - + Run all Alle ausführen - + Remove known Bekannte entfernen - + Detect and remove Erkennen und entfernen - + Allow opposing input directions Gegensätzliche Eingaberichtungen erlauben - - + + Screenshot Screenshot - - + + Save data Speicherdaten - - + + Cheat codes Cheat-Codes - + Enable rewind Rücklauf aktivieren @@ -4454,42 +4516,42 @@ wenn vorhanden Bilineare Filterung - + Rewind history: Rücklauf-Verlauf: - + Idle loops: Leerlaufprozesse: - + Savestate extra data: Zusätzliche Savestate-Daten: - + Load extra data: Lade zusätzliche Daten: - + Autofire interval: Autofeuer-Intervall: - + GB BIOS file: Datei mit GB-BIOS: - + GBA BIOS file: Datei mit GBA-BIOS: - + GBC BIOS file: Datei mit GBC-BIOS: @@ -4504,30 +4566,31 @@ wenn vorhanden Datei mit SGB-BIOS: - + Save games Spielstände - - - - + + + + + Same directory as the ROM Verzeichnis der ROM-Datei - + Save states Savestates - + Screenshots Screenshots - + Patches Patches diff --git a/src/platform/sdl/CMakeLists.txt b/src/platform/sdl/CMakeLists.txt index b3a8df028..1005c4cbf 100644 --- a/src/platform/sdl/CMakeLists.txt +++ b/src/platform/sdl/CMakeLists.txt @@ -95,7 +95,9 @@ endif() add_executable(${BINARY_NAME}-sdl WIN32 ${PLATFORM_SRC} ${MAIN_SRC}) set_target_properties(${BINARY_NAME}-sdl PROPERTIES COMPILE_DEFINITIONS "${FEATURE_DEFINES};${FUNCTION_DEFINES}") target_link_libraries(${BINARY_NAME}-sdl ${BINARY_NAME} ${PLATFORM_LIBRARY} ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY}) -set_target_properties(${BINARY_NAME}-sdl PROPERTIES OUTPUT_NAME ${BINARY_NAME}) +if(NOT WIN32) + set_target_properties(${BINARY_NAME}-sdl PROPERTIES OUTPUT_NAME ${BINARY_NAME}) +endif() install(TARGETS ${BINARY_NAME}-sdl DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${BINARY_NAME}-sdl) if(UNIX) install(FILES ${CMAKE_SOURCE_DIR}/doc/${BINARY_NAME}.6 DESTINATION ${MANDIR}/man6 COMPONENT ${BINARY_NAME}-sdl) diff --git a/src/platform/sdl/main.c b/src/platform/sdl/main.c index e9a7ce812..5a8a5707f 100644 --- a/src/platform/sdl/main.c +++ b/src/platform/sdl/main.c @@ -181,6 +181,7 @@ int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) { return 1; } mCoreAutoloadSave(renderer->core); + mCoreAutoloadCheats(renderer->core); #ifdef ENABLE_SCRIPTING struct mScriptBridge* bridge = mScriptBridgeCreate(); #ifdef ENABLE_PYTHON diff --git a/src/platform/sdl/sdl-audio.h b/src/platform/sdl/sdl-audio.h index dc21cb86c..2ae920af4 100644 --- a/src/platform/sdl/sdl-audio.h +++ b/src/platform/sdl/sdl-audio.h @@ -13,6 +13,14 @@ CXX_GUARD_START #include #include +// Altivec sometimes defines this +#ifdef vector +#undef vector +#endif +#ifdef bool +#undef bool +#define bool _Bool +#endif mLOG_DECLARE_CATEGORY(SDL_AUDIO); diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index ba0d9995b..215e7f01f 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -407,7 +407,7 @@ static void _mSDLHandleKeypress(struct mCoreThread* context, struct mSDLPlayer* if (!event->keysym.mod) { key = mInputMapKey(sdlContext->bindings, SDL_BINDING_KEY, event->keysym.sym); } - if (key != -1) { + if (key != -1 && !event->repeat) { mCoreThreadInterrupt(context); if (event->type == SDL_KEYDOWN) { context->core->addKeys(context->core, 1 << key); diff --git a/src/platform/sdl/sdl-events.h b/src/platform/sdl/sdl-events.h index 3c67222cd..c9d7b386f 100644 --- a/src/platform/sdl/sdl-events.h +++ b/src/platform/sdl/sdl-events.h @@ -16,6 +16,14 @@ CXX_GUARD_START #include #include +// Altivec sometimes defines this +#ifdef vector +#undef vector +#endif +#ifdef bool +#undef bool +#define bool _Bool +#endif mLOG_DECLARE_CATEGORY(SDL_EVENTS); diff --git a/src/third-party/inih/ini.c b/src/third-party/inih/ini.c index 2c278afaa..932efba8c 100755 --- a/src/third-party/inih/ini.c +++ b/src/third-party/inih/ini.c @@ -21,8 +21,8 @@ https://github.com/benhoyt/inih #include #endif -#define MAX_SECTION 50 -#define MAX_NAME 50 +#define MAX_SECTION 128 +#define MAX_NAME 128 /* Strip whitespace chars off end of given string, in place. Return s. */ static char* rstrip(char* s) diff --git a/src/util/png-io.c b/src/util/png-io.c index 24d301676..7ac760b93 100644 --- a/src/util/png-io.c +++ b/src/util/png-io.c @@ -47,16 +47,19 @@ static png_infop _pngWriteHeader(png_structp png, unsigned width, unsigned heigh return 0; } png_set_IHDR(png, info, width, height, 8, type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); - png_write_info(png, info); return info; } png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height) { - return _pngWriteHeader(png, width, height, PNG_COLOR_TYPE_RGB); + png_infop info = _pngWriteHeader(png, width, height, PNG_COLOR_TYPE_RGB); + png_write_info(png, info); + return info; } png_infop PNGWriteHeaderA(png_structp png, unsigned width, unsigned height) { - return _pngWriteHeader(png, width, height, PNG_COLOR_TYPE_RGB_ALPHA); + png_infop info = _pngWriteHeader(png, width, height, PNG_COLOR_TYPE_RGB_ALPHA); + png_write_info(png, info); + return info; } png_infop PNGWriteHeader8(png_structp png, unsigned width, unsigned height) { diff --git a/tools/deploy-win.sh b/tools/deploy-win.sh new file mode 100644 index 000000000..d5a3d3296 --- /dev/null +++ b/tools/deploy-win.sh @@ -0,0 +1,27 @@ +#!/bin/bash +BINARY=$1 +INSTALLPATH="$2" +WORKDIR="$3" + + +if [ -z "$DESTDIR" ]; then + OUTDIR="$INSTALLPATH" +else + if [ -n $(echo ${INSTALLPATH} | grep "^[A-Z]:") ]; then + INSTALLPATH="${INSTALLPATH:3}" + fi + OUTDIR="$WORKDIR/$DESTDIR/$INSTALLPATH" +fi + +IFS=$'\n' +if [ -n $(which ntldd 2>&1 | grep /ntldd) ]; then + DLLS=$(ntldd -R "$BINARY" | grep -i mingw | cut -d">" -f2 | sed -e 's/(0x[0-9a-f]\+)//' -e 's/^ \+//' -e 's/ \+$//' -e 's,\\,/,g') +elif [ -n $(which gdb 2>&1 | grep /gdb) ]; then + DLLS=$(gdb "$BINARY" --command=$(dirname $0)/dlls.gdb | grep -i mingw | cut -d" " -f7- | sed -e 's/^ \+//' -e 's/ \+$//' -e 's,\\,/,g') +else + echo "Please install gdb or ntldd for deploying DLLs" +fi +cp -vu $DLLS "$OUTDIR" +if [ -n $(which windeployqt 2>&1 | grep /windeployqt) ]; then + windeployqt --no-opengl-sw --no-svg --release --dir "$OUTDIR" "$BINARY" +fi diff --git a/tools/dlls.gdb b/tools/dlls.gdb new file mode 100644 index 000000000..017b39ac5 --- /dev/null +++ b/tools/dlls.gdb @@ -0,0 +1,3 @@ +start +info sharedlibrary +q