Merge branch 'master' (early part) into translations

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

View File

@ -12,7 +12,11 @@ install:
- vcpkg --no-dry-run upgrade
- 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
View File

@ -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

View File

@ -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)

View File

@ -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);

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

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2013-2016 Jeffrey Pfau
/* Copyright (c) 2013-2024 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* 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

View File

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

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

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

View File

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

View File

@ -48,6 +48,7 @@ struct GBADMA {
uint32_t nextDest;
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

View File

@ -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;

View File

@ -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);

View File

@ -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];

View File

@ -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

View File

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

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
/* Copyright (c) 2013-2024 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* 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

View File

@ -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); \

View File

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

View File

@ -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

View File

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

View File

@ -1,34 +1,21 @@
// Shader that replicates the LCD Colorspace from Gameboy Advance --
varying vec2 texCoord;
varying 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));
}

View File

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

View File

@ -6,6 +6,7 @@ passes=1
[pass.0]
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)

View File

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

View File

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

View File

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

View File

@ -88,7 +88,7 @@ DEFINE_IMMEDIATE_5_INSTRUCTION_THUMB(LSR1,
}
THUMB_NEUTRAL_S( , , cpu->gprs[rd]);)
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) {

View File

@ -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;

View File

@ -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) {

View File

@ -110,6 +110,9 @@ struct VFile* mDirectorySetOpenPath(struct mDirectorySet* dirs, const char* path
}
struct VFile* mDirectorySetOpenSuffix(struct mDirectorySet* dirs, struct VDir* dir, const char* suffix, int mode) {
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);

View File

@ -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);
}

View File

@ -1,10 +1,14 @@
/* Copyright (c) 2013-2016 Jeffrey Pfau
/* Copyright (c) 2013-2024 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* 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

View File

@ -520,7 +520,7 @@ mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite16, 2, U32, address, U16, va
mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite32, 2, U32, address, U32, value);
// 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;
}
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

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

View File

@ -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));
}
}
}

View File

@ -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;

View File

@ -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) {

View File

@ -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;
}

View File

@ -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) {

View File

@ -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;

View File

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

View File

@ -131,7 +131,7 @@ static void _latchTAMA6Rtc(struct mRTCSource* rtc, struct GBTAMA5State* tama5, t
timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 24) / 10;
} 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;

View File

@ -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) {

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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);
}
}

View File

@ -35,30 +35,6 @@ static int _mulWait(int32_t r) {
}
}
static void _SoftReset(struct GBA* gba) {
struct ARMCore* cpu = gba->cpu;
ARMSetPrivilegeMode(cpu, MODE_IRQ);
cpu->spsr.packed = 0;
cpu->gprs[ARM_LR] = 0;
cpu->gprs[ARM_SP] = GBA_SP_BASE_IRQ;
ARMSetPrivilegeMode(cpu, MODE_SUPERVISOR);
cpu->spsr.packed = 0;
cpu->gprs[ARM_LR] = 0;
cpu->gprs[ARM_SP] = GBA_SP_BASE_SUPERVISOR;
ARMSetPrivilegeMode(cpu, MODE_SYSTEM);
cpu->gprs[ARM_LR] = 0;
cpu->gprs[ARM_SP] = GBA_SP_BASE_SYSTEM;
int8_t flag = ((int8_t*) gba->memory.iwram)[0x7FFA];
memset(((int8_t*) gba->memory.iwram) + GBA_SIZE_IWRAM - 0x200, 0, 0x200);
if (flag) {
cpu->gprs[ARM_PC] = GBA_BASE_EWRAM;
} else {
cpu->gprs[ARM_PC] = GBA_BASE_ROM0;
}
_ARMSetMode(cpu, MODE_ARM);
ARMWritePC(cpu);
}
static void _RegisterRamReset(struct GBA* gba) {
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);

View File

@ -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);
}
}

View File

@ -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);

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

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

View File

@ -1,42 +1,50 @@
/* Copyright (c) 2016 taizou
* Copyright (c) 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;

View File

@ -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;

View File

@ -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];
}
}
}

View File

@ -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;
}

View File

@ -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) {

View File

@ -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;

View File

@ -20,7 +20,7 @@ const uint8_t hleBios[GBA_SIZE_BIOS] = {
0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x04, 0x40, 0xbd, 0xe8,
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
};

View File

@ -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

View File

@ -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);
}

View File

@ -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];

View File

@ -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[] = {

View File

@ -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"

View File

@ -45,7 +45,7 @@ static void _ashesToAshes(struct mTiming* timing, void* user, uint32_t cyclesLat
void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) {
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];
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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) {

View File

@ -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;
}

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -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);

View File

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

View File

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

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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):

View File

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

View File

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

View File

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

View File

@ -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)

View File

@ -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
;

View File

@ -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;

View File

@ -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) {

View File

@ -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));
}

View File

@ -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;

View File

@ -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() {

View File

@ -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;

View File

@ -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