Merge branch 'master' into translations
|
@ -1,12 +1,15 @@
|
|||
# Generic files
|
||||
*.user*
|
||||
*~
|
||||
*.swp
|
||||
*.pyc
|
||||
|
||||
# Build directories
|
||||
/build
|
||||
/build-*
|
||||
/.vs
|
||||
|
||||
# Build files
|
||||
*.a
|
||||
*.dylib
|
||||
*.dll
|
||||
|
@ -18,4 +21,9 @@ CMakeCache.txt
|
|||
CMakeFiles
|
||||
CMakeSettings.json
|
||||
cmake_install.cmake
|
||||
hle-bios.bin
|
||||
version.c
|
||||
|
||||
# Runtime generated cruft
|
||||
*.sav
|
||||
*.ss0
|
||||
|
|
64
CHANGES
|
@ -1,20 +1,76 @@
|
|||
0.11.0: (Future)
|
||||
Features:
|
||||
- Scripting: New `input` API for getting raw keyboard/mouse/controller state
|
||||
- Scripting: New `storage` API for saving data for a script, e.g. settings
|
||||
- Scripting: Debugger integration to allow for breakpoints and watchpoints
|
||||
- New unlicensed GB mappers: NT (older types 1 and 2), Li Cheng, GGB-81
|
||||
- Debugger: Add range watchpoints
|
||||
Emulation fixes:
|
||||
- GBA Memory: Make VRAM access stalls only apply to BG RAM
|
||||
- ARM: Remove obsolete force-alignment in `bx pc` (fixes mgba.io/i/2964)
|
||||
- ARM: Fake bpkt instruction should take no cycles (fixes mgba.io/i/2551)
|
||||
- GB Audio: Fix channels 1/2 staying muted if restarted after long silence
|
||||
- GB Audio: Fix channel 1 restarting if sweep applies after stop (fixes mgba.io/i/2965)
|
||||
- GB I/O: Read back proper SVBK value after writing 0 (fixes mgba.io/i/2921)
|
||||
- GB Serialize: Add missing Pocket Cam state to savestates
|
||||
- GB SIO: Disabling SIO should cancel pending transfers (fixes mgba.io/i/2537)
|
||||
- GB Video: Implement DMG-style sprite ordering
|
||||
- GBA: Unhandled bkpt should be treated as an undefined exception
|
||||
- GBA Audio: Fix sample timing drifting when changing sample interval
|
||||
- GBA Audio: Fix initial channel 3 wave RAM (fixes mgba.io/i/2947)
|
||||
- GBA GPIO: Fix tilt scale and orientation (fixes mgba.io/i/2703)
|
||||
- GBA BIOS: Fix clobbering registers with word-sized CpuSet
|
||||
- GBA SIO: Fix normal mode SI/SO semantics (fixes mgba.io/i/2925)
|
||||
- GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722)
|
||||
Other fixes:
|
||||
- Core: Allow sending thread requests to a crashed core (fixes mgba.io/i/2784)
|
||||
- Qt: Fix crash when attempting to use OpenGL 2.1 to 3.1 (fixes mgba.io/i/2794)
|
||||
- Qt: Disable sync while running scripts from main thread (fixes mgba.io/i/2738)
|
||||
- Core: Fix inconsistencies with setting game-specific overrides (fixes mgba.io/i/2963)
|
||||
- Debugger: Fix writing to specific segment in command-line debugger
|
||||
- mGUI: Fix cases where an older save state screenshot would be shown. (fixes mgba.io/i/2183)
|
||||
- Qt: Fix savestate preview sizes with different scales (fixes mgba.io/i/2560)
|
||||
Misc:
|
||||
- Core: Handle relative paths for saves, screenshots, etc consistently (fixes mgba.io/i/2826)
|
||||
- GB: Prevent incompatible BIOSes from being used on differing models
|
||||
- GB Serialize: Add missing savestate support for MBC6 and NT (newer)
|
||||
- GBA: Improve detection of valid ELF ROMs
|
||||
- mGUI: Enable auto-softpatching (closes mgba.io/i/2899)
|
||||
- Qt: Add exporting of SAV + RTC saves from Save Converter to strip RTC data
|
||||
- Qt: Handle multiple save game files for disparate games separately (fixes mgba.io/i/2887)
|
||||
- Scripting: Add `callbacks:oneshot` for single-call callbacks
|
||||
|
||||
0.10.2: (2023-04-23)
|
||||
Emulation fixes:
|
||||
- GBA Audio: Fix improperly deserializing GB audio registers (fixes mgba.io/i/2793)
|
||||
- GBA Audio: Clear GB audio state when disabled
|
||||
- GBA Memory: Make VRAM access stalls only apply to BG RAM
|
||||
- GBA Overrides: Fix saving in PMD:RRT (JP) (fixes mgba.io/i/2862)
|
||||
- GBA SIO: Fix SIOCNT SI pin value after attaching player 2 (fixes mgba.io/i/2805)
|
||||
- GBA SIO: Fix unconnected normal mode SIOCNT SI bit (fixes mgba.io/i/2810)
|
||||
- GBA SIO: Normal mode transfers with no clock should not finish (fixes mgba.io/i/2811)
|
||||
- GBA Timers: Cascading timers don't tick when disabled (fixes mgba.io/i/2812)
|
||||
- GBA Video: Fix interpolation issues with OpenGL renderer
|
||||
Other fixes:
|
||||
- Core: Allow sending thread requests to a crashed core (fixes mgba.io/i/2784)
|
||||
- FFmpeg: Force lower sample rate for codecs not supporting high rates (fixes mgba.io/i/2869)
|
||||
- Qt: Fix crash when attempting to use OpenGL 2.1 to 3.1 (fixes mgba.io/i/2794)
|
||||
- Qt: Disable sync while running scripts from main thread (fixes mgba.io/i/2738)
|
||||
- Qt: Properly cap number of attached players by platform (fixes mgba.io/i/2807)
|
||||
- Qt: Disable attempted linking betwen incompatible platforms (fixes mgba.io/i/2702)
|
||||
- Qt: Fix modifier key names in shortcut editor (fixes mgba.io/i/2817)
|
||||
- Qt: Fix a handful of edge cases with graphics viewers (fixes mgba.io/i/2827)
|
||||
- Qt: Fix full-buffer rewind
|
||||
- Qt: Fix crash if loading a shader fails
|
||||
- Qt: Fix black screen when starting with a game (fixes mgba.io/i/2781)
|
||||
- Qt: Fix OSD on modern macOS (fixes mgba.io/i/2736)
|
||||
- Qt: Fix checked state of mute menu option at load (fixes mgba.io/i/2701)
|
||||
- Qt: Remove OpenGL proxy thread and override SwapInterval directly instead
|
||||
- Scripting: Fix receiving packets for client sockets
|
||||
- Scripting: Fix empty receive calls returning unknown error on Windows
|
||||
- Scripting: Return proper callback ID from socket.add
|
||||
- Vita: Work around broken mktime implementation in Vita SDK (fixes mgba.io/i/2876)
|
||||
Misc:
|
||||
- Qt: Include wayland QPA in AppImage (fixes mgba.io/i/2796)
|
||||
- Qt: Stop eating boolean action key events (fixes mgba.io/i/2636)
|
||||
- Qt: Automatically change video file extension as appropriate
|
||||
- Qt: Swap P1 and other player's save if P1 loaded it first (closes mgba.io/i/2750)
|
||||
|
||||
0.10.1: (2023-01-10)
|
||||
Emulation fixes:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
cmake_minimum_required(VERSION 3.1)
|
||||
cmake_minimum_required(VERSION 3.3)
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/src/platform/cmake/")
|
||||
|
||||
if(POLICY CMP0025)
|
||||
|
@ -34,8 +34,12 @@ if(NOT MSVC)
|
|||
# mingw32 likes to complain about using the "wrong" format strings despite them actually working
|
||||
set(WARNING_FLAGS "${WARNING_FLAGS} -Wno-format")
|
||||
endif()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} -Werror=implicit-function-declaration -Werror=implicit-int")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS}")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} -Werror=implicit-function-declaration -Werror=implicit-int -fwrapv")
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
|
||||
# TODO: Remove this once mScript KV pairs support const correctness
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror=incompatible-pointer-types")
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS} -Woverloaded-virtual")
|
||||
else()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS /wd4003 /wd4244 /wd4146 /wd4267 /Zc:preprocessor-")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_CRT_SECURE_NO_WARNINGS /wd4003 /wd4244 /wd4146 /wd4267 /Zc:preprocessor-")
|
||||
|
@ -55,6 +59,7 @@ if(NOT LIBMGBA_ONLY)
|
|||
set(USE_SQLITE3 ON CACHE BOOL "Whether or not to enable SQLite3 support")
|
||||
set(USE_ELF ON CACHE BOOL "Whether or not to enable ELF support")
|
||||
set(USE_LUA ON CACHE BOOL "Whether or not to enable Lua scripting support")
|
||||
set(USE_JSON_C ON CACHE BOOL "Whether or not to enable JSON-C support")
|
||||
set(M_CORE_GBA ON CACHE BOOL "Build Game Boy Advance core")
|
||||
set(M_CORE_GB ON CACHE BOOL "Build Game Boy core")
|
||||
set(USE_LZMA ON CACHE BOOL "Whether or not to enable 7-Zip support")
|
||||
|
@ -337,6 +342,8 @@ find_function(popcount32)
|
|||
find_function(futimens)
|
||||
find_function(futimes)
|
||||
|
||||
find_function(realpath)
|
||||
|
||||
if(ANDROID AND ANDROID_NDK_MAJOR GREATER 13)
|
||||
find_function(localtime_r)
|
||||
set(HAVE_STRTOF_L ON)
|
||||
|
@ -419,6 +426,13 @@ if(BUILD_GL)
|
|||
elseif(UNIX AND NOT APPLE AND TARGET OpenGL::GL)
|
||||
set(OPENGL_LIBRARY OpenGL::GL)
|
||||
endif()
|
||||
if(OpenGL_GLX_FOUND)
|
||||
list(APPEND FEATURES GLX)
|
||||
endif()
|
||||
if(OpenGL_EGL_FOUND)
|
||||
list(APPEND FEATURES EGL)
|
||||
list(APPEND OPENGL_LIBRARY ${OPENGL_egl_LIBRARY})
|
||||
endif()
|
||||
endif()
|
||||
if(BUILD_GL)
|
||||
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gl.c)
|
||||
|
@ -477,6 +491,7 @@ endif()
|
|||
if(DISABLE_DEPS)
|
||||
set(USE_GDB_STUB OFF)
|
||||
set(USE_DISCORD_RPC OFF)
|
||||
set(USE_JSON_C OFF)
|
||||
set(USE_SQLITE3 OFF)
|
||||
set(USE_PNG OFF)
|
||||
set(USE_ZLIB OFF)
|
||||
|
@ -765,11 +780,36 @@ endif()
|
|||
|
||||
if(ENABLE_SCRIPTING)
|
||||
list(APPEND ENABLES SCRIPTING)
|
||||
find_feature(USE_JSON_C "json-c")
|
||||
if(NOT USE_LUA VERSION_LESS 5.1)
|
||||
find_feature(USE_LUA "Lua" ${USE_LUA})
|
||||
else()
|
||||
find_feature(USE_LUA "Lua")
|
||||
endif()
|
||||
if(USE_JSON_C)
|
||||
list(APPEND FEATURES JSON_C)
|
||||
if(TARGET json-c::json-c)
|
||||
list(APPEND DEPENDENCY_LIB json-c::json-c)
|
||||
get_target_property(JSON_C_SONAME json-c::json-c IMPORTED_SONAME_NONE)
|
||||
string(SUBSTRING "${JSON_C_SONAME}" 13 -1 JSON_C_SOVER)
|
||||
|
||||
# This is only needed on 0.15, but the target unhelpfully does not contain version info
|
||||
get_target_property(JSON_C_INCLUDE_DIR json-c::json-c INTERFACE_INCLUDE_DIRECTORIES)
|
||||
if(NOT JSON_C_INCLUDE_DIR MATCHES json-c$)
|
||||
include_directories(AFTER "${JSON_C_INCLUDE_DIR}/json-c")
|
||||
endif()
|
||||
else()
|
||||
if(${json-c_VERSION} VERSION_LESS 0.13.0)
|
||||
set(JSON_C_SOVER 3)
|
||||
elseif(${json-c_VERSION} VERSION_LESS 0.15.0)
|
||||
set(JSON_C_SOVER 4)
|
||||
endif()
|
||||
list(APPEND DEPENDENCY_LIB ${json-c_LIBRARIES})
|
||||
include_directories(AFTER ${json-c_INCLUDE_DIRS})
|
||||
link_directories(${json-c_LIBDIRS})
|
||||
endif()
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libjson-c${JSON_C_SOVER}")
|
||||
endif()
|
||||
if(USE_LUA)
|
||||
list(APPEND FEATURE_DEFINES USE_LUA)
|
||||
include_directories(AFTER ${LUA_INCLUDE_DIR})
|
||||
|
@ -1289,6 +1329,7 @@ if(NOT QUIET AND NOT LIBMGBA_ONLY)
|
|||
else()
|
||||
message(STATUS " Lua: ${USE_LUA}")
|
||||
endif()
|
||||
message(STATUS " storage API: ${USE_JSON_C}")
|
||||
endif()
|
||||
message(STATUS "Frontends:")
|
||||
message(STATUS " Qt: ${BUILD_QT}")
|
||||
|
|
|
@ -125,9 +125,9 @@ Compiling requires using CMake 3.1 or newer. GCC, Clang, and Visual Studio 2019
|
|||
|
||||
#### Docker building
|
||||
|
||||
The recommended way to build for most platforms is to use Docker. Several Docker images are provided that contain the requisite toolchain and dependencies for building mGBA across several platforms.
|
||||
The recommended way to build for most platforms is to use Docker. Several Docker images are provided that contain the requisite toolchain and dependencies for building mGBA across several platforms.
|
||||
|
||||
Note: If you are on an older Windows system before Windows 10, you may need to configure your Docker to use VirtualBox shared folders to correctly map your current `mgba` checkout directory to the Docker image's working directory. (See issue [#1985](https://mgba.io/i/1985) for details.)
|
||||
Note: If you are on an older Windows system before Windows 10, you may need to configure your Docker to use VirtualBox shared folders to correctly map your current `mgba` checkout directory to the Docker image's working directory. (See issue [#1985](https://mgba.io/i/1985) for details.)
|
||||
|
||||
To use a Docker image to build mGBA, simply run the following command while in the root of an mGBA checkout:
|
||||
|
||||
|
@ -234,6 +234,7 @@ mGBA has no hard dependencies, however, the following optional dependencies are
|
|||
- SQLite3: for game databases.
|
||||
- libelf: for ELF loading.
|
||||
- Lua: for scripting.
|
||||
- json-c: for the scripting `storage` API.
|
||||
|
||||
SQLite3, libpng, and zlib are included with the emulator, so they do not need to be externally compiled first.
|
||||
|
||||
|
@ -254,7 +255,7 @@ Footnotes
|
|||
Copyright
|
||||
---------
|
||||
|
||||
mGBA is Copyright © 2013 – 2022 Jeffrey Pfau. It is distributed under the [Mozilla Public License version 2.0](https://www.mozilla.org/MPL/2.0/). A copy of the license is available in the distributed LICENSE file.
|
||||
mGBA is Copyright © 2013 – 2023 Jeffrey Pfau. It is distributed under the [Mozilla Public License version 2.0](https://www.mozilla.org/MPL/2.0/). A copy of the license is available in the distributed LICENSE file.
|
||||
|
||||
mGBA contains the following third-party libraries:
|
||||
|
||||
|
|
After Width: | Height: | Size: 1.3 KiB |
|
@ -0,0 +1,6 @@
|
|||
[testinfo]
|
||||
skip=15
|
||||
frames=1
|
||||
|
||||
[ports.cinema]
|
||||
sgb.borders=0
|
After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 702 B After Width: | Height: | Size: 703 B |
Before Width: | Height: | Size: 711 B |
|
@ -44,6 +44,10 @@ CXX_GUARD_START
|
|||
#define restrict __restrict
|
||||
#endif
|
||||
|
||||
#ifndef containerof
|
||||
#define containerof(PTR, TYPE, MEMBER) ((TYPE*) ((uintptr_t) (PTR) - offsetof(TYPE, MEMBER)))
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <Windows.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -110,13 +114,13 @@ typedef intptr_t ssize_t;
|
|||
#define ATOMIC_LOAD_PTR(DST, SRC) DST = InterlockedCompareExchangePointer(&SRC, 0, 0)
|
||||
#else
|
||||
// TODO
|
||||
#define ATOMIC_STORE(DST, SRC) DST = SRC
|
||||
#define ATOMIC_LOAD(DST, SRC) DST = SRC
|
||||
#define ATOMIC_ADD(DST, OP) DST += OP
|
||||
#define ATOMIC_SUB(DST, OP) DST -= OP
|
||||
#define ATOMIC_OR(DST, OP) DST |= OP
|
||||
#define ATOMIC_AND(DST, OP) DST &= OP
|
||||
#define ATOMIC_CMPXCHG(DST, EXPECTED, OP) ((DST == EXPECTED) ? ((DST = OP), true) : false)
|
||||
#define ATOMIC_STORE(DST, SRC) ((DST) = (SRC))
|
||||
#define ATOMIC_LOAD(DST, SRC) ((DST) = (SRC))
|
||||
#define ATOMIC_ADD(DST, OP) ((DST) += (OP))
|
||||
#define ATOMIC_SUB(DST, OP) ((DST) -= (OP))
|
||||
#define ATOMIC_OR(DST, OP) ((DST) |= (OP))
|
||||
#define ATOMIC_AND(DST, OP) ((DST) &= (OP))
|
||||
#define ATOMIC_CMPXCHG(DST, EXPECTED, OP) (((DST) == (EXPECTED)) ? (((DST) = (OP)), true) : false)
|
||||
#define ATOMIC_STORE_PTR(DST, SRC) ATOMIC_STORE(DST, SRC)
|
||||
#define ATOMIC_LOAD_PTR(DST, SRC) ATOMIC_LOAD(DST, SRC)
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/* Copyright (c) 2013-2023 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 GEOMETRY_H
|
||||
#define GEOMETRY_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
struct mSize {
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
struct mRectangle {
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
void mRectangleUnion(struct mRectangle* dst, const struct mRectangle* add);
|
||||
bool mRectangleIntersection(struct mRectangle* dst, const struct mRectangle* add);
|
||||
void mRectangleCenter(const struct mRectangle* ref, struct mRectangle* rect);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -0,0 +1,397 @@
|
|||
/* Copyright (c) 2013-2015 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef M_IMAGE_H
|
||||
#define M_IMAGE_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
#ifdef COLOR_16_BIT
|
||||
typedef uint16_t color_t;
|
||||
#define BYTES_PER_PIXEL 2
|
||||
#else
|
||||
typedef uint32_t color_t;
|
||||
#define BYTES_PER_PIXEL 4
|
||||
#endif
|
||||
|
||||
#define M_R5(X) ((X) & 0x1F)
|
||||
#define M_G5(X) (((X) >> 5) & 0x1F)
|
||||
#define M_B5(X) (((X) >> 10) & 0x1F)
|
||||
|
||||
#define M_R8(X) ((M_R5(X) * 0x21) >> 2)
|
||||
#define M_G8(X) ((M_G5(X) * 0x21) >> 2)
|
||||
#define M_B8(X) ((M_B5(X) * 0x21) >> 2)
|
||||
|
||||
#define M_RGB5_TO_BGR8(X) ((M_R5(X) << 3) | (M_G5(X) << 11) | (M_B5(X) << 19))
|
||||
#define M_RGB5_TO_RGB8(X) ((M_R5(X) << 19) | (M_G5(X) << 11) | (M_B5(X) << 3))
|
||||
#define M_RGB8_TO_BGR5(X) ((((X) & 0xF8) >> 3) | (((X) & 0xF800) >> 6) | (((X) & 0xF80000) >> 9))
|
||||
#define M_RGB8_TO_RGB5(X) ((((X) & 0xF8) << 7) | (((X) & 0xF800) >> 6) | (((X) & 0xF80000) >> 19))
|
||||
|
||||
#ifndef COLOR_16_BIT
|
||||
#define M_COLOR_RED 0x000000FF
|
||||
#define M_COLOR_GREEN 0x0000FF00
|
||||
#define M_COLOR_BLUE 0x00FF0000
|
||||
#define M_COLOR_ALPHA 0xFF000000
|
||||
#define M_COLOR_WHITE 0x00FFFFFF
|
||||
|
||||
#define M_RGB8_TO_NATIVE(X) (((X) & 0x00FF00) | (((X) & 0x0000FF) << 16) | (((X) & 0xFF0000) >> 16))
|
||||
#elif defined(COLOR_5_6_5)
|
||||
#define M_COLOR_RED 0x001F
|
||||
#define M_COLOR_GREEN 0x07E0
|
||||
#define M_COLOR_BLUE 0xF800
|
||||
#define M_COLOR_ALPHA 0x0000
|
||||
#define M_COLOR_WHITE 0xFFDF
|
||||
|
||||
#define M_RGB8_TO_NATIVE(X) ((((X) & 0xF8) << 8) | (((X) & 0xFC00) >> 5) | (((X) & 0xF80000) >> 19))
|
||||
#else
|
||||
#define M_COLOR_RED 0x001F
|
||||
#define M_COLOR_GREEN 0x03E0
|
||||
#define M_COLOR_BLUE 0x7C00
|
||||
#define M_COLOR_ALPHA 0x1000
|
||||
#define M_COLOR_WHITE 0x7FFF
|
||||
|
||||
#define M_RGB8_TO_NATIVE(X) M_RGB8_TO_BGR5(X)
|
||||
#endif
|
||||
|
||||
enum mColorFormat {
|
||||
mCOLOR_XBGR8 = 0x00001,
|
||||
mCOLOR_XRGB8 = 0x00002,
|
||||
mCOLOR_BGRX8 = 0x00004,
|
||||
mCOLOR_RGBX8 = 0x00008,
|
||||
mCOLOR_ABGR8 = 0x00010,
|
||||
mCOLOR_ARGB8 = 0x00020,
|
||||
mCOLOR_BGRA8 = 0x00040,
|
||||
mCOLOR_RGBA8 = 0x00080,
|
||||
mCOLOR_RGB5 = 0x00100,
|
||||
mCOLOR_BGR5 = 0x00200,
|
||||
mCOLOR_RGB565 = 0x00400,
|
||||
mCOLOR_BGR565 = 0x00800,
|
||||
mCOLOR_ARGB5 = 0x01000,
|
||||
mCOLOR_ABGR5 = 0x02000,
|
||||
mCOLOR_RGBA5 = 0x04000,
|
||||
mCOLOR_BGRA5 = 0x08000,
|
||||
mCOLOR_RGB8 = 0x10000,
|
||||
mCOLOR_BGR8 = 0x20000,
|
||||
mCOLOR_L8 = 0x40000,
|
||||
mCOLOR_PAL8 = 0x80000,
|
||||
|
||||
mCOLOR_ANY = -1
|
||||
};
|
||||
|
||||
#ifndef COLOR_16_BIT
|
||||
#define mCOLOR_NATIVE mCOLOR_XBGR8
|
||||
#elif !defined(COLOR_5_6_5)
|
||||
#define mCOLOR_NATIVE mCOLOR_BGR5
|
||||
#else
|
||||
#define mCOLOR_NATIVE mCOLOR_RGB565
|
||||
#endif
|
||||
|
||||
struct mImage {
|
||||
void* data;
|
||||
uint32_t* palette;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned stride;
|
||||
unsigned depth;
|
||||
unsigned palSize;
|
||||
enum mColorFormat format;
|
||||
};
|
||||
|
||||
struct mPainter {
|
||||
struct mImage* backing;
|
||||
bool blend;
|
||||
bool fill;
|
||||
unsigned strokeWidth;
|
||||
uint32_t strokeColor;
|
||||
uint32_t fillColor;
|
||||
};
|
||||
|
||||
struct VFile;
|
||||
struct mImage* mImageCreate(unsigned width, unsigned height, enum mColorFormat format);
|
||||
struct mImage* mImageCreateWithStride(unsigned width, unsigned height, unsigned stride, enum mColorFormat format);
|
||||
struct mImage* mImageCreateFromConstBuffer(unsigned width, unsigned height, unsigned stride, enum mColorFormat format, const void* pixels);
|
||||
struct mImage* mImageLoad(const char* path);
|
||||
struct mImage* mImageLoadVF(struct VFile* vf);
|
||||
struct mImage* mImageConvertToFormat(const struct mImage*, enum mColorFormat format);
|
||||
void mImageDestroy(struct mImage*);
|
||||
|
||||
bool mImageSave(const struct mImage*, const char* path, const char* format);
|
||||
bool mImageSaveVF(const struct mImage*, struct VFile* vf, const char* format);
|
||||
|
||||
uint32_t mImageGetPixel(const struct mImage* image, unsigned x, unsigned y);
|
||||
uint32_t mImageGetPixelRaw(const struct mImage* image, unsigned x, unsigned y);
|
||||
void mImageSetPixel(struct mImage* image, unsigned x, unsigned y, uint32_t color);
|
||||
void mImageSetPixelRaw(struct mImage* image, unsigned x, unsigned y, uint32_t color);
|
||||
|
||||
void mImageSetPaletteSize(struct mImage* image, unsigned count);
|
||||
void mImageSetPaletteEntry(struct mImage* image, unsigned index, uint32_t color);
|
||||
|
||||
void mImageBlit(struct mImage* image, const struct mImage* source, int x, int y);
|
||||
void mImageComposite(struct mImage* image, const struct mImage* source, int x, int y);
|
||||
void mImageCompositeWithAlpha(struct mImage* image, const struct mImage* source, int x, int y, float alpha);
|
||||
|
||||
void mPainterInit(struct mPainter*, struct mImage* backing);
|
||||
void mPainterDrawRectangle(struct mPainter*, int x, int y, int width, int height);
|
||||
void mPainterDrawLine(struct mPainter*, int x1, int y1, int x2, int y2);
|
||||
void mPainterDrawCircle(struct mPainter*, int x, int y, int diameter);
|
||||
|
||||
uint32_t mColorConvert(uint32_t color, enum mColorFormat from, enum mColorFormat to);
|
||||
uint32_t mImageColorConvert(uint32_t color, const struct mImage* from, enum mColorFormat to);
|
||||
|
||||
#ifndef PYCPARSE
|
||||
static inline unsigned mColorFormatBytes(enum mColorFormat format) {
|
||||
switch (format) {
|
||||
case mCOLOR_XBGR8:
|
||||
case mCOLOR_XRGB8:
|
||||
case mCOLOR_BGRX8:
|
||||
case mCOLOR_RGBX8:
|
||||
case mCOLOR_ABGR8:
|
||||
case mCOLOR_ARGB8:
|
||||
case mCOLOR_BGRA8:
|
||||
case mCOLOR_RGBA8:
|
||||
return 4;
|
||||
case mCOLOR_RGB5:
|
||||
case mCOLOR_BGR5:
|
||||
case mCOLOR_RGB565:
|
||||
case mCOLOR_BGR565:
|
||||
case mCOLOR_ARGB5:
|
||||
case mCOLOR_ABGR5:
|
||||
case mCOLOR_RGBA5:
|
||||
case mCOLOR_BGRA5:
|
||||
return 2;
|
||||
case mCOLOR_RGB8:
|
||||
case mCOLOR_BGR8:
|
||||
return 3;
|
||||
case mCOLOR_L8:
|
||||
case mCOLOR_PAL8:
|
||||
return 1;
|
||||
case mCOLOR_ANY:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool mColorFormatHasAlpha(enum mColorFormat format) {
|
||||
switch (format) {
|
||||
case mCOLOR_XBGR8:
|
||||
case mCOLOR_XRGB8:
|
||||
case mCOLOR_BGRX8:
|
||||
case mCOLOR_RGBX8:
|
||||
case mCOLOR_RGB5:
|
||||
case mCOLOR_BGR5:
|
||||
case mCOLOR_RGB565:
|
||||
case mCOLOR_BGR565:
|
||||
case mCOLOR_RGB8:
|
||||
case mCOLOR_BGR8:
|
||||
case mCOLOR_L8:
|
||||
return false;
|
||||
case mCOLOR_ABGR8:
|
||||
case mCOLOR_ARGB8:
|
||||
case mCOLOR_BGRA8:
|
||||
case mCOLOR_RGBA8:
|
||||
case mCOLOR_ARGB5:
|
||||
case mCOLOR_ABGR5:
|
||||
case mCOLOR_RGBA5:
|
||||
case mCOLOR_BGRA5:
|
||||
case mCOLOR_PAL8:
|
||||
return true;
|
||||
case mCOLOR_ANY:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline color_t mColorFrom555(uint16_t value) {
|
||||
#ifdef COLOR_16_BIT
|
||||
#ifdef COLOR_5_6_5
|
||||
color_t color = 0;
|
||||
color |= (value & 0x001F) << 11;
|
||||
color |= (value & 0x03E0) << 1;
|
||||
color |= (value & 0x7C00) >> 10;
|
||||
#else
|
||||
color_t color = value;
|
||||
#endif
|
||||
#else
|
||||
color_t color = M_RGB5_TO_BGR8(value);
|
||||
color |= (color >> 5) & 0x070707;
|
||||
#endif
|
||||
return color;
|
||||
}
|
||||
|
||||
ATTRIBUTE_UNUSED static unsigned mColorMix5Bit(int weightA, unsigned colorA, int weightB, unsigned colorB) {
|
||||
unsigned c = 0;
|
||||
unsigned a, b;
|
||||
#ifdef COLOR_16_BIT
|
||||
#ifdef COLOR_5_6_5
|
||||
a = colorA & 0xF81F;
|
||||
b = colorB & 0xF81F;
|
||||
a |= (colorA & 0x7C0) << 16;
|
||||
b |= (colorB & 0x7C0) << 16;
|
||||
c = ((a * weightA + b * weightB) / 16);
|
||||
if (c & 0x08000000) {
|
||||
c = (c & ~0x0FC00000) | 0x07C00000;
|
||||
}
|
||||
if (c & 0x0020) {
|
||||
c = (c & ~0x003F) | 0x001F;
|
||||
}
|
||||
if (c & 0x10000) {
|
||||
c = (c & ~0x1F800) | 0xF800;
|
||||
}
|
||||
c = (c & 0xF81F) | ((c >> 16) & 0x07C0);
|
||||
#else
|
||||
a = colorA & 0x7C1F;
|
||||
b = colorB & 0x7C1F;
|
||||
a |= (colorA & 0x3E0) << 16;
|
||||
b |= (colorB & 0x3E0) << 16;
|
||||
c = ((a * weightA + b * weightB) / 16);
|
||||
if (c & 0x04000000) {
|
||||
c = (c & ~0x07E00000) | 0x03E00000;
|
||||
}
|
||||
if (c & 0x0020) {
|
||||
c = (c & ~0x003F) | 0x001F;
|
||||
}
|
||||
if (c & 0x8000) {
|
||||
c = (c & ~0xF800) | 0x7C00;
|
||||
}
|
||||
c = (c & 0x7C1F) | ((c >> 16) & 0x03E0);
|
||||
#endif
|
||||
#else
|
||||
a = colorA & 0xFF;
|
||||
b = colorB & 0xFF;
|
||||
c |= ((a * weightA + b * weightB) / 16) & 0x1FF;
|
||||
if (c & 0x00000100) {
|
||||
c = 0x000000FF;
|
||||
}
|
||||
|
||||
a = colorA & 0xFF00;
|
||||
b = colorB & 0xFF00;
|
||||
c |= ((a * weightA + b * weightB) / 16) & 0x1FF00;
|
||||
if (c & 0x00010000) {
|
||||
c = (c & 0x000000FF) | 0x0000FF00;
|
||||
}
|
||||
|
||||
a = colorA & 0xFF0000;
|
||||
b = colorB & 0xFF0000;
|
||||
c |= ((a * weightA + b * weightB) / 16) & 0x1FF0000;
|
||||
if (c & 0x01000000) {
|
||||
c = (c & 0x0000FFFF) | 0x00FF0000;
|
||||
}
|
||||
#endif
|
||||
return c;
|
||||
}
|
||||
|
||||
ATTRIBUTE_UNUSED static uint32_t mColorMixARGB8(uint32_t colorA, uint32_t colorB) {
|
||||
uint32_t alphaA = colorA >> 24;
|
||||
if (!alphaA) {
|
||||
return colorB;
|
||||
}
|
||||
uint32_t alphaB = colorB >> 24;
|
||||
uint32_t color = 0;
|
||||
|
||||
#if 1
|
||||
// TODO: Benchmark integer and float versions
|
||||
uint32_t a, b;
|
||||
uint32_t alpha = (alphaA * 0xFF) + alphaB * (0xFF - alphaA);
|
||||
|
||||
a = colorA & 0xFF;
|
||||
a *= alphaA * 0xFF;
|
||||
b = a;
|
||||
|
||||
a = colorB & 0xFF;
|
||||
a *= alphaB * (0xFF - alphaA);
|
||||
b += a;
|
||||
|
||||
b /= alpha;
|
||||
if (b > 0xFF) {
|
||||
color |= 0xFF;
|
||||
} else {
|
||||
color |= b;
|
||||
}
|
||||
|
||||
a = (colorA >> 8) & 0xFF;
|
||||
a *= alphaA * 0xFF;
|
||||
b = a;
|
||||
|
||||
a = (colorB >> 8) & 0xFF;
|
||||
a *= alphaB * (0xFF - alphaA);
|
||||
b += a;
|
||||
|
||||
b /= alpha;
|
||||
if (b > 0xFF) {
|
||||
color |= 0xFF00;
|
||||
} else {
|
||||
color |= b << 8;
|
||||
}
|
||||
|
||||
a = (colorA >> 16) & 0xFF;
|
||||
a *= alphaA * 0xFF;
|
||||
b = a;
|
||||
|
||||
a = (colorB >> 16) & 0xFF;
|
||||
a *= alphaB * (0xFF - alphaA);
|
||||
b += a;
|
||||
|
||||
b /= alpha;
|
||||
if (b > 0xFF) {
|
||||
color |= 0xFF0000;
|
||||
} else {
|
||||
color |= b << 16;
|
||||
}
|
||||
|
||||
alpha /= 0xFF;
|
||||
if (alpha > 0xFF) {
|
||||
color |= 0xFF000000;
|
||||
} else {
|
||||
color |= alpha << 24;
|
||||
}
|
||||
#else
|
||||
float ca, aa;
|
||||
float cb, ab;
|
||||
|
||||
static const float r255 = 1 / 255.f;
|
||||
aa = alphaA * r255;
|
||||
ab = alphaB * r255;
|
||||
float alpha = aa + ab * (1.f - aa);
|
||||
float ralpha = 1.f / alpha;
|
||||
alpha = alpha * 255.f;
|
||||
color = ((int) alpha) << 24;
|
||||
|
||||
ca = ((colorA >> 16) & 0xFF) * r255;
|
||||
cb = ((colorB >> 16) & 0xFF) * r255;
|
||||
ca = ca * aa + cb * ab * (1.f - aa);
|
||||
ca = ca * ralpha * 255.f;
|
||||
if (ca > 255.f) {
|
||||
ca = 255.f;
|
||||
}
|
||||
color |= ((int) ca) << 16;
|
||||
|
||||
ca = ((colorA >> 8) & 0xFF) * r255;
|
||||
cb = ((colorB >> 8) & 0xFF) * r255;
|
||||
ca = ca * aa + cb * ab * (1.f - aa);
|
||||
ca = ca * ralpha * 255.f;
|
||||
if (ca > 255.f) {
|
||||
ca = 255.f;
|
||||
}
|
||||
color |= ((int) ca) << 8;
|
||||
|
||||
ca = (colorA & 0xFF) * r255;
|
||||
cb = (colorB & 0xFF) * r255;
|
||||
ca = ca * aa + cb * ab * (1.f - aa);
|
||||
ca = ca * ralpha * 255.f;
|
||||
if (ca > 255.f) {
|
||||
ca = 255.f;
|
||||
}
|
||||
color |= (int) ca;
|
||||
#endif
|
||||
|
||||
return color;
|
||||
}
|
||||
#endif
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -1,10 +1,10 @@
|
|||
/* Copyright (c) 2013-2015 Jeffrey Pfau
|
||||
/* Copyright (c) 2013-2023 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 EXPORT_H
|
||||
#define EXPORT_H
|
||||
#ifndef M_IMAGE_EXPORT_H
|
||||
#define M_IMAGE_EXPORT_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
|
@ -12,8 +12,8 @@ CXX_GUARD_START
|
|||
|
||||
struct VFile;
|
||||
|
||||
bool exportPaletteRIFF(struct VFile* vf, size_t entries, const uint16_t* colors);
|
||||
bool exportPaletteACT(struct VFile* vf, size_t entries, const uint16_t* colors);
|
||||
bool mPaletteExportRIFF(struct VFile* vf, size_t entries, const uint16_t* colors);
|
||||
bool mPaletteExportACT(struct VFile* vf, size_t entries, const uint16_t* colors);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
|
@ -12,6 +12,8 @@ CXX_GUARD_START
|
|||
|
||||
#ifdef USE_PNG
|
||||
|
||||
#include <mgba-util/image.h>
|
||||
|
||||
// png.h defines its own version of restrict which conflicts with mGBA's.
|
||||
#ifdef restrict
|
||||
#undef restrict
|
||||
|
@ -25,13 +27,10 @@ enum {
|
|||
};
|
||||
|
||||
png_structp PNGWriteOpen(struct VFile* source);
|
||||
png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height);
|
||||
png_infop PNGWriteHeaderA(png_structp png, unsigned width, unsigned height);
|
||||
png_infop PNGWriteHeader8(png_structp png, unsigned width, unsigned height);
|
||||
bool PNGWritePalette(png_structp png, png_infop info, const uint32_t* palette, unsigned entries);
|
||||
bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels);
|
||||
bool PNGWritePixelsA(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels);
|
||||
bool PNGWritePixels8(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels);
|
||||
png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height, enum mColorFormat);
|
||||
png_infop PNGWriteHeaderPalette(png_structp png, unsigned width, unsigned height, const uint32_t* palette, unsigned entries);
|
||||
bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels, enum mColorFormat);
|
||||
bool PNGWritePixelsPalette(png_structp png, unsigned width, unsigned height, unsigned stride, const void* pixels);
|
||||
bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* data);
|
||||
void PNGWriteClose(png_structp png, png_infop info);
|
||||
|
|
@ -16,13 +16,13 @@
|
|||
#define _mCALL_0(FN, ...)
|
||||
#define _mCALL_1(FN, A) FN(A)
|
||||
#define _mCALL_2(FN, A, B) FN(A), FN(B)
|
||||
#define _mCALL_3(FN, A, ...) FN(A), _mCALL_2(FN, __VA_ARGS__)
|
||||
#define _mCALL_4(FN, A, ...) FN(A), _mCALL_3(FN, __VA_ARGS__)
|
||||
#define _mCALL_5(FN, A, ...) FN(A), _mCALL_4(FN, __VA_ARGS__)
|
||||
#define _mCALL_6(FN, A, ...) FN(A), _mCALL_5(FN, __VA_ARGS__)
|
||||
#define _mCALL_7(FN, A, ...) FN(A), _mCALL_6(FN, __VA_ARGS__)
|
||||
#define _mCALL_8(FN, A, ...) FN(A), _mCALL_7(FN, __VA_ARGS__)
|
||||
#define _mCALL_9(FN, A, ...) FN(A), _mCALL_8(FN, __VA_ARGS__)
|
||||
#define _mCALL_3(FN, A, B, C) FN(A), FN(B), FN(C)
|
||||
#define _mCALL_4(FN, A, B, C, D) FN(A), FN(B), FN(C), FN(D)
|
||||
#define _mCALL_5(FN, A, B, C, D, E) FN(A), FN(B), FN(C), FN(D), FN(E)
|
||||
#define _mCALL_6(FN, A, B, C, D, E, F) FN(A), FN(B), FN(C), FN(D), FN(E), FN(F)
|
||||
#define _mCALL_7(FN, A, B, C, D, E, F, G) FN(A), FN(B), FN(C), FN(D), FN(E), FN(F), FN(G)
|
||||
#define _mCALL_8(FN, A, B, C, D, E, F, G, H) FN(A), FN(B), FN(C), FN(D), FN(E), FN(F), FN(G), FN(H)
|
||||
#define _mCALL_9(FN, A, B, C, D, E, F, G, H, I) FN(A), FN(B), FN(C), FN(D), FN(E), FN(F), FN(G), FN(H), FN(I)
|
||||
|
||||
#define _mCOMMA_0(N, ...) N
|
||||
#define _mCOMMA_1(N, ...) N, __VA_ARGS__
|
||||
|
@ -37,25 +37,25 @@
|
|||
|
||||
#define _mEVEN_0(...)
|
||||
#define _mEVEN_1(A, B, ...) A
|
||||
#define _mEVEN_2(A, B, ...) A, _mIDENT(_mEVEN_1(__VA_ARGS__))
|
||||
#define _mEVEN_3(A, B, ...) A, _mIDENT(_mEVEN_2(__VA_ARGS__))
|
||||
#define _mEVEN_4(A, B, ...) A, _mIDENT(_mEVEN_3(__VA_ARGS__))
|
||||
#define _mEVEN_5(A, B, ...) A, _mIDENT(_mEVEN_4(__VA_ARGS__))
|
||||
#define _mEVEN_2(A, B, C, D, ...) A, C
|
||||
#define _mEVEN_3(A, B, C, D, E, F, ...) A, C, E
|
||||
#define _mEVEN_4(A, B, C, D, E, F, G, H, ...) A, C, E, G
|
||||
#define _mEVEN_5(A, B, C, D, E, F, G, H, I, J, ...) A, C, E, G, I
|
||||
#define _mEVEN_6(A, B, ...) A, _mIDENT(_mEVEN_5(__VA_ARGS__))
|
||||
#define _mEVEN_7(A, B, ...) A, _mIDENT(_mEVEN_6(__VA_ARGS__))
|
||||
#define _mEVEN_8(A, B, ...) A, _mIDENT(_mEVEN_7(__VA_ARGS__))
|
||||
#define _mEVEN_9(A, B, ...) A, _mIDENT(_mEVEN_7(__VA_ARGS__))
|
||||
#define _mEVEN_7(A, B, C, D, ...) A, C, _mIDENT(_mEVEN_5(__VA_ARGS__))
|
||||
#define _mEVEN_8(A, B, C, D, E, F, ...) A, C, E, _mIDENT(_mEVEN_5(__VA_ARGS__))
|
||||
#define _mEVEN_9(A, B, C, D, E, F, G, H, ...) A, C, E, G, _mIDENT(_mEVEN_5(__VA_ARGS__))
|
||||
|
||||
#define _mODD_0(...)
|
||||
#define _mODD_1(A, B, ...) B
|
||||
#define _mODD_2(A, B, ...) B, _mIDENT(_mODD_1(__VA_ARGS__))
|
||||
#define _mODD_3(A, B, ...) B, _mIDENT(_mODD_2(__VA_ARGS__))
|
||||
#define _mODD_4(A, B, ...) B, _mIDENT(_mODD_3(__VA_ARGS__))
|
||||
#define _mODD_5(A, B, ...) B, _mIDENT(_mODD_4(__VA_ARGS__))
|
||||
#define _mODD_2(A, B, C, D, ...) B, D
|
||||
#define _mODD_3(A, B, C, D, E, F, ...) B, D, F
|
||||
#define _mODD_4(A, B, C, D, E, F, G, H, ...) B, D, F, H
|
||||
#define _mODD_5(A, B, C, D, E, F, G, H, I, J, ...) B, D, F, H, J
|
||||
#define _mODD_6(A, B, ...) B, _mIDENT(_mODD_5(__VA_ARGS__))
|
||||
#define _mODD_7(A, B, ...) B, _mIDENT(_mODD_6(__VA_ARGS__))
|
||||
#define _mODD_8(A, B, ...) B, _mIDENT(_mODD_7(__VA_ARGS__))
|
||||
#define _mODD_9(A, B, ...) B, _mIDENT(_mODD_7(__VA_ARGS__))
|
||||
#define _mODD_7(A, B, C, D, ...) B, D, _mIDENT(_mODD_5(__VA_ARGS__))
|
||||
#define _mODD_8(A, B, C, D, E, F, ...) B, D, F, _mIDENT(_mODD_5(__VA_ARGS__))
|
||||
#define _mODD_9(A, B, C, D, E, F, G, H, ...) B, D, F, H, _mIDENT(_mODD_5(__VA_ARGS__))
|
||||
|
||||
#define _mIF0_0(...) __VA_ARGS__
|
||||
#define _mIF0_1(...)
|
||||
|
|
|
@ -25,7 +25,7 @@ static inline unsigned clz32(uint32_t bits) {
|
|||
}
|
||||
return __builtin_clz(bits);
|
||||
#else
|
||||
static const int table[256] = {
|
||||
static const int8_t table[256] = {
|
||||
8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
|
@ -55,6 +55,43 @@ static inline unsigned clz32(uint32_t bits) {
|
|||
#endif
|
||||
}
|
||||
|
||||
static inline unsigned ctz32(uint32_t bits) {
|
||||
#if defined(__GNUC__) || __clang__
|
||||
if (!bits) {
|
||||
return 32;
|
||||
}
|
||||
return __builtin_ctz(bits);
|
||||
#else
|
||||
static const int8_t table[256] = {
|
||||
8, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
};
|
||||
|
||||
if (bits & 0x000000FF) {
|
||||
return table[bits & 0xFF];
|
||||
} else if (bits & 0x0000FF00) {
|
||||
return table[(bits >> 8) & 0xFF] + 8;
|
||||
} else if (bits & 0x00FF0000) {
|
||||
return table[(bits >> 16) & 0xFF] + 16;
|
||||
}
|
||||
return table[bits >> 24] + 24;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint32_t toPow2(uint32_t bits) {
|
||||
if (!bits) {
|
||||
return 0;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <malloc.h>
|
||||
|
||||
#define THREAD_ENTRY void
|
||||
#define THREAD_EXIT(RES) return
|
||||
typedef ThreadFunc ThreadEntry;
|
||||
|
||||
typedef LightLock Mutex;
|
||||
|
|
|
@ -20,6 +20,7 @@ CXX_GUARD_START
|
|||
|
||||
#define THREAD_ENTRY void*
|
||||
typedef THREAD_ENTRY (*ThreadEntry)(void*);
|
||||
#define THREAD_EXIT(RES) return RES
|
||||
|
||||
typedef pthread_t Thread;
|
||||
typedef pthread_mutex_t Mutex;
|
||||
|
|
|
@ -17,6 +17,7 @@ typedef struct {
|
|||
} Condition;
|
||||
#define THREAD_ENTRY int
|
||||
typedef THREAD_ENTRY (*ThreadEntry)(void*);
|
||||
#define THREAD_EXIT(RES) return RES
|
||||
|
||||
static inline int MutexInit(Mutex* mutex) {
|
||||
Mutex id = sceKernelCreateMutex("mutex", 0, 0, 0);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <switch.h>
|
||||
|
||||
#define THREAD_ENTRY void
|
||||
#define THREAD_EXIT(RES) return
|
||||
typedef ThreadFunc ThreadEntry;
|
||||
typedef CondVar Condition;
|
||||
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct option;
|
||||
|
||||
#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */
|
||||
|
||||
#ifdef REPLACE_GETOPT
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <windows.h>
|
||||
#define THREAD_ENTRY DWORD WINAPI
|
||||
typedef THREAD_ENTRY ThreadEntry(LPVOID);
|
||||
#define THREAD_EXIT(RES) return RES
|
||||
|
||||
typedef HANDLE Thread;
|
||||
typedef CRITICAL_SECTION Mutex;
|
||||
|
|
|
@ -204,6 +204,7 @@ static inline Socket SocketOpenTCP(int port, const struct Address* bindAddress)
|
|||
err = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
|
||||
#endif
|
||||
if (err) {
|
||||
SocketCloseQuiet(sock);
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
|
@ -433,9 +434,9 @@ static inline int SocketPoll(size_t nSockets, Socket* reads, Socket* writes, Soc
|
|||
#else
|
||||
int result = select(maxFd, &rset, &wset, &eset, timeoutMillis < 0 ? 0 : &tv);
|
||||
#endif
|
||||
int r = 0;
|
||||
int w = 0;
|
||||
int e = 0;
|
||||
size_t r = 0;
|
||||
size_t w = 0;
|
||||
size_t e = 0;
|
||||
Socket j;
|
||||
for (j = 0; j < maxFd; ++j) {
|
||||
if (reads && FD_ISSET(j, &rset)) {
|
||||
|
@ -451,6 +452,21 @@ static inline int SocketPoll(size_t nSockets, Socket* reads, Socket* writes, Soc
|
|||
++e;
|
||||
}
|
||||
}
|
||||
if (reads) {
|
||||
for (; r < nSockets; ++r) {
|
||||
reads[r] = INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
if (writes) {
|
||||
for (; w < nSockets; ++w) {
|
||||
writes[w] = INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
if (errors) {
|
||||
for (; e < nSockets; ++e) {
|
||||
errors[e] = INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -100,6 +100,9 @@ struct VFile* VFileFromFILE(FILE* file);
|
|||
|
||||
void separatePath(const char* path, char* dirname, char* basename, char* extension);
|
||||
|
||||
bool isAbsolute(const char* path);
|
||||
void makeAbsolute(const char* path, const char* base, char* out);
|
||||
|
||||
struct VFile* VDirFindFirst(struct VDir* dir, bool (*filter)(struct VFile*));
|
||||
struct VFile* VDirFindNextAvailable(struct VDir*, const char* basename, const char* infix, const char* suffix, int mode);
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ struct mCoreOptions {
|
|||
int frameskip;
|
||||
bool rewindEnable;
|
||||
int rewindBufferCapacity;
|
||||
int rewindBufferInterval;
|
||||
float fpsTarget;
|
||||
size_t audioBuffers;
|
||||
unsigned sampleRate;
|
||||
|
|
|
@ -65,8 +65,13 @@ struct mCore {
|
|||
void (*setSync)(struct mCore*, struct mCoreSync*);
|
||||
void (*loadConfig)(struct mCore*, const struct mCoreConfig*);
|
||||
void (*reloadConfigOption)(struct mCore*, const char* option, const struct mCoreConfig*);
|
||||
void (*setOverride)(struct mCore*, const void* override);
|
||||
|
||||
void (*baseVideoSize)(const struct mCore*, unsigned* width, unsigned* height);
|
||||
void (*currentVideoSize)(const struct mCore*, unsigned* width, unsigned* height);
|
||||
unsigned (*videoScale)(const struct mCore*);
|
||||
size_t (*screenRegions)(const struct mCore*, const struct mCoreScreenRegion**);
|
||||
|
||||
void (*desiredVideoDimensions)(const struct mCore*, unsigned* width, unsigned* height);
|
||||
void (*setVideoBuffer)(struct mCore*, color_t* buffer, size_t stride);
|
||||
void (*setVideoGLTex)(struct mCore*, unsigned texid);
|
||||
|
||||
|
@ -116,6 +121,7 @@ struct mCore {
|
|||
void (*getGameCode)(const struct mCore*, char* title);
|
||||
|
||||
void (*setPeripheral)(struct mCore*, int type, void*);
|
||||
void* (*getPeripheral)(struct mCore*, int type);
|
||||
|
||||
uint32_t (*busRead8)(struct mCore*, uint32_t address);
|
||||
uint32_t (*busRead16)(struct mCore*, uint32_t address);
|
||||
|
|
|
@ -10,165 +10,14 @@
|
|||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba-util/image.h>
|
||||
#include <mgba-util/vector.h>
|
||||
|
||||
struct mCore;
|
||||
struct mStateExtdataItem;
|
||||
|
||||
#ifdef COLOR_16_BIT
|
||||
typedef uint16_t color_t;
|
||||
#define BYTES_PER_PIXEL 2
|
||||
#else
|
||||
typedef uint32_t color_t;
|
||||
#define BYTES_PER_PIXEL 4
|
||||
#endif
|
||||
|
||||
#define M_R5(X) ((X) & 0x1F)
|
||||
#define M_G5(X) (((X) >> 5) & 0x1F)
|
||||
#define M_B5(X) (((X) >> 10) & 0x1F)
|
||||
|
||||
#define M_R8(X) (((((X) << 3) & 0xF8) * 0x21) >> 5)
|
||||
#define M_G8(X) (((((X) >> 2) & 0xF8) * 0x21) >> 5)
|
||||
#define M_B8(X) (((((X) >> 7) & 0xF8) * 0x21) >> 5)
|
||||
|
||||
#define M_RGB5_TO_BGR8(X) ((M_R5(X) << 3) | (M_G5(X) << 11) | (M_B5(X) << 19))
|
||||
#define M_RGB5_TO_RGB8(X) ((M_R5(X) << 19) | (M_G5(X) << 11) | (M_B5(X) << 3))
|
||||
#define M_RGB8_TO_BGR5(X) ((((X) & 0xF8) >> 3) | (((X) & 0xF800) >> 6) | (((X) & 0xF80000) >> 9))
|
||||
#define M_RGB8_TO_RGB5(X) ((((X) & 0xF8) << 7) | (((X) & 0xF800) >> 6) | (((X) & 0xF80000) >> 19))
|
||||
|
||||
#ifndef COLOR_16_BIT
|
||||
#define M_COLOR_RED 0x000000FF
|
||||
#define M_COLOR_GREEN 0x0000FF00
|
||||
#define M_COLOR_BLUE 0x00FF0000
|
||||
#define M_COLOR_ALPHA 0xFF000000
|
||||
#define M_COLOR_WHITE 0x00FFFFFF
|
||||
|
||||
#define M_RGB8_TO_NATIVE(X) (((X) & 0x00FF00) | (((X) & 0x0000FF) << 16) | (((X) & 0xFF0000) >> 16))
|
||||
#elif defined(COLOR_5_6_5)
|
||||
#define M_COLOR_RED 0x001F
|
||||
#define M_COLOR_GREEN 0x07E0
|
||||
#define M_COLOR_BLUE 0xF800
|
||||
#define M_COLOR_ALPHA 0x0000
|
||||
#define M_COLOR_WHITE 0xFFDF
|
||||
|
||||
#define M_RGB8_TO_NATIVE(X) ((((X) & 0xF8) << 8) | (((X) & 0xFC00) >> 5) | (((X) & 0xF80000) >> 19))
|
||||
#else
|
||||
#define M_COLOR_RED 0x001F
|
||||
#define M_COLOR_GREEN 0x03E0
|
||||
#define M_COLOR_BLUE 0x7C00
|
||||
#define M_COLOR_ALPHA 0x1000
|
||||
#define M_COLOR_WHITE 0x7FFF
|
||||
|
||||
#define M_RGB8_TO_NATIVE(X) M_RGB8_TO_BGR5(X)
|
||||
#endif
|
||||
|
||||
#ifndef PYCPARSE
|
||||
static inline color_t mColorFrom555(uint16_t value) {
|
||||
#ifdef COLOR_16_BIT
|
||||
#ifdef COLOR_5_6_5
|
||||
color_t color = 0;
|
||||
color |= (value & 0x001F) << 11;
|
||||
color |= (value & 0x03E0) << 1;
|
||||
color |= (value & 0x7C00) >> 10;
|
||||
#else
|
||||
color_t color = value;
|
||||
#endif
|
||||
#else
|
||||
color_t color = M_RGB5_TO_BGR8(value);
|
||||
color |= (color >> 5) & 0x070707;
|
||||
#endif
|
||||
return color;
|
||||
}
|
||||
|
||||
ATTRIBUTE_UNUSED static unsigned mColorMix5Bit(int weightA, unsigned colorA, int weightB, unsigned colorB) {
|
||||
unsigned c = 0;
|
||||
unsigned a, b;
|
||||
#ifdef COLOR_16_BIT
|
||||
#ifdef COLOR_5_6_5
|
||||
a = colorA & 0xF81F;
|
||||
b = colorB & 0xF81F;
|
||||
a |= (colorA & 0x7C0) << 16;
|
||||
b |= (colorB & 0x7C0) << 16;
|
||||
c = ((a * weightA + b * weightB) / 16);
|
||||
if (c & 0x08000000) {
|
||||
c = (c & ~0x0FC00000) | 0x07C00000;
|
||||
}
|
||||
if (c & 0x0020) {
|
||||
c = (c & ~0x003F) | 0x001F;
|
||||
}
|
||||
if (c & 0x10000) {
|
||||
c = (c & ~0x1F800) | 0xF800;
|
||||
}
|
||||
c = (c & 0xF81F) | ((c >> 16) & 0x07C0);
|
||||
#else
|
||||
a = colorA & 0x7C1F;
|
||||
b = colorB & 0x7C1F;
|
||||
a |= (colorA & 0x3E0) << 16;
|
||||
b |= (colorB & 0x3E0) << 16;
|
||||
c = ((a * weightA + b * weightB) / 16);
|
||||
if (c & 0x04000000) {
|
||||
c = (c & ~0x07E00000) | 0x03E00000;
|
||||
}
|
||||
if (c & 0x0020) {
|
||||
c = (c & ~0x003F) | 0x001F;
|
||||
}
|
||||
if (c & 0x8000) {
|
||||
c = (c & ~0xF800) | 0x7C00;
|
||||
}
|
||||
c = (c & 0x7C1F) | ((c >> 16) & 0x03E0);
|
||||
#endif
|
||||
#else
|
||||
a = colorA & 0xFF;
|
||||
b = colorB & 0xFF;
|
||||
c |= ((a * weightA + b * weightB) / 16) & 0x1FF;
|
||||
if (c & 0x00000100) {
|
||||
c = 0x000000FF;
|
||||
}
|
||||
|
||||
a = colorA & 0xFF00;
|
||||
b = colorB & 0xFF00;
|
||||
c |= ((a * weightA + b * weightB) / 16) & 0x1FF00;
|
||||
if (c & 0x00010000) {
|
||||
c = (c & 0x000000FF) | 0x0000FF00;
|
||||
}
|
||||
|
||||
a = colorA & 0xFF0000;
|
||||
b = colorB & 0xFF0000;
|
||||
c |= ((a * weightA + b * weightB) / 16) & 0x1FF0000;
|
||||
if (c & 0x01000000) {
|
||||
c = (c & 0x0000FFFF) | 0x00FF0000;
|
||||
}
|
||||
#endif
|
||||
return c;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct blip_t;
|
||||
|
||||
enum mColorFormat {
|
||||
mCOLOR_XBGR8 = 0x00001,
|
||||
mCOLOR_XRGB8 = 0x00002,
|
||||
mCOLOR_BGRX8 = 0x00004,
|
||||
mCOLOR_RGBX8 = 0x00008,
|
||||
mCOLOR_ABGR8 = 0x00010,
|
||||
mCOLOR_ARGB8 = 0x00020,
|
||||
mCOLOR_BGRA8 = 0x00040,
|
||||
mCOLOR_RGBA8 = 0x00080,
|
||||
mCOLOR_RGB5 = 0x00100,
|
||||
mCOLOR_BGR5 = 0x00200,
|
||||
mCOLOR_RGB565 = 0x00400,
|
||||
mCOLOR_BGR565 = 0x00800,
|
||||
mCOLOR_ARGB5 = 0x01000,
|
||||
mCOLOR_ABGR5 = 0x02000,
|
||||
mCOLOR_RGBA5 = 0x04000,
|
||||
mCOLOR_BGRA5 = 0x08000,
|
||||
mCOLOR_RGB8 = 0x10000,
|
||||
mCOLOR_BGR8 = 0x20000,
|
||||
mCOLOR_L8 = 0x40000,
|
||||
|
||||
mCOLOR_ANY = -1
|
||||
};
|
||||
|
||||
enum mCoreFeature {
|
||||
mCORE_FEATURE_OPENGL = 1,
|
||||
};
|
||||
|
@ -293,6 +142,15 @@ struct mCoreMemoryBlock {
|
|||
uint32_t segmentStart;
|
||||
};
|
||||
|
||||
struct mCoreScreenRegion {
|
||||
size_t id;
|
||||
const char* description;
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
int16_t w;
|
||||
int16_t h;
|
||||
};
|
||||
|
||||
enum mCoreRegisterType {
|
||||
mCORE_REGISTER_GPR = 0,
|
||||
mCORE_REGISTER_FPR,
|
||||
|
|
|
@ -24,6 +24,7 @@ struct mCoreRewindContext {
|
|||
size_t size;
|
||||
struct VFile* previousState;
|
||||
struct VFile* currentState;
|
||||
int rewindFrameCounter;
|
||||
|
||||
#ifndef DISABLE_THREADING
|
||||
bool onThread;
|
||||
|
|
|
@ -12,6 +12,7 @@ CXX_GUARD_START
|
|||
|
||||
#include <mgba/core/cpu.h>
|
||||
#include <mgba/core/log.h>
|
||||
#include <mgba-util/table.h>
|
||||
#include <mgba-util/vector.h>
|
||||
#include <mgba/internal/debugger/stack-trace.h>
|
||||
|
||||
|
@ -28,6 +29,7 @@ enum mDebuggerType {
|
|||
};
|
||||
|
||||
enum mDebuggerState {
|
||||
DEBUGGER_CREATED = 0,
|
||||
DEBUGGER_PAUSED,
|
||||
DEBUGGER_RUNNING,
|
||||
DEBUGGER_CALLBACK,
|
||||
|
@ -56,8 +58,10 @@ enum mDebuggerEntryReason {
|
|||
DEBUGGER_ENTER_STACK
|
||||
};
|
||||
|
||||
struct mDebuggerModule;
|
||||
struct mDebuggerEntryInfo {
|
||||
uint32_t address;
|
||||
int segment;
|
||||
union {
|
||||
struct {
|
||||
uint32_t oldValue;
|
||||
|
@ -76,6 +80,7 @@ struct mDebuggerEntryInfo {
|
|||
} st;
|
||||
} type;
|
||||
ssize_t pointId;
|
||||
struct mDebuggerModule* target;
|
||||
};
|
||||
|
||||
struct mBreakpoint {
|
||||
|
@ -97,6 +102,7 @@ struct mWatchpoint {
|
|||
|
||||
DECLARE_VECTOR(mBreakpointList, struct mBreakpoint);
|
||||
DECLARE_VECTOR(mWatchpointList, struct mWatchpoint);
|
||||
DECLARE_VECTOR(mDebuggerModuleList, struct mDebuggerModule*);
|
||||
|
||||
struct mDebugger;
|
||||
struct ParseTree;
|
||||
|
@ -111,11 +117,11 @@ struct mDebuggerPlatform {
|
|||
void (*checkBreakpoints)(struct mDebuggerPlatform*);
|
||||
bool (*clearBreakpoint)(struct mDebuggerPlatform*, ssize_t id);
|
||||
|
||||
ssize_t (*setBreakpoint)(struct mDebuggerPlatform*, const struct mBreakpoint*);
|
||||
void (*listBreakpoints)(struct mDebuggerPlatform*, struct mBreakpointList*);
|
||||
ssize_t (*setBreakpoint)(struct mDebuggerPlatform*, struct mDebuggerModule*, const struct mBreakpoint*);
|
||||
void (*listBreakpoints)(struct mDebuggerPlatform*, struct mDebuggerModule*, struct mBreakpointList*);
|
||||
|
||||
ssize_t (*setWatchpoint)(struct mDebuggerPlatform*, const struct mWatchpoint*);
|
||||
void (*listWatchpoints)(struct mDebuggerPlatform*, struct mWatchpointList*);
|
||||
ssize_t (*setWatchpoint)(struct mDebuggerPlatform*, struct mDebuggerModule*, const struct mWatchpoint*);
|
||||
void (*listWatchpoints)(struct mDebuggerPlatform*, struct mDebuggerModule*, struct mWatchpointList*);
|
||||
|
||||
void (*trace)(struct mDebuggerPlatform*, char* out, size_t* length);
|
||||
|
||||
|
@ -130,28 +136,52 @@ struct mDebugger {
|
|||
struct mCPUComponent d;
|
||||
struct mDebuggerPlatform* platform;
|
||||
enum mDebuggerState state;
|
||||
enum mDebuggerType type;
|
||||
struct mCore* core;
|
||||
struct mScriptBridge* bridge;
|
||||
struct mStackTrace stackTrace;
|
||||
|
||||
void (*init)(struct mDebugger*);
|
||||
void (*deinit)(struct mDebugger*);
|
||||
|
||||
void (*paused)(struct mDebugger*);
|
||||
void (*update)(struct mDebugger*);
|
||||
void (*entered)(struct mDebugger*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*);
|
||||
void (*custom)(struct mDebugger*);
|
||||
|
||||
void (*interrupt)(struct mDebugger*);
|
||||
struct mDebuggerModuleList modules;
|
||||
struct Table pointOwner;
|
||||
};
|
||||
|
||||
struct mDebugger* mDebuggerCreate(enum mDebuggerType type, struct mCore*);
|
||||
struct mDebuggerModule {
|
||||
struct mDebugger* p;
|
||||
enum mDebuggerType type;
|
||||
bool isPaused;
|
||||
bool needsCallback;
|
||||
|
||||
void (*init)(struct mDebuggerModule*);
|
||||
void (*deinit)(struct mDebuggerModule*);
|
||||
|
||||
void (*paused)(struct mDebuggerModule*, int32_t timeoutMs);
|
||||
void (*update)(struct mDebuggerModule*);
|
||||
void (*entered)(struct mDebuggerModule*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*);
|
||||
void (*custom)(struct mDebuggerModule*);
|
||||
|
||||
void (*interrupt)(struct mDebuggerModule*);
|
||||
};
|
||||
|
||||
void mDebuggerInit(struct mDebugger*);
|
||||
void mDebuggerDeinit(struct mDebugger*);
|
||||
|
||||
void mDebuggerAttach(struct mDebugger*, struct mCore*);
|
||||
void mDebuggerAttachModule(struct mDebugger*, struct mDebuggerModule*);
|
||||
void mDebuggerDetachModule(struct mDebugger*, struct mDebuggerModule*);
|
||||
void mDebuggerRunTimeout(struct mDebugger* debugger, int32_t timeoutMs);
|
||||
void mDebuggerRun(struct mDebugger*);
|
||||
void mDebuggerRunFrame(struct mDebugger*);
|
||||
void mDebuggerEnter(struct mDebugger*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*);
|
||||
|
||||
void mDebuggerInterrupt(struct mDebugger*);
|
||||
void mDebuggerUpdatePaused(struct mDebugger*);
|
||||
void mDebuggerShutdown(struct mDebugger*);
|
||||
void mDebuggerUpdate(struct mDebugger*);
|
||||
|
||||
bool mDebuggerIsShutdown(const struct mDebugger*);
|
||||
|
||||
struct mDebuggerModule* mDebuggerCreateModule(enum mDebuggerType type, struct mCore*);
|
||||
void mDebuggerModuleSetNeedsCallback(struct mDebuggerModule*);
|
||||
|
||||
bool mDebuggerLookupIdentifier(struct mDebugger* debugger, const char* name, int32_t* value, int* segment);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
|
|
@ -25,8 +25,9 @@ struct mArguments {
|
|||
|
||||
struct Table configOverrides;
|
||||
|
||||
enum mDebuggerType debuggerType;
|
||||
bool debugAtStart;
|
||||
bool debugCli;
|
||||
bool debugGdb;
|
||||
bool showHelp;
|
||||
bool showVersion;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
/* Copyright (c) 2013-2023 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 PROXY_BACKEND_H
|
||||
#define PROXY_BACKEND_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba-util/ring-fifo.h>
|
||||
#include <mgba-util/threading.h>
|
||||
#include <mgba/feature/video-backend.h>
|
||||
|
||||
enum mVideoBackendCommandType {
|
||||
mVB_CMD_DUMMY = 0,
|
||||
mVB_CMD_INIT,
|
||||
mVB_CMD_DEINIT,
|
||||
mVB_CMD_SET_LAYER_DIMENSIONS,
|
||||
mVB_CMD_LAYER_DIMENSIONS,
|
||||
mVB_CMD_SWAP,
|
||||
mVB_CMD_CLEAR,
|
||||
mVB_CMD_CONTEXT_RESIZED,
|
||||
mVB_CMD_SET_IMAGE_SIZE,
|
||||
mVB_CMD_IMAGE_SIZE,
|
||||
mVB_CMD_SET_IMAGE,
|
||||
mVB_CMD_DRAW_FRAME,
|
||||
};
|
||||
|
||||
union mVideoBackendCommandData {
|
||||
struct mRectangle dims;
|
||||
struct {
|
||||
int width;
|
||||
int height;
|
||||
} s;
|
||||
struct {
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
} u;
|
||||
const void* image;
|
||||
};
|
||||
|
||||
struct mVideoBackendCommand {
|
||||
enum mVideoBackendCommandType cmd;
|
||||
|
||||
union {
|
||||
WHandle handle;
|
||||
enum VideoLayer layer;
|
||||
};
|
||||
union mVideoBackendCommandData data;
|
||||
};
|
||||
|
||||
struct mVideoProxyBackend {
|
||||
struct VideoBackend d;
|
||||
struct VideoBackend* backend;
|
||||
|
||||
struct RingFIFO in;
|
||||
struct RingFIFO out;
|
||||
|
||||
Mutex inLock;
|
||||
Mutex outLock;
|
||||
Condition inWait;
|
||||
Condition outWait;
|
||||
|
||||
void (*wakeupCb)(struct mVideoProxyBackend*, void* context);
|
||||
void* context;
|
||||
};
|
||||
|
||||
void mVideoProxyBackendInit(struct mVideoProxyBackend* proxy, struct VideoBackend* backend);
|
||||
void mVideoProxyBackendDeinit(struct mVideoProxyBackend* proxy);
|
||||
|
||||
void mVideoProxyBackendSubmit(struct mVideoProxyBackend* proxy, const struct mVideoBackendCommand* cmd, union mVideoBackendCommandData* out);
|
||||
bool mVideoProxyBackendRun(struct mVideoProxyBackend* proxy, bool block);
|
||||
|
||||
bool mVideoProxyBackendCommandIsBlocking(enum mVideoBackendCommandType);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -0,0 +1,80 @@
|
|||
/* Copyright (c) 2013-2015 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef VIDEO_BACKEND_H
|
||||
#define VIDEO_BACKEND_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba-util/geometry.h>
|
||||
#include <mgba/core/log.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
typedef HWND WHandle;
|
||||
#else
|
||||
typedef void* WHandle;
|
||||
#endif
|
||||
|
||||
mLOG_DECLARE_CATEGORY(VIDEO);
|
||||
|
||||
enum VideoLayer {
|
||||
VIDEO_LAYER_BACKGROUND = 0,
|
||||
VIDEO_LAYER_BEZEL,
|
||||
VIDEO_LAYER_IMAGE,
|
||||
VIDEO_LAYER_OVERLAY0,
|
||||
VIDEO_LAYER_OVERLAY1,
|
||||
VIDEO_LAYER_OVERLAY2,
|
||||
VIDEO_LAYER_OVERLAY3,
|
||||
VIDEO_LAYER_OVERLAY4,
|
||||
VIDEO_LAYER_OVERLAY5,
|
||||
VIDEO_LAYER_OVERLAY6,
|
||||
VIDEO_LAYER_OVERLAY7,
|
||||
VIDEO_LAYER_OVERLAY8,
|
||||
VIDEO_LAYER_OVERLAY9,
|
||||
VIDEO_LAYER_MAX
|
||||
};
|
||||
|
||||
#define VIDEO_LAYER_OVERLAY_COUNT VIDEO_LAYER_MAX - VIDEO_LAYER_OVERLAY0
|
||||
|
||||
struct VideoBackend {
|
||||
void (*init)(struct VideoBackend*, WHandle handle);
|
||||
void (*deinit)(struct VideoBackend*);
|
||||
void (*setLayerDimensions)(struct VideoBackend*, enum VideoLayer, const struct mRectangle*);
|
||||
void (*layerDimensions)(const struct VideoBackend*, enum VideoLayer, struct mRectangle*);
|
||||
void (*swap)(struct VideoBackend*);
|
||||
void (*clear)(struct VideoBackend*);
|
||||
void (*contextResized)(struct VideoBackend*, unsigned w, unsigned h);
|
||||
void (*setImageSize)(struct VideoBackend*, enum VideoLayer, int w, int h);
|
||||
void (*imageSize)(struct VideoBackend*, enum VideoLayer, int* w, int* h);
|
||||
void (*setImage)(struct VideoBackend*, enum VideoLayer, const void* frame);
|
||||
void (*drawFrame)(struct VideoBackend*);
|
||||
|
||||
void* user;
|
||||
|
||||
bool filter;
|
||||
bool lockAspectRatio;
|
||||
bool lockIntegerScaling;
|
||||
bool interframeBlending;
|
||||
enum VideoLayer cropToLayer;
|
||||
};
|
||||
|
||||
struct VideoShader {
|
||||
const char* name;
|
||||
const char* author;
|
||||
const char* description;
|
||||
void* preprocessShader;
|
||||
void* passes;
|
||||
size_t nPasses;
|
||||
};
|
||||
|
||||
void VideoBackendGetFrame(const struct VideoBackend*, struct mRectangle* frame);
|
||||
void VideoBackendGetFrameSize(const struct VideoBackend*, unsigned* width, unsigned* height);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -71,6 +71,7 @@ struct VFile;
|
|||
|
||||
bool GBIsROM(struct VFile* vf);
|
||||
bool GBIsBIOS(struct VFile* vf);
|
||||
bool GBIsCompatibleBIOS(struct VFile* vf, enum GBModel model);
|
||||
|
||||
enum GBModel GBNameToModel(const char*);
|
||||
const char* GBModelToName(enum GBModel);
|
||||
|
|
|
@ -45,7 +45,7 @@ struct ARMDebugger {
|
|||
};
|
||||
|
||||
struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void);
|
||||
ssize_t ARMDebuggerSetSoftwareBreakpoint(struct mDebuggerPlatform* debugger, uint32_t address, enum ExecutionMode mode);
|
||||
ssize_t ARMDebuggerSetSoftwareBreakpoint(struct mDebuggerPlatform* debugger, struct mDebuggerModule* owner, uint32_t address, enum ExecutionMode mode);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
|
|
|
@ -73,6 +73,7 @@ struct CLIDebuggerBackend {
|
|||
|
||||
ATTRIBUTE_FORMAT(printf, 2, 3)
|
||||
void (*printf)(struct CLIDebuggerBackend*, const char* fmt, ...);
|
||||
int (*poll)(struct CLIDebuggerBackend*, int32_t timeoutMs);
|
||||
const char* (*readline)(struct CLIDebuggerBackend*, size_t* len);
|
||||
void (*lineAppend)(struct CLIDebuggerBackend*, const char* line);
|
||||
const char* (*historyLast)(struct CLIDebuggerBackend*, size_t* len);
|
||||
|
@ -81,7 +82,7 @@ struct CLIDebuggerBackend {
|
|||
};
|
||||
|
||||
struct CLIDebugger {
|
||||
struct mDebugger d;
|
||||
struct mDebuggerModule d;
|
||||
|
||||
struct CLIDebuggerSystem* system;
|
||||
struct CLIDebuggerBackend* backend;
|
||||
|
|
|
@ -14,7 +14,7 @@ CXX_GUARD_START
|
|||
|
||||
#include <mgba-util/socket.h>
|
||||
|
||||
#define GDB_STUB_MAX_LINE 1200
|
||||
#define GDB_STUB_MAX_LINE 1400
|
||||
#define GDB_STUB_INTERVAL 32
|
||||
|
||||
enum GDBStubAckState {
|
||||
|
@ -31,7 +31,7 @@ enum GDBWatchpointsBehvaior {
|
|||
};
|
||||
|
||||
struct GDBStub {
|
||||
struct mDebugger d;
|
||||
struct mDebuggerModule d;
|
||||
|
||||
char line[GDB_STUB_MAX_LINE];
|
||||
char outgoing[GDB_STUB_MAX_LINE];
|
||||
|
@ -41,7 +41,6 @@ struct GDBStub {
|
|||
Socket socket;
|
||||
Socket connection;
|
||||
|
||||
bool shouldBlock;
|
||||
int untilPoll;
|
||||
|
||||
bool supportsSwbreak;
|
||||
|
@ -56,7 +55,7 @@ bool GDBStubListen(struct GDBStub*, int port, const struct Address* bindAddress,
|
|||
void GDBStubHangup(struct GDBStub*);
|
||||
void GDBStubShutdown(struct GDBStub*);
|
||||
|
||||
void GDBStubUpdate(struct GDBStub*);
|
||||
bool GDBStubUpdate(struct GDBStub*, int timeoutMs);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
|
|
|
@ -292,7 +292,7 @@ struct GBMemory {
|
|||
int currentSramBank1;
|
||||
uint8_t* sramBank1;
|
||||
|
||||
unsigned cartBusDecay;
|
||||
int cartBusDecay;
|
||||
uint16_t cartBusPc;
|
||||
uint8_t cartBus;
|
||||
|
||||
|
|
|
@ -419,6 +419,9 @@ struct GBSerializedState {
|
|||
uint8_t locked;
|
||||
uint8_t bank0;
|
||||
} mmm01;
|
||||
struct {
|
||||
uint8_t registersActive;
|
||||
} pocketCam;
|
||||
struct {
|
||||
uint64_t lastLatch;
|
||||
uint8_t reg;
|
||||
|
@ -484,6 +487,7 @@ struct GBSerializedState {
|
|||
|
||||
union {
|
||||
uint8_t huc3Registers[0x80];
|
||||
uint8_t pocketCamRegisters[0x36];
|
||||
struct {
|
||||
uint8_t registers[4];
|
||||
uint8_t reserved[4];
|
||||
|
|
|
@ -22,6 +22,9 @@ enum {
|
|||
GB_VIDEO_VBLANK_PIXELS = 10,
|
||||
GB_VIDEO_VERTICAL_TOTAL_PIXELS = 154,
|
||||
|
||||
SGB_VIDEO_HORIZONTAL_PIXELS = 256,
|
||||
SGB_VIDEO_VERTICAL_PIXELS = 224,
|
||||
|
||||
// TODO: Figure out exact lengths
|
||||
GB_VIDEO_MODE_2_LENGTH = 80,
|
||||
GB_VIDEO_MODE_3_LENGTH_BASE = 172,
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
/* Copyright (c) 2013-2023 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 M_SCRIPT_H
|
||||
#define M_SCRIPT_H
|
||||
|
||||
#include <mgba/script/base.h>
|
||||
#include <mgba/script/canvas.h>
|
||||
#include <mgba/script/context.h>
|
||||
#include <mgba/script/input.h>
|
||||
#include <mgba/script/macros.h>
|
||||
#include <mgba/script/storage.h>
|
||||
#include <mgba/script/types.h>
|
||||
|
||||
#endif
|
|
@ -0,0 +1,25 @@
|
|||
/* Copyright (c) 2013-2023 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 M_SCRIPT_BASE_H
|
||||
#define M_SCRIPT_BASE_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba/script/macros.h>
|
||||
#include <mgba-util/image.h>
|
||||
|
||||
mSCRIPT_DECLARE_STRUCT(mImage)
|
||||
|
||||
struct mScriptContext;
|
||||
void mScriptContextAttachImage(struct mScriptContext* context);
|
||||
void mScriptContextAttachStdlib(struct mScriptContext* context);
|
||||
void mScriptContextAttachSocket(struct mScriptContext* context);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -0,0 +1,24 @@
|
|||
/* Copyright (c) 2013-2023 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 M_SCRIPT_CANVAS_H
|
||||
#define M_SCRIPT_CANVAS_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba/script/context.h>
|
||||
#include <mgba/script/macros.h>
|
||||
|
||||
struct VideoBackend;
|
||||
void mScriptContextAttachCanvas(struct mScriptContext* context);
|
||||
void mScriptCanvasUpdate(struct mScriptContext* context);
|
||||
void mScriptCanvasUpdateBackend(struct mScriptContext* context, struct VideoBackend*);
|
||||
void mScriptCanvasSetInternalScale(struct mScriptContext* context, unsigned scale);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -91,13 +91,12 @@ struct mScriptValue* mScriptContextAccessWeakref(struct mScriptContext*, struct
|
|||
void mScriptContextClearWeakref(struct mScriptContext*, uint32_t weakref);
|
||||
void mScriptContextDisownWeakref(struct mScriptContext*, uint32_t weakref);
|
||||
|
||||
void mScriptContextAttachStdlib(struct mScriptContext* context);
|
||||
void mScriptContextAttachSocket(struct mScriptContext* context);
|
||||
void mScriptContextExportConstants(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* constants);
|
||||
void mScriptContextExportNamespace(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* value);
|
||||
|
||||
void mScriptContextTriggerCallback(struct mScriptContext*, const char* callback, struct mScriptList* args);
|
||||
uint32_t mScriptContextAddCallback(struct mScriptContext*, const char* callback, struct mScriptValue* value);
|
||||
uint32_t mScriptContextAddOneshot(struct mScriptContext*, const char* callback, struct mScriptValue* value);
|
||||
void mScriptContextRemoveCallback(struct mScriptContext*, uint32_t cbid);
|
||||
|
||||
void mScriptContextSetDocstring(struct mScriptContext*, const char* key, const char* docstring);
|
||||
|
|
|
@ -249,6 +249,7 @@ mSCRIPT_DECLARE_STRUCT(mScriptGamepad);
|
|||
void mScriptContextAttachInput(struct mScriptContext* context);
|
||||
|
||||
void mScriptContextFireEvent(struct mScriptContext*, struct mScriptEvent*);
|
||||
void mScriptContextClearKeys(struct mScriptContext*);
|
||||
|
||||
int mScriptContextGamepadAttach(struct mScriptContext*, struct mScriptGamepad*);
|
||||
bool mScriptContextGamepadDetach(struct mScriptContext*, int pad);
|
||||
|
|
|
@ -212,20 +212,30 @@ CXX_GUARD_START
|
|||
} \
|
||||
},
|
||||
|
||||
#define mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(STRUCT, TYPE, EXPORTED_NAME, NAME) { \
|
||||
#define _mSCRIPT_DEFINE_STRUCT_MEMBER(STRUCT, TYPE, EXPORTED_NAME, NAME, RO) { \
|
||||
.type = mSCRIPT_CLASS_INIT_INSTANCE_MEMBER, \
|
||||
.info = { \
|
||||
.member = { \
|
||||
.name = #EXPORTED_NAME, \
|
||||
.type = mSCRIPT_TYPE_MS_ ## TYPE, \
|
||||
.offset = offsetof(struct STRUCT, NAME) \
|
||||
.offset = offsetof(struct STRUCT, NAME), \
|
||||
.readonly = RO \
|
||||
} \
|
||||
} \
|
||||
},
|
||||
|
||||
#define mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(STRUCT, TYPE, EXPORTED_NAME, NAME) \
|
||||
_mSCRIPT_DEFINE_STRUCT_MEMBER(STRUCT, TYPE, EXPORTED_NAME, NAME, false)
|
||||
|
||||
#define mSCRIPT_DEFINE_STRUCT_CONST_MEMBER_NAMED(STRUCT, TYPE, EXPORTED_NAME, NAME) \
|
||||
_mSCRIPT_DEFINE_STRUCT_MEMBER(STRUCT, TYPE, EXPORTED_NAME, NAME, true)
|
||||
|
||||
#define mSCRIPT_DEFINE_STRUCT_MEMBER(STRUCT, TYPE, NAME) \
|
||||
mSCRIPT_DEFINE_STRUCT_MEMBER_NAMED(STRUCT, TYPE, NAME, NAME)
|
||||
|
||||
#define mSCRIPT_DEFINE_STRUCT_CONST_MEMBER(STRUCT, TYPE, NAME) \
|
||||
mSCRIPT_DEFINE_STRUCT_CONST_MEMBER_NAMED(STRUCT, TYPE, NAME, NAME)
|
||||
|
||||
#define mSCRIPT_DEFINE_INHERIT(PARENT) { \
|
||||
.type = mSCRIPT_CLASS_INIT_INHERIT, \
|
||||
.info = { \
|
||||
|
@ -393,6 +403,9 @@ CXX_GUARD_START
|
|||
static const struct mScriptValue _mSTStructBindingDefaults_doc_ ## TYPE ## _ ## NAME[mSCRIPT_PARAMS_MAX]; \
|
||||
_mSCRIPT_DECLARE_DOC_STRUCT_METHOD(SCOPE, TYPE, NAME, S, 0, 0, NPARAMS, _mIDENT(_mSTStructBindingDefaults_doc_ ## TYPE ## _ ## NAME), __VA_ARGS__) \
|
||||
|
||||
#define mSCRIPT_DEFINE_FUNCTION_BINDING_DEFAULTS(NAME) \
|
||||
static const struct mScriptValue _bindingDefaults_ ## NAME[mSCRIPT_PARAMS_MAX] = {
|
||||
|
||||
#define mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(TYPE, NAME) \
|
||||
static const struct mScriptValue _mSTStructBindingDefaults_ ## TYPE ## _ ## NAME[mSCRIPT_PARAMS_MAX] = { \
|
||||
mSCRIPT_NO_DEFAULT,
|
||||
|
@ -423,7 +436,7 @@ CXX_GUARD_START
|
|||
#define mSCRIPT_DEFINE_STRUCT_DEINIT(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(DEINIT, TYPE, _deinit, _deinit)
|
||||
#define mSCRIPT_DEFINE_STRUCT_DEINIT_NAMED(TYPE, NAME) _mSCRIPT_DEFINE_STRUCT_BINDING(DEINIT, TYPE, _deinit, NAME)
|
||||
#define mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(GET, TYPE, _get, _get)
|
||||
#define mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(TYPE) _mSCRIPT_DEFINE_STRUCT_BINDING(SET, TYPE, _set, _set)
|
||||
#define mSCRIPT_DEFINE_STRUCT_DEFAULT_SET(TYPE, SETTER) _mSCRIPT_DEFINE_STRUCT_BINDING(SET, TYPE, SETTER, SETTER)
|
||||
|
||||
#define mSCRIPT_DEFINE_DOC_STRUCT_METHOD(SCOPE, TYPE, NAME) mSCRIPT_DEFINE_STRUCT_METHOD_NAMED(doc_ ## TYPE, NAME, NAME)
|
||||
|
||||
|
@ -439,7 +452,7 @@ CXX_GUARD_START
|
|||
|
||||
#define mSCRIPT_DEFINE_END { .type = mSCRIPT_CLASS_INIT_END } } }
|
||||
|
||||
#define _mSCRIPT_BIND_FUNCTION(NAME, NRET, RETURN, NPARAMS, ...) \
|
||||
#define _mSCRIPT_BIND_FUNCTION(NAME, NRET, RETURN, DEFAULTS, NPARAMS, ...) \
|
||||
static struct mScriptFunction _function_ ## NAME = { \
|
||||
.call = _binding_ ## NAME \
|
||||
}; \
|
||||
|
@ -456,6 +469,7 @@ CXX_GUARD_START
|
|||
.count = NPARAMS, \
|
||||
.entries = { _mCALL(_mIF0_ ## NPARAMS, 0) _mCALL(mSCRIPT_PREFIX_ ## NPARAMS, mSCRIPT_TYPE_MS_, _mEVEN_ ## NPARAMS(__VA_ARGS__)) }, \
|
||||
.names = { _mCALL(_mIF0_ ## NPARAMS, 0) _mCALL(_mCALL_ ## NPARAMS, _mSTRINGIFY, _mODD_ ## NPARAMS(__VA_ARGS__)) }, \
|
||||
.defaults = DEFAULTS, \
|
||||
}, \
|
||||
.returnType = { \
|
||||
.count = NRET, \
|
||||
|
@ -472,7 +486,7 @@ CXX_GUARD_START
|
|||
} \
|
||||
}
|
||||
|
||||
#define mSCRIPT_BIND_FUNCTION(NAME, RETURN, FUNCTION, NPARAMS, ...) \
|
||||
#define _mSCRIPT_BIND_N_FUNCTION(NAME, RETURN, FUNCTION, DEFAULTS, NPARAMS, ...) \
|
||||
static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \
|
||||
UNUSED(ctx); \
|
||||
_mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
|
||||
|
@ -482,9 +496,9 @@ CXX_GUARD_START
|
|||
_mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS); \
|
||||
return true; \
|
||||
} \
|
||||
_mSCRIPT_BIND_FUNCTION(NAME, 1, mSCRIPT_TYPE_MS_ ## RETURN, NPARAMS, __VA_ARGS__)
|
||||
_mSCRIPT_BIND_FUNCTION(NAME, 1, mSCRIPT_TYPE_MS_ ## RETURN, DEFAULTS, NPARAMS, __VA_ARGS__)
|
||||
|
||||
#define mSCRIPT_BIND_VOID_FUNCTION(NAME, FUNCTION, NPARAMS, ...) \
|
||||
#define _mSCRIPT_BIND_VOID_FUNCTION(NAME, FUNCTION, DEFAULTS, NPARAMS, ...) \
|
||||
static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \
|
||||
UNUSED(ctx); \
|
||||
_mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
|
||||
|
@ -494,7 +508,21 @@ CXX_GUARD_START
|
|||
_mSCRIPT_CALL_VOID(FUNCTION, NPARAMS); \
|
||||
return true; \
|
||||
} \
|
||||
_mSCRIPT_BIND_FUNCTION(NAME, 0, 0, NPARAMS, __VA_ARGS__)
|
||||
_mSCRIPT_BIND_FUNCTION(NAME, 0, 0, NULL, NPARAMS, __VA_ARGS__)
|
||||
|
||||
#define mSCRIPT_BIND_FUNCTION(NAME, RETURN, FUNCTION, NPARAMS, ...) \
|
||||
_mSCRIPT_BIND_N_FUNCTION(NAME, RETURN, FUNCTION, NULL, NPARAMS, __VA_ARGS__)
|
||||
|
||||
#define mSCRIPT_BIND_VOID_FUNCTION(NAME, FUNCTION, NPARAMS, ...) \
|
||||
_mSCRIPT_BIND_VOID_FUNCTION(NAME, FUNCTION, NULL, NPARAMS, __VA_ARGS__)
|
||||
|
||||
#define mSCRIPT_BIND_FUNCTION_WITH_DEFAULTS(NAME, RETURN, FUNCTION, NPARAMS, ...) \
|
||||
static const struct mScriptValue _bindingDefaults_ ## NAME[mSCRIPT_PARAMS_MAX]; \
|
||||
_mSCRIPT_BIND_N_FUNCTION(NAME, RETURN, FUNCTION, _mIDENT(_bindingDefaults_ ## NAME), NPARAMS, __VA_ARGS__)
|
||||
|
||||
#define mSCRIPT_BIND_VOID_FUNCTION_WITH_DEFAULTS(NAME, FUNCTION, NPARAMS, ...) \
|
||||
static const struct mScriptValue _bindingDefaults_ ## _ ## NAME[mSCRIPT_PARAMS_MAX]; \
|
||||
_mSCRIPT_BIND_VOID_FUNCTION(NAME, FUNCTION, _mIDENT(_bindingDefaults_ ## NAME), NPARAMS, __VA_ARGS__)
|
||||
|
||||
#define _mSCRIPT_DEFINE_DOC_FUNCTION(SCOPE, NAME, NRET, RETURN, NPARAMS, ...) \
|
||||
static const struct mScriptType _mScriptDocType_ ## NAME = { \
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/* Copyright (c) 2013-2023 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 M_SCRIPT_STORAGE_H
|
||||
#define M_SCRIPT_STORAGE_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba/script/context.h>
|
||||
#include <mgba/script/macros.h>
|
||||
|
||||
struct VFile;
|
||||
void mScriptContextAttachStorage(struct mScriptContext* context);
|
||||
void mScriptStorageFlushAll(struct mScriptContext* context);
|
||||
|
||||
bool mScriptStorageSaveBucket(struct mScriptContext* context, const char* bucket);
|
||||
bool mScriptStorageSaveBucketVF(struct mScriptContext* context, const char* bucket, struct VFile* vf);
|
||||
bool mScriptStorageLoadBucket(struct mScriptContext* context, const char* bucket);
|
||||
bool mScriptStorageLoadBucketVF(struct mScriptContext* context, const char* bucket, struct VFile* vf);
|
||||
void mScriptStorageGetBucketPath(const char* bucket, char* out);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -35,16 +35,17 @@ CXX_GUARD_START
|
|||
#define mSCRIPT_TYPE_C_PTR void*
|
||||
#define mSCRIPT_TYPE_C_CPTR const void*
|
||||
#define mSCRIPT_TYPE_C_LIST struct mScriptList*
|
||||
#define mSCRIPT_TYPE_C_TABLE Table*
|
||||
#define mSCRIPT_TYPE_C_TABLE struct Table*
|
||||
#define mSCRIPT_TYPE_C_WRAPPER struct mScriptValue*
|
||||
#define mSCRIPT_TYPE_C_WEAKREF uint32_t
|
||||
#define mSCRIPT_TYPE_C_NUL void*
|
||||
#define mSCRIPT_TYPE_C_S(STRUCT) struct STRUCT*
|
||||
#define mSCRIPT_TYPE_C_CS(STRUCT) const struct STRUCT*
|
||||
#define mSCRIPT_TYPE_C_S_METHOD(STRUCT, NAME) _mSTStructFunctionType_ ## STRUCT ## _ ## NAME
|
||||
#define mSCRIPT_TYPE_C_PS(X) void
|
||||
#define mSCRIPT_TYPE_C_PCS(X) void
|
||||
#define mSCRIPT_TYPE_C_WSTR struct mScriptValue*
|
||||
#define mSCRIPT_TYPE_C_WLIST struct mScriptValue*
|
||||
#define mSCRIPT_TYPE_C_WTABLE struct mScriptValue*
|
||||
#define mSCRIPT_TYPE_C_W(X) struct mScriptValue*
|
||||
#define mSCRIPT_TYPE_C_CW(X) const struct mScriptValue*
|
||||
|
||||
|
@ -66,13 +67,14 @@ CXX_GUARD_START
|
|||
#define mSCRIPT_TYPE_FIELD_TABLE table
|
||||
#define mSCRIPT_TYPE_FIELD_WRAPPER opaque
|
||||
#define mSCRIPT_TYPE_FIELD_WEAKREF u32
|
||||
#define mSCRIPT_TYPE_FIELD_NUL opaque
|
||||
#define mSCRIPT_TYPE_FIELD_S(STRUCT) opaque
|
||||
#define mSCRIPT_TYPE_FIELD_CS(STRUCT) copaque
|
||||
#define mSCRIPT_TYPE_FIELD_S_METHOD(STRUCT, NAME) copaque
|
||||
#define mSCRIPT_TYPE_FIELD_PS(STRUCT) opaque
|
||||
#define mSCRIPT_TYPE_FIELD_PCS(STRUCT) copaque
|
||||
#define mSCRIPT_TYPE_FIELD_WSTR opaque
|
||||
#define mSCRIPT_TYPE_FIELD_WLIST opaque
|
||||
#define mSCRIPT_TYPE_FIELD_WTABLE opaque
|
||||
#define mSCRIPT_TYPE_FIELD_W(TYPE) opaque
|
||||
#define mSCRIPT_TYPE_FIELD_CW(TYPE) opaque
|
||||
|
||||
|
@ -94,13 +96,14 @@ CXX_GUARD_START
|
|||
#define mSCRIPT_TYPE_MS_TABLE (&mSTTable)
|
||||
#define mSCRIPT_TYPE_MS_WRAPPER (&mSTWrapper)
|
||||
#define mSCRIPT_TYPE_MS_WEAKREF (&mSTWeakref)
|
||||
#define mSCRIPT_TYPE_MS_NUL mSCRIPT_TYPE_MS_VOID
|
||||
#define mSCRIPT_TYPE_MS_S(STRUCT) (&mSTStruct_ ## STRUCT)
|
||||
#define mSCRIPT_TYPE_MS_CS(STRUCT) (&mSTStructConst_ ## STRUCT)
|
||||
#define mSCRIPT_TYPE_MS_S_METHOD(STRUCT, NAME) (&_mSTStructBindingType_ ## STRUCT ## _ ## NAME)
|
||||
#define mSCRIPT_TYPE_MS_PS(STRUCT) (&mSTStructPtr_ ## STRUCT)
|
||||
#define mSCRIPT_TYPE_MS_PCS(STRUCT) (&mSTStructPtrConst_ ## STRUCT)
|
||||
#define mSCRIPT_TYPE_MS_WSTR (&mSTStringWrapper)
|
||||
#define mSCRIPT_TYPE_MS_WLIST (&mSTListWrapper)
|
||||
#define mSCRIPT_TYPE_MS_WTABLE (&mSTTableWrapper)
|
||||
#define mSCRIPT_TYPE_MS_W(TYPE) (&mSTWrapper_ ## TYPE)
|
||||
#define mSCRIPT_TYPE_MS_CW(TYPE) (&mSTWrapperConst_ ## TYPE)
|
||||
#define mSCRIPT_TYPE_MS_DS(STRUCT) (&mSTStruct_doc_ ## STRUCT)
|
||||
|
@ -120,14 +123,16 @@ CXX_GUARD_START
|
|||
#define mSCRIPT_TYPE_CMP_STR(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_STR, TYPE)
|
||||
#define mSCRIPT_TYPE_CMP_CHARP(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_CHARP, TYPE)
|
||||
#define mSCRIPT_TYPE_CMP_LIST(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_LIST, TYPE)
|
||||
#define mSCRIPT_TYPE_CMP_TABLE(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_TABLE, TYPE)
|
||||
#define mSCRIPT_TYPE_CMP_PTR(TYPE) ((TYPE)->base >= mSCRIPT_TYPE_OPAQUE)
|
||||
#define mSCRIPT_TYPE_CMP_WRAPPER(TYPE) (true)
|
||||
#define mSCRIPT_TYPE_CMP_NUL(TYPE) mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_VOID, TYPE)
|
||||
#define mSCRIPT_TYPE_CMP_S(STRUCT) mSCRIPT_TYPE_MS_S(STRUCT)->name == _mSCRIPT_FIELD_NAME
|
||||
#define mSCRIPT_TYPE_CMP_CS(STRUCT) mSCRIPT_TYPE_MS_CS(STRUCT)->name == _mSCRIPT_FIELD_NAME
|
||||
#define mSCRIPT_TYPE_CMP_S_METHOD(STRUCT, NAME) mSCRIPT_TYPE_MS_S_METHOD(STRUCT, NAME)->name == _mSCRIPT_FIELD_NAME
|
||||
#define mSCRIPT_TYPE_CMP_WSTR(TYPE) (mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_WSTR, TYPE))
|
||||
#define mSCRIPT_TYPE_CMP_WLIST(TYPE) (mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_WLIST, TYPE))
|
||||
#define mSCRIPT_TYPE_CMP_WTABLE(TYPE) (mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_WTABLE, TYPE))
|
||||
#define mSCRIPT_TYPE_CMP(TYPE0, TYPE1) mSCRIPT_TYPE_CMP_ ## TYPE0(TYPE1)
|
||||
#define mSCRIPT_TYPE_CMP_WSTR(TYPE) (mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_STR, TYPE) || mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_CHARP, TYPE))
|
||||
#define mSCRIPT_TYPE_CMP_WLIST(TYPE) (mSCRIPT_TYPE_CMP_GENERIC(mSCRIPT_TYPE_MS_LIST, TYPE))
|
||||
|
||||
enum mScriptTypeBase {
|
||||
mSCRIPT_TYPE_VOID = 0,
|
||||
|
@ -159,7 +164,8 @@ enum mScriptClassInitType {
|
|||
};
|
||||
|
||||
enum {
|
||||
mSCRIPT_VALUE_FLAG_FREE_BUFFER = 1
|
||||
mSCRIPT_VALUE_FLAG_FREE_BUFFER = 1,
|
||||
mSCRIPT_VALUE_FLAG_DEINIT = 2,
|
||||
};
|
||||
|
||||
struct mScriptType;
|
||||
|
@ -183,6 +189,7 @@ extern const struct mScriptType mSTWrapper;
|
|||
extern const struct mScriptType mSTWeakref;
|
||||
extern const struct mScriptType mSTStringWrapper;
|
||||
extern const struct mScriptType mSTListWrapper;
|
||||
extern const struct mScriptType mSTTableWrapper;
|
||||
|
||||
extern struct mScriptValue mScriptValueNull;
|
||||
|
||||
|
@ -225,6 +232,7 @@ struct mScriptClassMember {
|
|||
const char* docstring;
|
||||
const struct mScriptType* type;
|
||||
size_t offset;
|
||||
bool readonly;
|
||||
};
|
||||
|
||||
struct mScriptClassCastMember {
|
||||
|
@ -250,10 +258,10 @@ struct mScriptTypeClass {
|
|||
bool internal;
|
||||
struct Table instanceMembers;
|
||||
struct Table castToMembers;
|
||||
struct Table setters;
|
||||
struct mScriptClassMember* alloc; // TODO
|
||||
struct mScriptClassMember* free;
|
||||
struct mScriptClassMember* get;
|
||||
struct mScriptClassMember* set; // TODO
|
||||
};
|
||||
|
||||
struct mScriptType {
|
||||
|
@ -327,6 +335,8 @@ bool mScriptTableIteratorLookup(struct mScriptValue* table, struct TableIterator
|
|||
void mScriptFrameInit(struct mScriptFrame* frame);
|
||||
void mScriptFrameDeinit(struct mScriptFrame* frame);
|
||||
|
||||
struct mScriptValue* mScriptLambdaCreate0(struct mScriptValue* fn, struct mScriptList* args);
|
||||
|
||||
void mScriptClassInit(struct mScriptTypeClass* cls);
|
||||
void mScriptClassDeinit(struct mScriptTypeClass* cls);
|
||||
|
||||
|
@ -335,6 +345,7 @@ bool mScriptObjectGetConst(const struct mScriptValue* obj, const char* member, s
|
|||
bool mScriptObjectSet(struct mScriptValue* obj, const char* member, struct mScriptValue*);
|
||||
bool mScriptObjectCast(const struct mScriptValue* input, const struct mScriptType* type, struct mScriptValue* output) ;
|
||||
void mScriptObjectFree(struct mScriptValue* obj);
|
||||
struct mScriptValue* mScriptObjectBindLambda(struct mScriptValue* obj, const char* member, struct mScriptList* args);
|
||||
|
||||
bool mScriptPopS32(struct mScriptList* list, int32_t* out);
|
||||
bool mScriptPopU32(struct mScriptList* list, uint32_t* out);
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||
<true/>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Only used when Game Boy Camera is selected and a physical camera is set</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
|
|
5309
res/nointro.dat
|
@ -8,9 +8,8 @@ gocha
|
|||
Jaime J. Denizard
|
||||
MichaelK__
|
||||
Miras Absar
|
||||
Nic Losby
|
||||
Petru-Sebastian Toader
|
||||
Stevoisiak
|
||||
Tyler Jenkins
|
||||
William K. Leung
|
||||
Zach
|
||||
Zhongchao Qian
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
local state = {}
|
||||
state.period = 4
|
||||
state.phase = 0
|
||||
state.x = 0
|
||||
state.y = 0
|
||||
|
||||
function state.update()
|
||||
state.phase = state.phase + 1
|
||||
if state.phase == state.period then
|
||||
state.phase = 0
|
||||
end
|
||||
if state.phase == 0 then
|
||||
if input.activeGamepad then
|
||||
local x = input.activeGamepad.axes[1] / 30000
|
||||
local y = input.activeGamepad.axes[2] / 30000
|
||||
-- Map the circle onto a square, since we don't
|
||||
-- want to have a duty of 1/sqrt(2) on the angles
|
||||
local theta = math.atan(y, x)
|
||||
local r = math.sqrt(x * x + y * y)
|
||||
if theta < math.pi * -3 / 4 then
|
||||
r = -r / math.cos(theta)
|
||||
elseif theta < math.pi * -1 / 4 then
|
||||
r = -r / math.sin(theta)
|
||||
elseif theta < math.pi * 1 / 4 then
|
||||
r = r / math.cos(theta)
|
||||
elseif theta < math.pi * 3 / 4 then
|
||||
r = r / math.sin(theta)
|
||||
elseif theta < math.pi * 5 / 4 then
|
||||
r = -r / math.cos(theta)
|
||||
end
|
||||
state.x = math.cos(theta) * r
|
||||
state.y = math.sin(theta) * r
|
||||
else
|
||||
state.x = 0
|
||||
state.y = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function state.read()
|
||||
emu:clearKeys(0xF0)
|
||||
if math.floor(math.abs(state.x) * state.period) > state.phase then
|
||||
if state.x > 0 then
|
||||
emu:addKey(C.GB_KEY.RIGHT)
|
||||
else
|
||||
emu:addKey(C.GB_KEY.LEFT)
|
||||
end
|
||||
end
|
||||
if math.floor(math.abs(state.y) * state.period) > state.phase then
|
||||
if state.y > 0 then
|
||||
emu:addKey(C.GB_KEY.DOWN)
|
||||
else
|
||||
emu:addKey(C.GB_KEY.UP)
|
||||
end
|
||||
end
|
||||
|
||||
-- The duty cycle approach can confuse menus and the like,
|
||||
-- so the POV hat setting should force a direction on
|
||||
if input.activeGamepad and #input.activeGamepad.hats > 0 then
|
||||
local hat = input.activeGamepad.hats[1]
|
||||
if hat & C.INPUT_DIR.UP ~= 0 then
|
||||
emu:addKey(C.GB_KEY.UP)
|
||||
end
|
||||
if hat & C.INPUT_DIR.DOWN ~= 0 then
|
||||
emu:addKey(C.GB_KEY.DOWN)
|
||||
end
|
||||
if hat & C.INPUT_DIR.LEFT ~= 0 then
|
||||
emu:addKey(C.GB_KEY.LEFT)
|
||||
end
|
||||
if hat & C.INPUT_DIR.RIGHT ~= 0 then
|
||||
emu:addKey(C.GB_KEY.RIGHT)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
callbacks:add("frame", state.update)
|
||||
callbacks:add("keysRead", state.read)
|
|
@ -0,0 +1,8 @@
|
|||
local theta = 0
|
||||
|
||||
function readLight()
|
||||
theta = math.fmod(theta + math.pi / 120, math.pi * 2)
|
||||
return (math.sin(theta) + 1) * 75
|
||||
end
|
||||
|
||||
emu:setSolarSensorCallback(readLight)
|
|
@ -0,0 +1,44 @@
|
|||
math.randomseed(os.time())
|
||||
local state = {}
|
||||
state.logo = image.load(script.dir .. "/logo.png")
|
||||
state.overlay = canvas:newLayer(state.logo.width, state.logo.height)
|
||||
state.overlay.image:drawImageOpaque(state.logo, 0, 0)
|
||||
state.x = math.random() * (canvas:screenWidth() - state.logo.width)
|
||||
state.y = math.random() * (canvas:screenHeight() - state.logo.height)
|
||||
state.direction = math.floor(math.random() * 3)
|
||||
state.speed = 0.5
|
||||
|
||||
state.overlay:setPosition(math.floor(state.x), math.floor(state.y))
|
||||
state.overlay:update()
|
||||
|
||||
function state.update()
|
||||
if state.direction & 1 == 1 then
|
||||
state.x = state.x + 1
|
||||
if state.x > canvas:screenWidth() - state.logo.width then
|
||||
state.x = (canvas:screenWidth() - state.logo.width) * 2 - state.x
|
||||
state.direction = state.direction ~ 1
|
||||
end
|
||||
else
|
||||
state.x = state.x - 1
|
||||
if state.x < 0 then
|
||||
state.x = -state.x
|
||||
state.direction = state.direction ~ 1
|
||||
end
|
||||
end
|
||||
if state.direction & 2 == 2 then
|
||||
state.y = state.y + 1
|
||||
if state.y > canvas:screenHeight() - state.logo.height then
|
||||
state.y = (canvas:screenHeight() - state.logo.height) * 2 - state.y
|
||||
state.direction = state.direction ~ 2
|
||||
end
|
||||
else
|
||||
state.y = state.y - 1
|
||||
if state.y < 0 then
|
||||
state.y = -state.y
|
||||
state.direction = state.direction ~ 2
|
||||
end
|
||||
end
|
||||
state.overlay:setPosition(math.floor(state.x), math.floor(state.y))
|
||||
end
|
||||
|
||||
callbacks:add("frame", state.update)
|
After Width: | Height: | Size: 1.6 KiB |
|
@ -174,7 +174,7 @@ function Generation1En._readPartyMon(game, address, nameAddress, otAddress)
|
|||
return mon
|
||||
end
|
||||
|
||||
function Generation2En._readBoxMon(game, address, nameAddress, otAddress)
|
||||
function Generation2En._readBoxMon(game, address, nameAddress, otAddress)
|
||||
local mon = {}
|
||||
mon.species = emu:read8(address + 0)
|
||||
mon.item = emu:read8(address + 1)
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
local r = 0
|
||||
local theta = 0
|
||||
local rotation = {}
|
||||
|
||||
math.randomseed()
|
||||
|
||||
function rotation.sample()
|
||||
theta = math.fmod(theta + math.random() / 20, math.pi * 2)
|
||||
r = math.min(math.max(r + (math.random() - 0.5) / 50, -1), 1)
|
||||
end
|
||||
|
||||
function rotation.readTiltX()
|
||||
return math.cos(theta) * r
|
||||
end
|
||||
|
||||
function rotation.readTiltY()
|
||||
return math.sin(theta) * r
|
||||
end
|
||||
|
||||
emu:setRotationCallbacks(rotation)
|
After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.2 KiB |
|
@ -0,0 +1 @@
|
|||
<svg viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5"><path style="fill:#d8d6d1;stroke:#000;stroke-width:2.56px" d="M13.324 16.625h101.352v94.75H13.324z" transform="matrix(1.17413 0 0 1.1715 -11.144 -8.976)"/><path style="fill:#d8d6d1;stroke:#000;stroke-width:2.62px" d="M29.324 13.125h69.352v105.75H29.324z" transform="matrix(1.1968 0 0 1.08747 -12.595 -7.773)"/><path d="M93.426 105.2c0-.662-.596-1.2-1.33-1.2H35.904c-.734 0-1.33.538-1.33 1.2V128h58.852v-22.8z" style="fill:none;stroke:#000;stroke-width:.73px" transform="matrix(1.24041 0 0 1.375 -15.386 -53.5)"/><path d="M27 79.375h74" style="fill:none;stroke:#000;stroke-width:.86px" transform="matrix(1.12162 0 0 1.11321 -7.784 -8.86)"/><path d="m11 26.625 16-.025" style="fill:none;stroke:#000;stroke-width:.86px" transform="matrix(1.125 0 0 1.11652 -7.375 -5.206)"/><path d="m11 26.625 16-.025" style="fill:none;stroke:#000;stroke-width:.86px" transform="matrix(1.125 0 0 1.11652 -7.375 6.78)"/><path d="m11 26.625 16-.025" style="fill:none;stroke:#000;stroke-width:.86px" transform="matrix(1.125 0 0 1.11652 -7.375 18.78)"/><path d="m11 26.625 16-.025" style="fill:none;stroke:#000;stroke-width:.86px" transform="matrix(1.125 0 0 1.11652 -7.375 30.78)"/><path d="m11 26.625 16-.025" style="fill:none;stroke:#000;stroke-width:.86px" transform="matrix(1.125 0 0 1.11652 -7.375 42.78)"/><path d="m11 26.625 16-.025" style="fill:none;stroke:#000;stroke-width:.86px" transform="matrix(1.125 0 0 1.11652 -7.375 54.78)"/><path d="m11 26.625 16-.025" style="fill:none;stroke:#000;stroke-width:.86px" transform="matrix(1.125 0 0 1.11652 -7.375 66.78)"/><path d="m11 26.625 16-.025" style="fill:none;stroke:#000;stroke-width:.86px" transform="matrix(1.125 0 0 1.11652 -7.375 78.78)"/><g><path d="m11 26.625 16-.025" style="fill:none;stroke:#000;stroke-width:.86px" transform="matrix(1.125 0 0 1.11652 92.625 -5.206)"/><path d="m11 26.625 16-.025" style="fill:none;stroke:#000;stroke-width:.86px" transform="matrix(1.125 0 0 1.11652 92.625 6.78)"/><path d="m11 26.625 16-.025" style="fill:none;stroke:#000;stroke-width:.86px" transform="matrix(1.125 0 0 1.11652 92.625 18.78)"/><path d="m11 26.625 16-.025" style="fill:none;stroke:#000;stroke-width:.86px" transform="matrix(1.125 0 0 1.11652 92.625 30.78)"/><path d="m11 26.625 16-.025" style="fill:none;stroke:#000;stroke-width:.86px" transform="matrix(1.125 0 0 1.11652 92.625 42.78)"/><path d="m11 26.625 16-.025" style="fill:none;stroke:#000;stroke-width:.86px" transform="matrix(1.125 0 0 1.11652 92.625 54.78)"/><path d="m11 26.625 16-.025" style="fill:none;stroke:#000;stroke-width:.86px" transform="matrix(1.125 0 0 1.11652 92.625 66.78)"/><path d="m11 26.625 16-.025" style="fill:none;stroke:#000;stroke-width:.86px" transform="matrix(1.125 0 0 1.11652 92.625 78.78)"/></g><g><path d="M98.526 55.513c0-2.008-1.67-3.638-3.728-3.638H33.202c-2.058 0-3.728 1.63-3.728 3.638v16.974c0 2.008 1.67 3.638 3.728 3.638h61.596c2.058 0 3.728-1.63 3.728-3.638V55.513z" style="fill:#fff;stroke:#000;stroke-width:.87px" transform="matrix(1.08613 0 0 1.1134 -5.512 -9.258)"/></g><g><path d="M88.526 41.125c-16.351 6.018-32.701 6.1-49.052 0V16.992h49.052v24.133z" style="fill:none;stroke:#000;stroke-width:.89px" transform="matrix(1.08047 0 0 1.0637 -5.15 -11.307)"/></g><g><path d="M39.474 35.169c16.351 6.1 32.701 6.017 49.052 0v5.956c-16.351 6.018-32.701 6.1-49.052 0v-5.956z" style="fill:#4e5247;stroke:#000;stroke-width:.88px" transform="matrix(1.08047 0 0 1.10238 -5.15 -7.057)"/></g><g><path d="M53.875 102.875v-13.5" style="fill:none;stroke:#000;stroke-width:.98px" transform="matrix(1 0 0 .96296 .625 3.435)"/><path d="M53.875 102.875v-13.5" style="fill:none;stroke:#000;stroke-width:.98px" transform="matrix(1 0 0 .96296 19.625 3.435)"/></g></svg>
|
After Width: | Height: | Size: 3.8 KiB |
|
@ -0,0 +1,143 @@
|
|||
/* MIT License
|
||||
*
|
||||
* Copyright (c) 2015-2023 Lior Halphon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
/* Based on this (really good) article: http://blog.pkh.me/p/19-butchering-hqx-scaling-filters.html */
|
||||
|
||||
/* The colorspace used by the HQnx filters is not really YUV, despite the algorithm description claims it is. It is
|
||||
also not normalized. Therefore, we shall call the colorspace used by HQnx "HQ Colorspace" to avoid confusion. */
|
||||
varying vec2 texCoord;
|
||||
uniform sampler2D tex;
|
||||
uniform vec2 texSize;
|
||||
|
||||
vec3 rgb_to_hq_colospace(vec4 rgb)
|
||||
{
|
||||
return vec3( 0.250 * rgb.r + 0.250 * rgb.g + 0.250 * rgb.b,
|
||||
0.250 * rgb.r - 0.000 * rgb.g - 0.250 * rgb.b,
|
||||
-0.125 * rgb.r + 0.250 * rgb.g - 0.125 * rgb.b);
|
||||
}
|
||||
|
||||
bool is_different(vec4 a, vec4 b)
|
||||
{
|
||||
vec3 diff = abs(rgb_to_hq_colospace(a) - rgb_to_hq_colospace(b));
|
||||
return diff.x > 0.018 || diff.y > 0.002 || diff.z > 0.005;
|
||||
}
|
||||
|
||||
#define P(m, r) ((pattern & (m)) == (r))
|
||||
|
||||
vec4 interp_2px(vec4 c1, float w1, vec4 c2, float w2)
|
||||
{
|
||||
return (c1 * w1 + c2 * w2) / (w1 + w2);
|
||||
}
|
||||
|
||||
vec4 interp_3px(vec4 c1, float w1, vec4 c2, float w2, vec4 c3, float w3)
|
||||
{
|
||||
return (c1 * w1 + c2 * w2 + c3 * w3) / (w1 + w2 + w3);
|
||||
}
|
||||
|
||||
vec4 scale(sampler2D image, vec2 position, vec2 input_resolution)
|
||||
{
|
||||
// o = offset, the width of a pixel
|
||||
vec2 o = vec2(1, 1) / input_resolution;
|
||||
|
||||
/* We always calculate the top left pixel. If we need a different pixel, we flip the image */
|
||||
|
||||
// p = the position within a pixel [0...1]
|
||||
vec2 p = fract(position * input_resolution);
|
||||
|
||||
if (p.x > 0.5) o.x = -o.x;
|
||||
if (p.y > 0.5) o.y = -o.y;
|
||||
|
||||
vec4 w0 = texture2D(image, position + vec2( -o.x, -o.y));
|
||||
vec4 w1 = texture2D(image, position + vec2( 0, -o.y));
|
||||
vec4 w2 = texture2D(image, position + vec2( o.x, -o.y));
|
||||
vec4 w3 = texture2D(image, position + vec2( -o.x, 0));
|
||||
vec4 w4 = texture2D(image, position + vec2( 0, 0));
|
||||
vec4 w5 = texture2D(image, position + vec2( o.x, 0));
|
||||
vec4 w6 = texture2D(image, position + vec2( -o.x, o.y));
|
||||
vec4 w7 = texture2D(image, position + vec2( 0, o.y));
|
||||
vec4 w8 = texture2D(image, position + vec2( o.x, o.y));
|
||||
|
||||
int pattern = 0;
|
||||
if (is_different(w0, w4)) pattern |= 1;
|
||||
if (is_different(w1, w4)) pattern |= 2;
|
||||
if (is_different(w2, w4)) pattern |= 4;
|
||||
if (is_different(w3, w4)) pattern |= 8;
|
||||
if (is_different(w5, w4)) pattern |= 16;
|
||||
if (is_different(w6, w4)) pattern |= 32;
|
||||
if (is_different(w7, w4)) pattern |= 64;
|
||||
if (is_different(w8, w4)) pattern |= 128;
|
||||
|
||||
if ((P(0xBF,0x37) || P(0xDB,0x13)) && is_different(w1, w5)) {
|
||||
return interp_2px(w4, 3.0, w3, 1.0);
|
||||
}
|
||||
if ((P(0xDB,0x49) || P(0xEF,0x6D)) && is_different(w7, w3)) {
|
||||
return interp_2px(w4, 3.0, w1, 1.0);
|
||||
}
|
||||
if ((P(0x0B,0x0B) || P(0xFE,0x4A) || P(0xFE,0x1A)) && is_different(w3, w1)) {
|
||||
return w4;
|
||||
}
|
||||
if ((P(0x6F,0x2A) || P(0x5B,0x0A) || P(0xBF,0x3A) || P(0xDF,0x5A) ||
|
||||
P(0x9F,0x8A) || P(0xCF,0x8A) || P(0xEF,0x4E) || P(0x3F,0x0E) ||
|
||||
P(0xFB,0x5A) || P(0xBB,0x8A) || P(0x7F,0x5A) || P(0xAF,0x8A) ||
|
||||
P(0xEB,0x8A)) && is_different(w3, w1)) {
|
||||
return interp_2px(w4, 3.0, w0, 1.0);
|
||||
}
|
||||
if (P(0x0B,0x08)) {
|
||||
return interp_3px(w4, 2.0, w0, 1.0, w1, 1.0);
|
||||
}
|
||||
if (P(0x0B,0x02)) {
|
||||
return interp_3px(w4, 2.0, w0, 1.0, w3, 1.0);
|
||||
}
|
||||
if (P(0x2F,0x2F)) {
|
||||
return interp_3px(w4, 4.0, w3, 1.0, w1, 1.0);
|
||||
}
|
||||
if (P(0xBF,0x37) || P(0xDB,0x13)) {
|
||||
return interp_3px(w4, 5.0, w1, 2.0, w3, 1.0);
|
||||
}
|
||||
if (P(0xDB,0x49) || P(0xEF,0x6D)) {
|
||||
return interp_3px(w4, 5.0, w3, 2.0, w1, 1.0);
|
||||
}
|
||||
if (P(0x1B,0x03) || P(0x4F,0x43) || P(0x8B,0x83) || P(0x6B,0x43)) {
|
||||
return interp_2px(w4, 3.0, w3, 1.0);
|
||||
}
|
||||
if (P(0x4B,0x09) || P(0x8B,0x89) || P(0x1F,0x19) || P(0x3B,0x19)) {
|
||||
return interp_2px(w4, 3.0, w1, 1.0);
|
||||
}
|
||||
if (P(0x7E,0x2A) || P(0xEF,0xAB) || P(0xBF,0x8F) || P(0x7E,0x0E)) {
|
||||
return interp_3px(w4, 2.0, w3, 3.0, w1, 3.0);
|
||||
}
|
||||
if (P(0xFB,0x6A) || P(0x6F,0x6E) || P(0x3F,0x3E) || P(0xFB,0xFA) ||
|
||||
P(0xDF,0xDE) || P(0xDF,0x1E)) {
|
||||
return interp_2px(w4, 3.0, w0, 1.0);
|
||||
}
|
||||
if (P(0x0A,0x00) || P(0x4F,0x4B) || P(0x9F,0x1B) || P(0x2F,0x0B) ||
|
||||
P(0xBE,0x0A) || P(0xEE,0x0A) || P(0x7E,0x0A) || P(0xEB,0x4B) ||
|
||||
P(0x3B,0x1B)) {
|
||||
return interp_3px(w4, 2.0, w3, 1.0, w1, 1.0);
|
||||
}
|
||||
|
||||
return interp_3px(w4, 6.0, w3, 1.0, w1, 1.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
gl_FragColor = scale(tex, texCoord, texSize);
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
[shader]
|
||||
name=hq2x
|
||||
author=Lior Halphon
|
||||
description="High Quality" 2x scaling
|
||||
passes=1
|
||||
|
||||
[pass.0]
|
||||
fragmentShader=hq2x.fs
|
||||
blend=0
|
||||
width=-2
|
||||
height=-2
|
|
@ -0,0 +1,9 @@
|
|||
[shader]
|
||||
name=OmniScale
|
||||
author=Lior Halphon
|
||||
description=Resolution-indepedent scaler inspired by the hqx family scalers
|
||||
passes=1
|
||||
|
||||
[pass.0]
|
||||
fragmentShader=omniscale.fs
|
||||
blend=0
|
|
@ -0,0 +1,292 @@
|
|||
/* MIT License
|
||||
*
|
||||
* Copyright (c) 2015-2023 Lior Halphon
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
/* OmniScale is derived from the pattern based design of HQnx, but with the following general differences:
|
||||
- The actual output calculating was completely redesigned as resolution independent graphic generator. This allows
|
||||
scaling to any factor.
|
||||
- HQnx approximations that were good enough for a 2x/3x/4x factor were refined, creating smoother gradients.
|
||||
- "Quarters" can be interpolated in more ways than in the HQnx filters
|
||||
- If a pattern does not provide enough information to determine the suitable scaling interpolation, up to 16 pixels
|
||||
per quarter are sampled (in contrast to the usual 9) in order to determine the best interpolation.
|
||||
*/
|
||||
varying vec2 texCoord;
|
||||
uniform sampler2D tex;
|
||||
uniform vec2 texSize;
|
||||
uniform vec2 outputSize;
|
||||
|
||||
/* We use the same colorspace as the HQ algorithms. */
|
||||
vec3 rgb_to_hq_colospace(vec4 rgb)
|
||||
{
|
||||
return vec3( 0.250 * rgb.r + 0.250 * rgb.g + 0.250 * rgb.b,
|
||||
0.250 * rgb.r - 0.000 * rgb.g - 0.250 * rgb.b,
|
||||
-0.125 * rgb.r + 0.250 * rgb.g - 0.125 * rgb.b);
|
||||
}
|
||||
|
||||
|
||||
bool is_different(vec4 a, vec4 b)
|
||||
{
|
||||
vec3 diff = abs(rgb_to_hq_colospace(a) - rgb_to_hq_colospace(b));
|
||||
return diff.x > 0.018 || diff.y > 0.002 || diff.z > 0.005;
|
||||
}
|
||||
|
||||
#define P(m, r) ((pattern & (m)) == (r))
|
||||
|
||||
vec4 scale(sampler2D image, vec2 position, vec2 input_resolution, vec2 output_resolution)
|
||||
{
|
||||
// o = offset, the width of a pixel
|
||||
vec2 o = vec2(1, 1) / input_resolution;
|
||||
|
||||
/* We always calculate the top left quarter. If we need a different quarter, we flip our co-ordinates */
|
||||
|
||||
// p = the position within a pixel [0...1]
|
||||
vec2 p = fract(position * input_resolution);
|
||||
|
||||
if (p.x > 0.5) {
|
||||
o.x = -o.x;
|
||||
p.x = 1.0 - p.x;
|
||||
}
|
||||
if (p.y > 0.5) {
|
||||
o.y = -o.y;
|
||||
p.y = 1.0 - p.y;
|
||||
}
|
||||
|
||||
vec4 w0 = texture2D(image, position + vec2( -o.x, -o.y));
|
||||
vec4 w1 = texture2D(image, position + vec2( 0, -o.y));
|
||||
vec4 w2 = texture2D(image, position + vec2( o.x, -o.y));
|
||||
vec4 w3 = texture2D(image, position + vec2( -o.x, 0));
|
||||
vec4 w4 = texture2D(image, position + vec2( 0, 0));
|
||||
vec4 w5 = texture2D(image, position + vec2( o.x, 0));
|
||||
vec4 w6 = texture2D(image, position + vec2( -o.x, o.y));
|
||||
vec4 w7 = texture2D(image, position + vec2( 0, o.y));
|
||||
vec4 w8 = texture2D(image, position + vec2( o.x, o.y));
|
||||
|
||||
int pattern = 0;
|
||||
if (is_different(w0, w4)) pattern |= 1 << 0;
|
||||
if (is_different(w1, w4)) pattern |= 1 << 1;
|
||||
if (is_different(w2, w4)) pattern |= 1 << 2;
|
||||
if (is_different(w3, w4)) pattern |= 1 << 3;
|
||||
if (is_different(w5, w4)) pattern |= 1 << 4;
|
||||
if (is_different(w6, w4)) pattern |= 1 << 5;
|
||||
if (is_different(w7, w4)) pattern |= 1 << 6;
|
||||
if (is_different(w8, w4)) pattern |= 1 << 7;
|
||||
|
||||
if ((P(0xBF,0x37) || P(0xDB,0x13)) && is_different(w1, w5)) {
|
||||
return mix(w4, w3, 0.5 - p.x);
|
||||
}
|
||||
if ((P(0xDB,0x49) || P(0xEF,0x6D)) && is_different(w7, w3)) {
|
||||
return mix(w4, w1, 0.5 - p.y);
|
||||
}
|
||||
if ((P(0x0B,0x0B) || P(0xFE,0x4A) || P(0xFE,0x1A)) && is_different(w3, w1)) {
|
||||
return w4;
|
||||
}
|
||||
if ((P(0x6F,0x2A) || P(0x5B,0x0A) || P(0xBF,0x3A) || P(0xDF,0x5A) ||
|
||||
P(0x9F,0x8A) || P(0xCF,0x8A) || P(0xEF,0x4E) || P(0x3F,0x0E) ||
|
||||
P(0xFB,0x5A) || P(0xBB,0x8A) || P(0x7F,0x5A) || P(0xAF,0x8A) ||
|
||||
P(0xEB,0x8A)) && is_different(w3, w1)) {
|
||||
return mix(w4, mix(w4, w0, 0.5 - p.x), 0.5 - p.y);
|
||||
}
|
||||
if (P(0x0B,0x08)) {
|
||||
return mix(mix(w0 * 0.375 + w1 * 0.25 + w4 * 0.375, w4 * 0.5 + w1 * 0.5, p.x * 2.0), w4, p.y * 2.0);
|
||||
}
|
||||
if (P(0x0B,0x02)) {
|
||||
return mix(mix(w0 * 0.375 + w3 * 0.25 + w4 * 0.375, w4 * 0.5 + w3 * 0.5, p.y * 2.0), w4, p.x * 2.0);
|
||||
}
|
||||
if (P(0x2F,0x2F)) {
|
||||
float dist = length(p - vec2(0.5));
|
||||
float pixel_size = length(1.0 / (output_resolution / input_resolution));
|
||||
if (dist < 0.5 - pixel_size / 2) {
|
||||
return w4;
|
||||
}
|
||||
vec4 r;
|
||||
if (is_different(w0, w1) || is_different(w0, w3)) {
|
||||
r = mix(w1, w3, p.y - p.x + 0.5);
|
||||
}
|
||||
else {
|
||||
r = mix(mix(w1 * 0.375 + w0 * 0.25 + w3 * 0.375, w3, p.y * 2.0), w1, p.x * 2.0);
|
||||
}
|
||||
|
||||
if (dist > 0.5 + pixel_size / 2) {
|
||||
return r;
|
||||
}
|
||||
return mix(w4, r, (dist - 0.5 + pixel_size / 2) / pixel_size);
|
||||
}
|
||||
if (P(0xBF,0x37) || P(0xDB,0x13)) {
|
||||
float dist = p.x - 2.0 * p.y;
|
||||
float pixel_size = length(1.0 / (output_resolution / input_resolution)) * sqrt(5.0);
|
||||
if (dist > pixel_size / 2) {
|
||||
return w1;
|
||||
}
|
||||
vec4 r = mix(w3, w4, p.x + 0.5);
|
||||
if (dist < -pixel_size / 2) {
|
||||
return r;
|
||||
}
|
||||
return mix(r, w1, (dist + pixel_size / 2) / pixel_size);
|
||||
}
|
||||
if (P(0xDB,0x49) || P(0xEF,0x6D)) {
|
||||
float dist = p.y - 2.0 * p.x;
|
||||
float pixel_size = length(1.0 / (output_resolution / input_resolution)) * sqrt(5.0);
|
||||
if (p.y - 2.0 * p.x > pixel_size / 2) {
|
||||
return w3;
|
||||
}
|
||||
vec4 r = mix(w1, w4, p.x + 0.5);
|
||||
if (dist < -pixel_size / 2) {
|
||||
return r;
|
||||
}
|
||||
return mix(r, w3, (dist + pixel_size / 2) / pixel_size);
|
||||
}
|
||||
if (P(0xBF,0x8F) || P(0x7E,0x0E)) {
|
||||
float dist = p.x + 2.0 * p.y;
|
||||
float pixel_size = length(1.0 / (output_resolution / input_resolution)) * sqrt(5.0);
|
||||
|
||||
if (dist > 1.0 + pixel_size / 2) {
|
||||
return w4;
|
||||
}
|
||||
|
||||
vec4 r;
|
||||
if (is_different(w0, w1) || is_different(w0, w3)) {
|
||||
r = mix(w1, w3, p.y - p.x + 0.5);
|
||||
}
|
||||
else {
|
||||
r = mix(mix(w1 * 0.375 + w0 * 0.25 + w3 * 0.375, w3, p.y * 2.0), w1, p.x * 2.0);
|
||||
}
|
||||
|
||||
if (dist < 1.0 - pixel_size / 2) {
|
||||
return r;
|
||||
}
|
||||
|
||||
return mix(r, w4, (dist + pixel_size / 2 - 1.0) / pixel_size);
|
||||
}
|
||||
|
||||
if (P(0x7E,0x2A) || P(0xEF,0xAB)) {
|
||||
float dist = p.y + 2.0 * p.x;
|
||||
float pixel_size = length(1.0 / (output_resolution / input_resolution)) * sqrt(5.0);
|
||||
|
||||
if (p.y + 2.0 * p.x > 1.0 + pixel_size / 2) {
|
||||
return w4;
|
||||
}
|
||||
|
||||
vec4 r;
|
||||
|
||||
if (is_different(w0, w1) || is_different(w0, w3)) {
|
||||
r = mix(w1, w3, p.y - p.x + 0.5);
|
||||
}
|
||||
else {
|
||||
r = mix(mix(w1 * 0.375 + w0 * 0.25 + w3 * 0.375, w3, p.y * 2.0), w1, p.x * 2.0);
|
||||
}
|
||||
|
||||
if (dist < 1.0 - pixel_size / 2) {
|
||||
return r;
|
||||
}
|
||||
|
||||
return mix(r, w4, (dist + pixel_size / 2 - 1.0) / pixel_size);
|
||||
}
|
||||
|
||||
if (P(0x1B,0x03) || P(0x4F,0x43) || P(0x8B,0x83) || P(0x6B,0x43)) {
|
||||
return mix(w4, w3, 0.5 - p.x);
|
||||
}
|
||||
|
||||
if (P(0x4B,0x09) || P(0x8B,0x89) || P(0x1F,0x19) || P(0x3B,0x19)) {
|
||||
return mix(w4, w1, 0.5 - p.y);
|
||||
}
|
||||
|
||||
if (P(0xFB,0x6A) || P(0x6F,0x6E) || P(0x3F,0x3E) || P(0xFB,0xFA) ||
|
||||
P(0xDF,0xDE) || P(0xDF,0x1E)) {
|
||||
return mix(w4, w0, (1.0 - p.x - p.y) / 2.0);
|
||||
}
|
||||
|
||||
if (P(0x4F,0x4B) || P(0x9F,0x1B) || P(0x2F,0x0B) ||
|
||||
P(0xBE,0x0A) || P(0xEE,0x0A) || P(0x7E,0x0A) || P(0xEB,0x4B) ||
|
||||
P(0x3B,0x1B)) {
|
||||
float dist = p.x + p.y;
|
||||
float pixel_size = length(1.0 / (output_resolution / input_resolution));
|
||||
|
||||
if (dist > 0.5 + pixel_size / 2) {
|
||||
return w4;
|
||||
}
|
||||
|
||||
vec4 r;
|
||||
if (is_different(w0, w1) || is_different(w0, w3)) {
|
||||
r = mix(w1, w3, p.y - p.x + 0.5);
|
||||
}
|
||||
else {
|
||||
r = mix(mix(w1 * 0.375 + w0 * 0.25 + w3 * 0.375, w3, p.y * 2.0), w1, p.x * 2.0);
|
||||
}
|
||||
|
||||
if (dist < 0.5 - pixel_size / 2) {
|
||||
return r;
|
||||
}
|
||||
|
||||
return mix(r, w4, (dist + pixel_size / 2 - 0.5) / pixel_size);
|
||||
}
|
||||
|
||||
if (P(0x0B,0x01)) {
|
||||
return mix(mix(w4, w3, 0.5 - p.x), mix(w1, (w1 + w3) / 2.0, 0.5 - p.x), 0.5 - p.y);
|
||||
}
|
||||
|
||||
if (P(0x0B,0x00)) {
|
||||
return mix(mix(w4, w3, 0.5 - p.x), mix(w1, w0, 0.5 - p.x), 0.5 - p.y);
|
||||
}
|
||||
|
||||
float dist = p.x + p.y;
|
||||
float pixel_size = length(1.0 / (output_resolution / input_resolution));
|
||||
|
||||
if (dist > 0.5 + pixel_size / 2) {
|
||||
return w4;
|
||||
}
|
||||
|
||||
/* We need more samples to "solve" this diagonal */
|
||||
vec4 x0 = texture2D(image, position + vec2( -o.x * 2.0, -o.y * 2.0));
|
||||
vec4 x1 = texture2D(image, position + vec2( -o.x , -o.y * 2.0));
|
||||
vec4 x2 = texture2D(image, position + vec2( 0.0 , -o.y * 2.0));
|
||||
vec4 x3 = texture2D(image, position + vec2( o.x , -o.y * 2.0));
|
||||
vec4 x4 = texture2D(image, position + vec2( -o.x * 2.0, -o.y ));
|
||||
vec4 x5 = texture2D(image, position + vec2( -o.x * 2.0, 0.0 ));
|
||||
vec4 x6 = texture2D(image, position + vec2( -o.x * 2.0, o.y ));
|
||||
|
||||
if (is_different(x0, w4)) pattern |= 1 << 8;
|
||||
if (is_different(x1, w4)) pattern |= 1 << 9;
|
||||
if (is_different(x2, w4)) pattern |= 1 << 10;
|
||||
if (is_different(x3, w4)) pattern |= 1 << 11;
|
||||
if (is_different(x4, w4)) pattern |= 1 << 12;
|
||||
if (is_different(x5, w4)) pattern |= 1 << 13;
|
||||
if (is_different(x6, w4)) pattern |= 1 << 14;
|
||||
|
||||
int diagonal_bias = -7;
|
||||
while (pattern != 0) {
|
||||
diagonal_bias += pattern & 1;
|
||||
pattern >>= 1;
|
||||
}
|
||||
|
||||
if (diagonal_bias <= 0) {
|
||||
vec4 r = mix(w1, w3, p.y - p.x + 0.5);
|
||||
if (dist < 0.5 - pixel_size / 2) {
|
||||
return r;
|
||||
}
|
||||
return mix(r, w4, (dist + pixel_size / 2 - 0.5) / pixel_size);
|
||||
}
|
||||
|
||||
return w4;
|
||||
}
|
||||
|
||||
void main() {
|
||||
gl_FragColor = scale(tex, texCoord, texSize, outputSize);
|
||||
}
|
|
@ -52,7 +52,7 @@ static inline void _printPSR(struct CLIDebuggerBackend* be, union PSR psr) {
|
|||
}
|
||||
|
||||
static void _disassemble(struct CLIDebuggerSystem* debugger, struct CLIDebugVector* dv) {
|
||||
struct ARMCore* cpu = debugger->p->d.core->cpu;
|
||||
struct ARMCore* cpu = debugger->p->d.p->core->cpu;
|
||||
_disassembleMode(debugger->p, dv, cpu->executionMode);
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,7 @@ static void _disassembleThumb(struct CLIDebugger* debugger, struct CLIDebugVecto
|
|||
}
|
||||
|
||||
static void _disassembleMode(struct CLIDebugger* debugger, struct CLIDebugVector* dv, enum ExecutionMode mode) {
|
||||
struct ARMCore* cpu = debugger->d.core->cpu;
|
||||
struct ARMCore* cpu = debugger->d.p->core->cpu;
|
||||
uint32_t address;
|
||||
int size;
|
||||
int wordSize;
|
||||
|
@ -98,7 +98,7 @@ static void _disassembleMode(struct CLIDebugger* debugger, struct CLIDebugVector
|
|||
|
||||
static inline uint32_t _printLine(struct CLIDebugger* debugger, uint32_t address, enum ExecutionMode mode) {
|
||||
struct CLIDebuggerBackend* be = debugger->backend;
|
||||
struct mCore* core = debugger->d.core;
|
||||
struct mCore* core = debugger->d.p->core;
|
||||
char disassembly[64];
|
||||
struct ARMInstructionInfo info;
|
||||
address &= ~(WORD_SIZE_THUMB - 1);
|
||||
|
@ -130,7 +130,7 @@ static inline uint32_t _printLine(struct CLIDebugger* debugger, uint32_t address
|
|||
|
||||
static void _printStatus(struct CLIDebuggerSystem* debugger) {
|
||||
struct CLIDebuggerBackend* be = debugger->p->backend;
|
||||
struct ARMCore* cpu = debugger->p->d.core->cpu;
|
||||
struct ARMCore* cpu = debugger->p->d.p->core->cpu;
|
||||
int r;
|
||||
for (r = 0; r < 16; r += 4) {
|
||||
be->printf(be, "%sr%i: %08X %sr%i: %08X %sr%i: %08X %sr%i: %08X\n",
|
||||
|
@ -141,7 +141,7 @@ static void _printStatus(struct CLIDebuggerSystem* debugger) {
|
|||
}
|
||||
be->printf(be, "cpsr: ");
|
||||
_printPSR(be, cpu->cpsr);
|
||||
be->printf(be, "Cycle: %" PRIu64 "\n", mTimingGlobalTime(debugger->p->d.core->timing));
|
||||
be->printf(be, "Cycle: %" PRIu64 "\n", mTimingGlobalTime(debugger->p->d.p->core->timing));
|
||||
int instructionLength;
|
||||
enum ExecutionMode mode = cpu->cpsr.t;
|
||||
if (mode == MODE_ARM) {
|
||||
|
@ -159,7 +159,7 @@ static void _setBreakpointARM(struct CLIDebugger* debugger, struct CLIDebugVecto
|
|||
return;
|
||||
}
|
||||
uint32_t address = dv->intValue;
|
||||
ssize_t id = ARMDebuggerSetSoftwareBreakpoint(debugger->d.platform, address, MODE_ARM);
|
||||
ssize_t id = ARMDebuggerSetSoftwareBreakpoint(debugger->d.p->platform, &debugger->d, address, MODE_ARM);
|
||||
if (id > 0) {
|
||||
debugger->backend->printf(debugger->backend, INFO_BREAKPOINT_ADDED, id);
|
||||
}
|
||||
|
@ -172,7 +172,7 @@ static void _setBreakpointThumb(struct CLIDebugger* debugger, struct CLIDebugVec
|
|||
return;
|
||||
}
|
||||
uint32_t address = dv->intValue;
|
||||
ssize_t id = ARMDebuggerSetSoftwareBreakpoint(debugger->d.platform, address, MODE_THUMB);
|
||||
ssize_t id = ARMDebuggerSetSoftwareBreakpoint(debugger->d.p->platform, &debugger->d, address, MODE_THUMB);
|
||||
if (id > 0) {
|
||||
debugger->backend->printf(debugger->backend, INFO_BREAKPOINT_ADDED, id);
|
||||
}
|
||||
|
|
|
@ -187,16 +187,18 @@ static struct ARMDebugBreakpoint* _lookupBreakpoint(struct ARMDebugBreakpointLis
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void _destroyBreakpoint(struct ARMDebugBreakpoint* breakpoint) {
|
||||
static void _destroyBreakpoint(struct mDebugger* debugger, struct ARMDebugBreakpoint* breakpoint) {
|
||||
if (breakpoint->d.condition) {
|
||||
parseFree(breakpoint->d.condition);
|
||||
}
|
||||
TableRemove(&debugger->pointOwner, breakpoint->d.id);
|
||||
}
|
||||
|
||||
static void _destroyWatchpoint(struct mWatchpoint* watchpoint) {
|
||||
static void _destroyWatchpoint(struct mDebugger* debugger, struct mWatchpoint* watchpoint) {
|
||||
if (watchpoint->condition) {
|
||||
parseFree(watchpoint->condition);
|
||||
}
|
||||
TableRemove(&debugger->pointOwner, watchpoint->id);
|
||||
}
|
||||
|
||||
static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform* d) {
|
||||
|
@ -220,7 +222,8 @@ static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform* d) {
|
|||
struct mDebuggerEntryInfo info = {
|
||||
.address = breakpoint->d.address,
|
||||
.type.bp.breakType = BREAKPOINT_HARDWARE,
|
||||
.pointId = breakpoint->d.id
|
||||
.pointId = breakpoint->d.id,
|
||||
.target = TableLookup(&d->p->pointOwner, breakpoint->d.id)
|
||||
};
|
||||
mDebuggerEnter(d->p, DEBUGGER_ENTER_BREAKPOINT, &info);
|
||||
}
|
||||
|
@ -230,11 +233,11 @@ static void ARMDebuggerDeinit(struct mDebuggerPlatform* platform);
|
|||
|
||||
static void ARMDebuggerEnter(struct mDebuggerPlatform* d, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info);
|
||||
|
||||
static ssize_t ARMDebuggerSetBreakpoint(struct mDebuggerPlatform*, const struct mBreakpoint*);
|
||||
static ssize_t ARMDebuggerSetBreakpoint(struct mDebuggerPlatform*, struct mDebuggerModule* owner, const struct mBreakpoint*);
|
||||
static bool ARMDebuggerClearBreakpoint(struct mDebuggerPlatform*, ssize_t id);
|
||||
static void ARMDebuggerListBreakpoints(struct mDebuggerPlatform*, struct mBreakpointList*);
|
||||
static ssize_t ARMDebuggerSetWatchpoint(struct mDebuggerPlatform*, const struct mWatchpoint*);
|
||||
static void ARMDebuggerListWatchpoints(struct mDebuggerPlatform*, struct mWatchpointList*);
|
||||
static void ARMDebuggerListBreakpoints(struct mDebuggerPlatform*, struct mDebuggerModule* owner, struct mBreakpointList*);
|
||||
static ssize_t ARMDebuggerSetWatchpoint(struct mDebuggerPlatform*, struct mDebuggerModule* owner, const struct mWatchpoint*);
|
||||
static void ARMDebuggerListWatchpoints(struct mDebuggerPlatform*, struct mDebuggerModule* owner, struct mWatchpointList*);
|
||||
static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform*);
|
||||
static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform*);
|
||||
static void ARMDebuggerTrace(struct mDebuggerPlatform*, char* out, size_t* length);
|
||||
|
@ -291,12 +294,12 @@ void ARMDebuggerDeinit(struct mDebuggerPlatform* platform) {
|
|||
|
||||
size_t i;
|
||||
for (i = 0; i < ARMDebugBreakpointListSize(&debugger->breakpoints); ++i) {
|
||||
_destroyBreakpoint(ARMDebugBreakpointListGetPointer(&debugger->breakpoints, i));
|
||||
_destroyBreakpoint(debugger->d.p, ARMDebugBreakpointListGetPointer(&debugger->breakpoints, i));
|
||||
}
|
||||
ARMDebugBreakpointListDeinit(&debugger->breakpoints);
|
||||
|
||||
for (i = 0; i < mWatchpointListSize(&debugger->watchpoints); ++i) {
|
||||
_destroyWatchpoint(mWatchpointListGetPointer(&debugger->watchpoints, i));
|
||||
_destroyWatchpoint(debugger->d.p, mWatchpointListGetPointer(&debugger->watchpoints, i));
|
||||
}
|
||||
ARMDebugBreakpointListDeinit(&debugger->swBreakpoints);
|
||||
mWatchpointListDeinit(&debugger->watchpoints);
|
||||
|
@ -323,12 +326,9 @@ static void ARMDebuggerEnter(struct mDebuggerPlatform* platform, enum mDebuggerE
|
|||
}
|
||||
}
|
||||
}
|
||||
if (debugger->d.p->entered) {
|
||||
debugger->d.p->entered(debugger->d.p, reason, info);
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t ARMDebuggerSetSoftwareBreakpoint(struct mDebuggerPlatform* d, uint32_t address, enum ExecutionMode mode) {
|
||||
ssize_t ARMDebuggerSetSoftwareBreakpoint(struct mDebuggerPlatform* d, struct mDebuggerModule* owner, uint32_t address, enum ExecutionMode mode) {
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
uint32_t opcode;
|
||||
if (!debugger->setSoftwareBreakpoint || !debugger->setSoftwareBreakpoint(debugger, address, mode, &opcode)) {
|
||||
|
@ -345,11 +345,12 @@ ssize_t ARMDebuggerSetSoftwareBreakpoint(struct mDebuggerPlatform* d, uint32_t a
|
|||
breakpoint->d.type = BREAKPOINT_SOFTWARE;
|
||||
breakpoint->sw.opcode = opcode;
|
||||
breakpoint->sw.mode = mode;
|
||||
TableInsert(&debugger->d.p->pointOwner, id, owner);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
static ssize_t ARMDebuggerSetBreakpoint(struct mDebuggerPlatform* d, const struct mBreakpoint* info) {
|
||||
static ssize_t ARMDebuggerSetBreakpoint(struct mDebuggerPlatform* d, struct mDebuggerModule* owner, const struct mBreakpoint* info) {
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->breakpoints);
|
||||
ssize_t id = debugger->nextId;
|
||||
|
@ -357,6 +358,7 @@ static ssize_t ARMDebuggerSetBreakpoint(struct mDebuggerPlatform* d, const struc
|
|||
breakpoint->d = *info;
|
||||
breakpoint->d.address &= ~1; // Clear Thumb bit since it's not part of a valid address
|
||||
breakpoint->d.id = id;
|
||||
TableInsert(&debugger->d.p->pointOwner, id, owner);
|
||||
if (info->type == BREAKPOINT_SOFTWARE) {
|
||||
// TODO
|
||||
abort();
|
||||
|
@ -371,7 +373,7 @@ static bool ARMDebuggerClearBreakpoint(struct mDebuggerPlatform* d, ssize_t id)
|
|||
struct ARMDebugBreakpointList* breakpoints = &debugger->breakpoints;
|
||||
for (i = 0; i < ARMDebugBreakpointListSize(breakpoints); ++i) {
|
||||
if (ARMDebugBreakpointListGetPointer(breakpoints, i)->d.id == id) {
|
||||
_destroyBreakpoint(ARMDebugBreakpointListGetPointer(breakpoints, i));
|
||||
_destroyBreakpoint(debugger->d.p, ARMDebugBreakpointListGetPointer(breakpoints, i));
|
||||
ARMDebugBreakpointListShift(breakpoints, i, 1);
|
||||
return true;
|
||||
}
|
||||
|
@ -391,7 +393,7 @@ static bool ARMDebuggerClearBreakpoint(struct mDebuggerPlatform* d, ssize_t id)
|
|||
struct mWatchpointList* watchpoints = &debugger->watchpoints;
|
||||
for (i = 0; i < mWatchpointListSize(watchpoints); ++i) {
|
||||
if (mWatchpointListGetPointer(watchpoints, i)->id == id) {
|
||||
_destroyWatchpoint(mWatchpointListGetPointer(watchpoints, i));
|
||||
_destroyWatchpoint(debugger->d.p, mWatchpointListGetPointer(watchpoints, i));
|
||||
mWatchpointListShift(watchpoints, i, 1);
|
||||
if (!mWatchpointListSize(&debugger->watchpoints)) {
|
||||
ARMDebuggerRemoveMemoryShim(debugger);
|
||||
|
@ -402,7 +404,7 @@ static bool ARMDebuggerClearBreakpoint(struct mDebuggerPlatform* d, ssize_t id)
|
|||
return false;
|
||||
}
|
||||
|
||||
static void ARMDebuggerListBreakpoints(struct mDebuggerPlatform* d, struct mBreakpointList* list) {
|
||||
static void ARMDebuggerListBreakpoints(struct mDebuggerPlatform* d, struct mDebuggerModule* owner, struct mBreakpointList* list) {
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
mBreakpointListClear(list);
|
||||
size_t i, s;
|
||||
|
@ -411,10 +413,20 @@ static void ARMDebuggerListBreakpoints(struct mDebuggerPlatform* d, struct mBrea
|
|||
struct ARMDebugBreakpoint* sw = NULL;
|
||||
if (i < ARMDebugBreakpointListSize(&debugger->breakpoints)) {
|
||||
hw = ARMDebugBreakpointListGetPointer(&debugger->breakpoints, i);
|
||||
if (owner && TableLookup(&debugger->d.p->pointOwner, hw->d.id) != owner) {
|
||||
hw = NULL;
|
||||
}
|
||||
}
|
||||
if (s < ARMDebugBreakpointListSize(&debugger->swBreakpoints)) {
|
||||
sw = ARMDebugBreakpointListGetPointer(&debugger->swBreakpoints, s);
|
||||
if (owner && TableLookup(&debugger->d.p->pointOwner, sw->d.id) != owner) {
|
||||
sw = NULL;
|
||||
}
|
||||
}
|
||||
if (!hw && !sw) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct mBreakpoint* b = mBreakpointListAppend(list);
|
||||
if (hw && sw) {
|
||||
if (hw->d.id < sw->d.id) {
|
||||
|
@ -430,8 +442,6 @@ static void ARMDebuggerListBreakpoints(struct mDebuggerPlatform* d, struct mBrea
|
|||
} else if (sw) {
|
||||
*b = sw->d;
|
||||
++s;
|
||||
} else {
|
||||
abort(); // Should be unreachable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -441,7 +451,7 @@ static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform* d) {
|
|||
return ARMDebugBreakpointListSize(&debugger->breakpoints) || mWatchpointListSize(&debugger->watchpoints) || debugger->stackTraceMode != STACK_TRACE_DISABLED;
|
||||
}
|
||||
|
||||
static ssize_t ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, const struct mWatchpoint* info) {
|
||||
static ssize_t ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, struct mDebuggerModule* owner, const struct mWatchpoint* info) {
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
if (!mWatchpointListSize(&debugger->watchpoints)) {
|
||||
ARMDebuggerInstallMemoryShim(debugger);
|
||||
|
@ -451,13 +461,25 @@ static ssize_t ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, const struc
|
|||
++debugger->nextId;
|
||||
*watchpoint = *info;
|
||||
watchpoint->id = id;
|
||||
TableInsert(&debugger->d.p->pointOwner, id, owner);
|
||||
return id;
|
||||
}
|
||||
|
||||
static void ARMDebuggerListWatchpoints(struct mDebuggerPlatform* d, struct mWatchpointList* list) {
|
||||
static void ARMDebuggerListWatchpoints(struct mDebuggerPlatform* d, struct mDebuggerModule* owner, struct mWatchpointList* list) {
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
mWatchpointListClear(list);
|
||||
mWatchpointListCopy(list, &debugger->watchpoints);
|
||||
if (owner) {
|
||||
size_t i;
|
||||
for (i = 0; i < mWatchpointListSize(&debugger->watchpoints); ++i) {
|
||||
struct mWatchpoint* point = mWatchpointListGetPointer(&debugger->watchpoints, i);
|
||||
if (TableLookup(&debugger->d.p->pointOwner, point->id) != owner) {
|
||||
continue;
|
||||
}
|
||||
memcpy(mWatchpointListAppend(list), point, sizeof(*point));
|
||||
}
|
||||
} else {
|
||||
mWatchpointListCopy(list, &debugger->watchpoints);
|
||||
}
|
||||
}
|
||||
|
||||
static void ARMDebuggerTrace(struct mDebuggerPlatform* d, char* out, size_t* length) {
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, struct mDebuggerEntryInfo* info, enum mWatchpointType type, uint32_t newValue, int width);
|
||||
static void _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, enum mWatchpointType type, uint32_t newValue, int width);
|
||||
|
||||
#define FIND_DEBUGGER(DEBUGGER, CPU) \
|
||||
do { \
|
||||
|
@ -39,10 +39,7 @@ static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, st
|
|||
static RETURN DebuggerShim_ ## NAME TYPES { \
|
||||
struct ARMDebugger* debugger; \
|
||||
FIND_DEBUGGER(debugger, cpu); \
|
||||
struct mDebuggerEntryInfo info; \
|
||||
if (_checkWatchpoints(debugger, address, &info, WATCHPOINT_READ, 0, WIDTH)) { \
|
||||
mDebuggerEnter(debugger->d.p, DEBUGGER_ENTER_WATCHPOINT, &info); \
|
||||
} \
|
||||
_checkWatchpoints(debugger, address, WATCHPOINT_READ, 0, WIDTH); \
|
||||
return debugger->originalMemory.NAME(cpu, __VA_ARGS__); \
|
||||
}
|
||||
|
||||
|
@ -50,10 +47,7 @@ static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, st
|
|||
static RETURN DebuggerShim_ ## NAME TYPES { \
|
||||
struct ARMDebugger* debugger; \
|
||||
FIND_DEBUGGER(debugger, cpu); \
|
||||
struct mDebuggerEntryInfo info; \
|
||||
if (_checkWatchpoints(debugger, address, &info, WATCHPOINT_WRITE, value, WIDTH)) { \
|
||||
mDebuggerEnter(debugger->d.p, DEBUGGER_ENTER_WATCHPOINT, &info); \
|
||||
} \
|
||||
_checkWatchpoints(debugger, address, WATCHPOINT_WRITE, value, WIDTH); \
|
||||
return debugger->originalMemory.NAME(cpu, __VA_ARGS__); \
|
||||
}
|
||||
|
||||
|
@ -73,10 +67,7 @@ static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, st
|
|||
} \
|
||||
unsigned i; \
|
||||
for (i = 0; i < popcount; ++i) { \
|
||||
struct mDebuggerEntryInfo info; \
|
||||
if (_checkWatchpoints(debugger, base + 4 * i, &info, ACCESS_TYPE, 0, 4)) { \
|
||||
mDebuggerEnter(debugger->d.p, DEBUGGER_ENTER_WATCHPOINT, &info); \
|
||||
} \
|
||||
_checkWatchpoints(debugger, base + 4 * i, ACCESS_TYPE, 0, 4); \
|
||||
} \
|
||||
return debugger->originalMemory.NAME(cpu, address, mask, direction, cycleCounter); \
|
||||
}
|
||||
|
@ -91,7 +82,7 @@ CREATE_MULTIPLE_WATCHPOINT_SHIM(loadMultiple, WATCHPOINT_READ)
|
|||
CREATE_MULTIPLE_WATCHPOINT_SHIM(storeMultiple, WATCHPOINT_WRITE)
|
||||
CREATE_SHIM(setActiveRegion, void, (struct ARMCore* cpu, uint32_t address), address)
|
||||
|
||||
static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, struct mDebuggerEntryInfo* info, enum mWatchpointType type, uint32_t newValue, int width) {
|
||||
static void _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, enum mWatchpointType type, uint32_t newValue, int width) {
|
||||
struct mWatchpoint* watchpoint;
|
||||
size_t i;
|
||||
uint32_t minAddress = address & ~(width - 1);
|
||||
|
@ -124,16 +115,18 @@ static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, st
|
|||
if ((watchpoint->type & WATCHPOINT_CHANGE) && newValue == oldValue) {
|
||||
continue;
|
||||
}
|
||||
info->type.wp.oldValue = oldValue;
|
||||
info->type.wp.newValue = newValue;
|
||||
info->address = address;
|
||||
info->type.wp.watchType = watchpoint->type;
|
||||
info->type.wp.accessType = type;
|
||||
info->pointId = watchpoint->id;
|
||||
return true;
|
||||
|
||||
struct mDebuggerEntryInfo info;
|
||||
info.type.wp.oldValue = oldValue;
|
||||
info.type.wp.newValue = newValue;
|
||||
info.address = address;
|
||||
info.type.wp.watchType = watchpoint->type;
|
||||
info.type.wp.accessType = type;
|
||||
info.pointId = watchpoint->id;
|
||||
info.target = TableLookup(&debugger->d.p->pointOwner, watchpoint->id);
|
||||
mDebuggerEnter(debugger->d.p, DEBUGGER_ENTER_WATCHPOINT, &info);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ARMDebuggerInstallMemoryShim(struct ARMDebugger* debugger) {
|
||||
|
|
|
@ -663,7 +663,9 @@ DEFINE_INSTRUCTION_ARM(MRC, ARM_STUB)
|
|||
|
||||
// Begin miscellaneous definitions
|
||||
|
||||
DEFINE_INSTRUCTION_ARM(BKPT, cpu->irqh.bkpt32(cpu, ((opcode >> 4) & 0xFFF0) | (opcode & 0xF))); // Not strictly in ARMv4T, but here for convenience
|
||||
DEFINE_INSTRUCTION_ARM(BKPT,
|
||||
cpu->irqh.bkpt32(cpu, ((opcode >> 4) & 0xFFF0) | (opcode & 0xF));
|
||||
currentCycles = 0;); // Not strictly in ARMv4T, but here for convenience
|
||||
DEFINE_INSTRUCTION_ARM(ILL, ARM_ILL) // Illegal opcode
|
||||
|
||||
DEFINE_INSTRUCTION_ARM(MSR,
|
||||
|
|
|
@ -381,7 +381,9 @@ DEFINE_LOAD_STORE_MULTIPLE_THUMB(PUSHR,
|
|||
cpu->gprs[ARM_SP] = address)
|
||||
|
||||
DEFINE_INSTRUCTION_THUMB(ILL, ARM_ILL)
|
||||
DEFINE_INSTRUCTION_THUMB(BKPT, cpu->irqh.bkpt16(cpu, opcode & 0xFF);)
|
||||
DEFINE_INSTRUCTION_THUMB(BKPT,
|
||||
cpu->irqh.bkpt16(cpu, opcode & 0xFF);
|
||||
currentCycles = 0;) // Not strictly in ARMv4T, but here for convenience
|
||||
DEFINE_INSTRUCTION_THUMB(B,
|
||||
int16_t immediate = (opcode & 0x07FF) << 5;
|
||||
cpu->gprs[ARM_PC] += (((int32_t) immediate) >> 4);
|
||||
|
@ -401,11 +403,7 @@ DEFINE_INSTRUCTION_THUMB(BL2,
|
|||
DEFINE_INSTRUCTION_THUMB(BX,
|
||||
int rm = (opcode >> 3) & 0xF;
|
||||
_ARMSetMode(cpu, cpu->gprs[rm] & 0x00000001);
|
||||
int misalign = 0;
|
||||
if (rm == ARM_PC) {
|
||||
misalign = cpu->gprs[rm] & 0x00000002;
|
||||
}
|
||||
cpu->gprs[ARM_PC] = (cpu->gprs[rm] & 0xFFFFFFFE) - misalign;
|
||||
cpu->gprs[ARM_PC] = cpu->gprs[rm] & 0xFFFFFFFE;
|
||||
if (cpu->executionMode == MODE_THUMB) {
|
||||
currentCycles += ThumbWritePC(cpu);
|
||||
} else {
|
||||
|
|
|
@ -34,12 +34,15 @@ void mCacheSetDeinit(struct mCacheSet* cache) {
|
|||
for (i = 0; i < mMapCacheSetSize(&cache->maps); ++i) {
|
||||
mMapCacheDeinit(mMapCacheSetGetPointer(&cache->maps, i));
|
||||
}
|
||||
mMapCacheSetDeinit(&cache->maps);
|
||||
for (i = 0; i < mBitmapCacheSetSize(&cache->bitmaps); ++i) {
|
||||
mBitmapCacheDeinit(mBitmapCacheSetGetPointer(&cache->bitmaps, i));
|
||||
}
|
||||
mBitmapCacheSetDeinit(&cache->bitmaps);
|
||||
for (i = 0; i < mTileCacheSetSize(&cache->tiles); ++i) {
|
||||
mTileCacheDeinit(mTileCacheSetGetPointer(&cache->tiles, i));
|
||||
}
|
||||
mTileCacheSetDeinit(&cache->tiles);
|
||||
}
|
||||
|
||||
void mCacheSetAssignVRAM(struct mCacheSet* cache, void* vram) {
|
||||
|
|
|
@ -489,8 +489,10 @@ bool mCheatParseEZFChtFile(struct mCheatDevice* device, struct VFile* vf) {
|
|||
return false;
|
||||
}
|
||||
char* name = gbkToUtf8(&cheat[1], end - cheat - 1);
|
||||
strncpy(cheatName, name, sizeof(cheatName) - 1);
|
||||
free(name);
|
||||
if (name) {
|
||||
strncpy(cheatName, name, sizeof(cheatName) - 1);
|
||||
free(name);
|
||||
}
|
||||
cheatNameLength = strlen(cheatName);
|
||||
continue;
|
||||
}
|
||||
|
@ -501,7 +503,10 @@ bool mCheatParseEZFChtFile(struct mCheatDevice* device, struct VFile* vf) {
|
|||
}
|
||||
if (strncmp(cheat, "ON", eq - cheat) != 0) {
|
||||
char* subname = gbkToUtf8(cheat, eq - cheat);
|
||||
snprintf(&cheatName[cheatNameLength], sizeof(cheatName) - cheatNameLength - 1, ": %s", subname);
|
||||
if (subname) {
|
||||
snprintf(&cheatName[cheatNameLength], sizeof(cheatName) - cheatNameLength - 1, ": %s", subname);
|
||||
free(subname);
|
||||
}
|
||||
}
|
||||
set = device->createSet(device, cheatName);
|
||||
set->enabled = false;
|
||||
|
|
|
@ -79,7 +79,7 @@ static const char* _lookupValue(const struct mCoreConfig* config, const char* ke
|
|||
|
||||
static bool _lookupCharValue(const struct mCoreConfig* config, const char* key, char** out) {
|
||||
const char* value = _lookupValue(config, key);
|
||||
if (!value) {
|
||||
if (!value || !value[0]) {
|
||||
return false;
|
||||
}
|
||||
if (*out) {
|
||||
|
@ -169,14 +169,14 @@ void mCoreConfigDeinit(struct mCoreConfig* config) {
|
|||
|
||||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||
bool mCoreConfigLoad(struct mCoreConfig* config) {
|
||||
char path[PATH_MAX];
|
||||
char path[PATH_MAX + 1];
|
||||
mCoreConfigDirectory(path, PATH_MAX);
|
||||
strncat(path, PATH_SEP "config.ini", PATH_MAX - strlen(path));
|
||||
return mCoreConfigLoadPath(config, path);
|
||||
}
|
||||
|
||||
bool mCoreConfigSave(const struct mCoreConfig* config) {
|
||||
char path[PATH_MAX];
|
||||
char path[PATH_MAX + 1];
|
||||
mCoreConfigDirectory(path, PATH_MAX);
|
||||
strncat(path, PATH_SEP "config.ini", PATH_MAX - strlen(path));
|
||||
return mCoreConfigSavePath(config, path);
|
||||
|
@ -279,8 +279,7 @@ void mCoreConfigDirectory(char* out, size_t outLength) {
|
|||
void mCoreConfigPortablePath(char* out, size_t outLength) {
|
||||
#ifdef _WIN32
|
||||
wchar_t wpath[MAX_PATH];
|
||||
HMODULE hModule = GetModuleHandleW(NULL);
|
||||
GetModuleFileNameW(hModule, wpath, MAX_PATH);
|
||||
GetModuleFileNameW(NULL, wpath, MAX_PATH);
|
||||
PathRemoveFileSpecW(wpath);
|
||||
if (PATH_SEP[0] != '\\') {
|
||||
WCHAR* pathSep;
|
||||
|
@ -304,7 +303,7 @@ void mCoreConfigPortablePath(char* out, size_t outLength) {
|
|||
CFRelease(suburl);
|
||||
}
|
||||
#endif
|
||||
strncat(out, PATH_SEP "portable.ini", outLength - strlen(out));
|
||||
strncat(out, PATH_SEP "portable.ini", outLength - strlen(out) - 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -407,6 +406,7 @@ void mCoreConfigMap(const struct mCoreConfig* config, struct mCoreOptions* opts)
|
|||
_lookupIntValue(config, "frameskip", &opts->frameskip);
|
||||
_lookupIntValue(config, "volume", &opts->volume);
|
||||
_lookupIntValue(config, "rewindBufferCapacity", &opts->rewindBufferCapacity);
|
||||
_lookupIntValue(config, "rewindBufferInterval", &opts->rewindBufferInterval);
|
||||
_lookupFloatValue(config, "fpsTarget", &opts->fpsTarget);
|
||||
unsigned audioBuffers;
|
||||
if (_lookupUIntValue(config, "audioBuffers", &audioBuffers)) {
|
||||
|
@ -449,6 +449,7 @@ void mCoreConfigLoadDefaults(struct mCoreConfig* config, const struct mCoreOptio
|
|||
ConfigurationSetIntValue(&config->defaultsTable, 0, "frameskip", opts->frameskip);
|
||||
ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindEnable", opts->rewindEnable);
|
||||
ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferCapacity", opts->rewindBufferCapacity);
|
||||
ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferInterval", opts->rewindBufferInterval);
|
||||
ConfigurationSetFloatValue(&config->defaultsTable, 0, "fpsTarget", opts->fpsTarget);
|
||||
ConfigurationSetUIntValue(&config->defaultsTable, 0, "audioBuffers", opts->audioBuffers);
|
||||
ConfigurationSetUIntValue(&config->defaultsTable, 0, "sampleRate", opts->sampleRate);
|
||||
|
|
|
@ -87,7 +87,7 @@ struct mCore* mCoreCreate(enum mPlatform platform) {
|
|||
}
|
||||
|
||||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||
#include <mgba-util/png-io.h>
|
||||
#include <mgba-util/image/png-io.h>
|
||||
|
||||
#ifdef PSP2
|
||||
#include <psp2/photoexport.h>
|
||||
|
@ -361,11 +361,11 @@ bool mCoreTakeScreenshotVF(struct mCore* core, struct VFile* vf) {
|
|||
size_t stride;
|
||||
const void* pixels = 0;
|
||||
unsigned width, height;
|
||||
core->desiredVideoDimensions(core, &width, &height);
|
||||
core->currentVideoSize(core, &width, &height);
|
||||
core->getPixels(core, &pixels, &stride);
|
||||
png_structp png = PNGWriteOpen(vf);
|
||||
png_infop info = PNGWriteHeader(png, width, height);
|
||||
bool success = PNGWritePixels(png, width, height, stride, pixels);
|
||||
png_infop info = PNGWriteHeader(png, width, height, mCOLOR_NATIVE);
|
||||
bool success = PNGWritePixels(png, width, height, stride, pixels, mCOLOR_NATIVE);
|
||||
PNGWriteClose(png, info);
|
||||
return success;
|
||||
#else
|
||||
|
|
|
@ -116,10 +116,15 @@ struct VFile* mDirectorySetOpenSuffix(struct mDirectorySet* dirs, struct VDir* d
|
|||
}
|
||||
|
||||
void mDirectorySetMapOptions(struct mDirectorySet* dirs, const struct mCoreOptions* opts) {
|
||||
char abspath[PATH_MAX + 1];
|
||||
char configDir[PATH_MAX + 1];
|
||||
mCoreConfigDirectory(configDir, sizeof(configDir));
|
||||
|
||||
if (opts->savegamePath) {
|
||||
struct VDir* dir = VDirOpen(opts->savegamePath);
|
||||
if (!dir && VDirCreate(opts->savegamePath)) {
|
||||
dir = VDirOpen(opts->savegamePath);
|
||||
makeAbsolute(opts->savegamePath, configDir, abspath);
|
||||
struct VDir* dir = VDirOpen(abspath);
|
||||
if (!dir && VDirCreate(abspath)) {
|
||||
dir = VDirOpen(abspath);
|
||||
}
|
||||
if (dir) {
|
||||
if (dirs->save && dirs->save != dirs->base) {
|
||||
|
@ -130,9 +135,10 @@ void mDirectorySetMapOptions(struct mDirectorySet* dirs, const struct mCoreOptio
|
|||
}
|
||||
|
||||
if (opts->savestatePath) {
|
||||
struct VDir* dir = VDirOpen(opts->savestatePath);
|
||||
if (!dir && VDirCreate(opts->savestatePath)) {
|
||||
dir = VDirOpen(opts->savestatePath);
|
||||
makeAbsolute(opts->savestatePath, configDir, abspath);
|
||||
struct VDir* dir = VDirOpen(abspath);
|
||||
if (!dir && VDirCreate(abspath)) {
|
||||
dir = VDirOpen(abspath);
|
||||
}
|
||||
if (dir) {
|
||||
if (dirs->state && dirs->state != dirs->base) {
|
||||
|
@ -143,9 +149,10 @@ void mDirectorySetMapOptions(struct mDirectorySet* dirs, const struct mCoreOptio
|
|||
}
|
||||
|
||||
if (opts->screenshotPath) {
|
||||
struct VDir* dir = VDirOpen(opts->screenshotPath);
|
||||
if (!dir && VDirCreate(opts->screenshotPath)) {
|
||||
dir = VDirOpen(opts->screenshotPath);
|
||||
makeAbsolute(opts->screenshotPath, configDir, abspath);
|
||||
struct VDir* dir = VDirOpen(abspath);
|
||||
if (!dir && VDirCreate(abspath)) {
|
||||
dir = VDirOpen(abspath);
|
||||
}
|
||||
if (dir) {
|
||||
if (dirs->screenshot && dirs->screenshot != dirs->base) {
|
||||
|
@ -156,9 +163,10 @@ void mDirectorySetMapOptions(struct mDirectorySet* dirs, const struct mCoreOptio
|
|||
}
|
||||
|
||||
if (opts->patchPath) {
|
||||
struct VDir* dir = VDirOpen(opts->patchPath);
|
||||
if (!dir && VDirCreate(opts->patchPath)) {
|
||||
dir = VDirOpen(opts->patchPath);
|
||||
makeAbsolute(opts->patchPath, configDir, abspath);
|
||||
struct VDir* dir = VDirOpen(abspath);
|
||||
if (!dir && VDirCreate(abspath)) {
|
||||
dir = VDirOpen(abspath);
|
||||
}
|
||||
if (dir) {
|
||||
if (dirs->patch && dirs->patch != dirs->base) {
|
||||
|
@ -169,9 +177,10 @@ void mDirectorySetMapOptions(struct mDirectorySet* dirs, const struct mCoreOptio
|
|||
}
|
||||
|
||||
if (opts->cheatsPath) {
|
||||
struct VDir* dir = VDirOpen(opts->cheatsPath);
|
||||
if (!dir && VDirCreate(opts->cheatsPath)) {
|
||||
dir = VDirOpen(opts->cheatsPath);
|
||||
makeAbsolute(opts->cheatsPath, configDir, abspath);
|
||||
struct VDir* dir = VDirOpen(abspath);
|
||||
if (!dir && VDirCreate(abspath)) {
|
||||
dir = VDirOpen(abspath);
|
||||
}
|
||||
if (dir) {
|
||||
if (dirs->cheats && dirs->cheats != dirs->base) {
|
||||
|
|
|
@ -79,6 +79,10 @@
|
|||
#cmakedefine USE_GDB_STUB
|
||||
#endif
|
||||
|
||||
#ifndef USE_JSON_C
|
||||
#cmakedefine USE_JSON_C
|
||||
#endif
|
||||
|
||||
#ifndef USE_LIBAV
|
||||
#cmakedefine USE_LIBAV
|
||||
#endif
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
#include <mgba-util/table.h>
|
||||
#include <mgba-util/vector.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#define SECTION_NAME_MAX 128
|
||||
#define KEY_NAME_MAX 32
|
||||
#define KEY_VALUE_MAX 16
|
||||
|
|
|
@ -50,7 +50,7 @@ const char* mLogCategoryName(int category) {
|
|||
}
|
||||
|
||||
const char* mLogCategoryId(int category) {
|
||||
if (category < MAX_CATEGORY) {
|
||||
if (category >= 0 && category < MAX_CATEGORY) {
|
||||
return _categoryIds[category];
|
||||
}
|
||||
return NULL;
|
||||
|
@ -88,6 +88,7 @@ void mLogExplicit(struct mLogger* context, int category, enum mLogLevel level, c
|
|||
if (!context->filter || mLogFilterTest(context->filter, category, level)) {
|
||||
context->log(context, category, level, format, args);
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void mLogFilterInit(struct mLogFilter* filter) {
|
||||
|
|
|
@ -30,6 +30,7 @@ void mCoreRewindContextInit(struct mCoreRewindContext* context, size_t entries,
|
|||
context->previousState = VFileMemChunk(0, 0);
|
||||
context->currentState = VFileMemChunk(0, 0);
|
||||
context->size = 0;
|
||||
context->rewindFrameCounter = 0;
|
||||
#ifndef DISABLE_THREADING
|
||||
context->onThread = onThread;
|
||||
context->ready = false;
|
||||
|
@ -175,7 +176,7 @@ THREAD_ENTRY _rewindThread(void* context) {
|
|||
rewindContext->ready = false;
|
||||
}
|
||||
MutexUnlock(&rewindContext->mutex);
|
||||
return 0;
|
||||
THREAD_EXIT(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/core/serialize.h>
|
||||
#ifdef M_CORE_GBA
|
||||
#include <mgba/gba/interface.h>
|
||||
#endif
|
||||
#include <mgba/script/base.h>
|
||||
#include <mgba/script/context.h>
|
||||
#include <mgba-util/table.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
@ -155,10 +159,49 @@ struct mScriptMemoryDomain {
|
|||
struct mCoreMemoryBlock block;
|
||||
};
|
||||
|
||||
#ifdef USE_DEBUGGERS
|
||||
struct mScriptBreakpointName {
|
||||
uint32_t address;
|
||||
uint32_t maxAddress;
|
||||
int segment : 9;
|
||||
int type : 1;
|
||||
int subtype : 3;
|
||||
};
|
||||
|
||||
struct mScriptBreakpoint {
|
||||
ssize_t id;
|
||||
struct mScriptBreakpointName name;
|
||||
struct Table callbacks;
|
||||
};
|
||||
|
||||
struct mScriptCoreAdapter;
|
||||
struct mScriptDebugger {
|
||||
struct mDebuggerModule d;
|
||||
struct mScriptCoreAdapter* p;
|
||||
struct Table breakpoints;
|
||||
struct Table cbidMap;
|
||||
struct Table bpidMap;
|
||||
int64_t nextBreakpoint;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct mScriptCoreAdapter {
|
||||
struct mCore* core;
|
||||
struct mScriptContext* context;
|
||||
struct mScriptValue memory;
|
||||
#ifdef USE_DEBUGGERS
|
||||
struct mScriptDebugger debugger;
|
||||
#endif
|
||||
struct mRumble rumble;
|
||||
struct mRumble* oldRumble;
|
||||
struct mRotationSource rotation;
|
||||
struct mScriptValue* rotationCbTable;
|
||||
struct mRotationSource* oldRotation;
|
||||
#ifdef M_CORE_GBA
|
||||
struct GBALuminanceSource luminance;
|
||||
struct mScriptValue* luminanceCb;
|
||||
struct GBALuminanceSource* oldLuminance;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct mScriptConsole {
|
||||
|
@ -399,6 +442,7 @@ static int _mScriptCoreLoadStateFile(struct mCore* core, const char* path, int f
|
|||
vf->close(vf);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void _mScriptCoreTakeScreenshot(struct mCore* core, const char* filename) {
|
||||
if (filename) {
|
||||
struct VFile* vf = VFileOpen(filename, O_WRONLY | O_CREAT | O_TRUNC);
|
||||
|
@ -412,6 +456,29 @@ static void _mScriptCoreTakeScreenshot(struct mCore* core, const char* filename)
|
|||
}
|
||||
}
|
||||
|
||||
static struct mScriptValue* _mScriptCoreTakeScreenshotToImage(struct mCore* core) {
|
||||
size_t stride;
|
||||
const void* pixels = 0;
|
||||
unsigned width, height;
|
||||
core->currentVideoSize(core, &width, &height);
|
||||
core->getPixels(core, &pixels, &stride);
|
||||
if (!pixels) {
|
||||
return NULL;
|
||||
}
|
||||
#ifndef COLOR_16_BIT
|
||||
struct mImage* image = mImageCreateFromConstBuffer(width, height, stride, mCOLOR_XBGR8, pixels);
|
||||
#elif COLOR_5_6_5
|
||||
struct mImage* image = mImageCreateFromConstBuffer(width, height, stride, mCOLOR_RGB565, pixels);
|
||||
#else
|
||||
struct mImage* image = mImageCreateFromConstBuffer(width, height, stride, mCOLOR_BGR5, pixels);
|
||||
#endif
|
||||
|
||||
struct mScriptValue* result = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mImage));
|
||||
result->value.opaque = image;
|
||||
result->flags = mSCRIPT_VALUE_FLAG_DEINIT;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Loading functions
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(mCore, BOOL, loadFile, mCoreLoadFile, 1, CHARP, path);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(mCore, BOOL, autoloadSave, mCoreAutoloadSave, 0);
|
||||
|
@ -464,6 +531,7 @@ mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, BOOL, loadStateFile, _mScript
|
|||
|
||||
// Miscellaneous functions
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD_WITH_DEFAULTS(mCore, screenshot, _mScriptCoreTakeScreenshot, 1, CHARP, filename);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(mCore, W(mImage), screenshotToImage, _mScriptCoreTakeScreenshotToImage, 0);
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT(mCore)
|
||||
mSCRIPT_DEFINE_CLASS_DOCSTRING(
|
||||
|
@ -549,8 +617,10 @@ mSCRIPT_DEFINE_STRUCT(mCore)
|
|||
mSCRIPT_DEFINE_DOCSTRING("Load state from the given path. See C.SAVESTATE for possible values for `flags`")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, loadStateFile)
|
||||
|
||||
mSCRIPT_DEFINE_DOCSTRING("Save a screenshot")
|
||||
mSCRIPT_DEFINE_DOCSTRING("Save a screenshot to a file")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, screenshot)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Get a screenshot in an struct::mImage")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mCore, screenshotToImage)
|
||||
mSCRIPT_DEFINE_END;
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mCore, checksum)
|
||||
|
@ -631,9 +701,239 @@ static void _rebuildMemoryMap(struct mScriptContext* context, struct mScriptCore
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef USE_DEBUGGERS
|
||||
static void _freeBreakpoint(void* bp) {
|
||||
struct mScriptBreakpoint* point = bp;
|
||||
HashTableDeinit(&point->callbacks);
|
||||
free(bp);
|
||||
}
|
||||
|
||||
static struct mScriptBreakpoint* _ensureBreakpoint(struct mScriptDebugger* debugger, struct mBreakpoint* breakpoint) {
|
||||
struct mDebuggerModule* module = &debugger->d;
|
||||
struct mScriptBreakpointName name = {
|
||||
.address = breakpoint->address,
|
||||
.maxAddress = 0,
|
||||
.segment = breakpoint->segment,
|
||||
.type = 0,
|
||||
.subtype = breakpoint->type
|
||||
};
|
||||
struct mScriptBreakpoint* point = HashTableLookupBinary(&debugger->breakpoints, &name, sizeof(name));
|
||||
if (point) {
|
||||
return point;
|
||||
}
|
||||
point = calloc(1, sizeof(*point));
|
||||
point->id = module->p->platform->setBreakpoint(module->p->platform, module, breakpoint);
|
||||
point->name = name;
|
||||
HashTableInit(&point->callbacks, 0, (void (*)(void*)) mScriptValueDeref);
|
||||
HashTableInsertBinary(&debugger->bpidMap, &point->id, sizeof(point->id), point);
|
||||
HashTableInsertBinary(&debugger->breakpoints, &name, sizeof(name), point);
|
||||
return point;
|
||||
}
|
||||
|
||||
static struct mScriptBreakpoint* _ensureWatchpoint(struct mScriptDebugger* debugger, struct mWatchpoint* watchpoint) {
|
||||
struct mDebuggerModule* module = &debugger->d;
|
||||
struct mScriptBreakpointName name = {
|
||||
.address = watchpoint->minAddress,
|
||||
.maxAddress = watchpoint->maxAddress,
|
||||
.segment = watchpoint->segment,
|
||||
.type = 1,
|
||||
.subtype = watchpoint->type
|
||||
};
|
||||
struct mScriptBreakpoint* point = HashTableLookupBinary(&debugger->breakpoints, &name, sizeof(name));
|
||||
if (point) {
|
||||
return point;
|
||||
}
|
||||
point = calloc(1, sizeof(*point));
|
||||
point->id = module->p->platform->setWatchpoint(module->p->platform, module, watchpoint);
|
||||
point->name = name;
|
||||
HashTableInit(&point->callbacks, 0, (void (*)(void*)) mScriptValueDeref);
|
||||
HashTableInsertBinary(&debugger->bpidMap, &point->id, sizeof(point->id), point);
|
||||
HashTableInsertBinary(&debugger->breakpoints, &name, sizeof(name), point);
|
||||
return point;
|
||||
}
|
||||
|
||||
static int64_t _addCallbackToBreakpoint(struct mScriptDebugger* debugger, struct mScriptBreakpoint* point, struct mScriptValue* callback) {
|
||||
int64_t cbid = debugger->nextBreakpoint;
|
||||
++debugger->nextBreakpoint;
|
||||
HashTableInsertBinary(&debugger->cbidMap, &cbid, sizeof(cbid), point);
|
||||
mScriptValueRef(callback);
|
||||
HashTableInsertBinary(&point->callbacks, &cbid, sizeof(cbid), callback);
|
||||
return cbid;
|
||||
}
|
||||
|
||||
static void _runCallbacks(struct mScriptBreakpoint* point) {
|
||||
struct TableIterator iter;
|
||||
if (!HashTableIteratorStart(&point->callbacks, &iter)) {
|
||||
return;
|
||||
}
|
||||
do {
|
||||
struct mScriptValue* fn = HashTableIteratorGetValue(&point->callbacks, &iter);
|
||||
struct mScriptFrame frame;
|
||||
mScriptFrameInit(&frame);
|
||||
mScriptInvoke(fn, &frame);
|
||||
mScriptFrameDeinit(&frame);
|
||||
} while (HashTableIteratorNext(&point->callbacks, &iter));
|
||||
}
|
||||
|
||||
static void _scriptDebuggerInit(struct mDebuggerModule* debugger) {
|
||||
struct mScriptDebugger* scriptDebugger = (struct mScriptDebugger*) debugger;
|
||||
debugger->isPaused = false;
|
||||
debugger->needsCallback = false;
|
||||
|
||||
HashTableInit(&scriptDebugger->breakpoints, 0, _freeBreakpoint);
|
||||
HashTableInit(&scriptDebugger->cbidMap, 0, NULL);
|
||||
HashTableInit(&scriptDebugger->bpidMap, 0, NULL);
|
||||
}
|
||||
|
||||
static void _scriptDebuggerDeinit(struct mDebuggerModule* debugger) {
|
||||
struct mScriptDebugger* scriptDebugger = (struct mScriptDebugger*) debugger;
|
||||
HashTableDeinit(&scriptDebugger->cbidMap);
|
||||
HashTableDeinit(&scriptDebugger->bpidMap);
|
||||
HashTableDeinit(&scriptDebugger->breakpoints);
|
||||
}
|
||||
|
||||
static void _scriptDebuggerPaused(struct mDebuggerModule* debugger, int32_t timeoutMs) {
|
||||
UNUSED(debugger);
|
||||
UNUSED(timeoutMs);
|
||||
}
|
||||
|
||||
static void _scriptDebuggerUpdate(struct mDebuggerModule* debugger) {
|
||||
UNUSED(debugger);
|
||||
}
|
||||
|
||||
static void _scriptDebuggerEntered(struct mDebuggerModule* debugger, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
|
||||
struct mScriptDebugger* scriptDebugger = (struct mScriptDebugger*) debugger;
|
||||
struct mScriptBreakpoint* point;
|
||||
switch (reason) {
|
||||
case DEBUGGER_ENTER_BREAKPOINT:
|
||||
case DEBUGGER_ENTER_WATCHPOINT:
|
||||
point = HashTableLookupBinary(&scriptDebugger->bpidMap, &info->pointId, sizeof(info->pointId));
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
_runCallbacks(point);
|
||||
debugger->isPaused = false;
|
||||
}
|
||||
|
||||
static void _scriptDebuggerCustom(struct mDebuggerModule* debugger) {
|
||||
UNUSED(debugger);
|
||||
}
|
||||
|
||||
static void _scriptDebuggerInterrupt(struct mDebuggerModule* debugger) {
|
||||
UNUSED(debugger);
|
||||
}
|
||||
|
||||
static bool _setupDebugger(struct mScriptCoreAdapter* adapter) {
|
||||
if (!adapter->core->debugger) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (adapter->debugger.d.p) {
|
||||
return true;
|
||||
}
|
||||
adapter->debugger.p = adapter;
|
||||
adapter->debugger.d.type = DEBUGGER_CUSTOM;
|
||||
adapter->debugger.d.init = _scriptDebuggerInit;
|
||||
adapter->debugger.d.deinit = _scriptDebuggerDeinit;
|
||||
adapter->debugger.d.paused = _scriptDebuggerPaused;
|
||||
adapter->debugger.d.update = _scriptDebuggerUpdate;
|
||||
adapter->debugger.d.entered = _scriptDebuggerEntered;
|
||||
adapter->debugger.d.custom = _scriptDebuggerCustom;
|
||||
adapter->debugger.d.interrupt = _scriptDebuggerInterrupt;
|
||||
adapter->debugger.d.isPaused = false;
|
||||
adapter->debugger.d.needsCallback = false;
|
||||
adapter->debugger.nextBreakpoint = 1;
|
||||
mDebuggerAttachModule(adapter->core->debugger, &adapter->debugger.d);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int64_t _mScriptCoreAdapterSetBreakpoint(struct mScriptCoreAdapter* adapter, struct mScriptValue* callback, uint32_t address, int32_t segment) {
|
||||
if (!_setupDebugger(adapter)) {
|
||||
return -1;
|
||||
}
|
||||
struct mBreakpoint breakpoint = {
|
||||
.address = address,
|
||||
.segment = segment,
|
||||
.type = BREAKPOINT_HARDWARE
|
||||
};
|
||||
|
||||
struct mDebuggerModule* module = &adapter->debugger.d;
|
||||
if (!module->p->platform->setBreakpoint) {
|
||||
return -1;
|
||||
}
|
||||
struct mScriptBreakpoint* point = _ensureBreakpoint(&adapter->debugger, &breakpoint);
|
||||
return _addCallbackToBreakpoint(&adapter->debugger, point, callback);
|
||||
}
|
||||
|
||||
static int64_t _mScriptCoreAdapterSetWatchpoint(struct mScriptCoreAdapter* adapter, struct mScriptValue* callback, uint32_t address, int type, int32_t segment) {
|
||||
if (!_setupDebugger(adapter)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct mWatchpoint watchpoint = {
|
||||
.minAddress = address,
|
||||
.maxAddress = address + 1,
|
||||
.segment = segment,
|
||||
.type = type,
|
||||
};
|
||||
struct mDebuggerModule* module = &adapter->debugger.d;
|
||||
if (!module->p->platform->setWatchpoint) {
|
||||
return -1;
|
||||
}
|
||||
struct mScriptBreakpoint* point = _ensureWatchpoint(&adapter->debugger, &watchpoint);
|
||||
return _addCallbackToBreakpoint(&adapter->debugger, point, callback);
|
||||
}
|
||||
|
||||
static int64_t _mScriptCoreAdapterSetRangeWatchpoint(struct mScriptCoreAdapter* adapter, struct mScriptValue* callback, uint32_t minAddress, uint32_t maxAddress, int type, int32_t segment) {
|
||||
if (!_setupDebugger(adapter)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct mWatchpoint watchpoint = {
|
||||
.minAddress = minAddress,
|
||||
.maxAddress = maxAddress,
|
||||
.segment = segment,
|
||||
.type = type,
|
||||
};
|
||||
struct mDebuggerModule* module = &adapter->debugger.d;
|
||||
if (!module->p->platform->setWatchpoint) {
|
||||
return -1;
|
||||
}
|
||||
struct mScriptBreakpoint* point = _ensureWatchpoint(&adapter->debugger, &watchpoint);
|
||||
return _addCallbackToBreakpoint(&adapter->debugger, point, callback);
|
||||
}
|
||||
|
||||
static bool _mScriptCoreAdapterClearBreakpoint(struct mScriptCoreAdapter* adapter, int64_t cbid) {
|
||||
if (!_setupDebugger(adapter)) {
|
||||
return false;
|
||||
}
|
||||
struct mScriptBreakpoint* point = HashTableLookupBinary(&adapter->debugger.cbidMap, &cbid, sizeof(cbid));
|
||||
if (!point) {
|
||||
return false;
|
||||
}
|
||||
HashTableRemoveBinary(&adapter->debugger.cbidMap, &cbid, sizeof(cbid));
|
||||
HashTableRemoveBinary(&point->callbacks, &cbid, sizeof(cbid));
|
||||
|
||||
if (!HashTableSize(&point->callbacks)) {
|
||||
struct mDebuggerModule* module = &adapter->debugger.d;
|
||||
module->p->platform->clearBreakpoint(module->p->platform, point->id);
|
||||
|
||||
struct mScriptBreakpointName name = point->name;
|
||||
HashTableRemoveBinary(&adapter->debugger.breakpoints, &name, sizeof(name));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void _mScriptCoreAdapterDeinit(struct mScriptCoreAdapter* adapter) {
|
||||
_clearMemoryMap(adapter->context, adapter, false);
|
||||
adapter->memory.type->free(&adapter->memory);
|
||||
#ifdef USE_DEBUGGERS
|
||||
if (adapter->core->debugger) {
|
||||
mDebuggerDetachModule(adapter->core->debugger, &adapter->debugger.d);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct mScriptValue* _mScriptCoreAdapterGet(struct mScriptCoreAdapter* adapter, const char* name) {
|
||||
|
@ -654,10 +954,61 @@ static void _mScriptCoreAdapterReset(struct mScriptCoreAdapter* adapter) {
|
|||
mScriptContextTriggerCallback(adapter->context, "reset", NULL);
|
||||
}
|
||||
|
||||
static struct mScriptValue* _mScriptCoreAdapterSetRotationCbTable(struct mScriptCoreAdapter* adapter, struct mScriptValue* cbTable) {
|
||||
if (cbTable) {
|
||||
mScriptValueRef(cbTable);
|
||||
}
|
||||
struct mScriptValue* oldTable = adapter->rotationCbTable;
|
||||
adapter->rotationCbTable = cbTable;
|
||||
return oldTable;
|
||||
}
|
||||
|
||||
static void _mScriptCoreAdapterSetLuminanceCb(struct mScriptCoreAdapter* adapter, struct mScriptValue* callback) {
|
||||
if (callback) {
|
||||
if (callback->type->base != mSCRIPT_TYPE_FUNCTION) {
|
||||
return;
|
||||
}
|
||||
mScriptValueRef(callback);
|
||||
}
|
||||
if (adapter->luminanceCb) {
|
||||
mScriptValueDeref(adapter->luminanceCb);
|
||||
}
|
||||
adapter->luminanceCb = callback;
|
||||
}
|
||||
|
||||
mSCRIPT_DECLARE_STRUCT(mScriptCoreAdapter);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(mScriptCoreAdapter, W(mCore), _get, _mScriptCoreAdapterGet, 1, CHARP, name);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCoreAdapter, _deinit, _mScriptCoreAdapterDeinit, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCoreAdapter, reset, _mScriptCoreAdapterReset, 0);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(mScriptCoreAdapter, WTABLE, setRotationCallbacks, _mScriptCoreAdapterSetRotationCbTable, 1, WTABLE, cbTable);
|
||||
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptCoreAdapter, setSolarSensorCallback, _mScriptCoreAdapterSetLuminanceCb, 1, WRAPPER, callback);
|
||||
#ifdef USE_DEBUGGERS
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mScriptCoreAdapter, S64, setBreakpoint, _mScriptCoreAdapterSetBreakpoint, 3, WRAPPER, callback, U32, address, S32, segment);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mScriptCoreAdapter, S64, setWatchpoint, _mScriptCoreAdapterSetWatchpoint, 4, WRAPPER, callback, U32, address, S32, type, S32, segment);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mScriptCoreAdapter, S64, setRangeWatchpoint, _mScriptCoreAdapterSetRangeWatchpoint, 5, WRAPPER, callback, U32, minAddress, U32, maxAddress, S32, type, S32, segment);
|
||||
mSCRIPT_DECLARE_STRUCT_METHOD(mScriptCoreAdapter, BOOL, clearBreakpoint, _mScriptCoreAdapterClearBreakpoint, 1, S64, cbid);
|
||||
#endif
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mScriptCoreAdapter, setBreakpoint)
|
||||
mSCRIPT_NO_DEFAULT,
|
||||
mSCRIPT_NO_DEFAULT,
|
||||
mSCRIPT_S32(-1)
|
||||
mSCRIPT_DEFINE_DEFAULTS_END;
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mScriptCoreAdapter, setWatchpoint)
|
||||
mSCRIPT_NO_DEFAULT,
|
||||
mSCRIPT_NO_DEFAULT,
|
||||
mSCRIPT_NO_DEFAULT,
|
||||
mSCRIPT_S32(-1)
|
||||
mSCRIPT_DEFINE_DEFAULTS_END;
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mScriptCoreAdapter, setRangeWatchpoint)
|
||||
mSCRIPT_NO_DEFAULT,
|
||||
mSCRIPT_NO_DEFAULT,
|
||||
mSCRIPT_NO_DEFAULT,
|
||||
mSCRIPT_NO_DEFAULT,
|
||||
mSCRIPT_S32(-1)
|
||||
mSCRIPT_DEFINE_DEFAULTS_END;
|
||||
|
||||
mSCRIPT_DEFINE_STRUCT(mScriptCoreAdapter)
|
||||
mSCRIPT_DEFINE_CLASS_DOCSTRING(
|
||||
|
@ -672,10 +1023,157 @@ mSCRIPT_DEFINE_STRUCT(mScriptCoreAdapter)
|
|||
mSCRIPT_DEFINE_STRUCT_DEFAULT_GET(mScriptCoreAdapter)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Reset the emulation. As opposed to struct::mCore.reset, this version calls the **reset** callback")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCoreAdapter, reset)
|
||||
mSCRIPT_DEFINE_DOCSTRING(
|
||||
"Sets the table of functions to be called when the game requests rotation data, for either a gyroscope or accelerometer. "
|
||||
"The following functions are supported, and if any isn't set then then default implementation for that function is called instead:\n\n"
|
||||
"- `sample`: Update (\"sample\") the values returned by the other functions. The values returned shouldn't change until the next time this is called\n"
|
||||
"- `readTiltX`: Return a value between -1.0 and +1.0 representing the X (left/right axis) direction of the linear acceleration vector, as for an accelerometer.\n"
|
||||
"- `readTiltY`: Return a value between -1.0 and +1.0 representing the Y (up/down axis) direction of the linear acceleration vector, as for an accelerometer.\n"
|
||||
"- `readGyroZ`: Return a value between -1.0 and +1.0 representing the roll (front/back axis) value of the rotational acceleration vector, as for an gyroscope.\n\n"
|
||||
"Optionally, you can also set a value `context` on the table that will be passed to the callbacks. This table is copied by value, so changes made to the table "
|
||||
"after being passed to this function will not be seen unless the function is called again. Therefore, the recommended usage of the `context` field is as an index "
|
||||
"or key into a separate table. Use cases may vary. If this function is called more than once, the previous value of the table is returned."
|
||||
)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCoreAdapter, setRotationCallbacks)
|
||||
mSCRIPT_DEFINE_DOCSTRING(
|
||||
"Set a callback that will be used to get the current value of the solar sensors between 0 (darkest) and 255 (brightest). "
|
||||
"Note that the full range of values is not used by games, and the exact range depends on the calibration done by the game itself."
|
||||
)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCoreAdapter, setSolarSensorCallback)
|
||||
#ifdef USE_DEBUGGERS
|
||||
mSCRIPT_DEFINE_DOCSTRING("Set a breakpoint at a given address")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCoreAdapter, setBreakpoint)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Clear a breakpoint or watchpoint for a given id returned by a previous call")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCoreAdapter, clearBreakpoint)
|
||||
mSCRIPT_DEFINE_DOCSTRING("Set a watchpoint at a given address of a given type")
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCoreAdapter, setWatchpoint)
|
||||
mSCRIPT_DEFINE_DOCSTRING(
|
||||
"Set a watchpoint in a given range of a given type. Note that the range is exclusive on the end, "
|
||||
"as though you've added the size, i.e. a 4-byte watch would specify the maximum as the minimum address + 4"
|
||||
)
|
||||
mSCRIPT_DEFINE_STRUCT_METHOD(mScriptCoreAdapter, setRangeWatchpoint)
|
||||
#endif
|
||||
mSCRIPT_DEFINE_STRUCT_CAST_TO_MEMBER(mScriptCoreAdapter, S(mCore), _core)
|
||||
mSCRIPT_DEFINE_STRUCT_CAST_TO_MEMBER(mScriptCoreAdapter, CS(mCore), _core)
|
||||
mSCRIPT_DEFINE_END;
|
||||
|
||||
static void _setRumble(struct mRumble* rumble, int enable) {
|
||||
struct mScriptCoreAdapter* adapter = containerof(rumble, struct mScriptCoreAdapter, rumble);
|
||||
|
||||
if (adapter->oldRumble) {
|
||||
adapter->oldRumble->setRumble(adapter->oldRumble, enable);
|
||||
}
|
||||
|
||||
struct mScriptList args;
|
||||
mScriptListInit(&args, 1);
|
||||
*mScriptListAppend(&args) = mSCRIPT_MAKE_BOOL(!!enable);
|
||||
mScriptContextTriggerCallback(adapter->context, "rumble", &args);
|
||||
mScriptListDeinit(&args);
|
||||
}
|
||||
|
||||
static bool _callRotationCb(struct mScriptCoreAdapter* adapter, const char* cbName, struct mScriptValue* out) {
|
||||
if (!adapter->rotationCbTable) {
|
||||
return false;
|
||||
}
|
||||
struct mScriptValue* cb = mScriptTableLookup(adapter->rotationCbTable, &mSCRIPT_MAKE_CHARP(cbName));
|
||||
if (!cb || cb->type->base != mSCRIPT_TYPE_FUNCTION) {
|
||||
return false;
|
||||
}
|
||||
struct mScriptFrame frame;
|
||||
struct mScriptValue* context = mScriptTableLookup(adapter->rotationCbTable, &mSCRIPT_MAKE_CHARP("context"));
|
||||
mScriptFrameInit(&frame);
|
||||
if (context) {
|
||||
mScriptValueWrap(context, mScriptListAppend(&frame.arguments));
|
||||
}
|
||||
bool ok = mScriptInvoke(cb, &frame);
|
||||
if (ok && out && mScriptListSize(&frame.returnValues) == 1) {
|
||||
if (!mScriptCast(mSCRIPT_TYPE_MS_F32, mScriptListGetPointer(&frame.returnValues, 0), out)) {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
mScriptFrameDeinit(&frame);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void _rotationSample(struct mRotationSource* rotation) {
|
||||
struct mScriptCoreAdapter* adapter = containerof(rotation, struct mScriptCoreAdapter, rotation);
|
||||
|
||||
_callRotationCb(adapter, "sample", NULL);
|
||||
|
||||
if (adapter->oldRotation && adapter->oldRotation->sample) {
|
||||
adapter->oldRotation->sample(adapter->oldRotation);
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t _rotationReadTiltX(struct mRotationSource* rotation) {
|
||||
struct mScriptCoreAdapter* adapter = containerof(rotation, struct mScriptCoreAdapter, rotation);
|
||||
|
||||
struct mScriptValue out;
|
||||
if (_callRotationCb(adapter, "readTiltX", &out)) {
|
||||
return out.value.f32 * INT32_MAX;
|
||||
}
|
||||
|
||||
if (adapter->oldRotation && adapter->oldRotation->readTiltX) {
|
||||
return adapter->oldRotation->readTiltX(adapter->oldRotation);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t _rotationReadTiltY(struct mRotationSource* rotation) {
|
||||
struct mScriptCoreAdapter* adapter = containerof(rotation, struct mScriptCoreAdapter, rotation);
|
||||
|
||||
struct mScriptValue out;
|
||||
if (_callRotationCb(adapter, "readTiltY", &out)) {
|
||||
return out.value.f32 * INT32_MAX;
|
||||
}
|
||||
|
||||
if (adapter->oldRotation && adapter->oldRotation->readTiltY) {
|
||||
return adapter->oldRotation->readTiltY(adapter->oldRotation);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t _rotationReadGyroZ(struct mRotationSource* rotation) {
|
||||
struct mScriptCoreAdapter* adapter = containerof(rotation, struct mScriptCoreAdapter, rotation);
|
||||
|
||||
struct mScriptValue out;
|
||||
if (_callRotationCb(adapter, "readGyroZ", &out)) {
|
||||
return out.value.f32 * INT32_MAX;
|
||||
}
|
||||
|
||||
if (adapter->oldRotation && adapter->oldRotation->readGyroZ) {
|
||||
return adapter->oldRotation->readGyroZ(adapter->oldRotation);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef M_CORE_GBA
|
||||
static uint8_t _readLuminance(struct GBALuminanceSource* luminance) {
|
||||
struct mScriptCoreAdapter* adapter = containerof(luminance, struct mScriptCoreAdapter, luminance);
|
||||
|
||||
if (adapter->luminanceCb) {
|
||||
struct mScriptFrame frame;
|
||||
mScriptFrameInit(&frame);
|
||||
bool ok = mScriptInvoke(adapter->luminanceCb, &frame);
|
||||
struct mScriptValue out = {0};
|
||||
if (ok && mScriptListSize(&frame.returnValues) == 1) {
|
||||
if (!mScriptCast(mSCRIPT_TYPE_MS_U8, mScriptListGetPointer(&frame.returnValues, 0), &out)) {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
mScriptFrameDeinit(&frame);
|
||||
if (ok) {
|
||||
return 0xFF - out.value.u32;
|
||||
}
|
||||
}
|
||||
if (adapter->oldLuminance) {
|
||||
adapter->oldLuminance->sample(adapter->oldLuminance);
|
||||
return adapter->oldLuminance->readLuminance(adapter->oldLuminance);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void mScriptContextAttachCore(struct mScriptContext* context, struct mCore* core) {
|
||||
struct mScriptValue* coreValue = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptCoreAdapter));
|
||||
struct mScriptCoreAdapter* adapter = calloc(1, sizeof(*adapter));
|
||||
|
@ -687,6 +1185,25 @@ void mScriptContextAttachCore(struct mScriptContext* context, struct mCore* core
|
|||
adapter->memory.type = mSCRIPT_TYPE_MS_TABLE;
|
||||
adapter->memory.type->alloc(&adapter->memory);
|
||||
|
||||
adapter->rumble.setRumble = _setRumble;
|
||||
adapter->rotation.sample = _rotationSample;
|
||||
adapter->rotation.readTiltX = _rotationReadTiltX;
|
||||
adapter->rotation.readTiltY = _rotationReadTiltY;
|
||||
adapter->rotation.readGyroZ = _rotationReadGyroZ;
|
||||
|
||||
adapter->oldRumble = core->getPeripheral(core, mPERIPH_RUMBLE);
|
||||
adapter->oldRotation = core->getPeripheral(core, mPERIPH_ROTATION);
|
||||
core->setPeripheral(core, mPERIPH_RUMBLE, &adapter->rumble);
|
||||
core->setPeripheral(core, mPERIPH_ROTATION, &adapter->rotation);
|
||||
|
||||
#ifdef M_CORE_GBA
|
||||
adapter->luminance.readLuminance = _readLuminance;
|
||||
if (core->platform(core) == mPLATFORM_GBA) {
|
||||
adapter->oldLuminance = core->getPeripheral(core, mPERIPH_GBA_LUMINANCE);
|
||||
core->setPeripheral(core, mPERIPH_GBA_LUMINANCE, &adapter->luminance);
|
||||
}
|
||||
#endif
|
||||
|
||||
_rebuildMemoryMap(context, adapter);
|
||||
|
||||
coreValue->value.opaque = adapter;
|
||||
|
@ -703,7 +1220,24 @@ void mScriptContextDetachCore(struct mScriptContext* context) {
|
|||
if (!value) {
|
||||
return;
|
||||
}
|
||||
_clearMemoryMap(context, value->value.opaque, true);
|
||||
|
||||
struct mScriptCoreAdapter* adapter = value->value.opaque;
|
||||
_clearMemoryMap(context, adapter, true);
|
||||
struct mCore* core = adapter->core;
|
||||
core->setPeripheral(core, mPERIPH_RUMBLE, adapter->oldRumble);
|
||||
core->setPeripheral(core, mPERIPH_ROTATION, adapter->oldRotation);
|
||||
if (adapter->rotationCbTable) {
|
||||
mScriptValueDeref(adapter->rotationCbTable);
|
||||
}
|
||||
#ifdef M_CORE_GBA
|
||||
if (core->platform(core) == mPLATFORM_GBA) {
|
||||
core->setPeripheral(core, mPERIPH_GBA_LUMINANCE, adapter->oldLuminance);
|
||||
}
|
||||
if (adapter->luminanceCb) {
|
||||
mScriptValueDeref(adapter->luminanceCb);
|
||||
}
|
||||
#endif
|
||||
|
||||
mScriptContextRemoveGlobal(context, "emu");
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include <mgba-util/vfs.h>
|
||||
|
||||
#ifdef USE_PNG
|
||||
#include <mgba-util/png-io.h>
|
||||
#include <mgba-util/image/png-io.h>
|
||||
#include <png.h>
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
@ -176,15 +176,15 @@ static bool _savePNGState(struct mCore* core, struct VFile* vf, struct mStateExt
|
|||
mappedMemoryFree(state, stateSize);
|
||||
|
||||
unsigned width, height;
|
||||
core->desiredVideoDimensions(core, &width, &height);
|
||||
core->currentVideoSize(core, &width, &height);
|
||||
png_structp png = PNGWriteOpen(vf);
|
||||
png_infop info = PNGWriteHeader(png, width, height);
|
||||
png_infop info = PNGWriteHeader(png, width, height, mCOLOR_NATIVE);
|
||||
if (!png || !info) {
|
||||
PNGWriteClose(png, info);
|
||||
free(buffer);
|
||||
return false;
|
||||
}
|
||||
PNGWritePixels(png, width, height, stride, pixels);
|
||||
PNGWritePixels(png, width, height, stride, pixels, mCOLOR_NATIVE);
|
||||
PNGWriteCustomChunk(png, "gbAs", len, buffer);
|
||||
if (extdata) {
|
||||
uint32_t i;
|
||||
|
@ -453,7 +453,7 @@ bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags) {
|
|||
UNUSED(flags);
|
||||
#endif
|
||||
vf->truncate(vf, stateSize);
|
||||
struct GBASerializedState* state = vf->map(vf, stateSize, MAP_WRITE);
|
||||
void* state = vf->map(vf, stateSize, MAP_WRITE);
|
||||
if (!state) {
|
||||
mStateExtdataDeinit(&extdata);
|
||||
if (cheatVf) {
|
||||
|
@ -529,7 +529,7 @@ bool mCoreLoadStateNamed(struct mCore* core, struct VFile* vf, int flags) {
|
|||
mappedMemoryFree(state, core->stateSize(core));
|
||||
|
||||
unsigned width, height;
|
||||
core->desiredVideoDimensions(core, &width, &height);
|
||||
core->currentVideoSize(core, &width, &height);
|
||||
|
||||
struct mStateExtdataItem item;
|
||||
if (flags & SAVESTATE_SCREENSHOT && mStateExtdataGet(&extdata, EXTDATA_SCREENSHOT, &item)) {
|
||||
|
|
|
@ -9,8 +9,7 @@
|
|||
#include <mgba/core/log.h>
|
||||
#include <mgba/core/scripting.h>
|
||||
#include <mgba/internal/script/lua.h>
|
||||
#include <mgba/script/context.h>
|
||||
#include <mgba/script/types.h>
|
||||
#include <mgba/script.h>
|
||||
|
||||
#include "script/test.h"
|
||||
|
||||
|
@ -143,8 +142,8 @@ M_TEST_DEFINE(globals) {
|
|||
LOAD_PROGRAM("assert(emu)");
|
||||
assert_true(lua->run(lua));
|
||||
|
||||
TEARDOWN_CORE;
|
||||
mScriptContextDeinit(&context);
|
||||
TEARDOWN_CORE;
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(infoFuncs) {
|
||||
|
@ -161,8 +160,8 @@ M_TEST_DEFINE(infoFuncs) {
|
|||
TEST_VALUE(S32, "frequency", core->frequency(core));
|
||||
TEST_VALUE(S32, "frameCycles", core->frameCycles(core));
|
||||
|
||||
TEARDOWN_CORE;
|
||||
mScriptContextDeinit(&context);
|
||||
TEARDOWN_CORE;
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(detach) {
|
||||
|
@ -195,8 +194,8 @@ M_TEST_DEFINE(detach) {
|
|||
);
|
||||
assert_false(lua->run(lua));
|
||||
|
||||
TEARDOWN_CORE;
|
||||
mScriptContextDeinit(&context);
|
||||
TEARDOWN_CORE;
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(runFrame) {
|
||||
|
@ -215,8 +214,8 @@ M_TEST_DEFINE(runFrame) {
|
|||
TEST_VALUE(S32, "frame", i);
|
||||
}
|
||||
|
||||
TEARDOWN_CORE;
|
||||
mScriptContextDeinit(&context);
|
||||
TEARDOWN_CORE;
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(memoryRead) {
|
||||
|
@ -250,8 +249,8 @@ M_TEST_DEFINE(memoryRead) {
|
|||
TEST_VALUE(S32, "b16", 0x0807);
|
||||
TEST_VALUE(S32, "a32", 0x0C0B0A09);
|
||||
|
||||
TEARDOWN_CORE;
|
||||
mScriptContextDeinit(&context);
|
||||
TEARDOWN_CORE;
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(memoryWrite) {
|
||||
|
@ -278,8 +277,8 @@ M_TEST_DEFINE(memoryWrite) {
|
|||
assert_int_equal(core->busRead8(core, RAM_BASE + i), i + 1);
|
||||
}
|
||||
|
||||
TEARDOWN_CORE;
|
||||
mScriptContextDeinit(&context);
|
||||
TEARDOWN_CORE;
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(logging) {
|
||||
|
@ -310,6 +309,415 @@ M_TEST_DEFINE(logging) {
|
|||
mScriptContextDeinit(&context);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(screenshot) {
|
||||
SETUP_LUA;
|
||||
CREATE_CORE;
|
||||
color_t* buffer = malloc(240 * 160 * sizeof(color_t));
|
||||
core->setVideoBuffer(core, buffer, 240);
|
||||
core->reset(core);
|
||||
core->runFrame(core);
|
||||
|
||||
TEST_PROGRAM("im = emu:screenshotToImage()");
|
||||
TEST_PROGRAM("assert(im)");
|
||||
TEST_PROGRAM("assert(im.width >= 160)");
|
||||
TEST_PROGRAM("assert(im.height >= 144)");
|
||||
|
||||
free(buffer);
|
||||
mScriptContextDeinit(&context);
|
||||
TEARDOWN_CORE;
|
||||
}
|
||||
|
||||
#ifdef USE_DEBUGGERS
|
||||
void _setupBp(struct mCore* core) {
|
||||
switch (core->platform(core)) {
|
||||
#ifdef M_CORE_GBA
|
||||
case mPLATFORM_GBA:
|
||||
core->busWrite32(core, 0x020000C0, 0xE0000000); // nop
|
||||
core->busWrite32(core, 0x020000C4, 0xE0000000); // nop
|
||||
core->busWrite32(core, 0x020000C8, 0xEAFFFFFD); // b 0x020000C4
|
||||
break;
|
||||
#endif
|
||||
#ifdef M_CORE_GB
|
||||
case mPLATFORM_GB:
|
||||
core->rawWrite8(core, 0x101, 0, 0xEE); // Jump to 0xF0
|
||||
core->rawWrite8(core, 0xF0, 0, 0x00); // nop
|
||||
core->rawWrite8(core, 0xF1, 0, 0x18); // Loop forecer
|
||||
core->rawWrite8(core, 0xF2, 0, 0xFD); // jr $-3
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef M_CORE_GBA
|
||||
M_TEST_DEFINE(basicBreakpointGBA) {
|
||||
SETUP_LUA;
|
||||
struct mCore* core = mCoreCreate(mPLATFORM_GBA);
|
||||
struct mDebugger debugger;
|
||||
assert_non_null(core);
|
||||
assert_true(core->init(core));
|
||||
mCoreInitConfig(core, NULL);
|
||||
core->reset(core);
|
||||
_setupBp(core);
|
||||
mScriptContextAttachCore(&context, core);
|
||||
|
||||
mDebuggerInit(&debugger);
|
||||
mDebuggerAttach(&debugger, core);
|
||||
|
||||
TEST_PROGRAM(
|
||||
"hit = 0\n"
|
||||
"function bkpt()\n"
|
||||
" hit = hit + 1\n"
|
||||
"end"
|
||||
);
|
||||
TEST_PROGRAM("cbid = emu:setBreakpoint(bkpt, 0x020000C4)");
|
||||
TEST_PROGRAM("assert(cbid == 1)");
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 20; ++i) {
|
||||
mDebuggerRun(&debugger);
|
||||
}
|
||||
|
||||
assert_int_equal(debugger.state, DEBUGGER_RUNNING);
|
||||
TEST_PROGRAM("assert(hit >= 1)");
|
||||
|
||||
mScriptContextDeinit(&context);
|
||||
TEARDOWN_CORE;
|
||||
mDebuggerDeinit(&debugger);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef M_CORE_GB
|
||||
M_TEST_DEFINE(basicBreakpointGB) {
|
||||
SETUP_LUA;
|
||||
struct mCore* core = mCoreCreate(mPLATFORM_GB);
|
||||
struct mDebugger debugger;
|
||||
assert_non_null(core);
|
||||
assert_true(core->init(core));
|
||||
mCoreInitConfig(core, NULL);
|
||||
assert_true(core->loadROM(core, VFileFromConstMemory(_fakeGBROM, sizeof(_fakeGBROM))));
|
||||
core->reset(core);
|
||||
_setupBp(core);
|
||||
mScriptContextAttachCore(&context, core);
|
||||
|
||||
mDebuggerInit(&debugger);
|
||||
mDebuggerAttach(&debugger, core);
|
||||
|
||||
TEST_PROGRAM(
|
||||
"hit = 0\n"
|
||||
"function bkpt()\n"
|
||||
" hit = hit + 1\n"
|
||||
"end"
|
||||
);
|
||||
TEST_PROGRAM("cbid = emu:setBreakpoint(bkpt, 0xF0)");
|
||||
TEST_PROGRAM("assert(cbid == 1)");
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 20; ++i) {
|
||||
mDebuggerRun(&debugger);
|
||||
}
|
||||
|
||||
assert_int_equal(debugger.state, DEBUGGER_RUNNING);
|
||||
TEST_PROGRAM("assert(hit >= 1)");
|
||||
|
||||
mScriptContextDeinit(&context);
|
||||
TEARDOWN_CORE;
|
||||
mDebuggerDeinit(&debugger);
|
||||
}
|
||||
#endif
|
||||
|
||||
M_TEST_DEFINE(multipleBreakpoint) {
|
||||
SETUP_LUA;
|
||||
struct mCore* core = mCoreCreate(TEST_PLATFORM);
|
||||
struct mDebugger debugger;
|
||||
assert_non_null(core);
|
||||
assert_true(core->init(core));
|
||||
mCoreInitConfig(core, NULL);
|
||||
core->reset(core);
|
||||
_setupBp(core);
|
||||
mScriptContextAttachCore(&context, core);
|
||||
|
||||
mDebuggerInit(&debugger);
|
||||
mDebuggerAttach(&debugger, core);
|
||||
|
||||
TEST_PROGRAM(
|
||||
"hit = 0\n"
|
||||
"function bkpt1()\n"
|
||||
" hit = hit + 1\n"
|
||||
"end\n"
|
||||
"function bkpt2()\n"
|
||||
" hit = hit + 100\n"
|
||||
"end"
|
||||
);
|
||||
#ifdef M_CORE_GBA
|
||||
TEST_PROGRAM("cbid1 = emu:setBreakpoint(bkpt1, 0x020000C4)");
|
||||
TEST_PROGRAM("cbid2 = emu:setBreakpoint(bkpt2, 0x020000C8)");
|
||||
#else
|
||||
TEST_PROGRAM("cbid1 = emu:setBreakpoint(bkpt1, 0xF0)");
|
||||
TEST_PROGRAM("cbid2 = emu:setBreakpoint(bkpt2, 0xF1)");
|
||||
#endif
|
||||
TEST_PROGRAM("assert(cbid1 == 1)");
|
||||
TEST_PROGRAM("assert(cbid2 == 2)");
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 20; ++i) {
|
||||
mDebuggerRun(&debugger);
|
||||
}
|
||||
|
||||
assert_int_equal(debugger.state, DEBUGGER_RUNNING);
|
||||
TEST_PROGRAM("assert(hit >= 101)");
|
||||
|
||||
mScriptContextDeinit(&context);
|
||||
TEARDOWN_CORE;
|
||||
mDebuggerDeinit(&debugger);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(basicWatchpoint) {
|
||||
SETUP_LUA;
|
||||
mScriptContextAttachStdlib(&context);
|
||||
CREATE_CORE;
|
||||
struct mDebugger debugger;
|
||||
core->reset(core);
|
||||
mScriptContextAttachCore(&context, core);
|
||||
|
||||
mDebuggerInit(&debugger);
|
||||
mDebuggerAttach(&debugger, core);
|
||||
|
||||
TEST_PROGRAM(
|
||||
"hit = 0\n"
|
||||
"function bkpt()\n"
|
||||
" hit = hit + 1\n"
|
||||
"end"
|
||||
);
|
||||
struct mScriptValue base = mSCRIPT_MAKE_S32(RAM_BASE);
|
||||
lua->setGlobal(lua, "base", &base);
|
||||
TEST_PROGRAM("assert(0 < emu:setWatchpoint(bkpt, base, C.WATCHPOINT_TYPE.READ))");
|
||||
TEST_PROGRAM("assert(0 < emu:setWatchpoint(bkpt, base + 1, C.WATCHPOINT_TYPE.WRITE))");
|
||||
TEST_PROGRAM("assert(0 < emu:setWatchpoint(bkpt, base + 2, C.WATCHPOINT_TYPE.RW))");
|
||||
TEST_PROGRAM("assert(0 < emu:setWatchpoint(bkpt, base + 3, C.WATCHPOINT_TYPE.WRITE_CHANGE))");
|
||||
TEST_PROGRAM("assert(hit == 0)");
|
||||
|
||||
uint8_t value;
|
||||
|
||||
// Read
|
||||
TEST_PROGRAM("hit = 0");
|
||||
value = core->rawRead8(core, RAM_BASE, -1);
|
||||
TEST_PROGRAM("assert(hit == 0)");
|
||||
core->busRead8(core, RAM_BASE);
|
||||
TEST_PROGRAM("assert(hit == 1)");
|
||||
core->busWrite8(core, RAM_BASE, value);
|
||||
TEST_PROGRAM("assert(hit == 1)");
|
||||
core->busWrite8(core, RAM_BASE, ~value);
|
||||
TEST_PROGRAM("assert(hit == 1)");
|
||||
|
||||
// Write
|
||||
TEST_PROGRAM("hit = 0");
|
||||
value = core->rawRead8(core, RAM_BASE + 1, -1);
|
||||
TEST_PROGRAM("assert(hit == 0)");
|
||||
core->busRead8(core, RAM_BASE + 1);
|
||||
TEST_PROGRAM("assert(hit == 0)");
|
||||
core->busWrite8(core, RAM_BASE + 1, value);
|
||||
TEST_PROGRAM("assert(hit == 1)");
|
||||
core->busWrite8(core, RAM_BASE + 1, ~value);
|
||||
TEST_PROGRAM("assert(hit == 2)");
|
||||
|
||||
// RW
|
||||
TEST_PROGRAM("hit = 0");
|
||||
value = core->rawRead8(core, RAM_BASE + 2, -1);
|
||||
TEST_PROGRAM("assert(hit == 0)");
|
||||
core->busRead8(core, RAM_BASE + 2);
|
||||
TEST_PROGRAM("assert(hit == 1)");
|
||||
core->busWrite8(core, RAM_BASE + 2, value);
|
||||
TEST_PROGRAM("assert(hit == 2)");
|
||||
core->busWrite8(core, RAM_BASE + 2, ~value);
|
||||
TEST_PROGRAM("assert(hit == 3)");
|
||||
|
||||
// Change
|
||||
TEST_PROGRAM("hit = 0");
|
||||
value = core->rawRead8(core, RAM_BASE + 3, -1);
|
||||
TEST_PROGRAM("assert(hit == 0)");
|
||||
core->busRead8(core, RAM_BASE + 3);
|
||||
TEST_PROGRAM("assert(hit == 0)");
|
||||
core->busWrite8(core, RAM_BASE + 3, value);
|
||||
TEST_PROGRAM("assert(hit == 0)");
|
||||
core->busWrite8(core, RAM_BASE + 3, ~value);
|
||||
TEST_PROGRAM("assert(hit == 1)");
|
||||
|
||||
mScriptContextDeinit(&context);
|
||||
TEARDOWN_CORE;
|
||||
mDebuggerDeinit(&debugger);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(removeBreakpoint) {
|
||||
SETUP_LUA;
|
||||
mScriptContextAttachStdlib(&context);
|
||||
CREATE_CORE;
|
||||
struct mDebugger debugger;
|
||||
core->reset(core);
|
||||
mScriptContextAttachCore(&context, core);
|
||||
|
||||
mDebuggerInit(&debugger);
|
||||
mDebuggerAttach(&debugger, core);
|
||||
|
||||
TEST_PROGRAM(
|
||||
"hit = 0\n"
|
||||
"function bkpt()\n"
|
||||
" hit = hit + 1\n"
|
||||
"end"
|
||||
);
|
||||
struct mScriptValue base = mSCRIPT_MAKE_S32(RAM_BASE);
|
||||
lua->setGlobal(lua, "base", &base);
|
||||
TEST_PROGRAM("cbid = emu:setWatchpoint(bkpt, base, C.WATCHPOINT_TYPE.READ)");
|
||||
|
||||
TEST_PROGRAM("assert(hit == 0)");
|
||||
core->busRead8(core, RAM_BASE);
|
||||
TEST_PROGRAM("assert(hit == 1)");
|
||||
core->busRead8(core, RAM_BASE);
|
||||
TEST_PROGRAM("assert(hit == 2)");
|
||||
TEST_PROGRAM("assert(emu:clearBreakpoint(cbid))");
|
||||
core->busRead8(core, RAM_BASE);
|
||||
TEST_PROGRAM("assert(hit == 2)");
|
||||
|
||||
mScriptContextDeinit(&context);
|
||||
TEARDOWN_CORE;
|
||||
mDebuggerDeinit(&debugger);
|
||||
}
|
||||
|
||||
|
||||
M_TEST_DEFINE(overlappingBreakpoint) {
|
||||
SETUP_LUA;
|
||||
struct mCore* core = mCoreCreate(TEST_PLATFORM);
|
||||
struct mDebugger debugger;
|
||||
assert_non_null(core);
|
||||
assert_true(core->init(core));
|
||||
mCoreInitConfig(core, NULL);
|
||||
core->reset(core);
|
||||
_setupBp(core);
|
||||
mScriptContextAttachCore(&context, core);
|
||||
|
||||
mDebuggerInit(&debugger);
|
||||
mDebuggerAttach(&debugger, core);
|
||||
|
||||
TEST_PROGRAM(
|
||||
"hit = 0\n"
|
||||
"function bkpt1()\n"
|
||||
" hit = hit + 1\n"
|
||||
"end\n"
|
||||
"function bkpt2()\n"
|
||||
" hit = hit + 100\n"
|
||||
"end"
|
||||
);
|
||||
#ifdef M_CORE_GBA
|
||||
TEST_PROGRAM("cbid1 = emu:setBreakpoint(bkpt1, 0x020000C4)");
|
||||
TEST_PROGRAM("cbid2 = emu:setBreakpoint(bkpt2, 0x020000C4)");
|
||||
#else
|
||||
TEST_PROGRAM("cbid1 = emu:setBreakpoint(bkpt1, 0xF0)");
|
||||
TEST_PROGRAM("cbid2 = emu:setBreakpoint(bkpt2, 0xF0)");
|
||||
#endif
|
||||
TEST_PROGRAM("assert(cbid1 == 1)");
|
||||
TEST_PROGRAM("assert(cbid2 == 2)");
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 20; ++i) {
|
||||
mDebuggerRun(&debugger);
|
||||
}
|
||||
|
||||
assert_int_equal(debugger.state, DEBUGGER_RUNNING);
|
||||
TEST_PROGRAM("assert(hit >= 101)");
|
||||
TEST_PROGRAM("oldHit = hit");
|
||||
|
||||
TEST_PROGRAM("assert(emu:clearBreakpoint(cbid2))");
|
||||
|
||||
for (i = 0; i < 10; ++i) {
|
||||
mDebuggerRun(&debugger);
|
||||
}
|
||||
TEST_PROGRAM("assert(hit - oldHit > 0)");
|
||||
TEST_PROGRAM("assert(hit - oldHit < 100)");
|
||||
|
||||
mScriptContextDeinit(&context);
|
||||
TEARDOWN_CORE;
|
||||
mDebuggerDeinit(&debugger);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(overlappingWatchpoint) {
|
||||
SETUP_LUA;
|
||||
mScriptContextAttachStdlib(&context);
|
||||
CREATE_CORE;
|
||||
struct mDebugger debugger;
|
||||
core->reset(core);
|
||||
mScriptContextAttachCore(&context, core);
|
||||
|
||||
mDebuggerInit(&debugger);
|
||||
mDebuggerAttach(&debugger, core);
|
||||
|
||||
TEST_PROGRAM(
|
||||
"hit = 0\n"
|
||||
"function bkpt()\n"
|
||||
" hit = hit + 1\n"
|
||||
"end"
|
||||
);
|
||||
struct mScriptValue base = mSCRIPT_MAKE_S32(RAM_BASE);
|
||||
lua->setGlobal(lua, "base", &base);
|
||||
TEST_PROGRAM("assert(0 < emu:setWatchpoint(bkpt, base, C.WATCHPOINT_TYPE.READ))");
|
||||
TEST_PROGRAM("assert(0 < emu:setWatchpoint(bkpt, base, C.WATCHPOINT_TYPE.WRITE))");
|
||||
TEST_PROGRAM("assert(0 < emu:setWatchpoint(bkpt, base, C.WATCHPOINT_TYPE.RW))");
|
||||
TEST_PROGRAM("assert(0 < emu:setWatchpoint(bkpt, base, C.WATCHPOINT_TYPE.WRITE_CHANGE))");
|
||||
TEST_PROGRAM("assert(hit == 0)");
|
||||
|
||||
uint8_t value;
|
||||
|
||||
// Read
|
||||
TEST_PROGRAM("hit = 0");
|
||||
value = core->rawRead8(core, RAM_BASE, -1);
|
||||
TEST_PROGRAM("assert(hit == 0)");
|
||||
core->busRead8(core, RAM_BASE);
|
||||
TEST_PROGRAM("assert(hit == 2)"); // Read, RW
|
||||
core->busWrite8(core, RAM_BASE, value);
|
||||
TEST_PROGRAM("assert(hit == 4)"); // Write, RW
|
||||
core->busWrite8(core, RAM_BASE, ~value);
|
||||
TEST_PROGRAM("assert(hit == 7)"); // Write, RW, change
|
||||
|
||||
mScriptContextDeinit(&context);
|
||||
TEARDOWN_CORE;
|
||||
mDebuggerDeinit(&debugger);
|
||||
}
|
||||
|
||||
M_TEST_DEFINE(rangeWatchpoint) {
|
||||
SETUP_LUA;
|
||||
mScriptContextAttachStdlib(&context);
|
||||
CREATE_CORE;
|
||||
struct mDebugger debugger;
|
||||
core->reset(core);
|
||||
mScriptContextAttachCore(&context, core);
|
||||
|
||||
mDebuggerInit(&debugger);
|
||||
mDebuggerAttach(&debugger, core);
|
||||
|
||||
TEST_PROGRAM(
|
||||
"hit = 0\n"
|
||||
"function bkpt()\n"
|
||||
" hit = hit + 1\n"
|
||||
"end"
|
||||
);
|
||||
struct mScriptValue base = mSCRIPT_MAKE_S32(RAM_BASE);
|
||||
lua->setGlobal(lua, "base", &base);
|
||||
TEST_PROGRAM("assert(0 < emu:setRangeWatchpoint(bkpt, base, base + 2, C.WATCHPOINT_TYPE.READ))");
|
||||
TEST_PROGRAM("assert(0 < emu:setRangeWatchpoint(bkpt, base + 1, base + 3, C.WATCHPOINT_TYPE.READ))");
|
||||
|
||||
// Read
|
||||
TEST_PROGRAM("assert(hit == 0)");
|
||||
core->busRead8(core, RAM_BASE);
|
||||
TEST_PROGRAM("assert(hit == 1)");
|
||||
core->busRead8(core, RAM_BASE + 1);
|
||||
TEST_PROGRAM("assert(hit == 3)");
|
||||
core->busRead8(core, RAM_BASE + 2);
|
||||
TEST_PROGRAM("assert(hit == 4)");
|
||||
|
||||
mScriptContextDeinit(&context);
|
||||
TEARDOWN_CORE;
|
||||
mDebuggerDeinit(&debugger);
|
||||
}
|
||||
#endif
|
||||
|
||||
M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptCore,
|
||||
cmocka_unit_test(globals),
|
||||
cmocka_unit_test(infoFuncs),
|
||||
|
@ -318,4 +726,19 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptCore,
|
|||
cmocka_unit_test(memoryRead),
|
||||
cmocka_unit_test(memoryWrite),
|
||||
cmocka_unit_test(logging),
|
||||
cmocka_unit_test(screenshot),
|
||||
#ifdef USE_DEBUGGERS
|
||||
#ifdef M_CORE_GBA
|
||||
cmocka_unit_test(basicBreakpointGBA),
|
||||
#endif
|
||||
#ifdef M_CORE_GB
|
||||
cmocka_unit_test(basicBreakpointGB),
|
||||
#endif
|
||||
cmocka_unit_test(multipleBreakpoint),
|
||||
cmocka_unit_test(basicWatchpoint),
|
||||
cmocka_unit_test(removeBreakpoint),
|
||||
cmocka_unit_test(overlappingBreakpoint),
|
||||
cmocka_unit_test(overlappingWatchpoint),
|
||||
cmocka_unit_test(rangeWatchpoint),
|
||||
#endif
|
||||
)
|
||||
|
|
|
@ -109,10 +109,7 @@ static void _wait(struct mCoreThreadInternal* threadContext) {
|
|||
|
||||
#ifdef USE_DEBUGGERS
|
||||
if (threadContext->core && threadContext->core->debugger) {
|
||||
struct mDebugger* debugger = threadContext->core->debugger;
|
||||
if (debugger->interrupt) {
|
||||
debugger->interrupt(debugger);
|
||||
}
|
||||
mDebuggerInterrupt(threadContext->core->debugger);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -157,7 +154,11 @@ void _frameStarted(void* context) {
|
|||
}
|
||||
if (thread->core->opts.rewindEnable && thread->core->opts.rewindBufferCapacity > 0) {
|
||||
if (!thread->impl->rewinding || !mCoreRewindRestore(&thread->impl->rewind, thread->core)) {
|
||||
mCoreRewindAppend(&thread->impl->rewind, thread->core);
|
||||
if (thread->impl->rewind.rewindFrameCounter == 0) {
|
||||
mCoreRewindAppend(&thread->impl->rewind, thread->core);
|
||||
thread->impl->rewind.rewindFrameCounter = thread->core->opts.rewindBufferInterval;
|
||||
}
|
||||
thread->impl->rewind.rewindFrameCounter--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -348,8 +349,8 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
|||
|
||||
while (impl->state >= mTHREAD_MIN_WAITING && impl->state <= mTHREAD_MAX_WAITING) {
|
||||
#ifdef USE_DEBUGGERS
|
||||
if (debugger && debugger->update && debugger->state != DEBUGGER_SHUTDOWN) {
|
||||
debugger->update(debugger);
|
||||
if (debugger && debugger->state != DEBUGGER_SHUTDOWN) {
|
||||
mDebuggerUpdate(debugger);
|
||||
ConditionWaitTimed(&impl->stateCond, &impl->stateMutex, 10);
|
||||
} else
|
||||
#endif
|
||||
|
|
|
@ -191,7 +191,7 @@ static void _breakInto(struct CLIDebugger* debugger, struct CLIDebugVector* dv)
|
|||
#endif
|
||||
|
||||
static bool CLIDebuggerCheckTraceMode(struct CLIDebugger* debugger, bool requireEnabled) {
|
||||
struct mDebuggerPlatform* platform = debugger->d.platform;
|
||||
struct mDebuggerPlatform* platform = debugger->d.p->platform;
|
||||
if (!platform->getStackTraceMode) {
|
||||
debugger->backend->printf(debugger->backend, "Stack tracing is not supported by this platform.\n");
|
||||
return false;
|
||||
|
@ -204,13 +204,14 @@ static bool CLIDebuggerCheckTraceMode(struct CLIDebugger* debugger, bool require
|
|||
|
||||
static void _continue(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
UNUSED(dv);
|
||||
debugger->d.state = debugger->traceRemaining != 0 ? DEBUGGER_CALLBACK : DEBUGGER_RUNNING;
|
||||
debugger->d.needsCallback = debugger->traceRemaining != 0;
|
||||
debugger->d.isPaused = false;
|
||||
}
|
||||
|
||||
static void _next(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
UNUSED(dv);
|
||||
struct mDebuggerPlatform* platform = debugger->d.platform;
|
||||
debugger->d.core->step(debugger->d.core);
|
||||
struct mDebuggerPlatform* platform = debugger->d.p->platform;
|
||||
debugger->d.p->core->step(debugger->d.p->core);
|
||||
if (platform->getStackTraceMode && platform->getStackTraceMode(platform) != STACK_TRACE_DISABLED) {
|
||||
platform->updateStackTrace(platform);
|
||||
}
|
||||
|
@ -221,7 +222,7 @@ static void _disassemble(struct CLIDebugger* debugger, struct CLIDebugVector* dv
|
|||
debugger->system->disassemble(debugger->system, dv);
|
||||
}
|
||||
|
||||
static bool _parseExpression(struct mDebugger* debugger, struct CLIDebugVector* dv, int32_t* intValue, int* segmentValue) {
|
||||
static bool _parseExpression(struct mDebuggerModule* debugger, struct CLIDebugVector* dv, int32_t* intValue, int* segmentValue) {
|
||||
size_t args = 0;
|
||||
struct CLIDebugVector* accum;
|
||||
for (accum = dv; accum; accum = accum->next) {
|
||||
|
@ -240,7 +241,7 @@ static bool _parseExpression(struct mDebugger* debugger, struct CLIDebugVector*
|
|||
if (!tree) {
|
||||
return false;
|
||||
}
|
||||
if (!mDebuggerEvaluateParseTree(debugger, tree, intValue, segmentValue)) {
|
||||
if (!mDebuggerEvaluateParseTree(debugger->p, tree, intValue, segmentValue)) {
|
||||
parseFree(tree);
|
||||
return false;
|
||||
}
|
||||
|
@ -366,7 +367,7 @@ static void _printHelp(struct CLIDebugger* debugger, struct CLIDebugVector* dv)
|
|||
|
||||
static void _quit(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
UNUSED(dv);
|
||||
debugger->d.state = DEBUGGER_SHUTDOWN;
|
||||
mDebuggerShutdown(debugger->d.p);
|
||||
}
|
||||
|
||||
static void _readByte(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
|
@ -377,17 +378,17 @@ static void _readByte(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
|||
uint32_t address = dv->intValue;
|
||||
uint8_t value;
|
||||
if (dv->segmentValue >= 0) {
|
||||
value = debugger->d.core->rawRead8(debugger->d.core, address, dv->segmentValue);
|
||||
value = debugger->d.p->core->rawRead8(debugger->d.p->core, address, dv->segmentValue);
|
||||
} else {
|
||||
value = debugger->d.core->busRead8(debugger->d.core, address);
|
||||
value = debugger->d.p->core->busRead8(debugger->d.p->core, address);
|
||||
}
|
||||
debugger->backend->printf(debugger->backend, " 0x%02X\n", value);
|
||||
}
|
||||
|
||||
static void _reset(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
UNUSED(dv);
|
||||
mStackTraceClear(&debugger->d.stackTrace);
|
||||
debugger->d.core->reset(debugger->d.core);
|
||||
mStackTraceClear(&debugger->d.p->stackTrace);
|
||||
debugger->d.p->core->reset(debugger->d.p->core);
|
||||
_printStatus(debugger, 0);
|
||||
}
|
||||
|
||||
|
@ -399,9 +400,9 @@ static void _readHalfword(struct CLIDebugger* debugger, struct CLIDebugVector* d
|
|||
uint32_t address = dv->intValue;
|
||||
uint16_t value;
|
||||
if (dv->segmentValue >= 0) {
|
||||
value = debugger->d.core->rawRead16(debugger->d.core, address & -1, dv->segmentValue);
|
||||
value = debugger->d.p->core->rawRead16(debugger->d.p->core, address & -1, dv->segmentValue);
|
||||
} else {
|
||||
value = debugger->d.core->busRead16(debugger->d.core, address & ~1);
|
||||
value = debugger->d.p->core->busRead16(debugger->d.p->core, address & ~1);
|
||||
}
|
||||
debugger->backend->printf(debugger->backend, " 0x%04X\n", value);
|
||||
}
|
||||
|
@ -414,9 +415,9 @@ static void _readWord(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
|||
uint32_t address = dv->intValue;
|
||||
uint32_t value;
|
||||
if (dv->segmentValue >= 0) {
|
||||
value = debugger->d.core->rawRead32(debugger->d.core, address & -3, dv->segmentValue);
|
||||
value = debugger->d.p->core->rawRead32(debugger->d.p->core, address & -3, dv->segmentValue);
|
||||
} else {
|
||||
value = debugger->d.core->busRead32(debugger->d.core, address & ~3);
|
||||
value = debugger->d.p->core->busRead32(debugger->d.p->core, address & ~3);
|
||||
}
|
||||
debugger->backend->printf(debugger->backend, " 0x%08X\n", value);
|
||||
}
|
||||
|
@ -437,9 +438,9 @@ static void _writeByte(struct CLIDebugger* debugger, struct CLIDebugVector* dv)
|
|||
return;
|
||||
}
|
||||
if (dv->segmentValue >= 0) {
|
||||
debugger->d.core->rawWrite8(debugger->d.core, address, value, dv->segmentValue);
|
||||
debugger->d.p->core->rawWrite8(debugger->d.p->core, address, dv->segmentValue, value);
|
||||
} else {
|
||||
debugger->d.core->busWrite8(debugger->d.core, address, value);
|
||||
debugger->d.p->core->busWrite8(debugger->d.p->core, address, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -459,9 +460,9 @@ static void _writeHalfword(struct CLIDebugger* debugger, struct CLIDebugVector*
|
|||
return;
|
||||
}
|
||||
if (dv->segmentValue >= 0) {
|
||||
debugger->d.core->rawWrite16(debugger->d.core, address, value, dv->segmentValue);
|
||||
debugger->d.p->core->rawWrite16(debugger->d.p->core, address, dv->segmentValue, value);
|
||||
} else {
|
||||
debugger->d.core->busWrite16(debugger->d.core, address, value);
|
||||
debugger->d.p->core->busWrite16(debugger->d.p->core, address, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -474,7 +475,7 @@ static void _writeRegister(struct CLIDebugger* debugger, struct CLIDebugVector*
|
|||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
|
||||
return;
|
||||
}
|
||||
if (!debugger->d.core->writeRegister(debugger->d.core, dv->charValue, &dv->next->intValue)) {
|
||||
if (!debugger->d.p->core->writeRegister(debugger->d.p->core, dv->charValue, &dv->next->intValue)) {
|
||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
|
||||
}
|
||||
}
|
||||
|
@ -491,9 +492,9 @@ static void _writeWord(struct CLIDebugger* debugger, struct CLIDebugVector* dv)
|
|||
uint32_t address = dv->intValue;
|
||||
uint32_t value = dv->next->intValue;
|
||||
if (dv->segmentValue >= 0) {
|
||||
debugger->d.core->rawWrite32(debugger->d.core, address, value, dv->segmentValue);
|
||||
debugger->d.p->core->rawWrite32(debugger->d.p->core, address, dv->segmentValue, value);
|
||||
} else {
|
||||
debugger->d.core->busWrite32(debugger->d.core, address, value);
|
||||
debugger->d.p->core->busWrite32(debugger->d.p->core, address, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -516,9 +517,9 @@ static void _dumpByte(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
|||
for (; line > 0; --line, ++address, --words) {
|
||||
uint32_t value;
|
||||
if (dv->segmentValue >= 0) {
|
||||
value = debugger->d.core->rawRead8(debugger->d.core, address, dv->segmentValue);
|
||||
value = debugger->d.p->core->rawRead8(debugger->d.p->core, address, dv->segmentValue);
|
||||
} else {
|
||||
value = debugger->d.core->busRead8(debugger->d.core, address);
|
||||
value = debugger->d.p->core->busRead8(debugger->d.p->core, address);
|
||||
}
|
||||
debugger->backend->printf(debugger->backend, " %02X", value);
|
||||
}
|
||||
|
@ -545,9 +546,9 @@ static void _dumpHalfword(struct CLIDebugger* debugger, struct CLIDebugVector* d
|
|||
for (; line > 0; --line, address += 2, --words) {
|
||||
uint32_t value;
|
||||
if (dv->segmentValue >= 0) {
|
||||
value = debugger->d.core->rawRead16(debugger->d.core, address, dv->segmentValue);
|
||||
value = debugger->d.p->core->rawRead16(debugger->d.p->core, address, dv->segmentValue);
|
||||
} else {
|
||||
value = debugger->d.core->busRead16(debugger->d.core, address);
|
||||
value = debugger->d.p->core->busRead16(debugger->d.p->core, address);
|
||||
}
|
||||
debugger->backend->printf(debugger->backend, " %04X", value);
|
||||
}
|
||||
|
@ -574,9 +575,9 @@ static void _dumpWord(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
|||
for (; line > 0; --line, address += 4, --words) {
|
||||
uint32_t value;
|
||||
if (dv->segmentValue >= 0) {
|
||||
value = debugger->d.core->rawRead32(debugger->d.core, address, dv->segmentValue);
|
||||
value = debugger->d.p->core->rawRead32(debugger->d.p->core, address, dv->segmentValue);
|
||||
} else {
|
||||
value = debugger->d.core->busRead32(debugger->d.core, address);
|
||||
value = debugger->d.p->core->busRead32(debugger->d.p->core, address);
|
||||
}
|
||||
debugger->backend->printf(debugger->backend, " %08X", value);
|
||||
}
|
||||
|
@ -590,8 +591,8 @@ static void _source(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
|||
debugger->backend->printf(debugger->backend, "Needs a filename\n");
|
||||
return;
|
||||
}
|
||||
if (debugger->d.bridge && mScriptBridgeLoadScript(debugger->d.bridge, dv->charValue)) {
|
||||
mScriptBridgeRun(debugger->d.bridge);
|
||||
if (debugger->d.p->bridge && mScriptBridgeLoadScript(debugger->d.p->bridge, dv->charValue)) {
|
||||
mScriptBridgeRun(debugger->d.p->bridge);
|
||||
} else {
|
||||
debugger->backend->printf(debugger->backend, "Failed to load script\n");
|
||||
}
|
||||
|
@ -647,7 +648,7 @@ static void _setBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector*
|
|||
return;
|
||||
}
|
||||
}
|
||||
ssize_t id = debugger->d.platform->setBreakpoint(debugger->d.platform, &breakpoint);
|
||||
ssize_t id = debugger->d.p->platform->setBreakpoint(debugger->d.p->platform, &debugger->d, &breakpoint);
|
||||
if (id > 0) {
|
||||
debugger->backend->printf(debugger->backend, INFO_BREAKPOINT_ADDED, id);
|
||||
}
|
||||
|
@ -658,7 +659,7 @@ static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector*
|
|||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
|
||||
return;
|
||||
}
|
||||
if (!debugger->d.platform->setWatchpoint) {
|
||||
if (!debugger->d.p->platform->setWatchpoint) {
|
||||
debugger->backend->printf(debugger->backend, "Watchpoints are not supported by this platform.\n");
|
||||
return;
|
||||
}
|
||||
|
@ -677,7 +678,7 @@ static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector*
|
|||
return;
|
||||
}
|
||||
}
|
||||
ssize_t id = debugger->d.platform->setWatchpoint(debugger->d.platform, &watchpoint);
|
||||
ssize_t id = debugger->d.p->platform->setWatchpoint(debugger->d.p->platform, &debugger->d, &watchpoint);
|
||||
if (id > 0) {
|
||||
debugger->backend->printf(debugger->backend, INFO_WATCHPOINT_ADDED, id);
|
||||
}
|
||||
|
@ -692,7 +693,7 @@ static void _setRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVec
|
|||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
|
||||
return;
|
||||
}
|
||||
if (!debugger->d.platform->setWatchpoint) {
|
||||
if (!debugger->d.p->platform->setWatchpoint) {
|
||||
debugger->backend->printf(debugger->backend, "Watchpoints are not supported by this platform.\n");
|
||||
return;
|
||||
}
|
||||
|
@ -719,7 +720,7 @@ static void _setRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVec
|
|||
return;
|
||||
}
|
||||
}
|
||||
ssize_t id = debugger->d.platform->setWatchpoint(debugger->d.platform, &watchpoint);
|
||||
ssize_t id = debugger->d.p->platform->setWatchpoint(debugger->d.p->platform, &debugger->d, &watchpoint);
|
||||
if (id > 0) {
|
||||
debugger->backend->printf(debugger->backend, INFO_WATCHPOINT_ADDED, id);
|
||||
}
|
||||
|
@ -763,14 +764,14 @@ static void _clearBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector
|
|||
return;
|
||||
}
|
||||
uint64_t id = dv->intValue;
|
||||
debugger->d.platform->clearBreakpoint(debugger->d.platform, id);
|
||||
debugger->d.p->platform->clearBreakpoint(debugger->d.p->platform, id);
|
||||
}
|
||||
|
||||
static void _listBreakpoints(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
UNUSED(dv);
|
||||
struct mBreakpointList breakpoints;
|
||||
mBreakpointListInit(&breakpoints, 0);
|
||||
debugger->d.platform->listBreakpoints(debugger->d.platform, &breakpoints);
|
||||
debugger->d.p->platform->listBreakpoints(debugger->d.p->platform, &debugger->d, &breakpoints);
|
||||
size_t i;
|
||||
for (i = 0; i < mBreakpointListSize(&breakpoints); ++i) {
|
||||
struct mBreakpoint* breakpoint = mBreakpointListGetPointer(&breakpoints, i);
|
||||
|
@ -787,7 +788,7 @@ static void _listWatchpoints(struct CLIDebugger* debugger, struct CLIDebugVector
|
|||
UNUSED(dv);
|
||||
struct mWatchpointList watchpoints;
|
||||
mWatchpointListInit(&watchpoints, 0);
|
||||
debugger->d.platform->listWatchpoints(debugger->d.platform, &watchpoints);
|
||||
debugger->d.p->platform->listWatchpoints(debugger->d.p->platform, &debugger->d, &watchpoints);
|
||||
size_t i;
|
||||
for (i = 0; i < mWatchpointListSize(&watchpoints); ++i) {
|
||||
struct mWatchpoint* watchpoint = mWatchpointListGetPointer(&watchpoints, i);
|
||||
|
@ -809,16 +810,21 @@ static void _listWatchpoints(struct CLIDebugger* debugger, struct CLIDebugVector
|
|||
}
|
||||
|
||||
static void _trace(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
if (!dv || dv->type != CLIDV_INT_TYPE) {
|
||||
if (!dv) {
|
||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
|
||||
return;
|
||||
}
|
||||
if (dv->type != CLIDV_INT_TYPE || dv->intValue < 0) {
|
||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
|
||||
return;
|
||||
}
|
||||
|
||||
debugger->traceRemaining = dv->intValue;
|
||||
if (debugger->traceVf) {
|
||||
debugger->traceVf->close(debugger->traceVf);
|
||||
debugger->traceVf = NULL;
|
||||
}
|
||||
debugger->d.needsCallback = debugger->traceRemaining != 0;
|
||||
if (debugger->traceRemaining == 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -826,7 +832,7 @@ static void _trace(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
|||
debugger->traceVf = VFileOpen(dv->next->charValue, O_CREAT | O_WRONLY | O_APPEND);
|
||||
}
|
||||
if (_doTrace(debugger)) {
|
||||
debugger->d.state = DEBUGGER_CALLBACK;
|
||||
mDebuggerUpdatePaused(debugger->d.p);
|
||||
} else {
|
||||
debugger->system->printStatus(debugger->system);
|
||||
}
|
||||
|
@ -836,7 +842,7 @@ static bool _doTrace(struct CLIDebugger* debugger) {
|
|||
char trace[1024];
|
||||
trace[sizeof(trace) - 1] = '\0';
|
||||
size_t traceSize = sizeof(trace) - 2;
|
||||
debugger->d.platform->trace(debugger->d.platform, trace, &traceSize);
|
||||
debugger->d.p->platform->trace(debugger->d.p->platform, trace, &traceSize);
|
||||
if (traceSize + 2 <= sizeof(trace)) {
|
||||
trace[traceSize] = '\n';
|
||||
trace[traceSize + 1] = '\0';
|
||||
|
@ -854,6 +860,7 @@ static bool _doTrace(struct CLIDebugger* debugger) {
|
|||
debugger->traceVf->close(debugger->traceVf);
|
||||
debugger->traceVf = NULL;
|
||||
}
|
||||
debugger->d.needsCallback = false;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -866,7 +873,7 @@ static void _printStatus(struct CLIDebugger* debugger, struct CLIDebugVector* dv
|
|||
|
||||
static void _events(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
UNUSED(dv);
|
||||
struct mTiming* timing = debugger->d.core->timing;
|
||||
struct mTiming* timing = debugger->d.p->core->timing;
|
||||
struct mTimingEvent* next = timing->root;
|
||||
for (; next; next = next->next) {
|
||||
debugger->backend->printf(debugger->backend, "%s in %i cycles\n", next->name, mTimingUntil(timing, next));
|
||||
|
@ -891,7 +898,7 @@ struct CLIDebugVector* CLIDVParse(struct CLIDebugger* debugger, const char* stri
|
|||
if (!parseLexedExpression(tree, &lv)) {
|
||||
dvTemp.type = CLIDV_ERROR_TYPE;
|
||||
} else {
|
||||
if (!mDebuggerEvaluateParseTree(&debugger->d, tree, &dvTemp.intValue, &dvTemp.segmentValue)) {
|
||||
if (!mDebuggerEvaluateParseTree(debugger->d.p, tree, &dvTemp.intValue, &dvTemp.segmentValue)) {
|
||||
dvTemp.type = CLIDV_ERROR_TYPE;
|
||||
}
|
||||
}
|
||||
|
@ -1085,7 +1092,7 @@ bool CLIDebuggerRunCommand(struct CLIDebugger* debugger, const char* line, size_
|
|||
return false;
|
||||
}
|
||||
|
||||
static void _commandLine(struct mDebugger* debugger) {
|
||||
static void _commandLine(struct mDebuggerModule* debugger, int32_t timeoutMs) {
|
||||
struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
|
||||
const char* line;
|
||||
size_t len;
|
||||
|
@ -1094,14 +1101,19 @@ static void _commandLine(struct mDebugger* debugger) {
|
|||
} else {
|
||||
_printStatus(cliDebugger, 0);
|
||||
}
|
||||
while (debugger->state == DEBUGGER_PAUSED) {
|
||||
line = cliDebugger->backend->readline(cliDebugger->backend, &len);
|
||||
if (!line || len == 0) {
|
||||
debugger->state = DEBUGGER_SHUTDOWN;
|
||||
while (debugger->isPaused && !mDebuggerIsShutdown(debugger->p)) {
|
||||
int poll = cliDebugger->backend->poll(cliDebugger->backend, timeoutMs);
|
||||
if (poll <= 0) {
|
||||
if (poll < 0) {
|
||||
mDebuggerShutdown(debugger->p);
|
||||
} else {
|
||||
cliDebugger->skipStatus = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (line[0] == '\033') {
|
||||
cliDebugger->skipStatus = true;
|
||||
line = cliDebugger->backend->readline(cliDebugger->backend, &len);
|
||||
if (!line || len == 0) {
|
||||
mDebuggerShutdown(debugger->p);
|
||||
return;
|
||||
}
|
||||
if (line[0] == '\n') {
|
||||
|
@ -1120,7 +1132,7 @@ static void _commandLine(struct mDebugger* debugger) {
|
|||
}
|
||||
}
|
||||
|
||||
static void _reportEntry(struct mDebugger* debugger, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
|
||||
static void _reportEntry(struct mDebuggerModule* debugger, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
|
||||
struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
|
||||
if (cliDebugger->traceRemaining > 0) {
|
||||
cliDebugger->traceRemaining = 0;
|
||||
|
@ -1162,7 +1174,7 @@ static void _reportEntry(struct mDebugger* debugger, enum mDebuggerEntryReason r
|
|||
case DEBUGGER_ENTER_STACK:
|
||||
if (info) {
|
||||
if (info->type.st.traceType == STACK_TRACE_BREAK_ON_CALL) {
|
||||
struct mStackTrace* stack = &cliDebugger->d.stackTrace;
|
||||
struct mStackTrace* stack = &cliDebugger->d.p->stackTrace;
|
||||
struct mStackFrame* frame = mStackTraceGetFrame(stack, 0);
|
||||
if (frame->interrupt) {
|
||||
cliDebugger->backend->printf(cliDebugger->backend, "Hit interrupt at at 0x%08X\n", info->address);
|
||||
|
@ -1180,7 +1192,7 @@ static void _reportEntry(struct mDebugger* debugger, enum mDebuggerEntryReason r
|
|||
}
|
||||
}
|
||||
|
||||
static void _cliDebuggerInit(struct mDebugger* debugger) {
|
||||
static void _cliDebuggerInit(struct mDebuggerModule* debugger) {
|
||||
struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
|
||||
cliDebugger->traceRemaining = 0;
|
||||
cliDebugger->traceVf = NULL;
|
||||
|
@ -1191,7 +1203,7 @@ static void _cliDebuggerInit(struct mDebugger* debugger) {
|
|||
}
|
||||
}
|
||||
|
||||
static void _cliDebuggerDeinit(struct mDebugger* debugger) {
|
||||
static void _cliDebuggerDeinit(struct mDebuggerModule* debugger) {
|
||||
struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
|
||||
if (cliDebugger->traceVf) {
|
||||
cliDebugger->traceVf->close(cliDebugger->traceVf);
|
||||
|
@ -1211,23 +1223,22 @@ static void _cliDebuggerDeinit(struct mDebugger* debugger) {
|
|||
}
|
||||
}
|
||||
|
||||
static void _cliDebuggerCustom(struct mDebugger* debugger) {
|
||||
static void _cliDebuggerCustom(struct mDebuggerModule* debugger) {
|
||||
struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
|
||||
bool retain = true;
|
||||
enum mDebuggerState next = DEBUGGER_RUNNING;
|
||||
if (cliDebugger->traceRemaining) {
|
||||
retain = _doTrace(cliDebugger) && retain;
|
||||
next = DEBUGGER_PAUSED;
|
||||
if (!_doTrace(cliDebugger)) {
|
||||
debugger->isPaused = true;
|
||||
debugger->needsCallback = false;
|
||||
}
|
||||
}
|
||||
if (cliDebugger->system) {
|
||||
retain = cliDebugger->system->custom(cliDebugger->system) && retain;
|
||||
}
|
||||
if (!retain && debugger->state == DEBUGGER_CALLBACK) {
|
||||
debugger->state = next;
|
||||
if (cliDebugger->system && cliDebugger->system->custom) {
|
||||
debugger->needsCallback = cliDebugger->system->custom(cliDebugger->system) || debugger->needsCallback;
|
||||
}
|
||||
|
||||
mDebuggerUpdatePaused(debugger->p);
|
||||
}
|
||||
|
||||
static void _cliDebuggerInterrupt(struct mDebugger* debugger) {
|
||||
static void _cliDebuggerInterrupt(struct mDebuggerModule* debugger) {
|
||||
struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
|
||||
if (cliDebugger->backend->interrupt) {
|
||||
cliDebugger->backend->interrupt(cliDebugger->backend);
|
||||
|
@ -1323,13 +1334,13 @@ static void _backtrace(struct CLIDebugger* debugger, struct CLIDebugVector* dv)
|
|||
if (!CLIDebuggerCheckTraceMode(debugger, true)) {
|
||||
return;
|
||||
}
|
||||
struct mStackTrace* stack = &debugger->d.stackTrace;
|
||||
struct mStackTrace* stack = &debugger->d.p->stackTrace;
|
||||
ssize_t frames = mStackTraceGetDepth(stack);
|
||||
if (dv && dv->type == CLIDV_INT_TYPE && dv->intValue < frames) {
|
||||
frames = dv->intValue;
|
||||
}
|
||||
ssize_t i;
|
||||
struct mDebuggerSymbols* symbolTable = debugger->d.core->symbolTable;
|
||||
struct mDebuggerSymbols* symbolTable = debugger->d.p->core->symbolTable;
|
||||
for (i = 0; i < frames; ++i) {
|
||||
char trace[1024];
|
||||
size_t traceSize = sizeof(trace) - 2;
|
||||
|
@ -1343,7 +1354,7 @@ static void _finish(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
|||
if (!CLIDebuggerCheckTraceMode(debugger, true)) {
|
||||
return;
|
||||
}
|
||||
struct mStackTrace* stack = &debugger->d.stackTrace;
|
||||
struct mStackTrace* stack = &debugger->d.p->stackTrace;
|
||||
struct mStackFrame* frame = mStackTraceGetFrame(stack, 0);
|
||||
if (!frame) {
|
||||
debugger->backend->printf(debugger->backend, "No current stack frame.\n");
|
||||
|
@ -1369,7 +1380,7 @@ static void _setStackTraceMode(struct CLIDebugger* debugger, struct CLIDebugVect
|
|||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
|
||||
return;
|
||||
}
|
||||
struct mDebuggerPlatform* platform = debugger->d.platform;
|
||||
struct mDebuggerPlatform* platform = debugger->d.p->platform;
|
||||
if (strcmp(dv->charValue, "off") == 0) {
|
||||
platform->setStackTraceMode(platform, STACK_TRACE_DISABLED);
|
||||
} else if (strcmp(dv->charValue, "trace-only") == 0) {
|
||||
|
@ -1386,7 +1397,7 @@ static void _setStackTraceMode(struct CLIDebugger* debugger, struct CLIDebugVect
|
|||
}
|
||||
|
||||
static void _loadSymbols(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
struct mDebuggerSymbols* symbolTable = debugger->d.core->symbolTable;
|
||||
struct mDebuggerSymbols* symbolTable = debugger->d.p->core->symbolTable;
|
||||
if (!symbolTable) {
|
||||
debugger->backend->printf(debugger->backend, "No symbol table available.\n");
|
||||
return;
|
||||
|
@ -1420,7 +1431,7 @@ static void _loadSymbols(struct CLIDebugger* debugger, struct CLIDebugVector* dv
|
|||
}
|
||||
|
||||
static void _setSymbol(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
struct mDebuggerSymbols* symbolTable = debugger->d.core->symbolTable;
|
||||
struct mDebuggerSymbols* symbolTable = debugger->d.p->core->symbolTable;
|
||||
if (!symbolTable) {
|
||||
debugger->backend->printf(debugger->backend, "No symbol table available.\n");
|
||||
return;
|
||||
|
@ -1437,7 +1448,7 @@ static void _setSymbol(struct CLIDebugger* debugger, struct CLIDebugVector* dv)
|
|||
}
|
||||
|
||||
static void _findSymbol(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
struct mDebuggerSymbols* symbolTable = debugger->d.core->symbolTable;
|
||||
struct mDebuggerSymbols* symbolTable = debugger->d.p->core->symbolTable;
|
||||
if (!symbolTable) {
|
||||
debugger->backend->printf(debugger->backend, "No symbol table available.\n");
|
||||
return;
|
||||
|
|
|
@ -24,17 +24,18 @@ mLOG_DEFINE_CATEGORY(DEBUGGER, "Debugger", "core.debugger");
|
|||
|
||||
DEFINE_VECTOR(mBreakpointList, struct mBreakpoint);
|
||||
DEFINE_VECTOR(mWatchpointList, struct mWatchpoint);
|
||||
DEFINE_VECTOR(mDebuggerModuleList, struct mDebuggerModule*);
|
||||
|
||||
static void mDebuggerInit(void* cpu, struct mCPUComponent* component);
|
||||
static void mDebuggerDeinit(struct mCPUComponent* component);
|
||||
static void _mDebuggerInit(void* cpu, struct mCPUComponent* component);
|
||||
static void _mDebuggerDeinit(struct mCPUComponent* component);
|
||||
|
||||
struct mDebugger* mDebuggerCreate(enum mDebuggerType type, struct mCore* core) {
|
||||
struct mDebuggerModule* mDebuggerCreateModule(enum mDebuggerType type, struct mCore* core) {
|
||||
if (!core->supportsDebuggerType(core, type)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
union DebugUnion {
|
||||
struct mDebugger d;
|
||||
struct mDebuggerModule d;
|
||||
struct CLIDebugger cli;
|
||||
#ifdef USE_GDB_STUB
|
||||
struct GDBStub gdb;
|
||||
|
@ -67,10 +68,21 @@ struct mDebugger* mDebuggerCreate(enum mDebuggerType type, struct mCore* core) {
|
|||
return &debugger->d;
|
||||
}
|
||||
|
||||
void mDebuggerInit(struct mDebugger* debugger) {
|
||||
memset(debugger, 0, sizeof(*debugger));
|
||||
mDebuggerModuleListInit(&debugger->modules, 4);
|
||||
TableInit(&debugger->pointOwner, 0, NULL);
|
||||
}
|
||||
|
||||
void mDebuggerDeinit(struct mDebugger* debugger) {
|
||||
mDebuggerModuleListDeinit(&debugger->modules);
|
||||
TableDeinit(&debugger->pointOwner);
|
||||
}
|
||||
|
||||
void mDebuggerAttach(struct mDebugger* debugger, struct mCore* core) {
|
||||
debugger->d.id = DEBUGGER_ID;
|
||||
debugger->d.init = mDebuggerInit;
|
||||
debugger->d.deinit = mDebuggerDeinit;
|
||||
debugger->d.init = _mDebuggerInit;
|
||||
debugger->d.deinit = _mDebuggerDeinit;
|
||||
debugger->core = core;
|
||||
if (!debugger->core->symbolTable) {
|
||||
debugger->core->loadSymbols(debugger->core, NULL);
|
||||
|
@ -80,7 +92,36 @@ void mDebuggerAttach(struct mDebugger* debugger, struct mCore* core) {
|
|||
core->attachDebugger(core, debugger);
|
||||
}
|
||||
|
||||
void mDebuggerRun(struct mDebugger* debugger) {
|
||||
void mDebuggerAttachModule(struct mDebugger* debugger, struct mDebuggerModule* module) {
|
||||
module->p = debugger;
|
||||
*mDebuggerModuleListAppend(&debugger->modules) = module;
|
||||
if (debugger->state > DEBUGGER_CREATED && debugger->state < DEBUGGER_SHUTDOWN) {
|
||||
if (module->init) {
|
||||
module->init(module);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mDebuggerDetachModule(struct mDebugger* debugger, struct mDebuggerModule* module) {
|
||||
size_t i;
|
||||
for (i = 0; i < mDebuggerModuleListSize(&debugger->modules); ++i) {
|
||||
if (module != *mDebuggerModuleListGetPointer(&debugger->modules, i)) {
|
||||
continue;
|
||||
}
|
||||
if (debugger->state > DEBUGGER_CREATED && debugger->state < DEBUGGER_SHUTDOWN) {
|
||||
if (module->deinit) {
|
||||
module->deinit(module);
|
||||
}
|
||||
}
|
||||
mDebuggerModuleListShift(&debugger->modules, i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void mDebuggerRunTimeout(struct mDebugger* debugger, int32_t timeoutMs) {
|
||||
size_t i;
|
||||
size_t anyPaused = 0;
|
||||
|
||||
switch (debugger->state) {
|
||||
case DEBUGGER_RUNNING:
|
||||
if (!debugger->platform->hasBreakpoints(debugger->platform)) {
|
||||
|
@ -93,20 +134,43 @@ void mDebuggerRun(struct mDebugger* debugger) {
|
|||
case DEBUGGER_CALLBACK:
|
||||
debugger->core->step(debugger->core);
|
||||
debugger->platform->checkBreakpoints(debugger->platform);
|
||||
debugger->custom(debugger);
|
||||
for (i = 0; i < mDebuggerModuleListSize(&debugger->modules); ++i) {
|
||||
struct mDebuggerModule* module = *mDebuggerModuleListGetPointer(&debugger->modules, i);
|
||||
if (module->needsCallback) {
|
||||
module->custom(module);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DEBUGGER_PAUSED:
|
||||
if (debugger->paused) {
|
||||
debugger->paused(debugger);
|
||||
} else {
|
||||
for (i = 0; i < mDebuggerModuleListSize(&debugger->modules); ++i) {
|
||||
struct mDebuggerModule* module = *mDebuggerModuleListGetPointer(&debugger->modules, i);
|
||||
if (module->isPaused) {
|
||||
if (module->paused) {
|
||||
module->paused(module, timeoutMs);
|
||||
}
|
||||
if (module->isPaused) {
|
||||
++anyPaused;
|
||||
}
|
||||
} else if (module->needsCallback) {
|
||||
module->custom(module);
|
||||
}
|
||||
}
|
||||
if (debugger->state == DEBUGGER_PAUSED && !anyPaused) {
|
||||
debugger->state = DEBUGGER_RUNNING;
|
||||
}
|
||||
break;
|
||||
case DEBUGGER_CREATED:
|
||||
mLOG(DEBUGGER, ERROR, "Attempted to run debugger before initializtion");
|
||||
return;
|
||||
case DEBUGGER_SHUTDOWN:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void mDebuggerRun(struct mDebugger* debugger) {
|
||||
mDebuggerRunTimeout(debugger, 50);
|
||||
}
|
||||
|
||||
void mDebuggerRunFrame(struct mDebugger* debugger) {
|
||||
uint32_t frame = debugger->core->frameCounter(debugger->core);
|
||||
do {
|
||||
|
@ -115,30 +179,115 @@ void mDebuggerRunFrame(struct mDebugger* debugger) {
|
|||
}
|
||||
|
||||
void mDebuggerEnter(struct mDebugger* debugger, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
|
||||
debugger->state = DEBUGGER_PAUSED;
|
||||
if (debugger->platform->entered) {
|
||||
debugger->platform->entered(debugger->platform, reason, info);
|
||||
}
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < mDebuggerModuleListSize(&debugger->modules); ++i) {
|
||||
struct mDebuggerModule* module = *mDebuggerModuleListGetPointer(&debugger->modules, i);
|
||||
if (info && info->target) {
|
||||
// This check needs to be in the loop to make sure we don't
|
||||
// accidentally enter a module that isn't registered.
|
||||
// This is an error by the caller, but it's good to check for.
|
||||
if (info->target != module) {
|
||||
continue;
|
||||
}
|
||||
// Make this the last loop so we don't hit this one twice
|
||||
i = mDebuggerModuleListSize(&debugger->modules) - 1;
|
||||
}
|
||||
module->isPaused = true;
|
||||
if (module->entered) {
|
||||
module->entered(module, reason, info);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
if (debugger->bridge) {
|
||||
mScriptBridgeDebuggerEntered(debugger->bridge, reason, info);
|
||||
}
|
||||
#endif
|
||||
|
||||
mDebuggerUpdatePaused(debugger);
|
||||
}
|
||||
|
||||
static void mDebuggerInit(void* cpu, struct mCPUComponent* component) {
|
||||
struct mDebugger* debugger = (struct mDebugger*) component;
|
||||
debugger->state = DEBUGGER_RUNNING;
|
||||
debugger->platform->init(cpu, debugger->platform);
|
||||
if (debugger->init) {
|
||||
debugger->init(debugger);
|
||||
void mDebuggerInterrupt(struct mDebugger* debugger) {
|
||||
size_t i;
|
||||
for (i = 0; i < mDebuggerModuleListSize(&debugger->modules); ++i) {
|
||||
struct mDebuggerModule* module = *mDebuggerModuleListGetPointer(&debugger->modules, i);
|
||||
if (module->interrupt) {
|
||||
module->interrupt(module);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mDebuggerDeinit(struct mCPUComponent* component) {
|
||||
void mDebuggerUpdatePaused(struct mDebugger* debugger) {
|
||||
if (debugger->state == DEBUGGER_SHUTDOWN) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t anyPaused = 0;
|
||||
size_t anyCallback = 0;
|
||||
size_t i;
|
||||
for (i = 0; i < mDebuggerModuleListSize(&debugger->modules); ++i) {
|
||||
struct mDebuggerModule* module = *mDebuggerModuleListGetPointer(&debugger->modules, i);
|
||||
if (module->isPaused) {
|
||||
++anyPaused;
|
||||
}
|
||||
if (module->needsCallback) {
|
||||
++anyCallback;
|
||||
}
|
||||
}
|
||||
if (anyPaused) {
|
||||
debugger->state = DEBUGGER_PAUSED;
|
||||
} else if (anyCallback) {
|
||||
debugger->state = DEBUGGER_CALLBACK;
|
||||
} else {
|
||||
debugger->state = DEBUGGER_RUNNING;
|
||||
}
|
||||
}
|
||||
|
||||
void mDebuggerShutdown(struct mDebugger* debugger) {
|
||||
debugger->state = DEBUGGER_SHUTDOWN;
|
||||
}
|
||||
|
||||
bool mDebuggerIsShutdown(const struct mDebugger* debugger) {
|
||||
return debugger->state == DEBUGGER_SHUTDOWN;
|
||||
}
|
||||
|
||||
void mDebuggerUpdate(struct mDebugger* debugger) {
|
||||
size_t i;
|
||||
for (i = 0; i < mDebuggerModuleListSize(&debugger->modules); ++i) {
|
||||
struct mDebuggerModule* module = *mDebuggerModuleListGetPointer(&debugger->modules, i);
|
||||
if (module->update) {
|
||||
module->update(module);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _mDebuggerInit(void* cpu, struct mCPUComponent* component) {
|
||||
struct mDebugger* debugger = (struct mDebugger*) component;
|
||||
if (debugger->deinit) {
|
||||
debugger->deinit(debugger);
|
||||
debugger->state = DEBUGGER_RUNNING;
|
||||
debugger->platform->init(cpu, debugger->platform);
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < mDebuggerModuleListSize(&debugger->modules); ++i) {
|
||||
struct mDebuggerModule* module = *mDebuggerModuleListGetPointer(&debugger->modules, i);
|
||||
if (module->init) {
|
||||
module->init(module);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _mDebuggerDeinit(struct mCPUComponent* component) {
|
||||
struct mDebugger* debugger = (struct mDebugger*) component;
|
||||
debugger->state = DEBUGGER_SHUTDOWN;
|
||||
size_t i;
|
||||
for (i = 0; i < mDebuggerModuleListSize(&debugger->modules); ++i) {
|
||||
struct mDebuggerModule* module = *mDebuggerModuleListGetPointer(&debugger->modules, i);
|
||||
if (module->deinit) {
|
||||
module->deinit(module);
|
||||
}
|
||||
}
|
||||
debugger->platform->deinit(debugger->platform);
|
||||
}
|
||||
|
@ -161,3 +310,8 @@ bool mDebuggerLookupIdentifier(struct mDebugger* debugger, const char* name, int
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void mDebuggerModuleSetNeedsCallback(struct mDebuggerModule* debugger) {
|
||||
debugger->needsCallback = true;
|
||||
mDebuggerUpdatePaused(debugger->p);
|
||||
}
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
#define SIGTRAP 5 /* Win32 Signals do not include SIGTRAP */
|
||||
#endif
|
||||
|
||||
#define SOCKET_TIMEOUT 50
|
||||
|
||||
enum GDBError {
|
||||
GDB_NO_ERROR = 0x00,
|
||||
GDB_BAD_ARGUMENTS = 0x06,
|
||||
|
@ -50,20 +48,30 @@ static const char* TARGET_XML = "<target version=\"1.0\">"
|
|||
"<reg name=\"sp\" bitsize=\"32\" type=\"data_ptr\"/>"
|
||||
"<reg name=\"lr\" bitsize=\"32\"/>"
|
||||
"<reg name=\"pc\" bitsize=\"32\" type=\"code_ptr\"/>"
|
||||
"<reg name=\"cpsr\" bitsize=\"32\" regnum=\"25\"/>"
|
||||
"<flags id=\"cpsr_flags\" size=\"4\">"
|
||||
"<field name=\"N\" start=\"31\" end=\"31\"/>"
|
||||
"<field name=\"Z\" start=\"30\" end=\"30\"/>"
|
||||
"<field name=\"C\" start=\"29\" end=\"29\"/>"
|
||||
"<field name=\"V\" start=\"28\" end=\"28\"/>"
|
||||
"<field name=\"I\" start=\"7\" end=\"7\"/>"
|
||||
"<field name=\"F\" start=\"6\" end=\"6\"/>"
|
||||
"<field name=\"T\" start=\"5\" end=\"5\"/>"
|
||||
"<field name=\"M\" start=\"0\" end=\"4\"/>"
|
||||
"</flags>"
|
||||
"<reg name=\"cpsr\" bitsize=\"32\" regnum=\"25\" type=\"cpsr_flags\"/>"
|
||||
"</feature>"
|
||||
"</target>";
|
||||
|
||||
static void _sendMessage(struct GDBStub* stub);
|
||||
|
||||
static void _gdbStubDeinit(struct mDebugger* debugger) {
|
||||
static void _gdbStubDeinit(struct mDebuggerModule* debugger) {
|
||||
struct GDBStub* stub = (struct GDBStub*) debugger;
|
||||
if (!SOCKET_FAILED(stub->socket)) {
|
||||
GDBStubShutdown(stub);
|
||||
}
|
||||
}
|
||||
|
||||
static void _gdbStubEntered(struct mDebugger* debugger, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
|
||||
static void _gdbStubEntered(struct mDebuggerModule* debugger, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
|
||||
struct GDBStub* stub = (struct GDBStub*) debugger;
|
||||
switch (reason) {
|
||||
case DEBUGGER_ENTER_MANUAL:
|
||||
|
@ -118,27 +126,24 @@ static void _gdbStubEntered(struct mDebugger* debugger, enum mDebuggerEntryReaso
|
|||
_sendMessage(stub);
|
||||
}
|
||||
|
||||
static void _gdbStubPoll(struct mDebugger* debugger) {
|
||||
static void _gdbStubPoll(struct mDebuggerModule* debugger) {
|
||||
struct GDBStub* stub = (struct GDBStub*) debugger;
|
||||
--stub->untilPoll;
|
||||
if (stub->untilPoll > 0) {
|
||||
return;
|
||||
}
|
||||
stub->untilPoll = GDB_STUB_INTERVAL;
|
||||
stub->shouldBlock = false;
|
||||
GDBStubUpdate(stub);
|
||||
GDBStubUpdate(stub, 0);
|
||||
}
|
||||
|
||||
static void _gdbStubWait(struct mDebugger* debugger) {
|
||||
static void _gdbStubWait(struct mDebuggerModule* debugger, int32_t timeoutMs) {
|
||||
struct GDBStub* stub = (struct GDBStub*) debugger;
|
||||
stub->shouldBlock = true;
|
||||
GDBStubUpdate(stub);
|
||||
GDBStubUpdate(stub, timeoutMs);
|
||||
}
|
||||
|
||||
static void _gdbStubUpdate(struct mDebugger* debugger) {
|
||||
static void _gdbStubUpdate(struct mDebuggerModule* debugger) {
|
||||
struct GDBStub* stub = (struct GDBStub*) debugger;
|
||||
stub->shouldBlock = false;
|
||||
GDBStubUpdate(stub);
|
||||
GDBStubUpdate(stub, 0);
|
||||
}
|
||||
|
||||
static void _ack(struct GDBStub* stub) {
|
||||
|
@ -242,14 +247,15 @@ static void _writeHostInfo(struct GDBStub* stub) {
|
|||
}
|
||||
|
||||
static void _continue(struct GDBStub* stub, const char* message) {
|
||||
stub->d.state = DEBUGGER_CALLBACK;
|
||||
mDebuggerModuleSetNeedsCallback(&stub->d);
|
||||
stub->untilPoll = GDB_STUB_INTERVAL;
|
||||
stub->d.isPaused = false;
|
||||
// TODO: parse message
|
||||
UNUSED(message);
|
||||
}
|
||||
|
||||
static void _step(struct GDBStub* stub, const char* message) {
|
||||
stub->d.core->step(stub->d.core);
|
||||
stub->d.p->core->step(stub->d.p->core);
|
||||
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGTRAP);
|
||||
_sendMessage(stub);
|
||||
// TODO: parse message
|
||||
|
@ -271,7 +277,7 @@ static void _writeMemoryBinary(struct GDBStub* stub, const char* message) {
|
|||
return;
|
||||
}
|
||||
|
||||
struct ARMCore* cpu = stub->d.core->cpu;
|
||||
struct ARMCore* cpu = stub->d.p->core->cpu;
|
||||
for (i = 0; i < size; i++) {
|
||||
uint8_t byte = *readAddress;
|
||||
++readAddress;
|
||||
|
@ -305,7 +311,7 @@ static void _writeMemory(struct GDBStub* stub, const char* message) {
|
|||
return;
|
||||
}
|
||||
|
||||
struct ARMCore* cpu = stub->d.core->cpu;
|
||||
struct ARMCore* cpu = stub->d.p->core->cpu;
|
||||
for (i = 0; i < size; ++i, readAddress += 2) {
|
||||
uint8_t byte = _hex2int(readAddress, 2);
|
||||
GBAPatch8(cpu, address + i, byte, 0);
|
||||
|
@ -325,7 +331,7 @@ static void _readMemory(struct GDBStub* stub, const char* message) {
|
|||
_error(stub, GDB_BAD_ARGUMENTS);
|
||||
return;
|
||||
}
|
||||
struct ARMCore* cpu = stub->d.core->cpu;
|
||||
struct ARMCore* cpu = stub->d.p->core->cpu;
|
||||
int writeAddress = 0;
|
||||
for (i = 0; i < size; ++i, writeAddress += 2) {
|
||||
uint8_t byte = cpu->memory.load8(cpu, address + i, 0);
|
||||
|
@ -336,7 +342,7 @@ static void _readMemory(struct GDBStub* stub, const char* message) {
|
|||
}
|
||||
|
||||
static void _writeGPRs(struct GDBStub* stub, const char* message) {
|
||||
struct ARMCore* cpu = stub->d.core->cpu;
|
||||
struct ARMCore* cpu = stub->d.p->core->cpu;
|
||||
const char* readAddress = message;
|
||||
|
||||
int r;
|
||||
|
@ -355,7 +361,7 @@ static void _writeGPRs(struct GDBStub* stub, const char* message) {
|
|||
}
|
||||
|
||||
static void _readGPRs(struct GDBStub* stub, const char* message) {
|
||||
struct ARMCore* cpu = stub->d.core->cpu;
|
||||
struct ARMCore* cpu = stub->d.p->core->cpu;
|
||||
UNUSED(message);
|
||||
int r;
|
||||
int i = 0;
|
||||
|
@ -379,7 +385,7 @@ static void _readGPRs(struct GDBStub* stub, const char* message) {
|
|||
}
|
||||
|
||||
static void _writeRegister(struct GDBStub* stub, const char* message) {
|
||||
struct ARMCore* cpu = stub->d.core->cpu;
|
||||
struct ARMCore* cpu = stub->d.p->core->cpu;
|
||||
const char* readAddress = message;
|
||||
|
||||
unsigned i = 0;
|
||||
|
@ -412,7 +418,7 @@ static void _writeRegister(struct GDBStub* stub, const char* message) {
|
|||
}
|
||||
|
||||
static void _readRegister(struct GDBStub* stub, const char* message) {
|
||||
struct ARMCore* cpu = stub->d.core->cpu;
|
||||
struct ARMCore* cpu = stub->d.p->core->cpu;
|
||||
const char* readAddress = message;
|
||||
unsigned i = 0;
|
||||
uint32_t reg = _readHex(readAddress, &i);
|
||||
|
@ -497,7 +503,7 @@ static void _generateMemoryMapXml(struct GDBStub* stub, char* memoryMap) {
|
|||
strncpy(memoryMap, "<memory-map version=\"1.0\">", 27);
|
||||
index += strlen(memoryMap);
|
||||
const struct mCoreMemoryBlock* blocks;
|
||||
size_t nBlocks = stub->d.core->listMemoryBlocks(stub->d.core, &blocks);
|
||||
size_t nBlocks = stub->d.p->core->listMemoryBlocks(stub->d.p->core, &blocks);
|
||||
size_t i;
|
||||
for (i = 0; i < nBlocks; ++i) {
|
||||
if (!(blocks[i].flags & mCORE_MEMORY_MAPPED)) {
|
||||
|
@ -559,7 +565,11 @@ static void _processVReadCommand(struct GDBStub* stub, const char* message) {
|
|||
stub->outgoing[0] = '\0';
|
||||
if (!strncmp("Attach", message, 6)) {
|
||||
strncpy(stub->outgoing, "1", GDB_STUB_MAX_LINE - 4);
|
||||
mDebuggerEnter(&stub->d, DEBUGGER_ENTER_MANUAL, 0);
|
||||
stub->d.isPaused = true;
|
||||
struct mDebuggerEntryInfo info = {
|
||||
.target = &stub->d
|
||||
};
|
||||
mDebuggerEnter(stub->d.p, DEBUGGER_ENTER_MANUAL, &info);
|
||||
}
|
||||
_sendMessage(stub);
|
||||
}
|
||||
|
@ -582,19 +592,19 @@ static void _setBreakpoint(struct GDBStub* stub, const char* message) {
|
|||
switch (message[0]) {
|
||||
case '0':
|
||||
case '1':
|
||||
stub->d.platform->setBreakpoint(stub->d.platform, &breakpoint);
|
||||
stub->d.p->platform->setBreakpoint(stub->d.p->platform, &stub->d, &breakpoint);
|
||||
break;
|
||||
case '2':
|
||||
watchpoint.type = stub->watchpointsBehavior == GDB_WATCHPOINT_OVERRIDE_LOGIC_ANY_WRITE ? WATCHPOINT_WRITE : WATCHPOINT_WRITE_CHANGE;
|
||||
stub->d.platform->setWatchpoint(stub->d.platform, &watchpoint);
|
||||
stub->d.p->platform->setWatchpoint(stub->d.p->platform, &stub->d, &watchpoint);
|
||||
break;
|
||||
case '3':
|
||||
watchpoint.type = WATCHPOINT_READ;
|
||||
stub->d.platform->setWatchpoint(stub->d.platform, &watchpoint);
|
||||
stub->d.p->platform->setWatchpoint(stub->d.p->platform, &stub->d, &watchpoint);
|
||||
break;
|
||||
case '4':
|
||||
watchpoint.type = WATCHPOINT_RW;
|
||||
stub->d.platform->setWatchpoint(stub->d.platform, &watchpoint);
|
||||
stub->d.p->platform->setWatchpoint(stub->d.p->platform, &stub->d, &watchpoint);
|
||||
break;
|
||||
default:
|
||||
stub->outgoing[0] = '\0';
|
||||
|
@ -617,12 +627,12 @@ static void _clearBreakpoint(struct GDBStub* stub, const char* message) {
|
|||
case '0':
|
||||
case '1':
|
||||
mBreakpointListInit(&breakpoints, 0);
|
||||
stub->d.platform->listBreakpoints(stub->d.platform, &breakpoints);
|
||||
stub->d.p->platform->listBreakpoints(stub->d.p->platform, &stub->d, &breakpoints);
|
||||
for (index = 0; index < mBreakpointListSize(&breakpoints); ++index) {
|
||||
if (mBreakpointListGetPointer(&breakpoints, index)->address != address) {
|
||||
continue;
|
||||
}
|
||||
stub->d.platform->clearBreakpoint(stub->d.platform, mBreakpointListGetPointer(&breakpoints, index)->id);
|
||||
stub->d.p->platform->clearBreakpoint(stub->d.p->platform, mBreakpointListGetPointer(&breakpoints, index)->id);
|
||||
}
|
||||
mBreakpointListDeinit(&breakpoints);
|
||||
break;
|
||||
|
@ -630,13 +640,13 @@ static void _clearBreakpoint(struct GDBStub* stub, const char* message) {
|
|||
case '3':
|
||||
case '4':
|
||||
mWatchpointListInit(&watchpoints, 0);
|
||||
stub->d.platform->listWatchpoints(stub->d.platform, &watchpoints);
|
||||
stub->d.p->platform->listWatchpoints(stub->d.p->platform, &stub->d, &watchpoints);
|
||||
for (index = 0; index < mWatchpointListSize(&watchpoints); ++index) {
|
||||
struct mWatchpoint* watchpoint = mWatchpointListGetPointer(&watchpoints, index);
|
||||
if (address >= watchpoint->minAddress && address < watchpoint->maxAddress) {
|
||||
continue;
|
||||
}
|
||||
stub->d.platform->clearBreakpoint(stub->d.platform, watchpoint->id);
|
||||
stub->d.p->platform->clearBreakpoint(stub->d.p->platform, watchpoint->id);
|
||||
}
|
||||
mWatchpointListDeinit(&watchpoints);
|
||||
break;
|
||||
|
@ -650,6 +660,9 @@ static void _clearBreakpoint(struct GDBStub* stub, const char* message) {
|
|||
size_t _parseGDBMessage(struct GDBStub* stub, const char* message) {
|
||||
uint8_t checksum = 0;
|
||||
int parsed = 1;
|
||||
struct mDebuggerEntryInfo info = {
|
||||
.target = &stub->d
|
||||
};
|
||||
switch (*message) {
|
||||
case '+':
|
||||
stub->lineAck = GDB_ACK_RECEIVED;
|
||||
|
@ -661,7 +674,8 @@ size_t _parseGDBMessage(struct GDBStub* stub, const char* message) {
|
|||
++message;
|
||||
break;
|
||||
case '\x03':
|
||||
mDebuggerEnter(&stub->d, DEBUGGER_ENTER_MANUAL, 0);
|
||||
stub->d.isPaused = true;
|
||||
mDebuggerEnter(stub->d.p, DEBUGGER_ENTER_MANUAL, &info);
|
||||
return parsed;
|
||||
default:
|
||||
_nak(stub);
|
||||
|
@ -773,7 +787,6 @@ void GDBStubCreate(struct GDBStub* stub) {
|
|||
stub->d.type = DEBUGGER_GDB;
|
||||
stub->untilPoll = GDB_STUB_INTERVAL;
|
||||
stub->lineAck = GDB_ACK_PENDING;
|
||||
stub->shouldBlock = false;
|
||||
}
|
||||
|
||||
bool GDBStubListen(struct GDBStub* stub, int port, const struct Address* bindAddress, enum GDBWatchpointsBehvaior watchpointsBehavior) {
|
||||
|
@ -810,9 +823,9 @@ void GDBStubHangup(struct GDBStub* stub) {
|
|||
SocketClose(stub->connection);
|
||||
stub->connection = INVALID_SOCKET;
|
||||
}
|
||||
if (stub->d.state == DEBUGGER_PAUSED) {
|
||||
stub->d.state = DEBUGGER_RUNNING;
|
||||
}
|
||||
stub->d.needsCallback = false;
|
||||
stub->d.isPaused = false;
|
||||
mDebuggerUpdatePaused(stub->d.p);
|
||||
}
|
||||
|
||||
void GDBStubShutdown(struct GDBStub* stub) {
|
||||
|
@ -823,54 +836,57 @@ void GDBStubShutdown(struct GDBStub* stub) {
|
|||
}
|
||||
}
|
||||
|
||||
void GDBStubUpdate(struct GDBStub* stub) {
|
||||
bool GDBStubUpdate(struct GDBStub* stub, int32_t timeoutMs) {
|
||||
if (stub->socket == INVALID_SOCKET) {
|
||||
if (stub->d.state == DEBUGGER_PAUSED) {
|
||||
stub->d.state = DEBUGGER_RUNNING;
|
||||
}
|
||||
return;
|
||||
stub->d.needsCallback = false;
|
||||
stub->d.isPaused = false;
|
||||
mDebuggerUpdatePaused(stub->d.p);
|
||||
return false;
|
||||
}
|
||||
if (stub->connection == INVALID_SOCKET) {
|
||||
if (stub->shouldBlock) {
|
||||
if (timeoutMs) {
|
||||
Socket reads = stub->socket;
|
||||
SocketPoll(1, &reads, 0, 0, SOCKET_TIMEOUT);
|
||||
SocketPoll(1, &reads, 0, 0, timeoutMs);
|
||||
}
|
||||
stub->connection = SocketAccept(stub->socket, 0);
|
||||
if (!SOCKET_FAILED(stub->connection)) {
|
||||
if (!SocketSetBlocking(stub->connection, false)) {
|
||||
goto connectionLost;
|
||||
}
|
||||
mDebuggerEnter(&stub->d, DEBUGGER_ENTER_ATTACHED, 0);
|
||||
mDebuggerEnter(stub->d.p, DEBUGGER_ENTER_ATTACHED, 0);
|
||||
} else if (SocketWouldBlock()) {
|
||||
return;
|
||||
return false;
|
||||
} else {
|
||||
goto connectionLost;
|
||||
}
|
||||
SocketSetTCPPush(stub->connection, 1);
|
||||
}
|
||||
while (true) {
|
||||
if (stub->shouldBlock) {
|
||||
Socket reads = stub->connection;
|
||||
SocketPoll(1, &reads, 0, 0, SOCKET_TIMEOUT);
|
||||
}
|
||||
ssize_t messageLen = SocketRecv(stub->connection, stub->line, GDB_STUB_MAX_LINE - 1);
|
||||
if (messageLen == 0) {
|
||||
goto connectionLost;
|
||||
}
|
||||
if (messageLen == -1) {
|
||||
if (SocketWouldBlock()) {
|
||||
return;
|
||||
}
|
||||
goto connectionLost;
|
||||
}
|
||||
stub->line[messageLen] = '\0';
|
||||
mLOG(DEBUGGER, DEBUG, "< %s", stub->line);
|
||||
ssize_t position = 0;
|
||||
while (position < messageLen) {
|
||||
position += _parseGDBMessage(stub, &stub->line[position]);
|
||||
}
|
||||
|
||||
if (timeoutMs) {
|
||||
Socket reads = stub->connection;
|
||||
SocketPoll(1, &reads, 0, 0, timeoutMs);
|
||||
}
|
||||
ssize_t messageLen = SocketRecv(stub->connection, stub->line, GDB_STUB_MAX_LINE - 1);
|
||||
if (messageLen == 0) {
|
||||
goto connectionLost;
|
||||
}
|
||||
if (messageLen == -1) {
|
||||
if (SocketWouldBlock()) {
|
||||
return false;
|
||||
}
|
||||
goto connectionLost;
|
||||
}
|
||||
|
||||
stub->line[messageLen] = '\0';
|
||||
mLOG(DEBUGGER, DEBUG, "< %s", stub->line);
|
||||
ssize_t position = 0;
|
||||
while (position < messageLen) {
|
||||
position += _parseGDBMessage(stub, &stub->line[position]);
|
||||
}
|
||||
return true;
|
||||
|
||||
connectionLost:
|
||||
mLOG(DEBUGGER, WARN, "Connection lost");
|
||||
GDBStubHangup(stub);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
include(ExportDirectory)
|
||||
set(SOURCE_FILES
|
||||
commandline.c
|
||||
proxy-backend.c
|
||||
thread-proxy.c
|
||||
updater.c
|
||||
video-backend.c
|
||||
video-logger.c)
|
||||
|
||||
set(GUI_FILES
|
||||
|
|
|
@ -137,18 +137,14 @@ bool mArgumentsParse(struct mArguments* args, int argc, char* const* argv, struc
|
|||
break;
|
||||
#ifdef USE_EDITLINE
|
||||
case 'd':
|
||||
if (args->debuggerType != DEBUGGER_NONE) {
|
||||
return false;
|
||||
}
|
||||
args->debuggerType = DEBUGGER_CLI;
|
||||
args->debugAtStart = true;
|
||||
args->debugCli = true;
|
||||
break;
|
||||
#endif
|
||||
#ifdef USE_GDB_STUB
|
||||
case 'g':
|
||||
if (args->debuggerType != DEBUGGER_NONE) {
|
||||
return false;
|
||||
}
|
||||
args->debuggerType = DEBUGGER_GDB;
|
||||
args->debugAtStart = true;
|
||||
args->debugGdb = true;
|
||||
break;
|
||||
#endif
|
||||
case 'h':
|
||||
|
|
|
@ -7,12 +7,31 @@
|
|||
|
||||
#include <mgba/core/config.h>
|
||||
#include <mgba/core/version.h>
|
||||
#include <mgba-util/threading.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
struct CLIDebuggerEditLineBackend {
|
||||
struct CLIDebuggerBackend d;
|
||||
|
||||
EditLine* elstate;
|
||||
History* histate;
|
||||
|
||||
int count;
|
||||
const char* prompt;
|
||||
bool doPrompt;
|
||||
Thread promptThread;
|
||||
Mutex promptMutex;
|
||||
Condition promptRead;
|
||||
Condition promptWrite;
|
||||
bool exitThread;
|
||||
};
|
||||
|
||||
static struct CLIDebugger* _activeDebugger;
|
||||
|
||||
static THREAD_ENTRY _promptThread(void*);
|
||||
|
||||
static char* _prompt(EditLine* el) {
|
||||
UNUSED(el);
|
||||
return "> ";
|
||||
|
@ -20,7 +39,10 @@ static char* _prompt(EditLine* el) {
|
|||
|
||||
static void _breakIntoDefault(int signal) {
|
||||
UNUSED(signal);
|
||||
mDebuggerEnter(&_activeDebugger->d, DEBUGGER_ENTER_MANUAL, 0);
|
||||
struct mDebuggerEntryInfo info = {
|
||||
.target = &_activeDebugger->d
|
||||
};
|
||||
mDebuggerEnter(_activeDebugger->d.p, DEBUGGER_ENTER_MANUAL, &info);
|
||||
}
|
||||
|
||||
static unsigned char _tabComplete(EditLine* elstate, int ch) {
|
||||
|
@ -40,7 +62,7 @@ static unsigned char _tabComplete(EditLine* elstate, int ch) {
|
|||
}
|
||||
|
||||
ATTRIBUTE_FORMAT(printf, 2, 3)
|
||||
void _CLIDebuggerEditLinePrintf(struct CLIDebuggerBackend* be, const char* fmt, ...) {
|
||||
static void CLIDebuggerEditLinePrintf(struct CLIDebuggerBackend* be, const char* fmt, ...) {
|
||||
UNUSED(be);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
|
@ -48,7 +70,7 @@ void _CLIDebuggerEditLinePrintf(struct CLIDebuggerBackend* be, const char* fmt,
|
|||
va_end(args);
|
||||
}
|
||||
|
||||
void _CLIDebuggerEditLineInit(struct CLIDebuggerBackend* be) {
|
||||
static void CLIDebuggerEditLineInit(struct CLIDebuggerBackend* be) {
|
||||
struct CLIDebuggerEditLineBackend* elbe = (struct CLIDebuggerEditLineBackend*) be;
|
||||
// TODO: get argv[0]
|
||||
elbe->elstate = el_init(binaryName, stdin, stdout, stderr);
|
||||
|
@ -78,12 +100,26 @@ void _CLIDebuggerEditLineInit(struct CLIDebuggerBackend* be) {
|
|||
}
|
||||
}
|
||||
|
||||
MutexInit(&elbe->promptMutex);
|
||||
ConditionInit(&elbe->promptRead);
|
||||
ConditionInit(&elbe->promptWrite);
|
||||
elbe->prompt = NULL;
|
||||
elbe->exitThread = false;
|
||||
elbe->doPrompt = false;
|
||||
ThreadCreate(&elbe->promptThread, _promptThread, elbe);
|
||||
|
||||
_activeDebugger = be->p;
|
||||
signal(SIGINT, _breakIntoDefault);
|
||||
}
|
||||
|
||||
void _CLIDebuggerEditLineDeinit(struct CLIDebuggerBackend* be) {
|
||||
static void CLIDebuggerEditLineDeinit(struct CLIDebuggerBackend* be) {
|
||||
struct CLIDebuggerEditLineBackend* elbe = (struct CLIDebuggerEditLineBackend*) be;
|
||||
MutexLock(&elbe->promptMutex);
|
||||
elbe->exitThread = true;
|
||||
ConditionWake(&elbe->promptWrite);
|
||||
MutexUnlock(&elbe->promptMutex);
|
||||
ThreadJoin(&elbe->promptThread);
|
||||
|
||||
char path[PATH_MAX + 1];
|
||||
mCoreConfigDirectory(path, PATH_MAX);
|
||||
if (path[0]) {
|
||||
|
@ -108,11 +144,52 @@ void _CLIDebuggerEditLineDeinit(struct CLIDebuggerBackend* be) {
|
|||
free(elbe);
|
||||
}
|
||||
|
||||
const char* _CLIDebuggerEditLineReadLine(struct CLIDebuggerBackend* be, size_t* len) {
|
||||
static THREAD_ENTRY _promptThread(void* context) {
|
||||
struct CLIDebuggerEditLineBackend* elbe = context;
|
||||
|
||||
MutexLock(&elbe->promptMutex);
|
||||
while (!elbe->exitThread) {
|
||||
if (elbe->doPrompt) {
|
||||
MutexUnlock(&elbe->promptMutex);
|
||||
elbe->prompt = el_gets(elbe->elstate, &elbe->count);
|
||||
MutexLock(&elbe->promptMutex);
|
||||
elbe->doPrompt = false;
|
||||
ConditionWake(&elbe->promptRead);
|
||||
}
|
||||
ConditionWait(&elbe->promptWrite, &elbe->promptMutex);
|
||||
}
|
||||
MutexUnlock(&elbe->promptMutex);
|
||||
THREAD_EXIT(0);
|
||||
}
|
||||
|
||||
static int CLIDebuggerEditLinePoll(struct CLIDebuggerBackend* be, int32_t timeoutMs) {
|
||||
struct CLIDebuggerEditLineBackend* elbe = (struct CLIDebuggerEditLineBackend*) be;
|
||||
int gotPrompt = 0;
|
||||
MutexLock(&elbe->promptMutex);
|
||||
if (!elbe->prompt) {
|
||||
elbe->doPrompt = true;
|
||||
ConditionWake(&elbe->promptWrite);
|
||||
ConditionWaitTimed(&elbe->promptRead, &elbe->promptMutex, timeoutMs);
|
||||
}
|
||||
if (elbe->prompt) {
|
||||
gotPrompt = 1;
|
||||
}
|
||||
MutexUnlock(&elbe->promptMutex);
|
||||
|
||||
return gotPrompt;
|
||||
}
|
||||
|
||||
static const char* CLIDebuggerEditLineReadLine(struct CLIDebuggerBackend* be, size_t* len) {
|
||||
struct CLIDebuggerEditLineBackend* elbe = (struct CLIDebuggerEditLineBackend*) be;
|
||||
int count;
|
||||
*len = 0;
|
||||
const char* line = el_gets(elbe->elstate, &count);
|
||||
if (CLIDebuggerEditLinePoll(be, -1) != 1) {
|
||||
return NULL;
|
||||
}
|
||||
MutexLock(&elbe->promptMutex);
|
||||
int count = elbe->count;
|
||||
const char* line = elbe->prompt;
|
||||
elbe->prompt = NULL;
|
||||
MutexUnlock(&elbe->promptMutex);
|
||||
if (line) {
|
||||
if (count > 1) {
|
||||
// Crop off newline
|
||||
|
@ -123,12 +200,13 @@ const char* _CLIDebuggerEditLineReadLine(struct CLIDebuggerBackend* be, size_t*
|
|||
}
|
||||
return line;
|
||||
}
|
||||
void _CLIDebuggerEditLineLineAppend(struct CLIDebuggerBackend* be, const char* line) {
|
||||
|
||||
static void CLIDebuggerEditLineLineAppend(struct CLIDebuggerBackend* be, const char* line) {
|
||||
struct CLIDebuggerEditLineBackend* elbe = (struct CLIDebuggerEditLineBackend*) be;
|
||||
el_insertstr(elbe->elstate, line);
|
||||
}
|
||||
|
||||
const char* _CLIDebuggerEditLineHistoryLast(struct CLIDebuggerBackend* be, size_t* len) {
|
||||
static const char* CLIDebuggerEditLineHistoryLast(struct CLIDebuggerBackend* be, size_t* len) {
|
||||
struct CLIDebuggerEditLineBackend* elbe = (struct CLIDebuggerEditLineBackend*) be;
|
||||
HistEvent ev;
|
||||
if (history(elbe->histate, &ev, H_FIRST) < 0) {
|
||||
|
@ -145,7 +223,7 @@ const char* _CLIDebuggerEditLineHistoryLast(struct CLIDebuggerBackend* be, size_
|
|||
return ev.str;
|
||||
}
|
||||
|
||||
void _CLIDebuggerEditLineHistoryAppend(struct CLIDebuggerBackend* be, const char* line) {
|
||||
static void CLIDebuggerEditLineHistoryAppend(struct CLIDebuggerBackend* be, const char* line) {
|
||||
struct CLIDebuggerEditLineBackend* elbe = (struct CLIDebuggerEditLineBackend*) be;
|
||||
HistEvent ev;
|
||||
history(elbe->histate, &ev, H_ENTER, line);
|
||||
|
@ -153,13 +231,14 @@ void _CLIDebuggerEditLineHistoryAppend(struct CLIDebuggerBackend* be, const char
|
|||
|
||||
struct CLIDebuggerBackend* CLIDebuggerEditLineBackendCreate(void) {
|
||||
struct CLIDebuggerEditLineBackend* elbe = calloc(1, sizeof(*elbe));
|
||||
elbe->d.printf = _CLIDebuggerEditLinePrintf;
|
||||
elbe->d.init = _CLIDebuggerEditLineInit;
|
||||
elbe->d.deinit = _CLIDebuggerEditLineDeinit;
|
||||
elbe->d.readline = _CLIDebuggerEditLineReadLine;
|
||||
elbe->d.lineAppend = _CLIDebuggerEditLineLineAppend;
|
||||
elbe->d.historyLast = _CLIDebuggerEditLineHistoryLast;
|
||||
elbe->d.historyAppend = _CLIDebuggerEditLineHistoryAppend;
|
||||
elbe->d.printf = CLIDebuggerEditLinePrintf;
|
||||
elbe->d.init = CLIDebuggerEditLineInit;
|
||||
elbe->d.deinit = CLIDebuggerEditLineDeinit;
|
||||
elbe->d.poll = CLIDebuggerEditLinePoll;
|
||||
elbe->d.readline = CLIDebuggerEditLineReadLine;
|
||||
elbe->d.lineAppend = CLIDebuggerEditLineLineAppend;
|
||||
elbe->d.historyLast = CLIDebuggerEditLineHistoryLast;
|
||||
elbe->d.historyAppend = CLIDebuggerEditLineHistoryAppend;
|
||||
elbe->d.interrupt = NULL;
|
||||
return &elbe->d;
|
||||
}
|
||||
|
|
|
@ -14,13 +14,6 @@ CXX_GUARD_START
|
|||
|
||||
#include <histedit.h>
|
||||
|
||||
struct CLIDebuggerEditLineBackend {
|
||||
struct CLIDebuggerBackend d;
|
||||
|
||||
EditLine* elstate;
|
||||
History* histate;
|
||||
};
|
||||
|
||||
struct CLIDebuggerBackend* CLIDebuggerEditLineBackendCreate(void);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
|
|
@ -62,13 +62,13 @@ void FFmpegEncoderInit(struct FFmpegEncoder* encoder) {
|
|||
encoder->audioCodec = NULL;
|
||||
encoder->videoCodec = NULL;
|
||||
encoder->containerFormat = NULL;
|
||||
encoder->isampleRate = PREFERRED_SAMPLE_RATE;
|
||||
FFmpegEncoderSetAudio(encoder, "flac", 0);
|
||||
FFmpegEncoderSetVideo(encoder, "libx264", 0, 0);
|
||||
FFmpegEncoderSetContainer(encoder, "matroska");
|
||||
FFmpegEncoderSetDimensions(encoder, GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
|
||||
encoder->iwidth = GBA_VIDEO_HORIZONTAL_PIXELS;
|
||||
encoder->iheight = GBA_VIDEO_VERTICAL_PIXELS;
|
||||
encoder->isampleRate = PREFERRED_SAMPLE_RATE;
|
||||
encoder->frameskip = 1;
|
||||
encoder->skipResidue = 0;
|
||||
encoder->loop = false;
|
||||
|
@ -154,19 +154,35 @@ bool FFmpegEncoderSetAudio(struct FFmpegEncoder* encoder, const char* acodec, un
|
|||
}
|
||||
encoder->sampleRate = encoder->isampleRate;
|
||||
if (codec->supported_samplerates) {
|
||||
bool gotSampleRate = false;
|
||||
int highestSampleRate = 0;
|
||||
for (i = 0; codec->supported_samplerates[i]; ++i) {
|
||||
if (codec->supported_samplerates[i] > highestSampleRate) {
|
||||
highestSampleRate = codec->supported_samplerates[i];
|
||||
}
|
||||
if (codec->supported_samplerates[i] < encoder->isampleRate) {
|
||||
continue;
|
||||
}
|
||||
if (encoder->sampleRate == encoder->isampleRate || encoder->sampleRate > codec->supported_samplerates[i]) {
|
||||
if (!gotSampleRate || encoder->sampleRate > codec->supported_samplerates[i]) {
|
||||
encoder->sampleRate = codec->supported_samplerates[i];
|
||||
gotSampleRate = true;
|
||||
}
|
||||
}
|
||||
if (!gotSampleRate) {
|
||||
// There are no available sample rates that are higher than the input sample rate
|
||||
// Let's use the highest available instead
|
||||
encoder->sampleRate = highestSampleRate;
|
||||
}
|
||||
} else if (codec->id == AV_CODEC_ID_FLAC) {
|
||||
// HACK: FLAC doesn't support > 65535Hz unless it's divisible by 10
|
||||
if (encoder->sampleRate >= 65535) {
|
||||
encoder->sampleRate -= encoder->isampleRate % 10;
|
||||
}
|
||||
} else if (codec->id == AV_CODEC_ID_VORBIS) {
|
||||
// HACK: FLAC doesn't support > 48000Hz but doesn't tell us
|
||||
if (encoder->sampleRate > 48000) {
|
||||
encoder->sampleRate = 48000;
|
||||
}
|
||||
} else if (codec->id == AV_CODEC_ID_AAC) {
|
||||
// HACK: AAC doesn't support 32768Hz (it rounds to 32000), but libfaac doesn't tell us that
|
||||
encoder->sampleRate = 48000;
|
||||
|
@ -881,7 +897,7 @@ void FFmpegEncoderSetInputSampleRate(struct FFmpegEncoder* encoder, int sampleRa
|
|||
}
|
||||
|
||||
void _ffmpegOpenResampleContext(struct FFmpegEncoder* encoder) {
|
||||
encoder->audioBufferSize = av_rescale_q(encoder->audioFrame->nb_samples, (AVRational) { 4, encoder->sampleRate }, (AVRational) { 1, encoder->isampleRate });
|
||||
encoder->audioBufferSize = av_rescale_q(encoder->audioFrame->nb_samples, (AVRational) { 1, encoder->sampleRate }, (AVRational) { 1, encoder->isampleRate }) * 4;
|
||||
encoder->audioBuffer = av_malloc(encoder->audioBufferSize);
|
||||
#ifdef USE_LIBAVRESAMPLE
|
||||
encoder->resampleContext = avresample_alloc_context();
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <mgba/core/core.h>
|
||||
#include "feature/gui/gui-runner.h"
|
||||
#include <mgba-util/gui/menu.h>
|
||||
#include <mgba-util/string.h>
|
||||
|
||||
enum mGUICheatAction {
|
||||
CHEAT_BACK = 0,
|
||||
|
|
|
@ -31,7 +31,7 @@ static bool _biosNamed(const char* name) {
|
|||
char ext[PATH_MAX + 1] = {};
|
||||
separatePath(name, NULL, NULL, ext);
|
||||
|
||||
if (strstr(name, "bios")) {
|
||||
if (strcasestr(name, "bios")) {
|
||||
return true;
|
||||
}
|
||||
if (!strncmp(ext, "bin", PATH_MAX)) {
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
#include <mgba-util/gui/file-select.h>
|
||||
#include <mgba-util/gui/font.h>
|
||||
#include <mgba-util/gui/menu.h>
|
||||
#include <mgba-util/image/png-io.h>
|
||||
#include <mgba-util/memory.h>
|
||||
#include <mgba-util/png-io.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
#ifdef PSP2
|
||||
|
@ -456,6 +456,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
|
|||
mLOG(GUI_RUNNER, DEBUG, "Loading save...");
|
||||
mCoreAutoloadSave(runner->core);
|
||||
mCoreAutoloadCheats(runner->core);
|
||||
mCoreAutoloadPatch(runner->core);
|
||||
if (runner->setup) {
|
||||
mLOG(GUI_RUNNER, DEBUG, "Setting up runner...");
|
||||
runner->setup(runner);
|
||||
|
@ -655,6 +656,9 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
|
|||
runner->core->reset(runner->core);
|
||||
break;
|
||||
case RUNNER_SAVE_STATE:
|
||||
// If we are saving state, then the screenshot stored for the state previously should no longer be considered up-to-date.
|
||||
// Therefore, mark it as stale so that at draw time we load the new save state's screenshot.
|
||||
((struct mGUIBackground*) stateSaveMenu.background)->screenshotId |= SCREENSHOT_INVALID;
|
||||
mCoreSaveState(runner->core, item->data.v.u >> 16, SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_RTC | SAVESTATE_METADATA);
|
||||
break;
|
||||
case RUNNER_LOAD_STATE:
|
||||
|
@ -840,5 +844,6 @@ THREAD_ENTRY mGUIAutosaveThread(void* context) {
|
|||
}
|
||||
}
|
||||
MutexUnlock(&autosave->mutex);
|
||||
THREAD_EXIT(0);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,284 @@
|
|||
/* Copyright (c) 2013-2023 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 <mgba/feature/proxy-backend.h>
|
||||
|
||||
static void _mVideoProxyBackendInit(struct VideoBackend* v, WHandle handle) {
|
||||
struct mVideoProxyBackend* proxy = (struct mVideoProxyBackend*) v;
|
||||
struct mVideoBackendCommand cmd = {
|
||||
.cmd = mVB_CMD_INIT,
|
||||
.handle = handle
|
||||
};
|
||||
mVideoProxyBackendSubmit(proxy, &cmd, NULL);
|
||||
}
|
||||
|
||||
static void _mVideoProxyBackendDeinit(struct VideoBackend* v) {
|
||||
struct mVideoProxyBackend* proxy = (struct mVideoProxyBackend*) v;
|
||||
struct mVideoBackendCommand cmd = {
|
||||
.cmd = mVB_CMD_DEINIT,
|
||||
};
|
||||
mVideoProxyBackendSubmit(proxy, &cmd, NULL);
|
||||
}
|
||||
|
||||
static void _mVideoProxyBackendSetLayerDimensions(struct VideoBackend* v, enum VideoLayer layer, const struct mRectangle* dims) {
|
||||
struct mVideoProxyBackend* proxy = (struct mVideoProxyBackend*) v;
|
||||
struct mVideoBackendCommand cmd = {
|
||||
.cmd = mVB_CMD_SET_LAYER_DIMENSIONS,
|
||||
.layer = layer,
|
||||
.data = {
|
||||
.dims = *dims
|
||||
}
|
||||
};
|
||||
mVideoProxyBackendSubmit(proxy, &cmd, NULL);
|
||||
}
|
||||
|
||||
static void _mVideoProxyBackendLayerDimensions(const struct VideoBackend* v, enum VideoLayer layer, struct mRectangle* dims) {
|
||||
struct mVideoProxyBackend* proxy = (struct mVideoProxyBackend*) v;
|
||||
struct mVideoBackendCommand cmd = {
|
||||
.cmd = mVB_CMD_LAYER_DIMENSIONS,
|
||||
.layer = layer,
|
||||
};
|
||||
union mVideoBackendCommandData out;
|
||||
mVideoProxyBackendSubmit(proxy, &cmd, &out);
|
||||
memcpy(dims, &out.dims, sizeof(*dims));
|
||||
}
|
||||
|
||||
static void _mVideoProxyBackendSwap(struct VideoBackend* v) {
|
||||
struct mVideoProxyBackend* proxy = (struct mVideoProxyBackend*) v;
|
||||
struct mVideoBackendCommand cmd = {
|
||||
.cmd = mVB_CMD_SWAP,
|
||||
};
|
||||
mVideoProxyBackendSubmit(proxy, &cmd, NULL);
|
||||
}
|
||||
|
||||
static void _mVideoProxyBackendClear(struct VideoBackend* v) {
|
||||
struct mVideoProxyBackend* proxy = (struct mVideoProxyBackend*) v;
|
||||
struct mVideoBackendCommand cmd = {
|
||||
.cmd = mVB_CMD_CLEAR,
|
||||
};
|
||||
mVideoProxyBackendSubmit(proxy, &cmd, NULL);
|
||||
}
|
||||
|
||||
static void _mVideoProxyBackendContextResized(struct VideoBackend* v, unsigned w, unsigned h) {
|
||||
struct mVideoProxyBackend* proxy = (struct mVideoProxyBackend*) v;
|
||||
struct mVideoBackendCommand cmd = {
|
||||
.cmd = mVB_CMD_CONTEXT_RESIZED,
|
||||
.data = {
|
||||
.u = {
|
||||
.width = w,
|
||||
.height = h,
|
||||
}
|
||||
}
|
||||
};
|
||||
mVideoProxyBackendSubmit(proxy, &cmd, NULL);
|
||||
}
|
||||
|
||||
static void _mVideoProxyBackendSetImageSize(struct VideoBackend* v, enum VideoLayer layer, int w, int h) {
|
||||
struct mVideoProxyBackend* proxy = (struct mVideoProxyBackend*) v;
|
||||
struct mVideoBackendCommand cmd = {
|
||||
.cmd = mVB_CMD_SET_IMAGE_SIZE,
|
||||
.layer = layer,
|
||||
.data = {
|
||||
.s = {
|
||||
.width = w,
|
||||
.height = h,
|
||||
}
|
||||
}
|
||||
};
|
||||
mVideoProxyBackendSubmit(proxy, &cmd, NULL);
|
||||
}
|
||||
|
||||
static void _mVideoProxyBackendImageSize(struct VideoBackend* v, enum VideoLayer layer, int* w, int* h) {
|
||||
struct mVideoProxyBackend* proxy = (struct mVideoProxyBackend*) v;
|
||||
struct mVideoBackendCommand cmd = {
|
||||
.cmd = mVB_CMD_IMAGE_SIZE,
|
||||
.layer = layer,
|
||||
};
|
||||
union mVideoBackendCommandData out;
|
||||
mVideoProxyBackendSubmit(proxy, &cmd, &out);
|
||||
*w = out.s.width;
|
||||
*h = out.s.height;
|
||||
}
|
||||
|
||||
static void _mVideoProxyBackendSetImage(struct VideoBackend* v, enum VideoLayer layer, const void* frame) {
|
||||
struct mVideoProxyBackend* proxy = (struct mVideoProxyBackend*) v;
|
||||
struct mVideoBackendCommand cmd = {
|
||||
.cmd = mVB_CMD_SET_IMAGE,
|
||||
.layer = layer,
|
||||
.data = {
|
||||
.image = frame
|
||||
}
|
||||
};
|
||||
mVideoProxyBackendSubmit(proxy, &cmd, NULL);
|
||||
}
|
||||
|
||||
static void _mVideoProxyBackendDrawFrame(struct VideoBackend* v) {
|
||||
struct mVideoProxyBackend* proxy = (struct mVideoProxyBackend*) v;
|
||||
struct mVideoBackendCommand cmd = {
|
||||
.cmd = mVB_CMD_DRAW_FRAME,
|
||||
};
|
||||
mVideoProxyBackendSubmit(proxy, &cmd, NULL);
|
||||
}
|
||||
|
||||
static bool mVideoProxyBackendReadIn(struct mVideoProxyBackend* proxy, struct mVideoBackendCommand* cmd, bool block);
|
||||
static void mVideoProxyBackendWriteOut(struct mVideoProxyBackend* proxy, const union mVideoBackendCommandData* out);
|
||||
|
||||
void mVideoProxyBackendInit(struct mVideoProxyBackend* proxy, struct VideoBackend* backend) {
|
||||
proxy->d.init = _mVideoProxyBackendInit;
|
||||
proxy->d.deinit = _mVideoProxyBackendDeinit;
|
||||
proxy->d.setLayerDimensions = _mVideoProxyBackendSetLayerDimensions;
|
||||
proxy->d.layerDimensions = _mVideoProxyBackendLayerDimensions;
|
||||
proxy->d.swap = _mVideoProxyBackendSwap;
|
||||
proxy->d.clear = _mVideoProxyBackendClear;
|
||||
proxy->d.contextResized = _mVideoProxyBackendContextResized;
|
||||
proxy->d.setImageSize = _mVideoProxyBackendSetImageSize;
|
||||
proxy->d.imageSize = _mVideoProxyBackendImageSize;
|
||||
proxy->d.setImage = _mVideoProxyBackendSetImage;
|
||||
proxy->d.drawFrame = _mVideoProxyBackendDrawFrame;
|
||||
proxy->backend = backend;
|
||||
|
||||
RingFIFOInit(&proxy->in, 0x400);
|
||||
RingFIFOInit(&proxy->out, 0x400);
|
||||
MutexInit(&proxy->inLock);
|
||||
MutexInit(&proxy->outLock);
|
||||
ConditionInit(&proxy->inWait);
|
||||
ConditionInit(&proxy->outWait);
|
||||
|
||||
proxy->wakeupCb = NULL;
|
||||
proxy->context = NULL;
|
||||
}
|
||||
|
||||
void mVideoProxyBackendDeinit(struct mVideoProxyBackend* proxy) {
|
||||
ConditionDeinit(&proxy->inWait);
|
||||
ConditionDeinit(&proxy->outWait);
|
||||
MutexDeinit(&proxy->inLock);
|
||||
MutexDeinit(&proxy->outLock);
|
||||
RingFIFODeinit(&proxy->in);
|
||||
RingFIFODeinit(&proxy->out);
|
||||
}
|
||||
|
||||
void mVideoProxyBackendSubmit(struct mVideoProxyBackend* proxy, const struct mVideoBackendCommand* cmd, union mVideoBackendCommandData* out) {
|
||||
MutexLock(&proxy->inLock);
|
||||
while (!RingFIFOWrite(&proxy->in, cmd, sizeof(*cmd))) {
|
||||
mLOG(VIDEO, DEBUG, "Can't write command. Proxy thread asleep?");
|
||||
ConditionWait(&proxy->inWait, &proxy->inLock);
|
||||
}
|
||||
MutexUnlock(&proxy->inLock);
|
||||
if (proxy->wakeupCb) {
|
||||
proxy->wakeupCb(proxy, proxy->context);
|
||||
}
|
||||
|
||||
if (!mVideoProxyBackendCommandIsBlocking(cmd->cmd)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MutexLock(&proxy->outLock);
|
||||
while (!RingFIFORead(&proxy->out, out, sizeof(*out))) {
|
||||
ConditionWait(&proxy->outWait, &proxy->outLock);
|
||||
}
|
||||
MutexUnlock(&proxy->outLock);
|
||||
}
|
||||
|
||||
bool mVideoProxyBackendRun(struct mVideoProxyBackend* proxy, bool block) {
|
||||
bool ok = false;
|
||||
do {
|
||||
struct mVideoBackendCommand cmd;
|
||||
union mVideoBackendCommandData out;
|
||||
if (mVideoProxyBackendReadIn(proxy, &cmd, block)) {
|
||||
switch (cmd.cmd) {
|
||||
case mVB_CMD_DUMMY:
|
||||
break;
|
||||
case mVB_CMD_INIT:
|
||||
proxy->backend->init(proxy->backend, cmd.handle);
|
||||
break;
|
||||
case mVB_CMD_DEINIT:
|
||||
proxy->backend->deinit(proxy->backend);
|
||||
break;
|
||||
case mVB_CMD_SET_LAYER_DIMENSIONS:
|
||||
proxy->backend->setLayerDimensions(proxy->backend, cmd.layer, &cmd.data.dims);
|
||||
break;
|
||||
case mVB_CMD_LAYER_DIMENSIONS:
|
||||
proxy->backend->layerDimensions(proxy->backend, cmd.layer, &out.dims);
|
||||
break;
|
||||
case mVB_CMD_SWAP:
|
||||
proxy->backend->swap(proxy->backend);
|
||||
break;
|
||||
case mVB_CMD_CLEAR:
|
||||
proxy->backend->clear(proxy->backend);
|
||||
break;
|
||||
case mVB_CMD_CONTEXT_RESIZED:
|
||||
proxy->backend->contextResized(proxy->backend, cmd.data.u.width, cmd.data.u.height);
|
||||
break;
|
||||
case mVB_CMD_SET_IMAGE_SIZE:
|
||||
proxy->backend->setImageSize(proxy->backend, cmd.layer, cmd.data.s.width, cmd.data.s.height);
|
||||
break;
|
||||
case mVB_CMD_IMAGE_SIZE:
|
||||
proxy->backend->imageSize(proxy->backend, cmd.layer, &out.s.width, &out.s.height);
|
||||
break;
|
||||
case mVB_CMD_SET_IMAGE:
|
||||
proxy->backend->setImage(proxy->backend, cmd.layer, cmd.data.image);
|
||||
break;
|
||||
case mVB_CMD_DRAW_FRAME:
|
||||
proxy->backend->drawFrame(proxy->backend);
|
||||
break;
|
||||
}
|
||||
if (mVideoProxyBackendCommandIsBlocking(cmd.cmd)) {
|
||||
mVideoProxyBackendWriteOut(proxy, &out);
|
||||
}
|
||||
ok = true;
|
||||
}
|
||||
} while (block);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool mVideoProxyBackendReadIn(struct mVideoProxyBackend* proxy, struct mVideoBackendCommand* cmd, bool block) {
|
||||
bool gotCmd = false;
|
||||
MutexLock(&proxy->inLock);
|
||||
do {
|
||||
gotCmd = RingFIFORead(&proxy->in, cmd, sizeof(*cmd));
|
||||
ConditionWake(&proxy->inWait);
|
||||
// TODO: interlock?
|
||||
if (block && !gotCmd) {
|
||||
mLOG(VIDEO, DEBUG, "Can't read command. Runner thread asleep?");
|
||||
}
|
||||
} while (block && !gotCmd);
|
||||
MutexUnlock(&proxy->inLock);
|
||||
return gotCmd;
|
||||
}
|
||||
|
||||
void mVideoProxyBackendWriteOut(struct mVideoProxyBackend* proxy, const union mVideoBackendCommandData* out) {
|
||||
bool gotReply = false;
|
||||
MutexLock(&proxy->outLock);
|
||||
while (!gotReply) {
|
||||
gotReply = RingFIFOWrite(&proxy->out, out, sizeof(*out));
|
||||
ConditionWake(&proxy->outWait);
|
||||
// TOOD: interlock?
|
||||
if (!gotReply) {
|
||||
mLOG(VIDEO, DEBUG, "Can't write reply. Runner thread asleep?");
|
||||
}
|
||||
}
|
||||
MutexUnlock(&proxy->outLock);
|
||||
}
|
||||
|
||||
bool mVideoProxyBackendCommandIsBlocking(enum mVideoBackendCommandType cmd) {
|
||||
switch (cmd) {
|
||||
case mVB_CMD_DUMMY:
|
||||
case mVB_CMD_CONTEXT_RESIZED:
|
||||
case mVB_CMD_SET_LAYER_DIMENSIONS:
|
||||
case mVB_CMD_CLEAR:
|
||||
case mVB_CMD_SET_IMAGE_SIZE:
|
||||
case mVB_CMD_DRAW_FRAME:
|
||||
return false;
|
||||
case mVB_CMD_INIT:
|
||||
case mVB_CMD_DEINIT:
|
||||
case mVB_CMD_LAYER_DIMENSIONS:
|
||||
case mVB_CMD_SWAP:
|
||||
case mVB_CMD_IMAGE_SIZE:
|
||||
case mVB_CMD_SET_IMAGE:
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|