mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' (early part) into translations
This commit is contained in:
commit
9145e2d65f
|
@ -12,7 +12,11 @@ install:
|
|||
- vcpkg --no-dry-run upgrade
|
||||
- rd /Q /S C:\Tools\vcpkg\buildtrees
|
||||
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:
|
||||
parallel: true
|
||||
project: mGBA.sln
|
||||
|
|
47
CHANGES
47
CHANGES
|
@ -8,42 +8,32 @@ Features:
|
|||
- Debugger: Add range watchpoints
|
||||
Emulation fixes:
|
||||
- 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 Video: Implement DMG-style sprite ordering
|
||||
- GBA: Unhandled bkpt should be treated as an undefined exception
|
||||
- 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 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: Improve emulation of window start/end conditions (fixes mgba.io/i/1945)
|
||||
Other fixes:
|
||||
- Core: Fix inconsistencies with setting game-specific overrides (fixes mgba.io/i/2963)
|
||||
- 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
|
||||
- GBA Hardware: Fix loading states unconditionally overwriting GPIO memory
|
||||
- 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 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:
|
||||
- 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: Add MD5 hashing for ROMs
|
||||
- GB: Prevent incompatible BIOSes from being used on differing models
|
||||
- GB Serialize: Add missing savestate support for MBC6 and NT (newer)
|
||||
- GBA: Improve detection of valid ELF ROMs
|
||||
- GBA Audio: Remove broken XQ audio pending rewrite
|
||||
- GBA BIOS: Move SoftReset implementation to assembly
|
||||
- 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
|
||||
- Libretro: Add Super Game Boy Color support (closes mgba.io/i/3188)
|
||||
- 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: 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 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
|
||||
|
||||
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)
|
||||
- Vita: Add imc0 and xmc0 mount point support
|
||||
|
||||
|
|
|
@ -242,8 +242,14 @@ if(APPLE)
|
|||
execute_process(COMMAND xcrun --show-sdk-version OUTPUT_VARIABLE MACOSX_SDK)
|
||||
add_definitions(-D_DARWIN_C_SOURCE)
|
||||
list(APPEND OS_LIB "-framework Foundation")
|
||||
|
||||
# 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
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=10.6")
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.6")
|
||||
endif()
|
||||
# Not supported until Xcode 9
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_LESS "9")
|
||||
|
@ -753,6 +759,7 @@ if(USE_ELF)
|
|||
endif()
|
||||
|
||||
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)
|
||||
list(APPEND FEATURES DISCORD_RPC)
|
||||
include_directories(AFTER ${CMAKE_CURRENT_SOURCE_DIR}/src/third-party/discord-rpc/include)
|
||||
|
|
|
@ -11,15 +11,15 @@
|
|||
CXX_GUARD_START
|
||||
|
||||
struct mSize {
|
||||
int width;
|
||||
int height;
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
struct mRectangle {
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
void mRectangleUnion(struct mRectangle* dst, const struct mRectangle* add);
|
||||
|
|
|
@ -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
|
|
@ -30,6 +30,7 @@ enum mPlatform {
|
|||
|
||||
enum mCoreChecksumType {
|
||||
mCHECKSUM_CRC32,
|
||||
mCHECKSUM_MD5,
|
||||
};
|
||||
|
||||
struct mAudioBuffer;
|
||||
|
|
|
@ -133,6 +133,7 @@ struct mRumbleIntegrator {
|
|||
};
|
||||
|
||||
void mRumbleIntegratorInit(struct mRumbleIntegrator*);
|
||||
void mRumbleIntegratorReset(struct mRumbleIntegrator*);
|
||||
|
||||
struct mCoreChannelInfo {
|
||||
size_t id;
|
||||
|
|
|
@ -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
|
||||
* 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
|
||||
|
||||
#endif
|
||||
|
|
|
@ -57,7 +57,7 @@ struct mVideoBackendCommand {
|
|||
struct mVideoProxyBackend {
|
||||
struct VideoBackend d;
|
||||
struct VideoBackend* backend;
|
||||
|
||||
|
||||
struct RingFIFO in;
|
||||
struct RingFIFO out;
|
||||
|
||||
|
|
|
@ -68,7 +68,9 @@ enum GBAHardwareDevice {
|
|||
HW_TILT = 16,
|
||||
HW_GB_PLAYER = 32,
|
||||
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;
|
||||
|
@ -81,7 +83,7 @@ extern MGBA_EXPORT const int GBA_LUX_LEVELS[10];
|
|||
|
||||
enum {
|
||||
mPERIPH_GBA_LUMINANCE = 0x1000,
|
||||
mPERIPH_GBA_BATTLECHIP_GATE,
|
||||
mPERIPH_GBA_LINK_PORT,
|
||||
};
|
||||
|
||||
struct GBACartridgeOverride {
|
||||
|
@ -110,13 +112,22 @@ struct GBASIODriver {
|
|||
|
||||
bool (*init)(struct GBASIODriver* driver);
|
||||
void (*deinit)(struct GBASIODriver* driver);
|
||||
bool (*load)(struct GBASIODriver* driver);
|
||||
bool (*unload)(struct GBASIODriver* driver);
|
||||
uint16_t (*writeRegister)(struct GBASIODriver* driver, uint32_t address, uint16_t value);
|
||||
void (*reset)(struct GBASIODriver* driver);
|
||||
uint32_t (*driverId)(const struct GBASIODriver* renderer);
|
||||
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 {
|
||||
GBA_FLAVOR_BATTLECHIP_GATE = 4,
|
||||
GBA_FLAVOR_PROGRESS_GATE = 5,
|
||||
|
@ -126,7 +137,6 @@ enum GBASIOBattleChipGateFlavor {
|
|||
|
||||
struct GBASIOBattlechipGate {
|
||||
struct GBASIODriver d;
|
||||
struct mTimingEvent event;
|
||||
uint16_t chipId;
|
||||
uint16_t data[2];
|
||||
int state;
|
||||
|
|
|
@ -9,23 +9,23 @@
|
|||
#define mSAVEDATA_CLEANUP_THRESHOLD 15
|
||||
|
||||
enum {
|
||||
mSAVEDATA_DIRT_NONE = 0,
|
||||
mSAVEDATA_DIRT_NONE = 0,
|
||||
mSAVEDATA_DIRT_NEW = 1,
|
||||
mSAVEDATA_DIRT_SEEN = 2,
|
||||
};
|
||||
|
||||
static inline bool mSavedataClean(int* dirty, uint32_t* dirtAge, uint32_t frameCount) {
|
||||
if (*dirty & mSAVEDATA_DIRT_NEW) {
|
||||
*dirtAge = frameCount;
|
||||
*dirty &= ~mSAVEDATA_DIRT_NEW;
|
||||
if (!(*dirty & mSAVEDATA_DIRT_SEEN)) {
|
||||
*dirty |= mSAVEDATA_DIRT_SEEN;
|
||||
}
|
||||
} else if ((*dirty & mSAVEDATA_DIRT_SEEN) && frameCount - *dirtAge > mSAVEDATA_CLEANUP_THRESHOLD) {
|
||||
*dirty = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
if (*dirty & mSAVEDATA_DIRT_NEW) {
|
||||
*dirtAge = frameCount;
|
||||
*dirty &= ~mSAVEDATA_DIRT_NEW;
|
||||
if (!(*dirty & mSAVEDATA_DIRT_SEEN)) {
|
||||
*dirty |= mSAVEDATA_DIRT_SEEN;
|
||||
}
|
||||
} else if ((*dirty & mSAVEDATA_DIRT_SEEN) && frameCount - *dirtAge > mSAVEDATA_CLEANUP_THRESHOLD) {
|
||||
*dirty = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -86,6 +86,7 @@ struct GBACartridgeHardware {
|
|||
};
|
||||
|
||||
void GBAHardwareInit(struct GBACartridgeHardware* gpio, uint16_t* gpioBase);
|
||||
void GBAHardwareReset(struct GBACartridgeHardware* gpio);
|
||||
void GBAHardwareClear(struct GBACartridgeHardware* gpio);
|
||||
|
||||
void GBAHardwareInitRTC(struct GBACartridgeHardware* gpio);
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -48,6 +48,7 @@ struct GBADMA {
|
|||
uint32_t nextDest;
|
||||
int32_t nextCount;
|
||||
uint32_t when;
|
||||
int32_t cycles;
|
||||
};
|
||||
|
||||
struct GBA;
|
||||
|
@ -65,6 +66,7 @@ void GBADMARunHblank(struct GBA* gba, int32_t cycles);
|
|||
void GBADMARunVblank(struct GBA* gba, int32_t cycles);
|
||||
void GBADMARunDisplayStart(struct GBA* gba, int32_t cycles);
|
||||
void GBADMAUpdate(struct GBA* gba);
|
||||
void GBADMARecalculateCycles(struct GBA* gba);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ CXX_GUARD_START
|
|||
#include <mgba/internal/gba/cart/ereader.h>
|
||||
#include <mgba/internal/gba/cart/gpio.h>
|
||||
#include <mgba/internal/gba/cart/matrix.h>
|
||||
#include <mgba/internal/gba/cart/vfame.h>
|
||||
#include <mgba/internal/gba/cart/unlicensed.h>
|
||||
|
||||
enum GBAMemoryRegion {
|
||||
GBA_REGION_BIOS = 0x0,
|
||||
|
@ -108,8 +108,8 @@ struct GBAMemory {
|
|||
|
||||
struct GBACartridgeHardware hw;
|
||||
struct GBASavedata savedata;
|
||||
struct GBAVFameCart vfame;
|
||||
struct GBAMatrix matrix;
|
||||
struct GBAUnlCart unl;
|
||||
struct GBACartEReader ereader;
|
||||
size_t romSize;
|
||||
uint32_t romMask;
|
||||
|
|
|
@ -91,6 +91,7 @@ struct GBASavedataRTCBuffer {
|
|||
};
|
||||
|
||||
void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf);
|
||||
void GBASavedataReset(struct GBASavedata* savedata);
|
||||
void GBASavedataDeinit(struct GBASavedata* savedata);
|
||||
|
||||
void GBASavedataMask(struct GBASavedata* savedata, struct VFile* vf, bool writeback);
|
||||
|
|
|
@ -186,12 +186,15 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
|
|||
* | bit 3: Reserved
|
||||
* | bits 4 - 15: Light counter
|
||||
* | 0x002C0 - 0x002C0: Light sample
|
||||
* | 0x002C1 - 0x002C3: Flags
|
||||
* | 0x002C1: Flags
|
||||
* | bits 0 - 1: Tilt state machine
|
||||
* | bits 2 - 3: GB Player inputs posted
|
||||
* | bits 4 - 8: GB Player transmit position
|
||||
* | bits 9 - 23: Reserved
|
||||
* 0x002C4 - 0x002C7: Game Boy Player next event
|
||||
* | bits 4 - 7: GB Player transmit position
|
||||
* | 0x002C2 - 0x002C3: Unlicensed cart flags
|
||||
* | 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
|
||||
* 0x002CC - 0x002CF: Last DMA transfer PC
|
||||
* 0x002D0 - 0x002DF: Matrix memory command buffer
|
||||
|
@ -230,8 +233,23 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
|
|||
* | bits 15 - 31: Reserved
|
||||
* 0x00320 - 0x00323: Next IRQ event
|
||||
* 0x00324 - 0x00327: Interruptable BIOS stall cycles
|
||||
* 0x00328 - 0x00367: Matrix memory mapping table
|
||||
* 0x00368 - 0x0036F: Reserved (leave zero)
|
||||
* 0x00328 - 0x0036F: Special cartridge state, one of:
|
||||
* | 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
|
||||
* 0x00380 - 0x0038F: Audio FIFO B samples
|
||||
* 0x00390 - 0x003CF: Audio rendered samples
|
||||
|
@ -269,9 +287,15 @@ DECL_BITS(GBASerializedHWFlags1, LightCounter, 4, 12);
|
|||
DECL_BITFIELD(GBASerializedHWFlags2, uint8_t);
|
||||
DECL_BITS(GBASerializedHWFlags2, TiltState, 0, 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_BITS(GBASerializedSavedataFlags, FlashState, 0, 2);
|
||||
|
@ -287,6 +311,7 @@ DECL_BITS(GBASerializedMiscFlags, KeyIRQKeys, 4, 11);
|
|||
|
||||
enum {
|
||||
GBA_SUBSYSTEM_VIDEO_RENDERER = 0,
|
||||
GBA_SUBSYSTEM_SIO_DRIVER = 1,
|
||||
GBA_SUBSYSTEM_MAX,
|
||||
};
|
||||
|
||||
|
@ -369,8 +394,8 @@ struct GBASerializedState {
|
|||
GBASerializedHWFlags1 flags1;
|
||||
uint8_t lightSample;
|
||||
GBASerializedHWFlags2 flags2;
|
||||
GBASerializedHWFlags3 flags3;
|
||||
uint32_t gbpNextEvent;
|
||||
GBASerializedUnlCartFlags unlCartFlags;
|
||||
uint32_t sioNextEvent;
|
||||
} hw;
|
||||
|
||||
uint32_t dmaTransferRegister;
|
||||
|
@ -406,8 +431,29 @@ struct GBASerializedState {
|
|||
uint32_t nextIrq;
|
||||
int32_t biosStall;
|
||||
|
||||
uint32_t matrixMappings[16];
|
||||
uint32_t reservedMatrix[2];
|
||||
union {
|
||||
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 {
|
||||
int8_t chA[16];
|
||||
|
|
|
@ -16,8 +16,6 @@ CXX_GUARD_START
|
|||
|
||||
#define MAX_GBAS 4
|
||||
|
||||
extern const int GBASIOCyclesPerTransfer[4][MAX_GBAS];
|
||||
|
||||
mLOG_DECLARE_CATEGORY(GBA_SIO);
|
||||
|
||||
enum {
|
||||
|
@ -54,37 +52,45 @@ DECL_BITS(GBASIOMultiplayer, Id, 4, 2);
|
|||
DECL_BIT(GBASIOMultiplayer, Error, 6);
|
||||
DECL_BIT(GBASIOMultiplayer, Busy, 7);
|
||||
DECL_BIT(GBASIOMultiplayer, Irq, 14);
|
||||
|
||||
struct GBASIODriverSet {
|
||||
struct GBASIODriver* normal;
|
||||
struct GBASIODriver* multiplayer;
|
||||
struct GBASIODriver* joybus;
|
||||
};
|
||||
DECL_BITFIELD(GBASIORegisterRCNT, uint16_t);
|
||||
DECL_BIT(GBASIORegisterRCNT, Sc, 0);
|
||||
DECL_BIT(GBASIORegisterRCNT, Sd, 1);
|
||||
DECL_BIT(GBASIORegisterRCNT, Si, 2);
|
||||
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 GBA* p;
|
||||
|
||||
enum GBASIOMode mode;
|
||||
struct GBASIODriverSet drivers;
|
||||
struct GBASIODriver* activeDriver;
|
||||
struct GBASIODriver* driver;
|
||||
|
||||
uint16_t rcnt;
|
||||
uint16_t siocnt;
|
||||
|
||||
struct GBASIOPlayer gbp;
|
||||
struct mTimingEvent completeEvent;
|
||||
};
|
||||
|
||||
void GBASIOInit(struct GBASIO* sio);
|
||||
void GBASIODeinit(struct GBASIO* sio);
|
||||
void GBASIOReset(struct GBASIO* sio);
|
||||
|
||||
void GBASIOSetDriverSet(struct GBASIO* sio, struct GBASIODriverSet* drivers);
|
||||
void GBASIOSetDriver(struct GBASIO* sio, struct GBASIODriver* driver, enum GBASIOMode mode);
|
||||
void GBASIOSetDriver(struct GBASIO* sio, struct GBASIODriver* driver);
|
||||
|
||||
void GBASIOWriteRCNT(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);
|
||||
|
||||
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);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
|
|
@ -21,7 +21,6 @@ struct GBASIOPlayer {
|
|||
struct GBA* p;
|
||||
unsigned inputsPosted;
|
||||
int txPosition;
|
||||
struct mTimingEvent event;
|
||||
struct GBASIOPlayerKeyCallback callback;
|
||||
bool oldOpposingDirections;
|
||||
struct mKeyCallback* oldCallback;
|
||||
|
|
|
@ -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
|
||||
* 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/timing.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 {
|
||||
struct mLockstep d;
|
||||
struct GBASIOLockstepNode* players[MAX_GBAS];
|
||||
int attachedMulti;
|
||||
int attachedNormal;
|
||||
#define MAX_LOCKSTEP_EVENTS 8
|
||||
|
||||
uint16_t multiRecv[MAX_GBAS];
|
||||
uint32_t normalRecv[MAX_GBAS];
|
||||
enum GBASIOLockstepEventType {
|
||||
SIO_EV_ATTACH,
|
||||
SIO_EV_DETACH,
|
||||
SIO_EV_HARD_SYNC,
|
||||
SIO_EV_MODE_SET,
|
||||
SIO_EV_TRANSFER_START,
|
||||
};
|
||||
|
||||
struct GBASIOLockstepNode {
|
||||
struct GBASIODriver d;
|
||||
struct GBASIOLockstep* p;
|
||||
struct mTimingEvent event;
|
||||
struct GBASIOLockstepCoordinator {
|
||||
struct Table players;
|
||||
Mutex mutex;
|
||||
|
||||
volatile int32_t nextEvent;
|
||||
int32_t eventDiff;
|
||||
bool normalSO;
|
||||
int id;
|
||||
unsigned nextId;
|
||||
|
||||
unsigned attachedPlayers[MAX_GBAS];
|
||||
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;
|
||||
bool transferFinished;
|
||||
#ifndef NDEBUG
|
||||
int transferId;
|
||||
enum mLockstepPhase phase;
|
||||
#endif
|
||||
enum GBASIOMode otherModes[MAX_GBAS];
|
||||
bool asleep;
|
||||
int32_t cycleOffset;
|
||||
struct GBASIOLockstepEvent* queue;
|
||||
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 GBASIOLockstepDetachNode(struct GBASIOLockstep*, struct GBASIOLockstepNode*);
|
||||
void GBASIOLockstepCoordinatorInit(struct GBASIOLockstepCoordinator*);
|
||||
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
|
||||
|
||||
|
|
|
@ -76,10 +76,14 @@ CXX_GUARD_START
|
|||
#define _mSCRIPT_FIELD_NAME(V) (V)->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) \
|
||||
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) \
|
||||
extern const struct mScriptType mSTStruct_ ## STRUCT; \
|
||||
|
@ -249,8 +253,8 @@ CXX_GUARD_START
|
|||
},
|
||||
|
||||
#define _mSCRIPT_STRUCT_METHOD_POP(TYPE, S, NPARAMS, ...) \
|
||||
_mCALL(_mCAT(mSCRIPT_POP_, _mSUCC_ ## NPARAMS), &frame->arguments, _mCOMMA_ ## NPARAMS(S(TYPE), __VA_ARGS__)); \
|
||||
if (mScriptListSize(&frame->arguments)) { \
|
||||
_mCALL(_mCAT(mSCRIPT_POP_, _mSUCC_ ## NPARAMS), &frame->stack, _mCOMMA_ ## NPARAMS(S(TYPE), __VA_ARGS__)); \
|
||||
if (mScriptListSize(&frame->stack)) { \
|
||||
return false; \
|
||||
}
|
||||
|
||||
|
@ -326,11 +330,11 @@ CXX_GUARD_START
|
|||
static const struct mScriptFunctionOverload _mSTStructBindingOverloads_ ## TYPE ## _ ## NAME[mSCRIPT_OVERLOADS_MAX]; \
|
||||
static bool _mSTStructBinding_ ## TYPE ## _ ## NAME(struct mScriptFrame* frame, void* 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) { \
|
||||
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 overload->function->call(frame, overload->function->context); \
|
||||
|
@ -552,8 +556,8 @@ CXX_GUARD_START
|
|||
#define _mSCRIPT_BIND_N_FUNCTION(NAME, RETURN, FUNCTION, DEFAULTS, NPARAMS, ...) \
|
||||
static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \
|
||||
UNUSED(ctx); \
|
||||
_mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
|
||||
if (mScriptListSize(&frame->arguments)) { \
|
||||
_mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->stack, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
|
||||
if (mScriptListSize(&frame->stack)) { \
|
||||
return false; \
|
||||
} \
|
||||
_mSCRIPT_CALL(RETURN, FUNCTION, NPARAMS); \
|
||||
|
@ -564,8 +568,8 @@ CXX_GUARD_START
|
|||
#define _mSCRIPT_BIND_VOID_FUNCTION(NAME, FUNCTION, DEFAULTS, NPARAMS, ...) \
|
||||
static bool _binding_ ## NAME(struct mScriptFrame* frame, void* ctx) { \
|
||||
UNUSED(ctx); \
|
||||
_mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->arguments, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
|
||||
if (mScriptListSize(&frame->arguments)) { \
|
||||
_mCALL(mSCRIPT_POP_ ## NPARAMS, &frame->stack, _mEVEN_ ## NPARAMS(__VA_ARGS__)); \
|
||||
if (mScriptListSize(&frame->stack)) { \
|
||||
return false; \
|
||||
} \
|
||||
_mSCRIPT_CALL_VOID(FUNCTION, NPARAMS); \
|
||||
|
|
|
@ -325,8 +325,7 @@ struct mScriptString {
|
|||
};
|
||||
|
||||
struct mScriptFrame {
|
||||
struct mScriptList arguments;
|
||||
struct mScriptList returnValues;
|
||||
struct mScriptList stack;
|
||||
// TODO: Exception/failure codes
|
||||
};
|
||||
|
||||
|
|
|
@ -10,3 +10,4 @@ Comment=Nintendo Game Boy Advance Emulator
|
|||
Categories=Game;Emulator;
|
||||
MimeType=application/x-gameboy-advance-rom;application/x-agb-rom;application/x-gba-rom;
|
||||
Keywords=emulator;Nintendo;advance;gba;Game Boy Advance;
|
||||
StartupWMClass=mGBA
|
||||
|
|
|
@ -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)
|
|
@ -1,34 +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.5;
|
||||
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);
|
||||
const float target_gamma = 2.0;
|
||||
const float display_gamma = 2.0;
|
||||
|
||||
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 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 = color * screen;
|
||||
gl_FragColor = pow(screen, vec4(1.0 / display_gamma + (darken_screen * 0.125)));
|
||||
screen = profile * screen;
|
||||
gl_FragColor = pow(screen, vec4(1.0 / display_gamma));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -6,6 +6,7 @@ passes=1
|
|||
|
||||
[pass.0]
|
||||
fragmentShader=gba-color.fs
|
||||
vertexShader=gba-color.vs
|
||||
blend=1
|
||||
width=-1
|
||||
height=-1
|
||||
|
@ -14,3 +15,10 @@ height=-1
|
|||
type=float
|
||||
default=0.5
|
||||
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)
|
||||
|
|
|
@ -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)
|
|
@ -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));
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -88,7 +88,7 @@ DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LSR1,
|
|||
}
|
||||
THUMB_NEUTRAL_S( , , cpu->gprs[rd]);)
|
||||
|
||||
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(ASR1,
|
||||
DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(ASR1,
|
||||
if (!immediate) {
|
||||
cpu->cpsr.c = ARM_SIGN(cpu->gprs[rm]);
|
||||
if (cpu->cpsr.c) {
|
||||
|
|
|
@ -37,7 +37,7 @@ static uint32_t _patchMakeKey(struct mCheatPatch* patch) {
|
|||
patchKey >>= 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
break;
|
||||
}
|
||||
// TODO: More than one segment
|
||||
if (patch->segment > 0) {
|
||||
|
@ -627,6 +627,9 @@ void mCheatAutosave(struct mCheatDevice* device) {
|
|||
if (!device->autosave) {
|
||||
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);
|
||||
if (!vf) {
|
||||
return;
|
||||
|
|
|
@ -239,20 +239,35 @@ bool mCoreAutoloadPatch(struct mCore* core) {
|
|||
if (!core->dirs.patch) {
|
||||
return false;
|
||||
}
|
||||
return core->loadPatch(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".ups", O_RDONLY)) ||
|
||||
core->loadPatch(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".ips", O_RDONLY)) ||
|
||||
core->loadPatch(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.patch, ".bps", O_RDONLY));
|
||||
struct VFile* vf = NULL;
|
||||
if (!vf) {
|
||||
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 success = true;
|
||||
bool success = !!core->dirs.cheats;
|
||||
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);
|
||||
if (vf) {
|
||||
struct mCheatDevice* device = core->cheatDevice(core);
|
||||
success = mCheatParseFile(device, vf);
|
||||
vf->close(vf);
|
||||
} else {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
if (!mCoreConfigGetIntValue(&core->config, "cheatAutosave", &cheatAuto) || cheatAuto) {
|
||||
|
|
|
@ -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) {
|
||||
if (!dir) {
|
||||
return NULL;
|
||||
}
|
||||
char name[PATH_MAX + 1] = "";
|
||||
snprintf(name, sizeof(name) - 1, "%s%s", dirs->baseName, suffix);
|
||||
return dir->openFile(dir, name, mode);
|
||||
|
|
|
@ -110,14 +110,14 @@ void mRTCGenericSourceInit(struct mRTCGenericSource* rtc, struct mCore* core) {
|
|||
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;
|
||||
integrator->state = enable;
|
||||
integrator->timeOn = 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;
|
||||
|
||||
if (integrator->state) {
|
||||
|
@ -127,7 +127,7 @@ static void mRumbleIntegratorSetRumble(struct mRumble* rumble, bool enable, uint
|
|||
integrator->state = enable;
|
||||
}
|
||||
|
||||
static void mRumbleIntegratorIntegrate(struct mRumble* rumble, uint32_t period) {
|
||||
static void _mRumbleIntegratorIntegrate(struct mRumble* rumble, uint32_t period) {
|
||||
if (!period) {
|
||||
return;
|
||||
}
|
||||
|
@ -144,7 +144,11 @@ static void mRumbleIntegratorIntegrate(struct mRumble* rumble, uint32_t period)
|
|||
|
||||
void mRumbleIntegratorInit(struct mRumbleIntegrator* integrator) {
|
||||
memset(integrator, 0, sizeof(*integrator));
|
||||
integrator->d.reset = mRumbleIntegratorReset;
|
||||
integrator->d.setRumble = mRumbleIntegratorSetRumble;
|
||||
integrator->d.integrate = mRumbleIntegratorIntegrate;
|
||||
integrator->d.reset = _mRumbleIntegratorReset;
|
||||
integrator->d.setRumble = _mRumbleIntegratorSetRumble;
|
||||
integrator->d.integrate = _mRumbleIntegratorIntegrate;
|
||||
}
|
||||
|
||||
void mRumbleIntegratorReset(struct mRumbleIntegrator* integrator) {
|
||||
_mRumbleIntegratorReset(&integrator->d, false);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
* 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/core/lockstep.h>
|
||||
|
||||
#ifndef DISABLE_THREADING
|
||||
#include <mgba/core/thread.h>
|
||||
#endif
|
||||
|
||||
void mLockstepInit(struct mLockstep* lockstep) {
|
||||
lockstep->attached = 0;
|
||||
lockstep->transferActive = 0;
|
||||
|
@ -19,4 +23,21 @@ void mLockstepDeinit(struct mLockstep* 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
|
||||
|
|
|
@ -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);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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"));
|
||||
mScriptFrameInit(&frame);
|
||||
if (context) {
|
||||
mScriptValueWrap(context, mScriptListAppend(&frame.arguments));
|
||||
mScriptValueWrap(context, mScriptListAppend(&frame.stack));
|
||||
}
|
||||
bool ok = mScriptContextInvoke(adapter->context, cb, &frame);
|
||||
if (ok && out && mScriptListSize(&frame.returnValues) == 1) {
|
||||
if (!mScriptCast(mSCRIPT_TYPE_MS_F32, mScriptListGetPointer(&frame.returnValues, 0), out)) {
|
||||
if (ok && out && mScriptListSize(&frame.stack) == 1) {
|
||||
if (!mScriptCast(mSCRIPT_TYPE_MS_F32, mScriptListGetPointer(&frame.stack, 0), out)) {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
@ -1278,8 +1278,8 @@ static uint8_t _readLuminance(struct GBALuminanceSource* luminance) {
|
|||
mScriptFrameInit(&frame);
|
||||
bool ok = mScriptContextInvoke(adapter->context, adapter->luminanceCb, &frame);
|
||||
struct mScriptValue out = {0};
|
||||
if (ok && mScriptListSize(&frame.returnValues) == 1) {
|
||||
if (!mScriptCast(mSCRIPT_TYPE_MS_U8, mScriptListGetPointer(&frame.returnValues, 0), &out)) {
|
||||
if (ok && mScriptListSize(&frame.stack) == 1) {
|
||||
if (!mScriptCast(mSCRIPT_TYPE_MS_U8, mScriptListGetPointer(&frame.stack, 0), &out)) {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -656,7 +656,7 @@ void mCoreThreadContinue(struct mCoreThread* threadContext) {
|
|||
if (threadContext->impl->requested) {
|
||||
threadContext->impl->state = mTHREAD_REQUEST;
|
||||
} else {
|
||||
threadContext->impl->state = mTHREAD_RUNNING;
|
||||
threadContext->impl->state = mTHREAD_RUNNING;
|
||||
}
|
||||
ConditionWake(&threadContext->impl->stateOnThreadCond);
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ bool FFmpegDecoderOpen(struct FFmpegDecoder* decoder, const char* infile) {
|
|||
codec = avcodec_find_decoder(context->codec_id);
|
||||
if (!codec) {
|
||||
FFmpegDecoderClose(decoder);
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
if (avcodec_open2(context, codec, NULL) < 0) {
|
||||
FFmpegDecoderClose(decoder);
|
||||
|
|
|
@ -893,7 +893,7 @@ void FFmpegEncoderSetInputFrameRate(struct FFmpegEncoder* encoder, int numerator
|
|||
|
||||
void FFmpegEncoderSetInputSampleRate(struct FFmpegEncoder* encoder, int sampleRate) {
|
||||
encoder->isampleRate = sampleRate;
|
||||
if (encoder->resampleContext) {
|
||||
if (encoder->resampleContext) {
|
||||
av_freep(&encoder->audioBuffer);
|
||||
#ifdef USE_LIBAVRESAMPLE
|
||||
avresample_close(encoder->resampleContext);
|
||||
|
|
|
@ -295,7 +295,7 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
|
|||
test.v.s = mCoreConfigGetValue(&runner->config, item->data.v.s);
|
||||
if (test.v.s && strcmp(test.v.s, v->v.s) == 0) {
|
||||
item->state = j;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case GUI_VARIANT_POINTER:
|
||||
|
|
|
@ -782,6 +782,9 @@ void mGUILoadInputMaps(struct mGUIRunner* runner) {
|
|||
size_t i;
|
||||
for (i = 0; runner->keySources[i].id; ++i) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ static void _updateList(const char* key, const char* value, void* user) {
|
|||
if (strncmp("medusa.", key, 7) == 0) {
|
||||
dotLoc = strchr(&key[7], '.');
|
||||
} else {
|
||||
dotLoc = strchr(key, '.');
|
||||
dotLoc = strchr(key, '.');
|
||||
}
|
||||
if (!dotLoc) {
|
||||
return;
|
||||
|
|
|
@ -339,17 +339,17 @@ bool mVideoLoggerRendererRunInjected(struct mVideoLogger* logger) {
|
|||
channel->injecting = true;
|
||||
bool res = mVideoLoggerRendererRun(logger, false);
|
||||
channel->injecting = false;
|
||||
return res;
|
||||
return res;
|
||||
}
|
||||
|
||||
void mVideoLoggerInjectionPoint(struct mVideoLogger* logger, enum mVideoLoggerInjectionPoint injectionPoint) {
|
||||
struct mVideoLogChannel* channel = logger->dataContext;
|
||||
channel->injectionPoint = injectionPoint;
|
||||
channel->injectionPoint = injectionPoint;
|
||||
}
|
||||
|
||||
void mVideoLoggerIgnoreAfterInjection(struct mVideoLogger* logger, uint32_t mask) {
|
||||
struct mVideoLogChannel* channel = logger->dataContext;
|
||||
channel->ignorePackets = mask;
|
||||
channel->ignorePackets = mask;
|
||||
}
|
||||
|
||||
static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length) {
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <mgba/internal/sm83/sm83.h>
|
||||
#include <mgba/internal/sm83/debugger/debugger.h>
|
||||
#include <mgba-util/crc32.h>
|
||||
#include <mgba-util/md5.h>
|
||||
#include <mgba-util/memory.h>
|
||||
#include <mgba-util/patch.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
@ -152,7 +153,7 @@ static bool _GBCoreInit(struct mCore* core) {
|
|||
#ifdef ENABLE_VFS
|
||||
mDirectorySetInit(&core->dirs);
|
||||
#endif
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -529,6 +530,15 @@ static void _GBCoreChecksum(const struct mCore* core, void* data, enum mCoreChec
|
|||
case mCHECKSUM_CRC32:
|
||||
memcpy(data, &gb->romCrc32, sizeof(gb->romCrc32));
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -267,7 +267,7 @@ void GBVideoProxyRendererDrawRange(struct GBVideoRenderer* renderer, int startX,
|
|||
_copyExtraState(proxyRenderer);
|
||||
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) {
|
||||
|
|
|
@ -152,7 +152,7 @@ bool GBLoadGBX(struct GBXMetadata* metadata, struct VFile* vf) {
|
|||
if (memcmp(footer, "MBC1", 4) == 0) {
|
||||
metadata->mapperVars.u8[0] = 5;
|
||||
} else if (memcmp(footer, "MB1M", 4) == 0) {
|
||||
metadata->mapperVars.u8[0] = 4;
|
||||
metadata->mapperVars.u8[0] = 4;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -483,12 +483,10 @@ void GBApplyPatch(struct GB* gb, struct Patch* patch) {
|
|||
mappedMemoryFree(newRom, GB_SIZE_CART_MAX);
|
||||
return;
|
||||
}
|
||||
if (gb->romVf) {
|
||||
if (gb->romVf && gb->isPristine) {
|
||||
#ifndef FIXED_ROM_BUFFER
|
||||
gb->romVf->unmap(gb->romVf, gb->memory.rom, gb->pristineRomSize);
|
||||
#endif
|
||||
gb->romVf->close(gb->romVf);
|
||||
gb->romVf = NULL;
|
||||
}
|
||||
gb->isPristine = false;
|
||||
if (gb->memory.romBase == gb->memory.rom) {
|
||||
|
@ -894,7 +892,7 @@ int GBValidModels(const uint8_t* bank0) {
|
|||
} else if (cart->cgb == 0xC0) {
|
||||
models = GB_MODEL_CGB;
|
||||
} else {
|
||||
models = GB_MODEL_MGB;
|
||||
models = GB_MODEL_MGB;
|
||||
}
|
||||
if (cart->sgb == 0x03 && cart->oldLicensee == 0x33) {
|
||||
models |= GB_MODEL_SGB;
|
||||
|
|
|
@ -163,7 +163,7 @@ static bool _isMulticart(const uint8_t* mem) {
|
|||
success = GBIsROM(vf);
|
||||
vf->close(vf);
|
||||
}
|
||||
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
|
|
@ -131,7 +131,7 @@ static void _latchTAMA6Rtc(struct mRTCSource* rtc, struct GBTAMA5State* tama5, t
|
|||
timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 24) / 10;
|
||||
} else {
|
||||
timerRegs[GBTAMA6_RTC_PA0_HOUR_1] = (diff % 12) % 10;
|
||||
timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 12) / 10 + (diff / 12) * 2;
|
||||
timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 12) / 10 + (diff / 12) * 2;
|
||||
}
|
||||
t /= 24;
|
||||
t += diff / 24;
|
||||
|
|
|
@ -349,7 +349,7 @@ void _GBHitek(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
break;
|
||||
case 0x300:
|
||||
// See hhugboy src/memory/mbc/MbcUnlHitek.cpp for commentary on this return
|
||||
return;
|
||||
return;
|
||||
}
|
||||
_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) {
|
||||
if (address > 0x2100 && address < 0x3000) {
|
||||
return;
|
||||
}
|
||||
_GBMBC5(gb, address, value);
|
||||
if (address > 0x2100 && address < 0x3000) {
|
||||
return;
|
||||
}
|
||||
_GBMBC5(gb, address, value);
|
||||
}
|
||||
|
||||
void _GBSachen(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
|
|
|
@ -1017,8 +1017,6 @@ void _pristineCow(struct GB* gb) {
|
|||
}
|
||||
if (gb->romVf) {
|
||||
gb->romVf->unmap(gb->romVf, gb->memory.rom, gb->memory.romSize);
|
||||
gb->romVf->close(gb->romVf);
|
||||
gb->romVf = NULL;
|
||||
}
|
||||
gb->memory.rom = newRom;
|
||||
GBMBCSwitchBank(gb, gb->memory.currentBank);
|
||||
|
|
|
@ -755,7 +755,7 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
|
|||
video->ly = 0;
|
||||
video->p->memory.io[GB_REG_LY] = 0;
|
||||
video->renderer->writePalette(video->renderer, 0, video->dmgPalette[0]);
|
||||
|
||||
|
||||
mTimingDeschedule(&video->p->timing, &video->modeEvent);
|
||||
mTimingDeschedule(&video->p->timing, &video->frameEvent);
|
||||
mTimingSchedule(&video->p->timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH << 1);
|
||||
|
|
|
@ -6,6 +6,7 @@ set(SOURCE_FILES
|
|||
cart/ereader.c
|
||||
cart/gpio.c
|
||||
cart/matrix.c
|
||||
cart/unlicensed.c
|
||||
cart/vfame.c
|
||||
cheats.c
|
||||
cheats/codebreaker.c
|
||||
|
@ -31,7 +32,6 @@ set(SOURCE_FILES
|
|||
sharkport.c
|
||||
sio.c
|
||||
sio/gbp.c
|
||||
sio/joybus.c
|
||||
timer.c
|
||||
video.c)
|
||||
|
||||
|
|
|
@ -309,6 +309,7 @@ void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles) {
|
|||
if (GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_CUSTOM) {
|
||||
dma->when = mTimingCurrentTime(&audio->p->timing) - cycles;
|
||||
dma->nextCount = 4;
|
||||
GBADMARecalculateCycles(audio->p);
|
||||
GBADMASchedule(audio->p, channel->dmaSource, dma);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
uint32_t registers = gba->cpu->gprs[0];
|
||||
struct ARMCore* cpu = gba->cpu;
|
||||
|
@ -445,7 +421,7 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
|
|||
bool useStall = false;
|
||||
switch (immediate) {
|
||||
case GBA_SWI_SOFT_RESET:
|
||||
_SoftReset(gba);
|
||||
ARMRaiseSWI(cpu);
|
||||
break;
|
||||
case GBA_SWI_REGISTER_RAM_RESET:
|
||||
_RegisterRamReset(gba);
|
||||
|
|
|
@ -47,6 +47,20 @@ void GBAHardwareInit(struct GBACartridgeHardware* hw, uint16_t* base) {
|
|||
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) {
|
||||
hw->devices = HW_NONE | (hw->devices & HW_GB_PLAYER_DETECTION);
|
||||
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);
|
||||
state->hw.lightSample = hw->lightSample;
|
||||
flags1 = GBASerializedHWFlags1SetLightEdge(flags1, hw->lightEdge);
|
||||
flags1 = GBASerializedHWFlags1SetLightCounter(flags1, hw->lightCounter);
|
||||
STORE_16(flags1, 0, &state->hw.flags1);
|
||||
|
||||
GBASerializedHWFlags2 flags2 = 0;
|
||||
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 = 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;
|
||||
}
|
||||
|
@ -502,6 +516,19 @@ void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, const struct GBASer
|
|||
LOAD_16(hw->direction, 0, &state->hw.pinDirection);
|
||||
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.transferStep, 0, &state->hw.rtcTransferStep);
|
||||
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->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.txPosition = GBASerializedHWFlags2GetGbpTxPosition(state->hw.flags2);
|
||||
|
||||
uint32_t when;
|
||||
LOAD_32(when, 0, &state->hw.gbpNextEvent);
|
||||
LOAD_32(when, 0, &state->hw.sioNextEvent);
|
||||
if (hw->devices & HW_GB_PLAYER) {
|
||||
GBASIOSetDriver(&hw->p->sio, &hw->p->sio.gbp.d, GBA_SIO_NORMAL_32);
|
||||
if (hw->p->memory.io[GBA_REG(SIOCNT)] & 0x0080) {
|
||||
mTimingSchedule(&hw->p->timing, &hw->p->sio.gbp.event, when);
|
||||
}
|
||||
GBASIOSetDriver(&hw->p->sio, &hw->p->sio.gbp.d);
|
||||
}
|
||||
if ((hw->p->memory.io[GBA_REG(SIOCNT)] & 0x0080) && when < 0x20000) {
|
||||
mTimingSchedule(&hw->p->timing, &hw->p->sio.completeEvent, when);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,7 +109,7 @@ void GBAMatrixSerialize(const struct GBA* gba, struct GBASerializedState* state)
|
|||
|
||||
int 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;
|
||||
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.vaddr = i << 9;
|
||||
_remapMatrix(gba);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1,42 +1,50 @@
|
|||
/* Copyright (c) 2016 taizou
|
||||
* 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/vfame.h>
|
||||
#include <mgba/internal/gba/cart/unlicensed.h>
|
||||
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
#include <mgba/internal/gba/memory.h>
|
||||
|
||||
static const uint8_t ADDRESS_REORDERING[4][16] = {
|
||||
{ 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 }
|
||||
#define DIGIMON_SAPPHIRE_CHINESE_CRC32 0x793A328F
|
||||
|
||||
static const uint8_t ADDRESS_REORDERING[][3][16] = {
|
||||
[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 },
|
||||
{ 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 }
|
||||
};
|
||||
static const uint8_t ADDRESS_REORDERING_ALTERNATE[4][16] = {
|
||||
{ 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 VALUE_REORDERING[4][16] = {
|
||||
{ 5, 4, 3, 2, 1, 0, 7, 6 },
|
||||
{ 3, 2, 1, 0, 7, 6, 5, 4 },
|
||||
{ 1, 0, 7, 6, 5, 4, 3, 2 }
|
||||
};
|
||||
static const uint8_t VALUE_REORDERING_GEORGE[4][16] = {
|
||||
{ 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 uint8_t VALUE_REORDERING[][3][8] = {
|
||||
[VFAME_STANDARD] = {
|
||||
{ 5, 4, 3, 2, 1, 0, 7, 6 },
|
||||
{ 3, 2, 1, 0, 7, 6, 5, 4 },
|
||||
{ 1, 0, 7, 6, 5, 4, 3, 2 }
|
||||
},
|
||||
[VFAME_GEORGE] = {
|
||||
{ 3, 0, 7, 2, 1, 4, 5, 6 },
|
||||
{ 1, 4, 3, 0, 5, 6, 7, 2 },
|
||||
{ 5, 2, 1, 6, 7, 0, 3, 4 }
|
||||
},
|
||||
[VFAME_ALTERNATE] = {
|
||||
{ 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 };
|
||||
|
@ -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 int _reorderBits(uint32_t value, const uint8_t* reordering, int reorderLength);
|
||||
|
||||
void GBAVFameInit(struct GBAVFameCart* cart) {
|
||||
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;
|
||||
|
||||
bool GBAVFameDetect(struct GBAVFameCart* cart, uint32_t* rom, size_t romSize, uint32_t crc32) {
|
||||
// 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..
|
||||
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
|
||||
// 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) {
|
||||
detected = true;
|
||||
cart->cartType = VFAME_STANDARD;
|
||||
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
|
||||
// Other undumped games may have similar differences
|
||||
if (memcmp("George Sango", &((struct GBACartridge*) rom)->title, 12) == 0) {
|
||||
detected = true;
|
||||
cart->cartType = VFAME_GEORGE;
|
||||
mLOG(GBA_MEM, INFO, "George mode");
|
||||
} else if (crc32 == DIGIMON_SAPPHIRE_CHINESE_CRC32) {
|
||||
// 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
|
||||
detected = true;
|
||||
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
|
||||
|
@ -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) {
|
||||
address &= 0x00FFFFFF;
|
||||
// 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
|
||||
if (address >= 0xFFF8 && address <= 0xFFFC) {
|
||||
|
@ -268,25 +279,14 @@ static uint32_t _modifySramAddress(enum GBAVFameCartType type, uint32_t address,
|
|||
mode &= 0x3;
|
||||
if (mode == 0) {
|
||||
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) {
|
||||
int reorderType = (mode & 0xF) >> 2;
|
||||
if (reorderType != 0) {
|
||||
if (type == VFAME_GEORGE) {
|
||||
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);
|
||||
}
|
||||
value = _reorderBits(value, VALUE_REORDERING[type][reorderType - 1], 8);
|
||||
}
|
||||
if (mode & 0x80) {
|
||||
value ^= 0xAA;
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#ifdef USE_ELF
|
||||
#include <mgba-util/elf-read.h>
|
||||
#endif
|
||||
#include <mgba-util/md5.h>
|
||||
#include <mgba-util/memory.h>
|
||||
#include <mgba-util/patch.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
@ -667,6 +668,9 @@ static size_t _GBACoreROMSize(const struct mCore* core) {
|
|||
if (gba->romVf) {
|
||||
return gba->romVf->size(gba->romVf);
|
||||
}
|
||||
if (gba->mbVf) {
|
||||
return gba->mbVf->size(gba->mbVf);
|
||||
}
|
||||
return gba->pristineRomSize;
|
||||
}
|
||||
|
||||
|
@ -676,6 +680,19 @@ static void _GBACoreChecksum(const struct mCore* core, void* data, enum mCoreChe
|
|||
case mCHECKSUM_CRC32:
|
||||
memcpy(data, &gba->romCrc32, sizeof(gba->romCrc32));
|
||||
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;
|
||||
}
|
||||
|
@ -842,7 +859,21 @@ static bool _GBACoreLoadExtraState(struct mCore* core, const struct mStateExtdat
|
|||
if (type == gba->video.renderer->rendererId(gba->video.renderer)) {
|
||||
ok = gba->video.renderer->loadState(gba->video.renderer,
|
||||
(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) {
|
||||
ok = false;
|
||||
|
@ -868,6 +899,27 @@ static bool _GBACoreSaveExtraState(struct mCore* core, struct mStateExtdata* ext
|
|||
}
|
||||
if (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;
|
||||
|
@ -927,9 +979,8 @@ static void _GBACoreSetPeripheral(struct mCore* core, int type, void* periph) {
|
|||
case mPERIPH_GBA_LUMINANCE:
|
||||
gba->luminanceSource = periph;
|
||||
break;
|
||||
case mPERIPH_GBA_BATTLECHIP_GATE:
|
||||
GBASIOSetDriver(&gba->sio, periph, GBA_SIO_MULTI);
|
||||
GBASIOSetDriver(&gba->sio, periph, GBA_SIO_NORMAL_32);
|
||||
case mPERIPH_GBA_LINK_PORT:
|
||||
GBASIOSetDriver(&gba->sio, periph);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
|
|
|
@ -256,15 +256,21 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
|
|||
if (info->count == info->nextCount) {
|
||||
if (width == 4) {
|
||||
cycles += memory->waitstatesNonseq32[sourceRegion] + memory->waitstatesNonseq32[destRegion];
|
||||
info->cycles = memory->waitstatesSeq32[sourceRegion] + memory->waitstatesSeq32[destRegion];
|
||||
} else {
|
||||
cycles += memory->waitstatesNonseq16[sourceRegion] + memory->waitstatesNonseq16[destRegion];
|
||||
info->cycles = memory->waitstatesSeq16[sourceRegion] + memory->waitstatesSeq16[destRegion];
|
||||
}
|
||||
} else {
|
||||
if (width == 4) {
|
||||
cycles += memory->waitstatesSeq32[sourceRegion] + memory->waitstatesSeq32[destRegion];
|
||||
} else {
|
||||
cycles += memory->waitstatesSeq16[sourceRegion] + memory->waitstatesSeq16[destRegion];
|
||||
// Crossed region boundary; recalculate cached cycles
|
||||
if (UNLIKELY(!(source & 0x00FFFFFC) || !(dest & 0x00FFFFFC))) {
|
||||
if (width == 4) {
|
||||
info->cycles = memory->waitstatesSeq32[sourceRegion] + memory->waitstatesSeq32[destRegion];
|
||||
} else {
|
||||
info->cycles = memory->waitstatesSeq16[sourceRegion] + memory->waitstatesSeq16[destRegion];
|
||||
}
|
||||
}
|
||||
cycles += info->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 |= memory->dmaTransferRegister << 16;
|
||||
}
|
||||
if (destRegion == GBA_REGION_ROM2_EX) {
|
||||
if (UNLIKELY(destRegion == GBA_REGION_ROM2_EX)) {
|
||||
if (memory->savedata.type == GBA_SAVEDATA_AUTODETECT) {
|
||||
mLOG(GBA_MEM, INFO, "Detected EEPROM savegame");
|
||||
GBASavedataInitEEPROM(&memory->savedata);
|
||||
|
@ -313,9 +319,11 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
|
|||
int i;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
struct GBADMA* dma = &memory->dma[i];
|
||||
int32_t time = dma->when - info->when;
|
||||
if (time < 0 && GBADMARegisterIsEnable(dma->reg) && dma->nextCount) {
|
||||
dma->when = info->when;
|
||||
if (GBADMARegisterIsEnable(dma->reg) && dma->nextCount) {
|
||||
int32_t time = 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);
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,28 +33,25 @@ enum {
|
|||
BATTLECHIP_CONTINUE = 0xFFFF,
|
||||
};
|
||||
|
||||
static bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver);
|
||||
static uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value);
|
||||
|
||||
static void _battlechipTransfer(struct GBASIOBattlechipGate* gate);
|
||||
static void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate);
|
||||
static bool GBASIOBattlechipGateInit(struct GBASIODriver* driver);
|
||||
static uint16_t GBASIOBattlechipGateWriteSIOCNT(struct GBASIODriver* driver, uint16_t value);
|
||||
static bool GBASIOBattlechipGateHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode);
|
||||
static int GBASIOBattlechipGateConnectedDevices(struct GBASIODriver* driver);
|
||||
static void GBASIOBattlechipGateFinishMultiplayer(struct GBASIODriver* driver, uint16_t data[4]);
|
||||
|
||||
void GBASIOBattlechipGateCreate(struct GBASIOBattlechipGate* gate) {
|
||||
gate->d.init = NULL;
|
||||
gate->d.deinit = NULL;
|
||||
gate->d.load = GBASIOBattlechipGateLoad;
|
||||
gate->d.unload = NULL;
|
||||
gate->d.writeRegister = GBASIOBattlechipGateWriteRegister;
|
||||
|
||||
gate->event.context = gate;
|
||||
gate->event.callback = _battlechipTransferEvent;
|
||||
gate->event.priority = 0x80;
|
||||
memset(&gate->d, 0, sizeof(gate->d));
|
||||
gate->d.init = GBASIOBattlechipGateInit;
|
||||
gate->d.writeSIOCNT = GBASIOBattlechipGateWriteSIOCNT;
|
||||
gate->d.handlesMode = GBASIOBattlechipGateHandlesMode;
|
||||
gate->d.connectedDevices = GBASIOBattlechipGateConnectedDevices;
|
||||
gate->d.finishMultiplayer = GBASIOBattlechipGateFinishMultiplayer;
|
||||
|
||||
gate->chipId = 0;
|
||||
gate->flavor = GBA_FLAVOR_BATTLECHIP_GATE;
|
||||
}
|
||||
|
||||
bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver) {
|
||||
bool GBASIOBattlechipGateInit(struct GBASIODriver* driver) {
|
||||
struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
|
||||
gate->state = BATTLECHIP_STATE_SYNC;
|
||||
gate->data[0] = 0x00FE;
|
||||
|
@ -62,58 +59,34 @@ bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver) {
|
|||
return true;
|
||||
}
|
||||
|
||||
uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) {
|
||||
struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
|
||||
switch (address) {
|
||||
case GBA_REG_SIOCNT:
|
||||
value &= ~0xC;
|
||||
value |= 0x8;
|
||||
if (value & 0x80) {
|
||||
_battlechipTransfer(gate);
|
||||
}
|
||||
break;
|
||||
case GBA_REG_SIOMLT_SEND:
|
||||
break;
|
||||
case GBA_REG_RCNT:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
uint16_t GBASIOBattlechipGateWriteSIOCNT(struct GBASIODriver* driver, uint16_t value) {
|
||||
UNUSED(driver);
|
||||
value &= ~0xC;
|
||||
value |= 0x8;
|
||||
return value;
|
||||
}
|
||||
|
||||
void _battlechipTransfer(struct GBASIOBattlechipGate* gate) {
|
||||
int32_t cycles;
|
||||
if (gate->d.p->mode == GBA_SIO_NORMAL_32) {
|
||||
cycles = GBA_ARM7TDMI_FREQUENCY / 0x40000;
|
||||
} else {
|
||||
cycles = GBASIOCyclesPerTransfer[GBASIOMultiplayerGetBaud(gate->d.p->siocnt)][1];
|
||||
static bool GBASIOBattlechipGateHandlesMode(struct GBASIODriver* driver, enum GBASIOMode mode) {
|
||||
UNUSED(driver);
|
||||
switch (mode) {
|
||||
case GBA_SIO_NORMAL_32:
|
||||
case GBA_SIO_MULTI:
|
||||
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) {
|
||||
UNUSED(timing);
|
||||
struct GBASIOBattlechipGate* gate = user;
|
||||
static int GBASIOBattlechipGateConnectedDevices(struct GBASIODriver* driver) {
|
||||
UNUSED(driver);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (gate->d.p->mode == GBA_SIO_NORMAL_32) {
|
||||
gate->d.p->p->memory.io[GBA_REG(SIODATA32_LO)] = 0;
|
||||
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;
|
||||
}
|
||||
static void GBASIOBattlechipGateFinishMultiplayer(struct GBASIODriver* driver, uint16_t data[4]) {
|
||||
struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
|
||||
|
||||
uint16_t cmd = gate->d.p->p->memory.io[GBA_REG(SIOMLT_SEND)];
|
||||
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);
|
||||
|
||||
|
@ -146,7 +119,7 @@ void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cycle
|
|||
case 0xA3D0:
|
||||
// EXE 4
|
||||
case 0xA6C0:
|
||||
mLOG(GBA_BATTLECHIP, DEBUG, "Resync detected");
|
||||
mLOG(GBA_BATTLECHIP, DEBUG, "Resync detected");
|
||||
gate->state = BATTLECHIP_STATE_SYNC;
|
||||
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);
|
||||
++gate->state;
|
||||
|
||||
gate->d.p->p->memory.io[GBA_REG(SIOMULTI1)] = reply;
|
||||
|
||||
if (GBASIOMultiplayerIsIrq(gate->d.p->siocnt)) {
|
||||
GBARaiseIRQ(gate->d.p->p, GBA_IRQ_SIO, cyclesLate);
|
||||
}
|
||||
data[0] = cmd;
|
||||
data[1] = reply;
|
||||
data[2] = 0xFFFF;
|
||||
data[3] = 0xFFFF;
|
||||
}
|
||||
|
|
|
@ -324,7 +324,7 @@ void GBAVideoProxyRendererSaveState(struct GBAVideoRenderer* renderer, void** st
|
|||
proxyRenderer->logger->stateSize = 0;
|
||||
} else {
|
||||
proxyRenderer->backend->saveState(proxyRenderer->backend, state, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GBAVideoProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
|
||||
|
|
|
@ -140,6 +140,9 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
|
|||
|
||||
void GBAUnloadROM(struct GBA* gba) {
|
||||
GBAMemoryClearAGBPrint(gba);
|
||||
if (gba->memory.unl.type) {
|
||||
GBAUnlCartUnload(gba);
|
||||
}
|
||||
if (gba->memory.rom && !gba->isPristine) {
|
||||
if (gba->yankedRomSize) {
|
||||
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
|
||||
gba->memory.hw.devices &= ~HW_GB_PLAYER;
|
||||
if (gba->sio.drivers.normal == &gba->sio.gbp.d) {
|
||||
GBASIOSetDriver(&gba->sio, NULL, GBA_SIO_NORMAL_32);
|
||||
if (gba->sio.driver == &gba->sio.gbp.d) {
|
||||
GBASIOSetDriver(&gba->sio, NULL);
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
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
|
||||
return true;
|
||||
}
|
||||
|
@ -557,16 +560,14 @@ void GBAApplyPatch(struct GBA* gba, struct Patch* patch) {
|
|||
mappedMemoryFree(newRom, GBA_SIZE_ROM0);
|
||||
return;
|
||||
}
|
||||
if (gba->romVf) {
|
||||
if (gba->memory.rom) {
|
||||
#ifndef FIXED_ROM_BUFFER
|
||||
if (!gba->isPristine) {
|
||||
mappedMemoryFree(gba->memory.rom, GBA_SIZE_ROM0);
|
||||
mappedMemoryFree(gba->memory.rom, gba->memory.romSize);
|
||||
} else {
|
||||
gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->pristineRomSize);
|
||||
}
|
||||
#endif
|
||||
gba->romVf->close(gba->romVf);
|
||||
gba->romVf = NULL;
|
||||
}
|
||||
gba->isPristine = false;
|
||||
gba->memory.rom = newRom;
|
||||
|
|
|
@ -20,7 +20,7 @@ const uint8_t hleBios[GBA_SIZE_BIOS] = {
|
|||
0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x04, 0x40, 0xbd, 0xe8,
|
||||
0x93, 0xf0, 0x29, 0xe3, 0x00, 0x08, 0xbd, 0xe8, 0x0b, 0xf0, 0x69, 0xe1,
|
||||
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,
|
||||
0xcc, 0x01, 0x00, 0x00, 0xc4, 0x01, 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,
|
||||
0x04, 0xb0, 0x5b, 0xe2, 0xfd, 0xff, 0xff, 0x8a, 0x1e, 0xff, 0x2f, 0xe1,
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
0x9c, 0xc0, 0xde, 0xe5, 0xa5, 0x00, 0x5c, 0xe3, 0x04, 0x00, 0x00, 0x1a,
|
||||
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,
|
||||
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
|
||||
};
|
||||
|
|
|
@ -129,7 +129,6 @@ subs pc, lr, #4
|
|||
.word 0x03A0E004
|
||||
|
||||
@ Unimplemented
|
||||
SoftReset:
|
||||
RegisterRamReset:
|
||||
Stop:
|
||||
GetBiosChecksum:
|
||||
|
@ -318,13 +317,14 @@ mov lr, #0x8000003
|
|||
ldrb r1, [lr], #-3
|
||||
cmp r1, #0
|
||||
movne r1, #0
|
||||
bxne lr
|
||||
bne 1f
|
||||
ldr lr, =0x20000C0
|
||||
ldr r1, [lr]
|
||||
cmp r1, #0
|
||||
mov r1, #0
|
||||
bxne lr
|
||||
bne 1f
|
||||
sub lr, #0xC0
|
||||
1:
|
||||
bx lr
|
||||
.word 0
|
||||
.word 0xE129F000
|
||||
|
@ -357,3 +357,44 @@ ldmdb sp!, {r12, lr}
|
|||
subs pc, lr, #4
|
||||
.word 0
|
||||
.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
|
||||
|
|
112
src/gba/io.c
112
src/gba/io.c
|
@ -214,8 +214,8 @@ static const int _isRSpecialRegister[GBA_REG(INTERNAL_MAX)] = {
|
|||
/* 10 */ 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
/* 11 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* SIO */
|
||||
/* 12 */ 1, 1, 1, 1, 1, 0, 0, 0,
|
||||
/* 13 */ 1, 1, 1, 0, 0, 0, 0, 0,
|
||||
/* 12 */ 1, 1, 1, 1, 0, 0, 0, 0,
|
||||
/* 13 */ 1, 1, 0, 0, 0, 0, 0, 0,
|
||||
/* 14 */ 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
/* 15 */ 1, 1, 1, 1, 1, 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;
|
||||
case GBA_REG_SOUND1CNT_HI:
|
||||
GBAAudioWriteSOUND1CNT_HI(&gba->audio, value);
|
||||
value &= 0xFFC0;
|
||||
break;
|
||||
case GBA_REG_SOUND1CNT_X:
|
||||
GBAAudioWriteSOUND1CNT_X(&gba->audio, value);
|
||||
value &= 0x47FF;
|
||||
value &= 0x4000;
|
||||
break;
|
||||
case GBA_REG_SOUND2CNT_LO:
|
||||
GBAAudioWriteSOUND2CNT_LO(&gba->audio, value);
|
||||
value &= 0xFFC0;
|
||||
break;
|
||||
case GBA_REG_SOUND2CNT_HI:
|
||||
GBAAudioWriteSOUND2CNT_HI(&gba->audio, value);
|
||||
value &= 0x47FF;
|
||||
value &= 0x4000;
|
||||
break;
|
||||
case GBA_REG_SOUND3CNT_LO:
|
||||
GBAAudioWriteSOUND3CNT_LO(&gba->audio, value);
|
||||
|
@ -344,16 +346,15 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
|
|||
break;
|
||||
case GBA_REG_SOUND3CNT_HI:
|
||||
GBAAudioWriteSOUND3CNT_HI(&gba->audio, value);
|
||||
value &= 0xE03F;
|
||||
value &= 0xE000;
|
||||
break;
|
||||
case GBA_REG_SOUND3CNT_X:
|
||||
GBAAudioWriteSOUND3CNT_X(&gba->audio, value);
|
||||
// TODO: The low bits need to not be readable, but still 8-bit writable
|
||||
value &= 0x47FF;
|
||||
value &= 0x4000;
|
||||
break;
|
||||
case GBA_REG_SOUND4CNT_LO:
|
||||
GBAAudioWriteSOUND4CNT_LO(&gba->audio, value);
|
||||
value &= 0xFF3F;
|
||||
value &= 0xFF00;
|
||||
break;
|
||||
case GBA_REG_SOUND4CNT_HI:
|
||||
GBAAudioWriteSOUND4CNT_HI(&gba->audio, value);
|
||||
|
@ -482,6 +483,7 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
|
|||
|
||||
// SIO
|
||||
case GBA_REG_SIOCNT:
|
||||
value &= 0x7FFF;
|
||||
GBASIOWriteSIOCNT(&gba->sio, value);
|
||||
break;
|
||||
case GBA_REG_RCNT:
|
||||
|
@ -585,9 +587,96 @@ void GBAIOWrite8(struct GBA* gba, uint32_t address, uint8_t value) {
|
|||
if (address > GBA_SIZE_IO) {
|
||||
return;
|
||||
}
|
||||
uint16_t value16 = value << (8 * (address & 1));
|
||||
value16 |= (gba->memory.io[(address & (GBA_SIZE_IO - 1)) >> 1]) & ~(0xFF << (8 * (address & 1)));
|
||||
GBAIOWrite(gba, address & 0xFFFFFFFE, value16);
|
||||
uint16_t 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) {
|
||||
|
@ -990,6 +1079,7 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
|||
LOAD_32(gba->dmaPC, 0, &state->dmaBlockPC);
|
||||
LOAD_32(gba->bus, 0, &state->bus);
|
||||
|
||||
GBADMARecalculateCycles(gba);
|
||||
GBADMAUpdate(gba);
|
||||
GBAHardwareDeserialize(&gba->memory.hw, state);
|
||||
}
|
||||
|
|
|
@ -93,7 +93,7 @@ void GBAMemoryInit(struct GBA* gba) {
|
|||
gba->memory.iwram = &gba->memory.wram[GBA_SIZE_EWRAM >> 2];
|
||||
|
||||
GBADMAInit(gba);
|
||||
GBAVFameInit(&gba->memory.vfame);
|
||||
GBAUnlCartInit(gba);
|
||||
|
||||
gba->memory.ereader.p = gba;
|
||||
gba->memory.ereader.dots = NULL;
|
||||
|
@ -138,7 +138,18 @@ void GBAMemoryReset(struct GBA* gba) {
|
|||
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);
|
||||
GBAUnlCartReset(gba);
|
||||
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]; \
|
||||
if ((address & (GBA_SIZE_ROM0 - 4)) < memory->romSize) { \
|
||||
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); \
|
||||
} else { \
|
||||
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];
|
||||
if ((address & (GBA_SIZE_ROM0 - 2)) < memory->romSize) {
|
||||
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);
|
||||
} else if ((address & (GBA_SIZE_ROM0 - 2)) >= AGB_PRINT_BASE) {
|
||||
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);
|
||||
} else if ((address & (GBA_SIZE_ROM0 - 2)) < memory->romSize) {
|
||||
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);
|
||||
} else {
|
||||
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];
|
||||
if ((address & (GBA_SIZE_ROM0 - 1)) < memory->romSize) {
|
||||
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);
|
||||
} else {
|
||||
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);
|
||||
|
||||
#define STORE_SRAM \
|
||||
if (address & 0x3) { \
|
||||
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); \
|
||||
}
|
||||
GBAStore8(cpu, address, value >> (8 * (address & 3)), cycleCounter);
|
||||
|
||||
#define STORE_BAD \
|
||||
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;
|
||||
case GBA_REGION_ROM0:
|
||||
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);
|
||||
break;
|
||||
}
|
||||
|
@ -969,6 +973,10 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
|
|||
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);
|
||||
break;
|
||||
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:
|
||||
if (address & 1) {
|
||||
mLOG(GBA_MEM, GAME_ERROR, "Unaligned SRAM Store16: 0x%08X", address);
|
||||
break;
|
||||
value >>= 8;
|
||||
}
|
||||
GBAStore8(cpu, address, value, cycleCounter);
|
||||
GBAStore8(cpu, address | 1, value, cycleCounter);
|
||||
break;
|
||||
default:
|
||||
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) {
|
||||
GBASavedataWriteFlash(&memory->savedata, address, value);
|
||||
} else if (memory->savedata.type == GBA_SAVEDATA_SRAM) {
|
||||
if (memory->vfame.cartType) {
|
||||
GBAVFameSramWrite(&memory->vfame, address, value, memory->savedata.data);
|
||||
if (memory->unl.type) {
|
||||
GBAUnlCartWriteSRAM(gba, address & 0xFFFF, value);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if (gba->performingDMA) {
|
||||
GBADMARecalculateCycles(gba);
|
||||
}
|
||||
}
|
||||
|
||||
void GBAAdjustEWRAMWaitstates(struct GBA* gba, uint16_t parameters) {
|
||||
|
@ -1894,8 +1905,6 @@ void _pristineCow(struct GBA* gba) {
|
|||
}
|
||||
if (gba->romVf) {
|
||||
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.hw.gpioBase = &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1];
|
||||
|
|
|
@ -377,6 +377,12 @@ void GBAOverrideApplyDefaults(struct GBA* gba, const struct Configuration* overr
|
|||
struct GBACartridgeOverride override = { .idleLoop = GBA_IDLE_LOOP_NONE };
|
||||
const struct GBACartridge* cart = (const struct GBACartridge*) gba->memory.rom;
|
||||
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));
|
||||
|
||||
static const uint32_t pokemonTable[] = {
|
||||
|
|
|
@ -539,7 +539,7 @@ static const char* const _renderWindow =
|
|||
"}\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"
|
||||
" }\n"
|
||||
" return crop(interpolate(top, bottom));\n"
|
||||
|
|
|
@ -45,7 +45,7 @@ static void _ashesToAshes(struct mTiming* timing, void* user, uint32_t cyclesLat
|
|||
|
||||
void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) {
|
||||
savedata->type = GBA_SAVEDATA_AUTODETECT;
|
||||
savedata->data = 0;
|
||||
savedata->data = NULL;
|
||||
savedata->command = EEPROM_COMMAND_NULL;
|
||||
savedata->flashState = FLASH_STATE_RAW;
|
||||
savedata->vf = vf;
|
||||
|
@ -63,6 +63,11 @@ void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) {
|
|||
savedata->dust.callback = _ashesToAshes;
|
||||
}
|
||||
|
||||
void GBASavedataReset(struct GBASavedata* savedata) {
|
||||
savedata->command = EEPROM_COMMAND_NULL;
|
||||
savedata->flashState = FLASH_STATE_RAW;
|
||||
}
|
||||
|
||||
void GBASavedataDeinit(struct GBASavedata* savedata) {
|
||||
if (savedata->vf) {
|
||||
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) {
|
||||
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];
|
||||
}
|
||||
|
|
|
@ -32,8 +32,17 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) {
|
|||
STORE_64LE(gba->timing.globalCycles, 0, &state->globalCycles);
|
||||
|
||||
if (gba->memory.rom) {
|
||||
state->id = ((struct GBACartridge*) gba->memory.rom)->id;
|
||||
memcpy(state->title, ((struct GBACartridge*) gba->memory.rom)->title, sizeof(state->title));
|
||||
switch (gba->memory.unl.type) {
|
||||
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 {
|
||||
state->id = 0;
|
||||
memset(state->title, 0, sizeof(state->title));
|
||||
|
@ -74,6 +83,7 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) {
|
|||
|
||||
GBAMemorySerialize(&gba->memory, state);
|
||||
GBAIOSerialize(gba, state);
|
||||
GBAUnlCartSerialize(gba, state);
|
||||
GBAVideoSerialize(&gba->video, state);
|
||||
GBAAudioSerialize(&gba->audio, state);
|
||||
GBASavedataSerialize(&gba->memory.savedata, state);
|
||||
|
@ -106,9 +116,21 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
|||
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)))) {
|
||||
mLOG(GBA_STATE, WARN, "Savestate is for a different game");
|
||||
error = true;
|
||||
if (gba->memory.rom) {
|
||||
struct GBACartridge* cart;
|
||||
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) {
|
||||
mLOG(GBA_STATE, WARN, "Savestate is for a game, but no game loaded");
|
||||
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");
|
||||
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->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
|
||||
if (state->biosPrefetch) {
|
||||
|
@ -195,7 +220,7 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
|||
if (GBASerializedMiscFlagsIsIrqPending(miscFlags)) {
|
||||
int32_t when;
|
||||
LOAD_32(when, 0, &state->nextIrq);
|
||||
mTimingSchedule(&gba->timing, &gba->irqEvent, when);
|
||||
mTimingSchedule(&gba->timing, &gba->irqEvent, when);
|
||||
}
|
||||
gba->cpuBlocked = GBASerializedMiscFlagsGetBlocked(miscFlags);
|
||||
gba->keysLast = GBASerializedMiscFlagsGetKeyIRQKeys(miscFlags);
|
||||
|
|
457
src/gba/sio.c
457
src/gba/sio.c
|
@ -11,26 +11,14 @@
|
|||
|
||||
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 },
|
||||
{ 8378, 16241, 24104, 31457 },
|
||||
{ 5750, 10998, 16241, 20972 },
|
||||
{ 3140, 5755, 8376, 10486 }
|
||||
};
|
||||
|
||||
static struct GBASIODriver* _lookupDriver(struct GBASIO* sio, enum GBASIOMode mode) {
|
||||
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 void _sioFinish(struct mTiming* timing, void* user, uint32_t cyclesLate);
|
||||
|
||||
static const char* _modeName(enum GBASIOMode mode) {
|
||||
switch (mode) {
|
||||
|
@ -58,25 +46,36 @@ static void _switchMode(struct GBASIO* sio) {
|
|||
newMode = (enum GBASIOMode) (mode & 0xC);
|
||||
}
|
||||
if (newMode != sio->mode) {
|
||||
if (sio->activeDriver && sio->activeDriver->unload) {
|
||||
sio->activeDriver->unload(sio->activeDriver);
|
||||
}
|
||||
if (sio->mode != (enum GBASIOMode) -1) {
|
||||
mLOG(GBA_SIO, DEBUG, "Switching mode from %s to %s", _modeName(sio->mode), _modeName(newMode));
|
||||
}
|
||||
sio->mode = newMode;
|
||||
sio->activeDriver = _lookupDriver(sio, sio->mode);
|
||||
if (sio->activeDriver && sio->activeDriver->load) {
|
||||
sio->activeDriver->load(sio->activeDriver);
|
||||
if (sio->driver && sio->driver->setMode) {
|
||||
sio->driver->setMode(sio->driver, newMode);
|
||||
}
|
||||
|
||||
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) {
|
||||
sio->drivers.normal = 0;
|
||||
sio->drivers.multiplayer = 0;
|
||||
sio->drivers.joybus = 0;
|
||||
sio->activeDriver = 0;
|
||||
sio->driver = NULL;
|
||||
|
||||
sio->completeEvent.context = sio;
|
||||
sio->completeEvent.name = "GBA SIO Complete";
|
||||
sio->completeEvent.callback = _sioFinish;
|
||||
sio->completeEvent.priority = 0x80;
|
||||
|
||||
sio->gbp.p = sio->p;
|
||||
GBASIOPlayerInit(&sio->gbp);
|
||||
|
@ -85,64 +84,28 @@ void GBASIOInit(struct GBASIO* sio) {
|
|||
}
|
||||
|
||||
void GBASIODeinit(struct GBASIO* sio) {
|
||||
if (sio->activeDriver && sio->activeDriver->unload) {
|
||||
sio->activeDriver->unload(sio->activeDriver);
|
||||
}
|
||||
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);
|
||||
if (sio->driver && sio->driver->deinit) {
|
||||
sio->driver->deinit(sio->driver);
|
||||
}
|
||||
}
|
||||
|
||||
void GBASIOReset(struct GBASIO* sio) {
|
||||
if (sio->activeDriver && sio->activeDriver->unload) {
|
||||
sio->activeDriver->unload(sio->activeDriver);
|
||||
if (sio->driver && sio->driver->reset) {
|
||||
sio->driver->reset(sio->driver);
|
||||
}
|
||||
sio->rcnt = RCNT_INITIAL;
|
||||
sio->siocnt = 0;
|
||||
sio->mode = -1;
|
||||
sio->activeDriver = NULL;
|
||||
_switchMode(sio);
|
||||
|
||||
GBASIOPlayerReset(&sio->gbp);
|
||||
}
|
||||
|
||||
void GBASIOSetDriverSet(struct GBASIO* sio, struct GBASIODriverSet* drivers) {
|
||||
GBASIOSetDriver(sio, drivers->normal, GBA_SIO_NORMAL_8);
|
||||
GBASIOSetDriver(sio, drivers->multiplayer, GBA_SIO_MULTI);
|
||||
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);
|
||||
}
|
||||
void GBASIOSetDriver(struct GBASIO* sio, struct GBASIODriver* driver) {
|
||||
if (sio->driver && sio->driver->deinit) {
|
||||
sio->driver->deinit(sio->driver);
|
||||
}
|
||||
sio->driver = driver;
|
||||
if (driver) {
|
||||
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) {
|
||||
sio->rcnt &= 0xF;
|
||||
sio->rcnt |= value & ~0xF;
|
||||
sio->rcnt &= 0x1FF;
|
||||
sio->rcnt |= value & 0xC000;
|
||||
_switchMode(sio);
|
||||
if (sio->activeDriver && sio->activeDriver->writeRegister) {
|
||||
sio->activeDriver->writeRegister(sio->activeDriver, GBA_REG_RCNT, value);
|
||||
if (sio->driver && sio->driver->writeRCNT) {
|
||||
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) {
|
||||
if ((value ^ sio->siocnt) & 0x3000) {
|
||||
sio->siocnt = value & 0x3000;
|
||||
_switchMode(sio);
|
||||
}
|
||||
if (sio->activeDriver && sio->activeDriver->writeRegister) {
|
||||
value = sio->activeDriver->writeRegister(sio->activeDriver, GBA_REG_SIOCNT, value);
|
||||
int id = 0;
|
||||
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 {
|
||||
// Dummy drivers
|
||||
switch (sio->mode) {
|
||||
case GBA_SIO_NORMAL_8:
|
||||
case GBA_SIO_NORMAL_32:
|
||||
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;
|
||||
case GBA_SIO_MULTI:
|
||||
value &= 0xFF83;
|
||||
value |= 0xC;
|
||||
value = GBASIOMultiplayerFillReady(value);
|
||||
break;
|
||||
default:
|
||||
// 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) {
|
||||
if (sio->activeDriver && sio->activeDriver->writeRegister) {
|
||||
return sio->activeDriver->writeRegister(sio->activeDriver, address, value);
|
||||
int id = 0;
|
||||
if (sio->driver && sio->driver->deviceId) {
|
||||
id = sio->driver->deviceId(sio->driver);
|
||||
}
|
||||
// Dummy drivers
|
||||
|
||||
bool handled = true;
|
||||
switch (sio->mode) {
|
||||
case GBA_SIO_JOYBUS:
|
||||
switch (address) {
|
||||
case GBA_REG_SIODATA8:
|
||||
mLOG(GBA_SIO, DEBUG, "JOY write: SIODATA8 (?) <- %04X", value);
|
||||
break;
|
||||
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:
|
||||
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;
|
||||
default:
|
||||
// TODO
|
||||
case GBA_SIO_NORMAL_8:
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -23,18 +23,22 @@ enum {
|
|||
};
|
||||
|
||||
static bool GBASIODolphinInit(struct GBASIODriver* driver);
|
||||
static bool GBASIODolphinLoad(struct GBASIODriver* driver);
|
||||
static bool GBASIODolphinUnload(struct GBASIODriver* driver);
|
||||
static void GBASIODolphinReset(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 int32_t _processCommand(struct GBASIODolphin* dol, uint32_t cyclesLate);
|
||||
static void _flush(struct GBASIODolphin* dol);
|
||||
|
||||
void GBASIODolphinCreate(struct GBASIODolphin* dol) {
|
||||
GBASIOJOYCreate(&dol->d);
|
||||
memset(&dol->d, 0, sizeof(dol->d));
|
||||
dol->d.init = GBASIODolphinInit;
|
||||
dol->d.load = GBASIODolphinLoad;
|
||||
dol->d.unload = GBASIODolphinUnload;
|
||||
dol->d.reset = GBASIODolphinReset;
|
||||
dol->d.setMode = GBASIODolphinSetMode;
|
||||
dol->d.handlesMode = GBASIODolphinHandlesMode;
|
||||
dol->d.connectedDevices = GBASIODolphinConnectedDevices;
|
||||
dol->event.context = dol;
|
||||
dol->event.name = "GB SIO Lockstep";
|
||||
dol->event.callback = GBASIODolphinProcessEvents;
|
||||
|
@ -94,26 +98,33 @@ bool GBASIODolphinConnect(struct GBASIODolphin* dol, const struct Address* addre
|
|||
|
||||
static bool GBASIODolphinInit(struct GBASIODriver* driver) {
|
||||
struct GBASIODolphin* dol = (struct GBASIODolphin*) driver;
|
||||
dol->active = false;
|
||||
dol->clockSlice = 0;
|
||||
dol->state = WAIT_FOR_FIRST_CLOCK;
|
||||
_flush(dol);
|
||||
GBASIODolphinReset(driver);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool GBASIODolphinLoad(struct GBASIODriver* driver) {
|
||||
static void GBASIODolphinReset(struct GBASIODriver* driver) {
|
||||
struct GBASIODolphin* dol = (struct GBASIODolphin*) driver;
|
||||
dol->active = true;
|
||||
dol->active = false;
|
||||
_flush(dol);
|
||||
mTimingDeschedule(&dol->d.p->p->timing, &dol->event);
|
||||
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;
|
||||
dol->active = false;
|
||||
return true;
|
||||
dol->active = mode == GBA_SIO_JOYBUS;
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
|
@ -13,8 +13,11 @@
|
|||
#include <mgba-util/memory.h>
|
||||
|
||||
static uint16_t _gbpRead(struct mKeyCallback*);
|
||||
static uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value);
|
||||
static void _gbpSioProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLate);
|
||||
static uint16_t _gbpSioWriteSIOCNT(struct GBASIODriver* driver, uint16_t value);
|
||||
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[] = {
|
||||
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.requireOpposingDirections = true;
|
||||
gbp->callback.p = gbp;
|
||||
gbp->d.init = 0;
|
||||
gbp->d.deinit = 0;
|
||||
gbp->d.load = 0;
|
||||
gbp->d.unload = 0;
|
||||
gbp->d.writeRegister = _gbpSioWriteRegister;
|
||||
gbp->event.context = gbp;
|
||||
gbp->event.name = "GBA SIO Game Boy Player";
|
||||
gbp->event.callback = _gbpSioProcessEvents;
|
||||
gbp->event.priority = 0x80;
|
||||
memset(&gbp->d, 0, sizeof(gbp->d));
|
||||
gbp->d.writeSIOCNT = _gbpSioWriteSIOCNT;
|
||||
gbp->d.handlesMode = _gbpSioHandlesMode;
|
||||
gbp->d.connectedDevices = _gbpSioConnectedDevices;
|
||||
gbp->d.start = _gbpSioStart;
|
||||
gbp->d.finishNormal32 = _gbpSioFinishNormal32;
|
||||
}
|
||||
|
||||
void GBASIOPlayerReset(struct GBASIOPlayer* gbp) {
|
||||
if (gbp->p->sio.drivers.normal == &gbp->d) {
|
||||
GBASIOSetDriver(&gbp->p->sio, NULL, GBA_SIO_NORMAL_32);
|
||||
if (gbp->p->sio.driver == &gbp->d) {
|
||||
GBASIOSetDriver(&gbp->p->sio, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,8 +87,9 @@ void GBASIOPlayerUpdate(struct GBA* gba) {
|
|||
gba->sio.gbp.inputsPosted = 0;
|
||||
gba->sio.gbp.oldCallback = gba->keyCallback;
|
||||
gba->keyCallback = &gba->sio.gbp.callback.d;
|
||||
// TODO: Check if the SIO driver is actually used first
|
||||
GBASIOSetDriver(&gba->sio, &gba->sio.gbp.d, GBA_SIO_NORMAL_32);
|
||||
if (!gba->sio.driver) {
|
||||
GBASIOSetDriver(&gba->sio, &gba->sio.gbp.d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,35 +101,41 @@ uint16_t _gbpRead(struct mKeyCallback* callback) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
uint16_t _gbpSioWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) {
|
||||
struct GBASIOPlayer* gbp = (struct GBASIOPlayer*) driver;
|
||||
if (address == GBA_REG_SIOCNT) {
|
||||
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;
|
||||
uint16_t _gbpSioWriteSIOCNT(struct GBASIODriver* driver, uint16_t value) {
|
||||
UNUSED(driver);
|
||||
return value & 0x78FB;
|
||||
}
|
||||
|
||||
void _gbpSioProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
||||
UNUSED(timing);
|
||||
UNUSED(cyclesLate);
|
||||
struct GBASIOPlayer* gbp = user;
|
||||
bool _gbpSioStart(struct GBASIODriver* driver) {
|
||||
struct GBASIOPlayer* gbp = (struct GBASIOPlayer*) driver;
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
int txPosition = gbp->txPosition;
|
||||
if (txPosition > 16) {
|
||||
|
@ -139,11 +146,5 @@ void _gbpSioProcessEvents(struct mTiming* timing, void* user, uint32_t cyclesLat
|
|||
}
|
||||
tx = _gbpTxData[txPosition];
|
||||
++gbp->txPosition;
|
||||
gbp->p->memory.io[GBA_REG(SIODATA32_LO)] = 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;
|
||||
return tx;
|
||||
}
|
||||
|
|
|
@ -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
|
@ -70,7 +70,7 @@ void GBAVideoReset(struct GBAVideo* video) {
|
|||
} else {
|
||||
// TODO: Verify exact scanline on hardware
|
||||
video->vcount = 0x7E;
|
||||
nextEvent = 117;
|
||||
nextEvent = 120;
|
||||
}
|
||||
video->p->memory.io[GBA_REG(VCOUNT)] = video->vcount;
|
||||
|
||||
|
|
|
@ -160,7 +160,7 @@ void ctrActivateTexture(const C3D_Tex* texture) {
|
|||
.m = {
|
||||
// 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, 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);
|
||||
|
|
|
@ -53,7 +53,7 @@ int main(int argc, char** argv) {
|
|||
SocketClose(sock);
|
||||
SocketSubsystemDeinit();
|
||||
didFail = true;
|
||||
goto cleanup;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Run the server
|
||||
|
|
|
@ -834,7 +834,7 @@ static void _setupMaps(struct mCore* core) {
|
|||
|
||||
void retro_reset(void) {
|
||||
core->reset(core);
|
||||
mRumbleIntegratorInit(&rumble);
|
||||
mRumbleIntegratorReset(&rumble);
|
||||
_setupMaps(core);
|
||||
}
|
||||
|
||||
|
|
|
@ -253,7 +253,7 @@ static void mGLContextImageSize(struct VideoBackend* v, enum VideoLayer layer, i
|
|||
*height = context->layerDims[layer].height;
|
||||
} else {
|
||||
*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;
|
||||
glBindTexture(GL_TEXTURE_2D, context->tex[context->activeTex]);
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_2D, context->layers[layer]);
|
||||
glBindTexture(GL_TEXTURE_2D, context->layers[layer]);
|
||||
}
|
||||
|
||||
int width = context->imageSizes[layer].width;
|
||||
|
|
|
@ -529,7 +529,7 @@ static void mGLES2ContextImageSize(struct VideoBackend* v, enum VideoLayer layer
|
|||
*height = context->layerDims[layer].height;
|
||||
} else {
|
||||
*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) {
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, shader->width, shader->height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
|
||||
} 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);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef PSP2_COMMON_H
|
||||
#define PSP2_COMMON_H
|
||||
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
#define PSP2_HORIZONTAL_PIXELS 960
|
||||
|
|
|
@ -22,8 +22,7 @@
|
|||
#define CXX_GUARD_END
|
||||
|
||||
#define PYCPARSE
|
||||
#define va_list void*
|
||||
|
||||
typedef ... va_list;
|
||||
typedef int... time_t;
|
||||
typedef int... off_t;
|
||||
typedef ...* png_structp;
|
||||
|
@ -46,7 +45,6 @@ void free(void*);
|
|||
#define PYEXPORT extern "Python+C"
|
||||
#include "platform/python/core.h"
|
||||
#include "platform/python/log.h"
|
||||
#include "platform/python/sio.h"
|
||||
#include "platform/python/vfs-py.h"
|
||||
#undef PYEXPORT
|
||||
|
||||
|
|
|
@ -45,7 +45,6 @@ ffi.set_source("mgba._pylib", """
|
|||
#define PYEXPORT
|
||||
#include "platform/python/core.h"
|
||||
#include "platform/python/log.h"
|
||||
#include "platform/python/sio.h"
|
||||
#include "platform/python/vfs-py.h"
|
||||
#undef PYEXPORT
|
||||
""", include_dirs=[incdir, srcdir],
|
||||
|
@ -53,7 +52,7 @@ ffi.set_source("mgba._pylib", """
|
|||
libraries=["mgba"],
|
||||
library_dirs=[bindir],
|
||||
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)
|
||||
|
||||
|
|
|
@ -52,72 +52,6 @@ class GBA(Core):
|
|||
super(GBA, self)._load()
|
||||
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):
|
||||
def __init__(self, core, romSize=lib.SIZE_CART0):
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -216,7 +216,7 @@ void BattleChipView::loadDeck() {
|
|||
error->show();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
QList<int> newDeck;
|
||||
QStringList deck = ini.value("deck").toString().split(',');
|
||||
for (const auto& item : deck) {
|
||||
|
|
|
@ -59,7 +59,10 @@ if(APPLE)
|
|||
list(APPEND QT_DEFINES USE_SHARE_WIDGET)
|
||||
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)
|
||||
elseif(Qt5Widgets_VERSION MATCHES "^5.15")
|
||||
set(MIN_VER 10.13)
|
||||
|
@ -74,8 +77,7 @@ if(APPLE)
|
|||
else()
|
||||
set(MIN_VER 10.8)
|
||||
endif()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=${MIN_VER}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=${MIN_VER}")
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET ${MIN_VER})
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||
endif()
|
||||
|
@ -427,6 +429,7 @@ set_target_properties(${BINARY_NAME}-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${PR
|
|||
|
||||
if(WIN32)
|
||||
set_target_properties(${BINARY_NAME}-qt PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
|
||||
target_link_libraries(${BINARY_NAME}-qt dwmapi)
|
||||
if(NOT MSVC)
|
||||
target_link_libraries(${BINARY_NAME}-qt -municode)
|
||||
endif()
|
||||
|
@ -445,7 +448,7 @@ if(QT_STATIC)
|
|||
if(CMAKE_CROSSCOMPILING)
|
||||
set(QWINDOWS_DEPS ${QT}EventDispatcherSupport ${QT}FontDatabaseSupport ${QT}ThemeSupport ${QT}WindowsUIAutomationSupport)
|
||||
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}::Gui PROPERTIES INTERFACE_LINK_LIBRARIES ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY})
|
||||
elseif(APPLE)
|
||||
|
@ -477,13 +480,21 @@ endif()
|
|||
if(APPLE)
|
||||
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
|
||||
get_target_property(QTCOCOA ${QT}::QCocoaIntegrationPlugin LOCATION)
|
||||
get_target_property(COREAUDIO ${QT}::CoreAudioPlugin LOCATION)
|
||||
get_target_property(QTAVFSERVICE ${QT}::AVFServicePlugin LOCATION)
|
||||
if(${QT_V} LESS 6)
|
||||
# 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)
|
||||
target_sources(${BINARY_NAME}-qt PRIVATE "${PLUGINS}")
|
||||
|
||||
set_source_files_properties("${QTCOCOA}" 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)
|
||||
if(${QT_V} LESS 6)
|
||||
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 "
|
||||
include(BundleUtilities)
|
||||
set(BU_CHMOD_BUNDLE_ITEMS ON)
|
||||
|
|
|
@ -154,11 +154,12 @@ ConfigController::ConfigController(QObject* parent)
|
|||
mSubParserGraphicsInit(&m_subparsers[0], &m_graphicsOpts);
|
||||
|
||||
m_subparsers[1].usage = "Frontend options:\n"
|
||||
" --ecard FILE Scan an e-Reader card in the first loaded game\n"
|
||||
" Can be passed multiple times for multiple cards\n"
|
||||
" --mb FILE Boot a multiboot image with FILE inserted into the ROM slot"
|
||||
" --ecard FILE Scan an e-Reader card in the first loaded game\n"
|
||||
" Can be passed multiple times for multiple cards\n"
|
||||
" --mb FILE Boot a multiboot image with FILE inserted into the ROM slot"
|
||||
#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
|
||||
;
|
||||
|
||||
|
|
|
@ -130,7 +130,7 @@ private:
|
|||
mGraphicsOpts m_graphicsOpts{};
|
||||
std::array<mSubParser, 2> m_subparsers;
|
||||
bool m_parsed = false;
|
||||
|
||||
|
||||
QHash<QString, QVariant> m_argvOptions;
|
||||
QHash<QString, ConfigOption*> m_optionSet;
|
||||
std::unique_ptr<QSettings> m_settings;
|
||||
|
|
|
@ -423,8 +423,8 @@ bool CoreController::attachDolphin(const Address& address) {
|
|||
return false;
|
||||
}
|
||||
if (GBASIODolphinConnect(&m_dolphin, &address, 0, 0)) {
|
||||
GBA* gba = static_cast<GBA*>(m_threadContext.core->board);
|
||||
GBASIOSetDriver(&gba->sio, &m_dolphin.d, GBA_SIO_JOYBUS);
|
||||
clearMultiplayerController();
|
||||
m_threadContext.core->setPeripheral(m_threadContext.core, mPERIPH_GBA_LINK_PORT, &m_dolphin.d);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -432,8 +432,8 @@ bool CoreController::attachDolphin(const Address& address) {
|
|||
|
||||
void CoreController::detachDolphin() {
|
||||
if (platform() == mPLATFORM_GBA) {
|
||||
GBA* gba = static_cast<GBA*>(m_threadContext.core->board);
|
||||
GBASIOSetDriver(&gba->sio, nullptr, GBA_SIO_JOYBUS);
|
||||
// TODO: Reattach to multiplayer controller
|
||||
m_threadContext.core->setPeripheral(m_threadContext.core, mPERIPH_GBA_LINK_PORT, NULL);
|
||||
}
|
||||
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->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) {
|
||||
return;
|
||||
}
|
||||
|
@ -1094,7 +1094,7 @@ void CoreController::attachBattleChipGate() {
|
|||
Interrupter interrupter(this);
|
||||
clearMultiplayerController();
|
||||
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() {
|
||||
|
@ -1102,7 +1102,7 @@ void CoreController::detachBattleChipGate() {
|
|||
return;
|
||||
}
|
||||
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) {
|
||||
|
|
|
@ -162,7 +162,7 @@ void DebuggerConsoleController::historyLoad() {
|
|||
if (line.endsWith("\r\n")) {
|
||||
line.chop(2);
|
||||
} else if (line.endsWith("\n")) {
|
||||
line.chop(1);
|
||||
line.chop(1);
|
||||
}
|
||||
history.append(QString::fromUtf8(line));
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ public:
|
|||
virtual void setVideoProxy(std::shared_ptr<VideoProxy> proxy) { m_videoProxy = std::move(proxy); }
|
||||
std::shared_ptr<VideoProxy> videoProxy() { return m_videoProxy; }
|
||||
virtual VideoBackend* videoBackend();
|
||||
|
||||
|
||||
signals:
|
||||
void drawingStarted();
|
||||
void showCursor();
|
||||
|
@ -81,7 +81,7 @@ public slots:
|
|||
virtual void filter(bool filter);
|
||||
virtual void swapInterval(int interval) = 0;
|
||||
virtual void framePosted() = 0;
|
||||
virtual void setShaders(struct VDir*) = 0;
|
||||
virtual bool setShaders(struct VDir*) = 0;
|
||||
virtual void clearShaders() = 0;
|
||||
virtual void resizeContext() = 0;
|
||||
|
||||
|
|
|
@ -192,6 +192,7 @@ DisplayGL::DisplayGL(const QSurfaceFormat& format, QWidget* parent)
|
|||
setAttribute(Qt::WA_NativeWindow);
|
||||
window()->windowHandle()->setFormat(format);
|
||||
windowHandle()->setSurfaceType(QSurface::OpenGLSurface);
|
||||
windowHandle()->destroy();
|
||||
windowHandle()->create();
|
||||
|
||||
#ifdef USE_SHARE_WIDGET
|
||||
|
@ -314,7 +315,7 @@ bool DisplayGL::highestCompatible(QSurfaceFormat& format) {
|
|||
if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) {
|
||||
format.setVersion(1, 4);
|
||||
} else {
|
||||
format.setVersion(1, 1);
|
||||
format.setVersion(1, 1);
|
||||
}
|
||||
format.setOption(QSurfaceFormat::DeprecatedFunctions);
|
||||
if (DisplayGL::supportsFormat(format)) {
|
||||
|
@ -461,8 +462,10 @@ void DisplayGL::framePosted() {
|
|||
QMetaObject::invokeMethod(m_painter.get(), "draw");
|
||||
}
|
||||
|
||||
void DisplayGL::setShaders(struct VDir* shaders) {
|
||||
QMetaObject::invokeMethod(m_painter.get(), "setShaders", Qt::BlockingQueuedConnection, Q_ARG(struct VDir*, shaders));
|
||||
bool DisplayGL::setShaders(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() {
|
||||
|
@ -995,10 +998,11 @@ void PainterGL::interrupt() {
|
|||
m_interrupter.interrupt(m_context);
|
||||
}
|
||||
|
||||
void PainterGL::setShaders(struct VDir* dir) {
|
||||
bool PainterGL::setShaders(struct VDir* dir) {
|
||||
if (!supportsShaders()) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
bool success = false;
|
||||
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
||||
if (!m_started) {
|
||||
makeCurrent();
|
||||
|
@ -1008,7 +1012,9 @@ void PainterGL::setShaders(struct VDir* dir) {
|
|||
mGLES2ShaderDetach(reinterpret_cast<mGLES2Context*>(m_backend));
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1016,6 +1022,7 @@ void PainterGL::setShaders(struct VDir* dir) {
|
|||
m_gl->doneCurrent();
|
||||
}
|
||||
#endif
|
||||
return success;
|
||||
}
|
||||
|
||||
void PainterGL::clearShaders() {
|
||||
|
|
|
@ -113,7 +113,7 @@ public slots:
|
|||
void filter(bool filter) override;
|
||||
void swapInterval(int interval) override;
|
||||
void framePosted() override;
|
||||
void setShaders(struct VDir*) override;
|
||||
bool setShaders(struct VDir*) override;
|
||||
void clearShaders() override;
|
||||
void resizeContext() override;
|
||||
void setVideoScale(int scale) override;
|
||||
|
@ -184,7 +184,7 @@ public slots:
|
|||
void resizeContext();
|
||||
void setBackgroundImage(const QImage&);
|
||||
|
||||
void setShaders(struct VDir*);
|
||||
bool setShaders(struct VDir*);
|
||||
void clearShaders();
|
||||
VideoShader* shaders();
|
||||
QSize contentSize() const;
|
||||
|
|
|
@ -41,7 +41,7 @@ public slots:
|
|||
void swapInterval(int) override {};
|
||||
void filter(bool filter) override;
|
||||
void framePosted() override;
|
||||
void setShaders(struct VDir*) override {}
|
||||
bool setShaders(struct VDir*) override { return false; }
|
||||
void clearShaders() override {}
|
||||
void resizeContext() override;
|
||||
void setBackgroundImage(const QImage&) override;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue