Merge branch 'master' (early part) into translations

This commit is contained in:
Vicki Pfau 2024-12-12 04:07:48 -08:00
commit 9145e2d65f
139 changed files with 4068 additions and 1960 deletions

View File

@ -12,7 +12,11 @@ install:
- vcpkg --no-dry-run upgrade - vcpkg --no-dry-run upgrade
- rd /Q /S C:\Tools\vcpkg\buildtrees - rd /Q /S C:\Tools\vcpkg\buildtrees
before_build: before_build:
- cmake . -DCMAKE_PREFIX_PATH=C:\Qt\5.15\msvc2019_64 -DCMAKE_TOOLCHAIN_FILE=C:\Tools\vcpkg\scripts\buildsystems\vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-release -DCMAKE_CONFIGURATION_TYPES=Release - cmake . -DCMAKE_PREFIX_PATH=C:\Qt\5.15\msvc2019_64 \
-DCMAKE_TOOLCHAIN_FILE=C:\Tools\vcpkg\scripts\buildsystems\vcpkg.cmake \
-DVCPKG_TARGET_TRIPLET=x64-windows-release \
-DCMAKE_CONFIGURATION_TYPES=Release \
-DCMAKE_SYSTEM_VERSION=10.0.22000.1
build: build:
parallel: true parallel: true
project: mGBA.sln project: mGBA.sln

47
CHANGES
View File

@ -8,42 +8,32 @@ Features:
- Debugger: Add range watchpoints - Debugger: Add range watchpoints
Emulation fixes: Emulation fixes:
- ARM: Add framework for coprocessor support - ARM: Add framework for coprocessor support
- GB Audio: Fix audio envelope timing resetting too often (fixes mgba.io/i/3164)
- GB I/O: Fix STAT writing IRQ trigger conditions (fixes mgba.io/i/2501)
- GB Serialize: Add missing Pocket Cam state to savestates - GB Serialize: Add missing Pocket Cam state to savestates
- GB Video: Implement DMG-style sprite ordering - GB Video: Implement DMG-style sprite ordering
- GBA: Unhandled bkpt should be treated as an undefined exception - GBA: Unhandled bkpt should be treated as an undefined exception
- GBA: Add baseline CP0 (Wii U VC) and CP1 (DCC) implementations - GBA: Add baseline CP0 (Wii U VC) and CP1 (DCC) implementations
- GBA GPIO: Fix gyro read-out start (fixes mgba.io/i/3141)
- GBA I/O: Fix HALTCNT access behavior (fixes mgba.io/i/2309)
- GBA Serialize: Fix some minor save state edge cases - GBA Serialize: Fix some minor save state edge cases
- GBA SIO: Fix MULTI mode SIOCNT bit 7 writes on secondary GBAs (fixes mgba.io/i/3110)
- GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722) - GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722)
- GBA Video: Improve emulation of window start/end conditions (fixes mgba.io/i/1945) - GBA Video: Improve emulation of window start/end conditions (fixes mgba.io/i/1945)
Other fixes: Other fixes:
- Core: Fix inconsistencies with setting game-specific overrides (fixes mgba.io/i/2963) - Core: Fix inconsistencies with setting game-specific overrides (fixes mgba.io/i/2963)
- Debugger: Fix writing to specific segment in command-line debugger - Debugger: Fix writing to specific segment in command-line debugger
- GB: Fix uninitialized save data when loading undersized temporary saves
- GB, GBA Core: Fix memory leak if reloading debug symbols
- GB Serialize: Prevent loading invalid states where LY >= 144 in modes other than 1 - GB Serialize: Prevent loading invalid states where LY >= 144 in modes other than 1
- GBA Hardware: Fix loading states unconditionally overwriting GPIO memory
- GBA: Fix getting game info for multiboot ROMs - GBA: Fix getting game info for multiboot ROMs
- GBA Audio: Fix crash if audio FIFOs and timers get out of sync
- GBA Audio: Fix crash in audio subsampling if timing lockstep breaks
- GBA Core: Fix loading symbols from ELF files if the file doesn't end with .elf
- GBA Memory: Let raw access read high MMIO addresses
- Qt: Fix savestate preview sizes with different scales (fixes mgba.io/i/2560) - Qt: Fix savestate preview sizes with different scales (fixes mgba.io/i/2560)
- Qt: Fix potential crash when configuring shortcuts - Qt: Fix potential crash when configuring shortcuts
- Qt: Fix crash when applying changes to GB I/O registers in I/O view
- Qt: Fix LCDC background priority/enable bit being mis-mapped in I/O view
- Updater: Fix updating appimage across filesystems
Misc: Misc:
- Core: Handle relative paths for saves, screenshots, etc consistently (fixes mgba.io/i/2826) - Core: Handle relative paths for saves, screenshots, etc consistently (fixes mgba.io/i/2826)
- Core: Improve rumble emulation by averaging state over entire frame (fixes mgba.io/i/3232) - Core: Improve rumble emulation by averaging state over entire frame (fixes mgba.io/i/3232)
- Core: Add MD5 hashing for ROMs
- GB: Prevent incompatible BIOSes from being used on differing models - GB: Prevent incompatible BIOSes from being used on differing models
- GB Serialize: Add missing savestate support for MBC6 and NT (newer) - GB Serialize: Add missing savestate support for MBC6 and NT (newer)
- GBA: Improve detection of valid ELF ROMs - GBA: Improve detection of valid ELF ROMs
- GBA Audio: Remove broken XQ audio pending rewrite - GBA Audio: Remove broken XQ audio pending rewrite
- GBA BIOS: Move SoftReset implementation to assembly
- GBA Memory: Improve VRAM access stall cycle estimation - GBA Memory: Improve VRAM access stall cycle estimation
- GBA SIO: Rewrite lockstep driver for improved stability
- GBA Video: Add special circlular window handling in OpenGL renderer - GBA Video: Add special circlular window handling in OpenGL renderer
- Libretro: Add Super Game Boy Color support (closes mgba.io/i/3188) - Libretro: Add Super Game Boy Color support (closes mgba.io/i/3188)
- mGUI: Enable auto-softpatching (closes mgba.io/i/2899) - mGUI: Enable auto-softpatching (closes mgba.io/i/2899)
@ -52,7 +42,36 @@ Misc:
- Qt: Remove maligned double-click-to-fullscreen shortcut (closes mgba.io/i/2632) - Qt: Remove maligned double-click-to-fullscreen shortcut (closes mgba.io/i/2632)
- Qt: Pass logging context through to video proxy thread (fixes mgba.io/i/3095) - Qt: Pass logging context through to video proxy thread (fixes mgba.io/i/3095)
- Qt: Show maker code and game version in ROM info - Qt: Show maker code and game version in ROM info
- Qt: Show a dummy shader settings tab if shaders aren't supported
- Res: Port NSO-gba-colors shader (closes mgba.io/i/2834)
- Scripting: Add `callbacks:oneshot` for single-call callbacks - Scripting: Add `callbacks:oneshot` for single-call callbacks
0.10.4: (2024-12-07)
Emulation fixes:
- GB Audio: Fix audio envelope timing resetting too often (fixes mgba.io/i/3164)
- GB I/O: Fix STAT writing IRQ trigger conditions (fixes mgba.io/i/2501)
- GBA GPIO: Fix gyro read-out start (fixes mgba.io/i/3141)
- GBA I/O: Fix HALTCNT access behavior (fixes mgba.io/i/2309)
- GBA I/O: Fix audio register 8-bit write behavior (fixes mgba.io/i/3086)
- GBA Serialize: Properly restore GPIO register state (fixes mgba.io/i/3294)
- GBA SIO: Fix MULTI mode SIOCNT bit 7 writes on secondary GBAs (fixes mgba.io/i/3110)
Other fixes:
- Core: Fix patch autoloading leaking the file handle
- GB: Fix uninitialized save data when loading undersized temporary saves
- GB, GBA Core: Fix memory leak if reloading debug symbols
- GB Serialize: Prevent loading invalid states where LY >= 144 in modes other than 1
- GBA Audio: Fix crash if audio FIFOs and timers get out of sync
- GBA Audio: Fix crash in audio subsampling if timing lockstep breaks
- GBA Core: Fix loading symbols from ELF files if the file doesn't end with .elf
- GBA Memory: Let raw access read high MMIO addresses
- Qt: Fix crash when applying changes to GB I/O registers in I/O view
- Qt: Fix LCDC background priority/enable bit being mis-mapped in I/O view
- Qt: Fix saving named states breaking when screenshot states disabled (fixes mgba.io/i/3320)
- Qt: Fix potential crash on Wayland with OpenGL (fixes mgba.io/i/3276)
- Qt: Fix installer updates if a version number is in the filename (fixes mgba.io/i/3109)
- Updater: Fix updating appimage across filesystems
Misc:
- Qt: Make window corners square on Windows 11 (fixes mgba.io/i/3285)
- Switch: Add bilinear filtering option (closes mgba.io/i/3111) - Switch: Add bilinear filtering option (closes mgba.io/i/3111)
- Vita: Add imc0 and xmc0 mount point support - Vita: Add imc0 and xmc0 mount point support

View File

@ -242,8 +242,14 @@ if(APPLE)
execute_process(COMMAND xcrun --show-sdk-version OUTPUT_VARIABLE MACOSX_SDK) execute_process(COMMAND xcrun --show-sdk-version OUTPUT_VARIABLE MACOSX_SDK)
add_definitions(-D_DARWIN_C_SOURCE) add_definitions(-D_DARWIN_C_SOURCE)
list(APPEND OS_LIB "-framework Foundation") list(APPEND OS_LIB "-framework Foundation")
# Xcode 15 introduced a warning about duplicate libraries that CMake doesn't disable itself, we do it here globally
if(MACOSX_SDK VERSION_GREATER_EQUAL 10.15)
add_link_options(LINKER:-no_warn_duplicate_libraries)
endif()
if(NOT CMAKE_SYSTEM_VERSION VERSION_LESS "10.0") # Darwin 10.x is Mac OS X 10.6 if(NOT CMAKE_SYSTEM_VERSION VERSION_LESS "10.0") # Darwin 10.x is Mac OS X 10.6
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.6") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.6")
endif() endif()
# Not supported until Xcode 9 # Not supported until Xcode 9
if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_LESS "9") if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_LESS "9")
@ -753,6 +759,7 @@ if(USE_ELF)
endif() endif()
if (USE_DISCORD_RPC) if (USE_DISCORD_RPC)
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.7")
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/third-party/discord-rpc discord-rpc EXCLUDE_FROM_ALL) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/third-party/discord-rpc discord-rpc EXCLUDE_FROM_ALL)
list(APPEND FEATURES DISCORD_RPC) list(APPEND FEATURES DISCORD_RPC)
include_directories(AFTER ${CMAKE_CURRENT_SOURCE_DIR}/src/third-party/discord-rpc/include) include_directories(AFTER ${CMAKE_CURRENT_SOURCE_DIR}/src/third-party/discord-rpc/include)

View File

@ -11,15 +11,15 @@
CXX_GUARD_START CXX_GUARD_START
struct mSize { struct mSize {
int width; int width;
int height; int height;
}; };
struct mRectangle { struct mRectangle {
int x; int x;
int y; int y;
int width; int width;
int height; int height;
}; };
void mRectangleUnion(struct mRectangle* dst, const struct mRectangle* add); void mRectangleUnion(struct mRectangle* dst, const struct mRectangle* add);

34
include/mgba-util/md5.h Normal file
View File

@ -0,0 +1,34 @@
/* Copyright (c) 2013-2014 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Based on https://github.com/Zunawe/md5-c
* Originally released under the Unlicense */
#ifndef MD5_H
#define MD5_H
#include <mgba-util/common.h>
CXX_GUARD_START
struct MD5Context {
size_t size; // Size of input in bytes
uint32_t buffer[4]; // Current accumulation of hash
uint8_t input[0x40]; // Input to be used in the next step
uint8_t digest[0x10]; // Result of algorithm
};
void md5Init(struct MD5Context* ctx);
void md5Update(struct MD5Context* ctx, const void* input, size_t len);
void md5Finalize(struct MD5Context* ctx);
void md5Buffer(const void* input, size_t len, uint8_t* result);
struct VFile;
bool md5File(struct VFile* vf, uint8_t* result);
CXX_GUARD_END
#endif

View File

@ -30,6 +30,7 @@ enum mPlatform {
enum mCoreChecksumType { enum mCoreChecksumType {
mCHECKSUM_CRC32, mCHECKSUM_CRC32,
mCHECKSUM_MD5,
}; };
struct mAudioBuffer; struct mAudioBuffer;

View File

@ -133,6 +133,7 @@ struct mRumbleIntegrator {
}; };
void mRumbleIntegratorInit(struct mRumbleIntegrator*); void mRumbleIntegratorInit(struct mRumbleIntegrator*);
void mRumbleIntegratorReset(struct mRumbleIntegrator*);
struct mCoreChannelInfo { struct mCoreChannelInfo {
size_t id; size_t id;

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2013-2016 Jeffrey Pfau /* Copyright (c) 2013-2024 Jeffrey Pfau
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
@ -53,6 +53,25 @@ static inline void mLockstepUnlock(struct mLockstep* lockstep) {
} }
} }
struct mLockstepUser {
void (*sleep)(struct mLockstepUser*);
void (*wake)(struct mLockstepUser*);
int (*requestedId)(struct mLockstepUser*);
void (*playerIdChanged)(struct mLockstepUser*, int id);
};
#ifndef DISABLE_THREADING
struct mCoreThread;
struct mLockstepThreadUser {
struct mLockstepUser d;
struct mCoreThread* thread;
};
void mLockstepThreadUserInit(struct mLockstepThreadUser* lockstep, struct mCoreThread* thread);
#endif
CXX_GUARD_END CXX_GUARD_END
#endif #endif

View File

@ -57,7 +57,7 @@ struct mVideoBackendCommand {
struct mVideoProxyBackend { struct mVideoProxyBackend {
struct VideoBackend d; struct VideoBackend d;
struct VideoBackend* backend; struct VideoBackend* backend;
struct RingFIFO in; struct RingFIFO in;
struct RingFIFO out; struct RingFIFO out;

View File

@ -68,7 +68,9 @@ enum GBAHardwareDevice {
HW_TILT = 16, HW_TILT = 16,
HW_GB_PLAYER = 32, HW_GB_PLAYER = 32,
HW_GB_PLAYER_DETECTION = 64, HW_GB_PLAYER_DETECTION = 64,
HW_EREADER = 128 HW_EREADER = 128,
HW_GPIO = HW_RTC | HW_RUMBLE | HW_LIGHT_SENSOR | HW_GYRO | HW_TILT,
}; };
struct Configuration; struct Configuration;
@ -81,7 +83,7 @@ extern MGBA_EXPORT const int GBA_LUX_LEVELS[10];
enum { enum {
mPERIPH_GBA_LUMINANCE = 0x1000, mPERIPH_GBA_LUMINANCE = 0x1000,
mPERIPH_GBA_BATTLECHIP_GATE, mPERIPH_GBA_LINK_PORT,
}; };
struct GBACartridgeOverride { struct GBACartridgeOverride {
@ -110,13 +112,22 @@ struct GBASIODriver {
bool (*init)(struct GBASIODriver* driver); bool (*init)(struct GBASIODriver* driver);
void (*deinit)(struct GBASIODriver* driver); void (*deinit)(struct GBASIODriver* driver);
bool (*load)(struct GBASIODriver* driver); void (*reset)(struct GBASIODriver* driver);
bool (*unload)(struct GBASIODriver* driver); uint32_t (*driverId)(const struct GBASIODriver* renderer);
uint16_t (*writeRegister)(struct GBASIODriver* driver, uint32_t address, uint16_t value); bool (*loadState)(struct GBASIODriver* renderer, const void* state, size_t size);
void (*saveState)(struct GBASIODriver* renderer, void** state, size_t* size);
void (*setMode)(struct GBASIODriver* driver, enum GBASIOMode mode);
bool (*handlesMode)(struct GBASIODriver* driver, enum GBASIOMode mode);
int (*connectedDevices)(struct GBASIODriver* driver);
int (*deviceId)(struct GBASIODriver* driver);
uint16_t (*writeSIOCNT)(struct GBASIODriver* driver, uint16_t value);
uint16_t (*writeRCNT)(struct GBASIODriver* driver, uint16_t value);
bool (*start)(struct GBASIODriver* driver);
void (*finishMultiplayer)(struct GBASIODriver* driver, uint16_t data[4]);
uint8_t (*finishNormal8)(struct GBASIODriver* driver);
uint32_t (*finishNormal32)(struct GBASIODriver* driver);
}; };
void GBASIOJOYCreate(struct GBASIODriver* sio);
enum GBASIOBattleChipGateFlavor { enum GBASIOBattleChipGateFlavor {
GBA_FLAVOR_BATTLECHIP_GATE = 4, GBA_FLAVOR_BATTLECHIP_GATE = 4,
GBA_FLAVOR_PROGRESS_GATE = 5, GBA_FLAVOR_PROGRESS_GATE = 5,
@ -126,7 +137,6 @@ enum GBASIOBattleChipGateFlavor {
struct GBASIOBattlechipGate { struct GBASIOBattlechipGate {
struct GBASIODriver d; struct GBASIODriver d;
struct mTimingEvent event;
uint16_t chipId; uint16_t chipId;
uint16_t data[2]; uint16_t data[2];
int state; int state;

View File

@ -9,23 +9,23 @@
#define mSAVEDATA_CLEANUP_THRESHOLD 15 #define mSAVEDATA_CLEANUP_THRESHOLD 15
enum { enum {
mSAVEDATA_DIRT_NONE = 0, mSAVEDATA_DIRT_NONE = 0,
mSAVEDATA_DIRT_NEW = 1, mSAVEDATA_DIRT_NEW = 1,
mSAVEDATA_DIRT_SEEN = 2, mSAVEDATA_DIRT_SEEN = 2,
}; };
static inline bool mSavedataClean(int* dirty, uint32_t* dirtAge, uint32_t frameCount) { static inline bool mSavedataClean(int* dirty, uint32_t* dirtAge, uint32_t frameCount) {
if (*dirty & mSAVEDATA_DIRT_NEW) { if (*dirty & mSAVEDATA_DIRT_NEW) {
*dirtAge = frameCount; *dirtAge = frameCount;
*dirty &= ~mSAVEDATA_DIRT_NEW; *dirty &= ~mSAVEDATA_DIRT_NEW;
if (!(*dirty & mSAVEDATA_DIRT_SEEN)) { if (!(*dirty & mSAVEDATA_DIRT_SEEN)) {
*dirty |= mSAVEDATA_DIRT_SEEN; *dirty |= mSAVEDATA_DIRT_SEEN;
} }
} else if ((*dirty & mSAVEDATA_DIRT_SEEN) && frameCount - *dirtAge > mSAVEDATA_CLEANUP_THRESHOLD) { } else if ((*dirty & mSAVEDATA_DIRT_SEEN) && frameCount - *dirtAge > mSAVEDATA_CLEANUP_THRESHOLD) {
*dirty = 0; *dirty = 0;
return true; return true;
} }
return false; return false;
} }
#endif #endif

View File

@ -86,6 +86,7 @@ struct GBACartridgeHardware {
}; };
void GBAHardwareInit(struct GBACartridgeHardware* gpio, uint16_t* gpioBase); void GBAHardwareInit(struct GBACartridgeHardware* gpio, uint16_t* gpioBase);
void GBAHardwareReset(struct GBACartridgeHardware* gpio);
void GBAHardwareClear(struct GBACartridgeHardware* gpio); void GBAHardwareClear(struct GBACartridgeHardware* gpio);
void GBAHardwareInitRTC(struct GBACartridgeHardware* gpio); void GBAHardwareInitRTC(struct GBACartridgeHardware* gpio);

View File

@ -0,0 +1,77 @@
/* Copyright (c) 2013-2024 Jeffrey Pfau
* Copyright (c) 2016 taizou
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GBA_UNLICENSED_H
#define GBA_UNLICENSED_H
#include <mgba-util/common.h>
CXX_GUARD_START
#include <mgba/core/timing.h>
enum GBAVFameCartType {
VFAME_STANDARD = 0,
VFAME_GEORGE = 1,
VFAME_ALTERNATE = 2,
};
enum GBAUnlCartType {
GBA_UNL_CART_NONE = 0,
GBA_UNL_CART_VFAME = 1,
GBA_UNL_CART_MULTICART = 2,
};
struct GBAVFameCart {
enum GBAVFameCartType cartType;
int sramMode;
int romMode;
int8_t writeSequence[5];
bool acceptingModeChange;
};
struct GBAMulticart {
struct mTimingEvent settle;
uint32_t* rom;
size_t fullSize;
uint8_t bank;
uint8_t offset;
uint8_t size;
bool sramActive;
bool locked;
uint8_t unk;
};
struct GBAUnlCart {
enum GBAUnlCartType type;
union {
struct GBAVFameCart vfame;
struct GBAMulticart multi;
};
};
struct GBA;
struct GBAMemory;
void GBAUnlCartInit(struct GBA*);
void GBAUnlCartReset(struct GBA*);
void GBAUnlCartUnload(struct GBA*);
void GBAUnlCartDetect(struct GBA*);
void GBAUnlCartWriteSRAM(struct GBA*, uint32_t address, uint8_t value);
void GBAUnlCartWriteROM(struct GBA*, uint32_t address, uint16_t value);
struct GBASerializedState;
void GBAUnlCartSerialize(const struct GBA* gba, struct GBASerializedState* state);
void GBAUnlCartDeserialize(struct GBA* gba, const struct GBASerializedState* state);
bool GBAVFameDetect(struct GBAVFameCart* cart, uint32_t* rom, size_t romSize, uint32_t crc32);
void GBAVFameSramWrite(struct GBAVFameCart* cart, uint32_t address, uint8_t value, uint8_t* sramData);
uint32_t GBAVFameModifyRomAddress(struct GBAVFameCart* cart, uint32_t address, size_t romSize);
uint32_t GBAVFameGetPatternValue(uint32_t address, int bits);
CXX_GUARD_END
#endif

View File

@ -1,41 +0,0 @@
/* Copyright (c) 2016 taizou
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Support for copy protected unlicensed games from the company Vast Fame
#ifndef GBA_VFAME_H
#define GBA_VFAME_H
#include <mgba-util/common.h>
CXX_GUARD_START
#define DIGIMON_SAPPHIRE_CHINESE_CRC32 0x793A328F
enum GBAVFameCartType {
VFAME_NO = 0,
VFAME_STANDARD = 1,
VFAME_GEORGE = 2,
VFAME_ALTERNATE = 3,
};
struct GBAVFameCart {
enum GBAVFameCartType cartType;
int sramMode;
int romMode;
int8_t writeSequence[5];
bool acceptingModeChange;
};
void GBAVFameInit(struct GBAVFameCart* cart);
void GBAVFameDetect(struct GBAVFameCart* cart, uint32_t* rom, size_t romSize, uint32_t crc32);
void GBAVFameSramWrite(struct GBAVFameCart* cart, uint32_t address, uint8_t value, uint8_t* sramData);
uint32_t GBAVFameModifyRomAddress(struct GBAVFameCart* cart, uint32_t address, size_t romSize);
uint32_t GBAVFameGetPatternValue(uint32_t address, int bits);
CXX_GUARD_END
#endif

View File

@ -48,6 +48,7 @@ struct GBADMA {
uint32_t nextDest; uint32_t nextDest;
int32_t nextCount; int32_t nextCount;
uint32_t when; uint32_t when;
int32_t cycles;
}; };
struct GBA; struct GBA;
@ -65,6 +66,7 @@ void GBADMARunHblank(struct GBA* gba, int32_t cycles);
void GBADMARunVblank(struct GBA* gba, int32_t cycles); void GBADMARunVblank(struct GBA* gba, int32_t cycles);
void GBADMARunDisplayStart(struct GBA* gba, int32_t cycles); void GBADMARunDisplayStart(struct GBA* gba, int32_t cycles);
void GBADMAUpdate(struct GBA* gba); void GBADMAUpdate(struct GBA* gba);
void GBADMARecalculateCycles(struct GBA* gba);
CXX_GUARD_END CXX_GUARD_END

View File

@ -18,7 +18,7 @@ CXX_GUARD_START
#include <mgba/internal/gba/cart/ereader.h> #include <mgba/internal/gba/cart/ereader.h>
#include <mgba/internal/gba/cart/gpio.h> #include <mgba/internal/gba/cart/gpio.h>
#include <mgba/internal/gba/cart/matrix.h> #include <mgba/internal/gba/cart/matrix.h>
#include <mgba/internal/gba/cart/vfame.h> #include <mgba/internal/gba/cart/unlicensed.h>
enum GBAMemoryRegion { enum GBAMemoryRegion {
GBA_REGION_BIOS = 0x0, GBA_REGION_BIOS = 0x0,
@ -108,8 +108,8 @@ struct GBAMemory {
struct GBACartridgeHardware hw; struct GBACartridgeHardware hw;
struct GBASavedata savedata; struct GBASavedata savedata;
struct GBAVFameCart vfame;
struct GBAMatrix matrix; struct GBAMatrix matrix;
struct GBAUnlCart unl;
struct GBACartEReader ereader; struct GBACartEReader ereader;
size_t romSize; size_t romSize;
uint32_t romMask; uint32_t romMask;

View File

@ -91,6 +91,7 @@ struct GBASavedataRTCBuffer {
}; };
void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf); void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf);
void GBASavedataReset(struct GBASavedata* savedata);
void GBASavedataDeinit(struct GBASavedata* savedata); void GBASavedataDeinit(struct GBASavedata* savedata);
void GBASavedataMask(struct GBASavedata* savedata, struct VFile* vf, bool writeback); void GBASavedataMask(struct GBASavedata* savedata, struct VFile* vf, bool writeback);

View File

@ -186,12 +186,15 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* | bit 3: Reserved * | bit 3: Reserved
* | bits 4 - 15: Light counter * | bits 4 - 15: Light counter
* | 0x002C0 - 0x002C0: Light sample * | 0x002C0 - 0x002C0: Light sample
* | 0x002C1 - 0x002C3: Flags * | 0x002C1: Flags
* | bits 0 - 1: Tilt state machine * | bits 0 - 1: Tilt state machine
* | bits 2 - 3: GB Player inputs posted * | bits 2 - 3: GB Player inputs posted
* | bits 4 - 8: GB Player transmit position * | bits 4 - 7: GB Player transmit position
* | bits 9 - 23: Reserved * | 0x002C2 - 0x002C3: Unlicensed cart flags
* 0x002C4 - 0x002C7: Game Boy Player next event * | bits 0 - 4: Cartridge type
* | bits 5 - 7: Cartridge subtype
* | bits 8 - 15: Reserved
* 0x002C4 - 0x002C7: SIO next event
* 0x002C8 - 0x002CB: Current DMA transfer word * 0x002C8 - 0x002CB: Current DMA transfer word
* 0x002CC - 0x002CF: Last DMA transfer PC * 0x002CC - 0x002CF: Last DMA transfer PC
* 0x002D0 - 0x002DF: Matrix memory command buffer * 0x002D0 - 0x002DF: Matrix memory command buffer
@ -230,8 +233,23 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* | bits 15 - 31: Reserved * | bits 15 - 31: Reserved
* 0x00320 - 0x00323: Next IRQ event * 0x00320 - 0x00323: Next IRQ event
* 0x00324 - 0x00327: Interruptable BIOS stall cycles * 0x00324 - 0x00327: Interruptable BIOS stall cycles
* 0x00328 - 0x00367: Matrix memory mapping table * 0x00328 - 0x0036F: Special cartridge state, one of:
* 0x00368 - 0x0036F: Reserved (leave zero) * | Matrix Memory:
* | 0x00328 - 0x00367: Matrix memory mapping table
* | 0x00368 - 0x0036F: Reserved (leave zero)
* | Unlicensed multicart:
* | 0x00328: Bank value
* | 0x00329: Offset value
* | 0x0032A: Size value
* | 0x0032B: SRAM active value
* | 0x0032C: Unknown value
* | 0x0032D: Current size
* | 0x0032E - 0x0032F: Current bank/offset
* | 0x00330 - 0x00333: Next settle event
* | 0x00334 - 0x00337: Flags
* | bit 0: Is settling occuring?
* | bits 1 - 31: Reserved
* | 0x00338 - 0x0036F: Reserved (leave zero)
* 0x00370 - 0x0037F: Audio FIFO A samples * 0x00370 - 0x0037F: Audio FIFO A samples
* 0x00380 - 0x0038F: Audio FIFO B samples * 0x00380 - 0x0038F: Audio FIFO B samples
* 0x00390 - 0x003CF: Audio rendered samples * 0x00390 - 0x003CF: Audio rendered samples
@ -269,9 +287,15 @@ DECL_BITS(GBASerializedHWFlags1, LightCounter, 4, 12);
DECL_BITFIELD(GBASerializedHWFlags2, uint8_t); DECL_BITFIELD(GBASerializedHWFlags2, uint8_t);
DECL_BITS(GBASerializedHWFlags2, TiltState, 0, 2); DECL_BITS(GBASerializedHWFlags2, TiltState, 0, 2);
DECL_BITS(GBASerializedHWFlags2, GbpInputsPosted, 2, 2); DECL_BITS(GBASerializedHWFlags2, GbpInputsPosted, 2, 2);
DECL_BITS(GBASerializedHWFlags2, GbpTxPosition, 4, 5); DECL_BITS(GBASerializedHWFlags2, GbpTxPosition, 4, 4);
DECL_BITFIELD(GBASerializedHWFlags3, uint16_t); DECL_BITFIELD(GBASerializedUnlCartFlags, uint16_t);
DECL_BITS(GBASerializedUnlCartFlags, Type, 0, 5);
DECL_BITS(GBASerializedUnlCartFlags, Subtype, 5, 3);
DECL_BITFIELD(GBASerializedMulticartFlags, uint32_t);
DECL_BIT(GBASerializedMulticartFlags, DustSettling, 0);
DECL_BIT(GBASerializedMulticartFlags, Locked, 1);
DECL_BITFIELD(GBASerializedSavedataFlags, uint8_t); DECL_BITFIELD(GBASerializedSavedataFlags, uint8_t);
DECL_BITS(GBASerializedSavedataFlags, FlashState, 0, 2); DECL_BITS(GBASerializedSavedataFlags, FlashState, 0, 2);
@ -287,6 +311,7 @@ DECL_BITS(GBASerializedMiscFlags, KeyIRQKeys, 4, 11);
enum { enum {
GBA_SUBSYSTEM_VIDEO_RENDERER = 0, GBA_SUBSYSTEM_VIDEO_RENDERER = 0,
GBA_SUBSYSTEM_SIO_DRIVER = 1,
GBA_SUBSYSTEM_MAX, GBA_SUBSYSTEM_MAX,
}; };
@ -369,8 +394,8 @@ struct GBASerializedState {
GBASerializedHWFlags1 flags1; GBASerializedHWFlags1 flags1;
uint8_t lightSample; uint8_t lightSample;
GBASerializedHWFlags2 flags2; GBASerializedHWFlags2 flags2;
GBASerializedHWFlags3 flags3; GBASerializedUnlCartFlags unlCartFlags;
uint32_t gbpNextEvent; uint32_t sioNextEvent;
} hw; } hw;
uint32_t dmaTransferRegister; uint32_t dmaTransferRegister;
@ -406,8 +431,29 @@ struct GBASerializedState {
uint32_t nextIrq; uint32_t nextIrq;
int32_t biosStall; int32_t biosStall;
uint32_t matrixMappings[16]; union {
uint32_t reservedMatrix[2]; struct {
uint32_t mappings[16];
uint32_t reserved[2];
} matrix2;
struct {
uint8_t bank;
uint8_t offset;
uint8_t size;
uint8_t sramActive;
uint8_t unk;
uint8_t currentSize;
uint16_t currentOffset;
uint32_t settleNextEvent;
GBASerializedMulticartFlags flags;
} multicart;
struct {
int16_t sramMode;
int16_t romMode;
int8_t writeSequence[5];
bool acceptingModeChange;
} vfame;
};
struct { struct {
int8_t chA[16]; int8_t chA[16];

View File

@ -16,8 +16,6 @@ CXX_GUARD_START
#define MAX_GBAS 4 #define MAX_GBAS 4
extern const int GBASIOCyclesPerTransfer[4][MAX_GBAS];
mLOG_DECLARE_CATEGORY(GBA_SIO); mLOG_DECLARE_CATEGORY(GBA_SIO);
enum { enum {
@ -54,37 +52,45 @@ DECL_BITS(GBASIOMultiplayer, Id, 4, 2);
DECL_BIT(GBASIOMultiplayer, Error, 6); DECL_BIT(GBASIOMultiplayer, Error, 6);
DECL_BIT(GBASIOMultiplayer, Busy, 7); DECL_BIT(GBASIOMultiplayer, Busy, 7);
DECL_BIT(GBASIOMultiplayer, Irq, 14); DECL_BIT(GBASIOMultiplayer, Irq, 14);
DECL_BITFIELD(GBASIORegisterRCNT, uint16_t);
struct GBASIODriverSet { DECL_BIT(GBASIORegisterRCNT, Sc, 0);
struct GBASIODriver* normal; DECL_BIT(GBASIORegisterRCNT, Sd, 1);
struct GBASIODriver* multiplayer; DECL_BIT(GBASIORegisterRCNT, Si, 2);
struct GBASIODriver* joybus; DECL_BIT(GBASIORegisterRCNT, So, 3);
}; DECL_BIT(GBASIORegisterRCNT, ScDirection, 4);
DECL_BIT(GBASIORegisterRCNT, SdDirection, 5);
DECL_BIT(GBASIORegisterRCNT, SiDirection, 6);
DECL_BIT(GBASIORegisterRCNT, SoDirection, 7);
struct GBASIO { struct GBASIO {
struct GBA* p; struct GBA* p;
enum GBASIOMode mode; enum GBASIOMode mode;
struct GBASIODriverSet drivers; struct GBASIODriver* driver;
struct GBASIODriver* activeDriver;
uint16_t rcnt; uint16_t rcnt;
uint16_t siocnt; uint16_t siocnt;
struct GBASIOPlayer gbp; struct GBASIOPlayer gbp;
struct mTimingEvent completeEvent;
}; };
void GBASIOInit(struct GBASIO* sio); void GBASIOInit(struct GBASIO* sio);
void GBASIODeinit(struct GBASIO* sio); void GBASIODeinit(struct GBASIO* sio);
void GBASIOReset(struct GBASIO* sio); void GBASIOReset(struct GBASIO* sio);
void GBASIOSetDriverSet(struct GBASIO* sio, struct GBASIODriverSet* drivers); void GBASIOSetDriver(struct GBASIO* sio, struct GBASIODriver* driver);
void GBASIOSetDriver(struct GBASIO* sio, struct GBASIODriver* driver, enum GBASIOMode mode);
void GBASIOWriteRCNT(struct GBASIO* sio, uint16_t value); void GBASIOWriteRCNT(struct GBASIO* sio, uint16_t value);
void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value); void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value);
uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t value); uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t value);
int32_t GBASIOTransferCycles(enum GBASIOMode mode, uint16_t siocnt, int connected);
void GBASIOMultiplayerFinishTransfer(struct GBASIO* sio, uint16_t data[4], uint32_t cyclesLate);
void GBASIONormal8FinishTransfer(struct GBASIO* sio, uint8_t data, uint32_t cyclesLate);
void GBASIONormal32FinishTransfer(struct GBASIO* sio, uint32_t data, uint32_t cyclesLate);
int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command, uint8_t* data); int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command, uint8_t* data);
CXX_GUARD_END CXX_GUARD_END

View File

@ -21,7 +21,6 @@ struct GBASIOPlayer {
struct GBA* p; struct GBA* p;
unsigned inputsPosted; unsigned inputsPosted;
int txPosition; int txPosition;
struct mTimingEvent event;
struct GBASIOPlayerKeyCallback callback; struct GBASIOPlayerKeyCallback callback;
bool oldOpposingDirections; bool oldOpposingDirections;
struct mKeyCallback* oldCallback; struct mKeyCallback* oldCallback;

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau /* Copyright (c) 2013-2024 Jeffrey Pfau
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
@ -13,40 +13,82 @@ CXX_GUARD_START
#include <mgba/core/lockstep.h> #include <mgba/core/lockstep.h>
#include <mgba/core/timing.h> #include <mgba/core/timing.h>
#include <mgba/internal/gba/sio.h> #include <mgba/internal/gba/sio.h>
#include <mgba-util/circle-buffer.h>
#include <mgba-util/table.h>
#include <mgba-util/threading.h>
struct GBASIOLockstep { #define MAX_LOCKSTEP_EVENTS 8
struct mLockstep d;
struct GBASIOLockstepNode* players[MAX_GBAS];
int attachedMulti;
int attachedNormal;
uint16_t multiRecv[MAX_GBAS]; enum GBASIOLockstepEventType {
uint32_t normalRecv[MAX_GBAS]; SIO_EV_ATTACH,
SIO_EV_DETACH,
SIO_EV_HARD_SYNC,
SIO_EV_MODE_SET,
SIO_EV_TRANSFER_START,
}; };
struct GBASIOLockstepNode { struct GBASIOLockstepCoordinator {
struct GBASIODriver d; struct Table players;
struct GBASIOLockstep* p; Mutex mutex;
struct mTimingEvent event;
volatile int32_t nextEvent; unsigned nextId;
int32_t eventDiff;
bool normalSO; unsigned attachedPlayers[MAX_GBAS];
int id; int nAttached;
uint32_t waiting;
bool transferActive;
enum GBASIOMode transferMode;
int32_t cycle;
int32_t nextHardSync;
uint16_t multiData[4];
uint32_t normalData[4];
};
struct GBASIOLockstepEvent {
enum GBASIOLockstepEventType type;
int32_t timestamp;
struct GBASIOLockstepEvent* next;
int playerId;
union {
enum GBASIOMode mode;
int32_t finishCycle;
};
};
struct GBASIOLockstepPlayer {
struct GBASIOLockstepDriver* driver;
int playerId;
enum GBASIOMode mode; enum GBASIOMode mode;
bool transferFinished; enum GBASIOMode otherModes[MAX_GBAS];
#ifndef NDEBUG bool asleep;
int transferId; int32_t cycleOffset;
enum mLockstepPhase phase; struct GBASIOLockstepEvent* queue;
#endif bool dataReceived;
struct GBASIOLockstepEvent buffer[MAX_LOCKSTEP_EVENTS];
struct GBASIOLockstepEvent* freeList;
}; };
void GBASIOLockstepInit(struct GBASIOLockstep*); struct GBASIOLockstepDriver {
struct GBASIODriver d;
struct GBASIOLockstepCoordinator* coordinator;
struct mTimingEvent event;
unsigned lockstepId;
void GBASIOLockstepNodeCreate(struct GBASIOLockstepNode*); struct mLockstepUser* user;
};
bool GBASIOLockstepAttachNode(struct GBASIOLockstep*, struct GBASIOLockstepNode*); void GBASIOLockstepCoordinatorInit(struct GBASIOLockstepCoordinator*);
void GBASIOLockstepDetachNode(struct GBASIOLockstep*, struct GBASIOLockstepNode*); void GBASIOLockstepCoordinatorDeinit(struct GBASIOLockstepCoordinator*);
void GBASIOLockstepCoordinatorAttach(struct GBASIOLockstepCoordinator*, struct GBASIOLockstepDriver*);
void GBASIOLockstepCoordinatorDetach(struct GBASIOLockstepCoordinator*, struct GBASIOLockstepDriver*);
size_t GBASIOLockstepCoordinatorAttached(struct GBASIOLockstepCoordinator*);
void GBASIOLockstepDriverCreate(struct GBASIOLockstepDriver*, struct mLockstepUser*);
CXX_GUARD_END CXX_GUARD_END

View File

@ -76,10 +76,14 @@ CXX_GUARD_START
#define _mSCRIPT_FIELD_NAME(V) (V)->type->name #define _mSCRIPT_FIELD_NAME(V) (V)->type->name
#define _mSCRIPT_WRAPPED_FIELD_NAME(V) (V)->value.wrapped->type->name #define _mSCRIPT_WRAPPED_FIELD_NAME(V) (V)->value.wrapped->type->name
#define _mSCRIPT_CALL_VOID(FUNCTION, NPARAMS) FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS)) #define _mSCRIPT_CALL_VOID(FUNCTION, NPARAMS) \
FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS)); \
mScriptListClear(&frame->stack)
#define _mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS) \ #define _mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS) \
mSCRIPT_TYPE_C_ ## RETURN out = FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS)); \ mSCRIPT_TYPE_C_ ## RETURN out = FUNCTION(_mCAT(mSCRIPT_ARG_NAMES_, NPARAMS)); \
mSCRIPT_PUSH(&frame->returnValues, RETURN, out) mScriptListClear(&frame->stack); \
mSCRIPT_PUSH(&frame->stack, RETURN, out)
#define mSCRIPT_DECLARE_STRUCT(STRUCT) \ #define mSCRIPT_DECLARE_STRUCT(STRUCT) \
extern const struct mScriptType mSTStruct_ ## STRUCT; \ extern const struct mScriptType mSTStruct_ ## STRUCT; \
@ -249,8 +253,8 @@ CXX_GUARD_START
}, },
#define _mSCRIPT_STRUCT_METHOD_POP(TYPE, S, NPARAMS, ...) \ #define _mSCRIPT_STRUCT_METHOD_POP(TYPE, S, NPARAMS, ...) \
_mCALL(_mCAT(mSCRIPT_POP_, _mSUCC_ ## NPARAMS), &frame->arguments, _mCOMMA_ ## NPARAMS(S(TYPE), __VA_ARGS__)); \ _mCALL(_mCAT(mSCRIPT_POP_, _mSUCC_ ## NPARAMS), &frame->stack, _mCOMMA_ ## NPARAMS(S(TYPE), __VA_ARGS__)); \
if (mScriptListSize(&frame->arguments)) { \ if (mScriptListSize(&frame->stack)) { \
return false; \ return false; \
} }
@ -326,11 +330,11 @@ CXX_GUARD_START
static const struct mScriptFunctionOverload _mSTStructBindingOverloads_ ## TYPE ## _ ## NAME[mSCRIPT_OVERLOADS_MAX]; \ static const struct mScriptFunctionOverload _mSTStructBindingOverloads_ ## TYPE ## _ ## NAME[mSCRIPT_OVERLOADS_MAX]; \
static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \ static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* ctx) { \
UNUSED(ctx); \ UNUSED(ctx); \
const struct mScriptFunctionOverload* overload = mScriptFunctionFindOverload(_mSTStructBindingOverloads_ ## TYPE ## _ ## NAME, &frame->arguments); \ const struct mScriptFunctionOverload* overload = mScriptFunctionFindOverload(_mSTStructBindingOverloads_ ## TYPE ## _ ## NAME, &frame->stack); \
if (!overload) { \ if (!overload) { \
return false; \ return false; \
} \ } \
if (!mScriptCoerceFrame(&overload->type->details.function.parameters, &frame->arguments, &frame->arguments)) { \ if (!mScriptCoerceFrame(&overload->type->details.function.parameters, &frame->stack, &frame->stack)) { \
return false; \ return false; \
} \ } \
return overload->function->call(frame, overload->function->context); \ return overload->function->call(frame, overload->function->context); \
@ -552,8 +556,8 @@ CXX_GUARD_START
#define _mSCRIPT_BIND_N_FUNCTION(NAME, RETURN, FUNCTION, DEFAULTS, NPARAMS, ...) \ #define _mSCRIPT_BIND_N_FUNCTION(NAME, RETURN, FUNCTION, DEFAULTS, NPARAMS, ...) \
static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \ static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \
UNUSED(ctx); \ UNUSED(ctx); \
_mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ _mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->stack, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
if (mScriptListSize(&frame->arguments)) { \ if (mScriptListSize(&frame->stack)) { \
return false; \ return false; \
} \ } \
_mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS); \ _mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS); \
@ -564,8 +568,8 @@ CXX_GUARD_START
#define _mSCRIPT_BIND_VOID_FUNCTION(NAME, FUNCTION, DEFAULTS, NPARAMS, ...) \ #define _mSCRIPT_BIND_VOID_FUNCTION(NAME, FUNCTION, DEFAULTS, NPARAMS, ...) \
static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \ static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \
UNUSED(ctx); \ UNUSED(ctx); \
_mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \ _mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->stack, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
if (mScriptListSize(&frame->arguments)) { \ if (mScriptListSize(&frame->stack)) { \
return false; \ return false; \
} \ } \
_mSCRIPT_CALL_VOID(FUNCTION, NPARAMS); \ _mSCRIPT_CALL_VOID(FUNCTION, NPARAMS); \

View File

@ -325,8 +325,7 @@ struct mScriptString {
}; };
struct mScriptFrame { struct mScriptFrame {
struct mScriptList arguments; struct mScriptList stack;
struct mScriptList returnValues;
// TODO: Exception/failure codes // TODO: Exception/failure codes
}; };

View File

@ -10,3 +10,4 @@ Comment=Nintendo Game Boy Advance Emulator
Categories=Game;Emulator; Categories=Game;Emulator;
MimeType=application/x-gameboy-advance-rom;application/x-agb-rom;application/x-gba-rom; MimeType=application/x-gameboy-advance-rom;application/x-agb-rom;application/x-gba-rom;
Keywords=emulator;Nintendo;advance;gba;Game Boy Advance; Keywords=emulator;Nintendo;advance;gba;Game Boy Advance;
StartupWMClass=mGBA

View File

@ -0,0 +1,122 @@
input_display = {
anchor = "top",
offset = {
x = 0,
y = 0,
}
}
local state = {
drawButton = {
[0] = function(state) -- A
state.painter:drawCircle(27, 6, 4)
end,
[1] = function(state) -- B
state.painter:drawCircle(23, 8, 4)
end,
[2] = function(state) -- Select
state.painter:drawCircle(13, 11, 3)
end,
[3] = function(state) -- Start
state.painter:drawCircle(18, 11, 3)
end,
[4] = function(state) -- Right
state.painter:drawRectangle(9, 7, 4, 3)
end,
[5] = function(state) -- Left
state.painter:drawRectangle(2, 7, 4, 3)
end,
[6] = function(state) -- Up
state.painter:drawRectangle(6, 3, 3, 4)
end,
[7] = function(state) -- Down
state.painter:drawRectangle(6, 10, 3, 4)
end,
[8] = function(state) -- R
state.painter:drawRectangle(28, 0, 4, 3)
end,
[9] = function(state) -- L
state.painter:drawRectangle(0, 0, 4, 3)
end
},
maxKey = {
[C.PLATFORM.GBA] = 9,
[C.PLATFORM.GB] = 7,
}
}
state.overlay = canvas:newLayer(32, 16)
state.painter = image.newPainter(state.overlay.image)
state.painter:setBlend(false)
state.painter:setFill(true)
function state.update()
local keys = util.expandBitmask(emu:getKeys())
local maxKey = state.maxKey[emu:platform()]
for key = 0, maxKey do
if emu:getKey(key) ~= 0 then
state.painter:setFillColor(0x80FFFFFF)
else
state.painter:setFillColor(0x40404040)
end
state.drawButton[key](state)
end
state.overlay:update()
end
function state.reset()
local endX = canvas:screenWidth() - 32
local endY = canvas:screenHeight() - 16
local anchors = {
topLeft = {
x = 0,
y = 0
},
top = {
x = endX / 2,
y = 0
},
topRight = {
x = endX,
y = 0
},
left = {
x = 0,
y = endY / 2
},
center = {
x = endX / 2,
y = endY / 2
},
right = {
x = endX,
y = endY / 2
},
bottomLeft = {
x = 0,
y = endY
},
bottom = {
x = endX / 2,
y = endY
},
bottomRight = {
x = endX,
y = endY
},
}
local pos = anchors[input_display.anchor];
pos.x = pos.x + input_display.offset.x;
pos.y = pos.y + input_display.offset.y;
state.overlay:setPosition(pos.x, pos.y);
state.painter:setFillColor(0x40808080)
state.painter:drawRectangle(0, 0, 32, 16)
state.overlay:update()
end
state.reset()
callbacks:add("frame", state.update)
callbacks:add("start", state.reset)

View File

@ -1,34 +1,21 @@
// Shader that replicates the LCD Colorspace from Gameboy Advance --
varying vec2 texCoord; varying vec2 texCoord;
varying mat4 profile;
uniform sampler2D tex; uniform sampler2D tex;
uniform vec2 texSize; uniform vec2 texSize;
uniform float darken_screen; uniform float darken_screen;
const float target_gamma = 2.2; const float target_gamma = 2.0;
const float display_gamma = 2.5; const float display_gamma = 2.0;
const float sat = 1.0;
const float lum = 0.99;
const float contrast = 1.0;
const vec3 bl = vec3(0.0, 0.0, 0.0);
const vec3 r = vec3(0.84, 0.09, 0.15);
const vec3 g = vec3(0.18, 0.67, 0.10);
const vec3 b = vec3(0.0, 0.26, 0.73);
void main() { void main() {
// bring out our stored luminance value
float lum = profile[3].w;
// our adjustments need to happen in linear gamma
vec4 screen = pow(texture2D(tex, texCoord), vec4(target_gamma + darken_screen)).rgba; vec4 screen = pow(texture2D(tex, texCoord), vec4(target_gamma + darken_screen)).rgba;
vec4 avglum = vec4(0.5);
screen = mix(screen, avglum, (1.0 - contrast));
mat4 color = mat4( r.r, r.g, r.b, 0.0,
g.r, g.g, g.b, 0.0,
b.r, b.g, b.b, 0.0,
bl.r, bl.g, bl.b, 1.0);
mat4 adjust = mat4( (1.0 - sat) * 0.3086 + sat, (1.0 - sat) * 0.3086, (1.0 - sat) * 0.3086, 1.0,
(1.0 - sat) * 0.6094, (1.0 - sat) * 0.6094 + sat, (1.0 - sat) * 0.6094, 1.0,
(1.0 - sat) * 0.0820, (1.0 - sat) * 0.0820, (1.0 - sat) * 0.0820 + sat, 1.0,
0.0, 0.0, 0.0, 1.0);
color *= adjust;
screen = clamp(screen * lum, 0.0, 1.0); screen = clamp(screen * lum, 0.0, 1.0);
screen = color * screen; screen = profile * screen;
gl_FragColor = pow(screen, vec4(1.0 / display_gamma + (darken_screen * 0.125))); gl_FragColor = pow(screen, vec4(1.0 / display_gamma));
} }

View File

@ -0,0 +1,34 @@
uniform int color_mode;
attribute vec4 position;
varying vec2 texCoord;
varying mat4 profile;
const mat4 GBA_sRGB = mat4(
0.80, 0.135, 0.195, 0.0, //red channel
0.275, 0.64, 0.155, 0.0, //green channel
-0.075, 0.225, 0.65, 0.0, //blue channel
0.0, 0.0, 0.0, 0.93 //alpha channel
);
const mat4 GBA_DCI = mat4(
0.685, 0.16, 0.20, 0.0, //red channel
0.34, 0.629, 0.19, 0.0, //green channel
-0.025, 0.211, 0.61, 0.0, //blue channel
0.0, 0.0, 0.0, 0.975 //alpha channel
);
const mat4 GBA_Rec2020 = mat4(
0.555, 0.1825, 0.20, 0.0, //red channel
0.395, 0.61, 0.195, 0.0, //green channel
0.05, 0.2075, 0.605, 0.0, //blue channel
0.0, 0.0, 0.0, 1.0 //alpha channel
);
void main() {
if (color_mode == 1) profile = GBA_sRGB;
else if (color_mode == 2) profile = GBA_DCI;
else if (color_mode == 3) profile = GBA_Rec2020;
gl_Position = position;
texCoord = (position.st + vec2(1.0, 1.0)) * vec2(0.5, 0.5);
}

View File

@ -6,6 +6,7 @@ passes=1
[pass.0] [pass.0]
fragmentShader=gba-color.fs fragmentShader=gba-color.fs
vertexShader=gba-color.vs
blend=1 blend=1
width=-1 width=-1
height=-1 height=-1
@ -14,3 +15,10 @@ height=-1
type=float type=float
default=0.5 default=0.5
readableName=Darken Screen readableName=Darken Screen
[pass.0.uniform.color_mode]
type=int
default=1
min=1
max=3
readableName=Color Profile (1=sRGB, 2=DCI, 3=Rec2020)

View File

@ -0,0 +1,24 @@
[shader]
name=NSO GBA Color
author=Pokefan531 and hunterk
description=Shader that replicates the Nintendo Switch Online's GBA color filter.
passes=1
[pass.0]
fragmentShader=nso-gba-color.fs
vertexShader=nso-gba-color.vs
blend=1
width=-1
height=-1
[pass.0.uniform.darken_screen]
type=float
default=0.8
readableName=Darken Screen
[pass.0.uniform.color_mode]
type=int
default=1
min=1
max=3
readableName=Color Profile (1=sRGB, 2=DCI, 3=Rec2020)

View File

@ -0,0 +1,21 @@
// Shader that replicates the LCD Colorspace from Gameboy Advance --
varying vec2 texCoord;
varying mat4 profile;
uniform sampler2D tex;
uniform vec2 texSize;
uniform float darken_screen;
const float target_gamma = 2.2;
const float display_gamma = 2.2;
void main() {
// bring out our stored luminance value
float lum = profile[3].w;
// our adjustments need to happen in linear gamma
vec4 screen = pow(texture2D(tex, texCoord), vec4(target_gamma + darken_screen)).rgba;
screen = clamp(screen * lum, 0.0, 1.0);
screen = profile * screen;
gl_FragColor = pow(screen, vec4(1.0 / display_gamma));
}

View File

@ -0,0 +1,34 @@
uniform int color_mode;
attribute vec4 position;
varying vec2 texCoord;
varying mat4 profile;
const mat4 GBA_sRGB = mat4(
0.865, 0.0575, 0.0575, 0.0, //red channel
0.1225, 0.925, 0.1225, 0.0, //green channel
0.0125, 0.0125, 0.82, 0.0, //blue channel
0.0, 0.0, 0.0, 1.0 //alpha channel
);
const mat4 GBA_DCI = mat4(
0.72, 0.0875, 0.0725, 0.0, //red channel
0.2675, 0.9, 0.185, 0.0, //green channel
0.0125, 0.0125, 0.7425, 0.0, //blue channel
0.0, 0.0, 0.0, 1.0 //alpha channel
);
const mat4 GBA_Rec2020 = mat4(
0.57, 0.115, 0.0725, 0.0, //red channel
0.3825, 0.8625, 0.195, 0.0, //green channel
0.0475, 0.0225, 0.7325, 0.0, //blue channel
0.0, 0.0, 0.0, 1.0 //alpha channel
);
void main() {
if (color_mode == 1) profile = GBA_sRGB;
else if (color_mode == 2) profile = GBA_DCI;
else if (color_mode == 3) profile = GBA_Rec2020;
gl_Position = position;
texCoord = (position.st + vec2(1.0, 1.0)) * vec2(0.5, 0.5);
}

View File

@ -88,7 +88,7 @@ DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LSR1,
} }
THUMB_NEUTRAL_S( , , cpu->gprs[rd]);) THUMB_NEUTRAL_S( , , cpu->gprs[rd]);)
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(ASR1, DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(ASR1,
if (!immediate) { if (!immediate) {
cpu->cpsr.c = ARM_SIGN(cpu->gprs[rm]); cpu->cpsr.c = ARM_SIGN(cpu->gprs[rm]);
if (cpu->cpsr.c) { if (cpu->cpsr.c) {

View File

@ -37,7 +37,7 @@ static uint32_t _patchMakeKey(struct mCheatPatch* patch) {
patchKey >>= 2; patchKey >>= 2;
break; break;
default: default:
break; break;
} }
// TODO: More than one segment // TODO: More than one segment
if (patch->segment > 0) { if (patch->segment > 0) {
@ -627,6 +627,9 @@ void mCheatAutosave(struct mCheatDevice* device) {
if (!device->autosave) { if (!device->autosave) {
return; return;
} }
if (!device->p->dirs.cheats) {
return;
}
struct VFile* vf = mDirectorySetOpenSuffix(&device->p->dirs, device->p->dirs.cheats, ".cheats", O_WRONLY | O_CREAT | O_TRUNC); struct VFile* vf = mDirectorySetOpenSuffix(&device->p->dirs, device->p->dirs.cheats, ".cheats", O_WRONLY | O_CREAT | O_TRUNC);
if (!vf) { if (!vf) {
return; return;

View File

@ -239,20 +239,35 @@ bool mCoreAutoloadPatch(struct mCore* core) {
if (!core->dirs.patch) { if (!core->dirs.patch) {
return false; return false;
} }
return core->loadPatch(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".ups", O_RDONLY)) || struct VFile* vf = NULL;
core->loadPatch(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".ips", O_RDONLY)) || if (!vf) {
core->loadPatch(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".bps", O_RDONLY)); vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".bps", O_RDONLY);
}
if (!vf) {
vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".ups", O_RDONLY);
}
if (!vf) {
vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".ips", O_RDONLY);
}
if (!vf) {
return false;
}
bool result = core->loadPatch(core, vf);
vf->close(vf);
return result;
} }
bool mCoreAutoloadCheats(struct mCore* core) { bool mCoreAutoloadCheats(struct mCore* core) {
bool success = true; bool success = !!core->dirs.cheats;
int cheatAuto; int cheatAuto;
if (!mCoreConfigGetIntValue(&core->config, "cheatAutoload", &cheatAuto) || cheatAuto) { if (success && (!mCoreConfigGetIntValue(&core->config, "cheatAutoload", &cheatAuto) || cheatAuto)) {
struct VFile* vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.cheats, ".cheats", O_RDONLY); struct VFile* vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.cheats, ".cheats", O_RDONLY);
if (vf) { if (vf) {
struct mCheatDevice* device = core->cheatDevice(core); struct mCheatDevice* device = core->cheatDevice(core);
success = mCheatParseFile(device, vf); success = mCheatParseFile(device, vf);
vf->close(vf); vf->close(vf);
} else {
success = false;
} }
} }
if (!mCoreConfigGetIntValue(&core->config, "cheatAutosave", &cheatAuto) || cheatAuto) { if (!mCoreConfigGetIntValue(&core->config, "cheatAutosave", &cheatAuto) || cheatAuto) {

View File

@ -110,6 +110,9 @@ struct VFile* mDirectorySetOpenPath(struct mDirectorySet* dirs, const char* path
} }
struct VFile* mDirectorySetOpenSuffix(struct mDirectorySet* dirs, struct VDir* dir, const char* suffix, int mode) { struct VFile* mDirectorySetOpenSuffix(struct mDirectorySet* dirs, struct VDir* dir, const char* suffix, int mode) {
if (!dir) {
return NULL;
}
char name[PATH_MAX + 1] = ""; char name[PATH_MAX + 1] = "";
snprintf(name, sizeof(name) - 1, "%s%s", dirs->baseName, suffix); snprintf(name, sizeof(name) - 1, "%s%s", dirs->baseName, suffix);
return dir->openFile(dir, name, mode); return dir->openFile(dir, name, mode);

View File

@ -110,14 +110,14 @@ void mRTCGenericSourceInit(struct mRTCGenericSource* rtc, struct mCore* core) {
rtc->d.deserialize = _rtcGenericDeserialize; rtc->d.deserialize = _rtcGenericDeserialize;
} }
static void mRumbleIntegratorReset(struct mRumble* rumble, bool enable) { static void _mRumbleIntegratorReset(struct mRumble* rumble, bool enable) {
struct mRumbleIntegrator* integrator = (struct mRumbleIntegrator*) rumble; struct mRumbleIntegrator* integrator = (struct mRumbleIntegrator*) rumble;
integrator->state = enable; integrator->state = enable;
integrator->timeOn = 0; integrator->timeOn = 0;
integrator->totalTime = 0; integrator->totalTime = 0;
} }
static void mRumbleIntegratorSetRumble(struct mRumble* rumble, bool enable, uint32_t sinceLast) { static void _mRumbleIntegratorSetRumble(struct mRumble* rumble, bool enable, uint32_t sinceLast) {
struct mRumbleIntegrator* integrator = (struct mRumbleIntegrator*) rumble; struct mRumbleIntegrator* integrator = (struct mRumbleIntegrator*) rumble;
if (integrator->state) { if (integrator->state) {
@ -127,7 +127,7 @@ static void mRumbleIntegratorSetRumble(struct mRumble* rumble, bool enable, uint
integrator->state = enable; integrator->state = enable;
} }
static void mRumbleIntegratorIntegrate(struct mRumble* rumble, uint32_t period) { static void _mRumbleIntegratorIntegrate(struct mRumble* rumble, uint32_t period) {
if (!period) { if (!period) {
return; return;
} }
@ -144,7 +144,11 @@ static void mRumbleIntegratorIntegrate(struct mRumble* rumble, uint32_t period)
void mRumbleIntegratorInit(struct mRumbleIntegrator* integrator) { void mRumbleIntegratorInit(struct mRumbleIntegrator* integrator) {
memset(integrator, 0, sizeof(*integrator)); memset(integrator, 0, sizeof(*integrator));
integrator->d.reset = mRumbleIntegratorReset; integrator->d.reset = _mRumbleIntegratorReset;
integrator->d.setRumble = mRumbleIntegratorSetRumble; integrator->d.setRumble = _mRumbleIntegratorSetRumble;
integrator->d.integrate = mRumbleIntegratorIntegrate; integrator->d.integrate = _mRumbleIntegratorIntegrate;
}
void mRumbleIntegratorReset(struct mRumbleIntegrator* integrator) {
_mRumbleIntegratorReset(&integrator->d, false);
} }

View File

@ -1,10 +1,14 @@
/* Copyright (c) 2013-2016 Jeffrey Pfau /* Copyright (c) 2013-2024 Jeffrey Pfau
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/core/lockstep.h> #include <mgba/core/lockstep.h>
#ifndef DISABLE_THREADING
#include <mgba/core/thread.h>
#endif
void mLockstepInit(struct mLockstep* lockstep) { void mLockstepInit(struct mLockstep* lockstep) {
lockstep->attached = 0; lockstep->attached = 0;
lockstep->transferActive = 0; lockstep->transferActive = 0;
@ -19,4 +23,21 @@ void mLockstepDeinit(struct mLockstep* lockstep) {
UNUSED(lockstep); UNUSED(lockstep);
} }
// TODO: Migrate nodes #ifndef DISABLE_THREADING
static void mLockstepThreadUserSleep(struct mLockstepUser* user) {
struct mLockstepThreadUser* lockstep = (struct mLockstepThreadUser*) user;
mCoreThreadWaitFromThread(lockstep->thread);
}
static void mLockstepThreadUserWake(struct mLockstepUser* user) {
struct mLockstepThreadUser* lockstep = (struct mLockstepThreadUser*) user;
mCoreThreadStopWaiting(lockstep->thread);
}
void mLockstepThreadUserInit(struct mLockstepThreadUser* lockstep, struct mCoreThread* thread) {
memset(lockstep, 0, sizeof(*lockstep));
lockstep->d.sleep = mLockstepThreadUserSleep;
lockstep->d.wake = mLockstepThreadUserWake;
lockstep->thread = thread;
}
#endif

View File

@ -520,7 +520,7 @@ mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite16, 2, U32, address, U16, va
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite32, 2, U32, address, U32, value); mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite32, 2, U32, address, U32, value);
// Register functions // Register functions
mSCRIPT_DECLARE_STRUCT_METHOD(mCore, WSTR, readRegister, _mScriptCoreReadRegister, 1, CHARP, regName); mSCRIPT_DECLARE_STRUCT_METHOD(mCore, WRAPPER, readRegister, _mScriptCoreReadRegister, 1, CHARP, regName);
mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mCore, writeRegister, _mScriptCoreWriteRegister, 2, CHARP, regName, S32, value); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mCore, writeRegister, _mScriptCoreWriteRegister, 2, CHARP, regName, S32, value);
// Savestate functions // Savestate functions
@ -1205,11 +1205,11 @@ static bool _callRotationCb(struct mScriptCoreAdapter* adapter, const char* cbNa
struct mScriptValue* context = mScriptTableLookup(adapter->rotationCbTable, &mSCRIPT_MAKE_CHARP("context")); struct mScriptValue* context = mScriptTableLookup(adapter->rotationCbTable, &mSCRIPT_MAKE_CHARP("context"));
mScriptFrameInit(&frame); mScriptFrameInit(&frame);
if (context) { if (context) {
mScriptValueWrap(context, mScriptListAppend(&frame.arguments)); mScriptValueWrap(context, mScriptListAppend(&frame.stack));
} }
bool ok = mScriptContextInvoke(adapter->context, cb, &frame); bool ok = mScriptContextInvoke(adapter->context, cb, &frame);
if (ok && out && mScriptListSize(&frame.returnValues) == 1) { if (ok && out && mScriptListSize(&frame.stack) == 1) {
if (!mScriptCast(mSCRIPT_TYPE_MS_F32, mScriptListGetPointer(&frame.returnValues, 0), out)) { if (!mScriptCast(mSCRIPT_TYPE_MS_F32, mScriptListGetPointer(&frame.stack, 0), out)) {
ok = false; ok = false;
} }
} }
@ -1278,8 +1278,8 @@ static uint8_t _readLuminance(struct GBALuminanceSource* luminance) {
mScriptFrameInit(&frame); mScriptFrameInit(&frame);
bool ok = mScriptContextInvoke(adapter->context, adapter->luminanceCb, &frame); bool ok = mScriptContextInvoke(adapter->context, adapter->luminanceCb, &frame);
struct mScriptValue out = {0}; struct mScriptValue out = {0};
if (ok && mScriptListSize(&frame.returnValues) == 1) { if (ok && mScriptListSize(&frame.stack) == 1) {
if (!mScriptCast(mSCRIPT_TYPE_MS_U8, mScriptListGetPointer(&frame.returnValues, 0), &out)) { if (!mScriptCast(mSCRIPT_TYPE_MS_U8, mScriptListGetPointer(&frame.stack, 0), &out)) {
ok = false; ok = false;
} }
} }

View File

@ -656,7 +656,7 @@ void mCoreThreadContinue(struct mCoreThread* threadContext) {
if (threadContext->impl->requested) { if (threadContext->impl->requested) {
threadContext->impl->state = mTHREAD_REQUEST; threadContext->impl->state = mTHREAD_REQUEST;
} else { } else {
threadContext->impl->state = mTHREAD_RUNNING; threadContext->impl->state = mTHREAD_RUNNING;
} }
ConditionWake(&threadContext->impl->stateOnThreadCond); ConditionWake(&threadContext->impl->stateOnThreadCond);
} }

View File

@ -71,7 +71,7 @@ bool FFmpegDecoderOpen(struct FFmpegDecoder* decoder, const char* infile) {
codec = avcodec_find_decoder(context->codec_id); codec = avcodec_find_decoder(context->codec_id);
if (!codec) { if (!codec) {
FFmpegDecoderClose(decoder); FFmpegDecoderClose(decoder);
return false; return false;
} }
if (avcodec_open2(context, codec, NULL) < 0) { if (avcodec_open2(context, codec, NULL) < 0) {
FFmpegDecoderClose(decoder); FFmpegDecoderClose(decoder);

View File

@ -893,7 +893,7 @@ void FFmpegEncoderSetInputFrameRate(struct FFmpegEncoder* encoder, int numerator
void FFmpegEncoderSetInputSampleRate(struct FFmpegEncoder* encoder, int sampleRate) { void FFmpegEncoderSetInputSampleRate(struct FFmpegEncoder* encoder, int sampleRate) {
encoder->isampleRate = sampleRate; encoder->isampleRate = sampleRate;
if (encoder->resampleContext) { if (encoder->resampleContext) {
av_freep(&encoder->audioBuffer); av_freep(&encoder->audioBuffer);
#ifdef USE_LIBAVRESAMPLE #ifdef USE_LIBAVRESAMPLE
avresample_close(encoder->resampleContext); avresample_close(encoder->resampleContext);

View File

@ -295,7 +295,7 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
test.v.s = mCoreConfigGetValue(&runner->config, item->data.v.s); test.v.s = mCoreConfigGetValue(&runner->config, item->data.v.s);
if (test.v.s && strcmp(test.v.s, v->v.s) == 0) { if (test.v.s && strcmp(test.v.s, v->v.s) == 0) {
item->state = j; item->state = j;
break; break;
} }
break; break;
case GUI_VARIANT_POINTER: case GUI_VARIANT_POINTER:

View File

@ -782,6 +782,9 @@ void mGUILoadInputMaps(struct mGUIRunner* runner) {
size_t i; size_t i;
for (i = 0; runner->keySources[i].id; ++i) { for (i = 0; runner->keySources[i].id; ++i) {
mInputMapLoad(&runner->params.keyMap, runner->keySources[i].id, mCoreConfigGetInput(&runner->config)); mInputMapLoad(&runner->params.keyMap, runner->keySources[i].id, mCoreConfigGetInput(&runner->config));
if (runner->core) {
mInputMapLoad(&runner->core->inputMap, runner->keySources[i].id, mCoreConfigGetInput(&runner->config));
}
} }
} }

View File

@ -46,7 +46,7 @@ static void _updateList(const char* key, const char* value, void* user) {
if (strncmp("medusa.", key, 7) == 0) { if (strncmp("medusa.", key, 7) == 0) {
dotLoc = strchr(&key[7], '.'); dotLoc = strchr(&key[7], '.');
} else { } else {
dotLoc = strchr(key, '.'); dotLoc = strchr(key, '.');
} }
if (!dotLoc) { if (!dotLoc) {
return; return;

View File

@ -339,17 +339,17 @@ bool mVideoLoggerRendererRunInjected(struct mVideoLogger* logger) {
channel->injecting = true; channel->injecting = true;
bool res = mVideoLoggerRendererRun(logger, false); bool res = mVideoLoggerRendererRun(logger, false);
channel->injecting = false; channel->injecting = false;
return res; return res;
} }
void mVideoLoggerInjectionPoint(struct mVideoLogger* logger, enum mVideoLoggerInjectionPoint injectionPoint) { void mVideoLoggerInjectionPoint(struct mVideoLogger* logger, enum mVideoLoggerInjectionPoint injectionPoint) {
struct mVideoLogChannel* channel = logger->dataContext; struct mVideoLogChannel* channel = logger->dataContext;
channel->injectionPoint = injectionPoint; channel->injectionPoint = injectionPoint;
} }
void mVideoLoggerIgnoreAfterInjection(struct mVideoLogger* logger, uint32_t mask) { void mVideoLoggerIgnoreAfterInjection(struct mVideoLogger* logger, uint32_t mask) {
struct mVideoLogChannel* channel = logger->dataContext; struct mVideoLogChannel* channel = logger->dataContext;
channel->ignorePackets = mask; channel->ignorePackets = mask;
} }
static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length) { static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length) {

View File

@ -21,6 +21,7 @@
#include <mgba/internal/sm83/sm83.h> #include <mgba/internal/sm83/sm83.h>
#include <mgba/internal/sm83/debugger/debugger.h> #include <mgba/internal/sm83/debugger/debugger.h>
#include <mgba-util/crc32.h> #include <mgba-util/crc32.h>
#include <mgba-util/md5.h>
#include <mgba-util/memory.h> #include <mgba-util/memory.h>
#include <mgba-util/patch.h> #include <mgba-util/patch.h>
#include <mgba-util/vfs.h> #include <mgba-util/vfs.h>
@ -152,7 +153,7 @@ static bool _GBCoreInit(struct mCore* core) {
#ifdef ENABLE_VFS #ifdef ENABLE_VFS
mDirectorySetInit(&core->dirs); mDirectorySetInit(&core->dirs);
#endif #endif
return true; return true;
} }
@ -529,6 +530,15 @@ static void _GBCoreChecksum(const struct mCore* core, void* data, enum mCoreChec
case mCHECKSUM_CRC32: case mCHECKSUM_CRC32:
memcpy(data, &gb->romCrc32, sizeof(gb->romCrc32)); memcpy(data, &gb->romCrc32, sizeof(gb->romCrc32));
break; break;
case mCHECKSUM_MD5:
if (gb->romVf) {
md5File(gb->romVf, data);
} else if (gb->memory.rom && gb->isPristine) {
md5Buffer(gb->memory.rom, gb->pristineRomSize, data);
} else {
md5Buffer("", 0, data);
}
break;
} }
return; return;
} }

View File

@ -267,7 +267,7 @@ void GBVideoProxyRendererDrawRange(struct GBVideoRenderer* renderer, int startX,
_copyExtraState(proxyRenderer); _copyExtraState(proxyRenderer);
proxyRenderer->backend->drawRange(proxyRenderer->backend, startX, endX, y); proxyRenderer->backend->drawRange(proxyRenderer->backend, startX, endX, y);
} }
mVideoLoggerRendererDrawRange(proxyRenderer->logger, startX, endX, y); mVideoLoggerRendererDrawRange(proxyRenderer->logger, startX, endX, y);
} }
void GBVideoProxyRendererFinishScanline(struct GBVideoRenderer* renderer, int y) { void GBVideoProxyRendererFinishScanline(struct GBVideoRenderer* renderer, int y) {

View File

@ -152,7 +152,7 @@ bool GBLoadGBX(struct GBXMetadata* metadata, struct VFile* vf) {
if (memcmp(footer, "MBC1", 4) == 0) { if (memcmp(footer, "MBC1", 4) == 0) {
metadata->mapperVars.u8[0] = 5; metadata->mapperVars.u8[0] = 5;
} else if (memcmp(footer, "MB1M", 4) == 0) { } else if (memcmp(footer, "MB1M", 4) == 0) {
metadata->mapperVars.u8[0] = 4; metadata->mapperVars.u8[0] = 4;
} }
return true; return true;
} }
@ -483,12 +483,10 @@ void GBApplyPatch(struct GB* gb, struct Patch* patch) {
mappedMemoryFree(newRom, GB_SIZE_CART_MAX); mappedMemoryFree(newRom, GB_SIZE_CART_MAX);
return; return;
} }
if (gb->romVf) { if (gb->romVf && gb->isPristine) {
#ifndef FIXED_ROM_BUFFER #ifndef FIXED_ROM_BUFFER
gb->romVf->unmap(gb->romVf, gb->memory.rom, gb->pristineRomSize); gb->romVf->unmap(gb->romVf, gb->memory.rom, gb->pristineRomSize);
#endif #endif
gb->romVf->close(gb->romVf);
gb->romVf = NULL;
} }
gb->isPristine = false; gb->isPristine = false;
if (gb->memory.romBase == gb->memory.rom) { if (gb->memory.romBase == gb->memory.rom) {
@ -894,7 +892,7 @@ int GBValidModels(const uint8_t* bank0) {
} else if (cart->cgb == 0xC0) { } else if (cart->cgb == 0xC0) {
models = GB_MODEL_CGB; models = GB_MODEL_CGB;
} else { } else {
models = GB_MODEL_MGB; models = GB_MODEL_MGB;
} }
if (cart->sgb == 0x03 && cart->oldLicensee == 0x33) { if (cart->sgb == 0x03 && cart->oldLicensee == 0x33) {
models |= GB_MODEL_SGB; models |= GB_MODEL_SGB;

View File

@ -163,7 +163,7 @@ static bool _isMulticart(const uint8_t* mem) {
success = GBIsROM(vf); success = GBIsROM(vf);
vf->close(vf); vf->close(vf);
} }
return success; return success;
} }

View File

@ -131,7 +131,7 @@ static void _latchTAMA6Rtc(struct mRTCSource* rtc, struct GBTAMA5State* tama5, t
timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 24) / 10; timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 24) / 10;
} else { } else {
timerRegs[GBTAMA6_RTC_PA0_HOUR_1] = (diff % 12) % 10; timerRegs[GBTAMA6_RTC_PA0_HOUR_1] = (diff % 12) % 10;
timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 12) / 10 + (diff / 12) * 2; timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 12) / 10 + (diff / 12) * 2;
} }
t /= 24; t /= 24;
t += diff / 24; t += diff / 24;

View File

@ -349,7 +349,7 @@ void _GBHitek(struct GB* gb, uint16_t address, uint8_t value) {
break; break;
case 0x300: case 0x300:
// See hhugboy src/memory/mbc/MbcUnlHitek.cpp for commentary on this return // See hhugboy src/memory/mbc/MbcUnlHitek.cpp for commentary on this return
return; return;
} }
_GBMBC5(gb, address, value); _GBMBC5(gb, address, value);
} }
@ -396,10 +396,10 @@ uint8_t _GBGGB81Read(struct GBMemory* memory, uint16_t address) {
} }
void _GBLiCheng(struct GB* gb, uint16_t address, uint8_t value) { void _GBLiCheng(struct GB* gb, uint16_t address, uint8_t value) {
if (address > 0x2100 && address < 0x3000) { if (address > 0x2100 && address < 0x3000) {
return; return;
} }
_GBMBC5(gb, address, value); _GBMBC5(gb, address, value);
} }
void _GBSachen(struct GB* gb, uint16_t address, uint8_t value) { void _GBSachen(struct GB* gb, uint16_t address, uint8_t value) {

View File

@ -1017,8 +1017,6 @@ void _pristineCow(struct GB* gb) {
} }
if (gb->romVf) { if (gb->romVf) {
gb->romVf->unmap(gb->romVf, gb->memory.rom, gb->memory.romSize); gb->romVf->unmap(gb->romVf, gb->memory.rom, gb->memory.romSize);
gb->romVf->close(gb->romVf);
gb->romVf = NULL;
} }
gb->memory.rom = newRom; gb->memory.rom = newRom;
GBMBCSwitchBank(gb, gb->memory.currentBank); GBMBCSwitchBank(gb, gb->memory.currentBank);

View File

@ -755,7 +755,7 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
video->ly = 0; video->ly = 0;
video->p->memory.io[GB_REG_LY] = 0; video->p->memory.io[GB_REG_LY] = 0;
video->renderer->writePalette(video->renderer, 0, video->dmgPalette[0]); video->renderer->writePalette(video->renderer, 0, video->dmgPalette[0]);
mTimingDeschedule(&video->p->timing, &video->modeEvent); mTimingDeschedule(&video->p->timing, &video->modeEvent);
mTimingDeschedule(&video->p->timing, &video->frameEvent); mTimingDeschedule(&video->p->timing, &video->frameEvent);
mTimingSchedule(&video->p->timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH << 1); mTimingSchedule(&video->p->timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH << 1);

View File

@ -6,6 +6,7 @@ set(SOURCE_FILES
cart/ereader.c cart/ereader.c
cart/gpio.c cart/gpio.c
cart/matrix.c cart/matrix.c
cart/unlicensed.c
cart/vfame.c cart/vfame.c
cheats.c cheats.c
cheats/codebreaker.c cheats/codebreaker.c
@ -31,7 +32,6 @@ set(SOURCE_FILES
sharkport.c sharkport.c
sio.c sio.c
sio/gbp.c sio/gbp.c
sio/joybus.c
timer.c timer.c
video.c) video.c)

View File

@ -309,6 +309,7 @@ void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles) {
if (GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_CUSTOM) { if (GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_CUSTOM) {
dma->when = mTimingCurrentTime(&audio->p->timing) - cycles; dma->when = mTimingCurrentTime(&audio->p->timing) - cycles;
dma->nextCount = 4; dma->nextCount = 4;
GBADMARecalculateCycles(audio->p);
GBADMASchedule(audio->p, channel->dmaSource, dma); GBADMASchedule(audio->p, channel->dmaSource, dma);
} }
} }

View File

@ -35,30 +35,6 @@ static int _mulWait(int32_t r) {
} }
} }
static void _SoftReset(struct GBA* gba) {
struct ARMCore* cpu = gba->cpu;
ARMSetPrivilegeMode(cpu, MODE_IRQ);
cpu->spsr.packed = 0;
cpu->gprs[ARM_LR] = 0;
cpu->gprs[ARM_SP] = GBA_SP_BASE_IRQ;
ARMSetPrivilegeMode(cpu, MODE_SUPERVISOR);
cpu->spsr.packed = 0;
cpu->gprs[ARM_LR] = 0;
cpu->gprs[ARM_SP] = GBA_SP_BASE_SUPERVISOR;
ARMSetPrivilegeMode(cpu, MODE_SYSTEM);
cpu->gprs[ARM_LR] = 0;
cpu->gprs[ARM_SP] = GBA_SP_BASE_SYSTEM;
int8_t flag = ((int8_t*) gba->memory.iwram)[0x7FFA];
memset(((int8_t*) gba->memory.iwram) + GBA_SIZE_IWRAM - 0x200, 0, 0x200);
if (flag) {
cpu->gprs[ARM_PC] = GBA_BASE_EWRAM;
} else {
cpu->gprs[ARM_PC] = GBA_BASE_ROM0;
}
_ARMSetMode(cpu, MODE_ARM);
ARMWritePC(cpu);
}
static void _RegisterRamReset(struct GBA* gba) { static void _RegisterRamReset(struct GBA* gba) {
uint32_t registers = gba->cpu->gprs[0]; uint32_t registers = gba->cpu->gprs[0];
struct ARMCore* cpu = gba->cpu; struct ARMCore* cpu = gba->cpu;
@ -445,7 +421,7 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
bool useStall = false; bool useStall = false;
switch (immediate) { switch (immediate) {
case GBA_SWI_SOFT_RESET: case GBA_SWI_SOFT_RESET:
_SoftReset(gba); ARMRaiseSWI(cpu);
break; break;
case GBA_SWI_REGISTER_RAM_RESET: case GBA_SWI_REGISTER_RAM_RESET:
_RegisterRamReset(gba); _RegisterRamReset(gba);

View File

@ -47,6 +47,20 @@ void GBAHardwareInit(struct GBACartridgeHardware* hw, uint16_t* base) {
GBAHardwareClear(hw); GBAHardwareClear(hw);
} }
void GBAHardwareReset(struct GBACartridgeHardware* hw) {
hw->readWrite = GPIO_WRITE_ONLY;
hw->pinState = 0;
hw->direction = 0;
hw->lightCounter = 0;
hw->lightEdge = false;
hw->lightSample = 0xFF;
hw->gyroSample = 0;
hw->gyroEdge = 0;
hw->tiltX = 0xFFF;
hw->tiltY = 0xFFF;
hw->tiltState = 0;
}
void GBAHardwareClear(struct GBACartridgeHardware* hw) { void GBAHardwareClear(struct GBACartridgeHardware* hw) {
hw->devices = HW_NONE | (hw->devices & HW_GB_PLAYER_DETECTION); hw->devices = HW_NONE | (hw->devices & HW_GB_PLAYER_DETECTION);
hw->readWrite = GPIO_WRITE_ONLY; hw->readWrite = GPIO_WRITE_ONLY;
@ -480,16 +494,16 @@ void GBAHardwareSerialize(const struct GBACartridgeHardware* hw, struct GBASeria
STORE_16(hw->tiltY, 0, &state->hw.tiltSampleY); STORE_16(hw->tiltY, 0, &state->hw.tiltSampleY);
state->hw.lightSample = hw->lightSample; state->hw.lightSample = hw->lightSample;
flags1 = GBASerializedHWFlags1SetLightEdge(flags1, hw->lightEdge); flags1 = GBASerializedHWFlags1SetLightEdge(flags1, hw->lightEdge);
flags1 = GBASerializedHWFlags1SetLightCounter(flags1, hw->lightCounter);
STORE_16(flags1, 0, &state->hw.flags1); STORE_16(flags1, 0, &state->hw.flags1);
GBASerializedHWFlags2 flags2 = 0; GBASerializedHWFlags2 flags2 = 0;
flags2 = GBASerializedHWFlags2SetTiltState(flags2, hw->tiltState); flags2 = GBASerializedHWFlags2SetTiltState(flags2, hw->tiltState);
flags2 = GBASerializedHWFlags1SetLightCounter(flags2, hw->lightCounter);
// GBP stuff is only here for legacy reasons // GBP/SIO stuff is only here for legacy reasons
flags2 = GBASerializedHWFlags2SetGbpInputsPosted(flags2, hw->p->sio.gbp.inputsPosted); flags2 = GBASerializedHWFlags2SetGbpInputsPosted(flags2, hw->p->sio.gbp.inputsPosted);
flags2 = GBASerializedHWFlags2SetGbpTxPosition(flags2, hw->p->sio.gbp.txPosition); flags2 = GBASerializedHWFlags2SetGbpTxPosition(flags2, hw->p->sio.gbp.txPosition);
STORE_32(hw->p->sio.gbp.event.when - mTimingCurrentTime(&hw->p->timing), 0, &state->hw.gbpNextEvent); STORE_32(hw->p->sio.completeEvent.when - mTimingCurrentTime(&hw->p->timing), 0, &state->hw.sioNextEvent);
state->hw.flags2 = flags2; state->hw.flags2 = flags2;
} }
@ -502,6 +516,19 @@ void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, const struct GBASer
LOAD_16(hw->direction, 0, &state->hw.pinDirection); LOAD_16(hw->direction, 0, &state->hw.pinDirection);
hw->devices = state->hw.devices; hw->devices = state->hw.devices;
if ((hw->devices & HW_GPIO) && hw->gpioBase) {
// TODO: This needs to update the pristine state somehow
if (hw->readWrite) {
STORE_16(hw->pinState, 0, hw->gpioBase);
STORE_16(hw->direction, 2, hw->gpioBase);
STORE_16(hw->readWrite, 4, hw->gpioBase);
} else {
hw->gpioBase[0] = 0;
hw->gpioBase[1] = 0;
hw->gpioBase[2] = 0;
}
}
LOAD_32(hw->rtc.bytesRemaining, 0, &state->hw.rtcBytesRemaining); LOAD_32(hw->rtc.bytesRemaining, 0, &state->hw.rtcBytesRemaining);
LOAD_32(hw->rtc.transferStep, 0, &state->hw.rtcTransferStep); LOAD_32(hw->rtc.transferStep, 0, &state->hw.rtcTransferStep);
LOAD_32(hw->rtc.bitsRead, 0, &state->hw.rtcBitsRead); LOAD_32(hw->rtc.bitsRead, 0, &state->hw.rtcBitsRead);
@ -520,16 +547,16 @@ void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, const struct GBASer
hw->lightSample = state->hw.lightSample; hw->lightSample = state->hw.lightSample;
hw->lightEdge = GBASerializedHWFlags1GetLightEdge(flags1); hw->lightEdge = GBASerializedHWFlags1GetLightEdge(flags1);
// GBP stuff is only here for legacy reasons // GBP/SIO stuff is only here for legacy reasons
hw->p->sio.gbp.inputsPosted = GBASerializedHWFlags2GetGbpInputsPosted(state->hw.flags2); hw->p->sio.gbp.inputsPosted = GBASerializedHWFlags2GetGbpInputsPosted(state->hw.flags2);
hw->p->sio.gbp.txPosition = GBASerializedHWFlags2GetGbpTxPosition(state->hw.flags2); hw->p->sio.gbp.txPosition = GBASerializedHWFlags2GetGbpTxPosition(state->hw.flags2);
uint32_t when; uint32_t when;
LOAD_32(when, 0, &state->hw.gbpNextEvent); LOAD_32(when, 0, &state->hw.sioNextEvent);
if (hw->devices & HW_GB_PLAYER) { if (hw->devices & HW_GB_PLAYER) {
GBASIOSetDriver(&hw->p->sio, &hw->p->sio.gbp.d, GBA_SIO_NORMAL_32); GBASIOSetDriver(&hw->p->sio, &hw->p->sio.gbp.d);
if (hw->p->memory.io[GBA_REG(SIOCNT)] & 0x0080) { }
mTimingSchedule(&hw->p->timing, &hw->p->sio.gbp.event, when); if ((hw->p->memory.io[GBA_REG(SIOCNT)] & 0x0080) && when < 0x20000) {
} mTimingSchedule(&hw->p->timing, &hw->p->sio.completeEvent, when);
} }
} }

View File

@ -109,7 +109,7 @@ void GBAMatrixSerialize(const struct GBA* gba, struct GBASerializedState* state)
int i; int i;
for (i = 0; i < 16; ++i) { for (i = 0; i < 16; ++i) {
STORE_32(gba->memory.matrix.mappings[i], i << 2, state->matrixMappings); STORE_32(gba->memory.matrix.mappings[i], i << 2, state->matrix2.mappings);
} }
} }
@ -121,7 +121,7 @@ void GBAMatrixDeserialize(struct GBA* gba, const struct GBASerializedState* stat
int i; int i;
for (i = 0; i < 16; ++i) { for (i = 0; i < 16; ++i) {
LOAD_32(gba->memory.matrix.mappings[i], i << 2, state->matrixMappings); LOAD_32(gba->memory.matrix.mappings[i], i << 2, state->matrix2.mappings);
gba->memory.matrix.paddr = gba->memory.matrix.mappings[i]; gba->memory.matrix.paddr = gba->memory.matrix.mappings[i];
gba->memory.matrix.vaddr = i << 9; gba->memory.matrix.vaddr = i << 9;
_remapMatrix(gba); _remapMatrix(gba);

255
src/gba/cart/unlicensed.c Normal file
View File

@ -0,0 +1,255 @@
/* Copyright (c) 2013-2024 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/internal/gba/cart/unlicensed.h>
#include <mgba/internal/arm/macros.h>
#include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/serialize.h>
#include <mgba-util/vfs.h>
#define MULTI_SETTLE 300
#define MULTI_BLOCK 0x80000
#define MULTI_BANK 0x2000000
enum GBMulticartCfgOffset {
GBA_MULTICART_CFG_BANK = 0x2,
GBA_MULTICART_CFG_OFFSET = 0x3,
GBA_MULTICART_CFG_SIZE = 0x4,
GBA_MULTICART_CFG_SRAM = 0x5,
GBA_MULTICART_CFG_UNK = 0x6,
};
static_assert(sizeof(((struct GBASerializedState*)(NULL))->vfame.writeSequence) ==
sizeof(((struct GBAVFameCart*)(NULL))->writeSequence), "GBA savestate vfame writeSequence size mismatch");
static void _multicartSettle(struct mTiming* timing, void* context, uint32_t cyclesLate);
void GBAUnlCartInit(struct GBA* gba) {
memset(&gba->memory.unl, 0, sizeof(gba->memory.unl));
}
void GBAUnlCartDetect(struct GBA* gba) {
if (!gba->memory.rom) {
return;
}
struct GBACartridge* cart = (struct GBACartridge*) gba->memory.rom;
if (GBAVFameDetect(&gba->memory.unl.vfame, gba->memory.rom, gba->memory.romSize, gba->romCrc32)) {
gba->memory.unl.type = GBA_UNL_CART_VFAME;
return;
}
if (memcmp(&cart->id, "AXVJ01", 6) == 0 || memcmp(&cart->id, "BI3P52", 6) == 0) {
if (gba->romVf && gba->romVf->size(gba->romVf) >= 0x04000000) {
// Bootleg multicart
// TODO: Identify a bit more precisely
gba->isPristine = false;
GBASavedataInitSRAM(&gba->memory.savedata);
gba->memory.unl.type = GBA_UNL_CART_MULTICART;
gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->memory.romSize);
gba->memory.unl.multi.fullSize = gba->romVf->size(gba->romVf);
gba->memory.unl.multi.rom = gba->romVf->map(gba->romVf, gba->memory.unl.multi.fullSize, MAP_READ);
gba->memory.rom = gba->memory.unl.multi.rom;
gba->memory.hw.gpioBase = NULL;
gba->memory.unl.multi.settle.context = gba;
gba->memory.unl.multi.settle.callback = _multicartSettle;
gba->memory.unl.multi.settle.name = "GBA Unlicensed Multicart Settle";
gba->memory.unl.multi.settle.priority = 0x71;
}
}
}
void GBAUnlCartReset(struct GBA* gba) {
if (gba->memory.unl.type == GBA_UNL_CART_MULTICART) {
gba->memory.unl.multi.bank = 0;
gba->memory.unl.multi.offset = 0;
gba->memory.unl.multi.size = 0;
gba->memory.unl.multi.locked = false;
gba->memory.rom = gba->memory.unl.multi.rom;
gba->memory.romSize = GBA_SIZE_ROM0;
}
}
void GBAUnlCartUnload(struct GBA* gba) {
if (gba->memory.unl.type == GBA_UNL_CART_MULTICART && gba->romVf) {
gba->romVf->unmap(gba->romVf, gba->memory.unl.multi.rom, gba->memory.unl.multi.size);
gba->memory.unl.multi.rom = NULL;
gba->memory.rom = NULL;
}
}
void GBAUnlCartWriteSRAM(struct GBA* gba, uint32_t address, uint8_t value) {
struct GBAUnlCart* unl = &gba->memory.unl;
switch (unl->type) {
case GBA_UNL_CART_VFAME:
GBAVFameSramWrite(&unl->vfame, address, value, gba->memory.savedata.data);
return;
case GBA_UNL_CART_MULTICART:
mLOG(GBA_MEM, DEBUG, "Multicart writing SRAM %06X:%02X", address, value);
switch (address) {
case GBA_MULTICART_CFG_BANK:
if (!unl->multi.locked) {
unl->multi.bank = value >> 4;
mTimingDeschedule(&gba->timing, &unl->multi.settle);
mTimingSchedule(&gba->timing, &unl->multi.settle, MULTI_SETTLE);
}
break;
case GBA_MULTICART_CFG_OFFSET:
if (!unl->multi.locked) {
unl->multi.offset = value;
mTimingDeschedule(&gba->timing, &unl->multi.settle);
mTimingSchedule(&gba->timing, &unl->multi.settle, MULTI_SETTLE);
if (unl->multi.offset & 0x80) {
unl->multi.locked = true;
}
}
break;
case GBA_MULTICART_CFG_SIZE:
unl->multi.size = 0x40 - (value & 0x3F);
if (!unl->multi.locked) {
mTimingDeschedule(&gba->timing, &unl->multi.settle);
mTimingSchedule(&gba->timing, &unl->multi.settle, MULTI_SETTLE);
}
break;
case GBA_MULTICART_CFG_SRAM:
if (value == 0 && unl->multi.sramActive) {
unl->multi.sramActive = false;
} else if (value == 1 && !unl->multi.sramActive) {
unl->multi.sramActive = true;
}
break;
case GBA_MULTICART_CFG_UNK:
// TODO: What does this do?
unl->multi.unk = value;
break;
default:
break;
}
break;
case GBA_UNL_CART_NONE:
break;
}
gba->memory.savedata.data[address & (GBA_SIZE_SRAM - 1)] = value;
}
void GBAUnlCartWriteROM(struct GBA* gba, uint32_t address, uint16_t value) {
struct GBAUnlCart* unl = &gba->memory.unl;
switch (unl->type) {
case GBA_UNL_CART_VFAME:
case GBA_UNL_CART_NONE:
break;
case GBA_UNL_CART_MULTICART:
mLOG(GBA_MEM, STUB, "Unimplemented writing to ROM %07X:%04X", address, value);
break;
}
}
static void _multicartSettle(struct mTiming* timing, void* context, uint32_t cyclesLate) {
UNUSED(timing);
UNUSED(cyclesLate);
struct GBA* gba = context;
mLOG(GBA_MEM, INFO, "Switching to bank %i offset %i, size %i",
gba->memory.unl.multi.bank, gba->memory.unl.multi.offset & 0x3F, gba->memory.unl.multi.size);
size_t offset = gba->memory.unl.multi.bank * (MULTI_BANK >> 2) + (gba->memory.unl.multi.offset & 0x3F) * (MULTI_BLOCK >> 2);
size_t size = gba->memory.unl.multi.size * MULTI_BLOCK;
if (offset * 4 >= gba->memory.unl.multi.fullSize || offset * 4 + size > gba->memory.unl.multi.fullSize) {
mLOG(GBA_MEM, GAME_ERROR, "Bank switch was out of bounds, %07" PRIz "X + %" PRIz "X > %07" PRIz "X",
offset * 4, size, gba->memory.unl.multi.fullSize);
return;
}
gba->memory.rom = gba->memory.unl.multi.rom + offset;
gba->memory.romSize = size;
}
void GBAUnlCartSerialize(const struct GBA* gba, struct GBASerializedState* state) {
GBASerializedUnlCartFlags flags = 0;
GBASerializedMulticartFlags multiFlags = 0;
const struct GBAUnlCart* unl = &gba->memory.unl;
switch (unl->type) {
case GBA_UNL_CART_NONE:
return;
case GBA_UNL_CART_VFAME:
flags = GBASerializedUnlCartFlagsSetType(flags, GBA_UNL_CART_VFAME);
flags = GBASerializedUnlCartFlagsSetSubtype(flags, unl->vfame.cartType);
STORE_16(unl->vfame.sramMode, 0, &state->vfame.sramMode);
STORE_16(unl->vfame.romMode, 0, &state->vfame.romMode);
memcpy(state->vfame.writeSequence, unl->vfame.writeSequence, sizeof(state->vfame.writeSequence));
state->vfame.acceptingModeChange = unl->vfame.acceptingModeChange;
break;
case GBA_UNL_CART_MULTICART:
flags = GBASerializedUnlCartFlagsSetType(0, GBA_UNL_CART_MULTICART);
state->multicart.bank = unl->multi.bank;
state->multicart.offset = unl->multi.offset;
state->multicart.size = unl->multi.size;
state->multicart.sramActive = unl->multi.sramActive;
state->multicart.unk = unl->multi.unk;
state->multicart.currentSize = gba->memory.romSize / MULTI_BLOCK;
multiFlags = GBASerializedMulticartFlagsSetLocked(flags, unl->multi.locked);
STORE_16((gba->memory.rom - unl->multi.rom) / 0x20000, 0, &state->multicart.currentOffset);
STORE_32(unl->multi.settle.when, 0, &state->multicart.settleNextEvent);
if (mTimingIsScheduled(&gba->timing, &unl->multi.settle)) {
multiFlags = GBASerializedMulticartFlagsFillDustSettling(multiFlags);
}
STORE_32(multiFlags, 0, &state->multicart.flags);
break;
}
STORE_32(flags, 0, &state->hw.unlCartFlags);
}
void GBAUnlCartDeserialize(struct GBA* gba, const struct GBASerializedState* state) {
GBASerializedUnlCartFlags flags;
struct GBAUnlCart* unl = &gba->memory.unl;
LOAD_32(flags, 0, &state->hw.unlCartFlags);
enum GBAUnlCartType type = GBASerializedUnlCartFlagsGetType(flags);
if (type != unl->type) {
mLOG(GBA_STATE, WARN, "Save state expects different bootleg type; not restoring bootleg state");
return;
}
uint32_t when;
uint32_t offset;
size_t size;
GBASerializedMulticartFlags multiFlags;
switch (type) {
case GBA_UNL_CART_NONE:
return;
case GBA_UNL_CART_VFAME:
LOAD_16(unl->vfame.sramMode, 0, &state->vfame.sramMode);
LOAD_16(unl->vfame.romMode, 0, &state->vfame.romMode);
memcpy(unl->vfame.writeSequence, state->vfame.writeSequence, sizeof(state->vfame.writeSequence));
unl->vfame.acceptingModeChange = state->vfame.acceptingModeChange;
return;
case GBA_UNL_CART_MULTICART:
unl->multi.bank = state->multicart.bank;
unl->multi.offset = state->multicart.offset;
unl->multi.size = state->multicart.size;
unl->multi.sramActive = state->multicart.sramActive;
unl->multi.unk = state->multicart.unk;
size = state->multicart.currentSize * MULTI_BLOCK;
LOAD_16(offset, 0, &state->multicart.currentOffset);
offset *= 0x20000;
if (offset * 4 >= gba->memory.unl.multi.fullSize || offset * 4 + size > gba->memory.unl.multi.fullSize) {
mLOG(GBA_STATE, WARN, "Multicart save state has corrupted ROM offset");
} else {
gba->memory.romSize = size;
gba->memory.rom = unl->multi.rom + offset;
}
LOAD_32(multiFlags, 0, &state->multicart.flags);
unl->multi.locked = GBASerializedMulticartFlagsGetLocked(multiFlags);
if (GBASerializedMulticartFlagsIsDustSettling(multiFlags)) {
LOAD_32(when, 0, &state->multicart.settleNextEvent);
mTimingSchedule(&gba->timing, &unl->multi.settle, when);
}
break;
}
}

View File

@ -1,42 +1,50 @@
/* Copyright (c) 2016 taizou /* Copyright (c) 2016 taizou
* Copyright (c) 2013-2024 Jeffrey Pfau
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/internal/gba/cart/vfame.h> #include <mgba/internal/gba/cart/unlicensed.h>
#include <mgba/internal/gba/gba.h> #include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/memory.h> #include <mgba/internal/gba/memory.h>
static const uint8_t ADDRESS_REORDERING[4][16] = { #define DIGIMON_SAPPHIRE_CHINESE_CRC32 0x793A328F
{ 15, 14, 9, 1, 8, 10, 7, 3, 5, 11, 4, 0, 13, 12, 2, 6 },
{ 15, 7, 13, 5, 11, 6, 0, 9, 12, 2, 10, 14, 3, 1, 8, 4 }, static const uint8_t ADDRESS_REORDERING[][3][16] = {
{ 15, 0, 3, 12, 2, 4, 14, 13, 1, 8, 6, 7, 9, 5, 11, 10 } [VFAME_STANDARD] = {
{ 15, 14, 9, 1, 8, 10, 7, 3, 5, 11, 4, 0, 13, 12, 2, 6 },
{ 15, 7, 13, 5, 11, 6, 0, 9, 12, 2, 10, 14, 3, 1, 8, 4 },
{ 15, 0, 3, 12, 2, 4, 14, 13, 1, 8, 6, 7, 9, 5, 11, 10 }
},
[VFAME_GEORGE] = {
{ 15, 7, 13, 1, 11, 10, 14, 9, 12, 2, 4, 0, 3, 5, 8, 6 },
{ 15, 14, 3, 12, 8, 4, 0, 13, 5, 11, 6, 7, 9, 1, 2, 10 },
{ 15, 0, 9, 5, 2, 6, 7, 3, 1, 8, 10, 14, 13, 12, 11, 4 }
},
[VFAME_ALTERNATE] = {
{ 15, 0, 13, 5, 8, 4, 7, 3, 1, 2, 10, 14, 9, 12, 11, 6 },
{ 15, 7, 9, 1, 2, 6, 14, 13, 12, 11, 4, 0, 3, 5, 8, 10 },
{ 15, 14, 3, 12, 11, 10, 0, 9, 5, 8, 6, 7, 13, 1, 2, 4 }
},
}; };
static const uint8_t ADDRESS_REORDERING_GEORGE[4][16] = {
{ 15, 7, 13, 1, 11, 10, 14, 9, 12, 2, 4, 0, 3, 5, 8, 6 }, static const uint8_t VALUE_REORDERING[][3][8] = {
{ 15, 14, 3, 12, 8, 4, 0, 13, 5, 11, 6, 7, 9, 1, 2, 10 }, [VFAME_STANDARD] = {
{ 15, 0, 9, 5, 2, 6, 7, 3, 1, 8, 10, 14, 13, 12, 11, 4 } { 5, 4, 3, 2, 1, 0, 7, 6 },
}; { 3, 2, 1, 0, 7, 6, 5, 4 },
static const uint8_t ADDRESS_REORDERING_ALTERNATE[4][16] = { { 1, 0, 7, 6, 5, 4, 3, 2 }
{ 15, 0, 13, 5, 8, 4, 7, 3, 1, 2, 10, 14, 9, 12, 11, 6 }, },
{ 15, 7, 9, 1, 2, 6, 14, 13, 12, 11, 4, 0, 3, 5, 8, 10 }, [VFAME_GEORGE] = {
{ 15, 14, 3, 12, 11, 10, 0, 9, 5, 8, 6, 7, 13, 1, 2, 4 } { 3, 0, 7, 2, 1, 4, 5, 6 },
}; { 1, 4, 3, 0, 5, 6, 7, 2 },
static const uint8_t VALUE_REORDERING[4][16] = { { 5, 2, 1, 6, 7, 0, 3, 4 }
{ 5, 4, 3, 2, 1, 0, 7, 6 }, },
{ 3, 2, 1, 0, 7, 6, 5, 4 }, [VFAME_ALTERNATE] = {
{ 1, 0, 7, 6, 5, 4, 3, 2 } { 5, 4, 7, 2, 1, 0, 3, 6 },
}; { 1, 2, 3, 0, 5, 6, 7, 4 },
static const uint8_t VALUE_REORDERING_GEORGE[4][16] = { { 3, 0, 1, 6, 7, 4, 5, 2 }
{ 3, 0, 7, 2, 1, 4, 5, 6 }, },
{ 1, 4, 3, 0, 5, 6, 7, 2 },
{ 5, 2, 1, 6, 7, 0, 3, 4 }
};
static const uint8_t VALUE_REORDERING_ALTERNATE[4][16] = {
{ 5, 4, 7, 2, 1, 0, 3, 6 },
{ 1, 2, 3, 0, 5, 6, 7, 4 },
{ 3, 0, 1, 6, 7, 4, 5, 2 }
}; };
static const int8_t MODE_CHANGE_START_SEQUENCE[5] = { 0x99, 0x02, 0x05, 0x02, 0x03 }; static const int8_t MODE_CHANGE_START_SEQUENCE[5] = { 0x99, 0x02, 0x05, 0x02, 0x03 };
@ -52,25 +60,19 @@ static int8_t _modifySramValue(enum GBAVFameCartType type, uint8_t value, int mo
static uint32_t _modifySramAddress(enum GBAVFameCartType type, uint32_t address, int mode); static uint32_t _modifySramAddress(enum GBAVFameCartType type, uint32_t address, int mode);
static int _reorderBits(uint32_t value, const uint8_t* reordering, int reorderLength); static int _reorderBits(uint32_t value, const uint8_t* reordering, int reorderLength);
void GBAVFameInit(struct GBAVFameCart* cart) { bool GBAVFameDetect(struct GBAVFameCart* cart, uint32_t* rom, size_t romSize, uint32_t crc32) {
cart->cartType = VFAME_NO;
cart->sramMode = -1;
cart->romMode = -1;
cart->acceptingModeChange = false;
}
void GBAVFameDetect(struct GBAVFameCart* cart, uint32_t* rom, size_t romSize, uint32_t crc32) {
cart->cartType = VFAME_NO;
// The initialisation code is also present & run in the dumps of Digimon Ruby & Sapphire from hacked/deprotected reprint carts, // The initialisation code is also present & run in the dumps of Digimon Ruby & Sapphire from hacked/deprotected reprint carts,
// which would break if run in "proper" VFame mode so we need to exclude those.. // which would break if run in "proper" VFame mode so we need to exclude those..
if (romSize == 0x2000000) { // the deprotected dumps are 32MB but no real VF games are this size if (romSize == 0x2000000) { // the deprotected dumps are 32MB but no real VF games are this size
return; return false;
} }
bool detected = false;
// Most games have the same init sequence in the same place // Most games have the same init sequence in the same place
// but LOTR/Mo Jie Qi Bing doesn't, probably because it's based on the Kiki KaiKai engine, so just detect based on its title // but LOTR/Mo Jie Qi Bing doesn't, probably because it's based on the Kiki KaiKai engine, so just detect based on its title
if (memcmp(INIT_SEQUENCE, &rom[0x57], sizeof(INIT_SEQUENCE)) == 0 || memcmp("\0LORD\0WORD\0\0AKIJ", &((struct GBACartridge*) rom)->title, 16) == 0) { if (memcmp(INIT_SEQUENCE, &rom[0x57], sizeof(INIT_SEQUENCE)) == 0 || memcmp("\0LORD\0WORD\0\0AKIJ", &((struct GBACartridge*) rom)->title, 16) == 0) {
detected = true;
cart->cartType = VFAME_STANDARD; cart->cartType = VFAME_STANDARD;
mLOG(GBA_MEM, INFO, "Vast Fame game detected"); mLOG(GBA_MEM, INFO, "Vast Fame game detected");
} }
@ -79,13 +81,23 @@ void GBAVFameDetect(struct GBAVFameCart* cart, uint32_t* rom, size_t romSize, ui
// Their initialisation seems to be identical so the difference must be in the cart HW itself // Their initialisation seems to be identical so the difference must be in the cart HW itself
// Other undumped games may have similar differences // Other undumped games may have similar differences
if (memcmp("George Sango", &((struct GBACartridge*) rom)->title, 12) == 0) { if (memcmp("George Sango", &((struct GBACartridge*) rom)->title, 12) == 0) {
detected = true;
cart->cartType = VFAME_GEORGE; cart->cartType = VFAME_GEORGE;
mLOG(GBA_MEM, INFO, "George mode"); mLOG(GBA_MEM, INFO, "George mode");
} else if (crc32 == DIGIMON_SAPPHIRE_CHINESE_CRC32) { } else if (crc32 == DIGIMON_SAPPHIRE_CHINESE_CRC32) {
// Chinese version of Digimon Sapphire; header is identical to the English version which uses the normal reordering // Chinese version of Digimon Sapphire; header is identical to the English version which uses the normal reordering
// so we have to use some other way to detect it // so we have to use some other way to detect it
detected = true;
cart->cartType = VFAME_ALTERNATE; cart->cartType = VFAME_ALTERNATE;
} }
if (detected) {
cart->sramMode = -1;
cart->romMode = -1;
cart->acceptingModeChange = false;
}
return detected;
} }
// This is not currently being used but would be called on ROM reads // This is not currently being used but would be called on ROM reads
@ -227,7 +239,6 @@ static uint32_t _patternRightShift2(uint32_t addr) {
} }
void GBAVFameSramWrite(struct GBAVFameCart* cart, uint32_t address, uint8_t value, uint8_t* sramData) { void GBAVFameSramWrite(struct GBAVFameCart* cart, uint32_t address, uint8_t value, uint8_t* sramData) {
address &= 0x00FFFFFF;
// A certain sequence of writes to SRAM FFF8->FFFC can enable or disable "mode change" mode // A certain sequence of writes to SRAM FFF8->FFFC can enable or disable "mode change" mode
// Currently unknown if these writes have to be sequential, or what happens if you write different values, if anything // Currently unknown if these writes have to be sequential, or what happens if you write different values, if anything
if (address >= 0xFFF8 && address <= 0xFFFC) { if (address >= 0xFFF8 && address <= 0xFFFC) {
@ -268,25 +279,14 @@ static uint32_t _modifySramAddress(enum GBAVFameCartType type, uint32_t address,
mode &= 0x3; mode &= 0x3;
if (mode == 0) { if (mode == 0) {
return address; return address;
} else if (type == VFAME_GEORGE) {
return _reorderBits(address, ADDRESS_REORDERING_GEORGE[mode - 1], 16);
} else if (type == VFAME_ALTERNATE) {
return _reorderBits(address, ADDRESS_REORDERING_ALTERNATE[mode - 1], 16);
} else {
return _reorderBits(address, ADDRESS_REORDERING[mode - 1], 16);
} }
return _reorderBits(address, ADDRESS_REORDERING[type][mode - 1], 16);
} }
static int8_t _modifySramValue(enum GBAVFameCartType type, uint8_t value, int mode) { static int8_t _modifySramValue(enum GBAVFameCartType type, uint8_t value, int mode) {
int reorderType = (mode & 0xF) >> 2; int reorderType = (mode & 0xF) >> 2;
if (reorderType != 0) { if (reorderType != 0) {
if (type == VFAME_GEORGE) { value = _reorderBits(value, VALUE_REORDERING[type][reorderType - 1], 8);
value = _reorderBits(value, VALUE_REORDERING_GEORGE[reorderType - 1], 8);
} else if (type == VFAME_ALTERNATE) {
value = _reorderBits(value, VALUE_REORDERING_ALTERNATE[reorderType - 1], 8);
} else {
value = _reorderBits(value, VALUE_REORDERING[reorderType - 1], 8);
}
} }
if (mode & 0x80) { if (mode & 0x80) {
value ^= 0xAA; value ^= 0xAA;

View File

@ -30,6 +30,7 @@
#ifdef USE_ELF #ifdef USE_ELF
#include <mgba-util/elf-read.h> #include <mgba-util/elf-read.h>
#endif #endif
#include <mgba-util/md5.h>
#include <mgba-util/memory.h> #include <mgba-util/memory.h>
#include <mgba-util/patch.h> #include <mgba-util/patch.h>
#include <mgba-util/vfs.h> #include <mgba-util/vfs.h>
@ -667,6 +668,9 @@ static size_t _GBACoreROMSize(const struct mCore* core) {
if (gba->romVf) { if (gba->romVf) {
return gba->romVf->size(gba->romVf); return gba->romVf->size(gba->romVf);
} }
if (gba->mbVf) {
return gba->mbVf->size(gba->mbVf);
}
return gba->pristineRomSize; return gba->pristineRomSize;
} }
@ -676,6 +680,19 @@ static void _GBACoreChecksum(const struct mCore* core, void* data, enum mCoreChe
case mCHECKSUM_CRC32: case mCHECKSUM_CRC32:
memcpy(data, &gba->romCrc32, sizeof(gba->romCrc32)); memcpy(data, &gba->romCrc32, sizeof(gba->romCrc32));
break; break;
case mCHECKSUM_MD5:
if (gba->romVf) {
md5File(gba->romVf, data);
} else if (gba->mbVf) {
md5File(gba->mbVf, data);
} else if (gba->memory.rom && gba->isPristine) {
md5Buffer(gba->memory.rom, gba->pristineRomSize, data);
} else if (gba->memory.rom) {
md5Buffer(gba->memory.rom, gba->memory.romSize, data);
} else {
md5Buffer("", 0, data);
}
break;
} }
return; return;
} }
@ -842,7 +859,21 @@ static bool _GBACoreLoadExtraState(struct mCore* core, const struct mStateExtdat
if (type == gba->video.renderer->rendererId(gba->video.renderer)) { if (type == gba->video.renderer->rendererId(gba->video.renderer)) {
ok = gba->video.renderer->loadState(gba->video.renderer, ok = gba->video.renderer->loadState(gba->video.renderer,
(void*) ((uintptr_t) item.data + sizeof(uint32_t)), (void*) ((uintptr_t) item.data + sizeof(uint32_t)),
item.size - sizeof(type)); item.size - sizeof(type)) && ok;
}
} else if (item.data) {
ok = false;
}
}
if (gba->sio.driver && gba->sio.driver->driverId && gba->sio.driver->loadState &&
mStateExtdataGet(extdata, EXTDATA_SUBSYSTEM_START + GBA_SUBSYSTEM_SIO_DRIVER, &item)) {
if ((uint32_t) item.size > sizeof(uint32_t)) {
uint32_t type;
LOAD_32(type, 0, item.data);
if (type == gba->sio.driver->driverId(gba->sio.driver)) {
ok = gba->sio.driver->loadState(gba->sio.driver,
(void*) ((uintptr_t) item.data + sizeof(uint32_t)),
item.size - sizeof(type)) && ok;
} }
} else if (item.data) { } else if (item.data) {
ok = false; ok = false;
@ -868,6 +899,27 @@ static bool _GBACoreSaveExtraState(struct mCore* core, struct mStateExtdata* ext
} }
if (buffer) { if (buffer) {
free(buffer); free(buffer);
buffer = NULL;
}
size = 0;
if (gba->sio.driver && gba->sio.driver->driverId && gba->sio.driver->saveState) {
gba->sio.driver->saveState(gba->sio.driver, &buffer, &size);
if (size > 0 && buffer) {
struct mStateExtdataItem item;
item.size = size + sizeof(uint32_t);
item.data = malloc(item.size);
item.clean = free;
uint32_t type = gba->sio.driver->driverId(gba->sio.driver);
STORE_32(type, 0, item.data);
memcpy((void*) ((uintptr_t) item.data + sizeof(uint32_t)), buffer, size);
mStateExtdataPut(extdata, EXTDATA_SUBSYSTEM_START + GBA_SUBSYSTEM_SIO_DRIVER, &item);
}
if (buffer) {
free(buffer);
buffer = NULL;
}
size = 0;
} }
return true; return true;
@ -927,9 +979,8 @@ static void _GBACoreSetPeripheral(struct mCore* core, int type, void* periph) {
case mPERIPH_GBA_LUMINANCE: case mPERIPH_GBA_LUMINANCE:
gba->luminanceSource = periph; gba->luminanceSource = periph;
break; break;
case mPERIPH_GBA_BATTLECHIP_GATE: case mPERIPH_GBA_LINK_PORT:
GBASIOSetDriver(&gba->sio, periph, GBA_SIO_MULTI); GBASIOSetDriver(&gba->sio, periph);
GBASIOSetDriver(&gba->sio, periph, GBA_SIO_NORMAL_32);
break; break;
default: default:
return; return;

View File

@ -256,15 +256,21 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
if (info->count == info->nextCount) { if (info->count == info->nextCount) {
if (width == 4) { if (width == 4) {
cycles += memory->waitstatesNonseq32[sourceRegion] + memory->waitstatesNonseq32[destRegion]; cycles += memory->waitstatesNonseq32[sourceRegion] + memory->waitstatesNonseq32[destRegion];
info->cycles = memory->waitstatesSeq32[sourceRegion] + memory->waitstatesSeq32[destRegion];
} else { } else {
cycles += memory->waitstatesNonseq16[sourceRegion] + memory->waitstatesNonseq16[destRegion]; cycles += memory->waitstatesNonseq16[sourceRegion] + memory->waitstatesNonseq16[destRegion];
info->cycles = memory->waitstatesSeq16[sourceRegion] + memory->waitstatesSeq16[destRegion];
} }
} else { } else {
if (width == 4) { // Crossed region boundary; recalculate cached cycles
cycles += memory->waitstatesSeq32[sourceRegion] + memory->waitstatesSeq32[destRegion]; if (UNLIKELY(!(source & 0x00FFFFFC) || !(dest & 0x00FFFFFC))) {
} else { if (width == 4) {
cycles += memory->waitstatesSeq16[sourceRegion] + memory->waitstatesSeq16[destRegion]; info->cycles = memory->waitstatesSeq32[sourceRegion] + memory->waitstatesSeq32[destRegion];
} else {
info->cycles = memory->waitstatesSeq16[sourceRegion] + memory->waitstatesSeq16[destRegion];
}
} }
cycles += info->cycles;
} }
info->when += cycles; info->when += cycles;
@ -281,7 +287,7 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
memory->dmaTransferRegister = cpu->memory.load16(cpu, source, 0); memory->dmaTransferRegister = cpu->memory.load16(cpu, source, 0);
memory->dmaTransferRegister |= memory->dmaTransferRegister << 16; memory->dmaTransferRegister |= memory->dmaTransferRegister << 16;
} }
if (destRegion == GBA_REGION_ROM2_EX) { if (UNLIKELY(destRegion == GBA_REGION_ROM2_EX)) {
if (memory->savedata.type == GBA_SAVEDATA_AUTODETECT) { if (memory->savedata.type == GBA_SAVEDATA_AUTODETECT) {
mLOG(GBA_MEM, INFO, "Detected EEPROM savegame"); mLOG(GBA_MEM, INFO, "Detected EEPROM savegame");
GBASavedataInitEEPROM(&memory->savedata); GBASavedataInitEEPROM(&memory->savedata);
@ -313,9 +319,11 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
int i; int i;
for (i = 0; i < 4; ++i) { for (i = 0; i < 4; ++i) {
struct GBADMA* dma = &memory->dma[i]; struct GBADMA* dma = &memory->dma[i];
int32_t time = dma->when - info->when; if (GBADMARegisterIsEnable(dma->reg) && dma->nextCount) {
if (time < 0 && GBADMARegisterIsEnable(dma->reg) && dma->nextCount) { int32_t time = dma->when - info->when;
dma->when = info->when; if (time < 0) {
dma->when = info->when;
}
} }
} }
@ -327,3 +335,22 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
} }
GBADMAUpdate(gba); GBADMAUpdate(gba);
} }
void GBADMARecalculateCycles(struct GBA* gba) {
int i;
for (i = 0; i < 4; ++i) {
struct GBADMA* dma = &gba->memory.dma[i];
if (!GBADMARegisterIsEnable(dma->reg)) {
continue;
}
uint32_t width = GBADMARegisterGetWidth(dma->reg);
uint32_t sourceRegion = dma->nextSource >> BASE_OFFSET;
uint32_t destRegion = dma->nextDest >> BASE_OFFSET;
if (width) {
dma->cycles = gba->memory.waitstatesSeq32[sourceRegion] + gba->memory.waitstatesSeq32[destRegion];
} else {
dma->cycles = gba->memory.waitstatesSeq16[sourceRegion] + gba->memory.waitstatesSeq16[destRegion];
}
}
}

View File

@ -33,28 +33,25 @@ enum {
BATTLECHIP_CONTINUE = 0xFFFF, BATTLECHIP_CONTINUE = 0xFFFF,
}; };
static bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver); static bool GBASIOBattlechipGateInit(struct GBASIODriver* driver);
static uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value); static uint16_t GBASIOBattlechipGateWriteSIOCNT(struct GBASIODriver* driver, uint16_t value);
static bool GBASIOBattlechipGateHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode);
static void _battlechipTransfer(struct GBASIOBattlechipGate* gate); static int GBASIOBattlechipGateConnectedDevices(struct GBASIODriver* driver);
static void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate); static void GBASIOBattlechipGateFinishMultiplayer(struct GBASIODriver* driver, uint16_t data[4]);
void GBASIOBattlechipGateCreate(struct GBASIOBattlechipGate* gate) { void GBASIOBattlechipGateCreate(struct GBASIOBattlechipGate* gate) {
gate->d.init = NULL; memset(&gate->d, 0, sizeof(gate->d));
gate->d.deinit = NULL; gate->d.init = GBASIOBattlechipGateInit;
gate->d.load = GBASIOBattlechipGateLoad; gate->d.writeSIOCNT = GBASIOBattlechipGateWriteSIOCNT;
gate->d.unload = NULL; gate->d.handlesMode = GBASIOBattlechipGateHandlesMode;
gate->d.writeRegister = GBASIOBattlechipGateWriteRegister; gate->d.connectedDevices = GBASIOBattlechipGateConnectedDevices;
gate->d.finishMultiplayer = GBASIOBattlechipGateFinishMultiplayer;
gate->event.context = gate;
gate->event.callback = _battlechipTransferEvent;
gate->event.priority = 0x80;
gate->chipId = 0; gate->chipId = 0;
gate->flavor = GBA_FLAVOR_BATTLECHIP_GATE; gate->flavor = GBA_FLAVOR_BATTLECHIP_GATE;
} }
bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver) { bool GBASIOBattlechipGateInit(struct GBASIODriver* driver) {
struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver; struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
gate->state = BATTLECHIP_STATE_SYNC; gate->state = BATTLECHIP_STATE_SYNC;
gate->data[0] = 0x00FE; gate->data[0] = 0x00FE;
@ -62,58 +59,34 @@ bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver) {
return true; return true;
} }
uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) { uint16_t GBASIOBattlechipGateWriteSIOCNT(struct GBASIODriver* driver, uint16_t value) {
struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver; UNUSED(driver);
switch (address) { value &= ~0xC;
case GBA_REG_SIOCNT: value |= 0x8;
value &= ~0xC;
value |= 0x8;
if (value & 0x80) {
_battlechipTransfer(gate);
}
break;
case GBA_REG_SIOMLT_SEND:
break;
case GBA_REG_RCNT:
break;
default:
break;
}
return value; return value;
} }
void _battlechipTransfer(struct GBASIOBattlechipGate* gate) { static bool GBASIOBattlechipGateHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode) {
int32_t cycles; UNUSED(driver);
if (gate->d.p->mode == GBA_SIO_NORMAL_32) { switch (mode) {
cycles = GBA_ARM7TDMI_FREQUENCY / 0x40000; case GBA_SIO_NORMAL_32:
} else { case GBA_SIO_MULTI:
cycles = GBASIOCyclesPerTransfer[GBASIOMultiplayerGetBaud(gate->d.p->siocnt)][1]; return true;
default:
return false;
} }
mTimingDeschedule(&gate->d.p->p->timing, &gate->event);
mTimingSchedule(&gate->d.p->p->timing, &gate->event, cycles);
} }
void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate) { static int GBASIOBattlechipGateConnectedDevices(struct GBASIODriver* driver) {
UNUSED(timing); UNUSED(driver);
struct GBASIOBattlechipGate* gate = user; return 1;
}
if (gate->d.p->mode == GBA_SIO_NORMAL_32) { static void GBASIOBattlechipGateFinishMultiplayer(struct GBASIODriver* driver, uint16_t data[4]) {
gate->d.p->p->memory.io[GBA_REG(SIODATA32_LO)] = 0; struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
gate->d.p->p->memory.io[GBA_REG(SIODATA32_HI)] = 0;
gate->d.p->siocnt = GBASIONormalClearStart(gate->d.p->siocnt);
if (GBASIONormalIsIrq(gate->d.p->siocnt)) {
GBARaiseIRQ(gate->d.p->p, GBA_IRQ_SIO, cyclesLate);
}
return;
}
uint16_t cmd = gate->d.p->p->memory.io[GBA_REG(SIOMLT_SEND)]; uint16_t cmd = gate->d.p->p->memory.io[GBA_REG(SIOMLT_SEND)];
uint16_t reply = 0xFFFF; uint16_t reply = 0xFFFF;
gate->d.p->p->memory.io[GBA_REG(SIOMULTI0)] = cmd;
gate->d.p->p->memory.io[GBA_REG(SIOMULTI2)] = 0xFFFF;
gate->d.p->p->memory.io[GBA_REG(SIOMULTI3)] = 0xFFFF;
gate->d.p->siocnt = GBASIOMultiplayerClearBusy(gate->d.p->siocnt);
gate->d.p->siocnt = GBASIOMultiplayerSetId(gate->d.p->siocnt, 0);
mLOG(GBA_BATTLECHIP, DEBUG, "Game: %04X (%i)", cmd, gate->state); mLOG(GBA_BATTLECHIP, DEBUG, "Game: %04X (%i)", cmd, gate->state);
@ -146,7 +119,7 @@ void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cycle
case 0xA3D0: case 0xA3D0:
// EXE 4 // EXE 4
case 0xA6C0: case 0xA6C0:
mLOG(GBA_BATTLECHIP, DEBUG, "Resync detected"); mLOG(GBA_BATTLECHIP, DEBUG, "Resync detected");
gate->state = BATTLECHIP_STATE_SYNC; gate->state = BATTLECHIP_STATE_SYNC;
break; break;
} }
@ -191,9 +164,8 @@ void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cycle
mLOG(GBA_BATTLECHIP, DEBUG, "Gate: %04X (%i)", reply, gate->state); mLOG(GBA_BATTLECHIP, DEBUG, "Gate: %04X (%i)", reply, gate->state);
++gate->state; ++gate->state;
gate->d.p->p->memory.io[GBA_REG(SIOMULTI1)] = reply; data[0] = cmd;
data[1] = reply;
if (GBASIOMultiplayerIsIrq(gate->d.p->siocnt)) { data[2] = 0xFFFF;
GBARaiseIRQ(gate->d.p->p, GBA_IRQ_SIO, cyclesLate); data[3] = 0xFFFF;
}
} }

View File

@ -324,7 +324,7 @@ void GBAVideoProxyRendererSaveState(struct GBAVideoRenderer* renderer, void** st
proxyRenderer->logger->stateSize = 0; proxyRenderer->logger->stateSize = 0;
} else { } else {
proxyRenderer->backend->saveState(proxyRenderer->backend, state, size); proxyRenderer->backend->saveState(proxyRenderer->backend, state, size);
} }
} }
void GBAVideoProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) { void GBAVideoProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {

View File

@ -140,6 +140,9 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
void GBAUnloadROM(struct GBA* gba) { void GBAUnloadROM(struct GBA* gba) {
GBAMemoryClearAGBPrint(gba); GBAMemoryClearAGBPrint(gba);
if (gba->memory.unl.type) {
GBAUnlCartUnload(gba);
}
if (gba->memory.rom && !gba->isPristine) { if (gba->memory.rom && !gba->isPristine) {
if (gba->yankedRomSize) { if (gba->yankedRomSize) {
gba->yankedRomSize = 0; gba->yankedRomSize = 0;
@ -263,8 +266,8 @@ void GBAReset(struct ARMCore* cpu) {
// GB Player SIO control should not be engaged before detection, even if we already know it's GBP // GB Player SIO control should not be engaged before detection, even if we already know it's GBP
gba->memory.hw.devices &= ~HW_GB_PLAYER; gba->memory.hw.devices &= ~HW_GB_PLAYER;
if (gba->sio.drivers.normal == &gba->sio.gbp.d) { if (gba->sio.driver == &gba->sio.gbp.d) {
GBASIOSetDriver(&gba->sio, NULL, GBA_SIO_NORMAL_32); GBASIOSetDriver(&gba->sio, NULL);
} }
bool isELF = false; bool isELF = false;
@ -492,7 +495,7 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) {
gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]); gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
} }
GBAHardwareInit(&gba->memory.hw, &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1]); GBAHardwareInit(&gba->memory.hw, &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1]);
GBAVFameDetect(&gba->memory.vfame, gba->memory.rom, gba->memory.romSize, gba->romCrc32); GBAUnlCartDetect(gba);
// TODO: error check // TODO: error check
return true; return true;
} }
@ -557,16 +560,14 @@ void GBAApplyPatch(struct GBA* gba, struct Patch* patch) {
mappedMemoryFree(newRom, GBA_SIZE_ROM0); mappedMemoryFree(newRom, GBA_SIZE_ROM0);
return; return;
} }
if (gba->romVf) { if (gba->memory.rom) {
#ifndef FIXED_ROM_BUFFER #ifndef FIXED_ROM_BUFFER
if (!gba->isPristine) { if (!gba->isPristine) {
mappedMemoryFree(gba->memory.rom, GBA_SIZE_ROM0); mappedMemoryFree(gba->memory.rom, gba->memory.romSize);
} else { } else {
gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->pristineRomSize); gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->pristineRomSize);
} }
#endif #endif
gba->romVf->close(gba->romVf);
gba->romVf = NULL;
} }
gba->isPristine = false; gba->isPristine = false;
gba->memory.rom = newRom; gba->memory.rom = newRom;

View File

@ -20,7 +20,7 @@ const uint8_t hleBios[GBA_SIZE_BIOS] = {
0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x04, 0x40, 0xbd, 0xe8, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x04, 0x40, 0xbd, 0xe8,
0x93, 0xf0, 0x29, 0xe3, 0x00, 0x08, 0xbd, 0xe8, 0x0b, 0xf0, 0x69, 0xe1, 0x93, 0xf0, 0x29, 0xe3, 0x00, 0x08, 0xbd, 0xe8, 0x0b, 0xf0, 0x69, 0xe1,
0x00, 0x58, 0xbd, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0xbd, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00,
0x04, 0x20, 0xa0, 0xe3, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0x04, 0x20, 0xa0, 0xe3, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x03, 0x00, 0x00,
0xb0, 0x01, 0x00, 0x00, 0xb4, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb4, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00,
0xcc, 0x01, 0x00, 0x00, 0xc4, 0x01, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00, 0xcc, 0x01, 0x00, 0x00, 0xc4, 0x01, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00,
0x48, 0x03, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00,
@ -75,16 +75,29 @@ const uint8_t hleBios[GBA_SIZE_BIOS] = {
0xf0, 0x07, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0xb0, 0x01, 0x00, 0x00, 0xf0, 0x07, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0xb0, 0x01, 0x00, 0x00,
0x04, 0xb0, 0x5b, 0xe2, 0xfd, 0xff, 0xff, 0x8a, 0x1e, 0xff, 0x2f, 0xe1, 0x04, 0xb0, 0x5b, 0xe2, 0xfd, 0xff, 0xff, 0x8a, 0x1e, 0xff, 0x2f, 0xe1,
0xc2, 0xe3, 0xa0, 0xe3, 0x03, 0x10, 0x5e, 0xe4, 0x00, 0x00, 0x51, 0xe3, 0xc2, 0xe3, 0xa0, 0xe3, 0x03, 0x10, 0x5e, 0xe4, 0x00, 0x00, 0x51, 0xe3,
0x00, 0x10, 0xa0, 0x13, 0x1e, 0xff, 0x2f, 0x11, 0x1c, 0xe0, 0x9f, 0xe5, 0x00, 0x10, 0xa0, 0x13, 0x05, 0x00, 0x00, 0x1a, 0x1c, 0xe0, 0x9f, 0xe5,
0x00, 0x10, 0x9e, 0xe5, 0x00, 0x00, 0x51, 0xe3, 0x00, 0x10, 0xa0, 0xe3, 0x00, 0x10, 0x9e, 0xe5, 0x00, 0x00, 0x51, 0xe3, 0x00, 0x10, 0xa0, 0xe3,
0x1e, 0xff, 0x2f, 0x11, 0xc0, 0xe0, 0x4e, 0xe2, 0x1e, 0xff, 0x2f, 0xe1, 0x00, 0x00, 0x00, 0x1a, 0xc0, 0xe0, 0x4e, 0xe2, 0x1e, 0xff, 0x2f, 0xe1,
0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x29, 0xe1, 0xc0, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x29, 0xe1, 0xc0, 0x00, 0x00, 0x02,
0x4c, 0xd0, 0x9f, 0xe5, 0x00, 0x50, 0x2d, 0xe9, 0x00, 0xc0, 0x4f, 0xe1, 0xdc, 0xd0, 0x9f, 0xe5, 0x00, 0x50, 0x2d, 0xe9, 0x00, 0xc0, 0x4f, 0xe1,
0x00, 0xe0, 0x0f, 0xe1, 0x00, 0x50, 0x2d, 0xe9, 0x02, 0xe3, 0xa0, 0xe3, 0x00, 0xe0, 0x0f, 0xe1, 0x00, 0x50, 0x2d, 0xe9, 0x02, 0xe3, 0xa0, 0xe3,
0x9c, 0xc0, 0xde, 0xe5, 0xa5, 0x00, 0x5c, 0xe3, 0x04, 0x00, 0x00, 0x1a, 0x9c, 0xc0, 0xde, 0xe5, 0xa5, 0x00, 0x5c, 0xe3, 0x04, 0x00, 0x00, 0x1a,
0xb4, 0xc0, 0xde, 0xe5, 0x80, 0x00, 0x1c, 0xe3, 0x04, 0xe0, 0x8f, 0xe2, 0xb4, 0xc0, 0xde, 0xe5, 0x80, 0x00, 0x1c, 0xe3, 0x04, 0xe0, 0x8f, 0xe2,
0x20, 0xf0, 0x9f, 0x15, 0x20, 0xf0, 0x9f, 0x05, 0x14, 0xd0, 0x9f, 0xe5, 0xb0, 0xf0, 0x9f, 0x15, 0xb0, 0xf0, 0x9f, 0x05, 0xa4, 0xd0, 0x9f, 0xe5,
0x10, 0xc0, 0x1d, 0xe5, 0x0c, 0xf0, 0x69, 0xe1, 0x00, 0x50, 0x3d, 0xe9, 0x10, 0xc0, 0x1d, 0xe5, 0x0c, 0xf0, 0x69, 0xe1, 0x00, 0x50, 0x3d, 0xe9,
0x04, 0xf0, 0x5e, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x04, 0xe0, 0xa0, 0x03, 0x04, 0xf0, 0x5e, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x04, 0xe0, 0xa0, 0x03,
0xf0, 0x7f, 0x00, 0x03, 0x00, 0x20, 0xfe, 0x09, 0x00, 0xc0, 0xff, 0x09 0x00, 0xf0, 0x69, 0xe3, 0x00, 0xe0, 0xa0, 0xe3, 0x8c, 0xd0, 0x9f, 0xe5,
0x92, 0xf0, 0x21, 0xe3, 0x00, 0xf0, 0x69, 0xe3, 0x00, 0xe0, 0xa0, 0xe3,
0x80, 0xd0, 0x9f, 0xe5, 0x93, 0xf0, 0x21, 0xe3, 0x00, 0xf0, 0x69, 0xe3,
0x00, 0xe0, 0xa0, 0xe3, 0x74, 0xd0, 0x9f, 0xe5, 0x01, 0x03, 0xa0, 0xe3,
0x02, 0x1c, 0x40, 0xe2, 0x06, 0x00, 0x50, 0xe5, 0x00, 0x20, 0xa0, 0xe3,
0x00, 0x30, 0xa0, 0xe3, 0x00, 0x40, 0xa0, 0xe3, 0x00, 0x50, 0xa0, 0xe3,
0x00, 0x60, 0xa0, 0xe3, 0x00, 0x70, 0xa0, 0xe3, 0x00, 0x80, 0xa0, 0xe3,
0x00, 0x90, 0xa0, 0xe3, 0x00, 0xa0, 0xa0, 0xe3, 0x00, 0xb0, 0xa0, 0xe3,
0x00, 0xc0, 0xa0, 0xe3, 0xfc, 0x03, 0xa1, 0xe8, 0x01, 0x03, 0x51, 0xe3,
0xfc, 0xff, 0xff, 0x1a, 0x00, 0x00, 0x50, 0xe3, 0x00, 0x00, 0xa0, 0xe3,
0x00, 0x10, 0xa0, 0xe3, 0x02, 0xe3, 0xa0, 0x03, 0x02, 0xe4, 0xa0, 0x13,
0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x29, 0xe1,
0xf0, 0x7f, 0x00, 0x03, 0x00, 0x20, 0xfe, 0x09, 0x00, 0xc0, 0xff, 0x09,
0x00, 0x7f, 0x00, 0x03, 0xa0, 0x7f, 0x00, 0x03, 0xe0, 0x7f, 0x00, 0x03
}; };

View File

@ -129,7 +129,6 @@ subs pc, lr, #4
.word 0x03A0E004 .word 0x03A0E004
@ Unimplemented @ Unimplemented
SoftReset:
RegisterRamReset: RegisterRamReset:
Stop: Stop:
GetBiosChecksum: GetBiosChecksum:
@ -318,13 +317,14 @@ mov lr, #0x8000003
ldrb r1, [lr], #-3 ldrb r1, [lr], #-3
cmp r1, #0 cmp r1, #0
movne r1, #0 movne r1, #0
bxne lr bne 1f
ldr lr, =0x20000C0 ldr lr, =0x20000C0
ldr r1, [lr] ldr r1, [lr]
cmp r1, #0 cmp r1, #0
mov r1, #0 mov r1, #0
bxne lr bne 1f
sub lr, #0xC0 sub lr, #0xC0
1:
bx lr bx lr
.word 0 .word 0
.word 0xE129F000 .word 0xE129F000
@ -357,3 +357,44 @@ ldmdb sp!, {r12, lr}
subs pc, lr, #4 subs pc, lr, #4
.word 0 .word 0
.word 0x03A0E004 .word 0x03A0E004
SoftReset:
msr spsr, #0
mov lr, #0
ldr sp, =0x03007F00
msr cpsr_c, #0x92
msr spsr, #0
mov lr, #0
ldr sp, =0x03007FA0
msr cpsr_c, #0x93
msr spsr, #0
mov lr, #0
ldr sp, =0x03007FE0
mov r0, #0x04000000
sub r1, r0, #0x200
ldrb r0, [r0, #-6]
mov r2, #0
mov r3, #0
mov r4, #0
mov r5, #0
mov r6, #0
mov r7, #0
mov r8, #0
mov r9, #0
mov r10, #0
mov r11, #0
mov r12, #0
1:
stmia r1!, {r2, r3, r4, r5, r6, r7, r8, r9}
cmp r1, #0x04000000
bne 1b
cmp r0, #0
mov r0, #0
mov r1, #0
moveq lr, #0x08000000
movne lr, #0x02000000
movs pc, lr
.word 0
.word 0xE129F000
.ltorg

View File

@ -214,8 +214,8 @@ static const int _isRSpecialRegister[GBA_REG(INTERNAL_MAX)] = {
/* 10 */ 1, 1, 1, 1, 1, 1, 1, 1, /* 10 */ 1, 1, 1, 1, 1, 1, 1, 1,
/* 11 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 11 */ 0, 0, 0, 0, 0, 0, 0, 0,
/* SIO */ /* SIO */
/* 12 */ 1, 1, 1, 1, 1, 0, 0, 0, /* 12 */ 1, 1, 1, 1, 0, 0, 0, 0,
/* 13 */ 1, 1, 1, 0, 0, 0, 0, 0, /* 13 */ 1, 1, 0, 0, 0, 0, 0, 0,
/* 14 */ 1, 0, 0, 0, 0, 0, 0, 0, /* 14 */ 1, 0, 0, 0, 0, 0, 0, 0,
/* 15 */ 1, 1, 1, 1, 1, 0, 0, 0, /* 15 */ 1, 1, 1, 1, 1, 0, 0, 0,
/* 16 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 16 */ 0, 0, 0, 0, 0, 0, 0, 0,
@ -326,17 +326,19 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
break; break;
case GBA_REG_SOUND1CNT_HI: case GBA_REG_SOUND1CNT_HI:
GBAAudioWriteSOUND1CNT_HI(&gba->audio, value); GBAAudioWriteSOUND1CNT_HI(&gba->audio, value);
value &= 0xFFC0;
break; break;
case GBA_REG_SOUND1CNT_X: case GBA_REG_SOUND1CNT_X:
GBAAudioWriteSOUND1CNT_X(&gba->audio, value); GBAAudioWriteSOUND1CNT_X(&gba->audio, value);
value &= 0x47FF; value &= 0x4000;
break; break;
case GBA_REG_SOUND2CNT_LO: case GBA_REG_SOUND2CNT_LO:
GBAAudioWriteSOUND2CNT_LO(&gba->audio, value); GBAAudioWriteSOUND2CNT_LO(&gba->audio, value);
value &= 0xFFC0;
break; break;
case GBA_REG_SOUND2CNT_HI: case GBA_REG_SOUND2CNT_HI:
GBAAudioWriteSOUND2CNT_HI(&gba->audio, value); GBAAudioWriteSOUND2CNT_HI(&gba->audio, value);
value &= 0x47FF; value &= 0x4000;
break; break;
case GBA_REG_SOUND3CNT_LO: case GBA_REG_SOUND3CNT_LO:
GBAAudioWriteSOUND3CNT_LO(&gba->audio, value); GBAAudioWriteSOUND3CNT_LO(&gba->audio, value);
@ -344,16 +346,15 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
break; break;
case GBA_REG_SOUND3CNT_HI: case GBA_REG_SOUND3CNT_HI:
GBAAudioWriteSOUND3CNT_HI(&gba->audio, value); GBAAudioWriteSOUND3CNT_HI(&gba->audio, value);
value &= 0xE03F; value &= 0xE000;
break; break;
case GBA_REG_SOUND3CNT_X: case GBA_REG_SOUND3CNT_X:
GBAAudioWriteSOUND3CNT_X(&gba->audio, value); GBAAudioWriteSOUND3CNT_X(&gba->audio, value);
// TODO: The low bits need to not be readable, but still 8-bit writable value &= 0x4000;
value &= 0x47FF;
break; break;
case GBA_REG_SOUND4CNT_LO: case GBA_REG_SOUND4CNT_LO:
GBAAudioWriteSOUND4CNT_LO(&gba->audio, value); GBAAudioWriteSOUND4CNT_LO(&gba->audio, value);
value &= 0xFF3F; value &= 0xFF00;
break; break;
case GBA_REG_SOUND4CNT_HI: case GBA_REG_SOUND4CNT_HI:
GBAAudioWriteSOUND4CNT_HI(&gba->audio, value); GBAAudioWriteSOUND4CNT_HI(&gba->audio, value);
@ -482,6 +483,7 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
// SIO // SIO
case GBA_REG_SIOCNT: case GBA_REG_SIOCNT:
value &= 0x7FFF;
GBASIOWriteSIOCNT(&gba->sio, value); GBASIOWriteSIOCNT(&gba->sio, value);
break; break;
case GBA_REG_RCNT: case GBA_REG_RCNT:
@ -585,9 +587,96 @@ void GBAIOWrite8(struct GBA* gba, uint32_t address, uint8_t value) {
if (address > GBA_SIZE_IO) { if (address > GBA_SIZE_IO) {
return; return;
} }
uint16_t value16 = value << (8 * (address & 1)); uint16_t value16;
value16 |= (gba->memory.io[(address & (GBA_SIZE_IO - 1)) >> 1]) & ~(0xFF << (8 * (address & 1)));
GBAIOWrite(gba, address & 0xFFFFFFFE, value16); switch (address) {
case GBA_REG_SOUND1CNT_HI:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR11(&gba->audio.psg, value);
gba->memory.io[GBA_REG(SOUND1CNT_HI)] &= 0xFF00;
gba->memory.io[GBA_REG(SOUND1CNT_HI)] |= value & 0xC0;
break;
case GBA_REG_SOUND1CNT_HI + 1:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR12(&gba->audio.psg, value);
gba->memory.io[GBA_REG(SOUND1CNT_HI)] &= 0x00C0;
gba->memory.io[GBA_REG(SOUND1CNT_HI)] |= value << 8;
break;
case GBA_REG_SOUND1CNT_X:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR13(&gba->audio.psg, value);
break;
case GBA_REG_SOUND1CNT_X + 1:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR14(&gba->audio.psg, value);
gba->memory.io[GBA_REG(SOUND1CNT_X)] = (value & 0x40) << 8;
break;
case GBA_REG_SOUND2CNT_LO:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR21(&gba->audio.psg, value);
gba->memory.io[GBA_REG(SOUND2CNT_LO)] &= 0xFF00;
gba->memory.io[GBA_REG(SOUND2CNT_LO)] |= value & 0xC0;
break;
case GBA_REG_SOUND2CNT_LO + 1:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR22(&gba->audio.psg, value);
gba->memory.io[GBA_REG(SOUND2CNT_LO)] &= 0x00C0;
gba->memory.io[GBA_REG(SOUND2CNT_LO)] |= value << 8;
break;
case GBA_REG_SOUND2CNT_HI:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR23(&gba->audio.psg, value);
break;
case GBA_REG_SOUND2CNT_HI + 1:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR24(&gba->audio.psg, value);
gba->memory.io[GBA_REG(SOUND2CNT_HI)] = (value & 0x40) << 8;
break;
case GBA_REG_SOUND3CNT_HI:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR31(&gba->audio.psg, value);
break;
case GBA_REG_SOUND3CNT_HI + 1:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
gba->audio.psg.ch3.volume = GBAudioRegisterBankVolumeGetVolumeGBA(value);
gba->memory.io[GBA_REG(SOUND3CNT_HI)] = (value & 0xE0) << 8;
break;
case GBA_REG_SOUND3CNT_X:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR33(&gba->audio.psg, value);
break;
case GBA_REG_SOUND3CNT_X + 1:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR34(&gba->audio.psg, value);
gba->memory.io[GBA_REG(SOUND3CNT_X)] = (value & 0x40) << 8;
break;
case GBA_REG_SOUND4CNT_LO:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR41(&gba->audio.psg, value);
break;
case GBA_REG_SOUND4CNT_LO + 1:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR42(&gba->audio.psg, value);
gba->memory.io[GBA_REG(SOUND4CNT_LO)] = value << 8;
break;
case GBA_REG_SOUND4CNT_HI:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR43(&gba->audio.psg, value);
gba->memory.io[GBA_REG(SOUND4CNT_HI)] &= 0x4000;
gba->memory.io[GBA_REG(SOUND4CNT_HI)] |= value;
break;
case GBA_REG_SOUND4CNT_HI + 1:
GBAAudioSample(&gba->audio, mTimingCurrentTime(&gba->timing));
GBAudioWriteNR44(&gba->audio.psg, value);
gba->memory.io[GBA_REG(SOUND4CNT_HI)] &= 0x00FF;
gba->memory.io[GBA_REG(SOUND4CNT_HI)] |= (value & 0x40) << 8;
break;
default:
value16 = value << (8 * (address & 1));
value16 |= (gba->memory.io[(address & (GBA_SIZE_IO - 1)) >> 1]) & ~(0xFF << (8 * (address & 1)));
GBAIOWrite(gba, address & 0xFFFFFFFE, value16);
break;
}
} }
void GBAIOWrite32(struct GBA* gba, uint32_t address, uint32_t value) { void GBAIOWrite32(struct GBA* gba, uint32_t address, uint32_t value) {
@ -990,6 +1079,7 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
LOAD_32(gba->dmaPC, 0, &state->dmaBlockPC); LOAD_32(gba->dmaPC, 0, &state->dmaBlockPC);
LOAD_32(gba->bus, 0, &state->bus); LOAD_32(gba->bus, 0, &state->bus);
GBADMARecalculateCycles(gba);
GBADMAUpdate(gba); GBADMAUpdate(gba);
GBAHardwareDeserialize(&gba->memory.hw, state); GBAHardwareDeserialize(&gba->memory.hw, state);
} }

View File

@ -93,7 +93,7 @@ void GBAMemoryInit(struct GBA* gba) {
gba->memory.iwram = &gba->memory.wram[GBA_SIZE_EWRAM >> 2]; gba->memory.iwram = &gba->memory.wram[GBA_SIZE_EWRAM >> 2];
GBADMAInit(gba); GBADMAInit(gba);
GBAVFameInit(&gba->memory.vfame); GBAUnlCartInit(gba);
gba->memory.ereader.p = gba; gba->memory.ereader.p = gba;
gba->memory.ereader.dots = NULL; gba->memory.ereader.dots = NULL;
@ -138,7 +138,18 @@ void GBAMemoryReset(struct GBA* gba) {
mLOG(GBA_MEM, FATAL, "Could not map memory"); mLOG(GBA_MEM, FATAL, "Could not map memory");
} }
if (!gba->memory.rom) {
gba->isPristine = false;
}
if (gba->memory.hw.devices & HW_GPIO) {
_pristineCow(gba);
}
GBASavedataReset(&gba->memory.savedata);
GBAHardwareReset(&gba->memory.hw);
GBADMAReset(gba); GBADMAReset(gba);
GBAUnlCartReset(gba);
memset(&gba->memory.matrix, 0, sizeof(gba->memory.matrix)); memset(&gba->memory.matrix, 0, sizeof(gba->memory.matrix));
} }
@ -411,7 +422,7 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
wait += waitstatesRegion[address >> BASE_OFFSET]; \ wait += waitstatesRegion[address >> BASE_OFFSET]; \
if ((address & (GBA_SIZE_ROM0 - 4)) < memory->romSize) { \ if ((address & (GBA_SIZE_ROM0 - 4)) < memory->romSize) { \
LOAD_32(value, address & (GBA_SIZE_ROM0 - 4), memory->rom); \ LOAD_32(value, address & (GBA_SIZE_ROM0 - 4), memory->rom); \
} else if (memory->vfame.cartType) { \ } else if (memory->unl.type == GBA_UNL_CART_VFAME) { \
value = GBAVFameGetPatternValue(address, 32); \ value = GBAVFameGetPatternValue(address, 32); \
} else { \ } else { \
mLOG(GBA_MEM, GAME_ERROR, "Out of bounds ROM Load32: 0x%08X", address); \ mLOG(GBA_MEM, GAME_ERROR, "Out of bounds ROM Load32: 0x%08X", address); \
@ -576,7 +587,7 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
wait = memory->waitstatesNonseq16[address >> BASE_OFFSET]; wait = memory->waitstatesNonseq16[address >> BASE_OFFSET];
if ((address & (GBA_SIZE_ROM0 - 2)) < memory->romSize) { if ((address & (GBA_SIZE_ROM0 - 2)) < memory->romSize) {
LOAD_16(value, address & (GBA_SIZE_ROM0 - 2), memory->rom); LOAD_16(value, address & (GBA_SIZE_ROM0 - 2), memory->rom);
} else if (memory->vfame.cartType) { } else if (memory->unl.type == GBA_UNL_CART_VFAME) {
value = GBAVFameGetPatternValue(address, 16); value = GBAVFameGetPatternValue(address, 16);
} else if ((address & (GBA_SIZE_ROM0 - 2)) >= AGB_PRINT_BASE) { } else if ((address & (GBA_SIZE_ROM0 - 2)) >= AGB_PRINT_BASE) {
uint32_t agbPrintAddr = address & 0x00FFFFFF; uint32_t agbPrintAddr = address & 0x00FFFFFF;
@ -601,7 +612,7 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
value = GBACartEReaderRead(&memory->ereader, address); value = GBACartEReaderRead(&memory->ereader, address);
} else if ((address & (GBA_SIZE_ROM0 - 2)) < memory->romSize) { } else if ((address & (GBA_SIZE_ROM0 - 2)) < memory->romSize) {
LOAD_16(value, address & (GBA_SIZE_ROM0 - 2), memory->rom); LOAD_16(value, address & (GBA_SIZE_ROM0 - 2), memory->rom);
} else if (memory->vfame.cartType) { } else if (memory->unl.type == GBA_UNL_CART_VFAME) {
value = GBAVFameGetPatternValue(address, 16); value = GBAVFameGetPatternValue(address, 16);
} else { } else {
mLOG(GBA_MEM, GAME_ERROR, "Out of bounds ROM Load16: 0x%08X", address); mLOG(GBA_MEM, GAME_ERROR, "Out of bounds ROM Load16: 0x%08X", address);
@ -692,7 +703,7 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
wait = memory->waitstatesNonseq16[address >> BASE_OFFSET]; wait = memory->waitstatesNonseq16[address >> BASE_OFFSET];
if ((address & (GBA_SIZE_ROM0 - 1)) < memory->romSize) { if ((address & (GBA_SIZE_ROM0 - 1)) < memory->romSize) {
value = ((uint8_t*) memory->rom)[address & (GBA_SIZE_ROM0 - 1)]; value = ((uint8_t*) memory->rom)[address & (GBA_SIZE_ROM0 - 1)];
} else if (memory->vfame.cartType) { } else if (memory->unl.type == GBA_UNL_CART_VFAME) {
value = GBAVFameGetPatternValue(address, 8); value = GBAVFameGetPatternValue(address, 8);
} else { } else {
mLOG(GBA_MEM, GAME_ERROR, "Out of bounds ROM Load8: 0x%08X", address); mLOG(GBA_MEM, GAME_ERROR, "Out of bounds ROM Load8: 0x%08X", address);
@ -802,14 +813,7 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
mLOG(GBA_MEM, STUB, "Unimplemented memory Store32: 0x%08X", address); mLOG(GBA_MEM, STUB, "Unimplemented memory Store32: 0x%08X", address);
#define STORE_SRAM \ #define STORE_SRAM \
if (address & 0x3) { \ GBAStore8(cpu, address, value >> (8 * (address & 3)), cycleCounter);
mLOG(GBA_MEM, GAME_ERROR, "Unaligned SRAM Store32: 0x%08X", address); \
} else { \
GBAStore8(cpu, address, value, cycleCounter); \
GBAStore8(cpu, address | 1, value, cycleCounter); \
GBAStore8(cpu, address | 2, value, cycleCounter); \
GBAStore8(cpu, address | 3, value, cycleCounter); \
}
#define STORE_BAD \ #define STORE_BAD \
mLOG(GBA_MEM, GAME_ERROR, "Bad memory Store32: 0x%08X", address); mLOG(GBA_MEM, GAME_ERROR, "Bad memory Store32: 0x%08X", address);
@ -921,7 +925,7 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
break; break;
case GBA_REGION_ROM0: case GBA_REGION_ROM0:
if (IS_GPIO_REGISTER(address & 0xFFFFFE)) { if (IS_GPIO_REGISTER(address & 0xFFFFFE)) {
if (memory->hw.devices == HW_NONE) { if (!(memory->hw.devices & HW_GPIO)) {
mLOG(GBA_HW, WARN, "Write to GPIO address %08X on cartridge without GPIO", address); mLOG(GBA_HW, WARN, "Write to GPIO address %08X on cartridge without GPIO", address);
break; break;
} }
@ -969,6 +973,10 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
break; break;
} }
} }
if (memory->unl.type) {
GBAUnlCartWriteROM(gba, address & (GBA_SIZE_ROM0 - 1), value);
break;
}
mLOG(GBA_MEM, GAME_ERROR, "Bad cartridge Store16: 0x%08X", address); mLOG(GBA_MEM, GAME_ERROR, "Bad cartridge Store16: 0x%08X", address);
break; break;
case GBA_REGION_ROM2_EX: case GBA_REGION_ROM2_EX:
@ -989,10 +997,9 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
case GBA_REGION_SRAM_MIRROR: case GBA_REGION_SRAM_MIRROR:
if (address & 1) { if (address & 1) {
mLOG(GBA_MEM, GAME_ERROR, "Unaligned SRAM Store16: 0x%08X", address); mLOG(GBA_MEM, GAME_ERROR, "Unaligned SRAM Store16: 0x%08X", address);
break; value >>= 8;
} }
GBAStore8(cpu, address, value, cycleCounter); GBAStore8(cpu, address, value, cycleCounter);
GBAStore8(cpu, address | 1, value, cycleCounter);
break; break;
default: default:
mLOG(GBA_MEM, GAME_ERROR, "Bad memory Store16: 0x%08X", address); mLOG(GBA_MEM, GAME_ERROR, "Bad memory Store16: 0x%08X", address);
@ -1064,8 +1071,8 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo
} else if (memory->savedata.type == GBA_SAVEDATA_FLASH512 || memory->savedata.type == GBA_SAVEDATA_FLASH1M) { } else if (memory->savedata.type == GBA_SAVEDATA_FLASH512 || memory->savedata.type == GBA_SAVEDATA_FLASH1M) {
GBASavedataWriteFlash(&memory->savedata, address, value); GBASavedataWriteFlash(&memory->savedata, address, value);
} else if (memory->savedata.type == GBA_SAVEDATA_SRAM) { } else if (memory->savedata.type == GBA_SAVEDATA_SRAM) {
if (memory->vfame.cartType) { if (memory->unl.type) {
GBAVFameSramWrite(&memory->vfame, address, value, memory->savedata.data); GBAUnlCartWriteSRAM(gba, address & 0xFFFF, value);
} else { } else {
memory->savedata.data[address & (GBA_SIZE_SRAM - 1)] = value; memory->savedata.data[address & (GBA_SIZE_SRAM - 1)] = value;
} }
@ -1734,6 +1741,10 @@ void GBAAdjustWaitstates(struct GBA* gba, uint16_t parameters) {
STORE_32(memory->agbPrintFuncBackup, AGB_PRINT_FLUSH_ADDR | base, memory->rom); STORE_32(memory->agbPrintFuncBackup, AGB_PRINT_FLUSH_ADDR | base, memory->rom);
} }
} }
if (gba->performingDMA) {
GBADMARecalculateCycles(gba);
}
} }
void GBAAdjustEWRAMWaitstates(struct GBA* gba, uint16_t parameters) { void GBAAdjustEWRAMWaitstates(struct GBA* gba, uint16_t parameters) {
@ -1894,8 +1905,6 @@ void _pristineCow(struct GBA* gba) {
} }
if (gba->romVf) { if (gba->romVf) {
gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->memory.romSize); gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->memory.romSize);
gba->romVf->close(gba->romVf);
gba->romVf = NULL;
} }
gba->memory.rom = newRom; gba->memory.rom = newRom;
gba->memory.hw.gpioBase = &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1]; gba->memory.hw.gpioBase = &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1];

View File

@ -377,6 +377,12 @@ void GBAOverrideApplyDefaults(struct GBA* gba, const struct Configuration* overr
struct GBACartridgeOverride override = { .idleLoop = GBA_IDLE_LOOP_NONE }; struct GBACartridgeOverride override = { .idleLoop = GBA_IDLE_LOOP_NONE };
const struct GBACartridge* cart = (const struct GBACartridge*) gba->memory.rom; const struct GBACartridge* cart = (const struct GBACartridge*) gba->memory.rom;
if (cart) { if (cart) {
if (gba->memory.unl.type == GBA_UNL_CART_MULTICART) {
override.savetype = GBA_SAVEDATA_SRAM;
GBAOverrideApply(gba, &override);
return;
}
memcpy(override.id, &cart->id, sizeof(override.id)); memcpy(override.id, &cart->id, sizeof(override.id));
static const uint32_t pokemonTable[] = { static const uint32_t pokemonTable[] = {

View File

@ -539,7 +539,7 @@ static const char* const _renderWindow =
"}\n" "}\n"
"bool test(vec3 circle, vec4 top, vec4 bottom) {\n" "bool test(vec3 circle, vec4 top, vec4 bottom) {\n"
" if (circle.z > 0) {\n" " if (circle.z > 0.) {\n"
" return distance(circle.xy, texCoord.xy) <= circle.z;\n" " return distance(circle.xy, texCoord.xy) <= circle.z;\n"
" }\n" " }\n"
" return crop(interpolate(top, bottom));\n" " return crop(interpolate(top, bottom));\n"

View File

@ -45,7 +45,7 @@ static void _ashesToAshes(struct mTiming* timing, void* user, uint32_t cyclesLat
void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) { void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) {
savedata->type = GBA_SAVEDATA_AUTODETECT; savedata->type = GBA_SAVEDATA_AUTODETECT;
savedata->data = 0; savedata->data = NULL;
savedata->command = EEPROM_COMMAND_NULL; savedata->command = EEPROM_COMMAND_NULL;
savedata->flashState = FLASH_STATE_RAW; savedata->flashState = FLASH_STATE_RAW;
savedata->vf = vf; savedata->vf = vf;
@ -63,6 +63,11 @@ void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) {
savedata->dust.callback = _ashesToAshes; savedata->dust.callback = _ashesToAshes;
} }
void GBASavedataReset(struct GBASavedata* savedata) {
savedata->command = EEPROM_COMMAND_NULL;
savedata->flashState = FLASH_STATE_RAW;
}
void GBASavedataDeinit(struct GBASavedata* savedata) { void GBASavedataDeinit(struct GBASavedata* savedata) {
if (savedata->vf) { if (savedata->vf) {
size_t size = GBASavedataSize(savedata); size_t size = GBASavedataSize(savedata);
@ -376,7 +381,10 @@ uint8_t GBASavedataReadFlash(struct GBASavedata* savedata, uint16_t address) {
} }
} }
if (mTimingIsScheduled(savedata->timing, &savedata->dust) && (address >> 12) == savedata->settling) { if (mTimingIsScheduled(savedata->timing, &savedata->dust) && (address >> 12) == savedata->settling) {
return 0x5F; // This should read Q7 XOR data bit 7 (data# polling), Q6 flipping
// every read (toggle bit), and /Q5 (error bit cleared), but implementing
// just data# polling is sufficient for games to figure it out
return (savedata->currentBank[address] ^ 0x80) & 0x80;
} }
return savedata->currentBank[address]; return savedata->currentBank[address];
} }

View File

@ -32,8 +32,17 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) {
STORE_64LE(gba->timing.globalCycles, 0, &state->globalCycles); STORE_64LE(gba->timing.globalCycles, 0, &state->globalCycles);
if (gba->memory.rom) { if (gba->memory.rom) {
state->id = ((struct GBACartridge*) gba->memory.rom)->id; switch (gba->memory.unl.type) {
memcpy(state->title, ((struct GBACartridge*) gba->memory.rom)->title, sizeof(state->title)); case GBA_UNL_CART_NONE:
case GBA_UNL_CART_VFAME:
state->id = ((struct GBACartridge*) gba->memory.rom)->id;
memcpy(state->title, ((struct GBACartridge*) gba->memory.rom)->title, sizeof(state->title));
break;
case GBA_UNL_CART_MULTICART:
state->id = ((struct GBACartridge*) gba->memory.unl.multi.rom)->id;
memcpy(state->title, ((struct GBACartridge*) gba->memory.unl.multi.rom)->title, sizeof(state->title));
break;
}
} else { } else {
state->id = 0; state->id = 0;
memset(state->title, 0, sizeof(state->title)); memset(state->title, 0, sizeof(state->title));
@ -74,6 +83,7 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) {
GBAMemorySerialize(&gba->memory, state); GBAMemorySerialize(&gba->memory, state);
GBAIOSerialize(gba, state); GBAIOSerialize(gba, state);
GBAUnlCartSerialize(gba, state);
GBAVideoSerialize(&gba->video, state); GBAVideoSerialize(&gba->video, state);
GBAAudioSerialize(&gba->audio, state); GBAAudioSerialize(&gba->audio, state);
GBASavedataSerialize(&gba->memory.savedata, state); GBASavedataSerialize(&gba->memory.savedata, state);
@ -106,9 +116,21 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
error = true; error = true;
} }
} }
if (gba->memory.rom && (state->id != ((struct GBACartridge*) gba->memory.rom)->id || memcmp(state->title, ((struct GBACartridge*) gba->memory.rom)->title, sizeof(state->title)))) { if (gba->memory.rom) {
mLOG(GBA_STATE, WARN, "Savestate is for a different game"); struct GBACartridge* cart;
error = true; switch (gba->memory.unl.type) {
case GBA_UNL_CART_NONE:
case GBA_UNL_CART_VFAME:
cart = (struct GBACartridge*) gba->memory.rom;
break;
case GBA_UNL_CART_MULTICART:
cart = (struct GBACartridge*) gba->memory.unl.multi.rom;
break;
}
if (state->id != cart->id || memcmp(state->title, cart->title, sizeof(state->title))) {
mLOG(GBA_STATE, WARN, "Savestate is for a different game");
error = true;
}
} else if (!gba->memory.rom && state->id != 0) { } else if (!gba->memory.rom && state->id != 0) {
mLOG(GBA_STATE, WARN, "Savestate is for a game, but no game loaded"); mLOG(GBA_STATE, WARN, "Savestate is for a game, but no game loaded");
error = true; error = true;
@ -159,6 +181,9 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
mLOG(GBA_STATE, WARN, "Savestate has unaligned PC and is probably corrupted"); mLOG(GBA_STATE, WARN, "Savestate has unaligned PC and is probably corrupted");
gba->cpu->gprs[ARM_PC] &= ~1; gba->cpu->gprs[ARM_PC] &= ~1;
} }
// Since this can remap the ROM, we need to do this before we reset the pipeline
GBAUnlCartDeserialize(gba, state);
gba->memory.activeRegion = -1; gba->memory.activeRegion = -1;
gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]); gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
if (state->biosPrefetch) { if (state->biosPrefetch) {
@ -195,7 +220,7 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
if (GBASerializedMiscFlagsIsIrqPending(miscFlags)) { if (GBASerializedMiscFlagsIsIrqPending(miscFlags)) {
int32_t when; int32_t when;
LOAD_32(when, 0, &state->nextIrq); LOAD_32(when, 0, &state->nextIrq);
mTimingSchedule(&gba->timing, &gba->irqEvent, when); mTimingSchedule(&gba->timing, &gba->irqEvent, when);
} }
gba->cpuBlocked = GBASerializedMiscFlagsGetBlocked(miscFlags); gba->cpuBlocked = GBASerializedMiscFlagsGetBlocked(miscFlags);
gba->keysLast = GBASerializedMiscFlagsGetKeyIRQKeys(miscFlags); gba->keysLast = GBASerializedMiscFlagsGetKeyIRQKeys(miscFlags);

View File

@ -11,26 +11,14 @@
mLOG_DEFINE_CATEGORY(GBA_SIO, "GBA Serial I/O", "gba.sio"); mLOG_DEFINE_CATEGORY(GBA_SIO, "GBA Serial I/O", "gba.sio");
const int GBASIOCyclesPerTransfer[4][MAX_GBAS] = { static const int GBASIOCyclesPerTransfer[4][MAX_GBAS] = {
{ 31976, 63427, 94884, 125829 }, { 31976, 63427, 94884, 125829 },
{ 8378, 16241, 24104, 31457 }, { 8378, 16241, 24104, 31457 },
{ 5750, 10998, 16241, 20972 }, { 5750, 10998, 16241, 20972 },
{ 3140, 5755, 8376, 10486 } { 3140, 5755, 8376, 10486 }
}; };
static struct GBASIODriver* _lookupDriver(struct GBASIO* sio, enum GBASIOMode mode) { static void _sioFinish(struct mTiming* timing, void* user, uint32_t cyclesLate);
switch (mode) {
case GBA_SIO_NORMAL_8:
case GBA_SIO_NORMAL_32:
return sio->drivers.normal;
case GBA_SIO_MULTI:
return sio->drivers.multiplayer;
case GBA_SIO_JOYBUS:
return sio->drivers.joybus;
default:
return 0;
}
}
static const char* _modeName(enum GBASIOMode mode) { static const char* _modeName(enum GBASIOMode mode) {
switch (mode) { switch (mode) {
@ -58,25 +46,36 @@ static void _switchMode(struct GBASIO* sio) {
newMode = (enum GBASIOMode) (mode & 0xC); newMode = (enum GBASIOMode) (mode & 0xC);
} }
if (newMode != sio->mode) { if (newMode != sio->mode) {
if (sio->activeDriver && sio->activeDriver->unload) {
sio->activeDriver->unload(sio->activeDriver);
}
if (sio->mode != (enum GBASIOMode) -1) { if (sio->mode != (enum GBASIOMode) -1) {
mLOG(GBA_SIO, DEBUG, "Switching mode from %s to %s", _modeName(sio->mode), _modeName(newMode)); mLOG(GBA_SIO, DEBUG, "Switching mode from %s to %s", _modeName(sio->mode), _modeName(newMode));
} }
sio->mode = newMode; sio->mode = newMode;
sio->activeDriver = _lookupDriver(sio, sio->mode); if (sio->driver && sio->driver->setMode) {
if (sio->activeDriver && sio->activeDriver->load) { sio->driver->setMode(sio->driver, newMode);
sio->activeDriver->load(sio->activeDriver); }
int id = 0;
switch (newMode) {
case GBA_SIO_MULTI:
if (sio->driver && sio->driver->deviceId) {
id = sio->driver->deviceId(sio->driver);
}
sio->rcnt = GBASIORegisterRCNTSetSi(sio->rcnt, !!id);
break;
default:
// TODO
break;
} }
} }
} }
void GBASIOInit(struct GBASIO* sio) { void GBASIOInit(struct GBASIO* sio) {
sio->drivers.normal = 0; sio->driver = NULL;
sio->drivers.multiplayer = 0;
sio->drivers.joybus = 0; sio->completeEvent.context = sio;
sio->activeDriver = 0; sio->completeEvent.name = "GBA SIO Complete";
sio->completeEvent.callback = _sioFinish;
sio->completeEvent.priority = 0x80;
sio->gbp.p = sio->p; sio->gbp.p = sio->p;
GBASIOPlayerInit(&sio->gbp); GBASIOPlayerInit(&sio->gbp);
@ -85,64 +84,28 @@ void GBASIOInit(struct GBASIO* sio) {
} }
void GBASIODeinit(struct GBASIO* sio) { void GBASIODeinit(struct GBASIO* sio) {
if (sio->activeDriver && sio->activeDriver->unload) { if (sio->driver && sio->driver->deinit) {
sio->activeDriver->unload(sio->activeDriver); sio->driver->deinit(sio->driver);
}
if (sio->drivers.multiplayer && sio->drivers.multiplayer->deinit) {
sio->drivers.multiplayer->deinit(sio->drivers.multiplayer);
}
if (sio->drivers.joybus && sio->drivers.joybus->deinit) {
sio->drivers.joybus->deinit(sio->drivers.joybus);
}
if (sio->drivers.normal && sio->drivers.normal->deinit) {
sio->drivers.normal->deinit(sio->drivers.normal);
} }
} }
void GBASIOReset(struct GBASIO* sio) { void GBASIOReset(struct GBASIO* sio) {
if (sio->activeDriver && sio->activeDriver->unload) { if (sio->driver && sio->driver->reset) {
sio->activeDriver->unload(sio->activeDriver); sio->driver->reset(sio->driver);
} }
sio->rcnt = RCNT_INITIAL; sio->rcnt = RCNT_INITIAL;
sio->siocnt = 0; sio->siocnt = 0;
sio->mode = -1; sio->mode = -1;
sio->activeDriver = NULL;
_switchMode(sio); _switchMode(sio);
GBASIOPlayerReset(&sio->gbp); GBASIOPlayerReset(&sio->gbp);
} }
void GBASIOSetDriverSet(struct GBASIO* sio, struct GBASIODriverSet* drivers) { void GBASIOSetDriver(struct GBASIO* sio, struct GBASIODriver* driver) {
GBASIOSetDriver(sio, drivers->normal, GBA_SIO_NORMAL_8); if (sio->driver && sio->driver->deinit) {
GBASIOSetDriver(sio, drivers->multiplayer, GBA_SIO_MULTI); sio->driver->deinit(sio->driver);
GBASIOSetDriver(sio, drivers->joybus, GBA_SIO_JOYBUS);
}
void GBASIOSetDriver(struct GBASIO* sio, struct GBASIODriver* driver, enum GBASIOMode mode) {
struct GBASIODriver** driverLoc;
switch (mode) {
case GBA_SIO_NORMAL_8:
case GBA_SIO_NORMAL_32:
driverLoc = &sio->drivers.normal;
break;
case GBA_SIO_MULTI:
driverLoc = &sio->drivers.multiplayer;
break;
case GBA_SIO_JOYBUS:
driverLoc = &sio->drivers.joybus;
break;
default:
mLOG(GBA_SIO, ERROR, "Setting an unsupported SIO driver: %x", mode);
return;
}
if (*driverLoc) {
if ((*driverLoc)->unload) {
(*driverLoc)->unload(*driverLoc);
}
if ((*driverLoc)->deinit) {
(*driverLoc)->deinit(*driverLoc);
}
} }
sio->driver = driver;
if (driver) { if (driver) {
driver->p = sio; driver->p = sio;
@ -154,48 +117,118 @@ void GBASIOSetDriver(struct GBASIO* sio, struct GBASIODriver* driver, enum GBASI
} }
} }
} }
if (sio->activeDriver == *driverLoc) {
sio->activeDriver = driver;
if (driver && driver->load) {
driver->load(driver);
}
}
*driverLoc = driver;
} }
void GBASIOWriteRCNT(struct GBASIO* sio, uint16_t value) { void GBASIOWriteRCNT(struct GBASIO* sio, uint16_t value) {
sio->rcnt &= 0xF; sio->rcnt &= 0x1FF;
sio->rcnt |= value & ~0xF; sio->rcnt |= value & 0xC000;
_switchMode(sio); _switchMode(sio);
if (sio->activeDriver && sio->activeDriver->writeRegister) { if (sio->driver && sio->driver->writeRCNT) {
sio->activeDriver->writeRegister(sio->activeDriver, GBA_REG_RCNT, value); switch (sio->mode) {
case GBA_SIO_GPIO:
sio->rcnt = (sio->driver->writeRCNT(sio->driver, value) & 0x01FF) | (sio->rcnt & 0xC000);
break;
default:
sio->rcnt = (sio->driver->writeRCNT(sio->driver, value) & 0x01F0) | (sio->rcnt & 0xC00F);
}
} else if (sio->mode == GBA_SIO_GPIO) {
sio->rcnt &= 0xC000;
sio->rcnt |= value & 0x1FF;
} else {
sio->rcnt &= 0xC00F;
sio->rcnt |= value & 0x1F0;
} }
} }
static void _startTransfer(struct GBASIO* sio) {
if (sio->driver && sio->driver->start) {
if (!sio->driver->start(sio->driver)) {
// Transfer completion is handled internally to the driver
return;
}
}
int connected = 0;
if (sio->driver && sio->driver->connectedDevices) {
connected = sio->driver->connectedDevices(sio->driver);
}
mTimingDeschedule(&sio->p->timing, &sio->completeEvent);
mTimingSchedule(&sio->p->timing, &sio->completeEvent, GBASIOTransferCycles(sio->mode, sio->siocnt, connected));
}
void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) { void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) {
if ((value ^ sio->siocnt) & 0x3000) { if ((value ^ sio->siocnt) & 0x3000) {
sio->siocnt = value & 0x3000; sio->siocnt = value & 0x3000;
_switchMode(sio); _switchMode(sio);
} }
if (sio->activeDriver && sio->activeDriver->writeRegister) { int id = 0;
value = sio->activeDriver->writeRegister(sio->activeDriver, GBA_REG_SIOCNT, value); int connected = 0;
bool handled = false;
if (sio->driver) {
handled = sio->driver->handlesMode(sio->driver, sio->mode);
if (handled) {
if (sio->driver->deviceId) {
id = sio->driver->deviceId(sio->driver);
}
connected = sio->driver->connectedDevices(sio->driver);
handled = !!sio->driver->writeSIOCNT;
}
}
switch (sio->mode) {
case GBA_SIO_MULTI:
value &= 0xFF83;
value = GBASIOMultiplayerSetSlave(value, id || !connected);
value = GBASIOMultiplayerSetId(value, id);
value |= sio->siocnt & 0x00FC;
// SC appears to float in multi mode when not doing a transfer. While
// it does spike at the end of a transfer, it appears to die down after
// around 20-30 microseconds. However, the docs on akkit.org
// (http://www.akkit.org/info/gba_comms.html) say this is high until
// a transfer starts and low while active. Further, the Mario Bros.
// multiplayer expects SC to be high in multi mode. This needs better
// investigation than I managed, apparently.
sio->rcnt = GBASIORegisterRCNTFillSc(sio->rcnt);
if (GBASIOMultiplayerIsBusy(value) && !GBASIOMultiplayerIsBusy(sio->siocnt)) {
if (!id) {
sio->p->memory.io[GBA_REG(SIOMULTI0)] = 0xFFFF;
sio->p->memory.io[GBA_REG(SIOMULTI1)] = 0xFFFF;
sio->p->memory.io[GBA_REG(SIOMULTI2)] = 0xFFFF;
sio->p->memory.io[GBA_REG(SIOMULTI3)] = 0xFFFF;
sio->rcnt = GBASIORegisterRCNTClearSc(sio->rcnt);
_startTransfer(sio);
} else {
// TODO
}
}
break;
case GBA_SIO_NORMAL_8:
case GBA_SIO_NORMAL_32:
// This line is pulled up by the clock owner while the clock is idle.
// If there is no clock owner it's just hi-Z.
if (GBASIONormalGetSc(value)) {
sio->rcnt = GBASIORegisterRCNTFillSc(sio->rcnt);
}
if (GBASIONormalIsStart(value) && !GBASIONormalIsStart(sio->siocnt)) {
_startTransfer(sio);
}
break;
default:
// TODO
break;
}
if (handled) {
value = sio->driver->writeSIOCNT(sio->driver, value);
} else { } else {
// Dummy drivers // Dummy drivers
switch (sio->mode) { switch (sio->mode) {
case GBA_SIO_NORMAL_8: case GBA_SIO_NORMAL_8:
case GBA_SIO_NORMAL_32: case GBA_SIO_NORMAL_32:
value = GBASIONormalFillSi(value); value = GBASIONormalFillSi(value);
if ((value & 0x0081) == 0x0081) {
if (GBASIONormalIsIrq(value)) {
// TODO: Test this on hardware to see if this is correct
GBARaiseIRQ(sio->p, GBA_IRQ_SIO, 0);
}
value = GBASIONormalClearStart(value);
}
break; break;
case GBA_SIO_MULTI: case GBA_SIO_MULTI:
value &= 0xFF83; value = GBASIOMultiplayerFillReady(value);
value |= 0xC;
break; break;
default: default:
// TODO // TODO
@ -206,22 +239,252 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) {
} }
uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t value) { uint16_t GBASIOWriteRegister(struct GBASIO* sio, uint32_t address, uint16_t value) {
if (sio->activeDriver && sio->activeDriver->writeRegister) { int id = 0;
return sio->activeDriver->writeRegister(sio->activeDriver, address, value); if (sio->driver && sio->driver->deviceId) {
id = sio->driver->deviceId(sio->driver);
} }
// Dummy drivers
bool handled = true;
switch (sio->mode) { switch (sio->mode) {
case GBA_SIO_JOYBUS: case GBA_SIO_JOYBUS:
switch (address) { switch (address) {
case GBA_REG_SIODATA8:
mLOG(GBA_SIO, DEBUG, "JOY write: SIODATA8 (?) <- %04X", value);
break;
case GBA_REG_JOYCNT: case GBA_REG_JOYCNT:
return (value & 0x0040) | (sio->p->memory.io[GBA_REG(JOYCNT)] & ~(value & 0x7) & ~0x0040); mLOG(GBA_SIO, DEBUG, "JOY write: CNT <- %04X", value);
value = (value & 0x0040) | (sio->p->memory.io[GBA_REG(JOYCNT)] & ~(value & 0x7) & ~0x0040);
break;
case GBA_REG_JOYSTAT: case GBA_REG_JOYSTAT:
return (value & 0x0030) | (sio->p->memory.io[GBA_REG(JOYSTAT)] & ~0x30); mLOG(GBA_SIO, DEBUG, "JOY write: STAT <- %04X", value);
value = (value & 0x0030) | (sio->p->memory.io[GBA_REG(JOYSTAT)] & ~0x30);
break;
case GBA_REG_JOY_TRANS_LO:
mLOG(GBA_SIO, DEBUG, "JOY write: TRANS_LO <- %04X", value);
break;
case GBA_REG_JOY_TRANS_HI:
mLOG(GBA_SIO, DEBUG, "JOY write: TRANS_HI <- %04X", value);
break;
default:
mLOG(GBA_SIO, GAME_ERROR, "JOY write: Unhandled %s <- %04X", GBAIORegisterNames[address >> 1], value);
handled = false;
break;
} }
break; break;
default: case GBA_SIO_NORMAL_8:
// TODO switch (address) {
case GBA_REG_SIODATA8:
mLOG(GBA_SIO, DEBUG, "NORMAL8 %i write: SIODATA8 <- %04X", id, value);
break;
case GBA_REG_JOYCNT:
mLOG(GBA_SIO, DEBUG, "NORMAL8 %i write: JOYCNT (?) <- %04X", id, value);
value = (value & 0x0040) | (sio->p->memory.io[GBA_REG(JOYCNT)] & ~(value & 0x7) & ~0x0040);
break;
default:
mLOG(GBA_SIO, GAME_ERROR, "NORMAL8 %i write: Unhandled %s <- %04X", id, GBAIORegisterNames[address >> 1], value);
handled = false;
break;
}
break; break;
case GBA_SIO_NORMAL_32:
switch (address) {
case GBA_REG_SIODATA32_LO:
mLOG(GBA_SIO, DEBUG, "NORMAL32 %i write: SIODATA32_LO <- %04X", id, value);
break;
case GBA_REG_SIODATA32_HI:
mLOG(GBA_SIO, DEBUG, "NORMAL32 %i write: SIODATA32_HI <- %04X", id, value);
break;
case GBA_REG_SIODATA8:
mLOG(GBA_SIO, DEBUG, "NORMAL32 %i write: SIODATA8 (?) <- %04X", id, value);
break;
case GBA_REG_JOYCNT:
mLOG(GBA_SIO, DEBUG, "NORMAL32 %i write: JOYCNT (?) <- %04X", id, value);
value = (value & 0x0040) | (sio->p->memory.io[GBA_REG(JOYCNT)] & ~(value & 0x7) & ~0x0040);
break;
default:
mLOG(GBA_SIO, GAME_ERROR, "NORMAL32 %i write: Unhandled %s <- %04X", id, GBAIORegisterNames[address >> 1], value);
handled = false;
break;
}
break;
case GBA_SIO_MULTI:
switch (address) {
case GBA_REG_SIOMLT_SEND:
mLOG(GBA_SIO, DEBUG, "MULTI %i write: SIOMLT_SEND <- %04X", id, value);
break;
case GBA_REG_JOYCNT:
mLOG(GBA_SIO, DEBUG, "MULTI %i write: JOYCNT (?) <- %04X", id, value);
value = (value & 0x0040) | (sio->p->memory.io[GBA_REG(JOYCNT)] & ~(value & 0x7) & ~0x0040);
break;
default:
mLOG(GBA_SIO, GAME_ERROR, "MULTI %i write: Unhandled %s <- %04X", id, GBAIORegisterNames[address >> 1], value);
handled = false;
break;
}
break;
case GBA_SIO_UART:
switch (address) {
case GBA_REG_SIODATA8:
mLOG(GBA_SIO, DEBUG, "UART write: SIODATA8 <- %04X", value);
break;
case GBA_REG_JOYCNT:
mLOG(GBA_SIO, DEBUG, "UART write: JOYCNT (?) <- %04X", value);
value = (value & 0x0040) | (sio->p->memory.io[GBA_REG(JOYCNT)] & ~(value & 0x7) & ~0x0040);
break;
default:
mLOG(GBA_SIO, GAME_ERROR, "UART write: Unhandled %s <- %04X", GBAIORegisterNames[address >> 1], value);
handled = false;
break;
}
break;
case GBA_SIO_GPIO:
mLOG(GBA_SIO, STUB, "GPIO write: Unhandled %s <- %04X", GBAIORegisterNames[address >> 1], value);
handled = false;
break;
}
if (!handled) {
value = sio->p->memory.io[address >> 1];
} }
return value; return value;
} }
int32_t GBASIOTransferCycles(enum GBASIOMode mode, uint16_t siocnt, int connected) {
if (connected < 0 || connected >= MAX_GBAS) {
mLOG(GBA_SIO, ERROR, "Invalid device count %i", connected);
return 0;
}
switch (mode) {
case GBA_SIO_MULTI:
return GBASIOCyclesPerTransfer[GBASIOMultiplayerGetBaud(siocnt)][connected];
case GBA_SIO_NORMAL_8:
return 8 * GBA_ARM7TDMI_FREQUENCY / ((GBASIONormalIsInternalSc(siocnt) ? 2048 : 256) * 1024);
case GBA_SIO_NORMAL_32:
return 32 * GBA_ARM7TDMI_FREQUENCY / ((GBASIONormalIsInternalSc(siocnt) ? 2048 : 256) * 1024);
default:
mLOG(GBA_SIO, STUB, "No cycle count implemented for mode %s", _modeName(mode));
break;
}
return 0;
}
void GBASIOMultiplayerFinishTransfer(struct GBASIO* sio, uint16_t data[4], uint32_t cyclesLate) {
int id = 0;
if (sio->driver && sio->driver->deviceId) {
id = sio->driver->deviceId(sio->driver);
}
sio->p->memory.io[GBA_REG(SIOMULTI0)] = data[0];
sio->p->memory.io[GBA_REG(SIOMULTI1)] = data[1];
sio->p->memory.io[GBA_REG(SIOMULTI2)] = data[2];
sio->p->memory.io[GBA_REG(SIOMULTI3)] = data[3];
sio->siocnt = GBASIOMultiplayerClearBusy(sio->siocnt);
sio->siocnt = GBASIOMultiplayerSetId(sio->siocnt, id);
sio->rcnt = GBASIORegisterRCNTFillSc(sio->rcnt);
if (GBASIOMultiplayerIsIrq(sio->siocnt)) {
GBARaiseIRQ(sio->p, GBA_IRQ_SIO, cyclesLate);
}
}
void GBASIONormal8FinishTransfer(struct GBASIO* sio, uint8_t data, uint32_t cyclesLate) {
sio->siocnt = GBASIONormalClearStart(sio->siocnt);
sio->p->memory.io[GBA_REG(SIODATA8)] = data;
if (GBASIONormalIsIrq(sio->siocnt)) {
GBARaiseIRQ(sio->p, GBA_IRQ_SIO, cyclesLate);
}
}
void GBASIONormal32FinishTransfer(struct GBASIO* sio, uint32_t data, uint32_t cyclesLate) {
sio->siocnt = GBASIONormalClearStart(sio->siocnt);
sio->p->memory.io[GBA_REG(SIODATA32_LO)] = data;
sio->p->memory.io[GBA_REG(SIODATA32_HI)] = data >> 16;
if (GBASIONormalIsIrq(sio->siocnt)) {
GBARaiseIRQ(sio->p, GBA_IRQ_SIO, cyclesLate);
}
}
static void _sioFinish(struct mTiming* timing, void* user, uint32_t cyclesLate) {
UNUSED(timing);
struct GBASIO* sio = user;
union {
uint16_t multi[4];
uint8_t normal8;
uint32_t normal32;
} data = {0};
switch (sio->mode) {
case GBA_SIO_MULTI:
if (sio->driver && sio->driver->finishMultiplayer) {
sio->driver->finishMultiplayer(sio->driver, data.multi);
}
GBASIOMultiplayerFinishTransfer(sio, data.multi, cyclesLate);
break;
case GBA_SIO_NORMAL_8:
if (sio->driver && sio->driver->finishNormal8) {
data.normal8 = sio->driver->finishNormal8(sio->driver);
}
GBASIONormal8FinishTransfer(sio, data.normal8, cyclesLate);
break;
case GBA_SIO_NORMAL_32:
if (sio->driver && sio->driver->finishNormal32) {
data.normal32 = sio->driver->finishNormal32(sio->driver);
}
GBASIONormal32FinishTransfer(sio, data.normal32, cyclesLate);
break;
default:
// TODO
mLOG(GBA_SIO, STUB, "No dummy finish implemented for mode %s", _modeName(sio->mode));
break;
}
}
int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command, uint8_t* data) {
switch (command) {
case JOY_RESET:
sio->p->p->memory.io[GBA_REG(JOYCNT)] |= JOYCNT_RESET;
if (sio->p->p->memory.io[GBA_REG(JOYCNT)] & 0x40) {
GBARaiseIRQ(sio->p->p, GBA_IRQ_SIO, 0);
}
// Fall through
case JOY_POLL:
data[0] = 0x00;
data[1] = 0x04;
data[2] = sio->p->p->memory.io[GBA_REG(JOYSTAT)];
mLOG(GBA_SIO, DEBUG, "JOY %s: %02X (%02X)", command == JOY_POLL ? "poll" : "reset", data[2], sio->p->p->memory.io[GBA_REG(JOYCNT)]);
return 3;
case JOY_RECV:
sio->p->p->memory.io[GBA_REG(JOYCNT)] |= JOYCNT_RECV;
sio->p->p->memory.io[GBA_REG(JOYSTAT)] |= JOYSTAT_RECV;
sio->p->p->memory.io[GBA_REG(JOY_RECV_LO)] = data[0] | (data[1] << 8);
sio->p->p->memory.io[GBA_REG(JOY_RECV_HI)] = data[2] | (data[3] << 8);
data[0] = sio->p->p->memory.io[GBA_REG(JOYSTAT)];
mLOG(GBA_SIO, DEBUG, "JOY recv: %02X (%02X)", data[0], sio->p->p->memory.io[GBA_REG(JOYCNT)]);
if (sio->p->p->memory.io[GBA_REG(JOYCNT)] & 0x40) {
GBARaiseIRQ(sio->p->p, GBA_IRQ_SIO, 0);
}
return 1;
case JOY_TRANS:
data[0] = sio->p->p->memory.io[GBA_REG(JOY_TRANS_LO)];
data[1] = sio->p->p->memory.io[GBA_REG(JOY_TRANS_LO)] >> 8;
data[2] = sio->p->p->memory.io[GBA_REG(JOY_TRANS_HI)];
data[3] = sio->p->p->memory.io[GBA_REG(JOY_TRANS_HI)] >> 8;
data[4] = sio->p->p->memory.io[GBA_REG(JOYSTAT)];
sio->p->p->memory.io[GBA_REG(JOYCNT)] |= JOYCNT_TRANS;
sio->p->p->memory.io[GBA_REG(JOYSTAT)] &= ~JOYSTAT_TRANS;
mLOG(GBA_SIO, DEBUG, "JOY trans: %02X%02X%02X%02X:%02X (%02X)", data[0], data[1], data[2], data[3], data[4], sio->p->p->memory.io[GBA_REG(JOYCNT)]);
if (sio->p->p->memory.io[GBA_REG(JOYCNT)] & 0x40) {
GBARaiseIRQ(sio->p->p, GBA_IRQ_SIO, 0);
}
return 5;
}
return 0;
}

View File

@ -23,18 +23,22 @@ enum {
}; };
static bool GBASIODolphinInit(struct GBASIODriver* driver); static bool GBASIODolphinInit(struct GBASIODriver* driver);
static bool GBASIODolphinLoad(struct GBASIODriver* driver); static void GBASIODolphinReset(struct GBASIODriver* driver);
static bool GBASIODolphinUnload(struct GBASIODriver* driver); static void GBASIODolphinSetMode(struct GBASIODriver* driver, enum GBASIOMode mode);
static bool GBASIODolphinHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode);
static int GBASIODolphinConnectedDevices(struct GBASIODriver* driver);
static void GBASIODolphinProcessEvents(struct mTiming* timing, void* context, uint32_t cyclesLate); static void GBASIODolphinProcessEvents(struct mTiming* timing, void* context, uint32_t cyclesLate);
static int32_t _processCommand(struct GBASIODolphin* dol, uint32_t cyclesLate); static int32_t _processCommand(struct GBASIODolphin* dol, uint32_t cyclesLate);
static void _flush(struct GBASIODolphin* dol); static void _flush(struct GBASIODolphin* dol);
void GBASIODolphinCreate(struct GBASIODolphin* dol) { void GBASIODolphinCreate(struct GBASIODolphin* dol) {
GBASIOJOYCreate(&dol->d); memset(&dol->d, 0, sizeof(dol->d));
dol->d.init = GBASIODolphinInit; dol->d.init = GBASIODolphinInit;
dol->d.load = GBASIODolphinLoad; dol->d.reset = GBASIODolphinReset;
dol->d.unload = GBASIODolphinUnload; dol->d.setMode = GBASIODolphinSetMode;
dol->d.handlesMode = GBASIODolphinHandlesMode;
dol->d.connectedDevices = GBASIODolphinConnectedDevices;
dol->event.context = dol; dol->event.context = dol;
dol->event.name = "GB SIO Lockstep"; dol->event.name = "GB SIO Lockstep";
dol->event.callback = GBASIODolphinProcessEvents; dol->event.callback = GBASIODolphinProcessEvents;
@ -94,26 +98,33 @@ bool GBASIODolphinConnect(struct GBASIODolphin* dol, const struct Address* addre
static bool GBASIODolphinInit(struct GBASIODriver* driver) { static bool GBASIODolphinInit(struct GBASIODriver* driver) {
struct GBASIODolphin* dol = (struct GBASIODolphin*) driver; struct GBASIODolphin* dol = (struct GBASIODolphin*) driver;
dol->active = false;
dol->clockSlice = 0; dol->clockSlice = 0;
dol->state = WAIT_FOR_FIRST_CLOCK; dol->state = WAIT_FOR_FIRST_CLOCK;
_flush(dol); GBASIODolphinReset(driver);
return true; return true;
} }
static bool GBASIODolphinLoad(struct GBASIODriver* driver) { static void GBASIODolphinReset(struct GBASIODriver* driver) {
struct GBASIODolphin* dol = (struct GBASIODolphin*) driver; struct GBASIODolphin* dol = (struct GBASIODolphin*) driver;
dol->active = true; dol->active = false;
_flush(dol); _flush(dol);
mTimingDeschedule(&dol->d.p->p->timing, &dol->event); mTimingDeschedule(&dol->d.p->p->timing, &dol->event);
mTimingSchedule(&dol->d.p->p->timing, &dol->event, 0); mTimingSchedule(&dol->d.p->p->timing, &dol->event, 0);
return true;
} }
static bool GBASIODolphinUnload(struct GBASIODriver* driver) { static void GBASIODolphinSetMode(struct GBASIODriver* driver, enum GBASIOMode mode) {
struct GBASIODolphin* dol = (struct GBASIODolphin*) driver; struct GBASIODolphin* dol = (struct GBASIODolphin*) driver;
dol->active = false; dol->active = mode == GBA_SIO_JOYBUS;
return true; }
static bool GBASIODolphinHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode) {
UNUSED(driver);
return mode == GBA_SIO_JOYBUS;
}
static int GBASIODolphinConnectedDevices(struct GBASIODriver* driver) {
UNUSED(driver);
return 1;
} }
void GBASIODolphinProcessEvents(struct mTiming* timing, void* context, uint32_t cyclesLate) { void GBASIODolphinProcessEvents(struct mTiming* timing, void* context, uint32_t cyclesLate) {

View File

@ -13,8 +13,11 @@
#include <mgba-util/memory.h> #include <mgba-util/memory.h>
static uint16_t _gbpRead(struct mKeyCallback*); static uint16_t _gbpRead(struct mKeyCallback*);
static uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value); static uint16_t _gbpSioWriteSIOCNT(struct GBASIODriver* driver, uint16_t value);
static void _gbpSioProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLate); static bool _gbpSioHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode);
static int _gbpSioConnectedDevices(struct GBASIODriver* driver);
static bool _gbpSioStart(struct GBASIODriver* driver);
static uint32_t _gbpSioFinishNormal32(struct GBASIODriver* driver);
static const uint8_t _logoPalette[] = { static const uint8_t _logoPalette[] = {
0xDF, 0xFF, 0x0C, 0x64, 0x0C, 0xE4, 0x2D, 0xE4, 0x4E, 0x64, 0x4E, 0xE4, 0x6E, 0xE4, 0xAF, 0x68, 0xDF, 0xFF, 0x0C, 0x64, 0x0C, 0xE4, 0x2D, 0xE4, 0x4E, 0x64, 0x4E, 0xE4, 0x6E, 0xE4, 0xAF, 0x68,
@ -43,20 +46,17 @@ void GBASIOPlayerInit(struct GBASIOPlayer* gbp) {
gbp->callback.d.readKeys = _gbpRead; gbp->callback.d.readKeys = _gbpRead;
gbp->callback.d.requireOpposingDirections = true; gbp->callback.d.requireOpposingDirections = true;
gbp->callback.p = gbp; gbp->callback.p = gbp;
gbp->d.init = 0; memset(&gbp->d, 0, sizeof(gbp->d));
gbp->d.deinit = 0; gbp->d.writeSIOCNT = _gbpSioWriteSIOCNT;
gbp->d.load = 0; gbp->d.handlesMode = _gbpSioHandlesMode;
gbp->d.unload = 0; gbp->d.connectedDevices = _gbpSioConnectedDevices;
gbp->d.writeRegister = _gbpSioWriteRegister; gbp->d.start = _gbpSioStart;
gbp->event.context = gbp; gbp->d.finishNormal32 = _gbpSioFinishNormal32;
gbp->event.name = "GBA SIO Game Boy Player";
gbp->event.callback = _gbpSioProcessEvents;
gbp->event.priority = 0x80;
} }
void GBASIOPlayerReset(struct GBASIOPlayer* gbp) { void GBASIOPlayerReset(struct GBASIOPlayer* gbp) {
if (gbp->p->sio.drivers.normal == &gbp->d) { if (gbp->p->sio.driver == &gbp->d) {
GBASIOSetDriver(&gbp->p->sio, NULL, GBA_SIO_NORMAL_32); GBASIOSetDriver(&gbp->p->sio, NULL);
} }
} }
@ -87,8 +87,9 @@ void GBASIOPlayerUpdate(struct GBA* gba) {
gba->sio.gbp.inputsPosted = 0; gba->sio.gbp.inputsPosted = 0;
gba->sio.gbp.oldCallback = gba->keyCallback; gba->sio.gbp.oldCallback = gba->keyCallback;
gba->keyCallback = &gba->sio.gbp.callback.d; gba->keyCallback = &gba->sio.gbp.callback.d;
// TODO: Check if the SIO driver is actually used first if (!gba->sio.driver) {
GBASIOSetDriver(&gba->sio, &gba->sio.gbp.d, GBA_SIO_NORMAL_32); GBASIOSetDriver(&gba->sio, &gba->sio.gbp.d);
}
} }
} }
@ -100,35 +101,41 @@ uint16_t _gbpRead(struct mKeyCallback* callback) {
return 0; return 0;
} }
uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) { uint16_t _gbpSioWriteSIOCNT(struct GBASIODriver* driver, uint16_t value) {
struct GBASIOPlayer* gbp = (struct GBASIOPlayer*) driver; UNUSED(driver);
if (address == GBA_REG_SIOCNT) { return value & 0x78FB;
if (value & 0x0080) {
uint32_t rx = gbp->p->memory.io[GBA_REG(SIODATA32_LO)] | (gbp->p->memory.io[GBA_REG(SIODATA32_HI)] << 16);
if (gbp->txPosition < 12 && gbp->txPosition > 0) {
// TODO: Check expected
} else if (gbp->txPosition >= 12) {
// 0x00 = Stop
// 0x11 = Hard Stop
// 0x22 = Start
if (gbp->p->rumble) {
int32_t currentTime = mTimingCurrentTime(&gbp->p->timing);
gbp->p->rumble->setRumble(gbp->p->rumble, (rx & 0x33) == 0x22, currentTime - gbp->p->lastRumble);
gbp->p->lastRumble = currentTime;
}
}
mTimingDeschedule(&gbp->p->timing, &gbp->event);
mTimingSchedule(&gbp->p->timing, &gbp->event, 2048);
}
value &= 0x78FB;
}
return value;
} }
void _gbpSioProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLate) { bool _gbpSioStart(struct GBASIODriver* driver) {
UNUSED(timing); struct GBASIOPlayer* gbp = (struct GBASIOPlayer*) driver;
UNUSED(cyclesLate); uint32_t rx = gbp->p->memory.io[GBA_REG(SIODATA32_LO)] | (gbp->p->memory.io[GBA_REG(SIODATA32_HI)] << 16);
struct GBASIOPlayer* gbp = user; if (gbp->txPosition < 12 && gbp->txPosition > 0) {
// TODO: Check expected
} else if (gbp->txPosition >= 12) {
// 0x00 = Stop
// 0x11 = Hard Stop
// 0x22 = Start
if (gbp->p->rumble) {
int32_t currentTime = mTimingCurrentTime(&gbp->p->timing);
gbp->p->rumble->setRumble(gbp->p->rumble, (rx & 0x33) == 0x22, currentTime - gbp->p->lastRumble);
gbp->p->lastRumble = currentTime;
}
}
return true;
}
static bool _gbpSioHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode) {
UNUSED(driver);
return mode == GBA_SIO_NORMAL_32;
}
static int _gbpSioConnectedDevices(struct GBASIODriver* driver) {
UNUSED(driver);
return 1;
}
uint32_t _gbpSioFinishNormal32(struct GBASIODriver* driver) {
struct GBASIOPlayer* gbp = (struct GBASIOPlayer*) driver;
uint32_t tx = 0; uint32_t tx = 0;
int txPosition = gbp->txPosition; int txPosition = gbp->txPosition;
if (txPosition > 16) { if (txPosition > 16) {
@ -139,11 +146,5 @@ void _gbpSioProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLat
} }
tx = _gbpTxData[txPosition]; tx = _gbpTxData[txPosition];
++gbp->txPosition; ++gbp->txPosition;
gbp->p->memory.io[GBA_REG(SIODATA32_LO)] = tx; return tx;
gbp->p->memory.io[GBA_REG(SIODATA32_HI)] = tx >> 16;
if (GBASIONormalIsIrq(gbp->d.p->siocnt)) {
GBARaiseIRQ(gbp->p, GBA_IRQ_SIO, cyclesLate);
}
gbp->d.p->siocnt = GBASIONormalClearStart(gbp->d.p->siocnt);
gbp->p->memory.io[GBA_REG(SIOCNT)] = gbp->d.p->siocnt & ~0x0080;
} }

View File

@ -1,92 +0,0 @@
/* Copyright (c) 2013-2017 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/internal/gba/sio.h>
#include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/io.h>
static uint16_t GBASIOJOYWriteRegister(struct GBASIODriver* sio, uint32_t address, uint16_t value);
void GBASIOJOYCreate(struct GBASIODriver* sio) {
sio->init = NULL;
sio->deinit = NULL;
sio->load = NULL;
sio->unload = NULL;
sio->writeRegister = GBASIOJOYWriteRegister;
}
uint16_t GBASIOJOYWriteRegister(struct GBASIODriver* sio, uint32_t address, uint16_t value) {
switch (address) {
case GBA_REG_JOYCNT:
mLOG(GBA_SIO, DEBUG, "JOY write: CNT <- %04X", value);
return (value & 0x0040) | (sio->p->p->memory.io[GBA_REG(JOYCNT)] & ~(value & 0x7) & ~0x0040);
case GBA_REG_JOYSTAT:
mLOG(GBA_SIO, DEBUG, "JOY write: STAT <- %04X", value);
return (value & 0x0030) | (sio->p->p->memory.io[GBA_REG(JOYSTAT)] & ~0x30);
case GBA_REG_JOY_TRANS_LO:
mLOG(GBA_SIO, DEBUG, "JOY write: TRANS_LO <- %04X", value);
break;
case GBA_REG_JOY_TRANS_HI:
mLOG(GBA_SIO, DEBUG, "JOY write: TRANS_HI <- %04X", value);
break;
default:
mLOG(GBA_SIO, DEBUG, "JOY write: Unknown reg %03X <- %04X", address, value);
// Fall through
case GBA_REG_RCNT:
break;
}
return value;
}
int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command, uint8_t* data) {
switch (command) {
case JOY_RESET:
sio->p->p->memory.io[GBA_REG(JOYCNT)] |= JOYCNT_RESET;
if (sio->p->p->memory.io[GBA_REG(JOYCNT)] & 0x40) {
GBARaiseIRQ(sio->p->p, GBA_IRQ_SIO, 0);
}
// Fall through
case JOY_POLL:
data[0] = 0x00;
data[1] = 0x04;
data[2] = sio->p->p->memory.io[GBA_REG(JOYSTAT)];
mLOG(GBA_SIO, DEBUG, "JOY %s: %02X (%02X)", command == JOY_POLL ? "poll" : "reset", data[2], sio->p->p->memory.io[GBA_REG(JOYCNT)]);
return 3;
case JOY_RECV:
sio->p->p->memory.io[GBA_REG(JOYCNT)] |= JOYCNT_RECV;
sio->p->p->memory.io[GBA_REG(JOYSTAT)] |= JOYSTAT_RECV;
sio->p->p->memory.io[GBA_REG(JOY_RECV_LO)] = data[0] | (data[1] << 8);
sio->p->p->memory.io[GBA_REG(JOY_RECV_HI)] = data[2] | (data[3] << 8);
data[0] = sio->p->p->memory.io[GBA_REG(JOYSTAT)];
mLOG(GBA_SIO, DEBUG, "JOY recv: %02X (%02X)", data[0], sio->p->p->memory.io[GBA_REG(JOYCNT)]);
if (sio->p->p->memory.io[GBA_REG(JOYCNT)] & 0x40) {
GBARaiseIRQ(sio->p->p, GBA_IRQ_SIO, 0);
}
return 1;
case JOY_TRANS:
data[0] = sio->p->p->memory.io[GBA_REG(JOY_TRANS_LO)];
data[1] = sio->p->p->memory.io[GBA_REG(JOY_TRANS_LO)] >> 8;
data[2] = sio->p->p->memory.io[GBA_REG(JOY_TRANS_HI)];
data[3] = sio->p->p->memory.io[GBA_REG(JOY_TRANS_HI)] >> 8;
data[4] = sio->p->p->memory.io[GBA_REG(JOYSTAT)];
sio->p->p->memory.io[GBA_REG(JOYCNT)] |= JOYCNT_TRANS;
sio->p->p->memory.io[GBA_REG(JOYSTAT)] &= ~JOYSTAT_TRANS;
mLOG(GBA_SIO, DEBUG, "JOY trans: %02X%02X%02X%02X:%02X (%02X)", data[0], data[1], data[2], data[3], data[4], sio->p->p->memory.io[GBA_REG(JOYCNT)]);
if (sio->p->p->memory.io[GBA_REG(JOYCNT)] & 0x40) {
GBARaiseIRQ(sio->p->p, GBA_IRQ_SIO, 0);
}
return 5;
}
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -70,7 +70,7 @@ void GBAVideoReset(struct GBAVideo* video) {
} else { } else {
// TODO: Verify exact scanline on hardware // TODO: Verify exact scanline on hardware
video->vcount = 0x7E; video->vcount = 0x7E;
nextEvent = 117; nextEvent = 120;
} }
video->p->memory.io[GBA_REG(VCOUNT)] = video->vcount; video->p->memory.io[GBA_REG(VCOUNT)] = video->vcount;

View File

@ -160,7 +160,7 @@ void ctrActivateTexture(const C3D_Tex* texture) {
.m = { .m = {
// Rows are in the order w z y x, because ctrulib // Rows are in the order w z y x, because ctrulib
0.0f, 0.0f, 0.0f, 1.0f / activeTexture->width, 0.0f, 0.0f, 0.0f, 1.0f / activeTexture->width,
0.0f, 0.0f, 1.0f / activeTexture->height, 0.0f 0.0f, 0.0f, 1.0f / activeTexture->height, 0.0f
} }
}; };
C3D_FVUnifMtx2x4(GPU_GEOMETRY_SHADER, GSH_FVEC_textureMtx, &textureMtx); C3D_FVUnifMtx2x4(GPU_GEOMETRY_SHADER, GSH_FVEC_textureMtx, &textureMtx);

View File

@ -53,7 +53,7 @@ int main(int argc, char** argv) {
SocketClose(sock); SocketClose(sock);
SocketSubsystemDeinit(); SocketSubsystemDeinit();
didFail = true; didFail = true;
goto cleanup; goto cleanup;
} }
// Run the server // Run the server

View File

@ -834,7 +834,7 @@ static void _setupMaps(struct mCore* core) {
void retro_reset(void) { void retro_reset(void) {
core->reset(core); core->reset(core);
mRumbleIntegratorInit(&rumble); mRumbleIntegratorReset(&rumble);
_setupMaps(core); _setupMaps(core);
} }

View File

@ -253,7 +253,7 @@ static void mGLContextImageSize(struct VideoBackend* v, enum VideoLayer layer, i
*height = context->layerDims[layer].height; *height = context->layerDims[layer].height;
} else { } else {
*width = context->imageSizes[layer].width; *width = context->imageSizes[layer].width;
*height = context->imageSizes[layer].height; *height = context->imageSizes[layer].height;
} }
} }
@ -266,7 +266,7 @@ void mGLContextPostFrame(struct VideoBackend* v, enum VideoLayer layer, const vo
context->activeTex ^= 1; context->activeTex ^= 1;
glBindTexture(GL_TEXTURE_2D, context->tex[context->activeTex]); glBindTexture(GL_TEXTURE_2D, context->tex[context->activeTex]);
} else { } else {
glBindTexture(GL_TEXTURE_2D, context->layers[layer]); glBindTexture(GL_TEXTURE_2D, context->layers[layer]);
} }
int width = context->imageSizes[layer].width; int width = context->imageSizes[layer].width;

View File

@ -529,7 +529,7 @@ static void mGLES2ContextImageSize(struct VideoBackend* v, enum VideoLayer layer
*height = context->layerDims[layer].height; *height = context->layerDims[layer].height;
} else { } else {
*width = context->imageSizes[layer].width; *width = context->imageSizes[layer].width;
*height = context->imageSizes[layer].height; *height = context->imageSizes[layer].height;
} }
} }
@ -617,7 +617,7 @@ void mGLES2ShaderInit(struct mGLES2Shader* shader, const char* vs, const char* f
if (shader->width > 0 && shader->height > 0) { if (shader->width > 0 && shader->height > 0) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, shader->width, shader->height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, shader->width, shader->height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
} else { } else {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 512, 512, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 512, 512, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
} }
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, shader->tex, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, shader->tex, 0);

View File

@ -5,7 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef PSP2_COMMON_H #ifndef PSP2_COMMON_H
#define PSP2_COMMON_H #define PSP2_COMMON_H
#include <mgba-util/common.h> #include <mgba-util/common.h>
#define PSP2_HORIZONTAL_PIXELS 960 #define PSP2_HORIZONTAL_PIXELS 960

View File

@ -22,8 +22,7 @@
#define CXX_GUARD_END #define CXX_GUARD_END
#define PYCPARSE #define PYCPARSE
#define va_list void* typedef ... va_list;
typedef int... time_t; typedef int... time_t;
typedef int... off_t; typedef int... off_t;
typedef ...* png_structp; typedef ...* png_structp;
@ -46,7 +45,6 @@ void free(void*);
#define PYEXPORT extern "Python+C" #define PYEXPORT extern "Python+C"
#include "platform/python/core.h" #include "platform/python/core.h"
#include "platform/python/log.h" #include "platform/python/log.h"
#include "platform/python/sio.h"
#include "platform/python/vfs-py.h" #include "platform/python/vfs-py.h"
#undef PYEXPORT #undef PYEXPORT

View File

@ -45,7 +45,6 @@ ffi.set_source("mgba._pylib", """
#define PYEXPORT #define PYEXPORT
#include "platform/python/core.h" #include "platform/python/core.h"
#include "platform/python/log.h" #include "platform/python/log.h"
#include "platform/python/sio.h"
#include "platform/python/vfs-py.h" #include "platform/python/vfs-py.h"
#undef PYEXPORT #undef PYEXPORT
""", include_dirs=[incdir, srcdir], """, include_dirs=[incdir, srcdir],
@ -53,7 +52,7 @@ ffi.set_source("mgba._pylib", """
libraries=["mgba"], libraries=["mgba"],
library_dirs=[bindir], library_dirs=[bindir],
runtime_library_dirs=[libdir], runtime_library_dirs=[libdir],
sources=[os.path.join(pydir, path) for path in ["vfs-py.c", "core.c", "log.c", "sio.c"]]) sources=[os.path.join(pydir, path) for path in ["vfs-py.c", "core.c", "log.c"]])
preprocessed = subprocess.check_output(cpp + ["-fno-inline", "-P"] + cppflags + [os.path.join(pydir, "_builder.h")], universal_newlines=True) preprocessed = subprocess.check_output(cpp + ["-fno-inline", "-P"] + cppflags + [os.path.join(pydir, "_builder.h")], universal_newlines=True)

View File

@ -52,72 +52,6 @@ class GBA(Core):
super(GBA, self)._load() super(GBA, self)._load()
self.memory = GBAMemory(self._core, self._native.memory.romSize) self.memory = GBAMemory(self._core, self._native.memory.romSize)
def attach_sio(self, link, mode=lib.GBA_SIO_MULTI):
self._sio.add(mode)
lib.GBASIOSetDriver(ffi.addressof(self._native.sio), link._native, mode)
def __del__(self):
for mode in self._sio:
lib.GBASIOSetDriver(ffi.addressof(self._native.sio), ffi.NULL, mode)
create_callback("GBASIOPythonDriver", "init")
create_callback("GBASIOPythonDriver", "deinit")
create_callback("GBASIOPythonDriver", "load")
create_callback("GBASIOPythonDriver", "unload")
create_callback("GBASIOPythonDriver", "writeRegister")
class GBASIODriver(object):
def __init__(self):
super(GBASIODriver, self).__init__()
self._handle = ffi.new_handle(self)
self._native = ffi.gc(lib.GBASIOPythonDriverCreate(self._handle), lib.free)
def init(self):
return True
def deinit(self):
pass
def load(self):
return True
def unload(self):
return True
def write_register(self, address, value):
return value
class GBASIOJOYDriver(GBASIODriver):
RESET = lib.JOY_RESET
POLL = lib.JOY_POLL
TRANS = lib.JOY_TRANS
RECV = lib.JOY_RECV
def __init__(self):
super(GBASIOJOYDriver, self).__init__()
self._native = ffi.gc(lib.GBASIOJOYPythonDriverCreate(self._handle), lib.free)
def send_command(self, cmd, data):
buffer = ffi.new('uint8_t[5]')
try:
buffer[0] = data[0]
buffer[1] = data[1]
buffer[2] = data[2]
buffer[3] = data[3]
buffer[4] = data[4]
except IndexError:
pass
outlen = lib.GBASIOJOYSendCommand(self._native, cmd, buffer)
if outlen > 0 and outlen <= 5:
return bytes(buffer[0:outlen])
return None
class GBAMemory(Memory): class GBAMemory(Memory):
def __init__(self, core, romSize=lib.SIZE_CART0): def __init__(self, core, romSize=lib.SIZE_CART0):

View File

@ -1,88 +0,0 @@
/* Copyright (c) 2013-2017 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/flags.h>
#define CREATE_SHIM(PLAT, NAME, RETURN) \
RETURN _py ## PLAT ## SIOPythonDriver ## NAME (void* driver); \
static RETURN _py ## PLAT ## SIOPythonDriver ## NAME ## Shim(struct PLAT ## SIODriver* driver) { \
struct PLAT ## SIODriver* py = (struct PLAT ## SIODriver*) driver; \
return _py ## PLAT ## SIOPythonDriver ## NAME(py); \
}
#define CREATE_SHIM_ARGS(PLAT, NAME, RETURN, TYPES, ...) \
RETURN _py ## PLAT ## SIOPythonDriver ## NAME TYPES; \
static RETURN _py ## PLAT ## SIOPythonDriver ## NAME ## Shim TYPES { \
struct PLAT ## SIODriver* py = (struct PLAT ## SIODriver*) driver; \
return _py ## PLAT ## SIOPythonDriver ## NAME(py, __VA_ARGS__); \
}
#ifdef M_CORE_GBA
#include <mgba/gba/interface.h>
struct GBASIOPythonDriver {
struct GBASIODriver d;
void* pyobj;
};
CREATE_SHIM(GBA, Init, bool);
CREATE_SHIM(GBA, Deinit, void);
CREATE_SHIM(GBA, Load, bool);
CREATE_SHIM(GBA, Unload, bool);
CREATE_SHIM_ARGS(GBA, WriteRegister, uint16_t, (struct GBASIODriver* driver, uint32_t address, uint16_t value), address, value);
struct GBASIODriver* GBASIOPythonDriverCreate(void* pyobj) {
struct GBASIOPythonDriver* driver = malloc(sizeof(*driver));
driver->d.init = _pyGBASIOPythonDriverInitShim;
driver->d.deinit = _pyGBASIOPythonDriverDeinitShim;
driver->d.load = _pyGBASIOPythonDriverLoadShim;
driver->d.unload = _pyGBASIOPythonDriverUnloadShim;
driver->d.writeRegister = _pyGBASIOPythonDriverWriteRegisterShim;
driver->pyobj = pyobj;
return &driver->d;
}
struct GBASIODriver* GBASIOJOYPythonDriverCreate(void* pyobj) {
struct GBASIOPythonDriver* driver = malloc(sizeof(*driver));
GBASIOJOYCreate(&driver->d);
driver->d.init = _pyGBASIOPythonDriverInitShim;
driver->d.deinit = _pyGBASIOPythonDriverDeinitShim;
driver->d.load = _pyGBASIOPythonDriverLoadShim;
driver->d.unload = _pyGBASIOPythonDriverUnloadShim;
driver->pyobj = pyobj;
return &driver->d;
}
#endif
#ifdef M_CORE_GB
#include <mgba/gb/interface.h>
struct GBSIOPythonDriver {
struct GBSIODriver d;
void* pyobj;
};
CREATE_SHIM(GB, Init, bool);
CREATE_SHIM(GB, Deinit, void);
CREATE_SHIM_ARGS(GB, WriteSB, void, (struct GBSIODriver* driver, uint8_t value), value);
CREATE_SHIM_ARGS(GB, WriteSC, uint8_t, (struct GBSIODriver* driver, uint8_t value), value);
struct GBSIODriver* GBSIOPythonDriverCreate(void* pyobj) {
struct GBSIOPythonDriver* driver = malloc(sizeof(*driver));
driver->d.init = _pyGBSIOPythonDriverInitShim;
driver->d.deinit = _pyGBSIOPythonDriverDeinitShim;
driver->d.writeSB = _pyGBSIOPythonDriverWriteSBShim;
driver->d.writeSC = _pyGBSIOPythonDriverWriteSCShim;
driver->pyobj = pyobj;
return &driver->d;
}
#endif

View File

@ -1,42 +0,0 @@
/* Copyright (c) 2013-2017 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifdef M_CORE_GBA
#include <mgba/gba/interface.h>
struct GBASIOPythonDriver {
struct GBASIODriver d;
void* pyobj;
};
struct GBASIODriver* GBASIOPythonDriverCreate(void* pyobj);
struct GBASIODriver* GBASIOJOYPythonDriverCreate(void* pyobj);
PYEXPORT bool _pyGBASIOPythonDriverInit(void* driver);
PYEXPORT void _pyGBASIOPythonDriverDeinit(void* driver);
PYEXPORT bool _pyGBASIOPythonDriverLoad(void* driver);
PYEXPORT bool _pyGBASIOPythonDriverUnload(void* driver);
PYEXPORT uint16_t _pyGBASIOPythonDriverWriteRegister(void* driver, uint32_t address, uint16_t value);
#endif
#ifdef M_CORE_GB
#include <mgba/gb/interface.h>
struct GBSIOPythonDriver {
struct GBSIODriver d;
void* pyobj;
};
struct GBSIODriver* GBSIOPythonDriverCreate(void* pyobj);
PYEXPORT bool _pyGBSIOPythonDriverInit(void* driver);
PYEXPORT void _pyGBSIOPythonDriverDeinit(void* driver);
PYEXPORT void _pyGBSIOPythonDriverWriteSB(void* driver, uint8_t value);
PYEXPORT uint8_t _pyGBSIOPythonDriverWriteSC(void* driver, uint8_t value);
#endif

View File

@ -216,7 +216,7 @@ void BattleChipView::loadDeck() {
error->show(); error->show();
return; return;
} }
QList<int> newDeck; QList<int> newDeck;
QStringList deck = ini.value("deck").toString().split(','); QStringList deck = ini.value("deck").toString().split(',');
for (const auto& item : deck) { for (const auto& item : deck) {

View File

@ -59,7 +59,10 @@ if(APPLE)
list(APPEND QT_DEFINES USE_SHARE_WIDGET) list(APPEND QT_DEFINES USE_SHARE_WIDGET)
endif() endif()
if(Qt6Widgets_VERSION) # Allows building with Qt that was built with std::filesystem support, which requires macOS 15
if(Qt6Widgets_VERSION AND MACOSX_SDK VERSION_GREATER_EQUAL 10.15)
set(MIN_VER 10.15)
elseif(Qt6Widgets_VERSION)
set(MIN_VER 10.14) set(MIN_VER 10.14)
elseif(Qt5Widgets_VERSION MATCHES "^5.15") elseif(Qt5Widgets_VERSION MATCHES "^5.15")
set(MIN_VER 10.13) set(MIN_VER 10.13)
@ -74,8 +77,7 @@ if(APPLE)
else() else()
set(MIN_VER 10.8) set(MIN_VER 10.8)
endif() endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=${MIN_VER}") set(CMAKE_OSX_DEPLOYMENT_TARGET ${MIN_VER})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=${MIN_VER}")
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
endif() endif()
@ -427,6 +429,7 @@ set_target_properties(${BINARY_NAME}-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${PR
if(WIN32) if(WIN32)
set_target_properties(${BINARY_NAME}-qt PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") set_target_properties(${BINARY_NAME}-qt PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
target_link_libraries(${BINARY_NAME}-qt dwmapi)
if(NOT MSVC) if(NOT MSVC)
target_link_libraries(${BINARY_NAME}-qt -municode) target_link_libraries(${BINARY_NAME}-qt -municode)
endif() endif()
@ -445,7 +448,7 @@ if(QT_STATIC)
if(CMAKE_CROSSCOMPILING) if(CMAKE_CROSSCOMPILING)
set(QWINDOWS_DEPS ${QT}EventDispatcherSupport ${QT}FontDatabaseSupport ${QT}ThemeSupport ${QT}WindowsUIAutomationSupport) set(QWINDOWS_DEPS ${QT}EventDispatcherSupport ${QT}FontDatabaseSupport ${QT}ThemeSupport ${QT}WindowsUIAutomationSupport)
endif() endif()
list(APPEND QT_LIBRARIES ${QT}::QWindowsIntegrationPlugin ${QWINDOWS_DEPS} amstrmid dwmapi uxtheme imm32 -static-libgcc -static-libstdc++) list(APPEND QT_LIBRARIES ${QT}::QWindowsIntegrationPlugin ${QWINDOWS_DEPS} amstrmid uxtheme imm32 -static-libgcc -static-libstdc++)
set_target_properties(${QT}::Core PROPERTIES INTERFACE_LINK_LIBRARIES "${QTPCRE};version;winmm;ssl;crypto;ws2_32;iphlpapi;crypt32;userenv;netapi32;wtsapi32") set_target_properties(${QT}::Core PROPERTIES INTERFACE_LINK_LIBRARIES "${QTPCRE};version;winmm;ssl;crypto;ws2_32;iphlpapi;crypt32;userenv;netapi32;wtsapi32")
set_target_properties(${QT}::Gui PROPERTIES INTERFACE_LINK_LIBRARIES ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY}) set_target_properties(${QT}::Gui PROPERTIES INTERFACE_LINK_LIBRARIES ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY})
elseif(APPLE) elseif(APPLE)
@ -477,13 +480,21 @@ endif()
if(APPLE) if(APPLE)
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
get_target_property(QTCOCOA ${QT}::QCocoaIntegrationPlugin LOCATION) get_target_property(QTCOCOA ${QT}::QCocoaIntegrationPlugin LOCATION)
get_target_property(COREAUDIO ${QT}::CoreAudioPlugin LOCATION) if(${QT_V} LESS 6)
get_target_property(QTAVFSERVICE ${QT}::AVFServicePlugin LOCATION) # QtMultimedia plugins were removed with Qt 6, skip checking for them
get_target_property(COREAUDIO ${QT}::CoreAudioPlugin LOCATION)
get_target_property(QTAVFSERVICE ${QT}::AVFServicePlugin LOCATION)
endif()
set(BUNDLE_PATH ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.app) set(BUNDLE_PATH ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.app)
target_sources(${BINARY_NAME}-qt PRIVATE "${PLUGINS}") target_sources(${BINARY_NAME}-qt PRIVATE "${PLUGINS}")
set_source_files_properties("${QTCOCOA}" PROPERTIES MACOSX_PACKAGE_LOCATION Contents/PlugIns) set_source_files_properties("${QTCOCOA}" PROPERTIES MACOSX_PACKAGE_LOCATION Contents/PlugIns)
set_source_files_properties("${COREAUDIO}" PROPERTIES MACOSX_PACKAGE_LOCATION Contents/PlugIns) if(${QT_V} LESS 6)
set_source_files_properties("${QTAVFSERVICE}" PROPERTIES MACOSX_PACKAGE_LOCATION Contents/PlugIns) set_source_files_properties("${COREAUDIO}" PROPERTIES MACOSX_PACKAGE_LOCATION Contents/PlugIns)
set_source_files_properties("${QTAVFSERVICE}" PROPERTIES MACOSX_PACKAGE_LOCATION Contents/PlugIns)
endif()
install(CODE " install(CODE "
include(BundleUtilities) include(BundleUtilities)
set(BU_CHMOD_BUNDLE_ITEMS ON) set(BU_CHMOD_BUNDLE_ITEMS ON)

View File

@ -154,11 +154,12 @@ ConfigController::ConfigController(QObject* parent)
mSubParserGraphicsInit(&m_subparsers[0], &m_graphicsOpts); mSubParserGraphicsInit(&m_subparsers[0], &m_graphicsOpts);
m_subparsers[1].usage = "Frontend options:\n" m_subparsers[1].usage = "Frontend options:\n"
" --ecard FILE Scan an e-Reader card in the first loaded game\n" " --ecard FILE Scan an e-Reader card in the first loaded game\n"
" Can be passed multiple times for multiple cards\n" " Can be passed multiple times for multiple cards\n"
" --mb FILE Boot a multiboot image with FILE inserted into the ROM slot" " --mb FILE Boot a multiboot image with FILE inserted into the ROM slot"
#ifdef ENABLE_SCRIPTING #ifdef ENABLE_SCRIPTING
"\n --script FILE Script file to load on start" "\n --script FILE Script file to load on start\n"
" Can be passed multiple times\n"
#endif #endif
; ;

View File

@ -130,7 +130,7 @@ private:
mGraphicsOpts m_graphicsOpts{}; mGraphicsOpts m_graphicsOpts{};
std::array<mSubParser, 2> m_subparsers; std::array<mSubParser, 2> m_subparsers;
bool m_parsed = false; bool m_parsed = false;
QHash<QString, QVariant> m_argvOptions; QHash<QString, QVariant> m_argvOptions;
QHash<QString, ConfigOption*> m_optionSet; QHash<QString, ConfigOption*> m_optionSet;
std::unique_ptr<QSettings> m_settings; std::unique_ptr<QSettings> m_settings;

View File

@ -423,8 +423,8 @@ bool CoreController::attachDolphin(const Address& address) {
return false; return false;
} }
if (GBASIODolphinConnect(&m_dolphin, &address, 0, 0)) { if (GBASIODolphinConnect(&m_dolphin, &address, 0, 0)) {
GBA* gba = static_cast<GBA*>(m_threadContext.core->board); clearMultiplayerController();
GBASIOSetDriver(&gba->sio, &m_dolphin.d, GBA_SIO_JOYBUS); m_threadContext.core->setPeripheral(m_threadContext.core, mPERIPH_GBA_LINK_PORT, &m_dolphin.d);
return true; return true;
} }
return false; return false;
@ -432,8 +432,8 @@ bool CoreController::attachDolphin(const Address& address) {
void CoreController::detachDolphin() { void CoreController::detachDolphin() {
if (platform() == mPLATFORM_GBA) { if (platform() == mPLATFORM_GBA) {
GBA* gba = static_cast<GBA*>(m_threadContext.core->board); // TODO: Reattach to multiplayer controller
GBASIOSetDriver(&gba->sio, nullptr, GBA_SIO_JOYBUS); m_threadContext.core->setPeripheral(m_threadContext.core, mPERIPH_GBA_LINK_PORT, NULL);
} }
GBASIODolphinDestroy(&m_dolphin); GBASIODolphinDestroy(&m_dolphin);
} }
@ -736,7 +736,7 @@ void CoreController::saveState(const QString& path, int flags) {
vf->read(vf, controller->m_backupSaveState.data(), controller->m_backupSaveState.size()); vf->read(vf, controller->m_backupSaveState.data(), controller->m_backupSaveState.size());
vf->close(vf); vf->close(vf);
} }
vf = VFileDevice::open(controller->m_statePath, O_WRONLY | O_CREAT | O_TRUNC); vf = VFileDevice::open(controller->m_statePath, O_RDWR | O_CREAT | O_TRUNC);
if (!vf) { if (!vf) {
return; return;
} }
@ -1094,7 +1094,7 @@ void CoreController::attachBattleChipGate() {
Interrupter interrupter(this); Interrupter interrupter(this);
clearMultiplayerController(); clearMultiplayerController();
GBASIOBattlechipGateCreate(&m_battlechip); GBASIOBattlechipGateCreate(&m_battlechip);
m_threadContext.core->setPeripheral(m_threadContext.core, mPERIPH_GBA_BATTLECHIP_GATE, &m_battlechip); m_threadContext.core->setPeripheral(m_threadContext.core, mPERIPH_GBA_LINK_PORT, &m_battlechip);
} }
void CoreController::detachBattleChipGate() { void CoreController::detachBattleChipGate() {
@ -1102,7 +1102,7 @@ void CoreController::detachBattleChipGate() {
return; return;
} }
Interrupter interrupter(this); Interrupter interrupter(this);
m_threadContext.core->setPeripheral(m_threadContext.core, mPERIPH_GBA_BATTLECHIP_GATE, nullptr); m_threadContext.core->setPeripheral(m_threadContext.core, mPERIPH_GBA_LINK_PORT, nullptr);
} }
void CoreController::setBattleChipId(uint16_t id) { void CoreController::setBattleChipId(uint16_t id) {

View File

@ -162,7 +162,7 @@ void DebuggerConsoleController::historyLoad() {
if (line.endsWith("\r\n")) { if (line.endsWith("\r\n")) {
line.chop(2); line.chop(2);
} else if (line.endsWith("\n")) { } else if (line.endsWith("\n")) {
line.chop(1); line.chop(1);
} }
history.append(QString::fromUtf8(line)); history.append(QString::fromUtf8(line));
} }

View File

@ -62,7 +62,7 @@ public:
virtual void setVideoProxy(std::shared_ptr<VideoProxy> proxy) { m_videoProxy = std::move(proxy); } virtual void setVideoProxy(std::shared_ptr<VideoProxy> proxy) { m_videoProxy = std::move(proxy); }
std::shared_ptr<VideoProxy> videoProxy() { return m_videoProxy; } std::shared_ptr<VideoProxy> videoProxy() { return m_videoProxy; }
virtual VideoBackend* videoBackend(); virtual VideoBackend* videoBackend();
signals: signals:
void drawingStarted(); void drawingStarted();
void showCursor(); void showCursor();
@ -81,7 +81,7 @@ public slots:
virtual void filter(bool filter); virtual void filter(bool filter);
virtual void swapInterval(int interval) = 0; virtual void swapInterval(int interval) = 0;
virtual void framePosted() = 0; virtual void framePosted() = 0;
virtual void setShaders(struct VDir*) = 0; virtual bool setShaders(struct VDir*) = 0;
virtual void clearShaders() = 0; virtual void clearShaders() = 0;
virtual void resizeContext() = 0; virtual void resizeContext() = 0;

View File

@ -192,6 +192,7 @@ DisplayGL::DisplayGL(const QSurfaceFormat& format, QWidget* parent)
setAttribute(Qt::WA_NativeWindow); setAttribute(Qt::WA_NativeWindow);
window()->windowHandle()->setFormat(format); window()->windowHandle()->setFormat(format);
windowHandle()->setSurfaceType(QSurface::OpenGLSurface); windowHandle()->setSurfaceType(QSurface::OpenGLSurface);
windowHandle()->destroy();
windowHandle()->create(); windowHandle()->create();
#ifdef USE_SHARE_WIDGET #ifdef USE_SHARE_WIDGET
@ -314,7 +315,7 @@ bool DisplayGL::highestCompatible(QSurfaceFormat& format) {
if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) { if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) {
format.setVersion(1, 4); format.setVersion(1, 4);
} else { } else {
format.setVersion(1, 1); format.setVersion(1, 1);
} }
format.setOption(QSurfaceFormat::DeprecatedFunctions); format.setOption(QSurfaceFormat::DeprecatedFunctions);
if (DisplayGL::supportsFormat(format)) { if (DisplayGL::supportsFormat(format)) {
@ -461,8 +462,10 @@ void DisplayGL::framePosted() {
QMetaObject::invokeMethod(m_painter.get(), "draw"); QMetaObject::invokeMethod(m_painter.get(), "draw");
} }
void DisplayGL::setShaders(struct VDir* shaders) { bool DisplayGL::setShaders(struct VDir* shaders) {
QMetaObject::invokeMethod(m_painter.get(), "setShaders", Qt::BlockingQueuedConnection, Q_ARG(struct VDir*, shaders)); bool success = false;
QMetaObject::invokeMethod(m_painter.get(), "setShaders", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, success), Q_ARG(struct VDir*, shaders));
return success;
} }
void DisplayGL::clearShaders() { void DisplayGL::clearShaders() {
@ -995,10 +998,11 @@ void PainterGL::interrupt() {
m_interrupter.interrupt(m_context); m_interrupter.interrupt(m_context);
} }
void PainterGL::setShaders(struct VDir* dir) { bool PainterGL::setShaders(struct VDir* dir) {
if (!supportsShaders()) { if (!supportsShaders()) {
return; return false;
} }
bool success = false;
#if defined(BUILD_GLES2) || defined(BUILD_GLES3) #if defined(BUILD_GLES2) || defined(BUILD_GLES3)
if (!m_started) { if (!m_started) {
makeCurrent(); makeCurrent();
@ -1008,7 +1012,9 @@ void PainterGL::setShaders(struct VDir* dir) {
mGLES2ShaderDetach(reinterpret_cast<mGLES2Context*>(m_backend)); mGLES2ShaderDetach(reinterpret_cast<mGLES2Context*>(m_backend));
mGLES2ShaderFree(&m_shader); mGLES2ShaderFree(&m_shader);
} }
if (mGLES2ShaderLoad(&m_shader, dir)) {
success = mGLES2ShaderLoad(&m_shader, dir);
if (success) {
mGLES2ShaderAttach(reinterpret_cast<mGLES2Context*>(m_backend), static_cast<mGLES2Shader*>(m_shader.passes), m_shader.nPasses); mGLES2ShaderAttach(reinterpret_cast<mGLES2Context*>(m_backend), static_cast<mGLES2Shader*>(m_shader.passes), m_shader.nPasses);
} }
@ -1016,6 +1022,7 @@ void PainterGL::setShaders(struct VDir* dir) {
m_gl->doneCurrent(); m_gl->doneCurrent();
} }
#endif #endif
return success;
} }
void PainterGL::clearShaders() { void PainterGL::clearShaders() {

View File

@ -113,7 +113,7 @@ public slots:
void filter(bool filter) override; void filter(bool filter) override;
void swapInterval(int interval) override; void swapInterval(int interval) override;
void framePosted() override; void framePosted() override;
void setShaders(struct VDir*) override; bool setShaders(struct VDir*) override;
void clearShaders() override; void clearShaders() override;
void resizeContext() override; void resizeContext() override;
void setVideoScale(int scale) override; void setVideoScale(int scale) override;
@ -184,7 +184,7 @@ public slots:
void resizeContext(); void resizeContext();
void setBackgroundImage(const QImage&); void setBackgroundImage(const QImage&);
void setShaders(struct VDir*); bool setShaders(struct VDir*);
void clearShaders(); void clearShaders();
VideoShader* shaders(); VideoShader* shaders();
QSize contentSize() const; QSize contentSize() const;

View File

@ -41,7 +41,7 @@ public slots:
void swapInterval(int) override {}; void swapInterval(int) override {};
void filter(bool filter) override; void filter(bool filter) override;
void framePosted() override; void framePosted() override;
void setShaders(struct VDir*) override {} bool setShaders(struct VDir*) override { return false; }
void clearShaders() override {} void clearShaders() override {}
void resizeContext() override; void resizeContext() override;
void setBackgroundImage(const QImage&) override; void setBackgroundImage(const QImage&) override;

Some files were not shown because too many files have changed in this diff Show More