Merge branch 'master' into translations
|
@ -10,10 +10,12 @@
|
|||
*.a
|
||||
*.dylib
|
||||
*.dll
|
||||
*.lib
|
||||
*.exe
|
||||
*.o
|
||||
*.so
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
CMakeSettings.json
|
||||
cmake_install.cmake
|
||||
version.c
|
||||
|
|
60
CHANGES
|
@ -1,4 +1,50 @@
|
|||
0.10.0: (Future)
|
||||
0.11.0: (Future)
|
||||
Features:
|
||||
- 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
|
||||
- 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)
|
||||
- Qt: Fix savestate preview sizes with different scales (fixes mgba.io/i/2560)
|
||||
Misc:
|
||||
- GB Serialize: Add missing savestate support for MBC6 and NT (newer)
|
||||
- GBA: Improve detection of valid ELF ROMs
|
||||
- Qt: Include wayland QPA in AppImage (fixes mgba.io/i/2796)
|
||||
|
||||
0.10.1: (2023-01-10)
|
||||
Emulation fixes:
|
||||
- GB Audio: Fix channels 1/2 not playing when resetting volume (fixes mgba.io/i/2614)
|
||||
- GB Audio: Fix channel 3 volume being changed between samples (fixes mgba.io/i/1896)
|
||||
- GB Audio: Fix up boot sequence
|
||||
- GB Audio: Fix updating channels other than 2 when writing NR5x
|
||||
- GB Memory: Actually, HDMAs should start when LCD is off (fixes mgba.io/i/2662)
|
||||
- GB Serialize: Don't write BGP/OBP when loading SCGB state (fixes mgba.io/i/2694)
|
||||
- GB SIO: Further fix bidirectional transfer starting
|
||||
- GBA: Fix resetting key IRQ state (fixes mgba.io/i/2716)
|
||||
- GBA BIOS: Include timing in degenerate ArcTan2 cases (fixes mgba.io/i/2763)
|
||||
- GBA Video: Ignore disabled backgrounds as OBJ blend target (fixes mgba.io/i/2489)
|
||||
Other fixes:
|
||||
- GBA: Fix forceskip BIOS logic for multiboot ROMs (fixes mgba.io/i/2753)
|
||||
- GBA Cheats: Fix issues detecting unencrypted cheats (fixes mgba.io/i/2724)
|
||||
- Qt: Manually split filename to avoid overzealous splitting (fixes mgba.io/i/2681)
|
||||
- Qt: Fix scanning specific e-Reader dotcodes (fixes mgba.io/i/2693)
|
||||
- Qt: Don't re-enable sync if GBA link modes aren't the same (fixes mgba.io/i/2044)
|
||||
- Qt: Improve handling of multiplayer syncing (fixes mgba.io/i/2720)
|
||||
- Qt: Fix initializing update revision info
|
||||
- Qt: Redo stable branch detection heuristic (fixes mgba.io/i/2679)
|
||||
- Res: Fix species name location in Ruby/Sapphire revs 1/2 (fixes mgba.io/i/2685)
|
||||
- VFS: Fix minizip write returning 0 on success instead of size
|
||||
Misc:
|
||||
- macOS: Add category to plist (closes mgba.io/i/2691)
|
||||
- macOS: Fix modern build with libepoxy (fixes mgba.io/i/2700)
|
||||
- Qt: Keep track of current palette preset name (fixes mgba.io/i/2680)
|
||||
- Qt: Move OpenGL proxy onto its own thread (fixes mgba.io/i/2493)
|
||||
|
||||
0.10.0: (2022-10-11)
|
||||
Features:
|
||||
- Preliminary Lua scripting support
|
||||
- Presets for Game Boy palettes
|
||||
|
@ -6,7 +52,7 @@ Features:
|
|||
- Tool for converting scanned pictures of e-Reader cards to raw dotcode data
|
||||
- Options for muting when inactive, minimized, or for different players in multiplayer
|
||||
- Cheat code support in homebrew ports
|
||||
- Acclerometer and gyro support for controllers on PC
|
||||
- Accelerometer and gyro support for controllers on PC
|
||||
- Support for combo "Super Game Boy Color" SGB + GBC ROM hacks
|
||||
- Improved support for HuC-3 mapper, including RTC
|
||||
- Support for 64 kiB SRAM saves used in some bootlegs
|
||||
|
@ -20,13 +66,13 @@ Emulation fixes:
|
|||
- ARM7: Fix unsigned multiply timing
|
||||
- GB: Copy logo from ROM if not running the BIOS intro (fixes mgba.io/i/2378)
|
||||
- GB: Fix HALT breaking M-cycle alignment (fixes mgba.io/i/250)
|
||||
- GB Audio: Fix channel 1/2 reseting edge cases (fixes mgba.io/i/1925)
|
||||
- GB Audio: Fix channel 1/2 resetting edge cases (fixes mgba.io/i/1925)
|
||||
- GB Audio: Properly apply per-model audio differences
|
||||
- GB Audio: Revamp channel rendering
|
||||
- GB Audio: Fix APU re-enable timing glitch
|
||||
- GB I/O: Fix writing to WAVE RAM behavior (fixes mgba.io/i/1334)
|
||||
- GB Memory: Add cursory cartridge open bus emulation (fixes mgba.io/i/2032)
|
||||
- GB MBC: Fix edge case with Pocket Cam register accesses (fixes mgba.io/i/2557)
|
||||
- GB Memory: Add cursory cartridge open bus emulation (fixes mgba.io/i/2032)
|
||||
- GB Serialize: Fix loading MBC1 states that affect bank 0 (fixes mgba.io/i/2402)
|
||||
- GB SIO: Fix bidirectional transfer starting (fixes mgba.io/i/2290)
|
||||
- GB Video: Draw SGB border pieces that overlap GB graphics (fixes mgba.io/i/1339)
|
||||
|
@ -130,7 +176,7 @@ Emulation fixes:
|
|||
Other fixes:
|
||||
- ARM Decoder: Fix decoding of lsl r0 (fixes mgba.io/i/2349)
|
||||
- FFmpeg: Don't attempt to use YUV 4:2:0 for lossless videos (fixes mgba.io/i/2084)
|
||||
- GB Video: Fix memory leak when reseting SGB games
|
||||
- GB Video: Fix memory leak when resetting SGB games
|
||||
- GBA: Fix out of bounds ROM accesses on patched ROMs smaller than 32 MiB
|
||||
- GBA: Fix maximum tile ID in caching for 256-color modes
|
||||
- GBA Video: Fix cache updating with proxy and GL renderers
|
||||
|
@ -241,7 +287,7 @@ Emulation fixes:
|
|||
- GBA BIOS: Implement dummy sound driver calls
|
||||
- GBA BIOS: Improve HLE BIOS timing
|
||||
- GBA BIOS: Fix reloading video registers after reset (fixes mgba.io/i/1808)
|
||||
- GBA BIOS: Make HLE BIOS calls interruptable (fixes mgba.io/i/1711 and mgba.io/i/1823)
|
||||
- GBA BIOS: Make HLE BIOS calls interruptible (fixes mgba.io/i/1711 and mgba.io/i/1823)
|
||||
- GBA BIOS: Fix invalid decompression bounds checking
|
||||
- GBA DMA: Linger last DMA on bus (fixes mgba.io/i/301 and mgba.io/i/1320)
|
||||
- GBA DMA: Fix ordering and timing of overlapping DMAs
|
||||
|
@ -257,7 +303,7 @@ Emulation fixes:
|
|||
- GBA Serialize: Fix alignment check when loading states
|
||||
- GBA SIO: Fix copying Normal mode transfer values
|
||||
- GBA SIO: Fix Normal mode being totally broken (fixes mgba.io/i/1800)
|
||||
- GBA SIO: Fix deseralizing SIO registers
|
||||
- GBA SIO: Fix deserializing SIO registers
|
||||
- GBA SIO: Fix hanging on starting a second multiplayer window (fixes mgba.io/i/854)
|
||||
- GBA SIO: Fix Normal mode transfer start timing (fixes mgba.io/i/425)
|
||||
- GBA Timers: Fix toggling timer cascading while timer is active (fixes mgba.io/i/2043)
|
||||
|
|
|
@ -34,7 +34,7 @@ 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")
|
||||
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}")
|
||||
else()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS /wd4003 /wd4244 /wd4146 /wd4267 /Zc:preprocessor-")
|
||||
|
@ -235,6 +235,7 @@ elseif(UNIX)
|
|||
endif()
|
||||
|
||||
if(APPLE)
|
||||
execute_process(COMMAND xcrun --show-sdk-version OUTPUT_VARIABLE MACOSX_SDK)
|
||||
add_definitions(-D_DARWIN_C_SOURCE)
|
||||
list(APPEND OS_LIB "-framework Foundation")
|
||||
if(NOT CMAKE_SYSTEM_VERSION VERSION_LESS "10.0") # Darwin 10.x is Mac OS X 10.6
|
||||
|
@ -413,7 +414,7 @@ endif()
|
|||
|
||||
if(BUILD_GL)
|
||||
find_package(OpenGL QUIET)
|
||||
if(NOT OPENGL_FOUND)
|
||||
if(NOT OPENGL_FOUND OR (APPLE AND MACOSX_SDK VERSION_GREATER 10.14))
|
||||
set(BUILD_GL OFF CACHE BOOL "OpenGL not found" FORCE)
|
||||
elseif(UNIX AND NOT APPLE AND TARGET OpenGL::GL)
|
||||
set(OPENGL_LIBRARY OpenGL::GL)
|
||||
|
@ -430,6 +431,11 @@ if(NOT BUILD_GL AND NOT LIBMGBA_ONLY)
|
|||
endif()
|
||||
|
||||
if(BUILD_GLES2 AND NOT BUILD_GL)
|
||||
if(APPLE AND MACOSX_SDK VERSION_GREATER 10.14)
|
||||
find_package(OpenGL QUIET)
|
||||
set(OPENGLES2_INCLUDE_DIR ${OPENGL_INCLUDE_DIR})
|
||||
set(OPENGLES2_LIBRARY ${OPENGL_LIBRARY})
|
||||
endif()
|
||||
find_path(OPENGLES2_INCLUDE_DIR NAMES GLES2/gl2.h)
|
||||
find_library(OPENGLES2_LIBRARY NAMES GLESv2 GLESv2_CM)
|
||||
if(NOT OPENGLES2_INCLUDE_DIR OR NOT OPENGLES2_LIBRARY)
|
||||
|
@ -443,6 +449,11 @@ if(BUILD_GLES2)
|
|||
endif()
|
||||
|
||||
if(BUILD_GLES3 AND NOT BUILD_GL)
|
||||
if(APPLE AND MACOSX_SDK VERSION_GREATER 10.14)
|
||||
find_package(OpenGL QUIET)
|
||||
set(OPENGLES3_INCLUDE_DIR ${OPENGL_INCLUDE_DIR})
|
||||
set(OPENGLES3_LIBRARY ${OPENGL_LIBRARY})
|
||||
endif()
|
||||
find_path(OPENGLES3_INCLUDE_DIR NAMES GLES3/gl3.h)
|
||||
find_library(OPENGLES3_LIBRARY NAMES GLESv3 GLESv2)
|
||||
if(NOT OPENGLES3_INCLUDE_DIR OR NOT OPENGLES3_LIBRARY)
|
||||
|
@ -629,14 +640,15 @@ endif()
|
|||
|
||||
if(USE_LIBZIP)
|
||||
if(TARGET libzip::zip)
|
||||
list(APPEND DEPENDENCY_LIB libzip::zip)
|
||||
set(ZIP_LIBRARIES libzip::zip)
|
||||
elseif(TARGET zip)
|
||||
list(APPEND DEPENDENCY_LIB zip)
|
||||
set(ZIP_LIBRARIES zip)
|
||||
else()
|
||||
include_directories(AFTER ${LIBZIP_INCLUDE_DIRS})
|
||||
link_directories(${LIBZIP_LIBRARY_DIRS})
|
||||
list(APPEND DEPENDENCY_LIB ${LIBZIP_LIBRARIES})
|
||||
set(ZIP_LIBRARIES ${LIBZIP_LIBRARIES})
|
||||
endif()
|
||||
list(APPEND DEPENDENCY_LIB ${ZIP_LIBRARIES})
|
||||
list(APPEND FEATURES LIBZIP)
|
||||
list(APPEND VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-zip.c)
|
||||
string(REGEX MATCH "^[0-9]+" LIBZIP_VERSION_MAJOR "${libzip_VERSION}")
|
||||
|
@ -650,6 +662,7 @@ if(USE_LIBZIP)
|
|||
elseif(USE_MINIZIP)
|
||||
include_directories(AFTER ${MINIZIP_INCLUDE_DIRS})
|
||||
link_directories(${MINIZIP_LIBRARY_DIRS})
|
||||
set(ZIP_LIBRARIES ${MINIZIP_LIBRARIES})
|
||||
list(APPEND DEPENDENCY_LIB ${MINIZIP_LIBRARIES})
|
||||
list(APPEND FEATURES MINIZIP)
|
||||
list(APPEND VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-zip.c)
|
||||
|
@ -705,8 +718,12 @@ if (USE_LZMA)
|
|||
endif()
|
||||
|
||||
if(USE_EPOXY)
|
||||
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gl.c ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gles2.c)
|
||||
list(APPEND FEATURE_DEFINES BUILD_GL BUILD_GLES2 BUILD_GLES3)
|
||||
if(NOT APPLE OR NOT MACOSX_SDK VERSION_GREATER 10.14)
|
||||
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gl.c)
|
||||
list(APPEND FEATURE_DEFINES BUILD_GL)
|
||||
endif()
|
||||
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gles2.c)
|
||||
list(APPEND FEATURE_DEFINES BUILD_GLES2 BUILD_GLES3)
|
||||
list(APPEND FEATURES EPOXY)
|
||||
include_directories(AFTER ${EPOXY_INCLUDE_DIRS})
|
||||
link_directories(${EPOXY_LIBRARY_DIRS})
|
||||
|
@ -778,6 +795,7 @@ add_subdirectory(src/sm83)
|
|||
add_subdirectory(src/util)
|
||||
|
||||
list(APPEND GUI_SRC ${EXTRA_GUI_SRC})
|
||||
list(APPEND UTIL_BASE_SRC ${CMAKE_CURRENT_BINARY_DIR}/version.c)
|
||||
list(APPEND UTIL_SRC ${CMAKE_CURRENT_BINARY_DIR}/version.c)
|
||||
|
||||
set(TEST_SRC ${CORE_TEST_SRC})
|
||||
|
@ -968,12 +986,12 @@ if(BUILD_QT AND (WIN32 OR APPLE OR CMAKE_SYSTEM_NAME STREQUAL "Linux"))
|
|||
endif()
|
||||
|
||||
if(BUILD_UPDATER)
|
||||
add_executable(updater-stub WIN32 ${CORE_VFS_SRC} ${OS_SRC} ${UTIL_SRC} ${THIRD_PARTY_SRC}
|
||||
add_executable(updater-stub WIN32 ${CORE_VFS_SRC} ${VFS_SRC} ${OS_SRC} ${UTIL_BASE_SRC} ${THIRD_PARTY_SRC}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/core/config.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/feature/updater.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/feature/updater-main.c)
|
||||
target_link_libraries(updater-stub ${OS_LIB} ${PLATFORM_LIBRARY})
|
||||
set_target_properties(updater-stub PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FUNCTION_DEFINES};BUILD_STATIC")
|
||||
target_link_libraries(updater-stub ${ZLIB_LIBRARY} ${ZLIB_LIBRARY} ${ZIP_LIBRARIES} ${OS_LIB} ${PLATFORM_LIBRARY})
|
||||
set_target_properties(updater-stub PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FUNCTION_DEFINES};${FEATURE_DEFINES};BUILD_STATIC")
|
||||
if(MSVC)
|
||||
set_target_properties(updater-stub PROPERTIES LINK_FLAGS /ENTRY:mainCRTStartup)
|
||||
else()
|
||||
|
|
10
README.md
|
@ -50,15 +50,15 @@ The following mappers are fully supported:
|
|||
- MBC2
|
||||
- MBC3
|
||||
- MBC3+RTC
|
||||
- MBC30
|
||||
- MBC5
|
||||
- MBC5+Rumble
|
||||
- MBC7
|
||||
- Wisdom Tree (unlicensed)
|
||||
- NT "old type" 1 and 2 (unlicensed multicart)
|
||||
- NT "new type" (unlicensed MBC5-like)
|
||||
- Pokémon Jade/Diamond (unlicensed)
|
||||
- BBD (unlicensed MBC5-like)
|
||||
- Hitek (unlicensed MBC5-like)
|
||||
- Sachen MMC1
|
||||
- Sachen MMC1 (unlicensed)
|
||||
|
||||
The following mappers are partially supported:
|
||||
|
||||
|
@ -69,6 +69,10 @@ The following mappers are partially supported:
|
|||
- HuC-1 (missing IR support)
|
||||
- HuC-3 (missing IR support)
|
||||
- Sachen MMC2 (missing alternate wiring support)
|
||||
- BBD (missing logo switching)
|
||||
- Hitek (missing logo switching)
|
||||
- GGB-81 (missing logo switching)
|
||||
- Li Cheng (missing logo switching)
|
||||
|
||||
### Planned features
|
||||
|
||||
|
|
After Width: | Height: | Size: 2.1 KiB |
|
@ -0,0 +1,3 @@
|
|||
[testinfo]
|
||||
skip=10
|
||||
frames=1
|
After Width: | Height: | Size: 6.4 KiB |
After Width: | Height: | Size: 6.4 KiB |
After Width: | Height: | Size: 6.3 KiB |
After Width: | Height: | Size: 6.2 KiB |
After Width: | Height: | Size: 6.2 KiB |
After Width: | Height: | Size: 6.0 KiB |
|
@ -0,0 +1,30 @@
|
|||
/* Copyright (c) 2013-2022 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef SFO_H
|
||||
#define SFO_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba-util/table.h>
|
||||
|
||||
void SfoInit(struct Table* sfo);
|
||||
|
||||
static inline void SfoDeinit(struct Table* sfo) {
|
||||
HashTableDeinit(sfo);
|
||||
}
|
||||
|
||||
struct VFile;
|
||||
bool SfoWrite(struct Table* sfo, struct VFile* vf);
|
||||
|
||||
bool SfoAddU32Value(struct Table* sfo, const char* name, uint32_t value);
|
||||
bool SfoAddStrValue(struct Table* sfo, const char* name, const char* value);
|
||||
bool SfoSetTitle(struct Table* sfo, const char* title);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -34,6 +34,16 @@ CXX_GUARD_START
|
|||
size_t NAME ## Index(const struct NAME* vector, const TYPE* member); \
|
||||
void NAME ## Copy(struct NAME* dest, const struct NAME* src);
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define VECTOR_BOUNDS_CHECK(NAME, V, L)
|
||||
#else
|
||||
#define VECTOR_BOUNDS_CHECK(NAME, V, L) \
|
||||
if ((L) >= (V)->size) { \
|
||||
fprintf(stderr, "Vector type %s invalid access of index %" PRIuPTR " into vector of size %" PRIuPTR "\n", #NAME, (L), (V)->size); \
|
||||
abort(); \
|
||||
}
|
||||
#endif
|
||||
|
||||
#define DEFINE_VECTOR(NAME, TYPE) \
|
||||
void NAME ## Init(struct NAME* vector, size_t capacity) { \
|
||||
vector->size = 0; \
|
||||
|
@ -50,9 +60,11 @@ CXX_GUARD_START
|
|||
vector->size = 0; \
|
||||
} \
|
||||
TYPE* NAME ## GetPointer(struct NAME* vector, size_t location) { \
|
||||
VECTOR_BOUNDS_CHECK(NAME, vector, location); \
|
||||
return &vector->vector[location]; \
|
||||
} \
|
||||
TYPE const* NAME ## GetConstPointer(const struct NAME* vector, size_t location) { \
|
||||
VECTOR_BOUNDS_CHECK(NAME, vector, location); \
|
||||
return &vector->vector[location]; \
|
||||
} \
|
||||
TYPE* NAME ## Append(struct NAME* vector) { \
|
||||
|
@ -78,10 +90,12 @@ CXX_GUARD_START
|
|||
vector->vector = realloc(vector->vector, vector->capacity * sizeof(TYPE)); \
|
||||
} \
|
||||
void NAME ## Shift(struct NAME* vector, size_t location, size_t difference) { \
|
||||
VECTOR_BOUNDS_CHECK(NAME, vector, location); \
|
||||
memmove(&vector->vector[location], &vector->vector[location + difference], (vector->size - location - difference) * sizeof(TYPE)); \
|
||||
vector->size -= difference; \
|
||||
} \
|
||||
void NAME ## Unshift(struct NAME* vector, size_t location, size_t difference) { \
|
||||
VECTOR_BOUNDS_CHECK(NAME, vector, location); \
|
||||
NAME ## Resize(vector, difference); \
|
||||
memmove(&vector->vector[location + difference], &vector->vector[location], (vector->size - location - difference) * sizeof(TYPE)); \
|
||||
} \
|
||||
|
@ -97,8 +111,16 @@ CXX_GUARD_START
|
|||
dest->size = src->size; \
|
||||
} \
|
||||
|
||||
DECLARE_VECTOR(StringList, char*);
|
||||
DECLARE_VECTOR(IntList, int);
|
||||
DECLARE_VECTOR(SInt8List, int8_t);
|
||||
DECLARE_VECTOR(SInt16List, int16_t);
|
||||
DECLARE_VECTOR(SInt32List, int32_t);
|
||||
DECLARE_VECTOR(SIntPtrList, intptr_t);
|
||||
DECLARE_VECTOR(UInt8List, uint8_t);
|
||||
DECLARE_VECTOR(UInt16List, uint16_t);
|
||||
DECLARE_VECTOR(UInt32List, uint32_t);
|
||||
DECLARE_VECTOR(UIntPtrList, uintptr_t);
|
||||
DECLARE_VECTOR(StringList, char*);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ int mInputMapKeyBits(const struct mInputMap* map, uint32_t type, uint32_t bits,
|
|||
void mInputBindKey(struct mInputMap*, uint32_t type, int key, int input);
|
||||
int mInputQueryBinding(const struct mInputMap*, uint32_t type, int input);
|
||||
void mInputUnbindKey(struct mInputMap*, uint32_t type, int input);
|
||||
void mInputUnbindAllKeys(struct mInputMap*, uint32_t type);
|
||||
|
||||
int mInputMapAxis(const struct mInputMap*, uint32_t type, int axis, int value);
|
||||
int mInputClearAxis(const struct mInputMap*, uint32_t type, int axis, int keys);
|
||||
|
@ -69,6 +70,7 @@ void mInputBindHat(struct mInputMap*, uint32_t type, int id, const struct mInput
|
|||
bool mInputQueryHat(const struct mInputMap*, uint32_t type, int id, struct mInputHatBindings* bindings);
|
||||
void mInputUnbindHat(struct mInputMap*, uint32_t type, int id);
|
||||
void mInputUnbindAllHats(struct mInputMap*, uint32_t type);
|
||||
void mInputEnumerateHats(const struct mInputMap*, uint32_t type, void (handler(int hat, const struct mInputHatBindings* bindings, void* user)), void* user);
|
||||
|
||||
bool mInputMapLoad(struct mInputMap*, uint32_t type, const struct Configuration*);
|
||||
void mInputMapSave(const struct mInputMap*, uint32_t type, struct Configuration*);
|
||||
|
|
|
@ -16,6 +16,7 @@ enum mStateExtdataTag {
|
|||
EXTDATA_SAVEDATA = 2,
|
||||
EXTDATA_CHEATS = 3,
|
||||
EXTDATA_RTC = 4,
|
||||
EXTDATA_SCREENSHOT_DIMENSIONS = 5,
|
||||
EXTDATA_META_TIME = 0x101,
|
||||
EXTDATA_META_CREATOR = 0x102,
|
||||
EXTDATA_MAX
|
||||
|
|
|
@ -88,8 +88,9 @@ struct mBreakpoint {
|
|||
|
||||
struct mWatchpoint {
|
||||
ssize_t id;
|
||||
uint32_t address;
|
||||
int segment;
|
||||
uint32_t minAddress;
|
||||
uint32_t maxAddress;
|
||||
enum mWatchpointType type;
|
||||
struct ParseTree* condition;
|
||||
};
|
||||
|
|
|
@ -41,9 +41,13 @@ enum GBMemoryBankControllerType {
|
|||
GB_MBC5_RUMBLE = 0x105,
|
||||
GB_UNL_WISDOM_TREE = 0x200,
|
||||
GB_UNL_PKJD = 0x203,
|
||||
GB_UNL_NT_OLD_1 = 0x210,
|
||||
GB_UNL_NT_OLD_2 = 0x211,
|
||||
GB_UNL_NT_NEW = 0x212,
|
||||
GB_UNL_BBD = 0x220, // Also used as a mask for MBCs that need special read behavior
|
||||
GB_UNL_BBD = 0x220,
|
||||
GB_UNL_HITEK = 0x221,
|
||||
GB_UNL_LI_CHENG = 0x222,
|
||||
GB_UNL_GGB81 = 0x223,
|
||||
GB_UNL_SACHEN_MMC1 = 0x230,
|
||||
GB_UNL_SACHEN_MMC2 = 0x231,
|
||||
};
|
||||
|
|
|
@ -191,7 +191,6 @@ struct GBMBC1State {
|
|||
};
|
||||
|
||||
struct GBMBC6State {
|
||||
bool sramAccess;
|
||||
bool flashBank0;
|
||||
bool flashBank1;
|
||||
};
|
||||
|
@ -238,6 +237,13 @@ struct GBPKJDState {
|
|||
uint8_t reg[2];
|
||||
};
|
||||
|
||||
struct GBNTOldState {
|
||||
bool swapped;
|
||||
uint8_t baseBank;
|
||||
uint8_t bankCount;
|
||||
bool rumble;
|
||||
};
|
||||
|
||||
struct GBNTNewState {
|
||||
bool splitMode;
|
||||
};
|
||||
|
@ -263,6 +269,7 @@ union GBMBCState {
|
|||
struct GBPocketCamState pocketCam;
|
||||
struct GBTAMA5State tama5;
|
||||
struct GBHuC3State huc3;
|
||||
struct GBNTOldState ntOld;
|
||||
struct GBNTNewState ntNew;
|
||||
struct GBPKJDState pkjd;
|
||||
struct GBBBDState bbd;
|
||||
|
|
|
@ -266,6 +266,10 @@ DECL_BITS(GBSerializedVideoFlags, Mode, 2, 2);
|
|||
DECL_BIT(GBSerializedVideoFlags, NotModeEventScheduled, 4);
|
||||
DECL_BIT(GBSerializedVideoFlags, NotFrameEventScheduled, 5);
|
||||
|
||||
DECL_BITFIELD(GBSerializedMBC6Flags, uint8_t);
|
||||
DECL_BIT(GBSerializedMBC6Flags, FlashBank0, 0);
|
||||
DECL_BIT(GBSerializedMBC6Flags, FlashBank1, 1);
|
||||
|
||||
DECL_BITFIELD(GBSerializedMBC7Flags, uint8_t);
|
||||
DECL_BITS(GBSerializedMBC7Flags, Command, 0, 2);
|
||||
DECL_BIT(GBSerializedMBC7Flags, Writable, 2);
|
||||
|
@ -274,6 +278,10 @@ DECL_BITFIELD(GBSerializedSachenFlags, uint8_t);
|
|||
DECL_BITS(GBSerializedSachenFlags, Transition, 0, 6);
|
||||
DECL_BITS(GBSerializedSachenFlags, Locked, 6, 2);
|
||||
|
||||
DECL_BITFIELD(GBSerializedNTOldFlags, uint8_t);
|
||||
DECL_BIT(GBSerializedNTOldFlags, Swapped, 0);
|
||||
DECL_BIT(GBSerializedNTOldFlags, Rumble, 1);
|
||||
|
||||
DECL_BITFIELD(GBSerializedMemoryFlags, uint16_t);
|
||||
DECL_BIT(GBSerializedMemoryFlags, SramAccess, 0);
|
||||
DECL_BIT(GBSerializedMemoryFlags, RtcAccess, 1);
|
||||
|
@ -392,6 +400,11 @@ struct GBSerializedState {
|
|||
struct {
|
||||
uint64_t lastLatch;
|
||||
} rtc;
|
||||
struct {
|
||||
GBSerializedMBC6Flags flags;
|
||||
uint8_t bank1;
|
||||
uint8_t sramBank1;
|
||||
} mbc6;
|
||||
struct {
|
||||
uint8_t state;
|
||||
GBMBC7Field eeprom;
|
||||
|
@ -416,6 +429,15 @@ struct GBSerializedState {
|
|||
uint8_t value;
|
||||
uint8_t mode;
|
||||
} huc3;
|
||||
struct {
|
||||
GBSerializedNTOldFlags flags;
|
||||
uint8_t baseBank;
|
||||
uint8_t bankCount;
|
||||
} ntOld;
|
||||
struct {
|
||||
uint8_t splitMode;
|
||||
uint8_t bank1;
|
||||
} ntNew;
|
||||
struct {
|
||||
uint8_t dataSwapMode;
|
||||
uint8_t bankSwapMode;
|
||||
|
|
|
@ -154,6 +154,12 @@ void GBAHalt(struct GBA* gba);
|
|||
void GBAStop(struct GBA* gba);
|
||||
void GBADebug(struct GBA* gba, uint16_t value);
|
||||
|
||||
#ifdef USE_ELF
|
||||
struct ELF;
|
||||
|
||||
bool GBAVerifyELFEntry(struct ELF* elf, uint32_t target);
|
||||
#endif
|
||||
|
||||
#ifdef USE_DEBUGGERS
|
||||
struct mDebugger;
|
||||
void GBAAttachDebugger(struct GBA* gba, struct mDebugger* debugger);
|
||||
|
|
|
@ -21,60 +21,60 @@ CXX_GUARD_START
|
|||
#include <mgba/internal/gba/cart/vfame.h>
|
||||
|
||||
enum GBAMemoryRegion {
|
||||
REGION_BIOS = 0x0,
|
||||
REGION_WORKING_RAM = 0x2,
|
||||
REGION_WORKING_IRAM = 0x3,
|
||||
REGION_IO = 0x4,
|
||||
REGION_PALETTE_RAM = 0x5,
|
||||
REGION_VRAM = 0x6,
|
||||
REGION_OAM = 0x7,
|
||||
REGION_CART0 = 0x8,
|
||||
REGION_CART0_EX = 0x9,
|
||||
REGION_CART1 = 0xA,
|
||||
REGION_CART1_EX = 0xB,
|
||||
REGION_CART2 = 0xC,
|
||||
REGION_CART2_EX = 0xD,
|
||||
REGION_CART_SRAM = 0xE,
|
||||
REGION_CART_SRAM_MIRROR = 0xF
|
||||
GBA_REGION_BIOS = 0x0,
|
||||
GBA_REGION_EWRAM = 0x2,
|
||||
GBA_REGION_IWRAM = 0x3,
|
||||
GBA_REGION_IO = 0x4,
|
||||
GBA_REGION_PALETTE_RAM = 0x5,
|
||||
GBA_REGION_VRAM = 0x6,
|
||||
GBA_REGION_OAM = 0x7,
|
||||
GBA_REGION_ROM0 = 0x8,
|
||||
GBA_REGION_ROM0_EX = 0x9,
|
||||
GBA_REGION_ROM1 = 0xA,
|
||||
GBA_REGION_ROM1_EX = 0xB,
|
||||
GBA_REGION_ROM2 = 0xC,
|
||||
GBA_REGION_ROM2_EX = 0xD,
|
||||
GBA_REGION_SRAM = 0xE,
|
||||
GBA_REGION_SRAM_MIRROR = 0xF
|
||||
};
|
||||
|
||||
enum GBAMemoryBase {
|
||||
BASE_BIOS = 0x00000000,
|
||||
BASE_WORKING_RAM = 0x02000000,
|
||||
BASE_WORKING_IRAM = 0x03000000,
|
||||
BASE_IO = 0x04000000,
|
||||
BASE_PALETTE_RAM = 0x05000000,
|
||||
BASE_VRAM = 0x06000000,
|
||||
BASE_OAM = 0x07000000,
|
||||
BASE_CART0 = 0x08000000,
|
||||
BASE_CART0_EX = 0x09000000,
|
||||
BASE_CART1 = 0x0A000000,
|
||||
BASE_CART1_EX = 0x0B000000,
|
||||
BASE_CART2 = 0x0C000000,
|
||||
BASE_CART2_EX = 0x0D000000,
|
||||
BASE_CART_SRAM = 0x0E000000,
|
||||
BASE_CART_SRAM_MIRROR = 0x0F000000
|
||||
GBA_BASE_BIOS = 0x00000000,
|
||||
GBA_BASE_EWRAM = 0x02000000,
|
||||
GBA_BASE_IWRAM = 0x03000000,
|
||||
GBA_BASE_IO = 0x04000000,
|
||||
GBA_BASE_PALETTE_RAM = 0x05000000,
|
||||
GBA_BASE_VRAM = 0x06000000,
|
||||
GBA_BASE_OAM = 0x07000000,
|
||||
GBA_BASE_ROM0 = 0x08000000,
|
||||
GBA_BASE_ROM0_EX = 0x09000000,
|
||||
GBA_BASE_ROM1 = 0x0A000000,
|
||||
GBA_BASE_ROM1_EX = 0x0B000000,
|
||||
GBA_BASE_ROM2 = 0x0C000000,
|
||||
GBA_BASE_ROM2_EX = 0x0D000000,
|
||||
GBA_BASE_SRAM = 0x0E000000,
|
||||
GBA_BASE_SRAM_MIRROR = 0x0F000000
|
||||
};
|
||||
|
||||
enum {
|
||||
SIZE_BIOS = 0x00004000,
|
||||
SIZE_WORKING_RAM = 0x00040000,
|
||||
SIZE_WORKING_IRAM = 0x00008000,
|
||||
SIZE_IO = 0x00000400,
|
||||
SIZE_PALETTE_RAM = 0x00000400,
|
||||
SIZE_VRAM = 0x00018000,
|
||||
SIZE_OAM = 0x00000400,
|
||||
SIZE_CART0 = 0x02000000,
|
||||
SIZE_CART1 = 0x02000000,
|
||||
SIZE_CART2 = 0x02000000,
|
||||
SIZE_CART_SRAM = 0x00008000,
|
||||
SIZE_CART_SRAM512 = 0x00010000,
|
||||
SIZE_CART_FLASH512 = 0x00010000,
|
||||
SIZE_CART_FLASH1M = 0x00020000,
|
||||
SIZE_CART_EEPROM = 0x00002000,
|
||||
SIZE_CART_EEPROM512 = 0x00000200,
|
||||
GBA_SIZE_BIOS = 0x00004000,
|
||||
GBA_SIZE_EWRAM = 0x00040000,
|
||||
GBA_SIZE_IWRAM = 0x00008000,
|
||||
GBA_SIZE_IO = 0x00000400,
|
||||
GBA_SIZE_PALETTE_RAM = 0x00000400,
|
||||
GBA_SIZE_VRAM = 0x00018000,
|
||||
GBA_SIZE_OAM = 0x00000400,
|
||||
GBA_SIZE_ROM0 = 0x02000000,
|
||||
GBA_SIZE_ROM1 = 0x02000000,
|
||||
GBA_SIZE_ROM2 = 0x02000000,
|
||||
GBA_SIZE_SRAM = 0x00008000,
|
||||
GBA_SIZE_SRAM512 = 0x00010000,
|
||||
GBA_SIZE_FLASH512 = 0x00010000,
|
||||
GBA_SIZE_FLASH1M = 0x00020000,
|
||||
GBA_SIZE_EEPROM = 0x00002000,
|
||||
GBA_SIZE_EEPROM512 = 0x00000200,
|
||||
|
||||
SIZE_AGB_PRINT = 0x10000
|
||||
GBA_SIZE_AGB_PRINT = 0x10000
|
||||
};
|
||||
|
||||
enum {
|
||||
|
|
|
@ -20,14 +20,12 @@ CXX_GUARD_START
|
|||
|
||||
#ifdef USE_EPOXY
|
||||
#include <epoxy/gl.h>
|
||||
#elif defined(BUILD_GL)
|
||||
#ifdef __APPLE__
|
||||
#elif defined(__APPLE__)
|
||||
#include <OpenGL/gl3.h>
|
||||
#else
|
||||
#elif defined(BUILD_GL)
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#endif
|
||||
#else
|
||||
#include <GLES3/gl3.h>
|
||||
#endif
|
||||
|
|
|
@ -407,12 +407,12 @@ struct GBASerializedState {
|
|||
|
||||
uint32_t reserved[12];
|
||||
|
||||
uint16_t io[SIZE_IO >> 1];
|
||||
uint16_t pram[SIZE_PALETTE_RAM >> 1];
|
||||
uint16_t oam[SIZE_OAM >> 1];
|
||||
uint16_t vram[SIZE_VRAM >> 1];
|
||||
uint8_t iwram[SIZE_WORKING_IRAM];
|
||||
uint8_t wram[SIZE_WORKING_RAM];
|
||||
uint16_t io[GBA_SIZE_IO >> 1];
|
||||
uint16_t pram[GBA_SIZE_PALETTE_RAM >> 1];
|
||||
uint16_t oam[GBA_SIZE_OAM >> 1];
|
||||
uint16_t vram[GBA_SIZE_VRAM >> 1];
|
||||
uint8_t iwram[GBA_SIZE_IWRAM];
|
||||
uint8_t wram[GBA_SIZE_EWRAM];
|
||||
};
|
||||
|
||||
static_assert(sizeof(struct GBASerializedState) == 0x61000, "GBA savestate struct sized wrong");
|
||||
|
|
|
@ -6,6 +6,10 @@
|
|||
#ifndef M_SCRIPT_SOCKET_H
|
||||
#define M_SCRIPT_SOCKET_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
enum mSocketErrorCode {
|
||||
mSCRIPT_SOCKERR_UNKNOWN_ERROR = -1,
|
||||
mSCRIPT_SOCKERR_OK = 0,
|
||||
|
@ -22,4 +26,6 @@ enum mSocketErrorCode {
|
|||
mSCRIPT_SOCKERR_UNSUPPORTED,
|
||||
};
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/* 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_TYPES_INTERNAL_H
|
||||
#define M_SCRIPT_TYPES_INTERNAL_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
struct Table;
|
||||
void mScriptContextGetInputTypes(struct Table*);
|
||||
|
||||
void mScriptTypeAdd(struct Table*, const struct mScriptType* type);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -89,13 +89,14 @@ uint32_t mScriptContextSetWeakref(struct mScriptContext*, struct mScriptValue* v
|
|||
struct mScriptValue* mScriptContextMakeWeakref(struct mScriptContext*, struct mScriptValue* value);
|
||||
struct mScriptValue* mScriptContextAccessWeakref(struct mScriptContext*, struct mScriptValue* value);
|
||||
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);
|
||||
void mScriptContextTriggerCallback(struct mScriptContext*, const char* callback, struct mScriptList* args);
|
||||
uint32_t mScriptContextAddCallback(struct mScriptContext*, const char* callback, struct mScriptValue* value);
|
||||
void mScriptContextRemoveCallback(struct mScriptContext*, uint32_t cbid);
|
||||
|
||||
|
|
|
@ -0,0 +1,271 @@
|
|||
/* Copyright (c) 2013-2022 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef M_SCRIPT_INPUT_H
|
||||
#define M_SCRIPT_INPUT_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
#include <mgba/script/context.h>
|
||||
#include <mgba/script/macros.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
enum mScriptEventType {
|
||||
mSCRIPT_EV_TYPE_NONE = 0,
|
||||
mSCRIPT_EV_TYPE_KEY,
|
||||
mSCRIPT_EV_TYPE_MOUSE_BUTTON,
|
||||
mSCRIPT_EV_TYPE_MOUSE_MOVE,
|
||||
mSCRIPT_EV_TYPE_MOUSE_WHEEL,
|
||||
mSCRIPT_EV_TYPE_GAMEPAD_BUTTON,
|
||||
mSCRIPT_EV_TYPE_GAMEPAD_HAT,
|
||||
mSCRIPT_EV_TYPE_TRIGGER,
|
||||
mSCRIPT_EV_TYPE_MAX
|
||||
};
|
||||
|
||||
enum mScriptInputState {
|
||||
mSCRIPT_INPUT_STATE_UP = 0,
|
||||
mSCRIPT_INPUT_STATE_DOWN = 1,
|
||||
mSCRIPT_INPUT_STATE_HELD = 2,
|
||||
};
|
||||
|
||||
enum mScriptInputDirection {
|
||||
mSCRIPT_INPUT_DIR_NONE = 0,
|
||||
|
||||
mSCRIPT_INPUT_DIR_NORTH = 1,
|
||||
mSCRIPT_INPUT_DIR_EAST = 2,
|
||||
mSCRIPT_INPUT_DIR_SOUTH = 4,
|
||||
mSCRIPT_INPUT_DIR_WEST = 8,
|
||||
|
||||
mSCRIPT_INPUT_DIR_UP = mSCRIPT_INPUT_DIR_NORTH,
|
||||
mSCRIPT_INPUT_DIR_RIGHT = mSCRIPT_INPUT_DIR_EAST,
|
||||
mSCRIPT_INPUT_DIR_DOWN = mSCRIPT_INPUT_DIR_SOUTH,
|
||||
mSCRIPT_INPUT_DIR_LEFT = mSCRIPT_INPUT_DIR_WEST,
|
||||
|
||||
mSCRIPT_INPUT_DIR_NORTHEAST = mSCRIPT_INPUT_DIR_NORTH | mSCRIPT_INPUT_DIR_EAST,
|
||||
mSCRIPT_INPUT_DIR_NORTHWEST = mSCRIPT_INPUT_DIR_NORTH | mSCRIPT_INPUT_DIR_WEST,
|
||||
mSCRIPT_INPUT_DIR_SOUTHEAST = mSCRIPT_INPUT_DIR_SOUTH | mSCRIPT_INPUT_DIR_EAST,
|
||||
mSCRIPT_INPUT_DIR_SOUTHWEST = mSCRIPT_INPUT_DIR_SOUTH | mSCRIPT_INPUT_DIR_WEST,
|
||||
};
|
||||
|
||||
enum mScriptKeyModifier {
|
||||
mSCRIPT_KMOD_NONE = 0,
|
||||
|
||||
mSCRIPT_KMOD_LSHIFT = 0x1,
|
||||
mSCRIPT_KMOD_RSHIFT = 0x2,
|
||||
mSCRIPT_KMOD_SHIFT = 0x3,
|
||||
|
||||
mSCRIPT_KMOD_LCONTROL = 0x4,
|
||||
mSCRIPT_KMOD_RCONTROL = 0x8,
|
||||
mSCRIPT_KMOD_CONTROL = 0xC,
|
||||
|
||||
mSCRIPT_KMOD_LALT = 0x10,
|
||||
mSCRIPT_KMOD_RALT = 0x20,
|
||||
mSCRIPT_KMOD_ALT = 0x30,
|
||||
|
||||
mSCRIPT_KMOD_LSUPER = 0x40,
|
||||
mSCRIPT_KMOD_RSUPER = 0x80,
|
||||
mSCRIPT_KMOD_SUPER = 0xC0,
|
||||
|
||||
mSCRIPT_KMOD_CAPS_LOCK = 0x100,
|
||||
mSCRIPT_KMOD_NUM_LOCK = 0x200,
|
||||
mSCRIPT_KMOD_SCROLL_LOCK = 0x400,
|
||||
};
|
||||
|
||||
#define mSCRIPT_KEYBASE 0x800000
|
||||
|
||||
enum mScriptKey {
|
||||
mSCRIPT_KEY_NONE = 0,
|
||||
|
||||
mSCRIPT_KEY_BACKSPACE = 0x000008,
|
||||
mSCRIPT_KEY_TAB = 0x000009,
|
||||
mSCRIPT_KEY_ENTER = 0x00000A,
|
||||
mSCRIPT_KEY_ESCAPE = 0x00001B,
|
||||
mSCRIPT_KEY_DELETE = 0x00007F,
|
||||
|
||||
mSCRIPT_KEY_F1 = mSCRIPT_KEYBASE | 1,
|
||||
mSCRIPT_KEY_F2,
|
||||
mSCRIPT_KEY_F3,
|
||||
mSCRIPT_KEY_F4,
|
||||
mSCRIPT_KEY_F5,
|
||||
mSCRIPT_KEY_F6,
|
||||
mSCRIPT_KEY_F7,
|
||||
mSCRIPT_KEY_F8,
|
||||
mSCRIPT_KEY_F9,
|
||||
mSCRIPT_KEY_F10,
|
||||
mSCRIPT_KEY_F11,
|
||||
mSCRIPT_KEY_F12,
|
||||
mSCRIPT_KEY_F13,
|
||||
mSCRIPT_KEY_F14,
|
||||
mSCRIPT_KEY_F15,
|
||||
mSCRIPT_KEY_F16,
|
||||
mSCRIPT_KEY_F17,
|
||||
mSCRIPT_KEY_F18,
|
||||
mSCRIPT_KEY_F19,
|
||||
mSCRIPT_KEY_F20,
|
||||
mSCRIPT_KEY_F21,
|
||||
mSCRIPT_KEY_F22,
|
||||
mSCRIPT_KEY_F23,
|
||||
mSCRIPT_KEY_F24,
|
||||
|
||||
mSCRIPT_KEY_UP = mSCRIPT_KEYBASE | 0x20,
|
||||
mSCRIPT_KEY_RIGHT,
|
||||
mSCRIPT_KEY_DOWN,
|
||||
mSCRIPT_KEY_LEFT,
|
||||
mSCRIPT_KEY_PAGE_UP,
|
||||
mSCRIPT_KEY_PAGE_DOWN,
|
||||
mSCRIPT_KEY_HOME,
|
||||
mSCRIPT_KEY_END,
|
||||
mSCRIPT_KEY_INSERT,
|
||||
mSCRIPT_KEY_BREAK,
|
||||
mSCRIPT_KEY_CLEAR,
|
||||
mSCRIPT_KEY_PRINT_SCREEN,
|
||||
mSCRIPT_KEY_SYSRQ,
|
||||
mSCRIPT_KEY_MENU,
|
||||
mSCRIPT_KEY_HELP,
|
||||
|
||||
mSCRIPT_KEY_LSHIFT = mSCRIPT_KEYBASE | 0x30,
|
||||
mSCRIPT_KEY_RSHIFT,
|
||||
mSCRIPT_KEY_SHIFT,
|
||||
mSCRIPT_KEY_LCONTROL,
|
||||
mSCRIPT_KEY_RCONTROL,
|
||||
mSCRIPT_KEY_CONTROL,
|
||||
mSCRIPT_KEY_LALT,
|
||||
mSCRIPT_KEY_RALT,
|
||||
mSCRIPT_KEY_ALT,
|
||||
mSCRIPT_KEY_LSUPER,
|
||||
mSCRIPT_KEY_RSUPER,
|
||||
mSCRIPT_KEY_SUPER,
|
||||
mSCRIPT_KEY_CAPS_LOCK,
|
||||
mSCRIPT_KEY_NUM_LOCK,
|
||||
mSCRIPT_KEY_SCROLL_LOCK,
|
||||
|
||||
mSCRIPT_KEY_KP_0 = mSCRIPT_KEYBASE | 0x40,
|
||||
mSCRIPT_KEY_KP_1,
|
||||
mSCRIPT_KEY_KP_2,
|
||||
mSCRIPT_KEY_KP_3,
|
||||
mSCRIPT_KEY_KP_4,
|
||||
mSCRIPT_KEY_KP_5,
|
||||
mSCRIPT_KEY_KP_6,
|
||||
mSCRIPT_KEY_KP_7,
|
||||
mSCRIPT_KEY_KP_8,
|
||||
mSCRIPT_KEY_KP_9,
|
||||
mSCRIPT_KEY_KP_PLUS,
|
||||
mSCRIPT_KEY_KP_MINUS,
|
||||
mSCRIPT_KEY_KP_MULTIPLY,
|
||||
mSCRIPT_KEY_KP_DIVIDE,
|
||||
mSCRIPT_KEY_KP_COMMA,
|
||||
mSCRIPT_KEY_KP_POINT,
|
||||
mSCRIPT_KEY_KP_ENTER,
|
||||
};
|
||||
|
||||
enum mScriptMouseButton {
|
||||
mSCRIPT_MOUSE_BUTTON_PRIMARY = 0,
|
||||
mSCRIPT_MOUSE_BUTTON_SECONDARY = 1,
|
||||
mSCRIPT_MOUSE_BUTTON_MIDDLE = 2,
|
||||
};
|
||||
|
||||
struct mScriptEvent {
|
||||
int32_t type;
|
||||
int32_t reserved;
|
||||
uint64_t seq;
|
||||
};
|
||||
|
||||
struct mScriptKeyEvent {
|
||||
struct mScriptEvent d;
|
||||
uint8_t state;
|
||||
uint16_t modifiers;
|
||||
uint32_t key;
|
||||
};
|
||||
|
||||
struct mScriptMouseButtonEvent {
|
||||
struct mScriptEvent d;
|
||||
uint8_t mouse;
|
||||
uint8_t context;
|
||||
uint8_t state;
|
||||
uint8_t button;
|
||||
};
|
||||
|
||||
struct mScriptMouseMoveEvent {
|
||||
struct mScriptEvent d;
|
||||
uint8_t mouse;
|
||||
uint8_t context;
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
};
|
||||
|
||||
struct mScriptMouseWheelEvent {
|
||||
struct mScriptEvent d;
|
||||
uint8_t mouse;
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
};
|
||||
|
||||
struct mScriptGamepadButtonEvent {
|
||||
struct mScriptEvent d;
|
||||
uint8_t state;
|
||||
uint8_t pad;
|
||||
uint16_t button;
|
||||
};
|
||||
|
||||
struct mScriptGamepadHatEvent {
|
||||
struct mScriptEvent d;
|
||||
uint8_t pad;
|
||||
uint8_t hat;
|
||||
uint8_t direction;
|
||||
};
|
||||
|
||||
struct mScriptTriggerEvent {
|
||||
struct mScriptEvent d;
|
||||
uint8_t trigger;
|
||||
bool state;
|
||||
};
|
||||
|
||||
struct mScriptGamepad {
|
||||
unsigned pad;
|
||||
|
||||
char visibleName[128];
|
||||
char internalName[64];
|
||||
|
||||
struct mScriptList axes;
|
||||
struct mScriptList buttons;
|
||||
struct mScriptList hats;
|
||||
};
|
||||
|
||||
mSCRIPT_DECLARE_STRUCT(mScriptEvent);
|
||||
mSCRIPT_DECLARE_STRUCT(mScriptKeyEvent);
|
||||
mSCRIPT_DECLARE_STRUCT(mScriptMouseButtonEvent);
|
||||
mSCRIPT_DECLARE_STRUCT(mScriptMouseMoveEvent);
|
||||
mSCRIPT_DECLARE_STRUCT(mScriptMouseWheelEvent);
|
||||
mSCRIPT_DECLARE_STRUCT(mScriptGamepadButtonEvent);
|
||||
mSCRIPT_DECLARE_STRUCT(mScriptGamepadHatEvent);
|
||||
mSCRIPT_DECLARE_STRUCT(mScriptSensorEvent);
|
||||
mSCRIPT_DECLARE_STRUCT(mScriptTriggerEvent);
|
||||
|
||||
mSCRIPT_DECLARE_STRUCT(mScriptGamepad);
|
||||
|
||||
void mScriptContextAttachInput(struct mScriptContext* context);
|
||||
|
||||
void mScriptContextFireEvent(struct mScriptContext*, struct mScriptEvent*);
|
||||
|
||||
int mScriptContextGamepadAttach(struct mScriptContext*, struct mScriptGamepad*);
|
||||
bool mScriptContextGamepadDetach(struct mScriptContext*, int pad);
|
||||
struct mScriptGamepad* mScriptContextGamepadLookup(struct mScriptContext*, int pad);
|
||||
|
||||
void mScriptGamepadInit(struct mScriptGamepad*);
|
||||
void mScriptGamepadDeinit(struct mScriptGamepad*);
|
||||
void mScriptGamepadSetAxisCount(struct mScriptGamepad*, unsigned);
|
||||
void mScriptGamepadSetButtonCount(struct mScriptGamepad*, unsigned);
|
||||
void mScriptGamepadSetHatCount(struct mScriptGamepad*, unsigned);
|
||||
void mScriptGamepadSetAxis(struct mScriptGamepad*, unsigned, int16_t value);
|
||||
void mScriptGamepadSetButton(struct mScriptGamepad*, unsigned, bool down);
|
||||
void mScriptGamepadSetHat(struct mScriptGamepad*, unsigned, int direction);
|
||||
int16_t mScriptGamepadGetAxis(struct mScriptGamepad*, unsigned);
|
||||
bool mScriptGamepadGetButton(struct mScriptGamepad*, unsigned);
|
||||
int mScriptGamepadGetHat(struct mScriptGamepad*, unsigned);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -76,6 +76,7 @@ CXX_GUARD_START
|
|||
#define mSCRIPT_TYPE_FIELD_W(TYPE) opaque
|
||||
#define mSCRIPT_TYPE_FIELD_CW(TYPE) opaque
|
||||
|
||||
#define mSCRIPT_TYPE_MS_VOID (&mSTVoid)
|
||||
#define mSCRIPT_TYPE_MS_S8 (&mSTSInt8)
|
||||
#define mSCRIPT_TYPE_MS_U8 (&mSTUInt8)
|
||||
#define mSCRIPT_TYPE_MS_S16 (&mSTSInt16)
|
||||
|
@ -97,7 +98,7 @@ CXX_GUARD_START
|
|||
#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) (&mSTStructConstPtr_ ## 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_W(TYPE) (&mSTWrapper_ ## TYPE)
|
||||
|
@ -303,6 +304,8 @@ void mScriptValueWrap(struct mScriptValue* val, struct mScriptValue* out);
|
|||
struct mScriptValue* mScriptValueUnwrap(struct mScriptValue* val);
|
||||
const struct mScriptValue* mScriptValueUnwrapConst(const struct mScriptValue* val);
|
||||
|
||||
void mScriptValueFollowPointer(struct mScriptValue* ptr, struct mScriptValue* out);
|
||||
|
||||
struct mScriptValue* mScriptStringCreateEmpty(size_t size);
|
||||
struct mScriptValue* mScriptStringCreateFromBytes(const void* string, size_t size);
|
||||
struct mScriptValue* mScriptStringCreateFromUTF8(const char* string);
|
||||
|
|
|
@ -56,5 +56,7 @@
|
|||
<string>Viewer</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.games</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
inputBuffer = console:createBuffer("Input")
|
||||
|
||||
function readPad()
|
||||
inputBuffer:clear()
|
||||
|
||||
if not input.activeGamepad then
|
||||
inputBuffer:print("No gamepad detected\n")
|
||||
return
|
||||
end
|
||||
|
||||
local gamepad = input.activeGamepad
|
||||
local axes = gamepad.axes
|
||||
local buttons = gamepad.buttons
|
||||
local hats = gamepad.hats
|
||||
|
||||
inputBuffer:print(gamepad.visibleName .. "\n")
|
||||
inputBuffer:print(string.format("%i buttons, %i axes, %i hats\n", #buttons, #axes, #hats))
|
||||
|
||||
local sbuttons = {}
|
||||
for k, v in ipairs(buttons) do
|
||||
if v then
|
||||
sbuttons[k] = "down"
|
||||
else
|
||||
sbuttons[k] = " up"
|
||||
end
|
||||
end
|
||||
|
||||
inputBuffer:print(string.format("Buttons: %s\n", table.concat(sbuttons, ", ")))
|
||||
inputBuffer:print(string.format("Axes: %s\n", table.concat(axes, ", ")))
|
||||
inputBuffer:print(string.format("Hats: %s\n", table.concat(hats, ", ")))
|
||||
end
|
||||
|
||||
callbacks:add("frame", readPad)
|
|
@ -412,6 +412,13 @@ local gameRubyEn = Generation3En:new{
|
|||
_speciesNameTable=0x1f716c,
|
||||
}
|
||||
|
||||
local gameRubyEnR1 = Generation3En:new{
|
||||
name="Ruby (USA)",
|
||||
_party=0x3004360,
|
||||
_partyCount=0x3004350,
|
||||
_speciesNameTable=0x1f7184,
|
||||
}
|
||||
|
||||
local gameSapphireEn = Generation3En:new{
|
||||
name="Sapphire (USA)",
|
||||
_party=0x3004360,
|
||||
|
@ -419,6 +426,13 @@ local gameSapphireEn = Generation3En:new{
|
|||
_speciesNameTable=0x1f70fc,
|
||||
}
|
||||
|
||||
local gameSapphireEnR1 = Generation3En:new{
|
||||
name="Sapphire (USA)",
|
||||
_party=0x3004360,
|
||||
_partyCount=0x3004350,
|
||||
_speciesNameTable=0x1f7114,
|
||||
}
|
||||
|
||||
local gameEmeraldEn = Generation3En:new{
|
||||
name="Emerald (USA)",
|
||||
_party=0x20244ec,
|
||||
|
@ -471,6 +485,10 @@ gameCrc32 = {
|
|||
[0x7d527d62] = gameYellowEn,
|
||||
[0x84ee4776] = gameFireRedEnR1,
|
||||
[0xdaffecec] = gameLeafGreenEnR1,
|
||||
[0x61641576] = gameRubyEnR1, -- Rev 1
|
||||
[0xaeac73e6] = gameRubyEnR1, -- Rev 2
|
||||
[0xbafedae5] = gameSapphireEnR1, -- Rev 1
|
||||
[0x9cc4410e] = gameSapphireEnR1, -- Rev 2
|
||||
}
|
||||
|
||||
function printPartyStatus(game, buffer)
|
||||
|
|
|
@ -92,12 +92,13 @@ 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) {
|
||||
--width;
|
||||
struct mWatchpoint* watchpoint;
|
||||
size_t i;
|
||||
uint32_t minAddress = address & ~(width - 1);
|
||||
uint32_t maxAddress = minAddress + width;
|
||||
for (i = 0; i < mWatchpointListSize(&debugger->watchpoints); ++i) {
|
||||
watchpoint = mWatchpointListGetPointer(&debugger->watchpoints, i);
|
||||
if (!((watchpoint->address ^ address) & ~width) && watchpoint->type & type) {
|
||||
if (watchpoint->type & type && watchpoint->minAddress < maxAddress && minAddress < watchpoint->maxAddress) {
|
||||
if (watchpoint->condition) {
|
||||
int32_t value;
|
||||
int segment;
|
||||
|
@ -107,7 +108,7 @@ static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, st
|
|||
}
|
||||
|
||||
uint32_t oldValue;
|
||||
switch (width + 1) {
|
||||
switch (width) {
|
||||
case 1:
|
||||
oldValue = debugger->originalMemory.load8(debugger->cpu, address, 0);
|
||||
break;
|
||||
|
|
|
@ -38,6 +38,11 @@ struct mInputAxisEnumerate {
|
|||
void* user;
|
||||
};
|
||||
|
||||
struct mInputHatEnumerate {
|
||||
void (*handler)(int axis, const struct mInputHatBindings* bindings, void* user);
|
||||
void* user;
|
||||
};
|
||||
|
||||
static void _makeSectionName(const char* platform, char* sectionName, size_t len, uint32_t type) {
|
||||
snprintf(sectionName, len, "%s.input.%c%c%c%c", platform, type >> 24, type >> 16, type >> 8, type);
|
||||
sectionName[len - 1] = '\0';
|
||||
|
@ -304,6 +309,12 @@ void _unbindAxis(uint32_t axis, void* dp, void* user) {
|
|||
}
|
||||
}
|
||||
|
||||
void _enumerateHat(uint32_t axis, void* dp, void* ep) {
|
||||
struct mInputHatEnumerate* enumUser = ep;
|
||||
const struct mInputHatBindings* description = dp;
|
||||
enumUser->handler(axis, description, enumUser->user);
|
||||
}
|
||||
|
||||
static bool _loadAll(struct mInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config) {
|
||||
if (!ConfigurationHasSection(config, sectionName)) {
|
||||
return false;
|
||||
|
@ -415,6 +426,16 @@ void mInputUnbindKey(struct mInputMap* map, uint32_t type, int input) {
|
|||
}
|
||||
}
|
||||
|
||||
void mInputUnbindAllKeys(struct mInputMap* map, uint32_t type) {
|
||||
struct mInputMapImpl* impl = _lookupMap(map, type);
|
||||
if (impl) {
|
||||
size_t i;
|
||||
for (i = 0; i < map->info->nKeys; ++i) {
|
||||
impl->map[i] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int mInputQueryBinding(const struct mInputMap* map, uint32_t type, int input) {
|
||||
if (input < 0 || (size_t) input >= map->info->nKeys) {
|
||||
return -1;
|
||||
|
@ -578,6 +599,18 @@ void mInputUnbindAllHats(struct mInputMap* map, uint32_t type) {
|
|||
}
|
||||
}
|
||||
|
||||
void mInputEnumerateHats(const struct mInputMap* map, uint32_t type, void (handler(int hat, const struct mInputHatBindings* bindings, void* user)), void* user) {
|
||||
const struct mInputMapImpl* impl = _lookupMapConst(map, type);
|
||||
if (!impl) {
|
||||
return;
|
||||
}
|
||||
struct mInputHatEnumerate enumUser = {
|
||||
handler,
|
||||
user
|
||||
};
|
||||
TableEnumerate(&impl->axes, _enumerateHat, &enumUser);
|
||||
}
|
||||
|
||||
bool mInputMapLoad(struct mInputMap* map, uint32_t type, const struct Configuration* config) {
|
||||
char sectionName[SECTION_NAME_MAX];
|
||||
_makeSectionName(map->info->platformName, sectionName, SECTION_NAME_MAX, type);
|
||||
|
|
|
@ -651,7 +651,7 @@ static struct mScriptValue* _mScriptCoreAdapterGet(struct mScriptCoreAdapter* ad
|
|||
|
||||
static void _mScriptCoreAdapterReset(struct mScriptCoreAdapter* adapter) {
|
||||
adapter->core->reset(adapter->core);
|
||||
mScriptContextTriggerCallback(adapter->context, "reset");
|
||||
mScriptContextTriggerCallback(adapter->context, "reset", NULL);
|
||||
}
|
||||
|
||||
mSCRIPT_DECLARE_STRUCT(mScriptCoreAdapter);
|
||||
|
@ -761,14 +761,16 @@ mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mScriptConsole, createBuffer)
|
|||
mSCRIPT_DEFINE_DEFAULTS_END;
|
||||
|
||||
static struct mScriptConsole* _ensureConsole(struct mScriptContext* context) {
|
||||
struct mScriptValue* value = mScriptContextEnsureGlobal(context, "console", mSCRIPT_TYPE_MS_S(mScriptConsole));
|
||||
struct mScriptConsole* console = value->value.opaque;
|
||||
if (!console) {
|
||||
console = calloc(1, sizeof(*console));
|
||||
value->value.opaque = console;
|
||||
value->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER;
|
||||
mScriptContextSetDocstring(context, "console", "Singleton instance of struct::mScriptConsole");
|
||||
struct mScriptValue* value = mScriptContextGetGlobal(context, "console");
|
||||
if (value) {
|
||||
return value->value.opaque;
|
||||
}
|
||||
struct mScriptConsole* console = calloc(1, sizeof(*console));
|
||||
value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptConsole));
|
||||
value->value.opaque = console;
|
||||
value->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER;
|
||||
mScriptContextSetGlobal(context, "console", value);
|
||||
mScriptContextSetDocstring(context, "console", "Singleton instance of struct::mScriptConsole");
|
||||
return console;
|
||||
}
|
||||
|
||||
|
|
|
@ -262,8 +262,18 @@ static void* _loadPNGState(struct mCore* core, struct VFile* vf, struct mStateEx
|
|||
PNGReadClose(png, info, end);
|
||||
return false;
|
||||
}
|
||||
unsigned width, height;
|
||||
core->desiredVideoDimensions(core, &width, &height);
|
||||
|
||||
if (!PNGReadHeader(png, info)) {
|
||||
PNGReadClose(png, info, end);
|
||||
return false;
|
||||
}
|
||||
unsigned width = png_get_image_width(png, info);
|
||||
unsigned height = png_get_image_height(png, info);
|
||||
if (width > 0x4000 || height > 0x4000) {
|
||||
// These images are ridiculously large...let's assume a DOS attempt and reject
|
||||
PNGReadClose(png, info, end);
|
||||
return false;
|
||||
}
|
||||
uint32_t* pixels = malloc(width * height * 4);
|
||||
if (!pixels) {
|
||||
PNGReadClose(png, info, end);
|
||||
|
@ -278,8 +288,8 @@ static void* _loadPNGState(struct mCore* core, struct VFile* vf, struct mStateEx
|
|||
.extdata = extdata
|
||||
};
|
||||
|
||||
bool success = true;
|
||||
PNGInstallChunkHandler(png, &bundle, _loadPNGChunkHandler, "gbAs gbAx");
|
||||
bool success = PNGReadHeader(png, info);
|
||||
success = success && PNGReadPixels(png, info, pixels, width, height, width);
|
||||
success = success && PNGReadFooter(png, end);
|
||||
PNGReadClose(png, info, end);
|
||||
|
@ -295,6 +305,12 @@ static void* _loadPNGState(struct mCore* core, struct VFile* vf, struct mStateEx
|
|||
.clean = free
|
||||
};
|
||||
mStateExtdataPut(extdata, EXTDATA_SCREENSHOT, &item);
|
||||
|
||||
uint16_t dims[2] = { width, height };
|
||||
item.size = sizeof(dims);
|
||||
item.data = malloc(item.size);
|
||||
memcpy(item.data, dims, item.size);
|
||||
mStateExtdataPut(extdata, EXTDATA_SCREENSHOT_DIMENSIONS, &item);
|
||||
} else {
|
||||
free(pixels);
|
||||
}
|
||||
|
|
|
@ -12,10 +12,12 @@
|
|||
#include <mgba/script/context.h>
|
||||
#include <mgba/script/types.h>
|
||||
|
||||
#include "script/test.h"
|
||||
|
||||
#ifdef M_CORE_GBA
|
||||
#include <mgba/internal/gba/memory.h>
|
||||
#define TEST_PLATFORM mPLATFORM_GBA
|
||||
#define RAM_BASE BASE_WORKING_IRAM
|
||||
#define RAM_BASE GBA_BASE_IWRAM
|
||||
#elif defined(M_CORE_GB)
|
||||
#include <mgba/internal/gb/memory.h>
|
||||
#define TEST_PLATFORM mPLATFORM_GB
|
||||
|
@ -66,22 +68,6 @@ static const uint8_t _fakeGBROM[0x4000] = {
|
|||
mCoreConfigDeinit(&core->config); \
|
||||
core->deinit(core)
|
||||
|
||||
#define LOAD_PROGRAM(PROG) \
|
||||
do { \
|
||||
struct VFile* vf = VFileFromConstMemory(PROG, strlen(PROG)); \
|
||||
assert_true(lua->load(lua, NULL, vf)); \
|
||||
vf->close(vf); \
|
||||
} while(0)
|
||||
|
||||
#define TEST_VALUE(TYPE, NAME, VALUE) \
|
||||
do { \
|
||||
struct mScriptValue val = mSCRIPT_MAKE(TYPE, VALUE); \
|
||||
struct mScriptValue* global = lua->getGlobal(lua, NAME); \
|
||||
assert_non_null(global); \
|
||||
assert_true(global->type->equal(global, &val)); \
|
||||
mScriptValueDeref(global); \
|
||||
} while(0)
|
||||
|
||||
static void _mScriptTestLog(struct mLogger* log, int category, enum mLogLevel level, const char* format, va_list args) {
|
||||
UNUSED(category);
|
||||
struct mScriptTestLogger* logger = (struct mScriptTestLogger*) log;
|
||||
|
|
|
@ -58,8 +58,19 @@ static void _waitOnInterrupt(struct mCoreThreadInternal* threadContext) {
|
|||
}
|
||||
|
||||
static void _pokeRequest(struct mCoreThreadInternal* threadContext) {
|
||||
if (threadContext->state == mTHREAD_RUNNING || threadContext->state == mTHREAD_PAUSED) {
|
||||
switch (threadContext->state) {
|
||||
case mTHREAD_RUNNING:
|
||||
case mTHREAD_PAUSED:
|
||||
case mTHREAD_CRASHED:
|
||||
threadContext->state = mTHREAD_REQUEST;
|
||||
break;
|
||||
case mTHREAD_INITIALIZED:
|
||||
case mTHREAD_REQUEST:
|
||||
case mTHREAD_INTERRUPTED:
|
||||
case mTHREAD_INTERRUPTING:
|
||||
case mTHREAD_EXITING:
|
||||
case mTHREAD_SHUTDOWN:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,7 +205,7 @@ void _script_ ## NAME(void* context) { \
|
|||
if (!threadContext->scriptContext) { \
|
||||
return; \
|
||||
} \
|
||||
mScriptContextTriggerCallback(threadContext->scriptContext, #NAME); \
|
||||
mScriptContextTriggerCallback(threadContext->scriptContext, #NAME, NULL); \
|
||||
}
|
||||
|
||||
ADD_CALLBACK(frame)
|
||||
|
@ -283,7 +294,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
|||
}
|
||||
}
|
||||
if (scriptContext) {
|
||||
mScriptContextTriggerCallback(scriptContext, "start");
|
||||
mScriptContextTriggerCallback(scriptContext, "start", NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -304,7 +315,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
|||
}
|
||||
}
|
||||
if (scriptContext) {
|
||||
mScriptContextTriggerCallback(scriptContext, "reset");
|
||||
mScriptContextTriggerCallback(scriptContext, "reset", NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -404,7 +415,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
|||
}
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
if (scriptContext) {
|
||||
mScriptContextTriggerCallback(scriptContext, "reset");
|
||||
mScriptContextTriggerCallback(scriptContext, "reset", NULL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -428,7 +439,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
|||
}
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
if (scriptContext) {
|
||||
mScriptContextTriggerCallback(scriptContext, "shutdown");
|
||||
mScriptContextTriggerCallback(scriptContext, "shutdown", NULL);
|
||||
mScriptContextDetachCore(scriptContext);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -60,6 +60,10 @@ static void _setReadWriteWatchpoint(struct CLIDebugger*, struct CLIDebugVector*)
|
|||
static void _setReadWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _setWriteWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _setWriteChangedWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _setReadWriteRangeWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _setReadRangeWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _setWriteRangeWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _setWriteChangedRangeWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _listWatchpoints(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _trace(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _writeByte(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
|
@ -114,6 +118,10 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = {
|
|||
{ "watch/c", _setWriteChangedWatchpoint, "Is", "Set a change watchpoint" },
|
||||
{ "watch/r", _setReadWatchpoint, "Is", "Set a read watchpoint" },
|
||||
{ "watch/w", _setWriteWatchpoint, "Is", "Set a write watchpoint" },
|
||||
{ "watch-range", _setReadWriteRangeWatchpoint, "IIs", "Set a range watchpoint" },
|
||||
{ "watch-range/c", _setWriteChangedRangeWatchpoint, "IIs", "Set a change range watchpoint" },
|
||||
{ "watch-range/r", _setReadRangeWatchpoint, "IIs", "Set a read range watchpoint" },
|
||||
{ "watch-range/w", _setWriteRangeWatchpoint, "IIs", "Set a write range watchpoint" },
|
||||
{ "x/1", _dumpByte, "Ii", "Examine bytes at a specified offset" },
|
||||
{ "x/2", _dumpHalfword, "Ii", "Examine halfwords at a specified offset" },
|
||||
{ "x/4", _dumpWord, "Ii", "Examine words at a specified offset" },
|
||||
|
@ -146,6 +154,14 @@ static struct CLIDebuggerCommandAlias _debuggerCommandAliases[] = {
|
|||
{ "p/x", "print/x" },
|
||||
{ "q", "quit" },
|
||||
{ "w", "watch" },
|
||||
{ "watchr", "watch-range" },
|
||||
{ "wr", "watch-range" },
|
||||
{ "watchr/c", "watch-range/c" },
|
||||
{ "wr/c", "watch-range/c" },
|
||||
{ "watchr/r", "watch-range/r" },
|
||||
{ "wr/r", "watch-range/r" },
|
||||
{ "watchr/w", "watch-range/w" },
|
||||
{ "wr/w", "watch-range/w" },
|
||||
{ ".", "source" },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
@ -647,8 +663,9 @@ static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector*
|
|||
return;
|
||||
}
|
||||
struct mWatchpoint watchpoint = {
|
||||
.address = dv->intValue,
|
||||
.segment = dv->segmentValue,
|
||||
.minAddress = dv->intValue,
|
||||
.maxAddress = dv->intValue + 1,
|
||||
.type = type
|
||||
};
|
||||
if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) {
|
||||
|
@ -666,6 +683,48 @@ static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector*
|
|||
}
|
||||
}
|
||||
|
||||
static void _setRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv, enum mWatchpointType type) {
|
||||
if (!dv || dv->type != CLIDV_INT_TYPE) {
|
||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
|
||||
return;
|
||||
}
|
||||
if (!dv->next || dv->next->type != CLIDV_INT_TYPE) {
|
||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
|
||||
return;
|
||||
}
|
||||
if (!debugger->d.platform->setWatchpoint) {
|
||||
debugger->backend->printf(debugger->backend, "Watchpoints are not supported by this platform.\n");
|
||||
return;
|
||||
}
|
||||
if (dv->intValue >= dv->next->intValue) {
|
||||
debugger->backend->printf(debugger->backend, "Range watchpoint end is before start. Note that the end of the range is not included.\n");
|
||||
return;
|
||||
}
|
||||
if (dv->segmentValue != dv->next->segmentValue) {
|
||||
debugger->backend->printf(debugger->backend, "Range watchpoint does not start and end in the same segment.\n");
|
||||
return;
|
||||
}
|
||||
struct mWatchpoint watchpoint = {
|
||||
.segment = dv->segmentValue,
|
||||
.minAddress = dv->intValue,
|
||||
.maxAddress = dv->next->intValue,
|
||||
.type = type
|
||||
};
|
||||
if (dv->next->next && dv->next->next->type == CLIDV_CHAR_TYPE) {
|
||||
struct ParseTree* tree = _parseTree((const char*[]) { dv->next->next->charValue, NULL });
|
||||
if (tree) {
|
||||
watchpoint.condition = tree;
|
||||
} else {
|
||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ssize_t id = debugger->d.platform->setWatchpoint(debugger->d.platform, &watchpoint);
|
||||
if (id > 0) {
|
||||
debugger->backend->printf(debugger->backend, INFO_WATCHPOINT_ADDED, id);
|
||||
}
|
||||
}
|
||||
|
||||
static void _setReadWriteWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
_setWatchpoint(debugger, dv, WATCHPOINT_RW);
|
||||
}
|
||||
|
@ -682,6 +741,22 @@ static void _setWriteChangedWatchpoint(struct CLIDebugger* debugger, struct CLID
|
|||
_setWatchpoint(debugger, dv, WATCHPOINT_WRITE_CHANGE);
|
||||
}
|
||||
|
||||
static void _setReadWriteRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
_setRangeWatchpoint(debugger, dv, WATCHPOINT_RW);
|
||||
}
|
||||
|
||||
static void _setReadRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
_setRangeWatchpoint(debugger, dv, WATCHPOINT_READ);
|
||||
}
|
||||
|
||||
static void _setWriteRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
_setRangeWatchpoint(debugger, dv, WATCHPOINT_WRITE);
|
||||
}
|
||||
|
||||
static void _setWriteChangedRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
_setRangeWatchpoint(debugger, dv, WATCHPOINT_WRITE_CHANGE);
|
||||
}
|
||||
|
||||
static void _clearBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
if (!dv || dv->type != CLIDV_INT_TYPE) {
|
||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
|
||||
|
@ -717,9 +792,17 @@ static void _listWatchpoints(struct CLIDebugger* debugger, struct CLIDebugVector
|
|||
for (i = 0; i < mWatchpointListSize(&watchpoints); ++i) {
|
||||
struct mWatchpoint* watchpoint = mWatchpointListGetPointer(&watchpoints, i);
|
||||
if (watchpoint->segment >= 0) {
|
||||
debugger->backend->printf(debugger->backend, "%" PRIz "i: %02X:%X\n", watchpoint->id, watchpoint->segment, watchpoint->address);
|
||||
if (watchpoint->maxAddress == watchpoint->minAddress + 1) {
|
||||
debugger->backend->printf(debugger->backend, "%" PRIz "i: %02X:%X\n", watchpoint->id, watchpoint->segment, watchpoint->minAddress);
|
||||
} else {
|
||||
debugger->backend->printf(debugger->backend, "%" PRIz "i: %02X:%X-%X\n", watchpoint->id, watchpoint->segment, watchpoint->minAddress, watchpoint->maxAddress);
|
||||
}
|
||||
} else {
|
||||
debugger->backend->printf(debugger->backend, "%" PRIz "i: 0x%X\n", watchpoint->id, watchpoint->address);
|
||||
if (watchpoint->maxAddress == watchpoint->minAddress + 1) {
|
||||
debugger->backend->printf(debugger->backend, "%" PRIz "i: 0x%X\n", watchpoint->id, watchpoint->minAddress);
|
||||
} else {
|
||||
debugger->backend->printf(debugger->backend, "%" PRIz "i: 0x%X-0x%X\n", watchpoint->id, watchpoint->minAddress, watchpoint->maxAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
mWatchpointListDeinit(&watchpoints);
|
||||
|
|
|
@ -532,7 +532,7 @@ static void _processQReadCommand(struct GDBStub* stub, const char* message) {
|
|||
} else if (!strncmp("Xfer:memory-map:read::", message, 22)) {
|
||||
if (strlen(stub->memoryMapXml) == 0) {
|
||||
_generateMemoryMapXml(stub, stub->memoryMapXml);
|
||||
}
|
||||
}
|
||||
_processQXferCommand(stub, message + 22, stub->memoryMapXml);
|
||||
} else if (!strncmp("Supported:", message, 10)) {
|
||||
_processQSupportedCommand(stub, message + 10);
|
||||
|
@ -575,7 +575,8 @@ static void _setBreakpoint(struct GDBStub* stub, const char* message) {
|
|||
.type = BREAKPOINT_HARDWARE
|
||||
};
|
||||
struct mWatchpoint watchpoint = {
|
||||
.address = address
|
||||
.minAddress = address,
|
||||
.maxAddress = address + 1
|
||||
};
|
||||
|
||||
switch (message[0]) {
|
||||
|
@ -631,10 +632,11 @@ static void _clearBreakpoint(struct GDBStub* stub, const char* message) {
|
|||
mWatchpointListInit(&watchpoints, 0);
|
||||
stub->d.platform->listWatchpoints(stub->d.platform, &watchpoints);
|
||||
for (index = 0; index < mWatchpointListSize(&watchpoints); ++index) {
|
||||
if (mWatchpointListGetPointer(&watchpoints, index)->address != address) {
|
||||
struct mWatchpoint* watchpoint = mWatchpointListGetPointer(&watchpoints, index);
|
||||
if (address >= watchpoint->minAddress && address < watchpoint->maxAddress) {
|
||||
continue;
|
||||
}
|
||||
stub->d.platform->clearBreakpoint(stub->d.platform, mWatchpointListGetPointer(&watchpoints, index)->id);
|
||||
stub->d.platform->clearBreakpoint(stub->d.platform, watchpoint->id);
|
||||
}
|
||||
mWatchpointListDeinit(&watchpoints);
|
||||
break;
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
|
||||
DEFINE_VECTOR(LexVector, struct Token);
|
||||
|
||||
DEFINE_VECTOR(IntList, int32_t);
|
||||
|
||||
enum LexState {
|
||||
LEX_ERROR = -1,
|
||||
LEX_ROOT = 0,
|
||||
|
|
|
@ -19,6 +19,12 @@
|
|||
#include <mgba-util/png-io.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
#ifdef PSP2
|
||||
#include <psp2/io/stat.h>
|
||||
#elif defined(__3DS__)
|
||||
#include <mgba-util/platform/3ds/3ds-vfs.h>
|
||||
#endif
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
mLOG_DECLARE_CATEGORY(GUI_RUNNER);
|
||||
|
@ -117,34 +123,39 @@ static void _drawState(struct GUIBackground* background, void* id) {
|
|||
struct mGUIBackground* gbaBackground = (struct mGUIBackground*) background;
|
||||
unsigned stateId = ((uint32_t) id) >> 16;
|
||||
if (gbaBackground->p->drawScreenshot) {
|
||||
unsigned w, h;
|
||||
gbaBackground->p->core->desiredVideoDimensions(gbaBackground->p->core, &w, &h);
|
||||
size_t size = w * h * BYTES_PER_PIXEL;
|
||||
if (size != gbaBackground->imageSize) {
|
||||
mappedMemoryFree(gbaBackground->image, gbaBackground->imageSize);
|
||||
gbaBackground->image = NULL;
|
||||
}
|
||||
if (gbaBackground->image && gbaBackground->screenshotId == (stateId | SCREENSHOT_VALID)) {
|
||||
gbaBackground->p->drawScreenshot(gbaBackground->p, gbaBackground->image, w, h, true);
|
||||
color_t* pixels = gbaBackground->image;
|
||||
if (pixels && gbaBackground->screenshotId == (stateId | SCREENSHOT_VALID)) {
|
||||
gbaBackground->p->drawScreenshot(gbaBackground->p, pixels, gbaBackground->w, gbaBackground->h, true);
|
||||
return;
|
||||
} else if (gbaBackground->screenshotId != (stateId | SCREENSHOT_INVALID)) {
|
||||
struct VFile* vf = mCoreGetState(gbaBackground->p->core, stateId, false);
|
||||
color_t* pixels = gbaBackground->image;
|
||||
if (!pixels) {
|
||||
pixels = anonymousMemoryMap(size);
|
||||
gbaBackground->image = pixels;
|
||||
gbaBackground->imageSize = size;
|
||||
}
|
||||
bool success = false;
|
||||
if (vf && isPNG(vf) && pixels) {
|
||||
unsigned w, h;
|
||||
if (vf && isPNG(vf)) {
|
||||
png_structp png = PNGReadOpen(vf, PNG_HEADER_BYTES);
|
||||
png_infop info = png_create_info_struct(png);
|
||||
png_infop end = png_create_info_struct(png);
|
||||
if (png && info && end) {
|
||||
success = PNGReadHeader(png, info);
|
||||
success = success && PNGReadPixels(png, info, pixels, w, h, w);
|
||||
success = success && PNGReadFooter(png, end);
|
||||
success = png && info && end;
|
||||
success = success && PNGReadHeader(png, info);
|
||||
w = png_get_image_width(png, info);
|
||||
h = png_get_image_height(png, info);
|
||||
size_t size = w * h * BYTES_PER_PIXEL;
|
||||
success = success && (w < 0x4000) && (h < 0x4000);
|
||||
if (success) {
|
||||
if (size != gbaBackground->imageSize) {
|
||||
mappedMemoryFree(pixels, gbaBackground->imageSize);
|
||||
pixels = anonymousMemoryMap(size);
|
||||
gbaBackground->image = pixels;
|
||||
gbaBackground->imageSize = size;
|
||||
}
|
||||
success = pixels;
|
||||
}
|
||||
success = success && PNGReadPixels(png, info, pixels, w, h, w);
|
||||
if (success) {
|
||||
gbaBackground->w = w;
|
||||
gbaBackground->h = h;
|
||||
}
|
||||
success = success && PNGReadFooter(png, end);
|
||||
PNGReadClose(png, info, end);
|
||||
}
|
||||
if (vf) {
|
||||
|
@ -450,11 +461,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
|
|||
runner->setup(runner);
|
||||
}
|
||||
if (runner->config.port && runner->keySources) {
|
||||
mLOG(GUI_RUNNER, DEBUG, "Loading key sources for %s...", runner->config.port);
|
||||
size_t i;
|
||||
for (i = 0; runner->keySources[i].id; ++i) {
|
||||
mInputMapLoad(&runner->core->inputMap, runner->keySources[i].id, mCoreConfigGetInput(&runner->config));
|
||||
}
|
||||
mGUILoadInputMaps(runner);
|
||||
}
|
||||
mLOG(GUI_RUNNER, DEBUG, "Reseting...");
|
||||
runner->core->reset(runner->core);
|
||||
|
@ -745,11 +752,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
|
|||
|
||||
void mGUIRunloop(struct mGUIRunner* runner) {
|
||||
if (runner->keySources) {
|
||||
mLOG(GUI_RUNNER, DEBUG, "Loading key sources for %s...", runner->config.port);
|
||||
size_t i;
|
||||
for (i = 0; runner->keySources[i].id; ++i) {
|
||||
mInputMapLoad(&runner->params.keyMap, runner->keySources[i].id, mCoreConfigGetInput(&runner->config));
|
||||
}
|
||||
mGUILoadInputMaps(runner);
|
||||
}
|
||||
while (!runner->running || runner->running(runner)) {
|
||||
char path[PATH_MAX];
|
||||
|
@ -770,6 +773,52 @@ void mGUIRunloop(struct mGUIRunner* runner) {
|
|||
}
|
||||
}
|
||||
|
||||
void mGUILoadInputMaps(struct mGUIRunner* runner) {
|
||||
mLOG(GUI_RUNNER, DEBUG, "Loading key sources for %s...", runner->config.port);
|
||||
size_t i;
|
||||
for (i = 0; runner->keySources[i].id; ++i) {
|
||||
mInputMapLoad(&runner->params.keyMap, runner->keySources[i].id, mCoreConfigGetInput(&runner->config));
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__3DS__) || defined(PSP2)
|
||||
bool mGUIGetRom(struct mGUIRunner* runner, char* out, size_t outLength) {
|
||||
#ifdef PSP2
|
||||
int fd = open("app0:/filename", O_RDONLY);
|
||||
strcpy(out, "app0:/");
|
||||
#elif defined(__3DS__)
|
||||
int fd = open("romfs:/filename", O_RDONLY);
|
||||
strcpy(out, "romfs:/");
|
||||
#endif
|
||||
if (fd < 0) {
|
||||
return false;
|
||||
}
|
||||
size_t len = strlen(out);
|
||||
ssize_t size = read(fd, out + len, outLength - len);
|
||||
if (size > 0 && out[len + size - 1] == '\n') {
|
||||
out[len + size - 1] = '\0';
|
||||
}
|
||||
close(fd);
|
||||
if (size <= 0) {
|
||||
return false;
|
||||
}
|
||||
char basedir[64];
|
||||
mCoreConfigDirectory(basedir, sizeof(basedir));
|
||||
strlcat(basedir, "/forwarders", sizeof(basedir));
|
||||
#ifdef PSP2
|
||||
sceIoMkdir(basedir, 0777);
|
||||
#elif defined(__3DS__)
|
||||
FSUSER_CreateDirectory(sdmcArchive, fsMakePath(PATH_ASCII, basedir), 0);
|
||||
#endif
|
||||
|
||||
mCoreConfigSetValue(&runner->config, "savegamePath", basedir);
|
||||
mCoreConfigSetValue(&runner->config, "savestatePath", basedir);
|
||||
mCoreConfigSetValue(&runner->config, "screenshotPath", basedir);
|
||||
mCoreConfigSetValue(&runner->config, "cheatsPath", basedir);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_THREADING
|
||||
THREAD_ENTRY mGUIAutosaveThread(void* context) {
|
||||
struct mGUIAutosaveContext* autosave = context;
|
||||
|
|
|
@ -33,6 +33,8 @@ struct mGUIBackground {
|
|||
|
||||
color_t* image;
|
||||
size_t imageSize;
|
||||
uint16_t w;
|
||||
uint16_t h;
|
||||
|
||||
unsigned screenshotId;
|
||||
};
|
||||
|
@ -95,9 +97,14 @@ struct mGUIRunner {
|
|||
|
||||
void mGUIInit(struct mGUIRunner*, const char* port);
|
||||
void mGUIDeinit(struct mGUIRunner*);
|
||||
void mGUILoadInputMaps(struct mGUIRunner* runner);
|
||||
void mGUIRun(struct mGUIRunner*, const char* path);
|
||||
void mGUIRunloop(struct mGUIRunner*);
|
||||
|
||||
#if defined(__3DS__) || defined(PSP2)
|
||||
bool mGUIGetRom(struct mGUIRunner* runner, char* out, size_t outLength);
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_THREADING
|
||||
THREAD_ENTRY mGUIAutosaveThread(void* context);
|
||||
#endif
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#define W_OK 02
|
||||
#endif
|
||||
|
||||
FILE* logfile;
|
||||
|
||||
bool extractArchive(struct VDir* archive, const char* root, bool prefix) {
|
||||
char path[PATH_MAX] = {0};
|
||||
struct VDirEntry* vde;
|
||||
|
@ -53,7 +55,7 @@ bool extractArchive(struct VDir* archive, const char* root, bool prefix) {
|
|||
}
|
||||
switch (vde->type(vde)) {
|
||||
case VFS_DIRECTORY:
|
||||
printf("mkdir %s\n", fname);
|
||||
fprintf(logfile, "mkdir %s\n", fname);
|
||||
if (mkdir(path, 0755) < 0 && errno != EEXIST) {
|
||||
return false;
|
||||
}
|
||||
|
@ -70,7 +72,7 @@ bool extractArchive(struct VDir* archive, const char* root, bool prefix) {
|
|||
}
|
||||
break;
|
||||
case VFS_FILE:
|
||||
printf("extract %s\n", fname);
|
||||
fprintf(logfile, "extract %s\n", fname);
|
||||
vfIn = archive->openFile(archive, vde->name(vde), O_RDONLY);
|
||||
errno = 0;
|
||||
vfOut = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC);
|
||||
|
@ -111,13 +113,17 @@ int main(int argc, char* argv[]) {
|
|||
const char* root;
|
||||
int ok = 1;
|
||||
|
||||
mCoreConfigDirectory(bin, sizeof(bin));
|
||||
strncat(bin, "/updater.log", sizeof(bin));
|
||||
logfile = fopen(bin, "w");
|
||||
|
||||
mCoreConfigInit(&config, "updater");
|
||||
if (!mCoreConfigLoad(&config)) {
|
||||
puts("Failed to load config");
|
||||
fputs("Failed to load config\n", logfile);
|
||||
} else if (!mUpdateGetArchivePath(&config, updateArchive, sizeof(updateArchive)) || !(root = mUpdateGetRoot(&config))) {
|
||||
puts("No pending update found");
|
||||
fputs("No pending update found\n", logfile);
|
||||
} else if (access(root, W_OK)) {
|
||||
puts("Cannot write to update path");
|
||||
fputs("Cannot write to update path\n", logfile);
|
||||
} else {
|
||||
#ifdef __APPLE__
|
||||
char subdir[PATH_MAX];
|
||||
|
@ -160,7 +166,6 @@ int main(int argc, char* argv[]) {
|
|||
}
|
||||
off_t diff = devend - devinfo - 1;
|
||||
memcpy(devpath, &devinfo[1], diff);
|
||||
puts(devpath);
|
||||
break;
|
||||
}
|
||||
int retstat;
|
||||
|
@ -177,11 +182,11 @@ int main(int argc, char* argv[]) {
|
|||
archive = VDirOpenArchive(updateArchive);
|
||||
}
|
||||
if (archive) {
|
||||
puts("Extracting update");
|
||||
fputs("Extracting update\n", logfile);
|
||||
if (extractArchive(archive, root, prefix)) {
|
||||
ok = 0;
|
||||
} else {
|
||||
puts("An error occurred");
|
||||
fputs("An error occurred\n", logfile);
|
||||
}
|
||||
archive->close(archive);
|
||||
unlink(updateArchive);
|
||||
|
@ -218,10 +223,10 @@ int main(int argc, char* argv[]) {
|
|||
close(infd);
|
||||
}
|
||||
if (ok == 2) {
|
||||
puts("Cannot move update over old file");
|
||||
fputs("Cannot move update over old file\n", logfile);
|
||||
}
|
||||
} else {
|
||||
puts("Cannot move update over old file");
|
||||
fputs("Cannot move update over old file\n", logfile);
|
||||
}
|
||||
} else {
|
||||
ok = 0;
|
||||
|
@ -232,10 +237,10 @@ int main(int argc, char* argv[]) {
|
|||
}
|
||||
#endif
|
||||
else {
|
||||
puts("Cannot open update archive");
|
||||
fputs("Cannot open update archive\n", logfile);
|
||||
}
|
||||
if (ok == 0) {
|
||||
puts("Complete");
|
||||
fputs("Complete", logfile);
|
||||
const char* command = mUpdateGetCommand(&config);
|
||||
strlcpy(bin, command, sizeof(bin));
|
||||
mUpdateDeregister(&config);
|
||||
|
@ -260,6 +265,7 @@ int main(int argc, char* argv[]) {
|
|||
}
|
||||
}
|
||||
mCoreConfigDeinit(&config);
|
||||
fclose(logfile);
|
||||
if (ok == 0) {
|
||||
#ifdef _WIN32
|
||||
char qbin[PATH_MAX + 2] = {0};
|
||||
|
|
|
@ -78,14 +78,7 @@ static void _updateMatch(const char* key, const char* value, void* user) {
|
|||
return;
|
||||
}
|
||||
const char* item = &key[dotLoc + 1];
|
||||
|
||||
struct Table* out = user;
|
||||
struct mUpdate* update = HashTableLookup(out, match->channel);
|
||||
if (!update) {
|
||||
update = calloc(1, sizeof(*update));
|
||||
HashTableInsert(out, match->channel, update);
|
||||
}
|
||||
_updateUpdate(update, item, value);
|
||||
_updateUpdate(match->out, item, value);
|
||||
}
|
||||
|
||||
bool mUpdaterInit(struct mUpdaterContext* context, const char* manifest) {
|
||||
|
|
|
@ -7,6 +7,12 @@ set(SOURCE_FILES
|
|||
input.c
|
||||
io.c
|
||||
mbc.c
|
||||
mbc/huc-3.c
|
||||
mbc/licensed.c
|
||||
mbc/mbc.c
|
||||
mbc/pocket-cam.c
|
||||
mbc/tama5.c
|
||||
mbc/unlicensed.c
|
||||
memory.c
|
||||
overrides.c
|
||||
serialize.c
|
||||
|
|
|
@ -269,6 +269,29 @@ void GBAudioWriteNR31(struct GBAudio* audio, uint8_t value) {
|
|||
void GBAudioWriteNR32(struct GBAudio* audio, uint8_t value) {
|
||||
GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x4);
|
||||
audio->ch3.volume = GBAudioRegisterBankVolumeGetVolumeGB(value);
|
||||
|
||||
audio->ch3.sample = audio->ch3.wavedata8[audio->ch3.window >> 1];
|
||||
if (!(audio->ch3.window & 1)) {
|
||||
audio->ch3.sample >>= 4;
|
||||
}
|
||||
audio->ch3.sample &= 0xF;
|
||||
int volume;
|
||||
switch (audio->ch3.volume) {
|
||||
case 0:
|
||||
volume = 4;
|
||||
break;
|
||||
case 1:
|
||||
volume = 0;
|
||||
break;
|
||||
case 2:
|
||||
volume = 1;
|
||||
break;
|
||||
default:
|
||||
case 3:
|
||||
volume = 2;
|
||||
break;
|
||||
}
|
||||
audio->ch3.sample >>= volume;
|
||||
}
|
||||
|
||||
void GBAudioWriteNR33(struct GBAudio* audio, uint8_t value) {
|
||||
|
@ -377,13 +400,13 @@ void GBAudioWriteNR44(struct GBAudio* audio, uint8_t value) {
|
|||
}
|
||||
|
||||
void GBAudioWriteNR50(struct GBAudio* audio, uint8_t value) {
|
||||
GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x2);
|
||||
GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0xF);
|
||||
audio->volumeRight = GBRegisterNR50GetVolumeRight(value);
|
||||
audio->volumeLeft = GBRegisterNR50GetVolumeLeft(value);
|
||||
}
|
||||
|
||||
void GBAudioWriteNR51(struct GBAudio* audio, uint8_t value) {
|
||||
GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x2);
|
||||
GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0xF);
|
||||
audio->ch1Right = GBRegisterNR51GetCh1Right(value);
|
||||
audio->ch2Right = GBRegisterNR51GetCh2Right(value);
|
||||
audio->ch3Right = GBRegisterNR51GetCh3Right(value);
|
||||
|
@ -476,11 +499,11 @@ void GBAudioRun(struct GBAudio* audio, int32_t timestamp, int channels) {
|
|||
if (!audio->enable) {
|
||||
return;
|
||||
}
|
||||
if (audio->p && channels != 0xF && timestamp - audio->lastSample > (int) (SAMPLE_INTERVAL * audio->timingFactor)) {
|
||||
if (audio->p && channels != 0x1F && timestamp - audio->lastSample > (int) (SAMPLE_INTERVAL * audio->timingFactor)) {
|
||||
GBAudioSample(audio, timestamp);
|
||||
}
|
||||
|
||||
if (audio->playingCh1 && (channels & 0x1)) {
|
||||
if (audio->playingCh1 && (channels & 0x1) && audio->ch1.envelope.dead != 2) {
|
||||
int period = 4 * (2048 - audio->ch1.control.frequency) * audio->timingFactor;
|
||||
int32_t diff = timestamp - audio->ch1.lastUpdate;
|
||||
if (diff >= period) {
|
||||
|
@ -490,7 +513,7 @@ void GBAudioRun(struct GBAudio* audio, int32_t timestamp, int channels) {
|
|||
_updateSquareSample(&audio->ch1);
|
||||
}
|
||||
}
|
||||
if (audio->playingCh2 && (channels & 0x2)) {
|
||||
if (audio->playingCh2 && (channels & 0x2) && audio->ch2.envelope.dead != 2) {
|
||||
int period = 4 * (2048 - audio->ch2.control.frequency) * audio->timingFactor;
|
||||
int32_t diff = timestamp - audio->ch2.lastUpdate;
|
||||
if (diff >= period) {
|
||||
|
@ -756,7 +779,7 @@ void GBAudioSample(struct GBAudio* audio, int32_t timestamp) {
|
|||
for (sample = audio->sampleIndex; timestamp >= interval && sample < GB_MAX_SAMPLES; ++sample, timestamp -= interval) {
|
||||
int16_t sampleLeft = 0;
|
||||
int16_t sampleRight = 0;
|
||||
GBAudioRun(audio, sample * interval + audio->lastSample, 0xF);
|
||||
GBAudioRun(audio, sample * interval + audio->lastSample, 0x1F);
|
||||
GBAudioSamplePSG(audio, &sampleLeft, &sampleRight);
|
||||
sampleLeft = (sampleLeft * audio->masterVolume * 6) >> 7;
|
||||
sampleRight = (sampleRight * audio->masterVolume * 6) >> 7;
|
||||
|
@ -863,7 +886,7 @@ bool _writeEnvelope(struct GBAudioEnvelope* envelope, uint8_t value, enum GBAudi
|
|||
envelope->currentVolume &= 0xF;
|
||||
}
|
||||
_updateEnvelopeDead(envelope);
|
||||
return (envelope->initialVolume || envelope->direction) && envelope->dead != 2;
|
||||
return envelope->initialVolume || envelope->direction;
|
||||
}
|
||||
|
||||
static void _updateSquareSample(struct GBAudioSquareChannel* ch) {
|
||||
|
|
21
src/gb/gb.c
|
@ -596,13 +596,13 @@ void GBReset(struct SM83Core* cpu) {
|
|||
GBVideoReset(&gb->video);
|
||||
GBTimerReset(&gb->timer);
|
||||
GBIOReset(gb);
|
||||
GBAudioReset(&gb->audio);
|
||||
if (!gb->biosVf && gb->memory.rom) {
|
||||
GBSkipBIOS(gb);
|
||||
} else {
|
||||
mTimingSchedule(&gb->timing, &gb->timer.event, 0);
|
||||
}
|
||||
|
||||
GBAudioReset(&gb->audio);
|
||||
GBSIOReset(&gb->sio);
|
||||
|
||||
cpu->memory.setActiveRegion(cpu, cpu->pc);
|
||||
|
@ -744,6 +744,25 @@ void GBSkipBIOS(struct GB* gb) {
|
|||
GBUnmapBIOS(gb);
|
||||
}
|
||||
|
||||
GBIOWrite(gb, GB_REG_NR52, 0xF1);
|
||||
GBIOWrite(gb, GB_REG_NR14, 0x3F);
|
||||
GBIOWrite(gb, GB_REG_NR10, 0x80);
|
||||
GBIOWrite(gb, GB_REG_NR11, 0xBF);
|
||||
GBIOWrite(gb, GB_REG_NR12, 0xF3);
|
||||
GBIOWrite(gb, GB_REG_NR13, 0xF3);
|
||||
GBIOWrite(gb, GB_REG_NR24, 0x3F);
|
||||
GBIOWrite(gb, GB_REG_NR21, 0x3F);
|
||||
GBIOWrite(gb, GB_REG_NR22, 0x00);
|
||||
GBIOWrite(gb, GB_REG_NR34, 0x3F);
|
||||
GBIOWrite(gb, GB_REG_NR30, 0x7F);
|
||||
GBIOWrite(gb, GB_REG_NR31, 0xFF);
|
||||
GBIOWrite(gb, GB_REG_NR32, 0x9F);
|
||||
GBIOWrite(gb, GB_REG_NR44, 0x3F);
|
||||
GBIOWrite(gb, GB_REG_NR41, 0xFF);
|
||||
GBIOWrite(gb, GB_REG_NR42, 0x00);
|
||||
GBIOWrite(gb, GB_REG_NR43, 0x00);
|
||||
GBIOWrite(gb, GB_REG_NR50, 0x77);
|
||||
GBIOWrite(gb, GB_REG_NR51, 0xF3);
|
||||
GBIOWrite(gb, GB_REG_LCDC, 0x91);
|
||||
gb->memory.io[GB_REG_BANK] = 0x1;
|
||||
GBVideoSkipBIOS(&gb->video);
|
||||
|
|
34
src/gb/io.c
|
@ -162,36 +162,7 @@ void GBIOReset(struct GB* gb) {
|
|||
GBIOWrite(gb, GB_REG_TMA, 0);
|
||||
GBIOWrite(gb, GB_REG_TAC, 0);
|
||||
GBIOWrite(gb, GB_REG_IF, 1);
|
||||
gb->audio.playingCh1 = false;
|
||||
gb->audio.playingCh2 = false;
|
||||
gb->audio.playingCh3 = false;
|
||||
gb->audio.playingCh4 = false;
|
||||
GBIOWrite(gb, GB_REG_NR52, 0xF1);
|
||||
GBIOWrite(gb, GB_REG_NR14, 0x3F);
|
||||
GBIOWrite(gb, GB_REG_NR10, 0x80);
|
||||
GBIOWrite(gb, GB_REG_NR11, 0xBF);
|
||||
GBIOWrite(gb, GB_REG_NR12, 0xF3);
|
||||
GBIOWrite(gb, GB_REG_NR13, 0xF3);
|
||||
GBIOWrite(gb, GB_REG_NR24, 0x3F);
|
||||
GBIOWrite(gb, GB_REG_NR21, 0x3F);
|
||||
GBIOWrite(gb, GB_REG_NR22, 0x00);
|
||||
GBIOWrite(gb, GB_REG_NR34, 0x3F);
|
||||
GBIOWrite(gb, GB_REG_NR30, 0x7F);
|
||||
GBIOWrite(gb, GB_REG_NR31, 0xFF);
|
||||
GBIOWrite(gb, GB_REG_NR32, 0x9F);
|
||||
GBIOWrite(gb, GB_REG_NR44, 0x3F);
|
||||
GBIOWrite(gb, GB_REG_NR41, 0xFF);
|
||||
GBIOWrite(gb, GB_REG_NR42, 0x00);
|
||||
GBIOWrite(gb, GB_REG_NR43, 0x00);
|
||||
GBIOWrite(gb, GB_REG_NR50, 0x77);
|
||||
GBIOWrite(gb, GB_REG_NR51, 0xF3);
|
||||
if (!gb->biosVf) {
|
||||
GBIOWrite(gb, GB_REG_LCDC, 0x91);
|
||||
gb->memory.io[GB_REG_BANK] = 1;
|
||||
} else {
|
||||
GBIOWrite(gb, GB_REG_LCDC, 0x00);
|
||||
gb->memory.io[GB_REG_BANK] = 0xFF;
|
||||
}
|
||||
GBIOWrite(gb, GB_REG_LCDC, 0x00);
|
||||
GBIOWrite(gb, GB_REG_SCY, 0x00);
|
||||
GBIOWrite(gb, GB_REG_SCX, 0x00);
|
||||
GBIOWrite(gb, GB_REG_LYC, 0x00);
|
||||
|
@ -203,6 +174,7 @@ void GBIOReset(struct GB* gb) {
|
|||
}
|
||||
GBIOWrite(gb, GB_REG_WY, 0x00);
|
||||
GBIOWrite(gb, GB_REG_WX, 0x00);
|
||||
gb->memory.io[GB_REG_BANK] = 0xFF;
|
||||
if (gb->model & GB_MODEL_CGB) {
|
||||
GBIOWrite(gb, GB_REG_KEY0, 0);
|
||||
GBIOWrite(gb, GB_REG_JOYP, 0xFF);
|
||||
|
@ -754,7 +726,7 @@ void GBIODeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
gb->video.renderer->writeVideoRegister(gb->video.renderer, GB_REG_SCX, state->io[GB_REG_SCX]);
|
||||
gb->video.renderer->writeVideoRegister(gb->video.renderer, GB_REG_WY, state->io[GB_REG_WY]);
|
||||
gb->video.renderer->writeVideoRegister(gb->video.renderer, GB_REG_WX, state->io[GB_REG_WX]);
|
||||
if (gb->model & GB_MODEL_SGB) {
|
||||
if (gb->model == GB_MODEL_SGB) {
|
||||
gb->video.renderer->writeVideoRegister(gb->video.renderer, GB_REG_BGP, state->io[GB_REG_BGP]);
|
||||
gb->video.renderer->writeVideoRegister(gb->video.renderer, GB_REG_OBP0, state->io[GB_REG_OBP0]);
|
||||
gb->video.renderer->writeVideoRegister(gb->video.renderer, GB_REG_OBP1, state->io[GB_REG_OBP1]);
|
||||
|
|
1803
src/gb/mbc.c
|
@ -0,0 +1,218 @@
|
|||
/* Copyright (c) 2013-2016 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 "gb/mbc/mbc-private.h"
|
||||
|
||||
#include <mgba/internal/gb/gb.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
static void _latchHuC3Rtc(struct mRTCSource* rtc, uint8_t* huc3Regs, time_t* rtcLastLatch) {
|
||||
time_t t;
|
||||
if (rtc) {
|
||||
if (rtc->sample) {
|
||||
rtc->sample(rtc);
|
||||
}
|
||||
t = rtc->unixTime(rtc);
|
||||
} else {
|
||||
t = time(0);
|
||||
}
|
||||
t -= *rtcLastLatch;
|
||||
t /= 60;
|
||||
|
||||
if (!t) {
|
||||
return;
|
||||
}
|
||||
*rtcLastLatch += t * 60;
|
||||
|
||||
int minutes = huc3Regs[GBHUC3_RTC_MINUTES_HI] << 8;
|
||||
minutes |= huc3Regs[GBHUC3_RTC_MINUTES_MI] << 4;
|
||||
minutes |= huc3Regs[GBHUC3_RTC_MINUTES_LO];
|
||||
minutes += t % 1440;
|
||||
t /= 1440;
|
||||
if (minutes >= 1440) {
|
||||
minutes -= 1440;
|
||||
++t;
|
||||
} else if (minutes < 0) {
|
||||
minutes += 1440;
|
||||
--t;
|
||||
}
|
||||
huc3Regs[GBHUC3_RTC_MINUTES_LO] = minutes & 0xF;
|
||||
huc3Regs[GBHUC3_RTC_MINUTES_MI] = (minutes >> 4) & 0xF;
|
||||
huc3Regs[GBHUC3_RTC_MINUTES_HI] = (minutes >> 8) & 0xF;
|
||||
|
||||
int days = huc3Regs[GBHUC3_RTC_DAYS_LO];
|
||||
days |= huc3Regs[GBHUC3_RTC_DAYS_MI] << 4;
|
||||
days |= huc3Regs[GBHUC3_RTC_DAYS_HI] << 8;
|
||||
|
||||
days += t;
|
||||
|
||||
huc3Regs[GBHUC3_RTC_DAYS_LO] = days & 0xF;
|
||||
huc3Regs[GBHUC3_RTC_DAYS_MI] = (days >> 4) & 0xF;
|
||||
huc3Regs[GBHUC3_RTC_DAYS_HI] = (days >> 8) & 0xF;
|
||||
}
|
||||
|
||||
static void _huc3Commit(struct GB* gb, struct GBHuC3State* state) {
|
||||
size_t c;
|
||||
switch (state->value & 0x70) {
|
||||
case 0x10:
|
||||
if ((state->index & 0xF8) == 0x10) {
|
||||
_latchHuC3Rtc(gb->memory.rtc, state->registers, &gb->memory.rtcLastLatch);
|
||||
}
|
||||
state->value &= 0xF0;
|
||||
state->value |= state->registers[state->index] & 0xF;
|
||||
mLOG(GB_MBC, DEBUG, "HuC-3 read: %02X:%X", state->index, state->value & 0xF);
|
||||
if (state->value & 0x10) {
|
||||
++state->index;
|
||||
}
|
||||
break;
|
||||
case 0x30:
|
||||
mLOG(GB_MBC, DEBUG, "HuC-3 write: %02X:%X", state->index, state->value & 0xF);
|
||||
state->registers[state->index] = state->value & 0xF;
|
||||
if (state->value & 0x10) {
|
||||
++state->index;
|
||||
}
|
||||
break;
|
||||
case 0x40:
|
||||
state->index &= 0xF0;
|
||||
state->index |= (state->value) & 0xF;
|
||||
mLOG(GB_MBC, DEBUG, "HuC-3 index (low): %02X", state->index);
|
||||
break;
|
||||
case 0x50:
|
||||
state->index &= 0x0F;
|
||||
state->index |= ((state->value) & 0xF) << 4;
|
||||
mLOG(GB_MBC, DEBUG, "HuC-3 index (high): %02X", state->index);
|
||||
break;
|
||||
case 0x60:
|
||||
switch (state->value & 0xF) {
|
||||
case GBHUC3_CMD_LATCH:
|
||||
_latchHuC3Rtc(gb->memory.rtc, state->registers, &gb->memory.rtcLastLatch);
|
||||
memcpy(state->registers, &state->registers[GBHUC3_RTC_MINUTES_LO], 6);
|
||||
mLOG(GB_MBC, DEBUG, "HuC-3 RTC latch");
|
||||
break;
|
||||
case GBHUC3_CMD_SET_RTC:
|
||||
memcpy(&state->registers[GBHUC3_RTC_MINUTES_LO], state->registers, 6);
|
||||
mLOG(GB_MBC, DEBUG, "HuC-3 set RTC");
|
||||
break;
|
||||
case GBHUC3_CMD_RO:
|
||||
mLOG(GB_MBC, STUB, "HuC-3 unimplemented read-only mode");
|
||||
break;
|
||||
case GBHUC3_CMD_TONE:
|
||||
if (state->registers[GBHUC3_SPEAKER_ENABLE] == 1) {
|
||||
for (c = 0; c < mCoreCallbacksListSize(&gb->coreCallbacks); ++c) {
|
||||
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gb->coreCallbacks, c);
|
||||
if (callbacks->alarm) {
|
||||
callbacks->alarm(callbacks->context);
|
||||
}
|
||||
}
|
||||
mLOG(GB_MBC, DEBUG, "HuC-3 tone %i", state->registers[GBHUC3_SPEAKER_TONE] & 3);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
mLOG(GB_MBC, STUB, "HuC-3 unknown command: %X", state->value & 0xF);
|
||||
break;
|
||||
}
|
||||
state->value = 0xE1;
|
||||
break;
|
||||
default:
|
||||
mLOG(GB_MBC, STUB, "HuC-3 unknown mode commit: %02X:%02X", state->index, state->value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
struct GBHuC3State* state = &memory->mbcState.huc3;
|
||||
int bank = value & 0x7F;
|
||||
if (address & 0x1FFF) {
|
||||
mLOG(GB_MBC, STUB, "HuC-3 unknown value %04X:%02X", address, value);
|
||||
}
|
||||
|
||||
switch (address >> 13) {
|
||||
case 0x0:
|
||||
switch (value) {
|
||||
case 0xA:
|
||||
memory->sramAccess = true;
|
||||
GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
|
||||
break;
|
||||
default:
|
||||
memory->sramAccess = false;
|
||||
break;
|
||||
}
|
||||
state->mode = value;
|
||||
break;
|
||||
case 0x1:
|
||||
GBMBCSwitchBank(gb, bank);
|
||||
break;
|
||||
case 0x2:
|
||||
GBMBCSwitchSramBank(gb, bank);
|
||||
break;
|
||||
case 0x5:
|
||||
switch (state->mode) {
|
||||
case GBHUC3_MODE_IN:
|
||||
state->value = 0x80 | value;
|
||||
break;
|
||||
case GBHUC3_MODE_COMMIT:
|
||||
_huc3Commit(gb, state);
|
||||
break;
|
||||
default:
|
||||
mLOG(GB_MBC, STUB, "HuC-3 unknown mode write: %02X:%02X", state->mode, value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// TODO
|
||||
mLOG(GB_MBC, STUB, "HuC-3 unknown address: %04X:%02X", address, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t _GBHuC3Read(struct GBMemory* memory, uint16_t address) {
|
||||
struct GBHuC3State* state = &memory->mbcState.huc3;
|
||||
switch (state->mode) {
|
||||
case GBHUC3_MODE_SRAM_RO:
|
||||
case GBHUC3_MODE_SRAM_RW:
|
||||
return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
|
||||
case GBHUC3_MODE_IN:
|
||||
case GBHUC3_MODE_OUT:
|
||||
return 0x80 | state->value;
|
||||
default:
|
||||
return 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
void GBMBCHuC3Read(struct GB* gb) {
|
||||
struct GBMBCHuC3SaveBuffer buffer;
|
||||
struct VFile* vf = gb->sramVf;
|
||||
if (!vf) {
|
||||
return;
|
||||
}
|
||||
vf->seek(vf, gb->sramSize, SEEK_SET);
|
||||
if (vf->read(vf, &buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < 0x80; ++i) {
|
||||
gb->memory.mbcState.huc3.registers[i * 2] = buffer.regs[i] & 0xF;
|
||||
gb->memory.mbcState.huc3.registers[i * 2 + 1] = buffer.regs[i] >> 4;
|
||||
}
|
||||
LOAD_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix);
|
||||
}
|
||||
|
||||
void GBMBCHuC3Write(struct GB* gb) {
|
||||
struct VFile* vf = gb->sramVf;
|
||||
if (!vf) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct GBMBCHuC3SaveBuffer buffer;
|
||||
size_t i;
|
||||
for (i = 0; i < 0x80; ++i) {
|
||||
buffer.regs[i] = gb->memory.mbcState.huc3.registers[i * 2] & 0xF;
|
||||
buffer.regs[i] |= gb->memory.mbcState.huc3.registers[i * 2 + 1] << 4;
|
||||
}
|
||||
STORE_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix);
|
||||
|
||||
_GBMBCAppendSaveSuffix(gb, &buffer, sizeof(buffer));
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/* Copyright (c) 2013-2016 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 "gb/mbc/mbc-private.h"
|
||||
|
||||
#include <mgba/internal/gb/gb.h>
|
||||
|
||||
void _GBMMM01(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
if (!memory->mbcState.mmm01.locked) {
|
||||
switch (address >> 13) {
|
||||
case 0x0:
|
||||
memory->mbcState.mmm01.locked = true;
|
||||
GBMBCSwitchBank0(gb, memory->mbcState.mmm01.currentBank0);
|
||||
break;
|
||||
case 0x1:
|
||||
memory->mbcState.mmm01.currentBank0 &= ~0x7F;
|
||||
memory->mbcState.mmm01.currentBank0 |= value & 0x7F;
|
||||
break;
|
||||
case 0x2:
|
||||
memory->mbcState.mmm01.currentBank0 &= ~0x180;
|
||||
memory->mbcState.mmm01.currentBank0 |= (value & 0x30) << 3;
|
||||
break;
|
||||
default:
|
||||
// TODO
|
||||
mLOG(GB_MBC, STUB, "MMM01 unknown address: %04X:%02X", address, value);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch (address >> 13) {
|
||||
case 0x0:
|
||||
switch (value) {
|
||||
case 0xA:
|
||||
memory->sramAccess = true;
|
||||
GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
|
||||
break;
|
||||
default:
|
||||
memory->sramAccess = false;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x1:
|
||||
GBMBCSwitchBank(gb, value + memory->mbcState.mmm01.currentBank0);
|
||||
break;
|
||||
case 0x2:
|
||||
GBMBCSwitchSramBank(gb, value);
|
||||
break;
|
||||
default:
|
||||
// TODO
|
||||
mLOG(GB_MBC, STUB, "MMM01 unknown address: %04X:%02X", address, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _GBHuC1(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
int bank = value & 0x3F;
|
||||
switch (address >> 13) {
|
||||
case 0x0:
|
||||
switch (value) {
|
||||
case 0xE:
|
||||
memory->sramAccess = false;
|
||||
break;
|
||||
default:
|
||||
memory->sramAccess = true;
|
||||
GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x1:
|
||||
GBMBCSwitchBank(gb, bank);
|
||||
break;
|
||||
case 0x2:
|
||||
GBMBCSwitchSramBank(gb, value);
|
||||
break;
|
||||
default:
|
||||
// TODO
|
||||
mLOG(GB_MBC, STUB, "HuC-1 unknown address: %04X:%02X", address, value);
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/* Copyright (c) 2013-2016 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 GB_MBC_PRIVATE_H
|
||||
#define GB_MBC_PRIVATE_H
|
||||
|
||||
#include <mgba/internal/gb/mbc.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
struct GB;
|
||||
struct GBMemory;
|
||||
struct mRTCSource;
|
||||
|
||||
void _GBMBC1(struct GB*, uint16_t address, uint8_t value);
|
||||
void _GBMBC2(struct GB*, uint16_t address, uint8_t value);
|
||||
void _GBMBC3(struct GB*, uint16_t address, uint8_t value);
|
||||
void _GBMBC5(struct GB*, uint16_t address, uint8_t value);
|
||||
void _GBMBC6(struct GB*, uint16_t address, uint8_t value);
|
||||
void _GBMBC7(struct GB*, uint16_t address, uint8_t value);
|
||||
|
||||
void _GBMMM01(struct GB*, uint16_t address, uint8_t value);
|
||||
void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value);
|
||||
void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value);
|
||||
|
||||
void _GBHuC1(struct GB*, uint16_t address, uint8_t value);
|
||||
void _GBHuC3(struct GB*, uint16_t address, uint8_t value);
|
||||
|
||||
void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value);
|
||||
void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value);
|
||||
void _GBNTOld1(struct GB* gb, uint16_t address, uint8_t value);
|
||||
void _GBNTOld2(struct GB* gb, uint16_t address, uint8_t value);
|
||||
void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value);
|
||||
void _GBBBD(struct GB* gb, uint16_t address, uint8_t value);
|
||||
void _GBHitek(struct GB* gb, uint16_t address, uint8_t value);
|
||||
void _GBLiCheng(struct GB* gb, uint16_t address, uint8_t value);
|
||||
void _GBGGB81(struct GB* gb, uint16_t address, uint8_t value);
|
||||
void _GBSachen(struct GB* gb, uint16_t address, uint8_t value);
|
||||
|
||||
uint8_t _GBMBC2Read(struct GBMemory*, uint16_t address);
|
||||
uint8_t _GBMBC6Read(struct GBMemory*, uint16_t address);
|
||||
uint8_t _GBMBC7Read(struct GBMemory*, uint16_t address);
|
||||
void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value);
|
||||
|
||||
uint8_t _GBPocketCamRead(struct GBMemory*, uint16_t address);
|
||||
uint8_t _GBTAMA5Read(struct GBMemory*, uint16_t address);
|
||||
uint8_t _GBHuC3Read(struct GBMemory*, uint16_t address);
|
||||
|
||||
uint8_t _GBPKJDRead(struct GBMemory*, uint16_t address);
|
||||
uint8_t _GBBBDRead(struct GBMemory*, uint16_t address);
|
||||
uint8_t _GBHitekRead(struct GBMemory*, uint16_t address);
|
||||
uint8_t _GBGGB81Read(struct GBMemory*, uint16_t address);
|
||||
uint8_t _GBSachenMMC1Read(struct GBMemory*, uint16_t address);
|
||||
uint8_t _GBSachenMMC2Read(struct GBMemory*, uint16_t address);
|
||||
|
||||
void _GBMBCLatchRTC(struct mRTCSource* rtc, uint8_t* rtcRegs, time_t* rtcLastLatch);
|
||||
void _GBMBCAppendSaveSuffix(struct GB* gb, const void* buffer, size_t size);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -0,0 +1,565 @@
|
|||
/* Copyright (c) 2013-2016 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 "gb/mbc/mbc-private.h"
|
||||
|
||||
#include <mgba/core/interface.h>
|
||||
#include <mgba/internal/defines.h>
|
||||
#include <mgba/internal/gb/gb.h>
|
||||
|
||||
static void _GBMBC6MapChip(struct GB*, int half, uint8_t value);
|
||||
|
||||
void _GBMBCLatchRTC(struct mRTCSource* rtc, uint8_t* rtcRegs, time_t* rtcLastLatch) {
|
||||
time_t t;
|
||||
if (rtc) {
|
||||
if (rtc->sample) {
|
||||
rtc->sample(rtc);
|
||||
}
|
||||
t = rtc->unixTime(rtc);
|
||||
} else {
|
||||
t = time(0);
|
||||
}
|
||||
time_t currentLatch = t;
|
||||
t -= *rtcLastLatch;
|
||||
*rtcLastLatch = currentLatch;
|
||||
|
||||
int64_t diff;
|
||||
diff = rtcRegs[0] + t % 60;
|
||||
if (diff < 0) {
|
||||
diff += 60;
|
||||
t -= 60;
|
||||
}
|
||||
rtcRegs[0] = diff % 60;
|
||||
t /= 60;
|
||||
t += diff / 60;
|
||||
|
||||
diff = rtcRegs[1] + t % 60;
|
||||
if (diff < 0) {
|
||||
diff += 60;
|
||||
t -= 60;
|
||||
}
|
||||
rtcRegs[1] = diff % 60;
|
||||
t /= 60;
|
||||
t += diff / 60;
|
||||
|
||||
diff = rtcRegs[2] + t % 24;
|
||||
if (diff < 0) {
|
||||
diff += 24;
|
||||
t -= 24;
|
||||
}
|
||||
rtcRegs[2] = diff % 24;
|
||||
t /= 24;
|
||||
t += diff / 24;
|
||||
|
||||
diff = rtcRegs[3] + ((rtcRegs[4] & 1) << 8) + (t & 0x1FF);
|
||||
rtcRegs[3] = diff;
|
||||
rtcRegs[4] &= 0xFE;
|
||||
rtcRegs[4] |= (diff >> 8) & 1;
|
||||
if (diff & 0x200) {
|
||||
rtcRegs[4] |= 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
static void _GBMBC1Update(struct GB* gb) {
|
||||
struct GBMBC1State* state = &gb->memory.mbcState.mbc1;
|
||||
int bank = state->bankLo;
|
||||
bank &= (1 << state->multicartStride) - 1;
|
||||
bank |= state->bankHi << state->multicartStride;
|
||||
if (state->mode) {
|
||||
GBMBCSwitchBank0(gb, state->bankHi << state->multicartStride);
|
||||
GBMBCSwitchSramBank(gb, state->bankHi & 3);
|
||||
} else {
|
||||
GBMBCSwitchBank0(gb, 0);
|
||||
GBMBCSwitchSramBank(gb, 0);
|
||||
}
|
||||
if (!(state->bankLo & 0x1F)) {
|
||||
++state->bankLo;
|
||||
++bank;
|
||||
}
|
||||
GBMBCSwitchBank(gb, bank);
|
||||
}
|
||||
|
||||
void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
int bank = value & 0x1F;
|
||||
switch (address >> 13) {
|
||||
case 0x0:
|
||||
switch (value & 0xF) {
|
||||
case 0:
|
||||
memory->sramAccess = false;
|
||||
break;
|
||||
case 0xA:
|
||||
memory->sramAccess = true;
|
||||
GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
|
||||
break;
|
||||
default:
|
||||
// TODO
|
||||
mLOG(GB_MBC, STUB, "MBC1 unknown value %02X", value);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x1:
|
||||
memory->mbcState.mbc1.bankLo = bank;
|
||||
_GBMBC1Update(gb);
|
||||
break;
|
||||
case 0x2:
|
||||
bank &= 3;
|
||||
memory->mbcState.mbc1.bankHi = bank;
|
||||
_GBMBC1Update(gb);
|
||||
break;
|
||||
case 0x3:
|
||||
memory->mbcState.mbc1.mode = value & 1;
|
||||
_GBMBC1Update(gb);
|
||||
break;
|
||||
default:
|
||||
// TODO
|
||||
mLOG(GB_MBC, STUB, "MBC1 unknown address: %04X:%02X", address, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _GBMBC2(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
int shift = (address & 1) * 4;
|
||||
int bank = value & 0xF;
|
||||
switch ((address & 0xC100) >> 8) {
|
||||
case 0x0:
|
||||
switch (value & 0x0F) {
|
||||
case 0:
|
||||
memory->sramAccess = false;
|
||||
break;
|
||||
case 0xA:
|
||||
memory->sramAccess = true;
|
||||
break;
|
||||
default:
|
||||
// TODO
|
||||
mLOG(GB_MBC, STUB, "MBC2 unknown value %02X", value);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x1:
|
||||
if (!bank) {
|
||||
++bank;
|
||||
}
|
||||
GBMBCSwitchBank(gb, bank);
|
||||
break;
|
||||
case 0x80:
|
||||
case 0x81:
|
||||
case 0x82:
|
||||
case 0x83:
|
||||
if (!memory->sramAccess) {
|
||||
return;
|
||||
}
|
||||
address &= 0x1FF;
|
||||
memory->sramBank[(address >> 1)] &= 0xF0 >> shift;
|
||||
memory->sramBank[(address >> 1)] |= (value & 0xF) << shift;
|
||||
gb->sramDirty |= mSAVEDATA_DIRT_NEW;
|
||||
break;
|
||||
default:
|
||||
// TODO
|
||||
mLOG(GB_MBC, STUB, "MBC2 unknown address: %04X:%02X", address, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t _GBMBC2Read(struct GBMemory* memory, uint16_t address) {
|
||||
if (!memory->sramAccess) {
|
||||
return 0xFF;
|
||||
}
|
||||
address &= 0x1FF;
|
||||
int shift = (address & 1) * 4;
|
||||
return (memory->sramBank[(address >> 1)] >> shift) | 0xF0;
|
||||
}
|
||||
|
||||
void _GBMBC3(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
int bank = value;
|
||||
switch (address >> 13) {
|
||||
case 0x0:
|
||||
switch (value & 0xF) {
|
||||
case 0:
|
||||
memory->sramAccess = false;
|
||||
break;
|
||||
case 0xA:
|
||||
memory->sramAccess = true;
|
||||
GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
|
||||
break;
|
||||
default:
|
||||
// TODO
|
||||
mLOG(GB_MBC, STUB, "MBC3 unknown value %02X", value);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x1:
|
||||
if (gb->memory.romSize < GB_SIZE_CART_BANK0 * 0x80) {
|
||||
bank &= 0x7F;
|
||||
}
|
||||
if (!bank) {
|
||||
++bank;
|
||||
}
|
||||
GBMBCSwitchBank(gb, bank);
|
||||
break;
|
||||
case 0x2:
|
||||
bank &= 0xF;
|
||||
if (bank < 8) {
|
||||
GBMBCSwitchSramBank(gb, value);
|
||||
memory->rtcAccess = false;
|
||||
} else if (bank <= 0xC) {
|
||||
memory->activeRtcReg = bank - 8;
|
||||
memory->rtcAccess = true;
|
||||
}
|
||||
break;
|
||||
case 0x3:
|
||||
if (memory->rtcLatched && value == 0) {
|
||||
memory->rtcLatched = false;
|
||||
} else if (!memory->rtcLatched && value == 1) {
|
||||
_GBMBCLatchRTC(gb->memory.rtc, gb->memory.rtcRegs, &gb->memory.rtcLastLatch);
|
||||
memory->rtcLatched = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _GBMBC5(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
int bank;
|
||||
switch (address >> 12) {
|
||||
case 0x0:
|
||||
case 0x1:
|
||||
switch (value) {
|
||||
case 0:
|
||||
memory->sramAccess = false;
|
||||
break;
|
||||
case 0xA:
|
||||
memory->sramAccess = true;
|
||||
GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
|
||||
break;
|
||||
default:
|
||||
// TODO
|
||||
mLOG(GB_MBC, STUB, "MBC5 unknown value %02X", value);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x2:
|
||||
bank = (memory->currentBank & 0x100) | value;
|
||||
GBMBCSwitchBank(gb, bank);
|
||||
break;
|
||||
case 0x3:
|
||||
bank = (memory->currentBank & 0xFF) | ((value & 1) << 8);
|
||||
GBMBCSwitchBank(gb, bank);
|
||||
break;
|
||||
case 0x4:
|
||||
case 0x5:
|
||||
if (memory->mbcType == GB_MBC5_RUMBLE && memory->rumble) {
|
||||
memory->rumble->setRumble(memory->rumble, (value >> 3) & 1);
|
||||
value &= ~8;
|
||||
}
|
||||
GBMBCSwitchSramBank(gb, value & 0xF);
|
||||
break;
|
||||
default:
|
||||
// TODO
|
||||
mLOG(GB_MBC, STUB, "MBC5 unknown address: %04X:%02X", address, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
int bank = value;
|
||||
switch (address >> 10) {
|
||||
case 0:
|
||||
switch (value) {
|
||||
case 0:
|
||||
memory->sramAccess = false;
|
||||
break;
|
||||
case 0xA:
|
||||
memory->sramAccess = true;
|
||||
break;
|
||||
default:
|
||||
// TODO
|
||||
mLOG(GB_MBC, STUB, "MBC6 unknown value %02X", value);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x1:
|
||||
GBMBCSwitchSramHalfBank(gb, 0, bank);
|
||||
break;
|
||||
case 0x2:
|
||||
GBMBCSwitchSramHalfBank(gb, 1, bank);
|
||||
break;
|
||||
case 0x3:
|
||||
mLOG(GB_MBC, STUB, "MBC6 unimplemented flash OE write: %04X:%02X", address, value);
|
||||
break;
|
||||
case 0x4:
|
||||
mLOG(GB_MBC, STUB, "MBC6 unimplemented flash WE write: %04X:%02X", address, value);
|
||||
break;
|
||||
case 0x8:
|
||||
case 0x9:
|
||||
GBMBCSwitchHalfBank(gb, 0, bank);
|
||||
break;
|
||||
case 0xA:
|
||||
case 0xB:
|
||||
_GBMBC6MapChip(gb, 0, value);
|
||||
break;
|
||||
case 0xC:
|
||||
case 0xD:
|
||||
GBMBCSwitchHalfBank(gb, 1, bank);
|
||||
break;
|
||||
case 0xE:
|
||||
case 0xF:
|
||||
_GBMBC6MapChip(gb, 1, value);
|
||||
break;
|
||||
case 0x28:
|
||||
case 0x29:
|
||||
case 0x2A:
|
||||
case 0x2B:
|
||||
if (memory->sramAccess) {
|
||||
memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value;
|
||||
gb->sramDirty |= mSAVEDATA_DIRT_NEW;
|
||||
}
|
||||
break;
|
||||
case 0x2C:
|
||||
case 0x2D:
|
||||
case 0x2E:
|
||||
case 0x2F:
|
||||
if (memory->sramAccess) {
|
||||
memory->sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
mLOG(GB_MBC, STUB, "MBC6 unknown address: %04X:%02X", address, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t _GBMBC6Read(struct GBMemory* memory, uint16_t address) {
|
||||
if (!memory->sramAccess) {
|
||||
return 0xFF;
|
||||
}
|
||||
switch (address >> 12) {
|
||||
case 0xA:
|
||||
return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)];
|
||||
case 0xB:
|
||||
return memory->sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)];
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
static void _GBMBC6MapChip(struct GB* gb, int half, uint8_t value) {
|
||||
if (!half) {
|
||||
gb->memory.mbcState.mbc6.flashBank0 = !!(value & 0x08);
|
||||
GBMBCSwitchHalfBank(gb, half, gb->memory.currentBank);
|
||||
} else {
|
||||
gb->memory.mbcState.mbc6.flashBank1 = !!(value & 0x08);
|
||||
GBMBCSwitchHalfBank(gb, half, gb->memory.currentBank1);
|
||||
}
|
||||
}
|
||||
|
||||
void _GBMBC7(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
int bank = value & 0x7F;
|
||||
switch (address >> 13) {
|
||||
case 0x0:
|
||||
switch (value) {
|
||||
default:
|
||||
case 0:
|
||||
gb->memory.mbcState.mbc7.access = 0;
|
||||
break;
|
||||
case 0xA:
|
||||
gb->memory.mbcState.mbc7.access |= 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x1:
|
||||
GBMBCSwitchBank(gb, bank);
|
||||
break;
|
||||
case 0x2:
|
||||
if (value == 0x40) {
|
||||
gb->memory.mbcState.mbc7.access |= 2;
|
||||
} else {
|
||||
gb->memory.mbcState.mbc7.access &= ~2;
|
||||
}
|
||||
break;
|
||||
case 0x5:
|
||||
_GBMBC7Write(&gb->memory, address, value);
|
||||
gb->sramDirty |= mSAVEDATA_DIRT_NEW;
|
||||
break;
|
||||
default:
|
||||
// TODO
|
||||
mLOG(GB_MBC, STUB, "MBC7 unknown address: %04X:%02X", address, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t _GBMBC7Read(struct GBMemory* memory, uint16_t address) {
|
||||
struct GBMBC7State* mbc7 = &memory->mbcState.mbc7;
|
||||
if (mbc7->access != 3) {
|
||||
return 0xFF;
|
||||
}
|
||||
switch (address & 0xF0) {
|
||||
case 0x20:
|
||||
if (memory->rotation && memory->rotation->readTiltX) {
|
||||
int32_t x = -memory->rotation->readTiltX(memory->rotation);
|
||||
x >>= 21;
|
||||
x += 0x81D0;
|
||||
return x;
|
||||
}
|
||||
return 0xFF;
|
||||
case 0x30:
|
||||
if (memory->rotation && memory->rotation->readTiltX) {
|
||||
int32_t x = -memory->rotation->readTiltX(memory->rotation);
|
||||
x >>= 21;
|
||||
x += 0x81D0;
|
||||
return x >> 8;
|
||||
}
|
||||
return 7;
|
||||
case 0x40:
|
||||
if (memory->rotation && memory->rotation->readTiltY) {
|
||||
int32_t y = -memory->rotation->readTiltY(memory->rotation);
|
||||
y >>= 21;
|
||||
y += 0x81D0;
|
||||
return y;
|
||||
}
|
||||
return 0xFF;
|
||||
case 0x50:
|
||||
if (memory->rotation && memory->rotation->readTiltY) {
|
||||
int32_t y = -memory->rotation->readTiltY(memory->rotation);
|
||||
y >>= 21;
|
||||
y += 0x81D0;
|
||||
return y >> 8;
|
||||
}
|
||||
return 7;
|
||||
case 0x60:
|
||||
return 0;
|
||||
case 0x80:
|
||||
return mbc7->eeprom;
|
||||
default:
|
||||
return 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value) {
|
||||
struct GBMBC7State* mbc7 = &memory->mbcState.mbc7;
|
||||
if (mbc7->access != 3) {
|
||||
return;
|
||||
}
|
||||
switch (address & 0xF0) {
|
||||
case 0x00:
|
||||
mbc7->latch = (value & 0x55) == 0x55;
|
||||
return;
|
||||
case 0x10:
|
||||
mbc7->latch |= (value & 0xAA);
|
||||
if (mbc7->latch == 0xAB && memory->rotation && memory->rotation->sample) {
|
||||
memory->rotation->sample(memory->rotation);
|
||||
}
|
||||
mbc7->latch = 0;
|
||||
return;
|
||||
default:
|
||||
mLOG(GB_MBC, STUB, "MBC7 unknown register: %04X:%02X", address, value);
|
||||
return;
|
||||
case 0x80:
|
||||
break;
|
||||
}
|
||||
GBMBC7Field old = memory->mbcState.mbc7.eeprom;
|
||||
value = GBMBC7FieldFillDO(value); // Hi-Z
|
||||
if (!GBMBC7FieldIsCS(old) && GBMBC7FieldIsCS(value)) {
|
||||
mbc7->state = GBMBC7_STATE_IDLE;
|
||||
}
|
||||
if (!GBMBC7FieldIsCLK(old) && GBMBC7FieldIsCLK(value)) {
|
||||
if (mbc7->state == GBMBC7_STATE_READ_COMMAND || mbc7->state == GBMBC7_STATE_EEPROM_WRITE || mbc7->state == GBMBC7_STATE_EEPROM_WRAL) {
|
||||
mbc7->sr <<= 1;
|
||||
mbc7->sr |= GBMBC7FieldGetDI(value);
|
||||
++mbc7->srBits;
|
||||
}
|
||||
switch (mbc7->state) {
|
||||
case GBMBC7_STATE_IDLE:
|
||||
if (GBMBC7FieldIsDI(value)) {
|
||||
mbc7->state = GBMBC7_STATE_READ_COMMAND;
|
||||
mbc7->srBits = 0;
|
||||
mbc7->sr = 0;
|
||||
}
|
||||
break;
|
||||
case GBMBC7_STATE_READ_COMMAND:
|
||||
if (mbc7->srBits == 10) {
|
||||
mbc7->state = 0x10 | (mbc7->sr >> 6);
|
||||
if (mbc7->state & 0xC) {
|
||||
mbc7->state &= ~0x3;
|
||||
}
|
||||
mbc7->srBits = 0;
|
||||
mbc7->address = mbc7->sr & 0x7F;
|
||||
}
|
||||
break;
|
||||
case GBMBC7_STATE_DO:
|
||||
value = GBMBC7FieldSetDO(value, mbc7->sr >> 15);
|
||||
mbc7->sr <<= 1;
|
||||
--mbc7->srBits;
|
||||
if (!mbc7->srBits) {
|
||||
mbc7->state = GBMBC7_STATE_IDLE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (mbc7->state) {
|
||||
case GBMBC7_STATE_EEPROM_EWEN:
|
||||
mbc7->writable = true;
|
||||
mbc7->state = GBMBC7_STATE_IDLE;
|
||||
break;
|
||||
case GBMBC7_STATE_EEPROM_EWDS:
|
||||
mbc7->writable = false;
|
||||
mbc7->state = GBMBC7_STATE_IDLE;
|
||||
break;
|
||||
case GBMBC7_STATE_EEPROM_WRITE:
|
||||
if (mbc7->srBits == 16) {
|
||||
if (mbc7->writable) {
|
||||
memory->sram[mbc7->address * 2] = mbc7->sr >> 8;
|
||||
memory->sram[mbc7->address * 2 + 1] = mbc7->sr;
|
||||
}
|
||||
mbc7->state = GBMBC7_STATE_IDLE;
|
||||
}
|
||||
break;
|
||||
case GBMBC7_STATE_EEPROM_ERASE:
|
||||
if (mbc7->writable) {
|
||||
memory->sram[mbc7->address * 2] = 0xFF;
|
||||
memory->sram[mbc7->address * 2 + 1] = 0xFF;
|
||||
}
|
||||
mbc7->state = GBMBC7_STATE_IDLE;
|
||||
break;
|
||||
case GBMBC7_STATE_EEPROM_READ:
|
||||
mbc7->srBits = 16;
|
||||
mbc7->sr = memory->sram[mbc7->address * 2] << 8;
|
||||
mbc7->sr |= memory->sram[mbc7->address * 2 + 1];
|
||||
mbc7->state = GBMBC7_STATE_DO;
|
||||
value = GBMBC7FieldClearDO(value);
|
||||
break;
|
||||
case GBMBC7_STATE_EEPROM_WRAL:
|
||||
if (mbc7->srBits == 16) {
|
||||
if (mbc7->writable) {
|
||||
int i;
|
||||
for (i = 0; i < 128; ++i) {
|
||||
memory->sram[i * 2] = mbc7->sr >> 8;
|
||||
memory->sram[i * 2 + 1] = mbc7->sr;
|
||||
}
|
||||
}
|
||||
mbc7->state = GBMBC7_STATE_IDLE;
|
||||
}
|
||||
break;
|
||||
case GBMBC7_STATE_EEPROM_ERAL:
|
||||
if (mbc7->writable) {
|
||||
int i;
|
||||
for (i = 0; i < 128; ++i) {
|
||||
memory->sram[i * 2] = 0xFF;
|
||||
memory->sram[i * 2 + 1] = 0xFF;
|
||||
}
|
||||
}
|
||||
mbc7->state = GBMBC7_STATE_IDLE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (GBMBC7FieldIsCS(value) && GBMBC7FieldIsCLK(old) && !GBMBC7FieldIsCLK(value)) {
|
||||
value = GBMBC7FieldSetDO(value, GBMBC7FieldGetDO(old));
|
||||
}
|
||||
mbc7->eeprom = value;
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
/* Copyright (c) 2013-2016 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 "gb/mbc/mbc-private.h"
|
||||
|
||||
#include <mgba/internal/defines.h>
|
||||
#include <mgba/internal/gb/gb.h>
|
||||
|
||||
static void _GBPocketCamCapture(struct GBMemory*);
|
||||
|
||||
void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
int bank = value & 0x3F;
|
||||
switch (address >> 13) {
|
||||
case 0x0:
|
||||
switch (value) {
|
||||
case 0:
|
||||
memory->sramAccess = false;
|
||||
break;
|
||||
case 0xA:
|
||||
memory->sramAccess = true;
|
||||
GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
|
||||
break;
|
||||
default:
|
||||
// TODO
|
||||
mLOG(GB_MBC, STUB, "Pocket Cam unknown value %02X", value);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x1:
|
||||
GBMBCSwitchBank(gb, bank);
|
||||
break;
|
||||
case 0x2:
|
||||
if (value < 0x10) {
|
||||
GBMBCSwitchSramBank(gb, value);
|
||||
memory->mbcState.pocketCam.registersActive = false;
|
||||
memory->directSramAccess = true;
|
||||
} else {
|
||||
memory->mbcState.pocketCam.registersActive = true;
|
||||
memory->directSramAccess = false;
|
||||
}
|
||||
break;
|
||||
case 0x5:
|
||||
if (!memory->mbcState.pocketCam.registersActive) {
|
||||
break;
|
||||
}
|
||||
address &= 0x7F;
|
||||
if (address == 0 && value & 1) {
|
||||
value &= 6; // TODO: Timing
|
||||
gb->sramDirty |= mSAVEDATA_DIRT_NEW;
|
||||
_GBPocketCamCapture(memory);
|
||||
}
|
||||
if (address < sizeof(memory->mbcState.pocketCam.registers)) {
|
||||
memory->mbcState.pocketCam.registers[address] = value;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
mLOG(GB_MBC, STUB, "Pocket Cam unknown address: %04X:%02X", address, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t _GBPocketCamRead(struct GBMemory* memory, uint16_t address) {
|
||||
if (memory->mbcState.pocketCam.registersActive) {
|
||||
if ((address & 0x7F) == 0) {
|
||||
return memory->mbcState.pocketCam.registers[0];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
|
||||
}
|
||||
|
||||
void _GBPocketCamCapture(struct GBMemory* memory) {
|
||||
if (!memory->cam) {
|
||||
return;
|
||||
}
|
||||
const void* image = NULL;
|
||||
size_t stride;
|
||||
enum mColorFormat format;
|
||||
memory->cam->requestImage(memory->cam, &image, &stride, &format);
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
memset(&memory->sram[0x100], 0, GBCAM_HEIGHT * GBCAM_WIDTH / 4);
|
||||
struct GBPocketCamState* pocketCam = &memory->mbcState.pocketCam;
|
||||
size_t x, y;
|
||||
for (y = 0; y < GBCAM_HEIGHT; ++y) {
|
||||
for (x = 0; x < GBCAM_WIDTH; ++x) {
|
||||
uint32_t gray;
|
||||
uint32_t color;
|
||||
switch (format) {
|
||||
case mCOLOR_XBGR8:
|
||||
case mCOLOR_XRGB8:
|
||||
case mCOLOR_ARGB8:
|
||||
case mCOLOR_ABGR8:
|
||||
color = ((const uint32_t*) image)[y * stride + x];
|
||||
gray = (color & 0xFF) + ((color >> 8) & 0xFF) + ((color >> 16) & 0xFF);
|
||||
break;
|
||||
case mCOLOR_BGRX8:
|
||||
case mCOLOR_RGBX8:
|
||||
case mCOLOR_RGBA8:
|
||||
case mCOLOR_BGRA8:
|
||||
color = ((const uint32_t*) image)[y * stride + x];
|
||||
gray = ((color >> 8) & 0xFF) + ((color >> 16) & 0xFF) + ((color >> 24) & 0xFF);
|
||||
break;
|
||||
case mCOLOR_BGR5:
|
||||
case mCOLOR_RGB5:
|
||||
case mCOLOR_ARGB5:
|
||||
case mCOLOR_ABGR5:
|
||||
color = ((const uint16_t*) image)[y * stride + x];
|
||||
gray = ((color << 3) & 0xF8) + ((color >> 2) & 0xF8) + ((color >> 7) & 0xF8);
|
||||
break;
|
||||
case mCOLOR_BGR565:
|
||||
case mCOLOR_RGB565:
|
||||
color = ((const uint16_t*) image)[y * stride + x];
|
||||
gray = ((color << 3) & 0xF8) + ((color >> 3) & 0xFC) + ((color >> 8) & 0xF8);
|
||||
break;
|
||||
case mCOLOR_BGRA5:
|
||||
case mCOLOR_RGBA5:
|
||||
color = ((const uint16_t*) image)[y * stride + x];
|
||||
gray = ((color << 2) & 0xF8) + ((color >> 3) & 0xF8) + ((color >> 8) & 0xF8);
|
||||
break;
|
||||
default:
|
||||
mLOG(GB_MBC, WARN, "Unsupported pixel format: %X", format);
|
||||
return;
|
||||
}
|
||||
uint16_t exposure = (pocketCam->registers[2] << 8) | (pocketCam->registers[3]);
|
||||
gray = (gray + 1) * exposure / 0x300;
|
||||
// TODO: Additional processing
|
||||
int matrixEntry = 3 * ((x & 3) + 4 * (y & 3));
|
||||
if (gray < pocketCam->registers[matrixEntry + 6]) {
|
||||
gray = 0x101;
|
||||
} else if (gray < pocketCam->registers[matrixEntry + 7]) {
|
||||
gray = 0x100;
|
||||
} else if (gray < pocketCam->registers[matrixEntry + 8]) {
|
||||
gray = 0x001;
|
||||
} else {
|
||||
gray = 0;
|
||||
}
|
||||
int coord = (((x >> 3) & 0xF) * 8 + (y & 0x7)) * 2 + (y & ~0x7) * 0x20;
|
||||
uint16_t existing;
|
||||
LOAD_16LE(existing, coord + 0x100, memory->sram);
|
||||
existing |= gray << (7 - (x & 7));
|
||||
STORE_16LE(existing, coord + 0x100, memory->sram);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,433 @@
|
|||
/* Copyright (c) 2013-2016 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 "gb/mbc/mbc-private.h"
|
||||
|
||||
#include <mgba/internal/defines.h>
|
||||
#include <mgba/internal/gb/gb.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
static const uint8_t _tama6RTCMask[32] = {
|
||||
//0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
0xF, 0x7, 0xF, 0x7, 0xF, 0x3, 0x7, 0xF, 0x3, 0xF, 0x1, 0xF, 0xF, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0xF, 0x7, 0xF, 0x3, 0x7, 0xF, 0x3, 0x0, 0x1, 0x3, 0x0, 0x0, 0x0, 0x0,
|
||||
};
|
||||
|
||||
static const int _daysToMonth[] = {
|
||||
[ 1] = 0,
|
||||
[ 2] = 31,
|
||||
[ 3] = 31 + 28,
|
||||
[ 4] = 31 + 28 + 31,
|
||||
[ 5] = 31 + 28 + 31 + 30,
|
||||
[ 6] = 31 + 28 + 31 + 30 + 31,
|
||||
[ 7] = 31 + 28 + 31 + 30 + 31 + 30,
|
||||
[ 8] = 31 + 28 + 31 + 30 + 31 + 30 + 31,
|
||||
[ 9] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
|
||||
[10] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
|
||||
[11] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
|
||||
[12] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
|
||||
};
|
||||
|
||||
static int _tama6DMYToDayOfYear(int day, int month, int year) {
|
||||
if (month < 1 || month > 12) {
|
||||
return -1;
|
||||
}
|
||||
day += _daysToMonth[month];
|
||||
if (month > 2 && (year & 3) == 0) {
|
||||
++day;
|
||||
}
|
||||
return day;
|
||||
}
|
||||
|
||||
static int _tama6DayOfYearToMonth(int day, int year) {
|
||||
int month;
|
||||
for (month = 1; month < 12; ++month) {
|
||||
if (day <= _daysToMonth[month + 1]) {
|
||||
return month;
|
||||
}
|
||||
if (month == 2 && (year & 3) == 0) {
|
||||
if (day == 60) {
|
||||
return 2;
|
||||
}
|
||||
--day;
|
||||
}
|
||||
}
|
||||
return 12;
|
||||
}
|
||||
|
||||
static int _tama6DayOfYearToDayOfMonth(int day, int year) {
|
||||
int month;
|
||||
for (month = 1; month < 12; ++month) {
|
||||
if (day <= _daysToMonth[month + 1]) {
|
||||
return day - _daysToMonth[month];
|
||||
}
|
||||
if (month == 2 && (year & 3) == 0) {
|
||||
if (day == 60) {
|
||||
return 29;
|
||||
}
|
||||
--day;
|
||||
}
|
||||
}
|
||||
return day - _daysToMonth[12];
|
||||
}
|
||||
|
||||
static void _latchTAMA6Rtc(struct mRTCSource* rtc, struct GBTAMA5State* tama5, time_t* rtcLastLatch) {
|
||||
time_t t;
|
||||
if (rtc) {
|
||||
if (rtc->sample) {
|
||||
rtc->sample(rtc);
|
||||
}
|
||||
t = rtc->unixTime(rtc);
|
||||
} else {
|
||||
t = time(0);
|
||||
}
|
||||
time_t currentLatch = t;
|
||||
t -= *rtcLastLatch;
|
||||
*rtcLastLatch = currentLatch;
|
||||
if (!t || tama5->disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t* timerRegs = tama5->rtcTimerPage;
|
||||
bool is24hour = tama5->rtcAlarmPage[GBTAMA6_RTC_PA1_24_HOUR];
|
||||
int64_t diff;
|
||||
diff = timerRegs[GBTAMA6_RTC_PA0_SECOND_1] + timerRegs[GBTAMA6_RTC_PA0_SECOND_10] * 10 + t % 60;
|
||||
if (diff < 0) {
|
||||
diff += 60;
|
||||
t -= 60;
|
||||
}
|
||||
timerRegs[GBTAMA6_RTC_PA0_SECOND_1] = diff % 10;
|
||||
timerRegs[GBTAMA6_RTC_PA0_SECOND_10] = (diff % 60) / 10;
|
||||
t /= 60;
|
||||
t += diff / 60;
|
||||
|
||||
diff = timerRegs[GBTAMA6_RTC_PA0_MINUTE_1] + timerRegs[GBTAMA6_RTC_PA0_MINUTE_10] * 10 + t % 60;
|
||||
if (diff < 0) {
|
||||
diff += 60;
|
||||
t -= 60;
|
||||
}
|
||||
timerRegs[GBTAMA6_RTC_PA0_MINUTE_1] = diff % 10;
|
||||
timerRegs[GBTAMA6_RTC_PA0_MINUTE_10] = (diff % 60) / 10;
|
||||
t /= 60;
|
||||
t += diff / 60;
|
||||
|
||||
diff = timerRegs[GBTAMA6_RTC_PA0_HOUR_1];
|
||||
if (is24hour) {
|
||||
diff += timerRegs[GBTAMA6_RTC_PA0_HOUR_10] * 10;
|
||||
} else {
|
||||
int hour10 = timerRegs[GBTAMA6_RTC_PA0_HOUR_10];
|
||||
diff += (hour10 & 1) * 10;
|
||||
diff += (hour10 & 2) * 12;
|
||||
}
|
||||
diff += t % 24;
|
||||
if (diff < 0) {
|
||||
diff += 24;
|
||||
t -= 24;
|
||||
}
|
||||
if (is24hour) {
|
||||
timerRegs[GBTAMA6_RTC_PA0_HOUR_1] = (diff % 24) % 10;
|
||||
timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 24) / 10;
|
||||
} else {
|
||||
timerRegs[GBTAMA6_RTC_PA0_HOUR_1] = (diff % 12) % 10;
|
||||
timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 12) / 10 + (diff / 12) * 2;
|
||||
}
|
||||
t /= 24;
|
||||
t += diff / 24;
|
||||
|
||||
int day = timerRegs[GBTAMA6_RTC_PA0_DAY_1] + timerRegs[GBTAMA6_RTC_PA0_DAY_10] * 10;
|
||||
int month = timerRegs[GBTAMA6_RTC_PA0_MONTH_1] + timerRegs[GBTAMA6_RTC_PA0_MONTH_10] * 10;
|
||||
int year = timerRegs[GBTAMA6_RTC_PA0_YEAR_1] + timerRegs[GBTAMA6_RTC_PA0_YEAR_10] * 10;
|
||||
int leapYear = tama5->rtcAlarmPage[GBTAMA6_RTC_PA1_LEAP_YEAR];
|
||||
int dayOfWeek = timerRegs[GBTAMA6_RTC_PA0_WEEK];
|
||||
int dayInYear = _tama6DMYToDayOfYear(day, month, leapYear);
|
||||
diff = dayInYear + t;
|
||||
while (diff <= 0) {
|
||||
// Previous year
|
||||
if (leapYear & 3) {
|
||||
diff += 365;
|
||||
} else {
|
||||
diff += 366;
|
||||
}
|
||||
--year;
|
||||
--leapYear;
|
||||
}
|
||||
while (diff > (leapYear & 3 ? 365 : 366)) {
|
||||
// Future year
|
||||
if (year % 4) {
|
||||
diff -= 365;
|
||||
} else {
|
||||
diff -= 366;
|
||||
}
|
||||
++year;
|
||||
++leapYear;
|
||||
}
|
||||
dayOfWeek = (dayOfWeek + diff) % 7;
|
||||
year %= 100;
|
||||
leapYear &= 3;
|
||||
|
||||
day = _tama6DayOfYearToDayOfMonth(diff, leapYear);
|
||||
month = _tama6DayOfYearToMonth(diff, leapYear);
|
||||
|
||||
timerRegs[GBTAMA6_RTC_PA0_WEEK] = dayOfWeek;
|
||||
tama5->rtcAlarmPage[GBTAMA6_RTC_PA1_LEAP_YEAR] = leapYear;
|
||||
|
||||
timerRegs[GBTAMA6_RTC_PA0_DAY_1] = day % 10;
|
||||
timerRegs[GBTAMA6_RTC_PA0_DAY_10] = day / 10;
|
||||
|
||||
timerRegs[GBTAMA6_RTC_PA0_MONTH_1] = month % 10;
|
||||
timerRegs[GBTAMA6_RTC_PA0_MONTH_10] = month / 10;
|
||||
|
||||
timerRegs[GBTAMA6_RTC_PA0_YEAR_1] = year % 10;
|
||||
timerRegs[GBTAMA6_RTC_PA0_YEAR_10] = year / 10;
|
||||
}
|
||||
|
||||
void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
struct GBTAMA5State* tama5 = &memory->mbcState.tama5;
|
||||
switch (address >> 13) {
|
||||
case 0x5:
|
||||
if (address & 1) {
|
||||
tama5->reg = value;
|
||||
} else {
|
||||
value &= 0xF;
|
||||
if (tama5->reg < GBTAMA5_MAX) {
|
||||
mLOG(GB_MBC, DEBUG, "TAMA5 write: %02X:%X", tama5->reg, value);
|
||||
tama5->registers[tama5->reg] = value;
|
||||
uint8_t address = ((tama5->registers[GBTAMA5_ADDR_HI] << 4) & 0x10) | tama5->registers[GBTAMA5_ADDR_LO];
|
||||
uint8_t out = (tama5->registers[GBTAMA5_WRITE_HI] << 4) | tama5->registers[GBTAMA5_WRITE_LO];
|
||||
switch (tama5->reg) {
|
||||
case GBTAMA5_BANK_LO:
|
||||
case GBTAMA5_BANK_HI:
|
||||
GBMBCSwitchBank(gb, tama5->registers[GBTAMA5_BANK_LO] | (tama5->registers[GBTAMA5_BANK_HI] << 4));
|
||||
break;
|
||||
case GBTAMA5_WRITE_LO:
|
||||
case GBTAMA5_WRITE_HI:
|
||||
case GBTAMA5_ADDR_HI:
|
||||
break;
|
||||
case GBTAMA5_ADDR_LO:
|
||||
switch (tama5->registers[GBTAMA5_ADDR_HI] >> 1) {
|
||||
case 0x0: // RAM write
|
||||
memory->sram[address] = out;
|
||||
gb->sramDirty |= mSAVEDATA_DIRT_NEW;
|
||||
break;
|
||||
case 0x1: // RAM read
|
||||
break;
|
||||
case 0x2: // Other commands
|
||||
switch (address) {
|
||||
case GBTAMA6_DISABLE_TIMER:
|
||||
tama5->disabled = true;
|
||||
tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] &= 0x7;
|
||||
tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] &= 0x7;
|
||||
tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] &= 0x7;
|
||||
tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] &= 0x7;
|
||||
break;
|
||||
case GBTAMA6_ENABLE_TIMER:
|
||||
tama5->disabled = false;
|
||||
tama5->rtcTimerPage[GBTAMA6_RTC_PA0_SECOND_1] = 0;
|
||||
tama5->rtcTimerPage[GBTAMA6_RTC_PA0_SECOND_10] = 0;
|
||||
tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] |= 0x8;
|
||||
tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] |= 0x8;
|
||||
tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] |= 0x8;
|
||||
tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] |= 0x8;
|
||||
break;
|
||||
case GBTAMA6_MINUTE_WRITE:
|
||||
tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_1] = out & 0xF;
|
||||
tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_10] = out >> 4;
|
||||
break;
|
||||
case GBTAMA6_HOUR_WRITE:
|
||||
tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_1] = out & 0xF;
|
||||
tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_10] = out >> 4;
|
||||
break;
|
||||
case GBTAMA6_DISABLE_ALARM:
|
||||
tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] &= 0xB;
|
||||
tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] &= 0xB;
|
||||
tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] &= 0xB;
|
||||
tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] &= 0xB;
|
||||
break;
|
||||
case GBTAMA6_ENABLE_ALARM:
|
||||
tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] |= 0x4;
|
||||
tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] |= 0x4;
|
||||
tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] |= 0x4;
|
||||
tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] |= 0x4;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x4: // RTC access
|
||||
address = tama5->registers[GBTAMA5_WRITE_LO];
|
||||
if (address >= GBTAMA6_RTC_PAGE) {
|
||||
break;
|
||||
}
|
||||
out = tama5->registers[GBTAMA5_WRITE_HI];
|
||||
switch (tama5->registers[GBTAMA5_ADDR_LO]) {
|
||||
case 0:
|
||||
out &= _tama6RTCMask[address];
|
||||
tama5->rtcTimerPage[address] = out;
|
||||
break;
|
||||
case 2:
|
||||
out &= _tama6RTCMask[address | 0x10];
|
||||
tama5->rtcAlarmPage[address] = out;
|
||||
break;
|
||||
case 4:
|
||||
tama5->rtcFreePage0[address] = out;
|
||||
break;
|
||||
case 6:
|
||||
tama5->rtcFreePage1[address] = out;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
mLOG(GB_MBC, STUB, "TAMA5 unknown address: %02X:%02X", address, out);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
mLOG(GB_MBC, STUB, "TAMA5 unknown write: %02X:%X", tama5->reg, value);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
mLOG(GB_MBC, STUB, "TAMA5 unknown write: %02X", tama5->reg);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
mLOG(GB_MBC, STUB, "TAMA5 unknown address: %04X:%02X", address, value);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t _GBTAMA5Read(struct GBMemory* memory, uint16_t address) {
|
||||
struct GBTAMA5State* tama5 = &memory->mbcState.tama5;
|
||||
if ((address & 0x1FFF) > 1) {
|
||||
mLOG(GB_MBC, STUB, "TAMA5 unknown address: %04X", address);
|
||||
}
|
||||
if (address & 1) {
|
||||
return 0xFF;
|
||||
} else {
|
||||
uint8_t value = 0xF0;
|
||||
uint8_t address = ((tama5->registers[GBTAMA5_ADDR_HI] << 4) & 0x10) | tama5->registers[GBTAMA5_ADDR_LO];
|
||||
switch (tama5->reg) {
|
||||
case GBTAMA5_ACTIVE:
|
||||
return 0xF1;
|
||||
case GBTAMA5_READ_LO:
|
||||
case GBTAMA5_READ_HI:
|
||||
switch (tama5->registers[GBTAMA5_ADDR_HI] >> 1) {
|
||||
case 0x1:
|
||||
value = memory->sram[address];
|
||||
break;
|
||||
case 0x2:
|
||||
mLOG(GB_MBC, STUB, "TAMA5 unknown read %s: %02X", tama5->reg == GBTAMA5_READ_HI ? "hi" : "lo", address);
|
||||
_latchTAMA6Rtc(memory->rtc, tama5, &memory->rtcLastLatch);
|
||||
switch (address) {
|
||||
case GBTAMA6_MINUTE_READ:
|
||||
value = (tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_10] << 4) | tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_1];
|
||||
break;
|
||||
case GBTAMA6_HOUR_READ:
|
||||
value = (tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_10] << 4) | tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_1];
|
||||
break;
|
||||
default:
|
||||
value = address;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x4:
|
||||
if (tama5->reg == GBTAMA5_READ_HI) {
|
||||
mLOG(GB_MBC, GAME_ERROR, "TAMA5 reading RTC incorrectly");
|
||||
break;
|
||||
}
|
||||
_latchTAMA6Rtc(memory->rtc, tama5, &memory->rtcLastLatch);
|
||||
address = tama5->registers[GBTAMA5_WRITE_LO];
|
||||
if (address > GBTAMA6_RTC_PAGE) {
|
||||
value = 0;
|
||||
break;
|
||||
}
|
||||
switch (tama5->registers[GBTAMA5_ADDR_LO]) {
|
||||
case 1:
|
||||
value = tama5->rtcTimerPage[address];
|
||||
break;
|
||||
case 3:
|
||||
value = tama5->rtcTimerPage[address];
|
||||
break;
|
||||
case 5:
|
||||
value = tama5->rtcTimerPage[address];
|
||||
break;
|
||||
case 7:
|
||||
value = tama5->rtcTimerPage[address];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
mLOG(GB_MBC, STUB, "TAMA5 unknown read %s: %02X", tama5->reg == GBTAMA5_READ_HI ? "hi" : "lo", address);
|
||||
break;
|
||||
}
|
||||
if (tama5->reg == GBTAMA5_READ_HI) {
|
||||
value >>= 4;
|
||||
}
|
||||
value |= 0xF0;
|
||||
return value;
|
||||
default:
|
||||
mLOG(GB_MBC, STUB, "TAMA5 unknown read: %02X", tama5->reg);
|
||||
return 0xF1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GBMBCTAMA5Read(struct GB* gb) {
|
||||
struct GBMBCTAMA5SaveBuffer buffer;
|
||||
struct VFile* vf = gb->sramVf;
|
||||
if (!vf) {
|
||||
return;
|
||||
}
|
||||
vf->seek(vf, gb->sramSize, SEEK_SET);
|
||||
if (vf->read(vf, &buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) {
|
||||
gb->memory.mbcState.tama5.disabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < 0x8; ++i) {
|
||||
gb->memory.mbcState.tama5.rtcTimerPage[i * 2] = buffer.rtcTimerPage[i] & 0xF;
|
||||
gb->memory.mbcState.tama5.rtcTimerPage[i * 2 + 1] = buffer.rtcTimerPage[i] >> 4;
|
||||
gb->memory.mbcState.tama5.rtcAlarmPage[i * 2] = buffer.rtcAlarmPage[i] & 0xF;
|
||||
gb->memory.mbcState.tama5.rtcAlarmPage[i * 2 + 1] = buffer.rtcAlarmPage[i] >> 4;
|
||||
gb->memory.mbcState.tama5.rtcFreePage0[i * 2] = buffer.rtcFreePage0[i] & 0xF;
|
||||
gb->memory.mbcState.tama5.rtcFreePage0[i * 2 + 1] = buffer.rtcFreePage0[i] >> 4;
|
||||
gb->memory.mbcState.tama5.rtcFreePage1[i * 2] = buffer.rtcFreePage1[i] & 0xF;
|
||||
gb->memory.mbcState.tama5.rtcFreePage1[i * 2 + 1] = buffer.rtcFreePage1[i] >> 4;
|
||||
}
|
||||
LOAD_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix);
|
||||
|
||||
gb->memory.mbcState.tama5.disabled = !(gb->memory.mbcState.tama5.rtcTimerPage[GBTAMA6_RTC_PAGE] & 0x8);
|
||||
|
||||
gb->memory.mbcState.tama5.rtcTimerPage[GBTAMA6_RTC_PAGE] &= 0xC;
|
||||
gb->memory.mbcState.tama5.rtcAlarmPage[GBTAMA6_RTC_PAGE] &= 0xC;
|
||||
gb->memory.mbcState.tama5.rtcAlarmPage[GBTAMA6_RTC_PAGE] |= 1;
|
||||
gb->memory.mbcState.tama5.rtcFreePage0[GBTAMA6_RTC_PAGE] &= 0xC;
|
||||
gb->memory.mbcState.tama5.rtcFreePage0[GBTAMA6_RTC_PAGE] |= 2;
|
||||
gb->memory.mbcState.tama5.rtcFreePage1[GBTAMA6_RTC_PAGE] &= 0xC;
|
||||
gb->memory.mbcState.tama5.rtcFreePage1[GBTAMA6_RTC_PAGE] |= 3;
|
||||
}
|
||||
|
||||
void GBMBCTAMA5Write(struct GB* gb) {
|
||||
struct VFile* vf = gb->sramVf;
|
||||
if (!vf) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct GBMBCTAMA5SaveBuffer buffer = {0};
|
||||
size_t i;
|
||||
for (i = 0; i < 8; ++i) {
|
||||
buffer.rtcTimerPage[i] = gb->memory.mbcState.tama5.rtcTimerPage[i * 2] & 0xF;
|
||||
buffer.rtcTimerPage[i] |= gb->memory.mbcState.tama5.rtcTimerPage[i * 2 + 1] << 4;
|
||||
buffer.rtcAlarmPage[i] = gb->memory.mbcState.tama5.rtcAlarmPage[i * 2] & 0xF;
|
||||
buffer.rtcAlarmPage[i] |= gb->memory.mbcState.tama5.rtcAlarmPage[i * 2 + 1] << 4;
|
||||
buffer.rtcFreePage0[i] = gb->memory.mbcState.tama5.rtcFreePage0[i * 2] & 0xF;
|
||||
buffer.rtcFreePage0[i] |= gb->memory.mbcState.tama5.rtcFreePage0[i * 2 + 1] << 4;
|
||||
buffer.rtcFreePage1[i] = gb->memory.mbcState.tama5.rtcFreePage1[i * 2] & 0xF;
|
||||
buffer.rtcFreePage1[i] |= gb->memory.mbcState.tama5.rtcFreePage1[i * 2 + 1] << 4;
|
||||
}
|
||||
STORE_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix);
|
||||
|
||||
_GBMBCAppendSaveSuffix(gb, &buffer, sizeof(buffer));
|
||||
}
|
|
@ -0,0 +1,500 @@
|
|||
/* Copyright (c) 2013-2016 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 "gb/mbc/mbc-private.h"
|
||||
|
||||
#include <mgba/internal/gb/gb.h>
|
||||
|
||||
void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
UNUSED(value);
|
||||
int bank = address & 0x3F;
|
||||
switch (address >> 14) {
|
||||
case 0x0:
|
||||
GBMBCSwitchBank0(gb, bank * 2);
|
||||
GBMBCSwitchBank(gb, bank * 2 + 1);
|
||||
break;
|
||||
default:
|
||||
// TODO
|
||||
mLOG(GB_MBC, STUB, "Wisdom Tree unknown address: %04X:%02X", address, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
switch (address >> 13) {
|
||||
case 0x2:
|
||||
if (value < 8) {
|
||||
memory->directSramAccess = true;
|
||||
memory->activeRtcReg = 0;
|
||||
} else if (value >= 0xD && value <= 0xF) {
|
||||
memory->directSramAccess = false;
|
||||
memory->rtcAccess = false;
|
||||
memory->activeRtcReg = value - 8;
|
||||
}
|
||||
break;
|
||||
case 0x5:
|
||||
if (!memory->sramAccess) {
|
||||
return;
|
||||
}
|
||||
switch (memory->activeRtcReg) {
|
||||
case 0:
|
||||
memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)] = value;
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
memory->mbcState.pkjd.reg[memory->activeRtcReg - 5] = value;
|
||||
break;
|
||||
case 7:
|
||||
switch (value) {
|
||||
case 0x11:
|
||||
memory->mbcState.pkjd.reg[0]--;
|
||||
break;
|
||||
case 0x12:
|
||||
memory->mbcState.pkjd.reg[1]--;
|
||||
break;
|
||||
case 0x41:
|
||||
memory->mbcState.pkjd.reg[0] += memory->mbcState.pkjd.reg[1];
|
||||
break;
|
||||
case 0x42:
|
||||
memory->mbcState.pkjd.reg[1] += memory->mbcState.pkjd.reg[0];
|
||||
break;
|
||||
case 0x51:
|
||||
memory->mbcState.pkjd.reg[0]++;
|
||||
break;
|
||||
case 0x52:
|
||||
memory->mbcState.pkjd.reg[1]--;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
_GBMBC3(gb, address, value);
|
||||
}
|
||||
|
||||
uint8_t _GBPKJDRead(struct GBMemory* memory, uint16_t address) {
|
||||
if (!memory->sramAccess) {
|
||||
return 0xFF;
|
||||
}
|
||||
switch (memory->activeRtcReg) {
|
||||
case 0:
|
||||
return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
|
||||
case 5:
|
||||
case 6:
|
||||
return memory->mbcState.pkjd.reg[memory->activeRtcReg - 5];
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static uint8_t _reorderBits(uint8_t input, const uint8_t* reorder) {
|
||||
uint8_t newbyte = 0;
|
||||
int i;
|
||||
for(i = 0; i < 8; ++i) {
|
||||
int oldbit = reorder[i];
|
||||
int newbit = i;
|
||||
newbyte += ((input >> oldbit) & 1) << newbit;
|
||||
}
|
||||
|
||||
return newbyte;
|
||||
}
|
||||
|
||||
static const uint8_t _ntOld1Reorder[8] = {
|
||||
0, 2, 1, 4, 3, 5, 6, 7
|
||||
};
|
||||
|
||||
void _ntOldMulticart(struct GB* gb, uint16_t address, uint8_t value, const uint8_t reorder[8]) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
struct GBNTOldState* mbcState = &memory->mbcState.ntOld;
|
||||
int bank = value;
|
||||
|
||||
switch (address & 3) {
|
||||
case 0:
|
||||
mLOG(GB_MBC, STUB, "Unimplemented NT Old 1 address 0");
|
||||
break;
|
||||
case 1:
|
||||
value &= 0x3F;
|
||||
mbcState->baseBank = value * 2;
|
||||
if (mbcState->baseBank) {
|
||||
GBMBCSwitchBank0(gb, mbcState->baseBank);
|
||||
GBMBCSwitchBank(gb, mbcState->baseBank + 1);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if ((value & 0xF0) == 0xE0) {
|
||||
gb->sramSize = 0x2000;
|
||||
GBResizeSram(gb, gb->sramSize);
|
||||
}
|
||||
switch (value & 0xF) {
|
||||
case 0x00:
|
||||
mbcState->bankCount = 32;
|
||||
break;
|
||||
case 0x08:
|
||||
mbcState->bankCount = 16;
|
||||
break;
|
||||
case 0xC:
|
||||
mbcState->bankCount = 8;
|
||||
break;
|
||||
case 0xE:
|
||||
mbcState->bankCount = 4;
|
||||
break;
|
||||
case 0xF:
|
||||
mbcState->bankCount = 2;
|
||||
break;
|
||||
default:
|
||||
mbcState->bankCount = 32;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
mbcState->swapped = !!(value & 0x10);
|
||||
|
||||
bank = memory->currentBank;
|
||||
if (mbcState->swapped) {
|
||||
bank = _reorderBits(bank, reorder);
|
||||
}
|
||||
GBMBCSwitchBank(gb, bank);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _GBNTOld1(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
struct GBNTOldState* mbcState = &memory->mbcState.ntOld;
|
||||
int bank = value;
|
||||
|
||||
switch (address >> 12) {
|
||||
case 0x0:
|
||||
case 0x1:
|
||||
_GBMBC3(gb, address, value);
|
||||
break;
|
||||
case 0x2:
|
||||
case 0x3:
|
||||
bank &= 0x1F;
|
||||
if (!bank) {
|
||||
bank = 1;
|
||||
}
|
||||
if (mbcState->swapped) {
|
||||
bank = _reorderBits(bank, _ntOld1Reorder);
|
||||
}
|
||||
if (mbcState->bankCount) {
|
||||
bank &= mbcState->bankCount - 1;
|
||||
}
|
||||
GBMBCSwitchBank(gb, bank + mbcState->baseBank);
|
||||
break;
|
||||
case 0x5:
|
||||
_ntOldMulticart(gb, address, value, _ntOld1Reorder);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const uint8_t _ntOld2Reorder[8] = {
|
||||
1, 2, 0, 3, 4, 5, 6, 7
|
||||
};
|
||||
|
||||
void _GBNTOld2(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
struct GBNTOldState* mbcState = &memory->mbcState.ntOld;
|
||||
int bank = value;
|
||||
|
||||
switch (address >> 12) {
|
||||
case 0x0:
|
||||
case 0x1:
|
||||
_GBMBC3(gb, address, value);
|
||||
break;
|
||||
case 0x2:
|
||||
case 0x3:
|
||||
if (!bank) {
|
||||
bank = 1;
|
||||
}
|
||||
if (mbcState->swapped) {
|
||||
bank = _reorderBits(bank, _ntOld2Reorder);
|
||||
}
|
||||
if (mbcState->bankCount) {
|
||||
bank &= mbcState->bankCount - 1;
|
||||
}
|
||||
GBMBCSwitchBank(gb, bank + mbcState->baseBank);
|
||||
break;
|
||||
case 0x5:
|
||||
_ntOldMulticart(gb, address, value, _ntOld2Reorder);
|
||||
// Fall through
|
||||
case 0x4:
|
||||
if (address == 0x5001) {
|
||||
mbcState->rumble = !!(value & 0x80);
|
||||
}
|
||||
|
||||
if (mbcState->rumble) {
|
||||
memory->rumble->setRumble(memory->rumble, !!(mbcState->swapped ? value & 0x08 : value & 0x02));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
if (address >> 8 == 0x14) {
|
||||
memory->mbcState.ntNew.splitMode = true;
|
||||
return;
|
||||
}
|
||||
if (memory->mbcState.ntNew.splitMode) {
|
||||
int bank = value;
|
||||
if (bank < 2) {
|
||||
bank = 2;
|
||||
}
|
||||
switch (address >> 10) {
|
||||
case 8:
|
||||
GBMBCSwitchHalfBank(gb, 0, bank);
|
||||
return;
|
||||
case 9:
|
||||
GBMBCSwitchHalfBank(gb, 1, bank);
|
||||
return;
|
||||
}
|
||||
}
|
||||
_GBMBC5(gb, address, value);
|
||||
}
|
||||
|
||||
static const uint8_t _bbdDataReordering[8][8] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 00 - Normal
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 01 - NOT KNOWN YET
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 02 - NOT KNOWN YET
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 03 - NOT KNOWN YET
|
||||
{ 0, 5, 1, 3, 4, 2, 6, 7 }, // 04 - Garou
|
||||
{ 0, 4, 2, 3, 1, 5, 6, 7 }, // 05 - Harry
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 06 - NOT KNOWN YET
|
||||
{ 0, 1, 5, 3, 4, 2, 6, 7 }, // 07 - Digimon
|
||||
};
|
||||
|
||||
static const uint8_t _bbdBankReordering[8][8] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 00 - Normal
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 01 - NOT KNOWN YET
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 02 - NOT KNOWN YET
|
||||
{ 3, 4, 2, 0, 1, 5, 6, 7 }, // 03 - 0,1 unconfirmed. Digimon/Garou
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 04 - NOT KNOWN YET
|
||||
{ 1, 2, 3, 4, 0, 5, 6, 7 }, // 05 - 0,1 unconfirmed. Harry
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 06 - NOT KNOWN YET
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 07 - NOT KNOWN YET
|
||||
};
|
||||
|
||||
void _GBBBD(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
switch (address & 0xF0FF) {
|
||||
case 0x2000:
|
||||
value = _reorderBits(value, _bbdBankReordering[memory->mbcState.bbd.bankSwapMode]);
|
||||
break;
|
||||
case 0x2001:
|
||||
memory->mbcState.bbd.dataSwapMode = value & 0x07;
|
||||
if (!(memory->mbcState.bbd.dataSwapMode == 0x07 || memory->mbcState.bbd.dataSwapMode == 0x05 || memory->mbcState.bbd.dataSwapMode == 0x04 || memory->mbcState.bbd.dataSwapMode == 0x00)) {
|
||||
mLOG(GB_MBC, STUB, "Bitswap mode unsupported: %X", memory->mbcState.bbd.dataSwapMode);
|
||||
}
|
||||
break;
|
||||
case 0x2080:
|
||||
memory->mbcState.bbd.bankSwapMode = value & 0x07;
|
||||
if (!(memory->mbcState.bbd.bankSwapMode == 0x03 || memory->mbcState.bbd.bankSwapMode == 0x05 || memory->mbcState.bbd.bankSwapMode == 0x00)) {
|
||||
mLOG(GB_MBC, STUB, "Bankswap mode unsupported: %X", memory->mbcState.bbd.dataSwapMode);
|
||||
}
|
||||
break;
|
||||
}
|
||||
_GBMBC5(gb, address, value);
|
||||
}
|
||||
|
||||
uint8_t _GBBBDRead(struct GBMemory* memory, uint16_t address) {
|
||||
switch (address >> 14) {
|
||||
case 0:
|
||||
default:
|
||||
return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
|
||||
case 1:
|
||||
return _reorderBits(memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)], _bbdDataReordering[memory->mbcState.bbd.dataSwapMode]);
|
||||
}
|
||||
}
|
||||
|
||||
static const uint8_t _hitekDataReordering[8][8] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 },
|
||||
{ 0, 6, 5, 3, 4, 1, 2, 7 },
|
||||
{ 0, 5, 6, 3, 4, 2, 1, 7 },
|
||||
{ 0, 6, 2, 3, 4, 5, 1, 7 },
|
||||
{ 0, 6, 1, 3, 4, 5, 2, 7 },
|
||||
{ 0, 1, 6, 3, 4, 5, 2, 7 },
|
||||
{ 0, 2, 6, 3, 4, 1, 5, 7 },
|
||||
{ 0, 6, 2, 3, 4, 1, 5, 7 },
|
||||
};
|
||||
|
||||
static const uint8_t _hitekBankReordering[8][8] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 },
|
||||
{ 3, 2, 1, 0, 4, 5, 6, 7 },
|
||||
{ 2, 1, 0, 3, 4, 5, 6, 7 },
|
||||
{ 1, 0, 3, 2, 4, 5, 6, 7 },
|
||||
{ 0, 3, 2, 1, 4, 5, 6, 7 },
|
||||
{ 2, 3, 0, 1, 4, 5, 6, 7 },
|
||||
{ 3, 0, 1, 2, 4, 5, 6, 7 },
|
||||
{ 2, 0, 3, 1, 4, 5, 6, 7 },
|
||||
};
|
||||
|
||||
void _GBHitek(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
switch (address & 0xF0FF) {
|
||||
case 0x2000:
|
||||
value = _reorderBits(value, _hitekBankReordering[memory->mbcState.bbd.bankSwapMode]);
|
||||
break;
|
||||
case 0x2001:
|
||||
memory->mbcState.bbd.dataSwapMode = value & 0x07;
|
||||
break;
|
||||
case 0x2080:
|
||||
memory->mbcState.bbd.bankSwapMode = value & 0x07;
|
||||
break;
|
||||
case 0x300:
|
||||
// See hhugboy src/memory/mbc/MbcUnlHitek.cpp for commentary on this return
|
||||
return;
|
||||
}
|
||||
_GBMBC5(gb, address, value);
|
||||
}
|
||||
|
||||
uint8_t _GBHitekRead(struct GBMemory* memory, uint16_t address) {
|
||||
switch (address >> 14) {
|
||||
case 0:
|
||||
default:
|
||||
return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
|
||||
case 1:
|
||||
return _reorderBits(memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)], _hitekDataReordering[memory->mbcState.bbd.dataSwapMode]);
|
||||
}
|
||||
}
|
||||
|
||||
static const uint8_t _ggb81DataReordering[8][8] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 },
|
||||
{ 0, 2, 1, 3, 4, 6, 5, 7 },
|
||||
{ 0, 6, 5, 3, 4, 2, 1, 7 },
|
||||
{ 0, 5, 1, 3, 4, 2, 6, 7 },
|
||||
{ 0, 5, 2, 3, 4, 1, 6, 7 },
|
||||
{ 0, 2, 6, 3, 4, 5, 1, 7 },
|
||||
{ 0, 1, 6, 3, 4, 2, 5, 7 },
|
||||
{ 0, 2, 5, 3, 4, 6, 1, 7 },
|
||||
};
|
||||
|
||||
void _GBGGB81(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
switch (address & 0xF0FF) {
|
||||
case 0x2001:
|
||||
memory->mbcState.bbd.dataSwapMode = value & 0x07;
|
||||
break;
|
||||
}
|
||||
_GBMBC5(gb, address, value);
|
||||
}
|
||||
|
||||
uint8_t _GBGGB81Read(struct GBMemory* memory, uint16_t address) {
|
||||
switch (address >> 14) {
|
||||
case 0:
|
||||
default:
|
||||
return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
|
||||
case 1:
|
||||
return _reorderBits(memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)], _ggb81DataReordering[memory->mbcState.bbd.dataSwapMode]);
|
||||
}
|
||||
}
|
||||
|
||||
void _GBLiCheng(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
if (address > 0x2100 && address < 0x3000) {
|
||||
return;
|
||||
}
|
||||
_GBMBC5(gb, address, value);
|
||||
}
|
||||
|
||||
void _GBSachen(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBSachenState* state = &gb->memory.mbcState.sachen;
|
||||
uint8_t bank = value;
|
||||
switch (address >> 13) {
|
||||
case 0:
|
||||
if ((state->unmaskedBank & 0x30) == 0x30) {
|
||||
state->baseBank = bank;
|
||||
GBMBCSwitchBank0(gb, state->baseBank & state->mask);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (!bank) {
|
||||
bank = 1;
|
||||
}
|
||||
state->unmaskedBank = bank;
|
||||
bank = (bank & ~state->mask) | (state->baseBank & state->mask);
|
||||
GBMBCSwitchBank(gb, bank);
|
||||
break;
|
||||
case 2:
|
||||
if ((state->unmaskedBank & 0x30) == 0x30) {
|
||||
state->mask = value;
|
||||
bank = (state->unmaskedBank & ~state->mask) | (state->baseBank & state->mask);
|
||||
GBMBCSwitchBank(gb, bank);
|
||||
GBMBCSwitchBank0(gb, state->baseBank & state->mask);
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
if (gb->memory.mbcType == GB_UNL_SACHEN_MMC2 && state->locked == GB_SACHEN_LOCKED_DMG) {
|
||||
state->locked = GB_SACHEN_LOCKED_CGB;
|
||||
state->transition = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t _unscrambleSachen(uint16_t address) {
|
||||
uint16_t unscrambled = address & 0xFFAC;
|
||||
unscrambled |= (address & 0x40) >> 6;
|
||||
unscrambled |= (address & 0x10) >> 3;
|
||||
unscrambled |= (address & 0x02) << 3;
|
||||
unscrambled |= (address & 0x01) << 6;
|
||||
return unscrambled;
|
||||
}
|
||||
|
||||
uint8_t _GBSachenMMC1Read(struct GBMemory* memory, uint16_t address) {
|
||||
struct GBSachenState* state = &memory->mbcState.sachen;
|
||||
if (state->locked != GB_SACHEN_UNLOCKED && (address & 0xFF00) == 0x100) {
|
||||
++state->transition;
|
||||
if (state->transition == 0x31) {
|
||||
state->locked = GB_SACHEN_UNLOCKED;
|
||||
} else {
|
||||
address |= 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
if ((address & 0xFF00) == 0x0100) {
|
||||
address = _unscrambleSachen(address);
|
||||
}
|
||||
|
||||
if (address < GB_BASE_CART_BANK1) {
|
||||
return memory->romBase[address];
|
||||
} else if (address < GB_BASE_VRAM) {
|
||||
return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
|
||||
} else {
|
||||
return 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t _GBSachenMMC2Read(struct GBMemory* memory, uint16_t address) {
|
||||
struct GBSachenState* state = &memory->mbcState.sachen;
|
||||
if (address >= 0xC000 && state->locked == GB_SACHEN_LOCKED_DMG) {
|
||||
state->transition = 0;
|
||||
state->locked = GB_SACHEN_LOCKED_CGB;
|
||||
}
|
||||
|
||||
if (state->locked != GB_SACHEN_UNLOCKED && (address & 0x8700) == 0x0100) {
|
||||
++state->transition;
|
||||
if (state->transition == 0x31) {
|
||||
++state->locked;
|
||||
state->transition = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ((address & 0xFF00) == 0x0100) {
|
||||
if (state->locked == GB_SACHEN_LOCKED_CGB) {
|
||||
address |= 0x80;
|
||||
}
|
||||
address = _unscrambleSachen(address);
|
||||
}
|
||||
|
||||
if (address < GB_BASE_CART_BANK1) {
|
||||
return memory->romBase[address];
|
||||
} else if (address < GB_BASE_VRAM) {
|
||||
return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
|
||||
} else {
|
||||
return 0xFF;
|
||||
}
|
||||
}
|
|
@ -558,7 +558,7 @@ uint8_t GBMemoryWriteHDMA5(struct GB* gb, uint8_t value) {
|
|||
gb->memory.hdmaDest |= 0x8000;
|
||||
bool wasHdma = gb->memory.isHdma;
|
||||
gb->memory.isHdma = value & 0x80;
|
||||
if ((!wasHdma && !gb->memory.isHdma) || (GBRegisterLCDCIsEnable(gb->memory.io[GB_REG_LCDC]) && gb->video.mode == 0)) {
|
||||
if ((!wasHdma && !gb->memory.isHdma) || gb->video.mode == 0) {
|
||||
if (gb->memory.isHdma) {
|
||||
gb->memory.hdmaRemaining = 0x10;
|
||||
} else {
|
||||
|
@ -566,8 +566,6 @@ uint8_t GBMemoryWriteHDMA5(struct GB* gb, uint8_t value) {
|
|||
}
|
||||
gb->cpuBlocked = true;
|
||||
mTimingSchedule(&gb->timing, &gb->memory.hdmaEvent, 0);
|
||||
} else if (gb->memory.isHdma && !GBRegisterLCDCIsEnable(gb->memory.io[GB_REG_LCDC])) {
|
||||
return 0x80 | ((value + 1) & 0x7F);
|
||||
}
|
||||
return value & 0x7F;
|
||||
}
|
||||
|
@ -755,6 +753,12 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) {
|
|||
case GB_MBC3_RTC:
|
||||
STORE_64LE(memory->rtcLastLatch, 0, &state->memory.rtc.lastLatch);
|
||||
break;
|
||||
case GB_MBC6:
|
||||
state->memory.mbc6.flags = GBSerializedMBC6FlagsSetFlashBank0(0, memory->mbcState.mbc6.flashBank0);
|
||||
state->memory.mbc6.flags = GBSerializedMBC6FlagsSetFlashBank1(state->memory.mbc6.flags, memory->mbcState.mbc6.flashBank1);
|
||||
state->memory.mbc6.bank1 = memory->currentBank1;
|
||||
state->memory.mbc6.sramBank1 = memory->currentSramBank1;
|
||||
break;
|
||||
case GB_MBC7:
|
||||
state->memory.mbc7.state = memory->mbcState.mbc7.state;
|
||||
state->memory.mbc7.eeprom = memory->mbcState.mbc7.eeprom;
|
||||
|
@ -797,8 +801,20 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) {
|
|||
state->memory.mmm01.locked = memory->mbcState.mmm01.locked;
|
||||
state->memory.mmm01.bank0 = memory->mbcState.mmm01.currentBank0;
|
||||
break;
|
||||
case GB_UNL_NT_OLD_1:
|
||||
case GB_UNL_NT_OLD_2:
|
||||
state->memory.ntOld.flags = GBSerializedNTOldFlagsSetSwapped(0, memory->mbcState.ntOld.swapped);
|
||||
state->memory.ntOld.flags = GBSerializedNTOldFlagsSetRumble(state->memory.ntOld.flags, memory->mbcState.ntOld.rumble);
|
||||
state->memory.ntOld.baseBank = memory->mbcState.ntOld.baseBank;
|
||||
state->memory.ntOld.bankCount = memory->mbcState.ntOld.bankCount;
|
||||
break;
|
||||
case GB_UNL_NT_NEW:
|
||||
state->memory.ntNew.splitMode = memory->mbcState.ntNew.splitMode;
|
||||
state->memory.ntNew.bank1 = memory->currentBank1;
|
||||
break;
|
||||
case GB_UNL_BBD:
|
||||
case GB_UNL_HITEK:
|
||||
case GB_UNL_GGB81:
|
||||
state->memory.bbd.dataSwapMode = memory->mbcState.bbd.dataSwapMode;
|
||||
state->memory.bbd.bankSwapMode = memory->mbcState.bbd.bankSwapMode;
|
||||
break;
|
||||
|
@ -823,9 +839,11 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
memory->wramCurrentBank = state->memory.wramCurrentBank;
|
||||
memory->sramCurrentBank = state->memory.sramCurrentBank;
|
||||
|
||||
GBMBCSwitchBank(gb, memory->currentBank);
|
||||
GBMemorySwitchWramBank(memory, memory->wramCurrentBank);
|
||||
GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
|
||||
if (memory->mbcType != GB_MBC6 && memory->mbcType != GB_UNL_NT_NEW) {
|
||||
GBMBCSwitchBank(gb, memory->currentBank);
|
||||
GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
|
||||
}
|
||||
|
||||
LOAD_16LE(memory->dmaSource, 0, &state->memory.dmaSource);
|
||||
LOAD_16LE(memory->dmaDest, 0, &state->memory.dmaDest);
|
||||
|
@ -882,6 +900,16 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
case GB_MBC3_RTC:
|
||||
LOAD_64LE(memory->rtcLastLatch, 0, &state->memory.rtc.lastLatch);
|
||||
break;
|
||||
case GB_MBC6:
|
||||
memory->mbcState.mbc6.flashBank0 = GBSerializedMBC6FlagsGetFlashBank0(state->memory.mbc6.flags);
|
||||
memory->mbcState.mbc6.flashBank1 = GBSerializedMBC6FlagsGetFlashBank1(state->memory.mbc6.flags);
|
||||
memory->currentBank1 = state->memory.mbc6.bank1;
|
||||
memory->currentSramBank1 = state->memory.mbc6.sramBank1;
|
||||
GBMBCSwitchHalfBank(gb, 0, memory->currentBank);
|
||||
GBMBCSwitchHalfBank(gb, 1, memory->currentBank1);
|
||||
GBMBCSwitchSramHalfBank(gb, 0, memory->sramCurrentBank);
|
||||
GBMBCSwitchSramHalfBank(gb, 1, memory->currentSramBank1);
|
||||
break;
|
||||
case GB_MBC7:
|
||||
memory->mbcState.mbc7.state = state->memory.mbc7.state;
|
||||
memory->mbcState.mbc7.eeprom = state->memory.mbc7.eeprom;
|
||||
|
@ -929,8 +957,27 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
GBMBCSwitchBank0(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 2);
|
||||
}
|
||||
break;
|
||||
case GB_UNL_NT_OLD_1:
|
||||
case GB_UNL_NT_OLD_2:
|
||||
memory->mbcState.ntOld.swapped = GBSerializedNTOldFlagsGetSwapped(state->memory.ntOld.flags);
|
||||
memory->mbcState.ntOld.rumble = GBSerializedNTOldFlagsGetRumble(state->memory.ntOld.flags);
|
||||
memory->mbcState.ntOld.baseBank = state->memory.ntOld.baseBank;
|
||||
memory->mbcState.ntOld.bankCount = state->memory.ntOld.bankCount;
|
||||
GBMBCSwitchBank0(gb, memory->mbcState.ntOld.baseBank);
|
||||
break;
|
||||
case GB_UNL_NT_NEW:
|
||||
memory->mbcState.ntNew.splitMode = state->memory.ntNew.splitMode;
|
||||
memory->currentBank1 = state->memory.ntNew.bank1;
|
||||
if (memory->mbcState.ntNew.splitMode) {
|
||||
GBMBCSwitchHalfBank(gb, 0, memory->currentBank);
|
||||
GBMBCSwitchHalfBank(gb, 1, memory->currentBank1);
|
||||
} else {
|
||||
GBMBCSwitchBank(gb, memory->currentBank);
|
||||
}
|
||||
break;
|
||||
case GB_UNL_BBD:
|
||||
case GB_UNL_HITEK:
|
||||
case GB_UNL_GGB81:
|
||||
memory->mbcState.bbd.dataSwapMode = state->memory.bbd.dataSwapMode & 0x7;
|
||||
memory->mbcState.bbd.bankSwapMode = state->memory.bbd.bankSwapMode & 0x7;
|
||||
break;
|
||||
|
|
|
@ -672,7 +672,19 @@ static const struct GBCartridgeOverride _overrides[] = {
|
|||
{ 0x630ED957, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (non-debug)
|
||||
{ 0x5AFF0038, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (debug)
|
||||
{ 0xA61856BD, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (non-debug)
|
||||
// Unlicensed bootlegs
|
||||
{ 0x30F8F86C, GB_MODEL_AUTODETECT, GB_UNL_PKJD, { 0 } }, // Pokemon Jade Version (Telefang Speed bootleg)
|
||||
{ 0xE1147E75, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_1, { 0 } }, // Rockman 8
|
||||
{ 0xEFF88FAA, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_1, { 0 } }, // True Color 25 in 1 (NT-9920)
|
||||
{ 0x811925D9, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // 23 in 1 (CR2011)
|
||||
{ 0x62A8016A, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // 29 in 1 (CR2020)
|
||||
{ 0x5758D6D9, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Caise Gedou 24 in 1 Diannao Huamian Xuan Game (CY2060)
|
||||
{ 0x62A8016A, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Caise Gedou 29 in 1 Diannao Huamian Xuan Game (CY2061)
|
||||
{ 0x80265A64, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Rockman X4 (Megaman X4)
|
||||
{ 0x805459DE, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Sonic Adventure 8
|
||||
{ 0x0B1B808A, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Super Donkey Kong 5
|
||||
{ 0x0B1B808A, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Super Donkey Kong 5 (Alt)
|
||||
{ 0x4650EB9A, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Super Mario Special 3
|
||||
{ 0xB289D95A, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Capcom vs SNK - Millennium Fight 2001
|
||||
{ 0x688D6713, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Digimon 02 4
|
||||
{ 0x8931A272, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Digimon 2
|
||||
|
@ -683,6 +695,7 @@ static const struct GBCartridgeOverride _overrides[] = {
|
|||
{ 0xBC75D7B8, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Pokemon - Mewtwo Strikes Back
|
||||
{ 0xFF0B60CC, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Shuma Baolong 02 4
|
||||
{ 0x14A992A6, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // /Street Fighter Zero 4
|
||||
{ 0x3EF5AFB2, GB_MODEL_AUTODETECT, GB_UNL_LI_CHENG, { 0 } }, // Pokemon Jade Version (Telefang Speed bootleg)
|
||||
|
||||
{ 0, 0, 0, { 0 } }
|
||||
};
|
||||
|
|
|
@ -157,7 +157,7 @@ static int32_t _masterUpdate(struct GBSIOLockstepNode* node) {
|
|||
}
|
||||
}
|
||||
// Tell the other GBs they can continue up to where we were
|
||||
node->p->d.addCycles(&node->p->d, node->id, node->eventDiff);
|
||||
node->p->d.addCycles(&node->p->d, 0, node->eventDiff);
|
||||
#ifndef NDEBUG
|
||||
node->phase = node->p->d.transferActive;
|
||||
#endif
|
||||
|
@ -169,26 +169,28 @@ static int32_t _masterUpdate(struct GBSIOLockstepNode* node) {
|
|||
|
||||
static uint32_t _slaveUpdate(struct GBSIOLockstepNode* node) {
|
||||
enum mLockstepPhase transferActive;
|
||||
int id;
|
||||
|
||||
ATOMIC_LOAD(transferActive, node->p->d.transferActive);
|
||||
ATOMIC_LOAD(id, node->id);
|
||||
|
||||
bool signal = false;
|
||||
switch (transferActive) {
|
||||
case TRANSFER_IDLE:
|
||||
node->p->d.addCycles(&node->p->d, node->id, LOCKSTEP_INCREMENT);
|
||||
node->p->d.addCycles(&node->p->d, id, LOCKSTEP_INCREMENT);
|
||||
break;
|
||||
case TRANSFER_STARTING:
|
||||
case TRANSFER_FINISHING:
|
||||
break;
|
||||
case TRANSFER_STARTED:
|
||||
if (node->p->d.unusedCycles(&node->p->d, node->id) > node->eventDiff) {
|
||||
if (node->p->d.unusedCycles(&node->p->d, id) > node->eventDiff) {
|
||||
break;
|
||||
}
|
||||
node->transferFinished = false;
|
||||
signal = true;
|
||||
break;
|
||||
case TRANSFER_FINISHED:
|
||||
if (node->p->d.unusedCycles(&node->p->d, node->id) > node->eventDiff) {
|
||||
if (node->p->d.unusedCycles(&node->p->d, id) > node->eventDiff) {
|
||||
break;
|
||||
}
|
||||
_finishTransfer(node);
|
||||
|
@ -199,7 +201,7 @@ static uint32_t _slaveUpdate(struct GBSIOLockstepNode* node) {
|
|||
node->phase = node->p->d.transferActive;
|
||||
#endif
|
||||
if (signal) {
|
||||
node->p->d.signal(&node->p->d, 1 << node->id);
|
||||
node->p->d.signal(&node->p->d, 1 << id);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -215,11 +217,13 @@ static void _GBSIOLockstepNodeProcessEvents(struct mTiming* timing, void* user,
|
|||
int32_t cycles = 0;
|
||||
node->nextEvent -= cyclesLate;
|
||||
if (node->nextEvent <= 0) {
|
||||
if (!node->id) {
|
||||
int id;
|
||||
ATOMIC_LOAD(id, node->id);
|
||||
if (!id) {
|
||||
cycles = _masterUpdate(node);
|
||||
} else {
|
||||
cycles = _slaveUpdate(node);
|
||||
cycles += node->p->d.useCycles(&node->p->d, node->id, node->eventDiff);
|
||||
cycles += node->p->d.useCycles(&node->p->d, id, node->eventDiff);
|
||||
}
|
||||
node->eventDiff = 0;
|
||||
} else {
|
||||
|
@ -240,7 +244,9 @@ static void _GBSIOLockstepNodeProcessEvents(struct mTiming* timing, void* user,
|
|||
|
||||
static void GBSIOLockstepNodeWriteSB(struct GBSIODriver* driver, uint8_t value) {
|
||||
struct GBSIOLockstepNode* node = (struct GBSIOLockstepNode*) driver;
|
||||
node->p->pendingSB[node->id] = value;
|
||||
int id;
|
||||
ATOMIC_LOAD(id, node->id);
|
||||
node->p->pendingSB[id] = value;
|
||||
}
|
||||
|
||||
static uint8_t GBSIOLockstepNodeWriteSC(struct GBSIODriver* driver, uint8_t value) {
|
||||
|
@ -252,11 +258,17 @@ static uint8_t GBSIOLockstepNodeWriteSC(struct GBSIODriver* driver, uint8_t valu
|
|||
mLockstepLock(&node->p->d);
|
||||
bool claimed = false;
|
||||
if (ATOMIC_CMPXCHG(node->p->masterClaimed, claimed, true)) {
|
||||
if (node->id != 0) {
|
||||
int id;
|
||||
ATOMIC_LOAD(id, node->id);
|
||||
if (id != 0) {
|
||||
unsigned sb;
|
||||
node->p->players[0]->id = 1;
|
||||
node->p->players[1] = node->p->players[0];
|
||||
node->p->players[0] = node->p->players[1];
|
||||
node->id = 0;
|
||||
node->p->players[1] = node->p->players[0];
|
||||
node->p->players[0] = node;
|
||||
sb = node->p->pendingSB[0];
|
||||
node->p->pendingSB[0] = node->p->pendingSB[1];
|
||||
node->p->pendingSB[1] = sb;
|
||||
}
|
||||
ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING);
|
||||
ATOMIC_STORE(node->p->d.transferCycles, GBSIOCyclesPerTransfer[(value >> 1) & 1]);
|
||||
|
|
|
@ -118,10 +118,10 @@ void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA*
|
|||
info->reg = GBADMARegisterSetDestControl(info->reg, GBA_DMA_FIXED);
|
||||
info->reg = GBADMARegisterSetWidth(info->reg, 1);
|
||||
switch (info->dest) {
|
||||
case BASE_IO | REG_FIFO_A_LO:
|
||||
case GBA_BASE_IO | REG_FIFO_A_LO:
|
||||
audio->chA.dmaSource = number;
|
||||
break;
|
||||
case BASE_IO | REG_FIFO_B_LO:
|
||||
case GBA_BASE_IO | REG_FIFO_B_LO:
|
||||
audio->chB.dmaSource = number;
|
||||
break;
|
||||
default:
|
||||
|
|
242
src/gba/bios.c
|
@ -49,11 +49,11 @@ static void _SoftReset(struct GBA* gba) {
|
|||
cpu->gprs[ARM_LR] = 0;
|
||||
cpu->gprs[ARM_SP] = GBA_SP_BASE_SYSTEM;
|
||||
int8_t flag = ((int8_t*) gba->memory.iwram)[0x7FFA];
|
||||
memset(((int8_t*) gba->memory.iwram) + SIZE_WORKING_IRAM - 0x200, 0, 0x200);
|
||||
memset(((int8_t*) gba->memory.iwram) + GBA_SIZE_IWRAM - 0x200, 0, 0x200);
|
||||
if (flag) {
|
||||
cpu->gprs[ARM_PC] = BASE_WORKING_RAM;
|
||||
cpu->gprs[ARM_PC] = GBA_BASE_EWRAM;
|
||||
} else {
|
||||
cpu->gprs[ARM_PC] = BASE_CART0;
|
||||
cpu->gprs[ARM_PC] = GBA_BASE_ROM0;
|
||||
}
|
||||
_ARMSetMode(cpu, MODE_ARM);
|
||||
ARMWritePC(cpu);
|
||||
|
@ -62,120 +62,120 @@ static void _SoftReset(struct GBA* gba) {
|
|||
static void _RegisterRamReset(struct GBA* gba) {
|
||||
uint32_t registers = gba->cpu->gprs[0];
|
||||
struct ARMCore* cpu = gba->cpu;
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DISPCNT, 0x0080, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DISPCNT, 0x0080, 0);
|
||||
if (registers & 0x01) {
|
||||
memset(gba->memory.wram, 0, SIZE_WORKING_RAM);
|
||||
memset(gba->memory.wram, 0, GBA_SIZE_EWRAM);
|
||||
}
|
||||
if (registers & 0x02) {
|
||||
memset(gba->memory.iwram, 0, SIZE_WORKING_IRAM - 0x200);
|
||||
memset(gba->memory.iwram, 0, GBA_SIZE_IWRAM - 0x200);
|
||||
}
|
||||
if (registers & 0x04) {
|
||||
memset(gba->video.palette, 0, SIZE_PALETTE_RAM);
|
||||
memset(gba->video.palette, 0, GBA_SIZE_PALETTE_RAM);
|
||||
}
|
||||
if (registers & 0x08) {
|
||||
memset(gba->video.vram, 0, SIZE_VRAM);
|
||||
memset(gba->video.vram, 0, GBA_SIZE_VRAM);
|
||||
}
|
||||
if (registers & 0x10) {
|
||||
memset(gba->video.oam.raw, 0, SIZE_OAM);
|
||||
memset(gba->video.oam.raw, 0, GBA_SIZE_OAM);
|
||||
}
|
||||
if (registers & 0x20) {
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SIOCNT, 0x0000, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_RCNT, RCNT_INITIAL, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SIOMLT_SEND, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_JOYCNT, 0, 0);
|
||||
cpu->memory.store32(cpu, BASE_IO | REG_JOY_RECV_LO, 0, 0);
|
||||
cpu->memory.store32(cpu, BASE_IO | REG_JOY_TRANS_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_SIOCNT, 0x0000, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_RCNT, RCNT_INITIAL, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_SIOMLT_SEND, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_JOYCNT, 0, 0);
|
||||
cpu->memory.store32(cpu, GBA_BASE_IO | REG_JOY_RECV_LO, 0, 0);
|
||||
cpu->memory.store32(cpu, GBA_BASE_IO | REG_JOY_TRANS_LO, 0, 0);
|
||||
}
|
||||
if (registers & 0x40) {
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUND1CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUND1CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUND1CNT_X, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUND2CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUND2CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUND3CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUND3CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUND3CNT_X, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUND4CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUND4CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUNDCNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUNDCNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUNDCNT_X, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_SOUNDBIAS, 0x200, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND1CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND1CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND1CNT_X, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND2CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND2CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND3CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND3CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND3CNT_X, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND4CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND4CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUNDCNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUNDCNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUNDCNT_X, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUNDBIAS, 0x200, 0);
|
||||
memset(gba->audio.psg.ch3.wavedata32, 0, sizeof(gba->audio.psg.ch3.wavedata32));
|
||||
}
|
||||
if (registers & 0x80) {
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DISPSTAT, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_VCOUNT, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG0CNT, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG1CNT, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG2CNT, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG3CNT, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG0HOFS, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG0VOFS, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG1HOFS, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG1VOFS, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG2HOFS, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG2VOFS, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG3HOFS, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG3VOFS, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG2PA, 0x100, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG2PB, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG2PC, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG2PD, 0x100, 0);
|
||||
cpu->memory.store32(cpu, BASE_IO | REG_BG2X_LO, 0, 0);
|
||||
cpu->memory.store32(cpu, BASE_IO | REG_BG2Y_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG3PA, 0x100, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG3PB, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG3PC, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BG3PD, 0x100, 0);
|
||||
cpu->memory.store32(cpu, BASE_IO | REG_BG3X_LO, 0, 0);
|
||||
cpu->memory.store32(cpu, BASE_IO | REG_BG3Y_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_WIN0H, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_WIN1H, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_WIN0V, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_WIN1V, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_WININ, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_WINOUT, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_MOSAIC, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BLDCNT, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BLDALPHA, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_BLDY, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA0SAD_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA0SAD_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA0DAD_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA0DAD_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA0CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA0CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA1SAD_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA1SAD_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA1DAD_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA1DAD_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA1CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA1CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA2SAD_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA2SAD_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA2DAD_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA2DAD_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA2CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA2CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA3SAD_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA3SAD_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA3DAD_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA3DAD_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA3CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_DMA3CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_TM0CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_TM0CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_TM1CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_TM1CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_TM2CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_TM2CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_TM3CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_TM3CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_IE, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_IF, 0xFFFF, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_WAITCNT, 0, 0);
|
||||
cpu->memory.store16(cpu, BASE_IO | REG_IME, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DISPSTAT, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_VCOUNT, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG0CNT, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG1CNT, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG2CNT, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG3CNT, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG0HOFS, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG0VOFS, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG1HOFS, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG1VOFS, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG2HOFS, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG2VOFS, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG3HOFS, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG3VOFS, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG2PA, 0x100, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG2PB, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG2PC, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG2PD, 0x100, 0);
|
||||
cpu->memory.store32(cpu, GBA_BASE_IO | REG_BG2X_LO, 0, 0);
|
||||
cpu->memory.store32(cpu, GBA_BASE_IO | REG_BG2Y_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG3PA, 0x100, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG3PB, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG3PC, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG3PD, 0x100, 0);
|
||||
cpu->memory.store32(cpu, GBA_BASE_IO | REG_BG3X_LO, 0, 0);
|
||||
cpu->memory.store32(cpu, GBA_BASE_IO | REG_BG3Y_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_WIN0H, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_WIN1H, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_WIN0V, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_WIN1V, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_WININ, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_WINOUT, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_MOSAIC, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_BLDCNT, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_BLDALPHA, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_BLDY, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA0SAD_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA0SAD_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA0DAD_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA0DAD_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA0CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA0CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA1SAD_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA1SAD_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA1DAD_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA1DAD_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA1CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA1CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA2SAD_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA2SAD_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA2DAD_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA2DAD_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA2CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA2CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA3SAD_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA3SAD_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA3DAD_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA3DAD_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA3CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA3CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_TM0CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_TM0CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_TM1CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_TM1CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_TM2CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_TM2CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_TM3CNT_LO, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_TM3CNT_HI, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_IE, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_IF, 0xFFFF, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_WAITCNT, 0, 0);
|
||||
cpu->memory.store16(cpu, GBA_BASE_IO | REG_IME, 0, 0);
|
||||
}
|
||||
if (registers & 0x9C) {
|
||||
gba->video.renderer->reset(gba->video.renderer);
|
||||
|
@ -267,7 +267,7 @@ static void _MidiKey2Freq(struct GBA* gba) {
|
|||
struct ARMCore* cpu = gba->cpu;
|
||||
|
||||
int oldRegion = gba->memory.activeRegion;
|
||||
gba->memory.activeRegion = REGION_BIOS;
|
||||
gba->memory.activeRegion = GBA_REGION_BIOS;
|
||||
uint32_t key = cpu->memory.load32(cpu, cpu->gprs[0] + 4, 0);
|
||||
gba->memory.activeRegion = oldRegion;
|
||||
|
||||
|
@ -336,12 +336,14 @@ static int16_t _ArcTan(int32_t i, int32_t* r1, int32_t* r3, uint32_t* cycles) {
|
|||
|
||||
static int16_t _ArcTan2(int32_t x, int32_t y, int32_t* r1, uint32_t* cycles) {
|
||||
if (!y) {
|
||||
*cycles = 11;
|
||||
if (x >= 0) {
|
||||
return 0;
|
||||
}
|
||||
return 0x8000;
|
||||
}
|
||||
if (!x) {
|
||||
*cycles = 11;
|
||||
if (y >= 0) {
|
||||
return 0x4000;
|
||||
}
|
||||
|
@ -484,7 +486,7 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
|
|||
break;
|
||||
case GBA_SWI_CPU_SET:
|
||||
case GBA_SWI_CPU_FAST_SET:
|
||||
if (cpu->gprs[0] >> BASE_OFFSET < REGION_WORKING_RAM) {
|
||||
if (cpu->gprs[0] >> BASE_OFFSET < GBA_REGION_EWRAM) {
|
||||
mLOG(GBA_BIOS, GAME_ERROR, "Cannot CpuSet from BIOS");
|
||||
break;
|
||||
}
|
||||
|
@ -499,7 +501,7 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
|
|||
case GBA_SWI_GET_BIOS_CHECKSUM:
|
||||
cpu->gprs[0] = GBA_BIOS_CHECKSUM;
|
||||
cpu->gprs[1] = 1;
|
||||
cpu->gprs[3] = SIZE_BIOS;
|
||||
cpu->gprs[3] = GBA_SIZE_BIOS;
|
||||
break;
|
||||
case GBA_SWI_BG_AFFINE_SET:
|
||||
_BgAffineSet(gba);
|
||||
|
@ -508,7 +510,7 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
|
|||
_ObjAffineSet(gba);
|
||||
break;
|
||||
case GBA_SWI_BIT_UNPACK:
|
||||
if (cpu->gprs[0] < BASE_WORKING_RAM) {
|
||||
if (cpu->gprs[0] < GBA_BASE_EWRAM) {
|
||||
mLOG(GBA_BIOS, GAME_ERROR, "Bad BitUnPack source");
|
||||
break;
|
||||
}
|
||||
|
@ -516,9 +518,9 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
|
|||
default:
|
||||
mLOG(GBA_BIOS, GAME_ERROR, "Bad BitUnPack destination");
|
||||
// Fall through
|
||||
case REGION_WORKING_RAM:
|
||||
case REGION_WORKING_IRAM:
|
||||
case REGION_VRAM:
|
||||
case GBA_REGION_EWRAM:
|
||||
case GBA_REGION_IWRAM:
|
||||
case GBA_REGION_VRAM:
|
||||
_unBitPack(gba);
|
||||
break;
|
||||
}
|
||||
|
@ -533,9 +535,9 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
|
|||
default:
|
||||
mLOG(GBA_BIOS, GAME_ERROR, "Bad LZ77 destination");
|
||||
// Fall through
|
||||
case REGION_WORKING_RAM:
|
||||
case REGION_WORKING_IRAM:
|
||||
case REGION_VRAM:
|
||||
case GBA_REGION_EWRAM:
|
||||
case GBA_REGION_IWRAM:
|
||||
case GBA_REGION_VRAM:
|
||||
useStall = true;
|
||||
_unLz77(gba, immediate == GBA_SWI_LZ77_UNCOMP_WRAM ? 1 : 2);
|
||||
break;
|
||||
|
@ -550,9 +552,9 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
|
|||
default:
|
||||
mLOG(GBA_BIOS, GAME_ERROR, "Bad Huffman destination");
|
||||
// Fall through
|
||||
case REGION_WORKING_RAM:
|
||||
case REGION_WORKING_IRAM:
|
||||
case REGION_VRAM:
|
||||
case GBA_REGION_EWRAM:
|
||||
case GBA_REGION_IWRAM:
|
||||
case GBA_REGION_VRAM:
|
||||
_unHuffman(gba);
|
||||
break;
|
||||
}
|
||||
|
@ -567,9 +569,9 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
|
|||
default:
|
||||
mLOG(GBA_BIOS, GAME_ERROR, "Bad RL destination");
|
||||
// Fall through
|
||||
case REGION_WORKING_RAM:
|
||||
case REGION_WORKING_IRAM:
|
||||
case REGION_VRAM:
|
||||
case GBA_REGION_EWRAM:
|
||||
case GBA_REGION_IWRAM:
|
||||
case GBA_REGION_VRAM:
|
||||
_unRl(gba, immediate == GBA_SWI_RL_UNCOMP_WRAM ? 1 : 2);
|
||||
break;
|
||||
}
|
||||
|
@ -585,9 +587,9 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
|
|||
default:
|
||||
mLOG(GBA_BIOS, GAME_ERROR, "Bad UnFilter destination");
|
||||
// Fall through
|
||||
case REGION_WORKING_RAM:
|
||||
case REGION_WORKING_IRAM:
|
||||
case REGION_VRAM:
|
||||
case GBA_REGION_EWRAM:
|
||||
case GBA_REGION_IWRAM:
|
||||
case GBA_REGION_VRAM:
|
||||
_unFilter(gba, immediate == GBA_SWI_DIFF_16BIT_UNFILTER ? 2 : 1, immediate == GBA_SWI_DIFF_8BIT_UNFILTER_WRAM ? 1 : 2);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -246,7 +246,7 @@ void GBAVFameSramWrite(struct GBAVFameCart* cart, uint32_t address, uint8_t valu
|
|||
// if mode has been set - the address and value of the SRAM write will be modified
|
||||
address = _modifySramAddress(cart->cartType, address, cart->sramMode);
|
||||
value = _modifySramValue(cart->cartType, value, cart->sramMode);
|
||||
address &= (SIZE_CART_SRAM - 1);
|
||||
address &= (GBA_SIZE_SRAM - 1);
|
||||
sramData[address] = value;
|
||||
}
|
||||
|
||||
|
|
|
@ -105,36 +105,36 @@ static bool GBACheatAddAutodetect(struct GBACheatSet* set, uint32_t op1, uint32_
|
|||
char line[18] = "XXXXXXXX XXXXXXXX";
|
||||
snprintf(line, sizeof(line), "%08X %08X", op1, op2);
|
||||
|
||||
int gsaP, rgsaP, parP, rparP;
|
||||
int nextProbability;
|
||||
int maxProbability = INT_MIN;
|
||||
switch (set->gsaVersion) {
|
||||
case 0:
|
||||
// Try to detect GameShark version
|
||||
GBACheatDecryptGameShark(&o1, &o2, GBACheatGameSharkSeeds);
|
||||
gsaP = GBACheatGameSharkProbability(o1, o2);
|
||||
nextProbability = GBACheatGameSharkProbability(o1, o2);
|
||||
o1 = op1;
|
||||
o2 = op2;
|
||||
if (gsaP > maxProbability) {
|
||||
maxProbability = gsaP;
|
||||
if (nextProbability > maxProbability) {
|
||||
maxProbability = nextProbability;
|
||||
GBACheatSetGameSharkVersion(set, GBA_GS_GSAV1);
|
||||
}
|
||||
|
||||
GBACheatDecryptGameShark(&o1, &o2, GBACheatProActionReplaySeeds);
|
||||
parP = GBACheatProActionReplayProbability(o1, o2);
|
||||
if (parP > maxProbability) {
|
||||
maxProbability = parP;
|
||||
nextProbability = GBACheatProActionReplayProbability(o1, o2);
|
||||
if (nextProbability > maxProbability) {
|
||||
maxProbability = nextProbability;
|
||||
GBACheatSetGameSharkVersion(set, GBA_GS_PARV3);
|
||||
}
|
||||
|
||||
rgsaP = GBACheatGameSharkProbability(op1, op1);
|
||||
if (rgsaP > maxProbability) {
|
||||
maxProbability = rgsaP;
|
||||
nextProbability = GBACheatGameSharkProbability(op1, op2);
|
||||
if (nextProbability > maxProbability) {
|
||||
maxProbability = nextProbability;
|
||||
GBACheatSetGameSharkVersion(set, GBA_GS_GSAV1_RAW);
|
||||
}
|
||||
|
||||
rparP = GBACheatProActionReplayProbability(op1, op1);
|
||||
if (rparP > maxProbability) {
|
||||
maxProbability = rparP;
|
||||
nextProbability = GBACheatProActionReplayProbability(op1, op2);
|
||||
if (nextProbability > maxProbability) {
|
||||
maxProbability = nextProbability;
|
||||
GBACheatSetGameSharkVersion(set, GBA_GS_PARV3_RAW);
|
||||
}
|
||||
|
||||
|
@ -323,49 +323,49 @@ static void GBACheatDumpDirectives(struct mCheatSet* set, struct StringList* dir
|
|||
|
||||
int GBACheatAddressIsReal(uint32_t address) {
|
||||
switch (address >> BASE_OFFSET) {
|
||||
case REGION_BIOS:
|
||||
case GBA_REGION_BIOS:
|
||||
return -0x80;
|
||||
break;
|
||||
case REGION_WORKING_RAM:
|
||||
if ((address & OFFSET_MASK) > SIZE_WORKING_RAM) {
|
||||
case GBA_REGION_EWRAM:
|
||||
if ((address & OFFSET_MASK) > GBA_SIZE_EWRAM) {
|
||||
return -0x40;
|
||||
}
|
||||
return 0x20;
|
||||
case REGION_WORKING_IRAM:
|
||||
if ((address & OFFSET_MASK) > SIZE_WORKING_IRAM) {
|
||||
case GBA_REGION_IWRAM:
|
||||
if ((address & OFFSET_MASK) > GBA_SIZE_IWRAM) {
|
||||
return -0x40;
|
||||
}
|
||||
return 0x20;
|
||||
case REGION_IO:
|
||||
if ((address & OFFSET_MASK) > SIZE_IO) {
|
||||
case GBA_REGION_IO:
|
||||
if ((address & OFFSET_MASK) > GBA_SIZE_IO) {
|
||||
return -0x80;
|
||||
}
|
||||
return 0x10;
|
||||
case REGION_OAM:
|
||||
if ((address & OFFSET_MASK) > SIZE_OAM) {
|
||||
case GBA_REGION_OAM:
|
||||
if ((address & OFFSET_MASK) > GBA_SIZE_OAM) {
|
||||
return -0x80;
|
||||
}
|
||||
return -0x8;
|
||||
case REGION_VRAM:
|
||||
if ((address & OFFSET_MASK) > SIZE_VRAM) {
|
||||
case GBA_REGION_VRAM:
|
||||
if ((address & OFFSET_MASK) > GBA_SIZE_VRAM) {
|
||||
return -0x80;
|
||||
}
|
||||
return -0x8;
|
||||
case REGION_PALETTE_RAM:
|
||||
if ((address & OFFSET_MASK) > SIZE_PALETTE_RAM) {
|
||||
case GBA_REGION_PALETTE_RAM:
|
||||
if ((address & OFFSET_MASK) > GBA_SIZE_PALETTE_RAM) {
|
||||
return -0x80;
|
||||
}
|
||||
return -0x8;
|
||||
case REGION_CART0:
|
||||
case REGION_CART0_EX:
|
||||
case REGION_CART1:
|
||||
case REGION_CART1_EX:
|
||||
case REGION_CART2:
|
||||
case REGION_CART2_EX:
|
||||
case GBA_REGION_ROM0:
|
||||
case GBA_REGION_ROM0_EX:
|
||||
case GBA_REGION_ROM1:
|
||||
case GBA_REGION_ROM1_EX:
|
||||
case GBA_REGION_ROM2:
|
||||
case GBA_REGION_ROM2_EX:
|
||||
return -0x8;
|
||||
case REGION_CART_SRAM:
|
||||
case REGION_CART_SRAM_MIRROR:
|
||||
if ((address & OFFSET_MASK) > SIZE_CART_FLASH512) {
|
||||
case GBA_REGION_SRAM:
|
||||
case GBA_REGION_SRAM_MIRROR:
|
||||
if ((address & OFFSET_MASK) > GBA_SIZE_FLASH512) {
|
||||
return -0x80;
|
||||
}
|
||||
return -0x8;
|
||||
|
|
|
@ -215,7 +215,7 @@ bool GBACheatAddCodeBreaker(struct GBACheatSet* cheats, uint32_t op1, uint16_t o
|
|||
return false;
|
||||
}
|
||||
cheats->hook = malloc(sizeof(*cheats->hook));
|
||||
cheats->hook->address = BASE_CART0 | (op1 & (SIZE_CART0 - 1));
|
||||
cheats->hook->address = GBA_BASE_ROM0 | (op1 & (GBA_SIZE_ROM0 - 1));
|
||||
cheats->hook->mode = MODE_THUMB;
|
||||
cheats->hook->refs = 1;
|
||||
cheats->hook->reentries = 0;
|
||||
|
@ -278,7 +278,7 @@ bool GBACheatAddCodeBreaker(struct GBACheatSet* cheats, uint32_t op1, uint16_t o
|
|||
cheat = mCheatListAppend(&cheats->d.list);
|
||||
cheat->type = CHEAT_IF_NAND;
|
||||
cheat->width = 2;
|
||||
cheat->address = BASE_IO | REG_KEYINPUT;
|
||||
cheat->address = GBA_BASE_IO | REG_KEYINPUT;
|
||||
cheat->operand = op2;
|
||||
cheat->repeat = 1;
|
||||
return true;
|
||||
|
|
|
@ -150,7 +150,7 @@ bool GBACheatAddGameSharkRaw(struct GBACheatSet* cheats, uint32_t op1, uint32_t
|
|||
break;
|
||||
case GSA_PATCH:
|
||||
romPatch = mCheatPatchListAppend(&cheats->d.romPatches);
|
||||
romPatch->address = BASE_CART0 | ((op1 & 0xFFFFFF) << 1);
|
||||
romPatch->address = GBA_BASE_ROM0 | ((op1 & 0xFFFFFF) << 1);
|
||||
romPatch->value = op2;
|
||||
romPatch->applied = false;
|
||||
romPatch->width = 2;
|
||||
|
@ -207,7 +207,7 @@ bool GBACheatAddGameSharkRaw(struct GBACheatSet* cheats, uint32_t op1, uint32_t
|
|||
return false;
|
||||
}
|
||||
cheats->hook = malloc(sizeof(*cheats->hook));
|
||||
cheats->hook->address = BASE_CART0 | (op1 & (SIZE_CART0 - 1));
|
||||
cheats->hook->address = GBA_BASE_ROM0 | (op1 & (GBA_SIZE_ROM0 - 1));
|
||||
cheats->hook->mode = MODE_THUMB;
|
||||
cheats->hook->refs = 1;
|
||||
cheats->hook->reentries = 0;
|
||||
|
|
|
@ -233,7 +233,7 @@ static bool _addPAR3Special(struct GBACheatSet* cheats, uint32_t op2) {
|
|||
}
|
||||
if (romPatch >= 0) {
|
||||
struct mCheatPatch* patch = mCheatPatchListAppend(&cheats->d.romPatches);
|
||||
patch->address = BASE_CART0 | ((op2 & 0xFFFFFF) << 1);
|
||||
patch->address = GBA_BASE_ROM0 | ((op2 & 0xFFFFFF) << 1);
|
||||
patch->applied = false;
|
||||
patch->check = false;
|
||||
patch->width = 2;
|
||||
|
@ -282,7 +282,7 @@ bool GBACheatAddProActionReplayRaw(struct GBACheatSet* cheats, uint32_t op1, uin
|
|||
return false;
|
||||
}
|
||||
cheats->hook = malloc(sizeof(*cheats->hook));
|
||||
cheats->hook->address = BASE_CART0 | (op1 & (SIZE_CART0 - 2));
|
||||
cheats->hook->address = GBA_BASE_ROM0 | (op1 & (GBA_SIZE_ROM0 - 2));
|
||||
cheats->hook->mode = MODE_THUMB;
|
||||
cheats->hook->refs = 1;
|
||||
cheats->hook->reentries = 0;
|
||||
|
@ -320,7 +320,7 @@ bool GBACheatAddProActionReplayRaw(struct GBACheatSet* cheats, uint32_t op1, uin
|
|||
case PAR3_BASE_OTHER:
|
||||
width = ((op1 >> 24) & 1) + 1;
|
||||
cheat->type = CHEAT_ASSIGN;
|
||||
cheat->address = BASE_IO | (op1 & OFFSET_MASK);
|
||||
cheat->address = GBA_BASE_IO | (op1 & OFFSET_MASK);
|
||||
break;
|
||||
}
|
||||
if (op1 & 0x01000000 && (op1 & 0xFE000000) != 0xC6000000) {
|
||||
|
|
168
src/gba/core.c
|
@ -57,76 +57,76 @@ static const struct mCoreChannelInfo _GBAAudioChannels[] = {
|
|||
|
||||
static const struct mCoreMemoryBlock _GBAMemoryBlocks[] = {
|
||||
{ -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL },
|
||||
{ REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_WORKING_IRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, BASE_WORKING_IRAM + SIZE_WORKING_IRAM, SIZE_WORKING_IRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_IO, "io", "MMIO", "Memory-Mapped I/O", BASE_IO, BASE_IO + SIZE_IO, SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", GBA_BASE_BIOS, GBA_SIZE_BIOS, GBA_SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_EWRAM, "wram", "EWRAM", "Working RAM (256kiB)", GBA_BASE_EWRAM, GBA_BASE_EWRAM + GBA_SIZE_EWRAM, GBA_SIZE_EWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_IWRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", GBA_BASE_IWRAM, GBA_BASE_IWRAM + GBA_SIZE_IWRAM, GBA_SIZE_IWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_IO, "io", "MMIO", "Memory-Mapped I/O", GBA_BASE_IO, GBA_BASE_IO + GBA_SIZE_IO, GBA_SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", GBA_BASE_PALETTE_RAM, GBA_BASE_PALETTE_RAM + GBA_SIZE_PALETTE_RAM, GBA_SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", GBA_BASE_VRAM, GBA_BASE_VRAM + GBA_SIZE_VRAM, GBA_SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", GBA_BASE_OAM, GBA_BASE_OAM + GBA_SIZE_OAM, GBA_SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_ROM0, "cart0", "ROM", "Game Pak (32MiB)", GBA_BASE_ROM0, GBA_BASE_ROM0 + GBA_SIZE_ROM0, GBA_SIZE_ROM0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_ROM1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", GBA_BASE_ROM1, GBA_BASE_ROM1 + GBA_SIZE_ROM1, GBA_SIZE_ROM1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_ROM2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", GBA_BASE_ROM2, GBA_BASE_ROM2 + GBA_SIZE_ROM2, GBA_SIZE_ROM2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
};
|
||||
|
||||
static const struct mCoreMemoryBlock _GBAMemoryBlocksSRAM[] = {
|
||||
{ -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL },
|
||||
{ REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_WORKING_IRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, BASE_WORKING_IRAM + SIZE_WORKING_IRAM, SIZE_WORKING_IRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_IO, "io", "MMIO", "Memory-Mapped I/O", BASE_IO, BASE_IO + SIZE_IO, SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART_SRAM, "sram", "SRAM", "Static RAM (64kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_SRAM, SIZE_CART_SRAM, true },
|
||||
{ GBA_REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", GBA_BASE_BIOS, GBA_SIZE_BIOS, GBA_SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_EWRAM, "wram", "EWRAM", "Working RAM (256kiB)", GBA_BASE_EWRAM, GBA_BASE_EWRAM + GBA_SIZE_EWRAM, GBA_SIZE_EWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_IWRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", GBA_BASE_IWRAM, GBA_BASE_IWRAM + GBA_SIZE_IWRAM, GBA_SIZE_IWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_IO, "io", "MMIO", "Memory-Mapped I/O", GBA_BASE_IO, GBA_BASE_IO + GBA_SIZE_IO, GBA_SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", GBA_BASE_PALETTE_RAM, GBA_BASE_PALETTE_RAM + GBA_SIZE_PALETTE_RAM, GBA_SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", GBA_BASE_VRAM, GBA_BASE_VRAM + GBA_SIZE_VRAM, GBA_SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", GBA_BASE_OAM, GBA_BASE_OAM + GBA_SIZE_OAM, GBA_SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_ROM0, "cart0", "ROM", "Game Pak (32MiB)", GBA_BASE_ROM0, GBA_BASE_ROM0 + GBA_SIZE_ROM0, GBA_SIZE_ROM0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_ROM1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", GBA_BASE_ROM1, GBA_BASE_ROM1 + GBA_SIZE_ROM1, GBA_SIZE_ROM1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_ROM2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", GBA_BASE_ROM2, GBA_BASE_ROM2 + GBA_SIZE_ROM2, GBA_SIZE_ROM2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_SRAM, "sram", "SRAM", "Static RAM (64kiB)", GBA_BASE_SRAM, GBA_BASE_SRAM + GBA_SIZE_SRAM, GBA_SIZE_SRAM, true },
|
||||
};
|
||||
|
||||
static const struct mCoreMemoryBlock _GBAMemoryBlocksFlash512[] = {
|
||||
{ -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL },
|
||||
{ REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_WORKING_IRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, BASE_WORKING_IRAM + SIZE_WORKING_IRAM, SIZE_WORKING_IRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_IO, "io", "MMIO", "Memory-Mapped I/O", BASE_IO, BASE_IO + SIZE_IO, SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART_SRAM, "sram", "Flash", "Flash Memory (64kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_FLASH512, SIZE_CART_FLASH512, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", GBA_BASE_BIOS, GBA_SIZE_BIOS, GBA_SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_EWRAM, "wram", "EWRAM", "Working RAM (256kiB)", GBA_BASE_EWRAM, GBA_BASE_EWRAM + GBA_SIZE_EWRAM, GBA_SIZE_EWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_IWRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", GBA_BASE_IWRAM, GBA_BASE_IWRAM + GBA_SIZE_IWRAM, GBA_SIZE_IWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_IO, "io", "MMIO", "Memory-Mapped I/O", GBA_BASE_IO, GBA_BASE_IO + GBA_SIZE_IO, GBA_SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", GBA_BASE_PALETTE_RAM, GBA_BASE_PALETTE_RAM + GBA_SIZE_PALETTE_RAM, GBA_SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", GBA_BASE_VRAM, GBA_BASE_VRAM + GBA_SIZE_VRAM, GBA_SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", GBA_BASE_OAM, GBA_BASE_OAM + GBA_SIZE_OAM, GBA_SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_ROM0, "cart0", "ROM", "Game Pak (32MiB)", GBA_BASE_ROM0, GBA_BASE_ROM0 + GBA_SIZE_ROM0, GBA_SIZE_ROM0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_ROM1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", GBA_BASE_ROM1, GBA_BASE_ROM1 + GBA_SIZE_ROM1, GBA_SIZE_ROM1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_ROM2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", GBA_BASE_ROM2, GBA_BASE_ROM2 + GBA_SIZE_ROM2, GBA_SIZE_ROM2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_SRAM, "sram", "Flash", "Flash Memory (64kiB)", GBA_BASE_SRAM, GBA_BASE_SRAM + GBA_SIZE_FLASH512, GBA_SIZE_FLASH512, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
};
|
||||
|
||||
static const struct mCoreMemoryBlock _GBAMemoryBlocksFlash1M[] = {
|
||||
{ -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL },
|
||||
{ REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_WORKING_IRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, BASE_WORKING_IRAM + SIZE_WORKING_IRAM, SIZE_WORKING_IRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_IO, "io", "MMIO", "Memory-Mapped I/O", BASE_IO, BASE_IO + SIZE_IO, SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART_SRAM, "sram", "Flash", "Flash Memory (64kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_FLASH512, SIZE_CART_FLASH1M, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 1 },
|
||||
{ GBA_REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", GBA_BASE_BIOS, GBA_SIZE_BIOS, GBA_SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_EWRAM, "wram", "EWRAM", "Working RAM (256kiB)", GBA_BASE_EWRAM, GBA_BASE_EWRAM + GBA_SIZE_EWRAM, GBA_SIZE_EWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_IWRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", GBA_BASE_IWRAM, GBA_BASE_IWRAM + GBA_SIZE_IWRAM, GBA_SIZE_IWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_IO, "io", "MMIO", "Memory-Mapped I/O", GBA_BASE_IO, GBA_BASE_IO + GBA_SIZE_IO, GBA_SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", GBA_BASE_PALETTE_RAM, GBA_BASE_PALETTE_RAM + GBA_SIZE_PALETTE_RAM, GBA_SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", GBA_BASE_VRAM, GBA_BASE_VRAM + GBA_SIZE_VRAM, GBA_SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", GBA_BASE_OAM, GBA_BASE_OAM + GBA_SIZE_OAM, GBA_SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_ROM0, "cart0", "ROM", "Game Pak (32MiB)", GBA_BASE_ROM0, GBA_BASE_ROM0 + GBA_SIZE_ROM0, GBA_SIZE_ROM0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_ROM1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", GBA_BASE_ROM1, GBA_BASE_ROM1 + GBA_SIZE_ROM1, GBA_SIZE_ROM1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_ROM2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", GBA_BASE_ROM2, GBA_BASE_ROM2 + GBA_SIZE_ROM2, GBA_SIZE_ROM2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_SRAM, "sram", "Flash", "Flash Memory (64kiB)", GBA_BASE_SRAM, GBA_BASE_SRAM + GBA_SIZE_FLASH512, GBA_SIZE_FLASH1M, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 1 },
|
||||
};
|
||||
|
||||
static const struct mCoreMemoryBlock _GBAMemoryBlocksEEPROM[] = {
|
||||
{ -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL },
|
||||
{ REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_WORKING_IRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, BASE_WORKING_IRAM + SIZE_WORKING_IRAM, SIZE_WORKING_IRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_IO, "io", "MMIO", "Memory-Mapped I/O", BASE_IO, BASE_IO + SIZE_IO, SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART_SRAM_MIRROR, "eeprom", "EEPROM", "EEPROM (8kiB)", 0, SIZE_CART_EEPROM, SIZE_CART_EEPROM, mCORE_MEMORY_RW },
|
||||
{ GBA_REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", GBA_BASE_BIOS, GBA_SIZE_BIOS, GBA_SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_EWRAM, "wram", "EWRAM", "Working RAM (256kiB)", GBA_BASE_EWRAM, GBA_BASE_EWRAM + GBA_SIZE_EWRAM, GBA_SIZE_EWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_IWRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", GBA_BASE_IWRAM, GBA_BASE_IWRAM + GBA_SIZE_IWRAM, GBA_SIZE_IWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_IO, "io", "MMIO", "Memory-Mapped I/O", GBA_BASE_IO, GBA_BASE_IO + GBA_SIZE_IO, GBA_SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", GBA_BASE_PALETTE_RAM, GBA_BASE_PALETTE_RAM + GBA_SIZE_PALETTE_RAM, GBA_SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", GBA_BASE_VRAM, GBA_BASE_VRAM + GBA_SIZE_VRAM, GBA_SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", GBA_BASE_OAM, GBA_BASE_OAM + GBA_SIZE_OAM, GBA_SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_ROM0, "cart0", "ROM", "Game Pak (32MiB)", GBA_BASE_ROM0, GBA_BASE_ROM0 + GBA_SIZE_ROM0, GBA_SIZE_ROM0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_ROM1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", GBA_BASE_ROM1, GBA_BASE_ROM1 + GBA_SIZE_ROM1, GBA_SIZE_ROM1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_ROM2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", GBA_BASE_ROM2, GBA_BASE_ROM2 + GBA_SIZE_ROM2, GBA_SIZE_ROM2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED },
|
||||
{ GBA_REGION_SRAM_MIRROR, "eeprom", "EEPROM", "EEPROM (8kiB)", 0, GBA_SIZE_EEPROM, GBA_SIZE_EEPROM, mCORE_MEMORY_RW },
|
||||
};
|
||||
|
||||
static const struct mCoreRegisterInfo _GBARegisters[] = {
|
||||
|
@ -237,7 +237,7 @@ static bool _GBACoreInit(struct mCore* core) {
|
|||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||
mDirectorySetInit(&core->dirs);
|
||||
#endif
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -512,7 +512,7 @@ static bool _GBACoreLoadROM(struct mCore* core, struct VFile* vf) {
|
|||
#ifdef USE_ELF
|
||||
struct ELF* elf = ELFOpen(vf);
|
||||
if (elf) {
|
||||
if (ELFEntry(elf) == BASE_CART0) {
|
||||
if (GBAVerifyELFEntry(elf, GBA_BASE_ROM0)) {
|
||||
GBALoadNull(core->board);
|
||||
}
|
||||
bool success = mCoreLoadELF(core, elf);
|
||||
|
@ -700,8 +700,8 @@ static void _GBACoreReset(struct mCore* core) {
|
|||
#endif
|
||||
|
||||
ARMReset(core->cpu);
|
||||
bool forceSkip = gba->romVf && GBAIsMB(gba->romVf);
|
||||
if (!(forceSkip || core->opts.skipBios) && (gba->romVf || gba->memory.rom) && gba->pristineRomSize >= 0xA0 && gba->biosVf) {
|
||||
bool forceSkip = gba->mbVf || core->opts.skipBios;
|
||||
if (!forceSkip && (gba->romVf || gba->memory.rom) && gba->pristineRomSize >= 0xA0 && gba->biosVf) {
|
||||
uint32_t crc = doCrc32(&gba->memory.rom[1], 0x9C);
|
||||
if (crc != LOGO_CRC32) {
|
||||
mLOG(STATUS, WARN, "Invalid logo, skipping BIOS");
|
||||
|
@ -709,7 +709,7 @@ static void _GBACoreReset(struct mCore* core) {
|
|||
}
|
||||
}
|
||||
|
||||
if (forceSkip || (core->opts.skipBios && (gba->romVf || gba->memory.rom))) {
|
||||
if (forceSkip) {
|
||||
GBASkipBIOS(core->board);
|
||||
}
|
||||
|
||||
|
@ -907,36 +907,36 @@ void* _GBACoreGetMemoryBlock(struct mCore* core, size_t id, size_t* sizeOut) {
|
|||
switch (id) {
|
||||
default:
|
||||
return NULL;
|
||||
case REGION_BIOS:
|
||||
*sizeOut = SIZE_BIOS;
|
||||
case GBA_REGION_BIOS:
|
||||
*sizeOut = GBA_SIZE_BIOS;
|
||||
return gba->memory.bios;
|
||||
case REGION_WORKING_RAM:
|
||||
*sizeOut = SIZE_WORKING_RAM;
|
||||
case GBA_REGION_EWRAM:
|
||||
*sizeOut = GBA_SIZE_EWRAM;
|
||||
return gba->memory.wram;
|
||||
case REGION_WORKING_IRAM:
|
||||
*sizeOut = SIZE_WORKING_IRAM;
|
||||
case GBA_REGION_IWRAM:
|
||||
*sizeOut = GBA_SIZE_IWRAM;
|
||||
return gba->memory.iwram;
|
||||
case REGION_PALETTE_RAM:
|
||||
*sizeOut = SIZE_PALETTE_RAM;
|
||||
case GBA_REGION_PALETTE_RAM:
|
||||
*sizeOut = GBA_SIZE_PALETTE_RAM;
|
||||
return gba->video.palette;
|
||||
case REGION_VRAM:
|
||||
*sizeOut = SIZE_VRAM;
|
||||
case GBA_REGION_VRAM:
|
||||
*sizeOut = GBA_SIZE_VRAM;
|
||||
return gba->video.vram;
|
||||
case REGION_OAM:
|
||||
*sizeOut = SIZE_OAM;
|
||||
case GBA_REGION_OAM:
|
||||
*sizeOut = GBA_SIZE_OAM;
|
||||
return gba->video.oam.raw;
|
||||
case REGION_CART0:
|
||||
case REGION_CART1:
|
||||
case REGION_CART2:
|
||||
case GBA_REGION_ROM0:
|
||||
case GBA_REGION_ROM1:
|
||||
case GBA_REGION_ROM2:
|
||||
*sizeOut = gba->memory.romSize;
|
||||
return gba->memory.rom;
|
||||
case REGION_CART_SRAM:
|
||||
case GBA_REGION_SRAM:
|
||||
if (gba->memory.savedata.type == SAVEDATA_FLASH1M) {
|
||||
*sizeOut = SIZE_CART_FLASH1M;
|
||||
*sizeOut = GBA_SIZE_FLASH1M;
|
||||
return gba->memory.savedata.currentBank;
|
||||
}
|
||||
// Fall through
|
||||
case REGION_CART_SRAM_MIRROR:
|
||||
case GBA_REGION_SRAM_MIRROR:
|
||||
*sizeOut = GBASavedataSize(&gba->memory.savedata);
|
||||
return gba->memory.savedata.data;
|
||||
}
|
||||
|
@ -1153,7 +1153,7 @@ static bool _GBACoreLookupIdentifier(struct mCore* core, const char* name, int32
|
|||
for (i = 0; i < REG_MAX; i += 2) {
|
||||
const char* reg = GBAIORegisterNames[i >> 1];
|
||||
if (reg && strcasecmp(reg, name) == 0) {
|
||||
*value = BASE_IO | i;
|
||||
*value = GBA_BASE_IO | i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1321,7 +1321,7 @@ static void _GBACoreStartVideoLog(struct mCore* core, struct mVideoLogContext* c
|
|||
|
||||
struct GBASerializedState* state = mVideoLogContextInitialState(context, NULL);
|
||||
state->id = 0;
|
||||
state->cpu.gprs[ARM_PC] = BASE_WORKING_RAM;
|
||||
state->cpu.gprs[ARM_PC] = GBA_BASE_EWRAM;
|
||||
|
||||
int channelId = mVideoLoggerAddChannel(context);
|
||||
gbacore->vlProxy.logger = malloc(sizeof(struct mVideoLogger));
|
||||
|
@ -1490,8 +1490,8 @@ static void _GBAVLPReset(struct mCore* core) {
|
|||
|
||||
// Make sure CPU loop never spins
|
||||
GBAHalt(gba);
|
||||
gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL);
|
||||
gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL);
|
||||
gba->cpu->memory.store16(gba->cpu, GBA_BASE_IO | REG_IME, 0, NULL);
|
||||
gba->cpu->memory.store16(gba->cpu, GBA_BASE_IO | REG_IE, 0, NULL);
|
||||
}
|
||||
|
||||
static bool _GBAVLPLoadROM(struct mCore* core, struct VFile* vf) {
|
||||
|
@ -1510,13 +1510,13 @@ static bool _GBAVLPLoadState(struct mCore* core, const void* state) {
|
|||
struct GBA* gba = (struct GBA*) core->board;
|
||||
|
||||
gba->timing.root = NULL;
|
||||
gba->cpu->gprs[ARM_PC] = BASE_WORKING_RAM;
|
||||
gba->cpu->gprs[ARM_PC] = GBA_BASE_EWRAM;
|
||||
gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
|
||||
|
||||
// Make sure CPU loop never spins
|
||||
GBAHalt(gba);
|
||||
gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL);
|
||||
gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL);
|
||||
gba->cpu->memory.store16(gba->cpu, GBA_BASE_IO | REG_IME, 0, NULL);
|
||||
gba->cpu->memory.store16(gba->cpu, GBA_BASE_IO | REG_IE, 0, NULL);
|
||||
GBAVideoDeserialize(&gba->video, state);
|
||||
GBAIODeserialize(gba, state);
|
||||
GBAAudioReset(&gba->audio);
|
||||
|
|
|
@ -33,14 +33,14 @@ void GBADMAReset(struct GBA* gba) {
|
|||
gba->memory.activeDMA = -1;
|
||||
}
|
||||
static bool _isValidDMASAD(int dma, uint32_t address) {
|
||||
if (dma == 0 && address >= BASE_CART0 && address < BASE_CART_SRAM) {
|
||||
if (dma == 0 && address >= GBA_BASE_ROM0 && address < GBA_BASE_SRAM) {
|
||||
return false;
|
||||
}
|
||||
return address >= BASE_WORKING_RAM;
|
||||
return address >= GBA_BASE_EWRAM;
|
||||
}
|
||||
|
||||
static bool _isValidDMADAD(int dma, uint32_t address) {
|
||||
return dma == 3 || address < BASE_CART0;
|
||||
return dma == 3 || address < GBA_BASE_ROM0;
|
||||
}
|
||||
|
||||
uint32_t GBADMAWriteSAD(struct GBA* gba, int dma, uint32_t address) {
|
||||
|
@ -244,7 +244,6 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
|
|||
struct GBAMemory* memory = &gba->memory;
|
||||
struct ARMCore* cpu = gba->cpu;
|
||||
uint32_t width = 2 << GBADMARegisterGetWidth(info->reg);
|
||||
int32_t wordsRemaining = info->nextCount;
|
||||
uint32_t source = info->nextSource;
|
||||
uint32_t dest = info->nextDest;
|
||||
uint32_t sourceRegion = source >> BASE_OFFSET;
|
||||
|
@ -252,6 +251,8 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
|
|||
int32_t cycles = 2;
|
||||
|
||||
gba->cpuBlocked = true;
|
||||
gba->performingDMA = 1 | (number << 1);
|
||||
|
||||
if (info->count == info->nextCount) {
|
||||
if (width == 4) {
|
||||
cycles += memory->waitstatesNonseq32[sourceRegion] + memory->waitstatesNonseq32[destRegion];
|
||||
|
@ -267,53 +268,47 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
|
|||
}
|
||||
info->when += cycles;
|
||||
|
||||
gba->performingDMA = 1 | (number << 1);
|
||||
if (width == 4) {
|
||||
if (source) {
|
||||
memory->dmaTransferRegister = cpu->memory.load32(cpu, source, 0);
|
||||
}
|
||||
gba->bus = memory->dmaTransferRegister;
|
||||
cpu->memory.store32(cpu, dest, memory->dmaTransferRegister, 0);
|
||||
} else {
|
||||
if (sourceRegion == REGION_CART2_EX && (memory->savedata.type == SAVEDATA_EEPROM || memory->savedata.type == SAVEDATA_EEPROM512)) {
|
||||
if (sourceRegion == GBA_REGION_ROM2_EX && (memory->savedata.type == SAVEDATA_EEPROM || memory->savedata.type == SAVEDATA_EEPROM512)) {
|
||||
memory->dmaTransferRegister = GBASavedataReadEEPROM(&memory->savedata);
|
||||
memory->dmaTransferRegister |= memory->dmaTransferRegister << 16;
|
||||
} else if (source) {
|
||||
memory->dmaTransferRegister = cpu->memory.load16(cpu, source, 0);
|
||||
memory->dmaTransferRegister |= memory->dmaTransferRegister << 16;
|
||||
}
|
||||
if (destRegion == REGION_CART2_EX) {
|
||||
if (destRegion == GBA_REGION_ROM2_EX) {
|
||||
if (memory->savedata.type == SAVEDATA_AUTODETECT) {
|
||||
mLOG(GBA_MEM, INFO, "Detected EEPROM savegame");
|
||||
GBASavedataInitEEPROM(&memory->savedata);
|
||||
}
|
||||
if (memory->savedata.type == SAVEDATA_EEPROM512 || memory->savedata.type == SAVEDATA_EEPROM) {
|
||||
GBASavedataWriteEEPROM(&memory->savedata, memory->dmaTransferRegister, wordsRemaining);
|
||||
GBASavedataWriteEEPROM(&memory->savedata, memory->dmaTransferRegister, info->nextCount);
|
||||
}
|
||||
} else {
|
||||
cpu->memory.store16(cpu, dest, memory->dmaTransferRegister, 0);
|
||||
|
||||
}
|
||||
gba->bus = memory->dmaTransferRegister;
|
||||
}
|
||||
gba->bus = memory->dmaTransferRegister;
|
||||
|
||||
int sourceOffset;
|
||||
if (info->nextSource >= BASE_CART0 && info->nextSource < BASE_CART_SRAM && GBADMARegisterGetSrcControl(info->reg) < 3) {
|
||||
if (info->nextSource >= GBA_BASE_ROM0 && info->nextSource < GBA_BASE_SRAM && GBADMARegisterGetSrcControl(info->reg) < 3) {
|
||||
sourceOffset = width;
|
||||
} else {
|
||||
sourceOffset = DMA_OFFSET[GBADMARegisterGetSrcControl(info->reg)] * width;
|
||||
}
|
||||
int destOffset = DMA_OFFSET[GBADMARegisterGetDestControl(info->reg)] * width;
|
||||
if (source) {
|
||||
source += sourceOffset;
|
||||
info->nextSource += sourceOffset;
|
||||
}
|
||||
dest += destOffset;
|
||||
--wordsRemaining;
|
||||
gba->performingDMA = 0;
|
||||
info->nextDest += destOffset;
|
||||
--info->nextCount;
|
||||
|
||||
info->nextCount = wordsRemaining;
|
||||
info->nextSource = source;
|
||||
info->nextDest = dest;
|
||||
gba->performingDMA = 0;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
|
@ -324,9 +319,9 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
|
|||
}
|
||||
}
|
||||
|
||||
if (!wordsRemaining) {
|
||||
if (!info->nextCount) {
|
||||
info->nextCount |= 0x80000000;
|
||||
if (sourceRegion < REGION_CART0 || destRegion < REGION_CART0) {
|
||||
if (sourceRegion < GBA_REGION_ROM0 || destRegion < GBA_REGION_ROM0) {
|
||||
info->when += 2;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -245,7 +245,7 @@ static void _mp2kReload(struct GBAAudioMixer* mixer) {
|
|||
}
|
||||
|
||||
bool _mp2kEngage(struct GBAAudioMixer* mixer, uint32_t address) {
|
||||
if (address < BASE_WORKING_RAM) {
|
||||
if (address < GBA_BASE_EWRAM) {
|
||||
return false;
|
||||
}
|
||||
if (address != mixer->contextAddress) {
|
||||
|
|
|
@ -63,9 +63,9 @@ void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct
|
|||
renderer->logger->parsePacket = _parsePacket;
|
||||
renderer->logger->handleEvent = _handleEvent;
|
||||
renderer->logger->vramBlock = _vramBlock;
|
||||
renderer->logger->paletteSize = SIZE_PALETTE_RAM;
|
||||
renderer->logger->vramSize = SIZE_VRAM;
|
||||
renderer->logger->oamSize = SIZE_OAM;
|
||||
renderer->logger->paletteSize = GBA_SIZE_PALETTE_RAM;
|
||||
renderer->logger->vramSize = GBA_SIZE_VRAM;
|
||||
renderer->logger->oamSize = GBA_SIZE_OAM;
|
||||
|
||||
renderer->backend = backend;
|
||||
}
|
||||
|
@ -82,9 +82,9 @@ static void _init(struct GBAVideoProxyRenderer* proxyRenderer) {
|
|||
}
|
||||
|
||||
static void _reset(struct GBAVideoProxyRenderer* proxyRenderer) {
|
||||
memcpy(proxyRenderer->logger->oam, &proxyRenderer->d.oam->raw, SIZE_OAM);
|
||||
memcpy(proxyRenderer->logger->palette, proxyRenderer->d.palette, SIZE_PALETTE_RAM);
|
||||
memcpy(proxyRenderer->logger->vram, proxyRenderer->d.vram, SIZE_VRAM);
|
||||
memcpy(proxyRenderer->logger->oam, &proxyRenderer->d.oam->raw, GBA_SIZE_OAM);
|
||||
memcpy(proxyRenderer->logger->palette, proxyRenderer->d.palette, GBA_SIZE_PALETTE_RAM);
|
||||
memcpy(proxyRenderer->logger->vram, proxyRenderer->d.vram, GBA_SIZE_VRAM);
|
||||
|
||||
mVideoLoggerRendererReset(proxyRenderer->logger);
|
||||
}
|
||||
|
@ -199,19 +199,19 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD
|
|||
proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value);
|
||||
break;
|
||||
case DIRTY_PALETTE:
|
||||
if (item->address < SIZE_PALETTE_RAM) {
|
||||
if (item->address < GBA_SIZE_PALETTE_RAM) {
|
||||
STORE_16LE(item->value, item->address, logger->palette);
|
||||
proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value);
|
||||
}
|
||||
break;
|
||||
case DIRTY_OAM:
|
||||
if (item->address < SIZE_OAM) {
|
||||
if (item->address < GBA_SIZE_OAM) {
|
||||
STORE_16LE(item->value, item->address << 1, logger->oam);
|
||||
proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address);
|
||||
}
|
||||
break;
|
||||
case DIRTY_VRAM:
|
||||
if (item->address <= SIZE_VRAM - 0x1000) {
|
||||
if (item->address <= GBA_SIZE_VRAM - 0x1000) {
|
||||
logger->readData(logger, &logger->vram[item->address >> 1], 0x1000, true);
|
||||
proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address);
|
||||
} else {
|
||||
|
|
138
src/gba/gba.c
|
@ -108,7 +108,7 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
|
|||
gba->keyCallback = NULL;
|
||||
mCoreCallbacksListInit(&gba->coreCallbacks, 0);
|
||||
|
||||
gba->biosChecksum = GBAChecksum(gba->memory.bios, SIZE_BIOS);
|
||||
gba->biosChecksum = GBAChecksum(gba->memory.bios, GBA_SIZE_BIOS);
|
||||
|
||||
gba->idleOptimization = IDLE_LOOP_REMOVE;
|
||||
gba->idleLoop = IDLE_LOOP_NONE;
|
||||
|
@ -137,7 +137,7 @@ void GBAUnloadROM(struct GBA* gba) {
|
|||
gba->yankedRomSize = 0;
|
||||
}
|
||||
#ifndef FIXED_ROM_BUFFER
|
||||
mappedMemoryFree(gba->memory.rom, SIZE_CART0);
|
||||
mappedMemoryFree(gba->memory.rom, GBA_SIZE_ROM0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -172,7 +172,7 @@ void GBADestroy(struct GBA* gba) {
|
|||
GBAUnloadMB(gba);
|
||||
|
||||
if (gba->biosVf) {
|
||||
gba->biosVf->unmap(gba->biosVf, gba->memory.bios, SIZE_BIOS);
|
||||
gba->biosVf->unmap(gba->biosVf, gba->memory.bios, GBA_SIZE_BIOS);
|
||||
gba->biosVf->close(gba->biosVf);
|
||||
gba->biosVf = 0;
|
||||
}
|
||||
|
@ -213,6 +213,7 @@ void GBAReset(struct ARMCore* cpu) {
|
|||
gba->earlyExit = false;
|
||||
gba->dmaPC = 0;
|
||||
gba->biosStall = 0;
|
||||
gba->keysLast = 0x400;
|
||||
if (gba->yankedRomSize) {
|
||||
gba->memory.romSize = gba->yankedRomSize;
|
||||
gba->memory.romMask = toPow2(gba->memory.romSize) - 1;
|
||||
|
@ -246,7 +247,7 @@ void GBAReset(struct ARMCore* cpu) {
|
|||
|
||||
if (GBAIsMB(gba->mbVf) && !isELF) {
|
||||
gba->mbVf->seek(gba->mbVf, 0, SEEK_SET);
|
||||
gba->mbVf->read(gba->mbVf, gba->memory.wram, SIZE_WORKING_RAM);
|
||||
gba->mbVf->read(gba->mbVf, gba->memory.wram, GBA_SIZE_EWRAM);
|
||||
}
|
||||
|
||||
gba->lastJump = 0;
|
||||
|
@ -258,7 +259,7 @@ void GBAReset(struct ARMCore* cpu) {
|
|||
memset(gba->debugString, 0, sizeof(gba->debugString));
|
||||
|
||||
|
||||
if (gba->romVf && gba->romVf->size(gba->romVf) > SIZE_CART0) {
|
||||
if (gba->romVf && gba->romVf->size(gba->romVf) > GBA_SIZE_ROM0) {
|
||||
char ident;
|
||||
gba->romVf->seek(gba->romVf, 0xAC, SEEK_SET);
|
||||
gba->romVf->read(gba->romVf, &ident, 1);
|
||||
|
@ -273,9 +274,11 @@ void GBASkipBIOS(struct GBA* gba) {
|
|||
struct ARMCore* cpu = gba->cpu;
|
||||
if (cpu->gprs[ARM_PC] == BASE_RESET + WORD_SIZE_ARM) {
|
||||
if (gba->memory.rom) {
|
||||
cpu->gprs[ARM_PC] = BASE_CART0;
|
||||
cpu->gprs[ARM_PC] = GBA_BASE_ROM0;
|
||||
} else if (gba->memory.wram[0x30]) {
|
||||
cpu->gprs[ARM_PC] = GBA_BASE_EWRAM + 0xC0;
|
||||
} else {
|
||||
cpu->gprs[ARM_PC] = BASE_WORKING_RAM + 0xC0;
|
||||
cpu->gprs[ARM_PC] = GBA_BASE_EWRAM;
|
||||
}
|
||||
gba->video.vcount = 0x7E;
|
||||
gba->memory.io[REG_VCOUNT >> 1] = 0x7E;
|
||||
|
@ -357,14 +360,14 @@ bool GBALoadNull(struct GBA* gba) {
|
|||
gba->romVf = NULL;
|
||||
gba->pristineRomSize = 0;
|
||||
#ifndef FIXED_ROM_BUFFER
|
||||
gba->memory.rom = anonymousMemoryMap(SIZE_CART0);
|
||||
gba->memory.rom = anonymousMemoryMap(GBA_SIZE_ROM0);
|
||||
#else
|
||||
gba->memory.rom = romBuffer;
|
||||
#endif
|
||||
gba->isPristine = false;
|
||||
gba->yankedRomSize = 0;
|
||||
gba->memory.romSize = SIZE_CART0;
|
||||
gba->memory.romMask = SIZE_CART0 - 1;
|
||||
gba->memory.romSize = GBA_SIZE_ROM0;
|
||||
gba->memory.romMask = GBA_SIZE_ROM0 - 1;
|
||||
gba->romCrc32 = 0;
|
||||
|
||||
if (gba->cpu) {
|
||||
|
@ -378,9 +381,9 @@ bool GBALoadMB(struct GBA* gba, struct VFile* vf) {
|
|||
GBAUnloadMB(gba);
|
||||
gba->mbVf = vf;
|
||||
vf->seek(vf, 0, SEEK_SET);
|
||||
memset(gba->memory.wram, 0, SIZE_WORKING_RAM);
|
||||
vf->read(vf, gba->memory.wram, SIZE_WORKING_RAM);
|
||||
if (gba->cpu && gba->memory.activeRegion == REGION_WORKING_RAM) {
|
||||
memset(gba->memory.wram, 0, GBA_SIZE_EWRAM);
|
||||
vf->read(vf, gba->memory.wram, GBA_SIZE_EWRAM);
|
||||
if (gba->cpu && gba->memory.activeRegion == GBA_REGION_IWRAM) {
|
||||
gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
|
||||
}
|
||||
return true;
|
||||
|
@ -402,7 +405,7 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) {
|
|||
gba->isPristine = true;
|
||||
gba->pristineRomSize = vf->size(vf);
|
||||
vf->seek(vf, 0, SEEK_SET);
|
||||
if (gba->pristineRomSize > SIZE_CART0) {
|
||||
if (gba->pristineRomSize > GBA_SIZE_ROM0) {
|
||||
char ident;
|
||||
vf->seek(vf, 0xAC, SEEK_SET);
|
||||
vf->read(vf, &ident, 1);
|
||||
|
@ -412,13 +415,13 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) {
|
|||
#ifdef FIXED_ROM_BUFFER
|
||||
gba->memory.rom = romBuffer;
|
||||
#else
|
||||
gba->memory.rom = anonymousMemoryMap(SIZE_CART0);
|
||||
gba->memory.rom = anonymousMemoryMap(GBA_SIZE_ROM0);
|
||||
#endif
|
||||
} else {
|
||||
gba->memory.rom = vf->map(vf, SIZE_CART0, MAP_READ);
|
||||
gba->memory.romSize = SIZE_CART0;
|
||||
gba->memory.rom = vf->map(vf, GBA_SIZE_ROM0, MAP_READ);
|
||||
gba->memory.romSize = GBA_SIZE_ROM0;
|
||||
}
|
||||
gba->pristineRomSize = SIZE_CART0;
|
||||
gba->pristineRomSize = GBA_SIZE_ROM0;
|
||||
} else if (gba->pristineRomSize == 0x00100000) {
|
||||
// 1 MiB ROMs (e.g. Classic NES) all appear as 4x mirrored, but not more
|
||||
gba->isPristine = false;
|
||||
|
@ -426,7 +429,7 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) {
|
|||
#ifdef FIXED_ROM_BUFFER
|
||||
gba->memory.rom = romBuffer;
|
||||
#else
|
||||
gba->memory.rom = anonymousMemoryMap(SIZE_CART0);
|
||||
gba->memory.rom = anonymousMemoryMap(GBA_SIZE_ROM0);
|
||||
#endif
|
||||
vf->read(vf, gba->memory.rom, gba->pristineRomSize);
|
||||
memcpy(&gba->memory.rom[0x40000], gba->memory.rom, 0x00100000);
|
||||
|
@ -447,15 +450,15 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) {
|
|||
if (popcount32(gba->memory.romSize) != 1) {
|
||||
// This ROM is either a bad dump or homebrew. Emulate flash cart behavior.
|
||||
#ifndef FIXED_ROM_BUFFER
|
||||
void* newRom = anonymousMemoryMap(SIZE_CART0);
|
||||
void* newRom = anonymousMemoryMap(GBA_SIZE_ROM0);
|
||||
memcpy(newRom, gba->memory.rom, gba->pristineRomSize);
|
||||
gba->memory.rom = newRom;
|
||||
#endif
|
||||
gba->memory.romSize = SIZE_CART0;
|
||||
gba->memory.romMask = SIZE_CART0 - 1;
|
||||
gba->memory.romSize = GBA_SIZE_ROM0;
|
||||
gba->memory.romMask = GBA_SIZE_ROM0 - 1;
|
||||
gba->isPristine = false;
|
||||
}
|
||||
if (gba->cpu && gba->memory.activeRegion >= REGION_CART0) {
|
||||
if (gba->cpu && gba->memory.activeRegion >= GBA_REGION_ROM0) {
|
||||
gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
|
||||
}
|
||||
GBAHardwareInit(&gba->memory.hw, &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1]);
|
||||
|
@ -482,23 +485,23 @@ void GBAYankROM(struct GBA* gba) {
|
|||
}
|
||||
|
||||
void GBALoadBIOS(struct GBA* gba, struct VFile* vf) {
|
||||
if (vf->size(vf) != SIZE_BIOS) {
|
||||
if (vf->size(vf) != GBA_SIZE_BIOS) {
|
||||
mLOG(GBA, WARN, "Incorrect BIOS size");
|
||||
return;
|
||||
}
|
||||
uint32_t* bios = vf->map(vf, SIZE_BIOS, MAP_READ);
|
||||
uint32_t* bios = vf->map(vf, GBA_SIZE_BIOS, MAP_READ);
|
||||
if (!bios) {
|
||||
mLOG(GBA, WARN, "Couldn't map BIOS");
|
||||
return;
|
||||
}
|
||||
if (gba->biosVf) {
|
||||
gba->biosVf->unmap(gba->biosVf, gba->memory.bios, SIZE_BIOS);
|
||||
gba->biosVf->unmap(gba->biosVf, gba->memory.bios, GBA_SIZE_BIOS);
|
||||
gba->biosVf->close(gba->biosVf);
|
||||
}
|
||||
gba->biosVf = vf;
|
||||
gba->memory.bios = bios;
|
||||
gba->memory.fullBios = 1;
|
||||
uint32_t checksum = GBAChecksum(gba->memory.bios, SIZE_BIOS);
|
||||
uint32_t checksum = GBAChecksum(gba->memory.bios, GBA_SIZE_BIOS);
|
||||
mLOG(GBA, DEBUG, "BIOS Checksum: 0x%X", checksum);
|
||||
if (checksum == GBA_BIOS_CHECKSUM) {
|
||||
mLOG(GBA, INFO, "Official GBA BIOS detected");
|
||||
|
@ -508,7 +511,7 @@ void GBALoadBIOS(struct GBA* gba, struct VFile* vf) {
|
|||
mLOG(GBA, WARN, "BIOS checksum incorrect");
|
||||
}
|
||||
gba->biosChecksum = checksum;
|
||||
if (gba->memory.activeRegion == REGION_BIOS) {
|
||||
if (gba->memory.activeRegion == GBA_REGION_BIOS) {
|
||||
gba->cpu->memory.activeRegion = gba->memory.bios;
|
||||
}
|
||||
// TODO: error check
|
||||
|
@ -516,18 +519,18 @@ void GBALoadBIOS(struct GBA* gba, struct VFile* vf) {
|
|||
|
||||
void GBAApplyPatch(struct GBA* gba, struct Patch* patch) {
|
||||
size_t patchedSize = patch->outputSize(patch, gba->memory.romSize);
|
||||
if (!patchedSize || patchedSize > SIZE_CART0) {
|
||||
if (!patchedSize || patchedSize > GBA_SIZE_ROM0) {
|
||||
return;
|
||||
}
|
||||
void* newRom = anonymousMemoryMap(SIZE_CART0);
|
||||
void* newRom = anonymousMemoryMap(GBA_SIZE_ROM0);
|
||||
if (!patch->applyPatch(patch, gba->memory.rom, gba->pristineRomSize, newRom, patchedSize)) {
|
||||
mappedMemoryFree(newRom, SIZE_CART0);
|
||||
mappedMemoryFree(newRom, GBA_SIZE_ROM0);
|
||||
return;
|
||||
}
|
||||
if (gba->romVf) {
|
||||
#ifndef FIXED_ROM_BUFFER
|
||||
if (!gba->isPristine) {
|
||||
mappedMemoryFree(gba->memory.rom, SIZE_CART0);
|
||||
mappedMemoryFree(gba->memory.rom, GBA_SIZE_ROM0);
|
||||
} else {
|
||||
gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->pristineRomSize);
|
||||
}
|
||||
|
@ -595,6 +598,63 @@ void GBADebug(struct GBA* gba, uint16_t flags) {
|
|||
gba->debugFlags = GBADebugFlagsClearSend(gba->debugFlags);
|
||||
}
|
||||
|
||||
#ifdef USE_ELF
|
||||
bool GBAVerifyELFEntry(struct ELF* elf, uint32_t target) {
|
||||
if (ELFEntry(elf) == target) {
|
||||
return true;
|
||||
}
|
||||
|
||||
struct ELFProgramHeaders ph;
|
||||
ELFProgramHeadersInit(&ph, 0);
|
||||
ELFGetProgramHeaders(elf, &ph);
|
||||
size_t i;
|
||||
for (i = 0; i < ELFProgramHeadersSize(&ph); ++i) {
|
||||
Elf32_Phdr* phdr = ELFProgramHeadersGetPointer(&ph, i);
|
||||
if (!phdr->p_filesz) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t phdrS = phdr->p_paddr;
|
||||
size_t phdrE = phdrS + phdr->p_filesz;
|
||||
|
||||
// Does the segment contain our target address?
|
||||
if (target < phdrS || target + 4 > phdrE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// File offset to what should be the rom entry instruction
|
||||
size_t off = phdr->p_offset + target - phdrS;
|
||||
|
||||
size_t eSize;
|
||||
const char* bytes = ELFBytes(elf, &eSize);
|
||||
|
||||
// Bounds and alignment check
|
||||
if (off >= eSize || off & 3) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t opcode;
|
||||
LOAD_32(opcode, off, bytes);
|
||||
struct ARMInstructionInfo info;
|
||||
ARMDecodeARM(opcode, &info);
|
||||
|
||||
if (info.branchType != ARM_BRANCH && info.branchType != ARM_BRANCH_LINKED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t bTarget = target + info.op1.immediate + 8;
|
||||
|
||||
if (ELFEntry(elf) == bTarget) {
|
||||
ELFProgramHeadersDeinit(&ph);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
ELFProgramHeadersDeinit(&ph);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool GBAIsROM(struct VFile* vf) {
|
||||
if (!vf) {
|
||||
return false;
|
||||
|
@ -606,7 +666,7 @@ bool GBAIsROM(struct VFile* vf) {
|
|||
uint32_t entry = ELFEntry(elf);
|
||||
bool isGBA = true;
|
||||
isGBA = isGBA && ELFMachine(elf) == EM_ARM;
|
||||
isGBA = isGBA && (entry == BASE_CART0 || entry == BASE_WORKING_RAM + 0xC0);
|
||||
isGBA = isGBA && (GBAVerifyELFEntry(elf, GBA_BASE_ROM0) || GBAVerifyELFEntry(elf, GBA_BASE_EWRAM + 0xC0));
|
||||
ELFClose(elf);
|
||||
return isGBA;
|
||||
}
|
||||
|
@ -662,12 +722,12 @@ bool GBAIsMB(struct VFile* vf) {
|
|||
#ifdef USE_ELF
|
||||
struct ELF* elf = ELFOpen(vf);
|
||||
if (elf) {
|
||||
bool isMB = ELFEntry(elf) == BASE_WORKING_RAM + 0xC0;
|
||||
bool isMB = GBAVerifyELFEntry(elf, GBA_BASE_EWRAM + 0xC0);
|
||||
ELFClose(elf);
|
||||
return isMB;
|
||||
}
|
||||
#endif
|
||||
if (vf->size(vf) > SIZE_WORKING_RAM) {
|
||||
if (vf->size(vf) > GBA_SIZE_EWRAM) {
|
||||
return false;
|
||||
}
|
||||
if (vf->seek(vf, GBA_MB_MAGIC_OFFSET, SEEK_SET) < 0) {
|
||||
|
@ -704,10 +764,10 @@ bool GBAIsMB(struct VFile* vf) {
|
|||
}
|
||||
pc += 4;
|
||||
LOAD_32(opcode, 0, &signature);
|
||||
if ((opcode & ~0x1FFFF) == BASE_WORKING_RAM) {
|
||||
if ((opcode & ~0x1FFFF) == GBA_BASE_EWRAM) {
|
||||
++wramAddrs;
|
||||
}
|
||||
if ((opcode & ~0x1FFFF) == BASE_CART0) {
|
||||
if ((opcode & ~0x1FFFF) == GBA_BASE_ROM0) {
|
||||
++romAddrs;
|
||||
}
|
||||
ARMDecodeARM(opcode, &info);
|
||||
|
@ -730,10 +790,10 @@ bool GBAIsMB(struct VFile* vf) {
|
|||
if (vf->seek(vf, pc, SEEK_SET) < 0) {
|
||||
break;
|
||||
}
|
||||
if ((immediate & ~0x1FFFF) == BASE_WORKING_RAM) {
|
||||
if ((immediate & ~0x1FFFF) == GBA_BASE_EWRAM) {
|
||||
++wramLoads;
|
||||
}
|
||||
if ((immediate & ~0x1FFFF) == BASE_CART0) {
|
||||
if ((immediate & ~0x1FFFF) == GBA_BASE_ROM0) {
|
||||
++romLoads;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include <mgba/internal/gba/memory.h>
|
||||
|
||||
const uint8_t hleBios[SIZE_BIOS] = {
|
||||
const uint8_t hleBios[GBA_SIZE_BIOS] = {
|
||||
0xd3, 0x00, 0x00, 0xea, 0x66, 0x00, 0x00, 0xea, 0x0c, 0x00, 0x00, 0xea,
|
||||
0xfe, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe1,
|
||||
0x59, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x00, 0x00, 0x00, 0x00,
|
||||
|
|
|
@ -15,4 +15,4 @@ hle-bios.c: hle-bios.bin
|
|||
echo >> $@
|
||||
echo '#include <mgba/internal/gba/memory.h>' >> $@
|
||||
echo >> $@
|
||||
xxd -i $< | sed -e 's/unsigned char hle_bios_bin\[\]/const uint8_t hleBios[SIZE_BIOS]/' -e 's/^ \+/\t/' | grep -v hle_bios_bin_len >> $@
|
||||
xxd -i $< | sed -e 's/unsigned char hle_bios_bin\[\]/const uint8_t hleBios[GBA_SIZE_BIOS]/' -e 's/^ \+/\t/' | grep -v hle_bios_bin_len >> $@
|
||||
|
|
|
@ -625,18 +625,18 @@ void GBAIOWrite8(struct GBA* gba, uint32_t address, uint8_t value) {
|
|||
return;
|
||||
}
|
||||
if (address == REG_POSTFLG) {
|
||||
gba->memory.io[(address & (SIZE_IO - 1)) >> 1] = value;
|
||||
gba->memory.io[(address & (GBA_SIZE_IO - 1)) >> 1] = value;
|
||||
return;
|
||||
}
|
||||
if (address >= REG_DEBUG_STRING && address - REG_DEBUG_STRING < sizeof(gba->debugString)) {
|
||||
gba->debugString[address - REG_DEBUG_STRING] = value;
|
||||
return;
|
||||
}
|
||||
if (address > SIZE_IO) {
|
||||
if (address > GBA_SIZE_IO) {
|
||||
return;
|
||||
}
|
||||
uint16_t value16 = value << (8 * (address & 1));
|
||||
value16 |= (gba->memory.io[(address & (SIZE_IO - 1)) >> 1]) & ~(0xFF << (8 * (address & 1)));
|
||||
value16 |= (gba->memory.io[(address & (GBA_SIZE_IO - 1)) >> 1]) & ~(0xFF << (8 * (address & 1)));
|
||||
GBAIOWrite(gba, address & 0xFFFFFFFE, value16);
|
||||
}
|
||||
|
||||
|
|
822
src/gba/memory.c
|
@ -61,7 +61,7 @@ void GBAVideoCacheAssociate(struct mCacheSet* cache, struct GBAVideo* video) {
|
|||
mCacheSetAssignVRAM(cache, video->vram);
|
||||
video->renderer->cache = cache;
|
||||
size_t i;
|
||||
for (i = 0; i < SIZE_PALETTE_RAM / 2; ++i) {
|
||||
for (i = 0; i < GBA_SIZE_PALETTE_RAM / 2; ++i) {
|
||||
mCacheSetWritePalette(cache, i, mColorFrom555(video->palette[i]));
|
||||
}
|
||||
GBAVideoCacheWriteVideoRegister(cache, REG_DISPCNT, video->p->memory.io[REG_DISPCNT >> 1]);
|
||||
|
|
|
@ -664,10 +664,12 @@ static const char* const _finalize =
|
|||
" if (((topFlags.y & 13) == 5 || topFlags.w > 0) && (bottomFlags.y & 2) == 2) {\n"
|
||||
" topPixel.rgb *= float(topFlags.z) / 16.;\n"
|
||||
" topPixel.rgb += bottomPixel.rgb * float(windowFlags.y) / 16.;\n"
|
||||
" } else if ((topFlags.y & 13) == 9) {\n"
|
||||
" topPixel.rgb += (1. - topPixel.rgb) * float(windowFlags.z) / 16.;\n"
|
||||
" } else if ((topFlags.y & 13) == 13) {\n"
|
||||
" topPixel.rgb -= topPixel.rgb * float(windowFlags.z) / 16.;\n"
|
||||
" } else if (topFlags.w == 0) { \n"
|
||||
" if ((topFlags.y & 13) == 9) {\n"
|
||||
" topPixel.rgb += (1. - topPixel.rgb) * float(windowFlags.z) / 16.;\n"
|
||||
" } else if ((topFlags.y & 13) == 13) {\n"
|
||||
" topPixel.rgb -= topPixel.rgb * float(windowFlags.z) / 16.;\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" color = topPixel;\n"
|
||||
"}";
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
#define DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_16(BLEND, OBJWIN) \
|
||||
paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
|
||||
palette = &mainPalette[paletteData]; \
|
||||
charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
|
||||
LOAD_32(tileData, charBase, vram); \
|
||||
if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
|
||||
|
@ -36,7 +35,6 @@
|
|||
charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
|
||||
LOAD_32(tileData, charBase, vram); \
|
||||
paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
|
||||
palette = &mainPalette[paletteData]; \
|
||||
pixel = &renderer->row[outX]; \
|
||||
if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
|
||||
if (outX < renderer->start) { \
|
||||
|
@ -93,7 +91,6 @@
|
|||
carryData = 0; \
|
||||
} else { \
|
||||
paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
|
||||
palette = &mainPalette[paletteData]; \
|
||||
LOAD_32(tileData, charBase, vram); \
|
||||
if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
|
||||
tileData >>= 4 * baseX; \
|
||||
|
@ -123,7 +120,6 @@
|
|||
carryData = 0; \
|
||||
} else { \
|
||||
paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
|
||||
palette = &mainPalette[paletteData]; \
|
||||
LOAD_32(tileData, charBase, vram); \
|
||||
if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
|
||||
tileData >>= x * 4; \
|
||||
|
@ -154,33 +150,26 @@
|
|||
localY = 7 - localY; \
|
||||
} \
|
||||
paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \
|
||||
palette = &mainPalette[paletteData]; \
|
||||
charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \
|
||||
if (UNLIKELY(charBase >= 0x10000)) { \
|
||||
pixel += 8; \
|
||||
continue; \
|
||||
} \
|
||||
LOAD_32(tileData, charBase, vram); \
|
||||
if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
|
||||
LOAD_32(tileData, charBase, vram); \
|
||||
} else { \
|
||||
LOAD_32BE(tileData, charBase, vram); \
|
||||
tileData = ((tileData & 0xF0F0F0F0) >> 4) | ((tileData & 0x0F0F0F0F) << 4); \
|
||||
} \
|
||||
if (tileData) { \
|
||||
if (!GBA_TEXT_MAP_HFLIP(mapData)) { \
|
||||
BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \
|
||||
BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 1); \
|
||||
BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 2); \
|
||||
BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 3); \
|
||||
BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 4); \
|
||||
BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 5); \
|
||||
BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 6); \
|
||||
BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 7); \
|
||||
} else { \
|
||||
BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 7); \
|
||||
BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 6); \
|
||||
BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 5); \
|
||||
BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 4); \
|
||||
BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 3); \
|
||||
BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 2); \
|
||||
BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 1); \
|
||||
BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \
|
||||
} \
|
||||
BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \
|
||||
BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 1); \
|
||||
BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 2); \
|
||||
BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 3); \
|
||||
BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 4); \
|
||||
BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 5); \
|
||||
BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 6); \
|
||||
BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 7); \
|
||||
} \
|
||||
pixel += 8; \
|
||||
}
|
||||
|
@ -523,17 +512,16 @@ void GBAVideoSoftwareRendererDrawBackgroundMode0(struct GBAVideoSoftwareRenderer
|
|||
|
||||
uint32_t screenBase;
|
||||
uint32_t charBase;
|
||||
color_t* mainPalette = renderer->normalPalette;
|
||||
color_t* palette = renderer->normalPalette;
|
||||
if (renderer->d.highlightAmount && background->highlight) {
|
||||
mainPalette = renderer->highlightPalette;
|
||||
palette = renderer->highlightPalette;
|
||||
}
|
||||
if (variant) {
|
||||
mainPalette = renderer->variantPalette;
|
||||
palette = renderer->variantPalette;
|
||||
if (renderer->d.highlightAmount && background->highlight) {
|
||||
mainPalette = renderer->highlightVariantPalette;
|
||||
palette = renderer->highlightVariantPalette;
|
||||
}
|
||||
}
|
||||
color_t* palette = mainPalette;
|
||||
PREPARE_OBJWIN;
|
||||
|
||||
int outX = renderer->start;
|
||||
|
|
|
@ -169,10 +169,10 @@ int GBAVideoSoftwareRendererPreprocessSprite(struct GBAVideoSoftwareRenderer* re
|
|||
(renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN);
|
||||
if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT || (renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || objwinSlowPath) {
|
||||
int target2 = renderer->target2Bd;
|
||||
target2 |= renderer->bg[0].target2;
|
||||
target2 |= renderer->bg[1].target2;
|
||||
target2 |= renderer->bg[2].target2;
|
||||
target2 |= renderer->bg[3].target2;
|
||||
target2 |= renderer->bg[0].target2 && renderer->bg[0].enabled;
|
||||
target2 |= renderer->bg[1].target2 && renderer->bg[1].enabled;
|
||||
target2 |= renderer->bg[2].target2 && renderer->bg[2].enabled;
|
||||
target2 |= renderer->bg[3].target2 && renderer->bg[3].enabled;
|
||||
if (target2) {
|
||||
renderer->forceTarget1 = true;
|
||||
flags |= FLAG_REBLEND;
|
||||
|
|
|
@ -86,28 +86,55 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
|
|||
|
||||
#define COMPOSITE_16_OBJWIN(BLEND, IDX) \
|
||||
if (objwinForceEnable || (!(current & FLAG_OBJWIN)) == objwinOnly) { \
|
||||
unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[paletteData | pixelData] : palette[pixelData]; \
|
||||
unsigned color; \
|
||||
unsigned mergedFlags = flags; \
|
||||
if (current & FLAG_OBJWIN) { \
|
||||
mergedFlags = objwinFlags; \
|
||||
color = objwinPalette[paletteData | pixelData]; \
|
||||
} else if ((current & (FLAG_IS_BACKGROUND | FLAG_REBLEND)) == FLAG_REBLEND) { \
|
||||
color = renderer->normalPalette[paletteData | pixelData]; \
|
||||
} else { \
|
||||
color = palette[paletteData | pixelData]; \
|
||||
} \
|
||||
_composite ## BLEND ## Objwin(renderer, &pixel[IDX], color | mergedFlags, current); \
|
||||
}
|
||||
|
||||
#define COMPOSITE_16_NO_OBJWIN(BLEND, IDX) \
|
||||
_composite ## BLEND ## NoObjwin(renderer, &pixel[IDX], palette[pixelData] | flags, current);
|
||||
{ \
|
||||
unsigned color; \
|
||||
if ((current & (FLAG_IS_BACKGROUND | FLAG_REBLEND)) == FLAG_REBLEND) { \
|
||||
color = renderer->normalPalette[paletteData | pixelData]; \
|
||||
} else { \
|
||||
color = palette[paletteData | pixelData]; \
|
||||
} \
|
||||
_composite ## BLEND ## NoObjwin(renderer, &pixel[IDX], color | flags, current); \
|
||||
}
|
||||
|
||||
#define COMPOSITE_256_OBJWIN(BLEND, IDX) \
|
||||
if (objwinForceEnable || (!(current & FLAG_OBJWIN)) == objwinOnly) { \
|
||||
unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[pixelData] : palette[pixelData]; \
|
||||
unsigned color; \
|
||||
unsigned mergedFlags = flags; \
|
||||
if (current & FLAG_OBJWIN) { \
|
||||
mergedFlags = objwinFlags; \
|
||||
color = objwinPalette[pixelData]; \
|
||||
} else if ((current & (FLAG_IS_BACKGROUND | FLAG_REBLEND)) == FLAG_REBLEND) { \
|
||||
color = renderer->normalPalette[pixelData]; \
|
||||
} else { \
|
||||
color = palette[pixelData]; \
|
||||
} \
|
||||
_composite ## BLEND ## Objwin(renderer, &pixel[IDX], color | mergedFlags, current); \
|
||||
}
|
||||
|
||||
#define COMPOSITE_256_NO_OBJWIN COMPOSITE_16_NO_OBJWIN
|
||||
#define COMPOSITE_256_NO_OBJWIN(BLEND, IDX) \
|
||||
{ \
|
||||
unsigned color; \
|
||||
if ((current & (FLAG_IS_BACKGROUND | FLAG_REBLEND)) == FLAG_REBLEND) { \
|
||||
color = renderer->normalPalette[pixelData]; \
|
||||
} else { \
|
||||
color = palette[pixelData]; \
|
||||
} \
|
||||
_composite ## BLEND ## NoObjwin(renderer, &pixel[IDX], color | flags, current); \
|
||||
}
|
||||
|
||||
#define BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, IDX) \
|
||||
pixelData = tileData & 0xF; \
|
||||
|
|
|
@ -69,22 +69,22 @@ void GBASavedataDeinit(struct GBASavedata* savedata) {
|
|||
} else {
|
||||
switch (savedata->type) {
|
||||
case SAVEDATA_SRAM:
|
||||
mappedMemoryFree(savedata->data, SIZE_CART_SRAM);
|
||||
mappedMemoryFree(savedata->data, GBA_SIZE_SRAM);
|
||||
break;
|
||||
case SAVEDATA_SRAM512:
|
||||
mappedMemoryFree(savedata->data, SIZE_CART_SRAM512);
|
||||
mappedMemoryFree(savedata->data, GBA_SIZE_SRAM512);
|
||||
break;
|
||||
case SAVEDATA_FLASH512:
|
||||
mappedMemoryFree(savedata->data, SIZE_CART_FLASH512);
|
||||
mappedMemoryFree(savedata->data, GBA_SIZE_FLASH512);
|
||||
break;
|
||||
case SAVEDATA_FLASH1M:
|
||||
mappedMemoryFree(savedata->data, SIZE_CART_FLASH1M);
|
||||
mappedMemoryFree(savedata->data, GBA_SIZE_FLASH1M);
|
||||
break;
|
||||
case SAVEDATA_EEPROM:
|
||||
mappedMemoryFree(savedata->data, SIZE_CART_EEPROM);
|
||||
mappedMemoryFree(savedata->data, GBA_SIZE_EEPROM);
|
||||
break;
|
||||
case SAVEDATA_EEPROM512:
|
||||
mappedMemoryFree(savedata->data, SIZE_CART_EEPROM512);
|
||||
mappedMemoryFree(savedata->data, GBA_SIZE_EEPROM512);
|
||||
break;
|
||||
case SAVEDATA_FORCE_NONE:
|
||||
case SAVEDATA_AUTODETECT:
|
||||
|
@ -129,17 +129,17 @@ bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) {
|
|||
if (savedata->data) {
|
||||
switch (savedata->type) {
|
||||
case SAVEDATA_SRAM:
|
||||
return out->write(out, savedata->data, SIZE_CART_SRAM) == SIZE_CART_SRAM;
|
||||
return out->write(out, savedata->data, GBA_SIZE_SRAM) == GBA_SIZE_SRAM;
|
||||
case SAVEDATA_SRAM512:
|
||||
return out->write(out, savedata->data, SIZE_CART_SRAM512) == SIZE_CART_SRAM512;
|
||||
return out->write(out, savedata->data, GBA_SIZE_SRAM512) == GBA_SIZE_SRAM512;
|
||||
case SAVEDATA_FLASH512:
|
||||
return out->write(out, savedata->data, SIZE_CART_FLASH512) == SIZE_CART_FLASH512;
|
||||
return out->write(out, savedata->data, GBA_SIZE_FLASH512) == GBA_SIZE_FLASH512;
|
||||
case SAVEDATA_FLASH1M:
|
||||
return out->write(out, savedata->data, SIZE_CART_FLASH1M) == SIZE_CART_FLASH1M;
|
||||
return out->write(out, savedata->data, GBA_SIZE_FLASH1M) == GBA_SIZE_FLASH1M;
|
||||
case SAVEDATA_EEPROM:
|
||||
return out->write(out, savedata->data, SIZE_CART_EEPROM) == SIZE_CART_EEPROM;
|
||||
return out->write(out, savedata->data, GBA_SIZE_EEPROM) == GBA_SIZE_EEPROM;
|
||||
case SAVEDATA_EEPROM512:
|
||||
return out->write(out, savedata->data, SIZE_CART_EEPROM512) == SIZE_CART_EEPROM512;
|
||||
return out->write(out, savedata->data, GBA_SIZE_EEPROM512) == GBA_SIZE_EEPROM512;
|
||||
case SAVEDATA_AUTODETECT:
|
||||
case SAVEDATA_FORCE_NONE:
|
||||
return true;
|
||||
|
@ -160,17 +160,17 @@ bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) {
|
|||
size_t GBASavedataSize(const struct GBASavedata* savedata) {
|
||||
switch (savedata->type) {
|
||||
case SAVEDATA_SRAM:
|
||||
return SIZE_CART_SRAM;
|
||||
return GBA_SIZE_SRAM;
|
||||
case SAVEDATA_SRAM512:
|
||||
return SIZE_CART_SRAM512;
|
||||
return GBA_SIZE_SRAM512;
|
||||
case SAVEDATA_FLASH512:
|
||||
return SIZE_CART_FLASH512;
|
||||
return GBA_SIZE_FLASH512;
|
||||
case SAVEDATA_FLASH1M:
|
||||
return SIZE_CART_FLASH1M;
|
||||
return GBA_SIZE_FLASH1M;
|
||||
case SAVEDATA_EEPROM:
|
||||
return SIZE_CART_EEPROM;
|
||||
return GBA_SIZE_EEPROM;
|
||||
case SAVEDATA_EEPROM512:
|
||||
return SIZE_CART_EEPROM512;
|
||||
return GBA_SIZE_EEPROM512;
|
||||
case SAVEDATA_FORCE_NONE:
|
||||
return 0;
|
||||
case SAVEDATA_AUTODETECT:
|
||||
|
@ -262,14 +262,14 @@ void GBASavedataInitFlash(struct GBASavedata* savedata) {
|
|||
mLOG(GBA_SAVE, WARN, "Can't re-initialize savedata");
|
||||
return;
|
||||
}
|
||||
int32_t flashSize = SIZE_CART_FLASH512;
|
||||
int32_t flashSize = GBA_SIZE_FLASH512;
|
||||
if (savedata->type == SAVEDATA_FLASH1M) {
|
||||
flashSize = SIZE_CART_FLASH1M;
|
||||
flashSize = GBA_SIZE_FLASH1M;
|
||||
}
|
||||
off_t end;
|
||||
if (!savedata->vf) {
|
||||
end = 0;
|
||||
savedata->data = anonymousMemoryMap(SIZE_CART_FLASH1M);
|
||||
savedata->data = anonymousMemoryMap(GBA_SIZE_FLASH1M);
|
||||
} else {
|
||||
end = savedata->vf->size(savedata->vf);
|
||||
if (end < flashSize) {
|
||||
|
@ -279,7 +279,7 @@ void GBASavedataInitFlash(struct GBASavedata* savedata) {
|
|||
}
|
||||
|
||||
savedata->currentBank = savedata->data;
|
||||
if (end < SIZE_CART_FLASH512) {
|
||||
if (end < GBA_SIZE_FLASH512) {
|
||||
memset(&savedata->data[end], 0xFF, flashSize - end);
|
||||
}
|
||||
}
|
||||
|
@ -291,14 +291,14 @@ void GBASavedataInitEEPROM(struct GBASavedata* savedata) {
|
|||
mLOG(GBA_SAVE, WARN, "Can't re-initialize savedata");
|
||||
return;
|
||||
}
|
||||
int32_t eepromSize = SIZE_CART_EEPROM512;
|
||||
int32_t eepromSize = GBA_SIZE_EEPROM512;
|
||||
if (savedata->type == SAVEDATA_EEPROM) {
|
||||
eepromSize = SIZE_CART_EEPROM;
|
||||
eepromSize = GBA_SIZE_EEPROM;
|
||||
}
|
||||
off_t end;
|
||||
if (!savedata->vf) {
|
||||
end = 0;
|
||||
savedata->data = anonymousMemoryMap(SIZE_CART_EEPROM);
|
||||
savedata->data = anonymousMemoryMap(GBA_SIZE_EEPROM);
|
||||
} else {
|
||||
end = savedata->vf->size(savedata->vf);
|
||||
if (end < eepromSize) {
|
||||
|
@ -306,8 +306,8 @@ void GBASavedataInitEEPROM(struct GBASavedata* savedata) {
|
|||
}
|
||||
savedata->data = savedata->vf->map(savedata->vf, eepromSize, savedata->mapMode);
|
||||
}
|
||||
if (end < SIZE_CART_EEPROM512) {
|
||||
memset(&savedata->data[end], 0xFF, SIZE_CART_EEPROM512 - end);
|
||||
if (end < GBA_SIZE_EEPROM512) {
|
||||
memset(&savedata->data[end], 0xFF, GBA_SIZE_EEPROM512 - end);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -321,17 +321,17 @@ void GBASavedataInitSRAM(struct GBASavedata* savedata) {
|
|||
off_t end;
|
||||
if (!savedata->vf) {
|
||||
end = 0;
|
||||
savedata->data = anonymousMemoryMap(SIZE_CART_SRAM);
|
||||
savedata->data = anonymousMemoryMap(GBA_SIZE_SRAM);
|
||||
} else {
|
||||
end = savedata->vf->size(savedata->vf);
|
||||
if (end < SIZE_CART_SRAM) {
|
||||
savedata->vf->truncate(savedata->vf, SIZE_CART_SRAM);
|
||||
if (end < GBA_SIZE_SRAM) {
|
||||
savedata->vf->truncate(savedata->vf, GBA_SIZE_SRAM);
|
||||
}
|
||||
savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_SRAM, savedata->mapMode);
|
||||
savedata->data = savedata->vf->map(savedata->vf, GBA_SIZE_SRAM, savedata->mapMode);
|
||||
}
|
||||
|
||||
if (end < SIZE_CART_SRAM) {
|
||||
memset(&savedata->data[end], 0xFF, SIZE_CART_SRAM - end);
|
||||
if (end < GBA_SIZE_SRAM) {
|
||||
memset(&savedata->data[end], 0xFF, GBA_SIZE_SRAM - end);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -345,17 +345,17 @@ void GBASavedataInitSRAM512(struct GBASavedata* savedata) {
|
|||
off_t end;
|
||||
if (!savedata->vf) {
|
||||
end = 0;
|
||||
savedata->data = anonymousMemoryMap(SIZE_CART_SRAM512);
|
||||
savedata->data = anonymousMemoryMap(GBA_SIZE_SRAM512);
|
||||
} else {
|
||||
end = savedata->vf->size(savedata->vf);
|
||||
if (end < SIZE_CART_SRAM512) {
|
||||
savedata->vf->truncate(savedata->vf, SIZE_CART_SRAM512);
|
||||
if (end < GBA_SIZE_SRAM512) {
|
||||
savedata->vf->truncate(savedata->vf, GBA_SIZE_SRAM512);
|
||||
}
|
||||
savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_SRAM512, savedata->mapMode);
|
||||
savedata->data = savedata->vf->map(savedata->vf, GBA_SIZE_SRAM512, savedata->mapMode);
|
||||
}
|
||||
|
||||
if (end < SIZE_CART_SRAM512) {
|
||||
memset(&savedata->data[end], 0xFF, SIZE_CART_SRAM512 - end);
|
||||
if (end < GBA_SIZE_SRAM512) {
|
||||
memset(&savedata->data[end], 0xFF, GBA_SIZE_SRAM512 - end);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -465,7 +465,7 @@ void GBASavedataWriteFlash(struct GBASavedata* savedata, uint16_t address, uint8
|
|||
}
|
||||
|
||||
static void _ensureEeprom(struct GBASavedata* savedata, uint32_t size) {
|
||||
if (size < SIZE_CART_EEPROM512) {
|
||||
if (size < GBA_SIZE_EEPROM512) {
|
||||
return;
|
||||
}
|
||||
if (savedata->type == SAVEDATA_EEPROM) {
|
||||
|
@ -475,13 +475,13 @@ static void _ensureEeprom(struct GBASavedata* savedata, uint32_t size) {
|
|||
if (!savedata->vf) {
|
||||
return;
|
||||
}
|
||||
savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_EEPROM512);
|
||||
if (savedata->vf->size(savedata->vf) < SIZE_CART_EEPROM) {
|
||||
savedata->vf->truncate(savedata->vf, SIZE_CART_EEPROM);
|
||||
savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_EEPROM, savedata->mapMode);
|
||||
memset(&savedata->data[SIZE_CART_EEPROM512], 0xFF, SIZE_CART_EEPROM - SIZE_CART_EEPROM512);
|
||||
savedata->vf->unmap(savedata->vf, savedata->data, GBA_SIZE_EEPROM512);
|
||||
if (savedata->vf->size(savedata->vf) < GBA_SIZE_EEPROM) {
|
||||
savedata->vf->truncate(savedata->vf, GBA_SIZE_EEPROM);
|
||||
savedata->data = savedata->vf->map(savedata->vf, GBA_SIZE_EEPROM, savedata->mapMode);
|
||||
memset(&savedata->data[GBA_SIZE_EEPROM512], 0xFF, GBA_SIZE_EEPROM - GBA_SIZE_EEPROM512);
|
||||
} else {
|
||||
savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_EEPROM, savedata->mapMode);
|
||||
savedata->data = savedata->vf->map(savedata->vf, GBA_SIZE_EEPROM, savedata->mapMode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -509,7 +509,7 @@ void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32
|
|||
savedata->writeAddress |= (value & 0x1) << 6;
|
||||
} else if (writeSize == 1) {
|
||||
savedata->command = EEPROM_COMMAND_NULL;
|
||||
} else if ((savedata->writeAddress >> 3) < SIZE_CART_EEPROM) {
|
||||
} else if ((savedata->writeAddress >> 3) < GBA_SIZE_EEPROM) {
|
||||
_ensureEeprom(savedata, savedata->writeAddress >> 3);
|
||||
uint8_t current = savedata->data[savedata->writeAddress >> 3];
|
||||
current &= ~(1 << (0x7 - (savedata->writeAddress & 0x7)));
|
||||
|
@ -551,7 +551,7 @@ uint16_t GBASavedataReadEEPROM(struct GBASavedata* savedata) {
|
|||
int step = 63 - savedata->readBitsRemaining;
|
||||
uint32_t address = (savedata->readAddress + step) >> 3;
|
||||
_ensureEeprom(savedata, address);
|
||||
if (address >= SIZE_CART_EEPROM) {
|
||||
if (address >= GBA_SIZE_EEPROM) {
|
||||
mLOG(GBA_SAVE, GAME_ERROR, "Reading beyond end of EEPROM: %08X", address);
|
||||
return 0xFF;
|
||||
}
|
||||
|
@ -699,13 +699,13 @@ void _flashSwitchBank(struct GBASavedata* savedata, int bank) {
|
|||
mLOG(GBA_SAVE, INFO, "Updating flash chip from 512kb to 1Mb");
|
||||
savedata->type = SAVEDATA_FLASH1M;
|
||||
if (savedata->vf) {
|
||||
savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_FLASH512);
|
||||
if (savedata->vf->size(savedata->vf) < SIZE_CART_FLASH1M) {
|
||||
savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M);
|
||||
savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_FLASH1M, MAP_WRITE);
|
||||
memset(&savedata->data[SIZE_CART_FLASH512], 0xFF, SIZE_CART_FLASH512);
|
||||
savedata->vf->unmap(savedata->vf, savedata->data, GBA_SIZE_FLASH512);
|
||||
if (savedata->vf->size(savedata->vf) < GBA_SIZE_FLASH1M) {
|
||||
savedata->vf->truncate(savedata->vf, GBA_SIZE_FLASH1M);
|
||||
savedata->data = savedata->vf->map(savedata->vf, GBA_SIZE_FLASH1M, MAP_WRITE);
|
||||
memset(&savedata->data[GBA_SIZE_FLASH512], 0xFF, GBA_SIZE_FLASH512);
|
||||
} else {
|
||||
savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_FLASH1M, MAP_WRITE);
|
||||
savedata->data = savedata->vf->map(savedata->vf, GBA_SIZE_FLASH1M, MAP_WRITE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -715,9 +715,9 @@ void _flashSwitchBank(struct GBASavedata* savedata, int bank) {
|
|||
void _flashErase(struct GBASavedata* savedata) {
|
||||
mLOG(GBA_SAVE, DEBUG, "Performing flash chip erase");
|
||||
savedata->dirty |= mSAVEDATA_DIRT_NEW;
|
||||
size_t size = SIZE_CART_FLASH512;
|
||||
size_t size = GBA_SIZE_FLASH512;
|
||||
if (savedata->type == SAVEDATA_FLASH1M) {
|
||||
size = SIZE_CART_FLASH1M;
|
||||
size = GBA_SIZE_FLASH1M;
|
||||
}
|
||||
memset(savedata->data, 0xFF, size);
|
||||
}
|
||||
|
|
|
@ -102,7 +102,7 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
|||
mLOG(GBA_STATE, WARN, "Savestate created using a different version of the BIOS: expected %08X, got %08X", gba->biosChecksum, ucheck);
|
||||
uint32_t pc;
|
||||
LOAD_32(pc, ARM_PC * sizeof(state->cpu.gprs[0]), state->cpu.gprs);
|
||||
if ((ucheck == GBA_BIOS_CHECKSUM || gba->biosChecksum == GBA_BIOS_CHECKSUM) && pc < SIZE_BIOS && pc >= 0x20) {
|
||||
if ((ucheck == GBA_BIOS_CHECKSUM || gba->biosChecksum == GBA_BIOS_CHECKSUM) && pc < GBA_SIZE_BIOS && pc >= 0x20) {
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
|||
}
|
||||
LOAD_32(check, ARM_PC * sizeof(state->cpu.gprs[0]), state->cpu.gprs);
|
||||
int region = (check >> BASE_OFFSET);
|
||||
if ((region == REGION_CART0 || region == REGION_CART1 || region == REGION_CART2) && ((check - WORD_SIZE_ARM) & SIZE_CART0) >= gba->memory.romSize - WORD_SIZE_ARM) {
|
||||
if ((region == GBA_REGION_ROM0 || region == GBA_REGION_ROM1 || region == GBA_REGION_ROM2) && ((check - WORD_SIZE_ARM) & GBA_SIZE_ROM0) >= gba->memory.romSize - WORD_SIZE_ARM) {
|
||||
mLOG(GBA_STATE, WARN, "Savestate created using a differently sized version of the ROM");
|
||||
error = true;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ static bool _importSavedata(struct GBA* gba, void* payload, size_t size) {
|
|||
bool success = false;
|
||||
switch (gba->memory.savedata.type) {
|
||||
case SAVEDATA_FLASH512:
|
||||
if (size > SIZE_CART_FLASH512) {
|
||||
if (size > GBA_SIZE_FLASH512) {
|
||||
GBASavedataForceType(&gba->memory.savedata, SAVEDATA_FLASH1M);
|
||||
}
|
||||
// Fall through
|
||||
|
@ -33,7 +33,7 @@ static bool _importSavedata(struct GBA* gba, void* payload, size_t size) {
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
if (size == SIZE_CART_EEPROM || size == SIZE_CART_EEPROM512) {
|
||||
if (size == GBA_SIZE_EEPROM || size == GBA_SIZE_EEPROM512) {
|
||||
size_t i;
|
||||
for (i = 0; i < size; i += 8) {
|
||||
uint32_t lo, hi;
|
||||
|
@ -119,7 +119,7 @@ int GBASavedataSharkPortPayloadSize(struct VFile* vf) {
|
|||
|
||||
void* GBASavedataSharkPortGetPayload(struct VFile* vf, size_t* osize, uint8_t* oheader, bool testChecksum) {
|
||||
int32_t size = GBASavedataSharkPortPayloadSize(vf);
|
||||
if (size < 0x1C || size > SIZE_CART_FLASH1M + 0x1C) {
|
||||
if (size < 0x1C || size > GBA_SIZE_FLASH1M + 0x1C) {
|
||||
return NULL;
|
||||
}
|
||||
size -= 0x1C;
|
||||
|
@ -336,15 +336,15 @@ int GBASavedataGSVPayloadSize(struct VFile* vf) {
|
|||
LOAD_32(type, 0, &header.type);
|
||||
switch (type) {
|
||||
case 2:
|
||||
return SIZE_CART_SRAM;
|
||||
return GBA_SIZE_SRAM;
|
||||
case 3:
|
||||
return SIZE_CART_EEPROM512;
|
||||
return GBA_SIZE_EEPROM512;
|
||||
case 4:
|
||||
return SIZE_CART_EEPROM;
|
||||
return GBA_SIZE_EEPROM;
|
||||
case 5:
|
||||
return SIZE_CART_FLASH512;
|
||||
return GBA_SIZE_FLASH512;
|
||||
case 6:
|
||||
return SIZE_CART_FLASH1M; // Unconfirmed
|
||||
return GBA_SIZE_FLASH1M; // Unconfirmed
|
||||
default:
|
||||
return vf->size(vf) - GSV_PAYLOAD_OFFSET;
|
||||
}
|
||||
|
@ -352,7 +352,7 @@ int GBASavedataGSVPayloadSize(struct VFile* vf) {
|
|||
|
||||
void* GBASavedataGSVGetPayload(struct VFile* vf, size_t* osize, uint8_t* ident, bool testChecksum) {
|
||||
int32_t size = GBASavedataGSVPayloadSize(vf);
|
||||
if (!size || size > SIZE_CART_FLASH1M) {
|
||||
if (!size || size > GBA_SIZE_FLASH1M) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ MGBA_EXPORT const int GBAVideoObjSizes[16][2] = {
|
|||
|
||||
void GBAVideoInit(struct GBAVideo* video) {
|
||||
video->renderer = NULL;
|
||||
video->vram = anonymousMemoryMap(SIZE_VRAM);
|
||||
video->vram = anonymousMemoryMap(GBA_SIZE_VRAM);
|
||||
video->frameskip = 0;
|
||||
video->event.name = "GBA Video";
|
||||
video->event.callback = NULL;
|
||||
|
@ -93,7 +93,7 @@ void GBAVideoReset(struct GBAVideo* video) {
|
|||
|
||||
void GBAVideoDeinit(struct GBAVideo* video) {
|
||||
video->renderer->deinit(video->renderer);
|
||||
mappedMemoryFree(video->vram, SIZE_VRAM);
|
||||
mappedMemoryFree(video->vram, GBA_SIZE_VRAM);
|
||||
}
|
||||
|
||||
void GBAVideoDummyRendererCreate(struct GBAVideoRenderer* renderer) {
|
||||
|
@ -325,9 +325,9 @@ static void GBAVideoDummyRendererPutPixels(struct GBAVideoRenderer* renderer, si
|
|||
}
|
||||
|
||||
void GBAVideoSerialize(const struct GBAVideo* video, struct GBASerializedState* state) {
|
||||
memcpy(state->vram, video->vram, SIZE_VRAM);
|
||||
memcpy(state->oam, video->oam.raw, SIZE_OAM);
|
||||
memcpy(state->pram, video->palette, SIZE_PALETTE_RAM);
|
||||
memcpy(state->vram, video->vram, GBA_SIZE_VRAM);
|
||||
memcpy(state->oam, video->oam.raw, GBA_SIZE_OAM);
|
||||
memcpy(state->pram, video->palette, GBA_SIZE_PALETTE_RAM);
|
||||
STORE_32(video->event.when - mTimingCurrentTime(&video->p->timing), 0, &state->video.nextEvent);
|
||||
int32_t flags = 0;
|
||||
if (video->event.callback == _startHdraw) {
|
||||
|
@ -340,16 +340,16 @@ void GBAVideoSerialize(const struct GBAVideo* video, struct GBASerializedState*
|
|||
}
|
||||
|
||||
void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState* state) {
|
||||
memcpy(video->vram, state->vram, SIZE_VRAM);
|
||||
memcpy(video->vram, state->vram, GBA_SIZE_VRAM);
|
||||
uint16_t value;
|
||||
int i;
|
||||
for (i = 0; i < SIZE_OAM; i += 2) {
|
||||
for (i = 0; i < GBA_SIZE_OAM; i += 2) {
|
||||
LOAD_16(value, i, state->oam);
|
||||
GBAStore16(video->p->cpu, BASE_OAM | i, value, 0);
|
||||
GBAStore16(video->p->cpu, GBA_BASE_OAM | i, value, 0);
|
||||
}
|
||||
for (i = 0; i < SIZE_PALETTE_RAM; i += 2) {
|
||||
for (i = 0; i < GBA_SIZE_PALETTE_RAM; i += 2) {
|
||||
LOAD_16(value, i, state->pram);
|
||||
GBAStore16(video->p->cpu, BASE_PALETTE_RAM | i, value, 0);
|
||||
GBAStore16(video->p->cpu, GBA_BASE_PALETTE_RAM | i, value, 0);
|
||||
}
|
||||
LOAD_32(video->frameCounter, 0, &state->video.frameCounter);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
set(USE_VFS_3DS ON CACHE BOOL "Use 3DS-specific file support")
|
||||
set(USE_VFS_3DS OFF CACHE BOOL "Use 3DS-specific file support")
|
||||
mark_as_advanced(USE_VFS_3DS)
|
||||
|
||||
find_program(3DSLINK 3dslink)
|
||||
|
|
|
@ -817,7 +817,16 @@ THREAD_ENTRY _core2Test(void* context) {
|
|||
UNUSED(context);
|
||||
}
|
||||
|
||||
int main() {
|
||||
int main(int argc, char* argv[]) {
|
||||
char initialPath[PATH_MAX] = { 0 };
|
||||
if (argc > 1) {
|
||||
strncpy(initialPath, argv[1], sizeof(PATH_MAX));
|
||||
} else {
|
||||
u8 hmac[0x20];
|
||||
memset(hmac, 0, sizeof(hmac));
|
||||
APT_ReceiveDeliverArg(initialPath, sizeof(initialPath), hmac, NULL, NULL);
|
||||
}
|
||||
|
||||
rotation.d.sample = _sampleRotation;
|
||||
rotation.d.readTiltX = _readTiltX;
|
||||
rotation.d.readTiltY = _readTiltY;
|
||||
|
@ -1046,9 +1055,29 @@ int main() {
|
|||
_map3DSKey(&runner.params.keyMap, KEY_CSTICK_UP, mGUI_INPUT_INCREASE_BRIGHTNESS);
|
||||
_map3DSKey(&runner.params.keyMap, KEY_CSTICK_DOWN, mGUI_INPUT_DECREASE_BRIGHTNESS);
|
||||
|
||||
mGUIRunloop(&runner);
|
||||
Result res = romfsInit();
|
||||
bool useRomfs = false;
|
||||
if (R_SUCCEEDED(res)) {
|
||||
useRomfs = mGUIGetRom(&runner, initialPath, sizeof(initialPath));
|
||||
if (!useRomfs) {
|
||||
romfsExit();
|
||||
_cleanup();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (initialPath[0] == '/' || useRomfs) {
|
||||
mGUILoadInputMaps(&runner);
|
||||
mGUIRun(&runner, initialPath);
|
||||
} else {
|
||||
mGUIRunloop(&runner);
|
||||
}
|
||||
|
||||
mGUIDeinit(&runner);
|
||||
|
||||
if (useRomfs) {
|
||||
romfsExit();
|
||||
}
|
||||
_cleanup();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -617,6 +617,39 @@ void retro_run(void) {
|
|||
core->desiredVideoDimensions(core, &width, &height);
|
||||
videoCallback(outputBuffer, width, height, BYTES_PER_PIXEL * 256);
|
||||
|
||||
#ifdef M_CORE_GBA
|
||||
if (core->platform(core) == mPLATFORM_GBA) {
|
||||
blip_t *audioChannelLeft = core->getAudioChannel(core, 0);
|
||||
blip_t *audioChannelRight = core->getAudioChannel(core, 1);
|
||||
int samplesAvail = blip_samples_avail(audioChannelLeft);
|
||||
if (samplesAvail > 0) {
|
||||
/* Update 'running average' of number of
|
||||
* samples per frame.
|
||||
* Note that this is not a true running
|
||||
* average, but just a leaky-integrator/
|
||||
* exponential moving average, used because
|
||||
* it is simple and fast (i.e. requires no
|
||||
* window of samples). */
|
||||
audioSamplesPerFrameAvg = (SAMPLES_PER_FRAME_MOVING_AVG_ALPHA * (float)samplesAvail) +
|
||||
((1.0f - SAMPLES_PER_FRAME_MOVING_AVG_ALPHA) * audioSamplesPerFrameAvg);
|
||||
size_t samplesToRead = (size_t)(audioSamplesPerFrameAvg);
|
||||
/* Resize audio output buffer, if required */
|
||||
if (audioSampleBufferSize < (samplesToRead * 2)) {
|
||||
audioSampleBufferSize = (samplesToRead * 2);
|
||||
audioSampleBuffer = realloc(audioSampleBuffer, audioSampleBufferSize * sizeof(int16_t));
|
||||
}
|
||||
int produced = blip_read_samples(audioChannelLeft, audioSampleBuffer, samplesToRead, true);
|
||||
blip_read_samples(audioChannelRight, audioSampleBuffer + 1, samplesToRead, true);
|
||||
if (produced > 0) {
|
||||
if (audioLowPassEnabled) {
|
||||
_audioLowPassFilter(audioSampleBuffer, produced);
|
||||
}
|
||||
audioCallback(audioSampleBuffer, (size_t)produced);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (rumbleCallback) {
|
||||
if (rumbleUp) {
|
||||
rumbleCallback(0, RETRO_RUMBLE_STRONG, rumbleUp * 0xFFFF / (rumbleUp + rumbleDown));
|
||||
|
@ -643,66 +676,66 @@ static void _setupMaps(struct mCore* core) {
|
|||
|
||||
/* Map internal working RAM */
|
||||
descs[0].ptr = gba->memory.iwram;
|
||||
descs[0].start = BASE_WORKING_IRAM;
|
||||
descs[0].len = SIZE_WORKING_IRAM;
|
||||
descs[0].start = GBA_BASE_IWRAM;
|
||||
descs[0].len = GBA_SIZE_IWRAM;
|
||||
descs[0].select = 0xFF000000;
|
||||
|
||||
/* Map working RAM */
|
||||
descs[1].ptr = gba->memory.wram;
|
||||
descs[1].start = BASE_WORKING_RAM;
|
||||
descs[1].len = SIZE_WORKING_RAM;
|
||||
descs[1].start = GBA_BASE_EWRAM;
|
||||
descs[1].len = GBA_SIZE_EWRAM;
|
||||
descs[1].select = 0xFF000000;
|
||||
|
||||
/* Map save RAM */
|
||||
/* TODO: if SRAM is flash, use start=0 addrspace="S" instead */
|
||||
descs[2].ptr = savedataSize ? savedata : NULL;
|
||||
descs[2].start = BASE_CART_SRAM;
|
||||
descs[2].start = GBA_BASE_SRAM;
|
||||
descs[2].len = savedataSize;
|
||||
|
||||
/* Map ROM */
|
||||
descs[3].ptr = gba->memory.rom;
|
||||
descs[3].start = BASE_CART0;
|
||||
descs[3].start = GBA_BASE_ROM0;
|
||||
descs[3].len = romSize;
|
||||
descs[3].flags = RETRO_MEMDESC_CONST;
|
||||
|
||||
descs[4].ptr = gba->memory.rom;
|
||||
descs[4].start = BASE_CART1;
|
||||
descs[4].start = GBA_BASE_ROM1;
|
||||
descs[4].len = romSize;
|
||||
descs[4].flags = RETRO_MEMDESC_CONST;
|
||||
|
||||
descs[5].ptr = gba->memory.rom;
|
||||
descs[5].start = BASE_CART2;
|
||||
descs[5].start = GBA_BASE_ROM2;
|
||||
descs[5].len = romSize;
|
||||
descs[5].flags = RETRO_MEMDESC_CONST;
|
||||
|
||||
/* Map BIOS */
|
||||
descs[6].ptr = gba->memory.bios;
|
||||
descs[6].start = BASE_BIOS;
|
||||
descs[6].len = SIZE_BIOS;
|
||||
descs[6].start = GBA_BASE_BIOS;
|
||||
descs[6].len = GBA_SIZE_BIOS;
|
||||
descs[6].flags = RETRO_MEMDESC_CONST;
|
||||
|
||||
/* Map VRAM */
|
||||
descs[7].ptr = gba->video.vram;
|
||||
descs[7].start = BASE_VRAM;
|
||||
descs[7].len = SIZE_VRAM;
|
||||
descs[7].start = GBA_BASE_VRAM;
|
||||
descs[7].len = GBA_SIZE_VRAM;
|
||||
descs[7].select = 0xFF000000;
|
||||
|
||||
/* Map palette RAM */
|
||||
descs[8].ptr = gba->video.palette;
|
||||
descs[8].start = BASE_PALETTE_RAM;
|
||||
descs[8].len = SIZE_PALETTE_RAM;
|
||||
descs[8].start = GBA_BASE_PALETTE_RAM;
|
||||
descs[8].len = GBA_SIZE_PALETTE_RAM;
|
||||
descs[8].select = 0xFF000000;
|
||||
|
||||
/* Map OAM */
|
||||
descs[9].ptr = &gba->video.oam; /* video.oam is a structure */
|
||||
descs[9].start = BASE_OAM;
|
||||
descs[9].len = SIZE_OAM;
|
||||
descs[9].start = GBA_BASE_OAM;
|
||||
descs[9].len = GBA_SIZE_OAM;
|
||||
descs[9].select = 0xFF000000;
|
||||
|
||||
/* Map mmapped I/O */
|
||||
descs[10].ptr = gba->memory.io;
|
||||
descs[10].start = BASE_IO;
|
||||
descs[10].len = SIZE_IO;
|
||||
descs[10].start = GBA_BASE_IO;
|
||||
descs[10].len = GBA_SIZE_IO;
|
||||
|
||||
mmaps.descriptors = descs;
|
||||
mmaps.num_descriptors = sizeof(descs) / sizeof(descs[0]);
|
||||
|
|
|
@ -12,14 +12,12 @@ CXX_GUARD_START
|
|||
|
||||
#ifdef USE_EPOXY
|
||||
#include <epoxy/gl.h>
|
||||
#elif defined(BUILD_GL)
|
||||
#ifdef __APPLE__
|
||||
#elif defined(__APPLE__)
|
||||
#include <OpenGL/gl3.h>
|
||||
#else
|
||||
#elif defined(BUILD_GL)
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#endif
|
||||
#elif defined(BUILD_GLES3)
|
||||
#include <GLES3/gl3.h>
|
||||
#else
|
||||
|
|
|
@ -153,6 +153,8 @@ static enum GUIKeyboardStatus _keyboardRun(struct GUIKeyboardParams* keyboard) {
|
|||
}
|
||||
|
||||
int main() {
|
||||
char initialPath[PATH_MAX] = { 0 };
|
||||
|
||||
vita2d_init();
|
||||
struct GUIFont* font = GUIFontCreate();
|
||||
struct mGUIRunner runner = {
|
||||
|
@ -278,7 +280,13 @@ int main() {
|
|||
mPSP2MapKey(&runner.params.keyMap, SCE_CTRL_SQUARE, mGUI_INPUT_SCREEN_MODE);
|
||||
|
||||
scePowerSetArmClockFrequency(444);
|
||||
mGUIRunloop(&runner);
|
||||
|
||||
if (mGUIGetRom(&runner, initialPath, sizeof(initialPath))) {
|
||||
mGUILoadInputMaps(&runner);
|
||||
mGUIRun(&runner, initialPath);
|
||||
} else {
|
||||
mGUIRunloop(&runner);
|
||||
}
|
||||
|
||||
vita2d_fini();
|
||||
mGUIDeinit(&runner);
|
||||
|
|
|
@ -123,19 +123,19 @@ class GBAMemory(Memory):
|
|||
def __init__(self, core, romSize=lib.SIZE_CART0):
|
||||
super(GBAMemory, self).__init__(core, 0x100000000)
|
||||
|
||||
self.bios = Memory(core, lib.SIZE_BIOS, lib.BASE_BIOS)
|
||||
self.wram = Memory(core, lib.SIZE_WORKING_RAM, lib.BASE_WORKING_RAM)
|
||||
self.iwram = Memory(core, lib.SIZE_WORKING_IRAM, lib.BASE_WORKING_IRAM)
|
||||
self.io = Memory(core, lib.SIZE_IO, lib.BASE_IO) # pylint: disable=invalid-name
|
||||
self.palette = Memory(core, lib.SIZE_PALETTE_RAM, lib.BASE_PALETTE_RAM)
|
||||
self.vram = Memory(core, lib.SIZE_VRAM, lib.BASE_VRAM)
|
||||
self.oam = Memory(core, lib.SIZE_OAM, lib.BASE_OAM)
|
||||
self.bios = Memory(core, lib.GBA_SIZE_BIOS, lib.GBA_BASE_BIOS)
|
||||
self.wram = Memory(core, lib.GBA_SIZE_EWRAM, lib.GBA_BASE_EWRAM)
|
||||
self.iwram = Memory(core, lib.GBA_SIZE_IWRAM, lib.GBA_BASE_IWRAM)
|
||||
self.io = Memory(core, lib.GBA_SIZE_IO, lib.GBA_BASE_IO) # pylint: disable=invalid-name
|
||||
self.palette = Memory(core, lib.GBA_SIZE_PALETTE_RAM, lib.GBA_BASE_PALETTE_RAM)
|
||||
self.vram = Memory(core, lib.GBA_SIZE_VRAM, lib.GBA_BASE_VRAM)
|
||||
self.oam = Memory(core, lib.GBA_SIZE_OAM, lib.GBA_BASE_OAM)
|
||||
self.cart0 = Memory(core, romSize, lib.BASE_CART0)
|
||||
self.cart1 = Memory(core, romSize, lib.BASE_CART1)
|
||||
self.cart2 = Memory(core, romSize, lib.BASE_CART2)
|
||||
self.cart = self.cart0
|
||||
self.rom = self.cart0
|
||||
self.sram = Memory(core, lib.SIZE_CART_SRAM, lib.BASE_CART_SRAM)
|
||||
self.sram = Memory(core, lib.GBA_SIZE_SRAM, lib.GBA_BASE_SRAM)
|
||||
|
||||
|
||||
class GBASprite(Sprite):
|
||||
|
|
|
@ -74,7 +74,7 @@ AboutScreen::AboutScreen(QWidget* parent)
|
|||
|
||||
{
|
||||
QString copyright = m_ui.copyright->text();
|
||||
copyright.replace("{year}", QLatin1String("2022"));
|
||||
copyright.replace("{year}", QLatin1String("2023"));
|
||||
m_ui.copyright->setText(copyright);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,16 +8,17 @@
|
|||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
|
||||
#include "GBAApp.h"
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
AbstractUpdater::AbstractUpdater(QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_netman(new QNetworkAccessManager(this))
|
||||
{
|
||||
}
|
||||
|
||||
void AbstractUpdater::checkUpdate() {
|
||||
QNetworkReply* reply = m_netman->get(QNetworkRequest(manifestLocation()));
|
||||
QNetworkReply* reply = GBAApp::app()->httpGet(manifestLocation());
|
||||
chaseRedirects(reply, &AbstractUpdater::manifestDownloaded);
|
||||
}
|
||||
|
||||
|
@ -36,7 +37,7 @@ void AbstractUpdater::downloadUpdate() {
|
|||
return;
|
||||
}
|
||||
m_isUpdating = true;
|
||||
QNetworkReply* reply = m_netman->get(QNetworkRequest(url));
|
||||
QNetworkReply* reply = GBAApp::app()->httpGet(url);
|
||||
chaseRedirects(reply, &AbstractUpdater::updateDownloaded);
|
||||
}
|
||||
|
||||
|
@ -54,7 +55,7 @@ void AbstractUpdater::chaseRedirects(QNetworkReply* reply, void (AbstractUpdater
|
|||
connect(reply, &QNetworkReply::finished, this, [this, reply, cb]() {
|
||||
// TODO: check domains, etc
|
||||
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() / 100 == 3) {
|
||||
QNetworkReply* newReply = m_netman->get(QNetworkRequest(reply->header(QNetworkRequest::LocationHeader).toString()));
|
||||
QNetworkReply* newReply = GBAApp::app()->httpGet(reply->header(QNetworkRequest::LocationHeader).toString());
|
||||
chaseRedirects(newReply, cb);
|
||||
} else {
|
||||
(this->*cb)(reply);
|
||||
|
@ -69,7 +70,7 @@ void AbstractUpdater::manifestDownloaded(QNetworkReply* reply) {
|
|||
if (!url.isValid()) {
|
||||
emit updateDone(false);
|
||||
} else {
|
||||
QNetworkReply* reply = m_netman->get(QNetworkRequest(url));
|
||||
QNetworkReply* reply = GBAApp::app()->httpGet(url);
|
||||
chaseRedirects(reply, &AbstractUpdater::updateDownloaded);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include <QFile>
|
||||
#include <QObject>
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
|
||||
namespace QGBA {
|
||||
|
@ -44,7 +43,6 @@ private:
|
|||
void updateDownloaded(QNetworkReply*);
|
||||
|
||||
bool m_isUpdating = false;
|
||||
QNetworkAccessManager* m_netman;
|
||||
QByteArray m_manifest;
|
||||
};
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include "ApplicationUpdatePrompt.h"
|
||||
#include "ConfigController.h"
|
||||
|
@ -71,9 +72,10 @@ QStringList ApplicationUpdater::listChannels() {
|
|||
}
|
||||
|
||||
QString ApplicationUpdater::currentChannel() {
|
||||
QLatin1String version(projectVersion);
|
||||
QLatin1String branch(gitBranch);
|
||||
if (branch == QLatin1String("heads/") + version) {
|
||||
QString version(projectVersion);
|
||||
QString branch(gitBranch);
|
||||
QRegularExpression stable("^(?:(?:refs/)?(?:tags|heads)/)?[0-9]+\\.[0-9]+\\.[0-9]+$");
|
||||
if (branch.contains(stable) || (branch == "(unknown)" && version.contains(stable))) {
|
||||
return QLatin1String("stable");
|
||||
} else {
|
||||
return QLatin1String("dev");
|
||||
|
@ -136,8 +138,16 @@ QUrl ApplicationUpdater::parseManifest(const QByteArray& manifest) {
|
|||
|
||||
QString ApplicationUpdater::destination() const {
|
||||
QFileInfo path(updateInfo().url.path());
|
||||
QDir dir(ConfigController::configDir());
|
||||
return dir.filePath(QLatin1String("update.") + path.completeSuffix());
|
||||
QDir dir(ConfigController::cacheDir());
|
||||
// QFileInfo::completeSuffix will eat all .'s in the filename...including
|
||||
// ones in the version string, turning mGBA-1.0.0-win32.7z into
|
||||
// 0.0-win32.7z instead of the intended .7z
|
||||
// As a result, so we have to split out the complete suffix manually.
|
||||
QString suffix(path.suffix());
|
||||
if (path.completeBaseName().endsWith(".tar")) {
|
||||
suffix = "tar." + suffix;
|
||||
}
|
||||
return dir.filePath(QLatin1String("update.") + suffix);
|
||||
}
|
||||
|
||||
const char* ApplicationUpdater::platform() {
|
||||
|
@ -166,7 +176,8 @@ const char* ApplicationUpdater::platform() {
|
|||
}
|
||||
|
||||
ApplicationUpdater::UpdateInfo::UpdateInfo(const QString& prefix, const mUpdate* update)
|
||||
: size(update->size)
|
||||
: rev(-1)
|
||||
, size(update->size)
|
||||
, url(prefix + update->path)
|
||||
{
|
||||
if (update->rev > 0) {
|
||||
|
|
|
@ -51,8 +51,8 @@ void AssetTile::setController(std::shared_ptr<CoreController> controller) {
|
|||
#ifdef M_CORE_GBA
|
||||
case mPLATFORM_GBA:
|
||||
m_addressWidth = 8;
|
||||
m_addressBase = BASE_VRAM;
|
||||
m_boundaryBase = BASE_VRAM | 0x10000;
|
||||
m_addressBase = GBA_BASE_VRAM;
|
||||
m_boundaryBase = GBA_BASE_VRAM | 0x10000;
|
||||
break;
|
||||
#endif
|
||||
#ifdef M_CORE_GB
|
||||
|
|
|
@ -39,7 +39,6 @@ if(NOT ${QT}Widgets_FOUND)
|
|||
endif()
|
||||
|
||||
if(APPLE)
|
||||
execute_process(COMMAND xcrun --show-sdk-version OUTPUT_VARIABLE MACOSX_SDK)
|
||||
if(MACOSX_SDK VERSION_GREATER 10.14)
|
||||
list(APPEND QT_DEFINES USE_SHARE_WIDGET)
|
||||
endif()
|
||||
|
@ -95,14 +94,24 @@ set(SOURCE_FILES
|
|||
Display.cpp
|
||||
DisplayGL.cpp
|
||||
DisplayQt.cpp
|
||||
ForwarderController.cpp
|
||||
ForwarderGenerator.cpp
|
||||
ForwarderGenerator3DS.cpp
|
||||
ForwarderGeneratorVita.cpp
|
||||
ForwarderView.cpp
|
||||
FrameView.cpp
|
||||
GBAApp.cpp
|
||||
GBAKeyEditor.cpp
|
||||
GIFView.cpp
|
||||
GamepadAxisEvent.cpp
|
||||
GamepadButtonEvent.cpp
|
||||
GamepadHatEvent.cpp
|
||||
IOViewer.cpp
|
||||
input/Gamepad.cpp
|
||||
input/GamepadAxisEvent.cpp
|
||||
input/GamepadButtonEvent.cpp
|
||||
input/GamepadHatEvent.cpp
|
||||
input/InputDriver.cpp
|
||||
input/InputSource.cpp
|
||||
input/InputMapper.cpp
|
||||
input/KeySource.cpp
|
||||
InputController.cpp
|
||||
InputProfile.cpp
|
||||
KeyEditor.cpp
|
||||
|
@ -119,6 +128,7 @@ set(SOURCE_FILES
|
|||
MessagePainter.cpp
|
||||
MultiplayerController.cpp
|
||||
ObjView.cpp
|
||||
OpenGLBug.cpp
|
||||
OverrideView.cpp
|
||||
PaletteView.cpp
|
||||
PlacementControl.cpp
|
||||
|
@ -152,6 +162,7 @@ set(UI_FILES
|
|||
CheatsView.ui
|
||||
DebuggerConsole.ui
|
||||
DolphinConnector.ui
|
||||
ForwarderView.ui
|
||||
FrameView.ui
|
||||
GIFView.ui
|
||||
IOViewer.ui
|
||||
|
@ -192,6 +203,7 @@ set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libqt5widgets5
|
|||
|
||||
set(AUDIO_SRC)
|
||||
if(BUILD_SDL)
|
||||
list(APPEND SOURCE_FILES input/SDLInputDriver.cpp)
|
||||
list(APPEND AUDIO_SRC AudioProcessorSDL.cpp)
|
||||
endif()
|
||||
|
||||
|
@ -414,7 +426,7 @@ if(QT_STATIC)
|
|||
list(APPEND QT_LIBRARIES "-framework AVFoundation" "-framework CoreMedia" "-framework SystemConfiguration" "-framework Security")
|
||||
set_target_properties(${QT}::Core PROPERTIES INTERFACE_LINK_LIBRARIES "${QTPCRE}")
|
||||
elseif(UNIX)
|
||||
list(APPEND QT_LIBRARIES ${QT}::FontDatabaseSupport ${QT}::XcbQpa)
|
||||
list(APPEND QT_LIBRARIES ${QT}::FontDatabaseSupport ${QT}::XcbQpa ${QT}::QWaylandIntegrationPlugin)
|
||||
endif()
|
||||
endif()
|
||||
target_link_libraries(${BINARY_NAME}-qt ${PLATFORM_LIBRARY} ${BINARY_NAME} ${QT_LIBRARIES})
|
||||
|
|
|
@ -382,3 +382,7 @@ const QString& ConfigController::configDir() {
|
|||
}
|
||||
return s_configDir;
|
||||
}
|
||||
|
||||
const QString& ConfigController::cacheDir() {
|
||||
return configDir();
|
||||
}
|
||||
|
|