mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' into feature/input-revamp
This commit is contained in:
commit
c784fad04a
47
CHANGES
47
CHANGES
|
@ -12,6 +12,17 @@ Features:
|
|||
- Add option for whether rewinding restores save games
|
||||
- Qt: German translation (by Lothar Serra Mari)
|
||||
- Savestates now contain any RTC override data
|
||||
- Command line ability to override configuration values
|
||||
- Add option to allow preloading the entire ROM before running
|
||||
- GB: Video/audio channel enabling/disabling
|
||||
- Add option to lock video to integer scaling
|
||||
- Video log recording for testing and bug reporting
|
||||
- Library view
|
||||
- Debugger: Segment/bank support
|
||||
- GB: Symbol table support
|
||||
- GB MBC: Add MBC1 multicart support
|
||||
- GBA: Implement keypad interrupts
|
||||
- LR35902: Watchpoints
|
||||
Bugfixes:
|
||||
- LR35902: Fix core never exiting with certain event patterns
|
||||
- GB Timer: Improve DIV reset behavior
|
||||
|
@ -31,6 +42,28 @@ Bugfixes:
|
|||
- Qt: Fix linking after some windows have been closed
|
||||
- GBA Video: Fix wrong palette on 256-color sprites in OBJWIN
|
||||
- Windows: Fix VDir.rewind
|
||||
- SDL: Fix game crash check
|
||||
- SDL: Fix race condition with audio thread when starting
|
||||
- GB: Fix flickering when screen is strobed quickly
|
||||
- FFmpeg: Fix overflow and general issues with audio encoding
|
||||
- Qt: Fix crash when changing audio settings after a game is closed
|
||||
- GBA BIOS: Fix ArcTan sign in HLE BIOS
|
||||
- GBA BIOS: Fix ArcTan2 sign in HLE BIOS (fixes mgba.io/i/689)
|
||||
- GBA Video: Don't update background scanline params in mode 0 (fixes mgba.io/i/377)
|
||||
- Qt: Ensure CLI backend is attached when submitting commands (fixes mgba.io/i/662)
|
||||
- Core: Fix crash with rewind if savestates shrink
|
||||
- Test: Fix crash when loading invalid file
|
||||
- GBA Hardware: Fix crash if a savestate lies about game hardware
|
||||
- Test: Fix crash when fuzzing fails to load a file
|
||||
- GBA: Fix multiboot loading resulting in too small WRAM
|
||||
- Test: Don't rely on core for frames elapsed
|
||||
- Test: Fix crash when loading invalid file
|
||||
- GBA Hardware: Fix crash if a savestate lies about game hardware
|
||||
- Test: Fix crash when fuzzing fails to load a file
|
||||
- Qt: Disable "New multiplayer window" when MAX_GBAS is reached (fixes mgba.io/i/107)
|
||||
- LR35902: Fix decoding LD r, $imm and 0-valued immediates (fixes mgba.io/i/735)
|
||||
- GB: Fix STAT blocking
|
||||
- GB MBC: Fix swapping carts not detect new MBC
|
||||
Misc:
|
||||
- SDL: Remove scancode key input
|
||||
- GBA Video: Clean up unused timers
|
||||
|
@ -77,6 +110,20 @@ Misc:
|
|||
- Qt: Remove audio thread
|
||||
- Qt: Remove audio buffer sizing in AudioProcessorQt
|
||||
- Qt: Re-enable QtMultimedia on Windows
|
||||
- FFmpeg: Return false if a file fails to open
|
||||
- FFmpeg: Force MP4 files to YUV420P
|
||||
- Qt: Make "Mute" able to be bound to a key
|
||||
- Core: Restore sleep callback
|
||||
- Qt: Add .gb/.gbc files to the extension list in Info.plist
|
||||
- Feature: Make -l option explicit
|
||||
- Core: Ability to enumerate and modify video and audio channels
|
||||
- Debugger: Make attaching a backend idempotent
|
||||
- VFS: Optimize expanding in-memory files
|
||||
- VFS: Add VFileFIFO for operating on circle buffers
|
||||
- Core: Move rewind diffing to its own thread
|
||||
- Util: Tune patch-fast extent sizes
|
||||
- Qt: Relax hard dependency on OpenGL
|
||||
- GB Video: Improved video timings
|
||||
|
||||
0.5.2: (2016-12-31)
|
||||
Bugfixes:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
cmake_minimum_required(VERSION 2.6)
|
||||
project(mGBA C)
|
||||
cmake_minimum_required(VERSION 2.8.11)
|
||||
project(mGBA)
|
||||
set(BINARY_NAME mgba CACHE INTERNAL "Name of output binaries")
|
||||
if(NOT MSVC)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-missing-field-initializers -std=c99")
|
||||
|
@ -54,11 +54,13 @@ file(GLOB UTIL_TEST_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/test/*.c)
|
|||
file(GLOB GUI_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/gui/*.c ${CMAKE_CURRENT_SOURCE_DIR}/src/feature/gui/*.c)
|
||||
file(GLOB GBA_RENDERER_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/renderers/*.c)
|
||||
file(GLOB GBA_SIO_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/sio/lockstep.c)
|
||||
file(GLOB GBA_EXTRA_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/extra/*.c)
|
||||
file(GLOB GB_SIO_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/sio/lockstep.c)
|
||||
file(GLOB GB_RENDERER_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/renderers/*.c)
|
||||
file(GLOB GB_EXTRA_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/extra/*.c)
|
||||
file(GLOB THIRD_PARTY_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/third-party/inih/*.c)
|
||||
set(CLI_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/feature/commandline.c)
|
||||
set(CORE_VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-mem.c)
|
||||
file(GLOB EXTRA_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/feature/*.c)
|
||||
set(CORE_VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-mem.c ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-fifo.c)
|
||||
set(VFS_SRC)
|
||||
source_group("ARM core" FILES ${ARM_SRC})
|
||||
source_group("LR35902 core" FILES ${LR35902_SRC})
|
||||
|
@ -301,6 +303,7 @@ else()
|
|||
endif()
|
||||
set(DISABLE_FRONTENDS ON)
|
||||
set(MINIMAL_CORE ON)
|
||||
set(ENABLE_EXTRA ON)
|
||||
endif()
|
||||
|
||||
check_function_exists(chmod HAVE_CHMOD)
|
||||
|
@ -392,6 +395,7 @@ find_feature(USE_SQLITE3 "sqlite3")
|
|||
set(DEBUGGER_SRC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/debugger/debugger.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/debugger/parser.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/debugger/symbols.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/debugger/cli-debugger.c)
|
||||
|
||||
set(FEATURE_SRC)
|
||||
|
@ -600,7 +604,9 @@ if(M_CORE_GB)
|
|||
list(APPEND DEBUGGER_SRC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/lr35902/debugger/cli-debugger.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/lr35902/debugger/debugger.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/gb/extra/cli.c)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/lr35902/debugger/memory-debugger.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/gb/debugger/cli.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/gb/debugger/symbols.c)
|
||||
list(APPEND TEST_SRC
|
||||
${LR35902_TEST_SRC}
|
||||
${GB_TEST_SRC})
|
||||
|
@ -617,7 +623,7 @@ if(M_CORE_GBA)
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/arm/debugger/cli-debugger.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/arm/debugger/debugger.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/arm/debugger/memory-debugger.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/gba/extra/cli.c)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/gba/debugger/cli.c)
|
||||
list(APPEND TEST_SRC
|
||||
${ARM_TEST_SRC}
|
||||
${GBA_TEST_SRC})
|
||||
|
@ -662,10 +668,9 @@ list(APPEND TEST_SRC ${UTIL_TEST_SRC})
|
|||
|
||||
set(SRC ${CORE_SRC} ${VFS_SRC})
|
||||
if(NOT MINIMAL_CORE)
|
||||
set(ENABLE_EXTRA ON)
|
||||
if(M_CORE_GBA)
|
||||
list(APPEND SRC
|
||||
${GBA_RR_SRC}
|
||||
${GBA_EXTRA_SRC}
|
||||
${GBA_SIO_SRC})
|
||||
endif()
|
||||
if(M_CORE_GB)
|
||||
|
@ -673,8 +678,21 @@ if(NOT MINIMAL_CORE)
|
|||
${GB_SIO_SRC})
|
||||
endif()
|
||||
list(APPEND SRC
|
||||
${FEATURE_SRC}
|
||||
${CLI_SRC})
|
||||
${FEATURE_SRC})
|
||||
endif()
|
||||
|
||||
if(ENABLE_EXTRA)
|
||||
if(M_CORE_GBA)
|
||||
list(APPEND SRC
|
||||
${GBA_RR_SRC}
|
||||
${GBA_EXTRA_SRC})
|
||||
endif()
|
||||
if(M_CORE_GB)
|
||||
list(APPEND SRC
|
||||
${GB_EXTRA_SRC})
|
||||
endif()
|
||||
list(APPEND SRC
|
||||
${EXTRA_SRC})
|
||||
endif()
|
||||
|
||||
if(NOT SKIP_LIBRARY)
|
||||
|
@ -771,7 +789,7 @@ if(BUILD_QT)
|
|||
endif()
|
||||
|
||||
if(BUILD_PERF)
|
||||
set(PERF_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/test/perf-main.c ${CLI_SRC})
|
||||
set(PERF_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/test/perf-main.c)
|
||||
if(UNIX AND NOT APPLE)
|
||||
list(APPEND PERF_LIB rt)
|
||||
endif()
|
||||
|
|
|
@ -26,6 +26,7 @@ void CircleBufferClear(struct CircleBuffer* buffer);
|
|||
int CircleBufferWrite8(struct CircleBuffer* buffer, int8_t value);
|
||||
int CircleBufferWrite16(struct CircleBuffer* buffer, int16_t value);
|
||||
int CircleBufferWrite32(struct CircleBuffer* buffer, int32_t value);
|
||||
size_t CircleBufferWrite(struct CircleBuffer* buffer, const void* input, size_t length);
|
||||
int CircleBufferRead8(struct CircleBuffer* buffer, int8_t* value);
|
||||
int CircleBufferRead16(struct CircleBuffer* buffer, int16_t* value);
|
||||
int CircleBufferRead32(struct CircleBuffer* buffer, int32_t* value);
|
||||
|
|
|
@ -56,6 +56,11 @@ typedef intptr_t ssize_t;
|
|||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#ifdef PSP2
|
||||
// For PATH_MAX on modern toolchains
|
||||
#include <sys/syslimits.h>
|
||||
#endif
|
||||
|
||||
#ifndef SSIZE_MAX
|
||||
#define SSIZE_MAX ((ssize_t) (SIZE_MAX >> 1))
|
||||
#endif
|
||||
|
|
|
@ -13,7 +13,7 @@ CXX_GUARD_START
|
|||
#include <mgba-util/patch.h>
|
||||
#include <mgba-util/vector.h>
|
||||
|
||||
#define PATCH_FAST_EXTENT 256
|
||||
#define PATCH_FAST_EXTENT 128
|
||||
|
||||
struct PatchFastExtent {
|
||||
size_t length;
|
||||
|
|
|
@ -14,6 +14,8 @@ CXX_GUARD_START
|
|||
#include <sys/time.h>
|
||||
#if defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
#include <pthread_np.h>
|
||||
#elif defined(__HAIKU__)
|
||||
#include <OS.h>
|
||||
#endif
|
||||
|
||||
#define THREAD_ENTRY void*
|
||||
|
@ -88,6 +90,9 @@ static inline int ThreadSetName(const char* name) {
|
|||
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
pthread_set_name_np(pthread_self(), name);
|
||||
return 0;
|
||||
#elif defined(__HAIKU__)
|
||||
rename_thread(find_thread(NULL), name);
|
||||
return 0;
|
||||
#elif !defined(BUILD_PANDORA) // Pandora's glibc is too old
|
||||
return pthread_setname_np(pthread_self(), name);
|
||||
#else
|
||||
|
|
|
@ -92,6 +92,8 @@ CXX_GUARD_START
|
|||
dest->size = src->size; \
|
||||
} \
|
||||
|
||||
DECLARE_VECTOR(StringList, char*);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
||||
|
|
|
@ -73,6 +73,9 @@ struct VFile* VFileFromMemory(void* mem, size_t size);
|
|||
struct VFile* VFileFromConstMemory(const void* mem, size_t size);
|
||||
struct VFile* VFileMemChunk(const void* mem, size_t size);
|
||||
|
||||
struct CircleBuffer;
|
||||
struct VFile* VFileFIFO(struct CircleBuffer* backing);
|
||||
|
||||
struct VDir* VDirOpen(const char* path);
|
||||
struct VDir* VDirOpenArchive(const char* path);
|
||||
|
||||
|
|
|
@ -48,7 +48,6 @@ struct mCheat {
|
|||
mLOG_DECLARE_CATEGORY(CHEATS);
|
||||
|
||||
DECLARE_VECTOR(mCheatList, struct mCheat);
|
||||
DECLARE_VECTOR(StringList, char*);
|
||||
|
||||
struct mCheatDevice;
|
||||
struct mCheatSet {
|
||||
|
|
|
@ -42,6 +42,7 @@ struct mCoreOptions {
|
|||
int width;
|
||||
int height;
|
||||
bool lockAspectRatio;
|
||||
bool lockIntegerScaling;
|
||||
bool resampleVideo;
|
||||
bool suspendScreensaver;
|
||||
char* shader;
|
||||
|
|
|
@ -26,10 +26,10 @@ CXX_GUARD_START
|
|||
enum mPlatform {
|
||||
PLATFORM_NONE = -1,
|
||||
#ifdef M_CORE_GBA
|
||||
PLATFORM_GBA,
|
||||
PLATFORM_GBA = 0,
|
||||
#endif
|
||||
#ifdef M_CORE_GB
|
||||
PLATFORM_GB,
|
||||
PLATFORM_GB = 1,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -39,11 +39,14 @@ enum mCoreChecksumType {
|
|||
|
||||
struct mCoreConfig;
|
||||
struct mCoreSync;
|
||||
struct mDebuggerSymbols;
|
||||
struct mStateExtdata;
|
||||
struct mVideoLogContext;
|
||||
struct mCore {
|
||||
void* cpu;
|
||||
void* board;
|
||||
struct mDebugger* debugger;
|
||||
struct mDebuggerSymbols* symbolTable;
|
||||
|
||||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||
struct mDirectorySet dirs;
|
||||
|
@ -111,8 +114,7 @@ struct mCore {
|
|||
void (*getGameTitle)(const struct mCore*, char* title);
|
||||
void (*getGameCode)(const struct mCore*, char* title);
|
||||
|
||||
void (*setRotation)(struct mCore*, struct mRotationSource*);
|
||||
void (*setRumble)(struct mCore*, struct mRumble*);
|
||||
void (*setPeripheral)(struct mCore*, int type, void*);
|
||||
|
||||
uint32_t (*busRead8)(struct mCore*, uint32_t address);
|
||||
uint32_t (*busRead16)(struct mCore*, uint32_t address);
|
||||
|
@ -136,18 +138,33 @@ struct mCore {
|
|||
struct CLIDebuggerSystem* (*cliDebuggerSystem)(struct mCore*);
|
||||
void (*attachDebugger)(struct mCore*, struct mDebugger*);
|
||||
void (*detachDebugger)(struct mCore*);
|
||||
|
||||
void (*loadSymbols)(struct mCore*, struct VFile*);
|
||||
#endif
|
||||
|
||||
struct mCheatDevice* (*cheatDevice)(struct mCore*);
|
||||
|
||||
size_t (*savedataClone)(struct mCore*, void** sram);
|
||||
bool (*savedataRestore)(struct mCore*, const void* sram, size_t size, bool writeback);
|
||||
|
||||
size_t (*listVideoLayers)(const struct mCore*, const struct mCoreChannelInfo**);
|
||||
size_t (*listAudioChannels)(const struct mCore*, const struct mCoreChannelInfo**);
|
||||
void (*enableVideoLayer)(struct mCore*, size_t id, bool enable);
|
||||
void (*enableAudioChannel)(struct mCore*, size_t id, bool enable);
|
||||
|
||||
#ifndef MINIMAL_CORE
|
||||
void (*startVideoLog)(struct mCore*, struct mVideoLogContext*);
|
||||
void (*endVideoLog)(struct mCore*);
|
||||
#endif
|
||||
};
|
||||
|
||||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||
struct mCore* mCoreFind(const char* path);
|
||||
bool mCoreLoadFile(struct mCore* core, const char* path);
|
||||
|
||||
bool mCorePreloadVF(struct mCore* core, struct VFile* vf);
|
||||
bool mCorePreloadFile(struct mCore* core, const char* path);
|
||||
|
||||
bool mCoreAutoloadSave(struct mCore* core);
|
||||
bool mCoreAutoloadPatch(struct mCore* core);
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ struct mCoreCallbacks {
|
|||
void (*videoFrameStarted)(void* context);
|
||||
void (*videoFrameEnded)(void* context);
|
||||
void (*coreCrashed)(void* context);
|
||||
void (*sleep)(void* context);
|
||||
};
|
||||
|
||||
DECLARE_VECTOR(mCoreCallbacksList, struct mCoreCallbacks);
|
||||
|
@ -53,8 +54,10 @@ struct mKeyCallback {
|
|||
uint16_t (*readKeys)(struct mKeyCallback*);
|
||||
};
|
||||
|
||||
struct mStopCallback {
|
||||
void (*stop)(struct mStopCallback*);
|
||||
enum mPeripheral {
|
||||
mPERIPH_ROTATION = 1,
|
||||
mPERIPH_RUMBLE,
|
||||
mPERIPH_CUSTOM = 0x1000
|
||||
};
|
||||
|
||||
struct mRotationSource {
|
||||
|
@ -102,6 +105,13 @@ struct mRumble {
|
|||
void (*setRumble)(struct mRumble*, int enable);
|
||||
};
|
||||
|
||||
struct mCoreChannelInfo {
|
||||
size_t id;
|
||||
const char* internalName;
|
||||
const char* visibleName;
|
||||
const char* visibleType;
|
||||
};
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
CXX_GUARD_START
|
||||
|
||||
#include <mgba-util/vector.h>
|
||||
#ifndef DISABLE_THREADING
|
||||
#include <mgba-util/threading.h>
|
||||
#endif
|
||||
|
||||
DECLARE_VECTOR(mCoreRewindPatches, struct PatchFast);
|
||||
|
||||
|
@ -22,9 +25,16 @@ struct mCoreRewindContext {
|
|||
int stateFlags;
|
||||
struct VFile* previousState;
|
||||
struct VFile* currentState;
|
||||
|
||||
#ifndef DISABLE_THREADING
|
||||
bool onThread;
|
||||
Thread thread;
|
||||
Condition cond;
|
||||
Mutex mutex;
|
||||
#endif
|
||||
};
|
||||
|
||||
void mCoreRewindContextInit(struct mCoreRewindContext*, size_t entries);
|
||||
void mCoreRewindContextInit(struct mCoreRewindContext*, size_t entries, bool onThread);
|
||||
void mCoreRewindContextDeinit(struct mCoreRewindContext*);
|
||||
|
||||
struct mCore;
|
||||
|
|
|
@ -62,6 +62,7 @@ struct mCoreThread {
|
|||
ThreadCallback resetCallback;
|
||||
ThreadCallback cleanCallback;
|
||||
ThreadCallback frameCallback;
|
||||
ThreadCallback sleepCallback;
|
||||
void* userData;
|
||||
void (*run)(struct mCoreThread*);
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba-util/table.h>
|
||||
|
||||
#include <mgba/internal/debugger/debugger.h>
|
||||
|
||||
struct mArguments {
|
||||
|
@ -21,6 +23,8 @@ struct mArguments {
|
|||
int logLevel;
|
||||
int frameskip;
|
||||
|
||||
struct Table configOverrides;
|
||||
|
||||
enum mDebuggerType debuggerType;
|
||||
bool debugAtStart;
|
||||
bool showHelp;
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2013-2015 Jeffrey Pfau
|
||||
/* 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
|
||||
|
@ -11,36 +11,29 @@
|
|||
CXX_GUARD_START
|
||||
|
||||
#include <mgba/internal/gba/video.h>
|
||||
#include "video-logger.h"
|
||||
#include <mgba-util/threading.h>
|
||||
#include <mgba-util/ring-fifo.h>
|
||||
|
||||
enum GBAVideoThreadProxyState {
|
||||
enum mVideoThreadProxyState {
|
||||
PROXY_THREAD_STOPPED = 0,
|
||||
PROXY_THREAD_IDLE,
|
||||
PROXY_THREAD_BUSY
|
||||
};
|
||||
|
||||
struct GBAVideoThreadProxyRenderer {
|
||||
struct GBAVideoRenderer d;
|
||||
struct GBAVideoRenderer* backend;
|
||||
struct mVideoThreadProxy {
|
||||
struct mVideoLogger d;
|
||||
|
||||
Thread thread;
|
||||
Condition fromThreadCond;
|
||||
Condition toThreadCond;
|
||||
Mutex mutex;
|
||||
enum GBAVideoThreadProxyState threadState;
|
||||
enum mVideoThreadProxyState threadState;
|
||||
|
||||
struct RingFIFO dirtyQueue;
|
||||
|
||||
uint32_t vramDirtyBitmap;
|
||||
uint32_t oamDirtyBitmap[16];
|
||||
|
||||
uint16_t* vramProxy;
|
||||
union GBAOAM oamProxy;
|
||||
uint16_t paletteProxy[512];
|
||||
};
|
||||
|
||||
void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend);
|
||||
void mVideoThreadProxyCreate(struct mVideoThreadProxy* renderer);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
/* 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/. */
|
||||
#ifndef VIDEO_LOGGER_H
|
||||
#define VIDEO_LOGGER_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba-util/circle-buffer.h>
|
||||
|
||||
#define mVL_MAX_CHANNELS 32
|
||||
|
||||
enum mVideoLoggerDirtyType {
|
||||
DIRTY_DUMMY = 0,
|
||||
DIRTY_FLUSH,
|
||||
DIRTY_SCANLINE,
|
||||
DIRTY_REGISTER,
|
||||
DIRTY_OAM,
|
||||
DIRTY_PALETTE,
|
||||
DIRTY_VRAM,
|
||||
DIRTY_FRAME,
|
||||
DIRTY_RANGE,
|
||||
DIRTY_BUFFER,
|
||||
};
|
||||
|
||||
struct mVideoLoggerDirtyInfo {
|
||||
enum mVideoLoggerDirtyType type;
|
||||
uint32_t address;
|
||||
uint32_t value;
|
||||
uint32_t value2;
|
||||
};
|
||||
|
||||
struct VFile;
|
||||
struct mVideoLogger {
|
||||
bool (*writeData)(struct mVideoLogger* logger, const void* data, size_t length);
|
||||
bool (*readData)(struct mVideoLogger* logger, void* data, size_t length, bool block);
|
||||
void* dataContext;
|
||||
|
||||
bool block;
|
||||
void (*init)(struct mVideoLogger*);
|
||||
void (*deinit)(struct mVideoLogger*);
|
||||
void (*reset)(struct mVideoLogger*);
|
||||
|
||||
void (*lock)(struct mVideoLogger*);
|
||||
void (*unlock)(struct mVideoLogger*);
|
||||
void (*wait)(struct mVideoLogger*);
|
||||
void (*wake)(struct mVideoLogger*, int y);
|
||||
void* context;
|
||||
|
||||
bool (*parsePacket)(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* packet);
|
||||
uint16_t* (*vramBlock)(struct mVideoLogger* logger, uint32_t address);
|
||||
|
||||
size_t vramSize;
|
||||
size_t oamSize;
|
||||
size_t paletteSize;
|
||||
|
||||
uint32_t* vramDirtyBitmap;
|
||||
uint32_t* oamDirtyBitmap;
|
||||
|
||||
uint16_t* vram;
|
||||
uint16_t* oam;
|
||||
uint16_t* palette;
|
||||
};
|
||||
|
||||
void mVideoLoggerRendererCreate(struct mVideoLogger* logger, bool readonly);
|
||||
void mVideoLoggerRendererInit(struct mVideoLogger* logger);
|
||||
void mVideoLoggerRendererDeinit(struct mVideoLogger* logger);
|
||||
void mVideoLoggerRendererReset(struct mVideoLogger* logger);
|
||||
|
||||
void mVideoLoggerRendererWriteVideoRegister(struct mVideoLogger* logger, uint32_t address, uint16_t value);
|
||||
void mVideoLoggerRendererWriteVRAM(struct mVideoLogger* logger, uint32_t address);
|
||||
void mVideoLoggerRendererWritePalette(struct mVideoLogger* logger, uint32_t address, uint16_t value);
|
||||
void mVideoLoggerRendererWriteOAM(struct mVideoLogger* logger, uint32_t address, uint16_t value);
|
||||
|
||||
void mVideoLoggerWriteBuffer(struct mVideoLogger* logger, uint32_t bufferId, uint32_t offset, uint32_t length, const void* data);
|
||||
|
||||
void mVideoLoggerRendererDrawScanline(struct mVideoLogger* logger, int y);
|
||||
void mVideoLoggerRendererDrawRange(struct mVideoLogger* logger, int startX, int endX, int y);
|
||||
void mVideoLoggerRendererFlush(struct mVideoLogger* logger);
|
||||
void mVideoLoggerRendererFinishFrame(struct mVideoLogger* logger);
|
||||
|
||||
bool mVideoLoggerRendererRun(struct mVideoLogger* logger, bool block);
|
||||
|
||||
struct mVideoLogContext;
|
||||
void mVideoLoggerAttachChannel(struct mVideoLogger* logger, struct mVideoLogContext* context, size_t channelId);
|
||||
|
||||
struct mCore;
|
||||
struct mVideoLogContext* mVideoLogContextCreate(struct mCore* core);
|
||||
|
||||
void mVideoLogContextSetOutput(struct mVideoLogContext*, struct VFile*);
|
||||
void mVideoLogContextWriteHeader(struct mVideoLogContext*, struct mCore* core);
|
||||
|
||||
bool mVideoLogContextLoad(struct mVideoLogContext*, struct VFile*);
|
||||
void mVideoLogContextDestroy(struct mCore* core, struct mVideoLogContext*);
|
||||
|
||||
void mVideoLogContextRewind(struct mVideoLogContext*, struct mCore*);
|
||||
void* mVideoLogContextInitialState(struct mVideoLogContext*, size_t* size);
|
||||
|
||||
int mVideoLoggerAddChannel(struct mVideoLogContext*);
|
||||
|
||||
struct mCore* mVideoLogCoreFind(struct VFile*);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -12,6 +12,9 @@ CXX_GUARD_START
|
|||
|
||||
struct mCore;
|
||||
struct mCore* GBCoreCreate(void);
|
||||
#ifndef MINIMAL_CORE
|
||||
struct mCore* GBVideoLogPlayerCreate(void);
|
||||
#endif
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
|
|
|
@ -34,6 +34,15 @@ enum GBMemoryBankControllerType {
|
|||
GB_MBC5_RUMBLE = 0x105
|
||||
};
|
||||
|
||||
struct GBSIODriver {
|
||||
struct GBSIO* p;
|
||||
|
||||
bool (*init)(struct GBSIODriver* driver);
|
||||
void (*deinit)(struct GBSIODriver* driver);
|
||||
void (*writeSB)(struct GBSIODriver* driver, uint8_t value);
|
||||
uint8_t (*writeSC)(struct GBSIODriver* driver, uint8_t value);
|
||||
};
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
||||
|
|
|
@ -12,6 +12,9 @@ CXX_GUARD_START
|
|||
|
||||
struct mCore;
|
||||
struct mCore* GBACoreCreate(void);
|
||||
#ifndef MINIMAL_CORE
|
||||
struct mCore* GBAVideoLogPlayerCreate(void);
|
||||
#endif
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
|
|
|
@ -28,6 +28,10 @@ struct GBAVideoRenderer;
|
|||
|
||||
extern const int GBA_LUX_LEVELS[10];
|
||||
|
||||
enum {
|
||||
mPERIPH_GBA_LUMINANCE = 0x1000
|
||||
};
|
||||
|
||||
struct GBALuminanceSource {
|
||||
void (*sample)(struct GBALuminanceSource*);
|
||||
|
||||
|
|
|
@ -87,13 +87,14 @@ struct mDebuggerPlatform {
|
|||
void (*entered)(struct mDebuggerPlatform*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*);
|
||||
|
||||
bool (*hasBreakpoints)(struct mDebuggerPlatform*);
|
||||
void (*setBreakpoint)(struct mDebuggerPlatform*, uint32_t address);
|
||||
void (*clearBreakpoint)(struct mDebuggerPlatform*, uint32_t address);
|
||||
void (*setWatchpoint)(struct mDebuggerPlatform*, uint32_t address, enum mWatchpointType type);
|
||||
void (*clearWatchpoint)(struct mDebuggerPlatform*, uint32_t address);
|
||||
void (*setBreakpoint)(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
void (*clearBreakpoint)(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
void (*setWatchpoint)(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type);
|
||||
void (*clearWatchpoint)(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
void (*checkBreakpoints)(struct mDebuggerPlatform*);
|
||||
};
|
||||
|
||||
struct mDebuggerSymbols;
|
||||
struct mDebugger {
|
||||
struct mCPUComponent d;
|
||||
struct mDebuggerPlatform* platform;
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/* 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/. */
|
||||
#ifndef DEBUGGER_SYMBOLS_H
|
||||
#define DEBUGGER_SYMBOLS_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
struct mDebuggerSymbols;
|
||||
|
||||
struct mDebuggerSymbols* mDebuggerSymbolTableCreate(void);
|
||||
void mDebuggerSymbolTableDestroy(struct mDebuggerSymbols*);
|
||||
|
||||
bool mDebuggerSymbolLookup(const struct mDebuggerSymbols*, const char* name, int32_t* value, int* segment);
|
||||
|
||||
void mDebuggerSymbolAdd(struct mDebuggerSymbols*, const char* name, int32_t value, int segment);
|
||||
void mDebuggerSymbolRemove(struct mDebuggerSymbols*, const char* name);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -0,0 +1,19 @@
|
|||
/* 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/. */
|
||||
#ifndef GB_SYMBOLS_H
|
||||
#define GB_SYMBOLS_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
struct mDebuggerSymbols;
|
||||
struct VFile;
|
||||
void GBLoadSymbols(struct mDebuggerSymbols*, struct VFile* vf);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -85,6 +85,7 @@ enum GBMBC7MachineState {
|
|||
|
||||
struct GBMBC1State {
|
||||
int mode;
|
||||
int multicartStride;
|
||||
};
|
||||
|
||||
struct GBMBC7State {
|
||||
|
@ -161,14 +162,13 @@ void GBMemorySwitchWramBank(struct GBMemory* memory, int bank);
|
|||
uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address);
|
||||
void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value);
|
||||
|
||||
int GBCurrentSegment(struct LR35902Core* cpu, uint16_t address);
|
||||
|
||||
uint8_t GBView8(struct LR35902Core* cpu, uint16_t address, int segment);
|
||||
|
||||
void GBMemoryDMA(struct GB* gb, uint16_t base);
|
||||
void GBMemoryWriteHDMA5(struct GB* gb, uint8_t value);
|
||||
|
||||
uint8_t GBDMALoad8(struct LR35902Core* cpu, uint16_t address);
|
||||
void GBDMAStore8(struct LR35902Core* cpu, uint16_t address, int8_t value);
|
||||
|
||||
void GBPatch8(struct LR35902Core* cpu, uint16_t address, int8_t value, int8_t* old, int segment);
|
||||
|
||||
struct GBSerializedState;
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/* 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/. */
|
||||
#ifndef GB_VIDEO_PROXY_H
|
||||
#define GB_VIDEO_PROXY_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba/internal/gb/video.h>
|
||||
#include <mgba/feature/video-logger.h>
|
||||
|
||||
struct GBVideoProxyRenderer {
|
||||
struct GBVideoRenderer d;
|
||||
struct GBVideoRenderer* backend;
|
||||
struct mVideoLogger* logger;
|
||||
|
||||
struct GBObj objThisLine[40];
|
||||
size_t oamMax;
|
||||
};
|
||||
|
||||
void GBVideoProxyRendererCreate(struct GBVideoProxyRenderer* renderer, struct GBVideoRenderer* backend);
|
||||
void GBVideoProxyRendererShim(struct GBVideo* video, struct GBVideoProxyRenderer* renderer);
|
||||
void GBVideoProxyRendererUnshim(struct GBVideo* video, struct GBVideoProxyRenderer* renderer);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -12,6 +12,7 @@ CXX_GUARD_START
|
|||
|
||||
#include <mgba/core/log.h>
|
||||
#include <mgba/core/timing.h>
|
||||
#include <mgba/gb/interface.h>
|
||||
|
||||
#define MAX_GBS 2
|
||||
|
||||
|
@ -34,15 +35,6 @@ struct GBSIO {
|
|||
uint8_t pendingSB;
|
||||
};
|
||||
|
||||
struct GBSIODriver {
|
||||
struct GBSIO* p;
|
||||
|
||||
bool (*init)(struct GBSIODriver* driver);
|
||||
void (*deinit)(struct GBSIODriver* driver);
|
||||
void (*writeSB)(struct GBSIODriver* driver, uint8_t value);
|
||||
uint8_t (*writeSC)(struct GBSIODriver* driver, uint8_t value);
|
||||
};
|
||||
|
||||
DECL_BITFIELD(GBRegisterSC, uint8_t);
|
||||
DECL_BIT(GBRegisterSC, ShiftClock, 0);
|
||||
DECL_BIT(GBRegisterSC, ClockSpeed, 1);
|
||||
|
|
|
@ -20,9 +20,9 @@ enum {
|
|||
GB_VIDEO_VERTICAL_TOTAL_PIXELS = 154,
|
||||
|
||||
// TODO: Figure out exact lengths
|
||||
GB_VIDEO_MODE_2_LENGTH = 76,
|
||||
GB_VIDEO_MODE_3_LENGTH_BASE = 171,
|
||||
GB_VIDEO_MODE_0_LENGTH_BASE = 209,
|
||||
GB_VIDEO_MODE_2_LENGTH = 80,
|
||||
GB_VIDEO_MODE_3_LENGTH_BASE = 172,
|
||||
GB_VIDEO_MODE_0_LENGTH_BASE = 204,
|
||||
|
||||
GB_VIDEO_HORIZONTAL_LENGTH = 456,
|
||||
|
||||
|
@ -61,6 +61,7 @@ struct GBVideoRenderer {
|
|||
uint8_t (*writeVideoRegister)(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
|
||||
void (*writeVRAM)(struct GBVideoRenderer* renderer, uint16_t address);
|
||||
void (*writePalette)(struct GBVideoRenderer* renderer, int index, uint16_t value);
|
||||
void (*writeOAM)(struct GBVideoRenderer* renderer, uint16_t oam);
|
||||
void (*drawRange)(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* objOnLine, size_t nObj);
|
||||
void (*finishScanline)(struct GBVideoRenderer* renderer, int y);
|
||||
void (*finishFrame)(struct GBVideoRenderer* renderer);
|
||||
|
@ -71,6 +72,10 @@ struct GBVideoRenderer {
|
|||
uint8_t* vram;
|
||||
union GBOAM* oam;
|
||||
struct mTileCache* cache;
|
||||
|
||||
bool disableBG;
|
||||
bool disableOBJ;
|
||||
bool disableWIN;
|
||||
};
|
||||
|
||||
DECL_BITFIELD(GBRegisterLCDC, uint8_t);
|
||||
|
@ -119,6 +124,7 @@ struct GBVideo {
|
|||
int ocpIndex;
|
||||
bool ocpIncrement;
|
||||
|
||||
uint16_t dmgPalette[4];
|
||||
uint16_t palette[64];
|
||||
|
||||
int32_t frameCounter;
|
||||
|
@ -138,6 +144,8 @@ void GBVideoWriteLYC(struct GBVideo* video, uint8_t value);
|
|||
void GBVideoWritePalette(struct GBVideo* video, uint16_t address, uint8_t value);
|
||||
void GBVideoSwitchBank(struct GBVideo* video, uint8_t value);
|
||||
|
||||
void GBVideoSetPalette(struct GBVideo* video, unsigned index, uint16_t color);
|
||||
|
||||
struct GBSerializedState;
|
||||
void GBVideoSerialize(const struct GBVideo* video, struct GBSerializedState* state);
|
||||
void GBVideoDeserialize(struct GBVideo* video, const struct GBSerializedState* state);
|
||||
|
|
|
@ -99,7 +99,6 @@ struct GBA {
|
|||
|
||||
struct mAVStream* stream;
|
||||
struct mKeyCallback* keyCallback;
|
||||
struct mStopCallback* stopCallback;
|
||||
struct mCoreCallbacksList coreCallbacks;
|
||||
|
||||
enum GBAIdleLoopOptimization idleOptimization;
|
||||
|
@ -176,6 +175,8 @@ bool GBAIsBIOS(struct VFile* vf);
|
|||
void GBAGetGameCode(const struct GBA* gba, char* out);
|
||||
void GBAGetGameTitle(const struct GBA* gba, char* out);
|
||||
|
||||
void GBATestKeypadIRQ(struct GBA* gba);
|
||||
|
||||
void GBAFrameStarted(struct GBA* gba);
|
||||
void GBAFrameEnded(struct GBA* gba);
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/* 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/. */
|
||||
#ifndef GBA_VIDEO_PROXY_H
|
||||
#define GBA_VIDEO_PROXY_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba/internal/gba/video.h>
|
||||
#include <mgba/feature/video-logger.h>
|
||||
|
||||
struct GBAVideoProxyRenderer {
|
||||
struct GBAVideoRenderer d;
|
||||
struct GBAVideoRenderer* backend;
|
||||
struct mVideoLogger* logger;
|
||||
};
|
||||
|
||||
void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct GBAVideoRenderer* backend);
|
||||
void GBAVideoProxyRendererShim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer);
|
||||
void GBAVideoProxyRendererUnshim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -334,9 +334,6 @@ struct VDir;
|
|||
void GBASerialize(struct GBA* gba, struct GBASerializedState* state);
|
||||
bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state);
|
||||
|
||||
struct GBASerializedState* GBAAllocateState(void);
|
||||
void GBADeallocateState(struct GBASerializedState* state);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
||||
|
|
|
@ -12,6 +12,9 @@ CXX_GUARD_START
|
|||
|
||||
#include <mgba/internal/debugger/debugger.h>
|
||||
|
||||
#include <mgba/internal/lr35902/lr35902.h>
|
||||
|
||||
|
||||
struct LR35902DebugBreakpoint {
|
||||
uint16_t address;
|
||||
int segment;
|
||||
|
@ -23,6 +26,12 @@ struct LR35902DebugWatchpoint {
|
|||
enum mWatchpointType type;
|
||||
};
|
||||
|
||||
struct LR35902Segment {
|
||||
uint16_t start;
|
||||
uint16_t end;
|
||||
const char* name;
|
||||
};
|
||||
|
||||
DECLARE_VECTOR(LR35902DebugBreakpointList, struct LR35902DebugBreakpoint);
|
||||
DECLARE_VECTOR(LR35902DebugWatchpointList, struct LR35902DebugWatchpoint);
|
||||
|
||||
|
@ -32,6 +41,9 @@ struct LR35902Debugger {
|
|||
|
||||
struct LR35902DebugBreakpointList breakpoints;
|
||||
struct LR35902DebugWatchpointList watchpoints;
|
||||
struct LR35902Memory originalMemory;
|
||||
|
||||
const struct LR35902Segment* segments;
|
||||
};
|
||||
|
||||
struct mDebuggerPlatform* LR35902DebuggerPlatformCreate(void);
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/* 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/. */
|
||||
#ifndef LR35902_MEMORY_DEBUGGER_H
|
||||
#define LR35902_MEMORY_DEBUGGER_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
struct LR35902Debugger;
|
||||
|
||||
void LR35902DebuggerInstallMemoryShim(struct LR35902Debugger* debugger);
|
||||
void LR35902DebuggerRemoveMemoryShim(struct LR35902Debugger* debugger);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -54,6 +54,8 @@ struct LR35902Memory {
|
|||
uint8_t (*load8)(struct LR35902Core*, uint16_t address);
|
||||
void (*store8)(struct LR35902Core*, uint16_t address, int8_t value);
|
||||
|
||||
int (*currentSegment)(struct LR35902Core*, uint16_t address);
|
||||
|
||||
uint8_t* activeRegion;
|
||||
uint16_t activeMask;
|
||||
uint16_t activeRegionEnd;
|
||||
|
|
|
@ -44,6 +44,17 @@
|
|||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>gb</string>
|
||||
<string>gbc</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>Game Boy ROM Image</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
Trey Boyer
|
||||
Christopher Cole
|
||||
Jaime J. Denizard
|
||||
Fog
|
||||
Reilly Grant
|
||||
Philip Horton
|
||||
Jordan Jorgensen
|
||||
Joshua Minor
|
||||
Rohit Nirmal
|
||||
Rhys Powell
|
||||
rootfather
|
||||
Yuri Kunde Schlesner
|
||||
|
|
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
fish shader
|
||||
|
||||
algorithm and original implementation by Miloslav "drummyfish" Ciz
|
||||
(tastyfish@seznam.cz)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
precision highp float;
|
||||
|
||||
varying vec2 texCoord;
|
||||
uniform sampler2D tex;
|
||||
uniform vec2 texSize;
|
||||
|
||||
uniform float similarity_threshold;
|
||||
|
||||
#define screen_res 240,160
|
||||
|
||||
vec4 texel_fetch(sampler2D t, ivec2 c) // because GLSL TexelFetch is not supported
|
||||
{
|
||||
return texture2D(tex, (2 * vec2(c) + vec2(1,1)) / (2 * vec2(screen_res)) );
|
||||
}
|
||||
|
||||
float pixel_brightness(vec4 pixel)
|
||||
{
|
||||
return 0.21 * pixel.x + 0.72 * pixel.y + 0.07 * pixel.z;
|
||||
}
|
||||
|
||||
bool pixel_is_brighter(vec4 pixel1, vec4 pixel2)
|
||||
{
|
||||
return pixel_brightness(pixel1) > pixel_brightness(pixel2);
|
||||
}
|
||||
|
||||
vec3 pixel_to_yuv(vec4 pixel)
|
||||
{
|
||||
float y = 0.299 * pixel.x + 0.587 * pixel.y + 0.114 * pixel.z;
|
||||
return vec3(y, 0.492 * (pixel.z - y), 0.877 * (pixel.x - y));
|
||||
}
|
||||
|
||||
bool yuvs_are_similar(vec3 yuv1, vec3 yuv2)
|
||||
{
|
||||
vec3 yuv_difference = abs(yuv1 - yuv2);
|
||||
return yuv_difference.x <= similarity_threshold && yuv_difference.y <= similarity_threshold && yuv_difference.z <= similarity_threshold;
|
||||
}
|
||||
|
||||
bool pixels_are_similar(vec4 pixel1, vec4 pixel2)
|
||||
{
|
||||
vec3 yuv1 = pixel_to_yuv(pixel1);
|
||||
vec3 yuv2 = pixel_to_yuv(pixel2);
|
||||
|
||||
return yuvs_are_similar(yuv1, yuv2);
|
||||
}
|
||||
|
||||
vec4 interpolate_nondiagonal(vec4 neighbour1, vec4 neighbour2)
|
||||
{
|
||||
if (pixels_are_similar(neighbour1,neighbour2))
|
||||
return mix(neighbour1,neighbour2,0.5);
|
||||
else
|
||||
return pixel_is_brighter(neighbour1, neighbour2) ? neighbour1 : neighbour2;
|
||||
}
|
||||
|
||||
vec4 mix3(vec4 value1, vec4 value2, vec4 value3)
|
||||
{
|
||||
return (value1 + value2 + value3) / 3.0;
|
||||
}
|
||||
|
||||
vec4 straight_line(vec4 p0, vec4 p1, vec4 p2, vec4 p3)
|
||||
{
|
||||
return pixel_is_brighter(p2,p0) ? mix(p2,p3,0.5) : mix(p0,p1,0.5);
|
||||
}
|
||||
|
||||
vec4 corner(vec4 p0, vec4 p1, vec4 p2, vec4 p3)
|
||||
{
|
||||
return pixel_is_brighter(p1,p0) ? mix3(p1,p2,p3) : mix3(p0,p1,p2);
|
||||
}
|
||||
|
||||
vec4 interpolate_diagonal(vec4 a, vec4 b, vec4 c, vec4 d)
|
||||
{
|
||||
// a b
|
||||
// c d
|
||||
|
||||
vec3 a_yuv = pixel_to_yuv(a);
|
||||
vec3 b_yuv = pixel_to_yuv(b);
|
||||
vec3 c_yuv = pixel_to_yuv(c);
|
||||
vec3 d_yuv = pixel_to_yuv(d);
|
||||
|
||||
bool ad = yuvs_are_similar(a_yuv,d_yuv);
|
||||
bool bc = yuvs_are_similar(b_yuv,c_yuv);
|
||||
bool ab = yuvs_are_similar(a_yuv,b_yuv);
|
||||
bool cd = yuvs_are_similar(c_yuv,d_yuv);
|
||||
bool ac = yuvs_are_similar(a_yuv,c_yuv);
|
||||
bool bd = yuvs_are_similar(b_yuv,d_yuv);
|
||||
|
||||
if (ad && cd && ab) // all pixels are equal?
|
||||
return( mix(mix(a,b,0.5), mix(c,d,0.5), 0.5) );
|
||||
|
||||
else if (ac && cd && ! ab) // corner 1?
|
||||
return corner(b,a,d,c);
|
||||
else if (bd && cd && ! ab) // corner 2?
|
||||
return corner(a,b,c,d);
|
||||
else if (ac && ab && ! bd) // corner 3?
|
||||
return corner(d,c,b,a);
|
||||
else if (ab && bd && ! ac) // corner 4?
|
||||
return corner(c,a,d,b);
|
||||
|
||||
else if (ad && (!bc || pixel_is_brighter(b,a))) // diagonal line 1?
|
||||
return mix(a,d,0.5);
|
||||
else if (bc && (!ad || pixel_is_brighter(a,b))) // diagonal line 2?
|
||||
return mix(b,c,0.5);
|
||||
|
||||
else if (ab) // horizontal line 1?
|
||||
return straight_line(a,b,c,d);
|
||||
else if (cd) // horizontal line 2?
|
||||
return straight_line(c,d,a,b);
|
||||
|
||||
else if (ac) // vertical line 1?
|
||||
return straight_line(a,c,b,d);
|
||||
else if (bd) // vertical line 2?
|
||||
return straight_line(b,d,a,c);
|
||||
|
||||
return( mix(mix(a,b,0.5), mix(c,d,0.5), 0.5) );
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
ivec2 pixel_coords2 = ivec2(texCoord * vec2(screen_res) * 2);
|
||||
ivec2 pixel_coords = pixel_coords2 / 2;
|
||||
|
||||
bool x_even = mod(pixel_coords2.x,2) == 0;
|
||||
bool y_even = mod(pixel_coords2.y,2) == 0;
|
||||
|
||||
if (x_even)
|
||||
{
|
||||
if (y_even)
|
||||
{
|
||||
|
||||
gl_FragColor = interpolate_diagonal(
|
||||
texel_fetch(tex, pixel_coords + ivec2(-1,-1)),
|
||||
texel_fetch(tex, pixel_coords + ivec2(0,-1)),
|
||||
texel_fetch(tex, pixel_coords + ivec2(-1,0)),
|
||||
texel_fetch(tex, pixel_coords + ivec2(0,0))
|
||||
);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
gl_FragColor = interpolate_nondiagonal
|
||||
(
|
||||
texel_fetch(tex, pixel_coords + ivec2(-1,0)),
|
||||
texel_fetch(tex, pixel_coords)
|
||||
);
|
||||
}
|
||||
}
|
||||
else if (y_even)
|
||||
{
|
||||
gl_FragColor = interpolate_nondiagonal
|
||||
(
|
||||
texel_fetch(tex, pixel_coords + ivec2(0,-1)),
|
||||
texel_fetch(tex, pixel_coords)
|
||||
);
|
||||
}
|
||||
else
|
||||
gl_FragColor = texel_fetch(tex, pixel_coords);
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
[shader]
|
||||
name=fish
|
||||
author=Drummyfish
|
||||
description=Attempts to keep thin lines thin.
|
||||
passes=1
|
||||
|
||||
[pass.0]
|
||||
fragmentShader=fish.fs
|
||||
integerScaling=1
|
||||
|
||||
[pass.0.uniform.similarity_threshold]
|
||||
type=float
|
||||
default=0.2
|
||||
readableName=Similarity Threshold
|
||||
min=0
|
||||
max=1
|
|
@ -0,0 +1,34 @@
|
|||
varying vec2 texCoord;
|
||||
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);
|
||||
|
||||
void main() {
|
||||
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)));
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
[shader]
|
||||
name=GBA Color
|
||||
author=Pokefan531 and hunterk
|
||||
description=Modifies the color output to simulate the GBA LCD characteristics.
|
||||
passes=1
|
||||
|
||||
[pass.0]
|
||||
fragmentShader=gba-color.fs
|
||||
blend=1
|
||||
|
||||
[pass.0.uniform.darken_screen]
|
||||
type=float
|
||||
default=0.5
|
||||
readableName=Darken Screen
|
|
@ -48,10 +48,10 @@ static void ARMDebuggerDeinit(struct mDebuggerPlatform* platform);
|
|||
|
||||
static void ARMDebuggerEnter(struct mDebuggerPlatform* d, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info);
|
||||
|
||||
static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform*, uint32_t address);
|
||||
static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform*, uint32_t address);
|
||||
static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform*, uint32_t address, enum mWatchpointType type);
|
||||
static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address);
|
||||
static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type);
|
||||
static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform*);
|
||||
static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform*);
|
||||
|
||||
|
@ -157,14 +157,16 @@ void ARMDebuggerClearSoftwareBreakpoint(struct mDebuggerPlatform* d, uint32_t ad
|
|||
}
|
||||
}
|
||||
|
||||
static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t address) {
|
||||
static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
|
||||
UNUSED(segment);
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->breakpoints);
|
||||
breakpoint->address = address;
|
||||
breakpoint->isSw = false;
|
||||
}
|
||||
|
||||
static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform* d, uint32_t address) {
|
||||
static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
|
||||
UNUSED(segment);
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
struct ARMDebugBreakpointList* breakpoints = &debugger->breakpoints;
|
||||
size_t i;
|
||||
|
@ -180,7 +182,8 @@ static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform* d) {
|
|||
return ARMDebugBreakpointListSize(&debugger->breakpoints) || ARMDebugWatchpointListSize(&debugger->watchpoints);
|
||||
}
|
||||
|
||||
static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t address, enum mWatchpointType type) {
|
||||
static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type) {
|
||||
UNUSED(segment);
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
if (!ARMDebugWatchpointListSize(&debugger->watchpoints)) {
|
||||
ARMDebuggerInstallMemoryShim(debugger);
|
||||
|
@ -190,7 +193,8 @@ static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t addre
|
|||
watchpoint->type = type;
|
||||
}
|
||||
|
||||
static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t address) {
|
||||
static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
|
||||
UNUSED(segment);
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
struct ARMDebugWatchpointList* watchpoints = &debugger->watchpoints;
|
||||
size_t i;
|
||||
|
|
|
@ -17,7 +17,6 @@ mLOG_DEFINE_CATEGORY(CHEATS, "Cheats", "core.cheats");
|
|||
|
||||
DEFINE_VECTOR(mCheatList, struct mCheat);
|
||||
DEFINE_VECTOR(mCheatSets, struct mCheatSet*);
|
||||
DEFINE_VECTOR(StringList, char*);
|
||||
|
||||
static int32_t _readMem(struct mCore* core, uint32_t address, int width) {
|
||||
switch (width) {
|
||||
|
|
|
@ -349,6 +349,9 @@ void mCoreConfigMap(const struct mCoreConfig* config, struct mCoreOptions* opts)
|
|||
if (_lookupIntValue(config, "lockAspectRatio", &fakeBool)) {
|
||||
opts->lockAspectRatio = fakeBool;
|
||||
}
|
||||
if (_lookupIntValue(config, "lockIntegerScaling", &fakeBool)) {
|
||||
opts->lockIntegerScaling = fakeBool;
|
||||
}
|
||||
if (_lookupIntValue(config, "resampleVideo", &fakeBool)) {
|
||||
opts->resampleVideo = fakeBool;
|
||||
}
|
||||
|
@ -396,6 +399,7 @@ void mCoreConfigLoadDefaults(struct mCoreConfig* config, const struct mCoreOptio
|
|||
ConfigurationSetIntValue(&config->defaultsTable, 0, "volume", opts->volume);
|
||||
ConfigurationSetIntValue(&config->defaultsTable, 0, "mute", opts->mute);
|
||||
ConfigurationSetIntValue(&config->defaultsTable, 0, "lockAspectRatio", opts->lockAspectRatio);
|
||||
ConfigurationSetIntValue(&config->defaultsTable, 0, "lockIntegerScaling", opts->lockIntegerScaling);
|
||||
ConfigurationSetIntValue(&config->defaultsTable, 0, "resampleVideo", opts->resampleVideo);
|
||||
ConfigurationSetIntValue(&config->defaultsTable, 0, "suspendScreensaver", opts->suspendScreensaver);
|
||||
}
|
||||
|
|
|
@ -18,8 +18,11 @@
|
|||
#include <mgba/gba/core.h>
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
#endif
|
||||
#ifndef MINIMAL_CORE
|
||||
#include <mgba/feature/video-logger.h>
|
||||
#endif
|
||||
|
||||
static struct mCoreFilter {
|
||||
const static struct mCoreFilter {
|
||||
bool (*filter)(struct VFile*);
|
||||
struct mCore* (*open)(void);
|
||||
enum mPlatform platform;
|
||||
|
@ -37,7 +40,7 @@ struct mCore* mCoreFindVF(struct VFile* vf) {
|
|||
if (!vf) {
|
||||
return NULL;
|
||||
}
|
||||
struct mCoreFilter* filter;
|
||||
const struct mCoreFilter* filter;
|
||||
for (filter = &_filters[0]; filter->filter; ++filter) {
|
||||
if (filter->filter(vf)) {
|
||||
break;
|
||||
|
@ -46,6 +49,9 @@ struct mCore* mCoreFindVF(struct VFile* vf) {
|
|||
if (filter->open) {
|
||||
return filter->open();
|
||||
}
|
||||
#ifndef MINIMAL_CORE
|
||||
return mVideoLogCoreFind(vf);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -53,7 +59,7 @@ enum mPlatform mCoreIsCompatible(struct VFile* vf) {
|
|||
if (!vf) {
|
||||
return false;
|
||||
}
|
||||
struct mCoreFilter* filter;
|
||||
const struct mCoreFilter* filter;
|
||||
for (filter = &_filters[0]; filter->filter; ++filter) {
|
||||
if (filter->filter(vf)) {
|
||||
return filter->platform;
|
||||
|
@ -115,6 +121,35 @@ bool mCoreLoadFile(struct mCore* core, const char* path) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool mCorePreloadVF(struct mCore* core, struct VFile* vf) {
|
||||
struct VFile* vfm = VFileMemChunk(NULL, vf->size(vf));
|
||||
uint8_t buffer[2048];
|
||||
ssize_t read;
|
||||
vf->seek(vf, 0, SEEK_SET);
|
||||
while ((read = vf->read(vf, buffer, sizeof(buffer))) > 0) {
|
||||
vfm->write(vfm, buffer, read);
|
||||
}
|
||||
vf->close(vf);
|
||||
bool ret = core->loadROM(core, vfm);
|
||||
if (!ret) {
|
||||
vfm->close(vfm);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool mCorePreloadFile(struct mCore* core, const char* path) {
|
||||
struct VFile* rom = mDirectorySetOpenPath(&core->dirs, path, core->isROM);
|
||||
if (!rom) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ret = mCorePreloadVF(core, rom);
|
||||
if (!ret) {
|
||||
rom->close(rom);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool mCoreAutoloadSave(struct mCore* core) {
|
||||
return core->loadSave(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.save, ".sav", O_CREAT | O_RDWR));
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
#include <inttypes.h>
|
||||
|
||||
#define SECTION_NAME_MAX 128
|
||||
#define SECTION_NAME_MAX 50
|
||||
#define KEY_NAME_MAX 32
|
||||
#define KEY_VALUE_MAX 16
|
||||
#define AXIS_INFO_MAX 12
|
||||
|
@ -563,7 +563,7 @@ void mInputUnbindHat(struct mInputMap* map, uint32_t type, int id) {
|
|||
mInputHatListResize(&impl->hats, -1);
|
||||
} else {
|
||||
struct mInputHatBindings* description = mInputHatListGetPointer(&impl->hats, id);
|
||||
memset(description, -1, sizeof(&description));
|
||||
memset(description, -1, sizeof(*description));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,13 @@
|
|||
|
||||
DEFINE_VECTOR(mCoreRewindPatches, struct PatchFast);
|
||||
|
||||
void mCoreRewindContextInit(struct mCoreRewindContext* context, size_t entries) {
|
||||
void _rewindDiff(struct mCoreRewindContext* context);
|
||||
|
||||
#ifndef DISABLE_THREADING
|
||||
THREAD_ENTRY _rewindThread(void* context);
|
||||
#endif
|
||||
|
||||
void mCoreRewindContextInit(struct mCoreRewindContext* context, size_t entries, bool onThread) {
|
||||
mCoreRewindPatchesInit(&context->patchMemory, entries);
|
||||
size_t e;
|
||||
for (e = 0; e < entries; ++e) {
|
||||
|
@ -22,9 +28,30 @@ void mCoreRewindContextInit(struct mCoreRewindContext* context, size_t entries)
|
|||
context->currentState = VFileMemChunk(0, 0);
|
||||
context->size = 0;
|
||||
context->stateFlags = SAVESTATE_SAVEDATA;
|
||||
#ifndef DISABLE_THREADING
|
||||
context->onThread = onThread;
|
||||
if (onThread) {
|
||||
MutexInit(&context->mutex);
|
||||
ConditionInit(&context->cond);
|
||||
ThreadCreate(&context->thread, _rewindThread, context);
|
||||
}
|
||||
#else
|
||||
UNUSED(onThread);
|
||||
#endif
|
||||
}
|
||||
|
||||
void mCoreRewindContextDeinit(struct mCoreRewindContext* context) {
|
||||
#ifndef DISABLE_THREADING
|
||||
if (context->onThread) {
|
||||
MutexLock(&context->mutex);
|
||||
context->onThread = false;
|
||||
MutexUnlock(&context->mutex);
|
||||
ConditionWake(&context->cond);
|
||||
ThreadJoin(context->thread);
|
||||
MutexDeinit(&context->mutex);
|
||||
ConditionDeinit(&context->cond);
|
||||
}
|
||||
#endif
|
||||
context->previousState->close(context->previousState);
|
||||
context->currentState->close(context->currentState);
|
||||
size_t s;
|
||||
|
@ -35,7 +62,26 @@ void mCoreRewindContextDeinit(struct mCoreRewindContext* context) {
|
|||
}
|
||||
|
||||
void mCoreRewindAppend(struct mCoreRewindContext* context, struct mCore* core) {
|
||||
#ifndef DISABLE_THREADING
|
||||
if (context->onThread) {
|
||||
MutexLock(&context->mutex);
|
||||
}
|
||||
#endif
|
||||
struct VFile* nextState = context->previousState;
|
||||
mCoreSaveStateNamed(core, nextState, context->stateFlags);
|
||||
context->previousState = context->currentState;
|
||||
context->currentState = nextState;
|
||||
#ifndef DISABLE_THREADING
|
||||
if (context->onThread) {
|
||||
ConditionWake(&context->cond);
|
||||
MutexUnlock(&context->mutex);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
_rewindDiff(context);
|
||||
}
|
||||
|
||||
void _rewindDiff(struct mCoreRewindContext* context) {
|
||||
++context->current;
|
||||
if (context->size < mCoreRewindPatchesSize(&context->patchMemory)) {
|
||||
++context->size;
|
||||
|
@ -43,25 +89,34 @@ void mCoreRewindAppend(struct mCoreRewindContext* context, struct mCore* core) {
|
|||
if (context->current >= mCoreRewindPatchesSize(&context->patchMemory)) {
|
||||
context->current = 0;
|
||||
}
|
||||
mCoreSaveStateNamed(core, nextState, context->stateFlags);
|
||||
struct PatchFast* patch = mCoreRewindPatchesGetPointer(&context->patchMemory, context->current);
|
||||
size_t size2 = nextState->size(nextState);
|
||||
size_t size = context->currentState->size(context->currentState);
|
||||
size_t size2 = context->currentState->size(context->currentState);
|
||||
size_t size = context->previousState->size(context->previousState);
|
||||
if (size2 > size) {
|
||||
context->currentState->truncate(context->currentState, size2);
|
||||
context->previousState->truncate(context->previousState, size2);
|
||||
size = size2;
|
||||
} else if (size > size2) {
|
||||
context->currentState->truncate(context->currentState, size);
|
||||
}
|
||||
void* current = context->currentState->map(context->currentState, size, MAP_READ);
|
||||
void* next = nextState->map(nextState, size, MAP_READ);
|
||||
void* current = context->previousState->map(context->previousState, size, MAP_READ);
|
||||
void* next = context->currentState->map(context->currentState, size, MAP_READ);
|
||||
diffPatchFast(patch, current, next, size);
|
||||
context->currentState->unmap(context->currentState, current, size);
|
||||
nextState->unmap(next, nextState, size);
|
||||
context->previousState = context->currentState;
|
||||
context->currentState = nextState;
|
||||
context->previousState->unmap(context->previousState, current, size);
|
||||
context->currentState->unmap(context->currentState, next, size);
|
||||
}
|
||||
|
||||
bool mCoreRewindRestore(struct mCoreRewindContext* context, struct mCore* core) {
|
||||
#ifndef DISABLE_THREADING
|
||||
if (context->onThread) {
|
||||
MutexLock(&context->mutex);
|
||||
}
|
||||
#endif
|
||||
if (!context->size) {
|
||||
#ifndef DISABLE_THREADING
|
||||
if (context->onThread) {
|
||||
MutexUnlock(&context->mutex);
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
--context->size;
|
||||
|
@ -86,5 +141,29 @@ bool mCoreRewindRestore(struct mCoreRewindContext* context, struct mCore* core)
|
|||
context->current = mCoreRewindPatchesSize(&context->patchMemory);
|
||||
}
|
||||
--context->current;
|
||||
#ifndef DISABLE_THREADING
|
||||
if (context->onThread) {
|
||||
MutexUnlock(&context->mutex);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_THREADING
|
||||
THREAD_ENTRY _rewindThread(void* context) {
|
||||
struct mCoreRewindContext* rewindContext = context;
|
||||
ThreadSetName("Rewind Diff Thread");
|
||||
MutexLock(&rewindContext->mutex);
|
||||
struct VFile* state = rewindContext->currentState;
|
||||
while (rewindContext->onThread) {
|
||||
if (rewindContext->currentState != state) {
|
||||
_rewindDiff(rewindContext);
|
||||
state = rewindContext->currentState;
|
||||
}
|
||||
ConditionWait(&rewindContext->cond, &rewindContext->mutex);
|
||||
}
|
||||
MutexUnlock(&rewindContext->mutex);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -120,6 +120,16 @@ void _crashed(void* context) {
|
|||
_changeState(thread, THREAD_CRASHED, true);
|
||||
}
|
||||
|
||||
void _coreSleep(void* context) {
|
||||
struct mCoreThread* thread = context;
|
||||
if (!thread) {
|
||||
return;
|
||||
}
|
||||
if (thread->sleepCallback) {
|
||||
thread->sleepCallback(thread);
|
||||
}
|
||||
}
|
||||
|
||||
static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
||||
struct mCoreThread* threadContext = context;
|
||||
#ifdef USE_PTHREADS
|
||||
|
@ -143,6 +153,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
|||
.videoFrameStarted = _frameStarted,
|
||||
.videoFrameEnded = _frameEnded,
|
||||
.coreCrashed = _crashed,
|
||||
.sleep = _coreSleep,
|
||||
.context = threadContext
|
||||
};
|
||||
core->addCoreCallbacks(core, &callbacks);
|
||||
|
@ -157,7 +168,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
|||
}
|
||||
|
||||
if (core->opts.rewindEnable && core->opts.rewindBufferCapacity > 0) {
|
||||
mCoreRewindContextInit(&threadContext->rewind, core->opts.rewindBufferCapacity);
|
||||
mCoreRewindContextInit(&threadContext->rewind, core->opts.rewindBufferCapacity, true);
|
||||
threadContext->rewind.stateFlags = core->opts.rewindSave ? SAVESTATE_SAVEDATA : 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include <mgba/internal/debugger/cli-debugger.h>
|
||||
|
||||
#include <mgba/internal/debugger/symbols.h>
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/core/version.h>
|
||||
#include <mgba/internal/debugger/parser.h>
|
||||
|
@ -135,6 +137,10 @@ static void _disassemble(struct CLIDebugger* debugger, struct CLIDebugVector* dv
|
|||
|
||||
static void _print(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
for (; dv; dv = dv->next) {
|
||||
if (dv->segmentValue >= 0) {
|
||||
debugger->backend->printf(debugger->backend, " $%02X:%04X", dv->segmentValue, dv->intValue);
|
||||
continue;
|
||||
}
|
||||
debugger->backend->printf(debugger->backend, " %u", dv->intValue);
|
||||
}
|
||||
debugger->backend->printf(debugger->backend, "\n");
|
||||
|
@ -209,7 +215,12 @@ static void _readByte(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
|||
return;
|
||||
}
|
||||
uint32_t address = dv->intValue;
|
||||
uint8_t value = debugger->d.core->busRead8(debugger->d.core, address);
|
||||
uint8_t value;
|
||||
if (dv->segmentValue >= 0) {
|
||||
value = debugger->d.core->rawRead8(debugger->d.core, address, dv->segmentValue);
|
||||
} else {
|
||||
value = debugger->d.core->busRead8(debugger->d.core, address);
|
||||
}
|
||||
debugger->backend->printf(debugger->backend, " 0x%02X\n", value);
|
||||
}
|
||||
|
||||
|
@ -225,7 +236,12 @@ static void _readHalfword(struct CLIDebugger* debugger, struct CLIDebugVector* d
|
|||
return;
|
||||
}
|
||||
uint32_t address = dv->intValue;
|
||||
uint16_t value = debugger->d.core->busRead16(debugger->d.core, address & ~1);
|
||||
uint16_t value;
|
||||
if (dv->segmentValue >= 0) {
|
||||
value = debugger->d.core->rawRead16(debugger->d.core, address & -1, dv->segmentValue);
|
||||
} else {
|
||||
value = debugger->d.core->busRead16(debugger->d.core, address & ~1);
|
||||
}
|
||||
debugger->backend->printf(debugger->backend, " 0x%04X\n", value);
|
||||
}
|
||||
|
||||
|
@ -235,7 +251,12 @@ static void _readWord(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
|||
return;
|
||||
}
|
||||
uint32_t address = dv->intValue;
|
||||
uint32_t value = debugger->d.core->busRead32(debugger->d.core, address & ~3);
|
||||
uint32_t value;
|
||||
if (dv->segmentValue >= 0) {
|
||||
value = debugger->d.core->rawRead32(debugger->d.core, address & -3, dv->segmentValue);
|
||||
} else {
|
||||
value = debugger->d.core->busRead32(debugger->d.core, address & ~3);
|
||||
}
|
||||
debugger->backend->printf(debugger->backend, " 0x%08X\n", value);
|
||||
}
|
||||
|
||||
|
@ -254,7 +275,11 @@ static void _writeByte(struct CLIDebugger* debugger, struct CLIDebugVector* dv)
|
|||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_OVERFLOW);
|
||||
return;
|
||||
}
|
||||
debugger->d.core->busWrite8(debugger->d.core, address, value);
|
||||
if (dv->segmentValue >= 0) {
|
||||
debugger->d.core->rawWrite8(debugger->d.core, address, value, dv->segmentValue);
|
||||
} else {
|
||||
debugger->d.core->busWrite8(debugger->d.core, address, value);
|
||||
}
|
||||
}
|
||||
|
||||
static void _writeHalfword(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
|
@ -272,7 +297,11 @@ static void _writeHalfword(struct CLIDebugger* debugger, struct CLIDebugVector*
|
|||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_OVERFLOW);
|
||||
return;
|
||||
}
|
||||
debugger->d.core->busWrite16(debugger->d.core, address, value);
|
||||
if (dv->segmentValue >= 0) {
|
||||
debugger->d.core->rawWrite16(debugger->d.core, address, value, dv->segmentValue);
|
||||
} else {
|
||||
debugger->d.core->busWrite16(debugger->d.core, address, value);
|
||||
}
|
||||
}
|
||||
|
||||
static void _writeWord(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
|
@ -286,7 +315,11 @@ static void _writeWord(struct CLIDebugger* debugger, struct CLIDebugVector* dv)
|
|||
}
|
||||
uint32_t address = dv->intValue;
|
||||
uint32_t value = dv->next->intValue;
|
||||
debugger->d.core->busWrite32(debugger->d.core, address, value);
|
||||
if (dv->segmentValue >= 0) {
|
||||
debugger->d.core->rawWrite32(debugger->d.core, address, value, dv->segmentValue);
|
||||
} else {
|
||||
debugger->d.core->busWrite32(debugger->d.core, address, value);
|
||||
}
|
||||
}
|
||||
|
||||
static void _dumpByte(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
|
@ -306,7 +339,12 @@ static void _dumpByte(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
|||
}
|
||||
debugger->backend->printf(debugger->backend, "0x%08X:", address);
|
||||
for (; line > 0; --line, ++address, --words) {
|
||||
uint32_t value = debugger->d.core->busRead8(debugger->d.core, address);
|
||||
uint32_t value;
|
||||
if (dv->segmentValue >= 0) {
|
||||
value = debugger->d.core->rawRead8(debugger->d.core, address, dv->segmentValue);
|
||||
} else {
|
||||
value = debugger->d.core->busRead8(debugger->d.core, address);
|
||||
}
|
||||
debugger->backend->printf(debugger->backend, " %02X", value);
|
||||
}
|
||||
debugger->backend->printf(debugger->backend, "\n");
|
||||
|
@ -330,7 +368,12 @@ static void _dumpHalfword(struct CLIDebugger* debugger, struct CLIDebugVector* d
|
|||
}
|
||||
debugger->backend->printf(debugger->backend, "0x%08X:", address);
|
||||
for (; line > 0; --line, address += 2, --words) {
|
||||
uint32_t value = debugger->d.core->busRead16(debugger->d.core, address);
|
||||
uint32_t value;
|
||||
if (dv->segmentValue >= 0) {
|
||||
value = debugger->d.core->rawRead16(debugger->d.core, address, dv->segmentValue);
|
||||
} else {
|
||||
value = debugger->d.core->busRead16(debugger->d.core, address);
|
||||
}
|
||||
debugger->backend->printf(debugger->backend, " %04X", value);
|
||||
}
|
||||
debugger->backend->printf(debugger->backend, "\n");
|
||||
|
@ -354,7 +397,12 @@ static void _dumpWord(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
|||
}
|
||||
debugger->backend->printf(debugger->backend, "0x%08X:", address);
|
||||
for (; line > 0; --line, address += 4, --words) {
|
||||
uint32_t value = debugger->d.core->busRead32(debugger->d.core, address);
|
||||
uint32_t value;
|
||||
if (dv->segmentValue >= 0) {
|
||||
value = debugger->d.core->rawRead32(debugger->d.core, address, dv->segmentValue);
|
||||
} else {
|
||||
value = debugger->d.core->busRead32(debugger->d.core, address);
|
||||
}
|
||||
debugger->backend->printf(debugger->backend, " %08X", value);
|
||||
}
|
||||
debugger->backend->printf(debugger->backend, "\n");
|
||||
|
@ -367,7 +415,7 @@ static void _setBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector*
|
|||
return;
|
||||
}
|
||||
uint32_t address = dv->intValue;
|
||||
debugger->d.platform->setBreakpoint(debugger->d.platform, address);
|
||||
debugger->d.platform->setBreakpoint(debugger->d.platform, address, dv->segmentValue);
|
||||
}
|
||||
|
||||
static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
|
@ -380,7 +428,7 @@ static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector*
|
|||
return;
|
||||
}
|
||||
uint32_t address = dv->intValue;
|
||||
debugger->d.platform->setWatchpoint(debugger->d.platform, address, WATCHPOINT_RW);
|
||||
debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_RW);
|
||||
}
|
||||
|
||||
static void _setReadWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
|
@ -393,7 +441,7 @@ static void _setReadWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVect
|
|||
return;
|
||||
}
|
||||
uint32_t address = dv->intValue;
|
||||
debugger->d.platform->setWatchpoint(debugger->d.platform, address, WATCHPOINT_READ);
|
||||
debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_READ);
|
||||
}
|
||||
|
||||
static void _setWriteWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
|
@ -406,7 +454,7 @@ static void _setWriteWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVec
|
|||
return;
|
||||
}
|
||||
uint32_t address = dv->intValue;
|
||||
debugger->d.platform->setWatchpoint(debugger->d.platform, address, WATCHPOINT_WRITE);
|
||||
debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_WRITE);
|
||||
}
|
||||
|
||||
static void _clearBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
|
@ -415,9 +463,9 @@ static void _clearBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector
|
|||
return;
|
||||
}
|
||||
uint32_t address = dv->intValue;
|
||||
debugger->d.platform->clearBreakpoint(debugger->d.platform, address);
|
||||
debugger->d.platform->clearBreakpoint(debugger->d.platform, address, dv->segmentValue);
|
||||
if (debugger->d.platform->clearWatchpoint) {
|
||||
debugger->d.platform->clearWatchpoint(debugger->d.platform, address);
|
||||
debugger->d.platform->clearWatchpoint(debugger->d.platform, address, dv->segmentValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -455,7 +503,11 @@ static uint32_t _performOperation(enum Operation operation, uint32_t current, ui
|
|||
static void _lookupIdentifier(struct mDebugger* debugger, const char* name, struct CLIDebugVector* dv) {
|
||||
struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
|
||||
if (cliDebugger->system) {
|
||||
uint32_t value = cliDebugger->system->lookupPlatformIdentifier(cliDebugger->system, name, dv);
|
||||
uint32_t value;
|
||||
if (debugger->core->symbolTable && mDebuggerSymbolLookup(debugger->core->symbolTable, name, &dv->intValue, &dv->segmentValue)) {
|
||||
return;
|
||||
}
|
||||
value = cliDebugger->system->lookupPlatformIdentifier(cliDebugger->system, name, dv);
|
||||
if (dv->type != CLIDV_ERROR_TYPE) {
|
||||
dv->intValue = value;
|
||||
return;
|
||||
|
@ -751,6 +803,9 @@ void CLIDebuggerAttachSystem(struct CLIDebugger* debugger, struct CLIDebuggerSys
|
|||
}
|
||||
|
||||
void CLIDebuggerAttachBackend(struct CLIDebugger* debugger, struct CLIDebuggerBackend* backend) {
|
||||
if (debugger->backend == backend) {
|
||||
return;
|
||||
}
|
||||
if (debugger->backend && debugger->backend->deinit) {
|
||||
debugger->backend->deinit(debugger->backend);
|
||||
}
|
||||
|
|
|
@ -62,6 +62,9 @@ void mDebuggerAttach(struct mDebugger* debugger, struct mCore* core) {
|
|||
debugger->d.init = mDebuggerInit;
|
||||
debugger->d.deinit = mDebuggerDeinit;
|
||||
debugger->core = core;
|
||||
if (!debugger->core->symbolTable) {
|
||||
debugger->core->loadSymbols(debugger->core, NULL);
|
||||
}
|
||||
debugger->platform = core->debuggerPlatform(core);
|
||||
debugger->platform->p = debugger;
|
||||
core->attachDebugger(core, debugger);
|
||||
|
|
|
@ -321,13 +321,31 @@ static void _readGPRs(struct GDBStub* stub, const char* message) {
|
|||
UNUSED(message);
|
||||
int r;
|
||||
int i = 0;
|
||||
|
||||
// General purpose registers
|
||||
for (r = 0; r < ARM_PC; ++r) {
|
||||
_int2hex32(cpu->gprs[r], &stub->outgoing[i]);
|
||||
i += 8;
|
||||
}
|
||||
|
||||
// Program counter
|
||||
_int2hex32(cpu->gprs[ARM_PC] - (cpu->cpsr.t ? WORD_SIZE_THUMB : WORD_SIZE_ARM), &stub->outgoing[i]);
|
||||
i += 8;
|
||||
|
||||
// Floating point registers, unused on the GBA (8 of them, 24 bits each)
|
||||
for (r = 0; r < 8 * 3; ++r) {
|
||||
_int2hex32(0, &stub->outgoing[i]);
|
||||
i += 8;
|
||||
}
|
||||
|
||||
// Floating point status, unused on the GBA (32 bits)
|
||||
_int2hex32(0, &stub->outgoing[i]);
|
||||
i += 8;
|
||||
|
||||
// CPU status
|
||||
_int2hex32(cpu->cpsr.packed, &stub->outgoing[i]);
|
||||
i += 8;
|
||||
|
||||
stub->outgoing[i] = 0;
|
||||
_sendMessage(stub);
|
||||
}
|
||||
|
@ -477,16 +495,16 @@ static void _setBreakpoint(struct GDBStub* stub, const char* message) {
|
|||
ARMDebuggerSetSoftwareBreakpoint(stub->d.platform, address, kind == 2 ? MODE_THUMB : MODE_ARM);
|
||||
break;
|
||||
case '1':
|
||||
stub->d.platform->setBreakpoint(stub->d.platform, address);
|
||||
stub->d.platform->setBreakpoint(stub->d.platform, address, -1);
|
||||
break;
|
||||
case '2':
|
||||
stub->d.platform->setWatchpoint(stub->d.platform, address, WATCHPOINT_WRITE);
|
||||
stub->d.platform->setWatchpoint(stub->d.platform, address, -1, WATCHPOINT_WRITE);
|
||||
break;
|
||||
case '3':
|
||||
stub->d.platform->setWatchpoint(stub->d.platform, address, WATCHPOINT_READ);
|
||||
stub->d.platform->setWatchpoint(stub->d.platform, address, -1, WATCHPOINT_READ);
|
||||
break;
|
||||
case '4':
|
||||
stub->d.platform->setWatchpoint(stub->d.platform, address, WATCHPOINT_RW);
|
||||
stub->d.platform->setWatchpoint(stub->d.platform, address, -1, WATCHPOINT_RW);
|
||||
break;
|
||||
default:
|
||||
stub->outgoing[0] = '\0';
|
||||
|
@ -506,12 +524,12 @@ static void _clearBreakpoint(struct GDBStub* stub, const char* message) {
|
|||
ARMDebuggerClearSoftwareBreakpoint(stub->d.platform, address);
|
||||
break;
|
||||
case '1':
|
||||
stub->d.platform->clearBreakpoint(stub->d.platform, address);
|
||||
stub->d.platform->clearBreakpoint(stub->d.platform, address, -1);
|
||||
break;
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
stub->d.platform->clearWatchpoint(stub->d.platform, address);
|
||||
stub->d.platform->clearWatchpoint(stub->d.platform, address, -1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/* 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/debugger/symbols.h>
|
||||
|
||||
#include <mgba-util/table.h>
|
||||
|
||||
struct mDebuggerSymbol {
|
||||
int32_t value;
|
||||
int segment;
|
||||
};
|
||||
|
||||
struct mDebuggerSymbols {
|
||||
struct Table names;
|
||||
};
|
||||
|
||||
struct mDebuggerSymbols* mDebuggerSymbolTableCreate(void) {
|
||||
struct mDebuggerSymbols* st = malloc(sizeof(*st));
|
||||
HashTableInit(&st->names, 0, free);
|
||||
return st;
|
||||
}
|
||||
|
||||
void mDebuggerSymbolTableDestroy(struct mDebuggerSymbols* st) {
|
||||
HashTableDeinit(&st->names);
|
||||
free(st);
|
||||
}
|
||||
|
||||
bool mDebuggerSymbolLookup(const struct mDebuggerSymbols* st, const char* name, int32_t* value, int* segment) {
|
||||
struct mDebuggerSymbol* sym = HashTableLookup(&st->names, name);
|
||||
if (!sym) {
|
||||
return false;
|
||||
}
|
||||
*value = sym->value;
|
||||
*segment = sym->segment;
|
||||
return true;
|
||||
}
|
||||
|
||||
void mDebuggerSymbolAdd(struct mDebuggerSymbols* st, const char* name, int32_t value, int segment) {
|
||||
struct mDebuggerSymbol* sym = malloc(sizeof(*sym));
|
||||
sym->value = value;
|
||||
sym->segment = segment;
|
||||
HashTableInsert(&st->names, name, sym);
|
||||
}
|
||||
|
||||
void mDebuggerSymbolRemove(struct mDebuggerSymbols* st, const char* name) {
|
||||
HashTableRemove(&st->names, name);
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
* 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 "commandline.h"
|
||||
#include <mgba/feature/commandline.h>
|
||||
|
||||
#include <mgba/core/config.h>
|
||||
#include <mgba/core/version.h>
|
||||
|
@ -38,6 +38,7 @@ static const struct option _options[] = {
|
|||
{ "gdb", no_argument, 0, 'g' },
|
||||
#endif
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
{ "log-level", required_argument, 0, 'l' },
|
||||
{ "movie", required_argument, 0, 'v' },
|
||||
{ "patch", required_argument, 0, 'p' },
|
||||
{ "version", no_argument, 0, '\0' },
|
||||
|
@ -47,10 +48,27 @@ static const struct option _options[] = {
|
|||
static bool _parseGraphicsArg(struct mSubParser* parser, int option, const char* arg);
|
||||
static void _applyGraphicsArgs(struct mSubParser* parser, struct mCoreConfig* config);
|
||||
|
||||
static void _tableInsert(struct Table* table, const char* pair) {
|
||||
char* eq = strchr(pair, '=');
|
||||
if (eq) {
|
||||
char option[128] = "";
|
||||
strncpy(option, pair, eq - pair);
|
||||
option[sizeof(option) - 1] = '\0';
|
||||
HashTableInsert(table, option, strdup(&eq[1]));
|
||||
} else {
|
||||
HashTableInsert(table, pair, strdup("1"));
|
||||
}
|
||||
}
|
||||
|
||||
static void _tableApply(const char* key, void* value, void* user) {
|
||||
struct mCoreConfig* config = user;
|
||||
mCoreConfigSetOverrideValue(config, key, value);
|
||||
}
|
||||
|
||||
bool parseArguments(struct mArguments* args, int argc, char* const* argv, struct mSubParser* subparser) {
|
||||
int ch;
|
||||
char options[64] =
|
||||
"b:c:hl:p:s:v:"
|
||||
"b:c:C:hl:p:s:v:"
|
||||
#ifdef USE_EDITLINE
|
||||
"d"
|
||||
#endif
|
||||
|
@ -61,6 +79,7 @@ bool parseArguments(struct mArguments* args, int argc, char* const* argv, struct
|
|||
memset(args, 0, sizeof(*args));
|
||||
args->frameskip = -1;
|
||||
args->logLevel = INT_MIN;
|
||||
HashTableInit(&args->configOverrides, 0, free);
|
||||
if (subparser && subparser->extraOptions) {
|
||||
// TODO: modularize options to subparsers
|
||||
strncat(options, subparser->extraOptions, sizeof(options) - strlen(options) - 1);
|
||||
|
@ -82,6 +101,9 @@ bool parseArguments(struct mArguments* args, int argc, char* const* argv, struct
|
|||
case 'c':
|
||||
args->cheatsFile = strdup(optarg);
|
||||
break;
|
||||
case 'C':
|
||||
_tableInsert(&args->configOverrides, optarg);
|
||||
break;
|
||||
#ifdef USE_EDITLINE
|
||||
case 'd':
|
||||
if (args->debuggerType != DEBUGGER_NONE) {
|
||||
|
@ -144,6 +166,7 @@ void applyArguments(const struct mArguments* args, struct mSubParser* subparser,
|
|||
if (args->bios) {
|
||||
mCoreConfigSetOverrideValue(config, "bios", args->bios);
|
||||
}
|
||||
HashTableEnumerate(&args->configOverrides, _tableApply, config);
|
||||
if (subparser) {
|
||||
subparser->apply(subparser, config);
|
||||
}
|
||||
|
@ -164,6 +187,8 @@ void freeArguments(struct mArguments* args) {
|
|||
|
||||
free(args->bios);
|
||||
args->bios = 0;
|
||||
|
||||
HashTableDeinit(&args->configOverrides);
|
||||
}
|
||||
|
||||
void initParserForGraphics(struct mSubParser* parser, struct mGraphicsOpts* opts) {
|
||||
|
@ -209,18 +234,20 @@ void _applyGraphicsArgs(struct mSubParser* parser, struct mCoreConfig* config) {
|
|||
void usage(const char* arg0, const char* extraOptions) {
|
||||
printf("usage: %s [option ...] file\n", arg0);
|
||||
puts("\nGeneric options:");
|
||||
puts(" -b, --bios FILE GBA BIOS file to use");
|
||||
puts(" -c, --cheats FILE Apply cheat codes from a file");
|
||||
puts(" -b, --bios FILE GBA BIOS file to use");
|
||||
puts(" -c, --cheats FILE Apply cheat codes from a file");
|
||||
puts(" -C, --config OPTION=VALUE Override config value");
|
||||
#ifdef USE_EDITLINE
|
||||
puts(" -d, --debug Use command-line debugger");
|
||||
puts(" -d, --debug Use command-line debugger");
|
||||
#endif
|
||||
#ifdef USE_GDB_STUB
|
||||
puts(" -g, --gdb Start GDB session (default port 2345)");
|
||||
puts(" -g, --gdb Start GDB session (default port 2345)");
|
||||
#endif
|
||||
puts(" -v, --movie FILE Play back a movie of recorded input");
|
||||
puts(" -p, --patch FILE Apply a specified patch file when running");
|
||||
puts(" -s, --frameskip N Skip every N frames");
|
||||
puts(" --version Print version and exit");
|
||||
puts(" -l, --log-level N Log level mask");
|
||||
puts(" -v, --movie FILE Play back a movie of recorded input");
|
||||
puts(" -p, --patch FILE Apply a specified patch file when running");
|
||||
puts(" -s, --frameskip N Skip every N frames");
|
||||
puts(" --version Print version and exit");
|
||||
if (extraOptions) {
|
||||
puts(extraOptions);
|
||||
}
|
||||
|
|
|
@ -304,6 +304,14 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
|
|||
}
|
||||
av_opt_set(encoder->video->priv_data, "tune", "zerolatency", 0);
|
||||
}
|
||||
|
||||
if (encoder->video->codec->id == AV_CODEC_ID_H264 &&
|
||||
(strcasecmp(encoder->containerFormat, "mp4") ||
|
||||
strcasecmp(encoder->containerFormat, "m4v") ||
|
||||
strcasecmp(encoder->containerFormat, "mov"))) {
|
||||
// QuickTime and a few other things require YUV420
|
||||
encoder->video->pix_fmt = AV_PIX_FMT_YUV420P;
|
||||
}
|
||||
avcodec_open2(encoder->video, vcodec, 0);
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 55
|
||||
encoder->videoFrame = av_frame_alloc();
|
||||
|
@ -320,7 +328,9 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
|
|||
avcodec_parameters_from_context(encoder->videoStream->codecpar, encoder->video);
|
||||
#endif
|
||||
|
||||
avio_open(&encoder->context->pb, outfile, AVIO_FLAG_WRITE);
|
||||
if (avio_open(&encoder->context->pb, outfile, AVIO_FLAG_WRITE) < 0) {
|
||||
return false;
|
||||
}
|
||||
return avformat_write_header(encoder->context, 0) >= 0;
|
||||
}
|
||||
|
||||
|
@ -389,27 +399,27 @@ void _ffmpegPostAudioFrame(struct mAVStream* stream, int16_t left, int16_t right
|
|||
encoder->audioBuffer[encoder->currentAudioSample * 2] = left;
|
||||
encoder->audioBuffer[encoder->currentAudioSample * 2 + 1] = right;
|
||||
|
||||
++encoder->currentAudioFrame;
|
||||
++encoder->currentAudioSample;
|
||||
|
||||
if ((encoder->currentAudioSample * 4) < encoder->audioBufferSize) {
|
||||
if (encoder->currentAudioSample * 4 < encoder->audioBufferSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
int channelSize = 2 * av_get_bytes_per_sample(encoder->audio->sample_fmt);
|
||||
avresample_convert(encoder->resampleContext,
|
||||
0, 0, 0,
|
||||
(uint8_t**) &encoder->audioBuffer, 0, encoder->audioBufferSize / 4);
|
||||
avresample_convert(encoder->resampleContext, 0, 0, 0,
|
||||
(uint8_t**) &encoder->audioBuffer, 0, encoder->audioBufferSize / 4);
|
||||
|
||||
encoder->currentAudioSample = 0;
|
||||
if (avresample_available(encoder->resampleContext) < encoder->audioFrame->nb_samples) {
|
||||
return;
|
||||
}
|
||||
#if LIBAVCODEC_VERSION_MAJOR >= 55
|
||||
av_frame_make_writable(encoder->audioFrame);
|
||||
#endif
|
||||
avresample_read(encoder->resampleContext, encoder->audioFrame->data, encoder->postaudioBufferSize / channelSize);
|
||||
int samples = avresample_read(encoder->resampleContext, encoder->audioFrame->data, encoder->postaudioBufferSize / channelSize);
|
||||
|
||||
encoder->audioFrame->pts = av_rescale_q(encoder->currentAudioFrame - encoder->currentAudioSample, encoder->audio->time_base, encoder->audioStream->time_base);
|
||||
encoder->currentAudioSample = 0;
|
||||
encoder->audioFrame->pts = av_rescale_q(encoder->currentAudioFrame, encoder->audio->time_base, encoder->audioStream->time_base);
|
||||
encoder->currentAudioFrame += samples;
|
||||
|
||||
AVPacket packet;
|
||||
av_init_packet(&packet);
|
||||
|
|
|
@ -298,7 +298,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
|
|||
return;
|
||||
}
|
||||
if (runner->core->platform(runner->core) == PLATFORM_GBA) {
|
||||
((struct GBA*) runner->core->board)->luminanceSource = &runner->luminanceSource.d;
|
||||
runner->core->setPeripheral(runner->core, mPERIPH_GBA_LUMINANCE, &runner->luminanceSource.d);
|
||||
}
|
||||
mLOG(GUI_RUNNER, DEBUG, "Loading config...");
|
||||
mCoreLoadForeignConfig(runner->core, &runner->config);
|
||||
|
|
|
@ -0,0 +1,195 @@
|
|||
/* 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/feature/thread-proxy.h>
|
||||
|
||||
#include <mgba/core/tile-cache.h>
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
#include <mgba/internal/gba/io.h>
|
||||
|
||||
#ifndef DISABLE_THREADING
|
||||
|
||||
static void mVideoThreadProxyInit(struct mVideoLogger* logger);
|
||||
static void mVideoThreadProxyReset(struct mVideoLogger* logger);
|
||||
static void mVideoThreadProxyDeinit(struct mVideoLogger* logger);
|
||||
|
||||
static THREAD_ENTRY _proxyThread(void* renderer);
|
||||
|
||||
static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length);
|
||||
static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block);
|
||||
|
||||
static void _lock(struct mVideoLogger* logger);
|
||||
static void _unlock(struct mVideoLogger* logger);
|
||||
static void _wait(struct mVideoLogger* logger);
|
||||
static void _wake(struct mVideoLogger* logger, int y);
|
||||
|
||||
void mVideoThreadProxyCreate(struct mVideoThreadProxy* renderer) {
|
||||
mVideoLoggerRendererCreate(&renderer->d, false);
|
||||
renderer->d.block = true;
|
||||
|
||||
renderer->d.init = mVideoThreadProxyInit;
|
||||
renderer->d.reset = mVideoThreadProxyReset;
|
||||
renderer->d.deinit = mVideoThreadProxyDeinit;
|
||||
renderer->d.lock = _lock;
|
||||
renderer->d.unlock = _unlock;
|
||||
renderer->d.wait = _wait;
|
||||
renderer->d.wake = _wake;
|
||||
|
||||
renderer->d.writeData = _writeData;
|
||||
renderer->d.readData = _readData;
|
||||
}
|
||||
|
||||
void mVideoThreadProxyInit(struct mVideoLogger* logger) {
|
||||
struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
|
||||
ConditionInit(&proxyRenderer->fromThreadCond);
|
||||
ConditionInit(&proxyRenderer->toThreadCond);
|
||||
MutexInit(&proxyRenderer->mutex);
|
||||
RingFIFOInit(&proxyRenderer->dirtyQueue, 0x40000);
|
||||
|
||||
proxyRenderer->threadState = PROXY_THREAD_IDLE;
|
||||
ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
|
||||
}
|
||||
|
||||
void mVideoThreadProxyReset(struct mVideoLogger* logger) {
|
||||
struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
|
||||
MutexLock(&proxyRenderer->mutex);
|
||||
while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
|
||||
ConditionWake(&proxyRenderer->toThreadCond);
|
||||
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
|
||||
}
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
}
|
||||
|
||||
void mVideoThreadProxyDeinit(struct mVideoLogger* logger) {
|
||||
struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
|
||||
bool waiting = false;
|
||||
MutexLock(&proxyRenderer->mutex);
|
||||
while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
|
||||
ConditionWake(&proxyRenderer->toThreadCond);
|
||||
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
|
||||
}
|
||||
if (proxyRenderer->threadState == PROXY_THREAD_IDLE) {
|
||||
proxyRenderer->threadState = PROXY_THREAD_STOPPED;
|
||||
ConditionWake(&proxyRenderer->toThreadCond);
|
||||
waiting = true;
|
||||
}
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
if (waiting) {
|
||||
ThreadJoin(proxyRenderer->thread);
|
||||
}
|
||||
ConditionDeinit(&proxyRenderer->fromThreadCond);
|
||||
ConditionDeinit(&proxyRenderer->toThreadCond);
|
||||
MutexDeinit(&proxyRenderer->mutex);
|
||||
}
|
||||
|
||||
void _proxyThreadRecover(struct mVideoThreadProxy* proxyRenderer) {
|
||||
MutexLock(&proxyRenderer->mutex);
|
||||
if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
return;
|
||||
}
|
||||
RingFIFOClear(&proxyRenderer->dirtyQueue);
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
ThreadJoin(proxyRenderer->thread);
|
||||
proxyRenderer->threadState = PROXY_THREAD_IDLE;
|
||||
ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
|
||||
}
|
||||
|
||||
static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length) {
|
||||
struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
|
||||
while (!RingFIFOWrite(&proxyRenderer->dirtyQueue, data, length)) {
|
||||
mLOG(GBA_VIDEO, DEBUG, "Can't write %"PRIz"u bytes. Proxy thread asleep?", length);
|
||||
MutexLock(&proxyRenderer->mutex);
|
||||
if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
|
||||
mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!");
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
return false;
|
||||
}
|
||||
ConditionWake(&proxyRenderer->toThreadCond);
|
||||
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block) {
|
||||
struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
|
||||
bool read = false;
|
||||
while (true) {
|
||||
read = RingFIFORead(&proxyRenderer->dirtyQueue, data, length);
|
||||
if (!block || read) {
|
||||
break;
|
||||
}
|
||||
mLOG(GBA_VIDEO, DEBUG, "Proxy thread can't read VRAM. CPU thread asleep?");
|
||||
MutexLock(&proxyRenderer->mutex);
|
||||
ConditionWake(&proxyRenderer->fromThreadCond);
|
||||
ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
static void _lock(struct mVideoLogger* logger) {
|
||||
struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
|
||||
MutexLock(&proxyRenderer->mutex);
|
||||
}
|
||||
|
||||
static void _wait(struct mVideoLogger* logger) {
|
||||
struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
|
||||
if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
|
||||
mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!");
|
||||
_proxyThreadRecover(proxyRenderer);
|
||||
return;
|
||||
}
|
||||
while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
|
||||
ConditionWake(&proxyRenderer->toThreadCond);
|
||||
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static void _unlock(struct mVideoLogger* logger) {
|
||||
struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
}
|
||||
|
||||
static void _wake(struct mVideoLogger* logger, int y) {
|
||||
struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
|
||||
if ((y & 15) == 15) {
|
||||
ConditionWake(&proxyRenderer->toThreadCond);
|
||||
}
|
||||
}
|
||||
|
||||
static THREAD_ENTRY _proxyThread(void* logger) {
|
||||
struct mVideoThreadProxy* proxyRenderer = logger;
|
||||
ThreadSetName("Proxy Renderer Thread");
|
||||
|
||||
MutexLock(&proxyRenderer->mutex);
|
||||
while (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
|
||||
ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
|
||||
if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
|
||||
break;
|
||||
}
|
||||
proxyRenderer->threadState = PROXY_THREAD_BUSY;
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
if (!mVideoLoggerRendererRun(&proxyRenderer->d, false)) {
|
||||
// FIFO was corrupted
|
||||
proxyRenderer->threadState = PROXY_THREAD_STOPPED;
|
||||
mLOG(GBA_VIDEO, ERROR, "Proxy thread queue got corrupted!");
|
||||
}
|
||||
MutexLock(&proxyRenderer->mutex);
|
||||
ConditionWake(&proxyRenderer->fromThreadCond);
|
||||
if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
|
||||
proxyRenderer->threadState = PROXY_THREAD_IDLE;
|
||||
}
|
||||
}
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
|
||||
#ifdef _3DS
|
||||
svcExitThread();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,951 @@
|
|||
/* 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/feature/video-logger.h>
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba-util/memory.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
#include <mgba-util/math.h>
|
||||
|
||||
#ifdef M_CORE_GBA
|
||||
#include <mgba/gba/core.h>
|
||||
#endif
|
||||
#ifdef M_CORE_GB
|
||||
#include <mgba/gb/core.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_ZLIB
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
#define BUFFER_BASE_SIZE 0x20000
|
||||
#define MAX_BLOCK_SIZE 0x800000
|
||||
|
||||
const char mVL_MAGIC[] = "mVL\0";
|
||||
|
||||
const static struct mVLDescriptor {
|
||||
enum mPlatform platform;
|
||||
struct mCore* (*open)(void);
|
||||
} _descriptors[] = {
|
||||
#ifdef M_CORE_GBA
|
||||
{ PLATFORM_GBA, GBAVideoLogPlayerCreate },
|
||||
#endif
|
||||
#ifdef M_CORE_GB
|
||||
{ PLATFORM_GB, GBVideoLogPlayerCreate },
|
||||
#endif
|
||||
{ PLATFORM_NONE, 0 }
|
||||
};
|
||||
|
||||
enum mVLBlockType {
|
||||
mVL_BLOCK_DUMMY = 0,
|
||||
mVL_BLOCK_INITIAL_STATE,
|
||||
mVL_BLOCK_CHANNEL_HEADER,
|
||||
mVL_BLOCK_DATA,
|
||||
mVL_BLOCK_FOOTER = 0x784C566D
|
||||
};
|
||||
|
||||
enum mVLHeaderFlag {
|
||||
mVL_FLAG_HAS_INITIAL_STATE = 1
|
||||
};
|
||||
|
||||
struct mVLBlockHeader {
|
||||
uint32_t blockType;
|
||||
uint32_t length;
|
||||
uint32_t channelId;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
enum mVLBlockFlag {
|
||||
mVL_FLAG_BLOCK_COMPRESSED = 1
|
||||
};
|
||||
|
||||
struct mVideoLogHeader {
|
||||
char magic[4];
|
||||
uint32_t flags;
|
||||
uint32_t platform;
|
||||
uint32_t nChannels;
|
||||
};
|
||||
|
||||
struct mVideoLogContext;
|
||||
struct mVideoLogChannel {
|
||||
struct mVideoLogContext* p;
|
||||
|
||||
uint32_t type;
|
||||
void* initialState;
|
||||
size_t initialStateSize;
|
||||
|
||||
off_t currentPointer;
|
||||
size_t bufferRemaining;
|
||||
#ifdef USE_ZLIB
|
||||
bool inflating;
|
||||
z_stream inflateStream;
|
||||
#endif
|
||||
|
||||
struct CircleBuffer buffer;
|
||||
};
|
||||
|
||||
struct mVideoLogContext {
|
||||
void* initialState;
|
||||
size_t initialStateSize;
|
||||
uint32_t nChannels;
|
||||
struct mVideoLogChannel channels[mVL_MAX_CHANNELS];
|
||||
|
||||
bool write;
|
||||
uint32_t activeChannel;
|
||||
struct VFile* backing;
|
||||
};
|
||||
|
||||
|
||||
static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length);
|
||||
static bool _writeNull(struct mVideoLogger* logger, const void* data, size_t length);
|
||||
static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block);
|
||||
|
||||
static ssize_t mVideoLoggerReadChannel(struct mVideoLogChannel* channel, void* data, size_t length);
|
||||
static ssize_t mVideoLoggerWriteChannel(struct mVideoLogChannel* channel, const void* data, size_t length);
|
||||
|
||||
static inline size_t _roundUp(size_t value, int shift) {
|
||||
value += (1 << shift) - 1;
|
||||
return value >> shift;
|
||||
}
|
||||
|
||||
void mVideoLoggerRendererCreate(struct mVideoLogger* logger, bool readonly) {
|
||||
if (readonly) {
|
||||
logger->writeData = _writeNull;
|
||||
logger->block = true;
|
||||
} else {
|
||||
logger->writeData = _writeData;
|
||||
}
|
||||
logger->readData = _readData;
|
||||
logger->dataContext = NULL;
|
||||
|
||||
logger->init = NULL;
|
||||
logger->deinit = NULL;
|
||||
logger->reset = NULL;
|
||||
|
||||
logger->lock = NULL;
|
||||
logger->unlock = NULL;
|
||||
logger->wait = NULL;
|
||||
logger->wake = NULL;
|
||||
}
|
||||
|
||||
void mVideoLoggerRendererInit(struct mVideoLogger* logger) {
|
||||
logger->palette = anonymousMemoryMap(logger->paletteSize);
|
||||
logger->vram = anonymousMemoryMap(logger->vramSize);
|
||||
logger->oam = anonymousMemoryMap(logger->oamSize);
|
||||
|
||||
logger->vramDirtyBitmap = calloc(_roundUp(logger->vramSize, 17), sizeof(uint32_t));
|
||||
logger->oamDirtyBitmap = calloc(_roundUp(logger->oamSize, 6), sizeof(uint32_t));
|
||||
|
||||
if (logger->init) {
|
||||
logger->init(logger);
|
||||
}
|
||||
}
|
||||
|
||||
void mVideoLoggerRendererDeinit(struct mVideoLogger* logger) {
|
||||
if (logger->deinit) {
|
||||
logger->deinit(logger);
|
||||
}
|
||||
|
||||
mappedMemoryFree(logger->palette, logger->paletteSize);
|
||||
mappedMemoryFree(logger->vram, logger->vramSize);
|
||||
mappedMemoryFree(logger->oam, logger->oamSize);
|
||||
|
||||
free(logger->vramDirtyBitmap);
|
||||
free(logger->oamDirtyBitmap);
|
||||
}
|
||||
|
||||
void mVideoLoggerRendererReset(struct mVideoLogger* logger) {
|
||||
memset(logger->vramDirtyBitmap, 0, sizeof(uint32_t) * _roundUp(logger->vramSize, 17));
|
||||
memset(logger->oamDirtyBitmap, 0, sizeof(uint32_t) * _roundUp(logger->oamSize, 6));
|
||||
|
||||
if (logger->reset) {
|
||||
logger->reset(logger);
|
||||
}
|
||||
}
|
||||
|
||||
void mVideoLoggerRendererWriteVideoRegister(struct mVideoLogger* logger, uint32_t address, uint16_t value) {
|
||||
struct mVideoLoggerDirtyInfo dirty = {
|
||||
DIRTY_REGISTER,
|
||||
address,
|
||||
value,
|
||||
0xDEADBEEF,
|
||||
};
|
||||
logger->writeData(logger, &dirty, sizeof(dirty));
|
||||
}
|
||||
|
||||
void mVideoLoggerRendererWriteVRAM(struct mVideoLogger* logger, uint32_t address) {
|
||||
int bit = 1 << (address >> 12);
|
||||
if (logger->vramDirtyBitmap[address >> 17] & bit) {
|
||||
return;
|
||||
}
|
||||
logger->vramDirtyBitmap[address >> 17] |= bit;
|
||||
}
|
||||
|
||||
void mVideoLoggerRendererWritePalette(struct mVideoLogger* logger, uint32_t address, uint16_t value) {
|
||||
struct mVideoLoggerDirtyInfo dirty = {
|
||||
DIRTY_PALETTE,
|
||||
address,
|
||||
value,
|
||||
0xDEADBEEF,
|
||||
};
|
||||
logger->writeData(logger, &dirty, sizeof(dirty));
|
||||
}
|
||||
|
||||
void mVideoLoggerRendererWriteOAM(struct mVideoLogger* logger, uint32_t address, uint16_t value) {
|
||||
struct mVideoLoggerDirtyInfo dirty = {
|
||||
DIRTY_OAM,
|
||||
address,
|
||||
value,
|
||||
0xDEADBEEF,
|
||||
};
|
||||
logger->writeData(logger, &dirty, sizeof(dirty));
|
||||
}
|
||||
|
||||
static void _flushVRAM(struct mVideoLogger* logger) {
|
||||
size_t i;
|
||||
for (i = 0; i < _roundUp(logger->vramSize, 17); ++i) {
|
||||
if (logger->vramDirtyBitmap[i]) {
|
||||
uint32_t bitmap = logger->vramDirtyBitmap[i];
|
||||
logger->vramDirtyBitmap[i] = 0;
|
||||
int j;
|
||||
for (j = 0; j < mVL_MAX_CHANNELS; ++j) {
|
||||
if (!(bitmap & (1 << j))) {
|
||||
continue;
|
||||
}
|
||||
struct mVideoLoggerDirtyInfo dirty = {
|
||||
DIRTY_VRAM,
|
||||
j * 0x1000,
|
||||
0x1000,
|
||||
0xDEADBEEF,
|
||||
};
|
||||
logger->writeData(logger, &dirty, sizeof(dirty));
|
||||
logger->writeData(logger, logger->vramBlock(logger, j * 0x1000), 0x1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mVideoLoggerRendererDrawScanline(struct mVideoLogger* logger, int y) {
|
||||
_flushVRAM(logger);
|
||||
struct mVideoLoggerDirtyInfo dirty = {
|
||||
DIRTY_SCANLINE,
|
||||
y,
|
||||
0,
|
||||
0xDEADBEEF,
|
||||
};
|
||||
logger->writeData(logger, &dirty, sizeof(dirty));
|
||||
}
|
||||
|
||||
void mVideoLoggerRendererDrawRange(struct mVideoLogger* logger, int startX, int endX, int y) {
|
||||
_flushVRAM(logger);
|
||||
struct mVideoLoggerDirtyInfo dirty = {
|
||||
DIRTY_RANGE,
|
||||
y,
|
||||
startX,
|
||||
endX,
|
||||
};
|
||||
logger->writeData(logger, &dirty, sizeof(dirty));
|
||||
}
|
||||
|
||||
void mVideoLoggerRendererFlush(struct mVideoLogger* logger) {
|
||||
struct mVideoLoggerDirtyInfo dirty = {
|
||||
DIRTY_FLUSH,
|
||||
0,
|
||||
0,
|
||||
0xDEADBEEF,
|
||||
};
|
||||
logger->writeData(logger, &dirty, sizeof(dirty));
|
||||
}
|
||||
|
||||
void mVideoLoggerRendererFinishFrame(struct mVideoLogger* logger) {
|
||||
struct mVideoLoggerDirtyInfo dirty = {
|
||||
DIRTY_FRAME,
|
||||
0,
|
||||
0,
|
||||
0xDEADBEEF,
|
||||
};
|
||||
logger->writeData(logger, &dirty, sizeof(dirty));
|
||||
}
|
||||
|
||||
void mVideoLoggerWriteBuffer(struct mVideoLogger* logger, uint32_t bufferId, uint32_t offset, uint32_t length, const void* data) {
|
||||
struct mVideoLoggerDirtyInfo dirty = {
|
||||
DIRTY_BUFFER,
|
||||
bufferId,
|
||||
offset,
|
||||
length,
|
||||
};
|
||||
logger->writeData(logger, &dirty, sizeof(dirty));
|
||||
logger->writeData(logger, data, length);
|
||||
}
|
||||
|
||||
bool mVideoLoggerRendererRun(struct mVideoLogger* logger, bool block) {
|
||||
struct mVideoLoggerDirtyInfo item = {0};
|
||||
while (logger->readData(logger, &item, sizeof(item), block)) {
|
||||
switch (item.type) {
|
||||
case DIRTY_REGISTER:
|
||||
case DIRTY_PALETTE:
|
||||
case DIRTY_OAM:
|
||||
case DIRTY_VRAM:
|
||||
case DIRTY_SCANLINE:
|
||||
case DIRTY_FLUSH:
|
||||
case DIRTY_FRAME:
|
||||
case DIRTY_RANGE:
|
||||
case DIRTY_BUFFER:
|
||||
if (!logger->parsePacket(logger, &item)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return !block;
|
||||
}
|
||||
|
||||
static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length) {
|
||||
struct mVideoLogChannel* channel = logger->dataContext;
|
||||
return mVideoLoggerWriteChannel(channel, data, length) == (ssize_t) length;
|
||||
}
|
||||
|
||||
static bool _writeNull(struct mVideoLogger* logger, const void* data, size_t length) {
|
||||
UNUSED(logger);
|
||||
UNUSED(data);
|
||||
UNUSED(length);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block) {
|
||||
UNUSED(block);
|
||||
struct mVideoLogChannel* channel = logger->dataContext;
|
||||
return mVideoLoggerReadChannel(channel, data, length) == (ssize_t) length;
|
||||
}
|
||||
|
||||
#ifdef USE_ZLIB
|
||||
static void _copyVf(struct VFile* dest, struct VFile* src) {
|
||||
size_t size = src->size(src);
|
||||
void* mem = src->map(src, size, MAP_READ);
|
||||
dest->write(dest, mem, size);
|
||||
src->unmap(src, mem, size);
|
||||
}
|
||||
|
||||
static void _compress(struct VFile* dest, struct VFile* src) {
|
||||
uint8_t writeBuffer[0x800];
|
||||
uint8_t compressBuffer[0x400];
|
||||
z_stream zstr;
|
||||
zstr.zalloc = Z_NULL;
|
||||
zstr.zfree = Z_NULL;
|
||||
zstr.opaque = Z_NULL;
|
||||
zstr.avail_in = 0;
|
||||
zstr.avail_out = sizeof(compressBuffer);
|
||||
zstr.next_out = (Bytef*) compressBuffer;
|
||||
if (deflateInit(&zstr, 9) != Z_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
size_t read = src->read(src, writeBuffer, sizeof(writeBuffer));
|
||||
if (!read) {
|
||||
break;
|
||||
}
|
||||
zstr.avail_in = read;
|
||||
zstr.next_in = (Bytef*) writeBuffer;
|
||||
while (zstr.avail_in) {
|
||||
if (deflate(&zstr, Z_NO_FLUSH) == Z_STREAM_ERROR) {
|
||||
break;
|
||||
}
|
||||
dest->write(dest, compressBuffer, sizeof(compressBuffer) - zstr.avail_out);
|
||||
zstr.avail_out = sizeof(compressBuffer);
|
||||
zstr.next_out = (Bytef*) compressBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
zstr.avail_out = sizeof(compressBuffer);
|
||||
zstr.next_out = (Bytef*) compressBuffer;
|
||||
zstr.avail_in = 0;
|
||||
int ret = deflate(&zstr, Z_FINISH);
|
||||
if (ret == Z_STREAM_ERROR) {
|
||||
break;
|
||||
}
|
||||
dest->write(dest, compressBuffer, sizeof(compressBuffer) - zstr.avail_out);
|
||||
} while (sizeof(compressBuffer) - zstr.avail_out);
|
||||
}
|
||||
|
||||
static bool _decompress(struct VFile* dest, struct VFile* src, size_t compressedLength) {
|
||||
uint8_t fbuffer[0x400];
|
||||
uint8_t zbuffer[0x800];
|
||||
z_stream zstr;
|
||||
zstr.zalloc = Z_NULL;
|
||||
zstr.zfree = Z_NULL;
|
||||
zstr.opaque = Z_NULL;
|
||||
zstr.avail_in = 0;
|
||||
zstr.avail_out = sizeof(zbuffer);
|
||||
zstr.next_out = (Bytef*) zbuffer;
|
||||
bool started = false;
|
||||
|
||||
while (true) {
|
||||
size_t thisWrite = sizeof(zbuffer);
|
||||
size_t thisRead = 0;
|
||||
if (zstr.avail_in) {
|
||||
zstr.next_out = zbuffer;
|
||||
zstr.avail_out = thisWrite;
|
||||
thisRead = zstr.avail_in;
|
||||
} else if (compressedLength) {
|
||||
thisRead = sizeof(fbuffer);
|
||||
if (thisRead > compressedLength) {
|
||||
thisRead = compressedLength;
|
||||
}
|
||||
|
||||
thisRead = src->read(src, fbuffer, thisRead);
|
||||
if (thisRead <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
zstr.next_in = fbuffer;
|
||||
zstr.avail_in = thisRead;
|
||||
zstr.next_out = zbuffer;
|
||||
zstr.avail_out = thisWrite;
|
||||
|
||||
if (!started) {
|
||||
if (inflateInit(&zstr) != Z_OK) {
|
||||
break;
|
||||
}
|
||||
started = true;
|
||||
}
|
||||
} else {
|
||||
zstr.next_in = Z_NULL;
|
||||
zstr.avail_in = 0;
|
||||
zstr.next_out = zbuffer;
|
||||
zstr.avail_out = thisWrite;
|
||||
}
|
||||
|
||||
int ret = inflate(&zstr, Z_NO_FLUSH);
|
||||
|
||||
if (zstr.next_in != Z_NULL) {
|
||||
thisRead -= zstr.avail_in;
|
||||
compressedLength -= thisRead;
|
||||
}
|
||||
|
||||
if (ret != Z_OK) {
|
||||
inflateEnd(&zstr);
|
||||
started = false;
|
||||
if (ret != Z_STREAM_END) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
thisWrite = dest->write(dest, zbuffer, thisWrite - zstr.avail_out);
|
||||
|
||||
if (!started) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return !compressedLength;
|
||||
}
|
||||
#endif
|
||||
|
||||
void mVideoLoggerAttachChannel(struct mVideoLogger* logger, struct mVideoLogContext* context, size_t channelId) {
|
||||
if (channelId >= mVL_MAX_CHANNELS) {
|
||||
return;
|
||||
}
|
||||
logger->dataContext = &context->channels[channelId];
|
||||
}
|
||||
|
||||
struct mVideoLogContext* mVideoLogContextCreate(struct mCore* core) {
|
||||
struct mVideoLogContext* context = malloc(sizeof(*context));
|
||||
memset(context, 0, sizeof(*context));
|
||||
|
||||
context->write = !!core;
|
||||
context->initialStateSize = 0;
|
||||
context->initialState = NULL;
|
||||
|
||||
if (core) {
|
||||
context->initialStateSize = core->stateSize(core);
|
||||
context->initialState = anonymousMemoryMap(context->initialStateSize);
|
||||
core->saveState(core, context->initialState);
|
||||
core->startVideoLog(core, context);
|
||||
}
|
||||
|
||||
context->activeChannel = 0;
|
||||
return context;
|
||||
}
|
||||
|
||||
void mVideoLogContextSetOutput(struct mVideoLogContext* context, struct VFile* vf) {
|
||||
context->backing = vf;
|
||||
vf->truncate(vf, 0);
|
||||
vf->seek(vf, 0, SEEK_SET);
|
||||
}
|
||||
|
||||
void mVideoLogContextWriteHeader(struct mVideoLogContext* context, struct mCore* core) {
|
||||
struct mVideoLogHeader header = { { 0 } };
|
||||
memcpy(header.magic, mVL_MAGIC, sizeof(header.magic));
|
||||
enum mPlatform platform = core->platform(core);
|
||||
STORE_32LE(platform, 0, &header.platform);
|
||||
STORE_32LE(context->nChannels, 0, &header.nChannels);
|
||||
|
||||
uint32_t flags = 0;
|
||||
if (context->initialState) {
|
||||
flags |= mVL_FLAG_HAS_INITIAL_STATE;
|
||||
}
|
||||
STORE_32LE(flags, 0, &header.flags);
|
||||
context->backing->write(context->backing, &header, sizeof(header));
|
||||
if (context->initialState) {
|
||||
struct mVLBlockHeader chheader = { 0 };
|
||||
STORE_32LE(mVL_BLOCK_INITIAL_STATE, 0, &chheader.blockType);
|
||||
#ifdef USE_ZLIB
|
||||
STORE_32LE(mVL_FLAG_BLOCK_COMPRESSED, 0, &chheader.flags);
|
||||
|
||||
struct VFile* vfm = VFileMemChunk(NULL, 0);
|
||||
struct VFile* src = VFileFromConstMemory(context->initialState, context->initialStateSize);
|
||||
_compress(vfm, src);
|
||||
src->close(src);
|
||||
STORE_32LE(vfm->size(vfm), 0, &chheader.length);
|
||||
context->backing->write(context->backing, &chheader, sizeof(chheader));
|
||||
_copyVf(context->backing, vfm);
|
||||
vfm->close(vfm);
|
||||
#else
|
||||
STORE_32LE(context->initialStateSize, 0, &chheader.length);
|
||||
context->backing->write(context->backing, &chheader, sizeof(chheader));
|
||||
context->backing->write(context->backing, context->initialState, context->initialStateSize);
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < context->nChannels; ++i) {
|
||||
struct mVLBlockHeader chheader = { 0 };
|
||||
STORE_32LE(mVL_BLOCK_CHANNEL_HEADER, 0, &chheader.blockType);
|
||||
STORE_32LE(i, 0, &chheader.channelId);
|
||||
context->backing->write(context->backing, &chheader, sizeof(chheader));
|
||||
}
|
||||
}
|
||||
|
||||
bool _readBlockHeader(struct mVideoLogContext* context, struct mVLBlockHeader* header) {
|
||||
struct mVLBlockHeader buffer;
|
||||
if (context->backing->read(context->backing, &buffer, sizeof(buffer)) != sizeof(buffer)) {
|
||||
return false;
|
||||
}
|
||||
LOAD_32LE(header->blockType, 0, &buffer.blockType);
|
||||
LOAD_32LE(header->length, 0, &buffer.length);
|
||||
LOAD_32LE(header->channelId, 0, &buffer.channelId);
|
||||
LOAD_32LE(header->flags, 0, &buffer.flags);
|
||||
|
||||
if (header->length > MAX_BLOCK_SIZE) {
|
||||
// Pre-emptively reject blocks that are too big.
|
||||
// If we encounter one, the file is probably corrupted.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _readHeader(struct mVideoLogContext* context) {
|
||||
struct mVideoLogHeader header;
|
||||
context->backing->seek(context->backing, 0, SEEK_SET);
|
||||
if (context->backing->read(context->backing, &header, sizeof(header)) != sizeof(header)) {
|
||||
return false;
|
||||
}
|
||||
if (memcmp(header.magic, mVL_MAGIC, sizeof(header.magic)) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LOAD_32LE(context->nChannels, 0, &header.nChannels);
|
||||
if (context->nChannels > mVL_MAX_CHANNELS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t flags;
|
||||
LOAD_32LE(flags, 0, &header.flags);
|
||||
if (flags & mVL_FLAG_HAS_INITIAL_STATE) {
|
||||
struct mVLBlockHeader header;
|
||||
if (!_readBlockHeader(context, &header)) {
|
||||
return false;
|
||||
}
|
||||
if (header.blockType != mVL_BLOCK_INITIAL_STATE || !header.length) {
|
||||
return false;
|
||||
}
|
||||
if (context->initialState) {
|
||||
mappedMemoryFree(context->initialState, context->initialStateSize);
|
||||
context->initialState = NULL;
|
||||
context->initialStateSize = 0;
|
||||
}
|
||||
if (header.flags & mVL_FLAG_BLOCK_COMPRESSED) {
|
||||
#ifdef USE_ZLIB
|
||||
struct VFile* vfm = VFileMemChunk(NULL, 0);
|
||||
if (!_decompress(vfm, context->backing, header.length)) {
|
||||
vfm->close(vfm);
|
||||
return false;
|
||||
}
|
||||
context->initialStateSize = vfm->size(vfm);
|
||||
context->initialState = anonymousMemoryMap(context->initialStateSize);
|
||||
void* mem = vfm->map(vfm, context->initialStateSize, MAP_READ);
|
||||
memcpy(context->initialState, mem, context->initialStateSize);
|
||||
vfm->unmap(vfm, mem, context->initialStateSize);
|
||||
vfm->close(vfm);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
} else {
|
||||
context->initialStateSize = header.length;
|
||||
context->initialState = anonymousMemoryMap(header.length);
|
||||
context->backing->read(context->backing, context->initialState, context->initialStateSize);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mVideoLogContextLoad(struct mVideoLogContext* context, struct VFile* vf) {
|
||||
context->backing = vf;
|
||||
|
||||
if (!_readHeader(context)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
off_t pointer = context->backing->seek(context->backing, 0, SEEK_CUR);
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < context->nChannels; ++i) {
|
||||
CircleBufferInit(&context->channels[i].buffer, BUFFER_BASE_SIZE);
|
||||
context->channels[i].bufferRemaining = 0;
|
||||
context->channels[i].currentPointer = pointer;
|
||||
context->channels[i].p = context;
|
||||
#ifdef USE_ZLIB
|
||||
context->channels[i].inflating = false;
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef USE_ZLIB
|
||||
static void _flushBufferCompressed(struct mVideoLogContext* context) {
|
||||
struct CircleBuffer* buffer = &context->channels[context->activeChannel].buffer;
|
||||
if (!CircleBufferSize(buffer)) {
|
||||
return;
|
||||
}
|
||||
struct VFile* vfm = VFileMemChunk(NULL, 0);
|
||||
struct VFile* src = VFileFIFO(buffer);
|
||||
_compress(vfm, src);
|
||||
src->close(src);
|
||||
|
||||
size_t size = vfm->size(vfm);
|
||||
|
||||
struct mVLBlockHeader header = { 0 };
|
||||
STORE_32LE(mVL_BLOCK_DATA, 0, &header.blockType);
|
||||
STORE_32LE(context->activeChannel, 0, &header.channelId);
|
||||
STORE_32LE(mVL_FLAG_BLOCK_COMPRESSED, 0, &header.flags);
|
||||
STORE_32LE(size, 0, &header.length);
|
||||
|
||||
context->backing->write(context->backing, &header, sizeof(header));
|
||||
_copyVf(context->backing, vfm);
|
||||
vfm->close(vfm);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void _flushBuffer(struct mVideoLogContext* context) {
|
||||
#ifdef USE_ZLIB
|
||||
// TODO: Make optional
|
||||
_flushBufferCompressed(context);
|
||||
return;
|
||||
#endif
|
||||
|
||||
struct CircleBuffer* buffer = &context->channels[context->activeChannel].buffer;
|
||||
if (!CircleBufferSize(buffer)) {
|
||||
return;
|
||||
}
|
||||
struct mVLBlockHeader header = { 0 };
|
||||
STORE_32LE(mVL_BLOCK_DATA, 0, &header.blockType);
|
||||
STORE_32LE(CircleBufferSize(buffer), 0, &header.length);
|
||||
STORE_32LE(context->activeChannel, 0, &header.channelId);
|
||||
|
||||
context->backing->write(context->backing, &header, sizeof(header));
|
||||
|
||||
uint8_t writeBuffer[0x800];
|
||||
while (CircleBufferSize(buffer)) {
|
||||
size_t read = CircleBufferRead(buffer, writeBuffer, sizeof(writeBuffer));
|
||||
context->backing->write(context->backing, writeBuffer, read);
|
||||
}
|
||||
}
|
||||
|
||||
void mVideoLogContextDestroy(struct mCore* core, struct mVideoLogContext* context) {
|
||||
if (context->write) {
|
||||
_flushBuffer(context);
|
||||
|
||||
struct mVLBlockHeader header = { 0 };
|
||||
STORE_32LE(mVL_BLOCK_FOOTER, 0, &header.blockType);
|
||||
context->backing->write(context->backing, &header, sizeof(header));
|
||||
}
|
||||
|
||||
if (core) {
|
||||
core->endVideoLog(core);
|
||||
}
|
||||
if (context->initialState) {
|
||||
mappedMemoryFree(context->initialState, context->initialStateSize);
|
||||
}
|
||||
free(context);
|
||||
}
|
||||
|
||||
void mVideoLogContextRewind(struct mVideoLogContext* context, struct mCore* core) {
|
||||
_readHeader(context);
|
||||
if (core && core->stateSize(core) == context->initialStateSize) {
|
||||
core->loadState(core, context->initialState);
|
||||
}
|
||||
|
||||
off_t pointer = context->backing->seek(context->backing, 0, SEEK_CUR);
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < context->nChannels; ++i) {
|
||||
CircleBufferClear(&context->channels[i].buffer);
|
||||
context->channels[i].bufferRemaining = 0;
|
||||
context->channels[i].currentPointer = pointer;
|
||||
#ifdef USE_ZLIB
|
||||
if (context->channels[i].inflating) {
|
||||
inflateEnd(&context->channels[i].inflateStream);
|
||||
context->channels[i].inflating = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void* mVideoLogContextInitialState(struct mVideoLogContext* context, size_t* size) {
|
||||
if (size) {
|
||||
*size = context->initialStateSize;
|
||||
}
|
||||
return context->initialState;
|
||||
}
|
||||
|
||||
int mVideoLoggerAddChannel(struct mVideoLogContext* context) {
|
||||
if (context->nChannels >= mVL_MAX_CHANNELS) {
|
||||
return -1;
|
||||
}
|
||||
int chid = context->nChannels;
|
||||
++context->nChannels;
|
||||
context->channels[chid].p = context;
|
||||
CircleBufferInit(&context->channels[chid].buffer, BUFFER_BASE_SIZE);
|
||||
return chid;
|
||||
}
|
||||
|
||||
#ifdef USE_ZLIB
|
||||
static size_t _readBufferCompressed(struct VFile* vf, struct mVideoLogChannel* channel, size_t length) {
|
||||
uint8_t fbuffer[0x400];
|
||||
uint8_t zbuffer[0x800];
|
||||
size_t read = 0;
|
||||
|
||||
// TODO: Share with _decompress
|
||||
channel->inflateStream.avail_in = 0;
|
||||
while (length) {
|
||||
size_t thisWrite = sizeof(zbuffer);
|
||||
if (thisWrite > length) {
|
||||
thisWrite = length;
|
||||
}
|
||||
|
||||
size_t thisRead = 0;
|
||||
if (channel->inflating && channel->inflateStream.avail_in) {
|
||||
channel->inflateStream.next_out = zbuffer;
|
||||
channel->inflateStream.avail_out = thisWrite;
|
||||
thisRead = channel->inflateStream.avail_in;
|
||||
} else if (channel->bufferRemaining) {
|
||||
thisRead = sizeof(fbuffer);
|
||||
if (thisRead > channel->bufferRemaining) {
|
||||
thisRead = channel->bufferRemaining;
|
||||
}
|
||||
|
||||
thisRead = vf->read(vf, fbuffer, thisRead);
|
||||
if (thisRead <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
channel->inflateStream.next_in = fbuffer;
|
||||
channel->inflateStream.avail_in = thisRead;
|
||||
channel->inflateStream.next_out = zbuffer;
|
||||
channel->inflateStream.avail_out = thisWrite;
|
||||
|
||||
if (!channel->inflating) {
|
||||
if (inflateInit(&channel->inflateStream) != Z_OK) {
|
||||
break;
|
||||
}
|
||||
channel->inflating = true;
|
||||
}
|
||||
} else {
|
||||
channel->inflateStream.next_in = Z_NULL;
|
||||
channel->inflateStream.avail_in = 0;
|
||||
channel->inflateStream.next_out = zbuffer;
|
||||
channel->inflateStream.avail_out = thisWrite;
|
||||
}
|
||||
|
||||
int ret = inflate(&channel->inflateStream, Z_NO_FLUSH);
|
||||
|
||||
if (channel->inflateStream.next_in != Z_NULL) {
|
||||
thisRead -= channel->inflateStream.avail_in;
|
||||
channel->currentPointer += thisRead;
|
||||
channel->bufferRemaining -= thisRead;
|
||||
}
|
||||
|
||||
if (ret != Z_OK) {
|
||||
inflateEnd(&channel->inflateStream);
|
||||
channel->inflating = false;
|
||||
if (ret != Z_STREAM_END) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
thisWrite = CircleBufferWrite(&channel->buffer, zbuffer, thisWrite - channel->inflateStream.avail_out);
|
||||
length -= thisWrite;
|
||||
read += thisWrite;
|
||||
|
||||
if (!channel->inflating) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return read;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void _readBuffer(struct VFile* vf, struct mVideoLogChannel* channel, size_t length) {
|
||||
uint8_t buffer[0x800];
|
||||
while (length) {
|
||||
size_t thisRead = sizeof(buffer);
|
||||
if (thisRead > length) {
|
||||
thisRead = length;
|
||||
}
|
||||
thisRead = vf->read(vf, buffer, thisRead);
|
||||
if (thisRead <= 0) {
|
||||
return;
|
||||
}
|
||||
size_t thisWrite = CircleBufferWrite(&channel->buffer, buffer, thisRead);
|
||||
length -= thisWrite;
|
||||
channel->bufferRemaining -= thisWrite;
|
||||
channel->currentPointer += thisWrite;
|
||||
if (thisWrite < thisRead) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool _fillBuffer(struct mVideoLogContext* context, size_t channelId, size_t length) {
|
||||
struct mVideoLogChannel* channel = &context->channels[channelId];
|
||||
context->backing->seek(context->backing, channel->currentPointer, SEEK_SET);
|
||||
struct mVLBlockHeader header;
|
||||
while (length) {
|
||||
size_t bufferRemaining = channel->bufferRemaining;
|
||||
if (bufferRemaining) {
|
||||
#ifdef USE_ZLIB
|
||||
if (channel->inflating) {
|
||||
length -= _readBufferCompressed(context->backing, channel, length);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
if (bufferRemaining > length) {
|
||||
bufferRemaining = length;
|
||||
}
|
||||
|
||||
_readBuffer(context->backing, channel, bufferRemaining);
|
||||
length -= bufferRemaining;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_readBlockHeader(context, &header)) {
|
||||
return false;
|
||||
}
|
||||
if (header.blockType == mVL_BLOCK_FOOTER) {
|
||||
return true;
|
||||
}
|
||||
if (header.channelId != channelId || header.blockType != mVL_BLOCK_DATA) {
|
||||
context->backing->seek(context->backing, header.length, SEEK_CUR);
|
||||
continue;
|
||||
}
|
||||
channel->currentPointer = context->backing->seek(context->backing, 0, SEEK_CUR);
|
||||
if (!header.length) {
|
||||
continue;
|
||||
}
|
||||
channel->bufferRemaining = header.length;
|
||||
|
||||
if (header.flags & mVL_FLAG_BLOCK_COMPRESSED) {
|
||||
#ifdef USE_ZLIB
|
||||
length -= _readBufferCompressed(context->backing, channel, length);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static ssize_t mVideoLoggerReadChannel(struct mVideoLogChannel* channel, void* data, size_t length) {
|
||||
struct mVideoLogContext* context = channel->p;
|
||||
unsigned channelId = channel - context->channels;
|
||||
if (channelId >= mVL_MAX_CHANNELS) {
|
||||
return 0;
|
||||
}
|
||||
if (CircleBufferSize(&channel->buffer) >= length) {
|
||||
return CircleBufferRead(&channel->buffer, data, length);
|
||||
}
|
||||
ssize_t size = 0;
|
||||
if (CircleBufferSize(&channel->buffer)) {
|
||||
size = CircleBufferRead(&channel->buffer, data, CircleBufferSize(&channel->buffer));
|
||||
if (size <= 0) {
|
||||
return size;
|
||||
}
|
||||
data = (uint8_t*) data + size;
|
||||
length -= size;
|
||||
}
|
||||
if (!_fillBuffer(context, channelId, BUFFER_BASE_SIZE)) {
|
||||
return size;
|
||||
}
|
||||
size += CircleBufferRead(&channel->buffer, data, length);
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t mVideoLoggerWriteChannel(struct mVideoLogChannel* channel, const void* data, size_t length) {
|
||||
struct mVideoLogContext* context = channel->p;
|
||||
unsigned channelId = channel - context->channels;
|
||||
if (channelId >= mVL_MAX_CHANNELS) {
|
||||
return 0;
|
||||
}
|
||||
if (channelId != context->activeChannel) {
|
||||
_flushBuffer(context);
|
||||
context->activeChannel = channelId;
|
||||
}
|
||||
if (CircleBufferCapacity(&channel->buffer) - CircleBufferSize(&channel->buffer) < length) {
|
||||
_flushBuffer(context);
|
||||
if (CircleBufferCapacity(&channel->buffer) < length) {
|
||||
CircleBufferDeinit(&channel->buffer);
|
||||
CircleBufferInit(&channel->buffer, toPow2(length << 1));
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t read = CircleBufferWrite(&channel->buffer, data, length);
|
||||
if (CircleBufferCapacity(&channel->buffer) == CircleBufferSize(&channel->buffer)) {
|
||||
_flushBuffer(context);
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
struct mCore* mVideoLogCoreFind(struct VFile* vf) {
|
||||
if (!vf) {
|
||||
return NULL;
|
||||
}
|
||||
struct mVideoLogHeader header = { { 0 } };
|
||||
vf->seek(vf, 0, SEEK_SET);
|
||||
ssize_t read = vf->read(vf, &header, sizeof(header));
|
||||
if (read != sizeof(header)) {
|
||||
return NULL;
|
||||
}
|
||||
if (memcmp(header.magic, mVL_MAGIC, sizeof(header.magic)) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
enum mPlatform platform;
|
||||
LOAD_32LE(platform, 0, &header.platform);
|
||||
|
||||
const struct mVLDescriptor* descriptor;
|
||||
for (descriptor = &_descriptors[0]; descriptor->platform != PLATFORM_NONE; ++descriptor) {
|
||||
if (platform == descriptor->platform) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
struct mCore* core = NULL;
|
||||
if (descriptor->open) {
|
||||
core = descriptor->open();
|
||||
}
|
||||
return core;
|
||||
}
|
|
@ -640,8 +640,8 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
|||
audio->lastRight = sampleRight;
|
||||
audio->clock += audio->sampleInterval;
|
||||
if (audio->clock >= CLOCKS_PER_BLIP_FRAME) {
|
||||
blip_end_frame(audio->left, audio->clock);
|
||||
blip_end_frame(audio->right, audio->clock);
|
||||
blip_end_frame(audio->left, CLOCKS_PER_BLIP_FRAME);
|
||||
blip_end_frame(audio->right, CLOCKS_PER_BLIP_FRAME);
|
||||
audio->clock -= CLOCKS_PER_BLIP_FRAME;
|
||||
}
|
||||
}
|
||||
|
|
285
src/gb/core.c
285
src/gb/core.c
|
@ -6,13 +6,17 @@
|
|||
#include <mgba/gb/core.h>
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/internal/debugger/symbols.h>
|
||||
#include <mgba/internal/gb/cheats.h>
|
||||
#include <mgba/internal/gb/debugger/symbols.h>
|
||||
#include <mgba/internal/gb/extra/cli.h>
|
||||
#include <mgba/internal/gb/io.h>
|
||||
#include <mgba/internal/gb/gb.h>
|
||||
#include <mgba/internal/gb/input.h>
|
||||
#include <mgba/internal/gb/mbc.h>
|
||||
#include <mgba/internal/gb/overrides.h>
|
||||
#include <mgba/internal/gb/renderers/software.h>
|
||||
#include <mgba/internal/gb/renderers/proxy.h>
|
||||
#include <mgba/internal/gb/serialize.h>
|
||||
#include <mgba/internal/lr35902/lr35902.h>
|
||||
#include <mgba/internal/lr35902/debugger/debugger.h>
|
||||
|
@ -25,9 +29,40 @@
|
|||
#include <mgba/internal/gba/input.h>
|
||||
#endif
|
||||
|
||||
const static struct mCoreChannelInfo _GBVideoLayers[] = {
|
||||
{ 0, "bg", "Background", NULL },
|
||||
{ 1, "obj", "Objects", NULL },
|
||||
{ 2, "win", "Window", NULL },
|
||||
};
|
||||
|
||||
const static struct mCoreChannelInfo _GBAudioChannels[] = {
|
||||
{ 0, "ch0", "Channel 0", "Square/Sweep" },
|
||||
{ 1, "ch1", "Channel 1", "Square" },
|
||||
{ 2, "ch2", "Channel 2", "PCM" },
|
||||
{ 3, "ch3", "Channel 3", "Noise" },
|
||||
};
|
||||
|
||||
const static struct LR35902Segment _GBSegments[] = {
|
||||
{ .name = "ROM", .start = GB_BASE_CART_BANK1, .end = GB_BASE_VRAM },
|
||||
{ .name = "RAM", .start = GB_BASE_EXTERNAL_RAM, .end = GB_BASE_WORKING_RAM_BANK0 },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
const static struct LR35902Segment _GBCSegments[] = {
|
||||
{ .name = "ROM", .start = GB_BASE_CART_BANK1, .end = GB_BASE_VRAM },
|
||||
{ .name = "RAM", .start = GB_BASE_EXTERNAL_RAM, .end = GB_BASE_WORKING_RAM_BANK0 },
|
||||
{ .name = "WRAM", .start = GB_BASE_WORKING_RAM_BANK1, .end = 0xE000 },
|
||||
{ .name = "VRAM", .start = GB_BASE_VRAM, .end = GB_BASE_EXTERNAL_RAM },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
struct mVideoLogContext;
|
||||
struct GBCore {
|
||||
struct mCore d;
|
||||
struct GBVideoSoftwareRenderer renderer;
|
||||
struct GBVideoProxyRenderer proxyRenderer;
|
||||
struct mVideoLogContext* logContext;
|
||||
struct mCoreCallbacks logCallbacks;
|
||||
uint8_t keys;
|
||||
struct mCPUComponent* components[CPU_COMPONENT_MAX];
|
||||
const struct Configuration* overrides;
|
||||
|
@ -80,8 +115,11 @@ static void _GBCoreDeinit(struct mCore* core) {
|
|||
GBDestroy(core->board);
|
||||
mappedMemoryFree(core->cpu, sizeof(struct LR35902Core));
|
||||
mappedMemoryFree(core->board, sizeof(struct GB));
|
||||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||
#if defined USE_DEBUGGERS && (!defined(MINIMAL_CORE) || MINIMAL_CORE < 2)
|
||||
mDirectorySetDeinit(&core->dirs);
|
||||
if (core->symbolTable) {
|
||||
mDebuggerSymbolTableDestroy(core->symbolTable);
|
||||
}
|
||||
#endif
|
||||
|
||||
struct GBCore* gbcore = (struct GBCore*) core;
|
||||
|
@ -114,6 +152,21 @@ static void _GBCoreLoadConfig(struct mCore* core, const struct mCoreConfig* conf
|
|||
gb->audio.masterVolume = core->opts.volume;
|
||||
}
|
||||
gb->video.frameskip = core->opts.frameskip;
|
||||
|
||||
int color;
|
||||
if (mCoreConfigGetIntValue(&core->config, "gb.pal[0]", &color)) {
|
||||
GBVideoSetPalette(&gb->video, 0, color);
|
||||
}
|
||||
if (mCoreConfigGetIntValue(&core->config, "gb.pal[1]", &color)) {
|
||||
GBVideoSetPalette(&gb->video, 1, color);
|
||||
}
|
||||
if (mCoreConfigGetIntValue(&core->config, "gb.pal[2]", &color)) {
|
||||
GBVideoSetPalette(&gb->video, 2, color);
|
||||
}
|
||||
if (mCoreConfigGetIntValue(&core->config, "gb.pal[3]", &color)) {
|
||||
GBVideoSetPalette(&gb->video, 3, color);
|
||||
}
|
||||
|
||||
mCoreConfigCopyValue(&core->config, config, "gb.bios");
|
||||
mCoreConfigCopyValue(&core->config, config, "gbc.bios");
|
||||
|
||||
|
@ -402,14 +455,18 @@ static void _GBCoreGetGameCode(const struct mCore* core, char* title) {
|
|||
GBGetGameCode(core->board, title);
|
||||
}
|
||||
|
||||
static void _GBCoreSetRotation(struct mCore* core, struct mRotationSource* rotation) {
|
||||
static void _GBCoreSetPeripheral(struct mCore* core, int type, void* periph) {
|
||||
struct GB* gb = core->board;
|
||||
gb->memory.rotation = rotation;
|
||||
}
|
||||
|
||||
static void _GBCoreSetRumble(struct mCore* core, struct mRumble* rumble) {
|
||||
struct GB* gb = core->board;
|
||||
gb->memory.rumble = rumble;
|
||||
switch (type) {
|
||||
case mPERIPH_ROTATION:
|
||||
gb->memory.rotation = periph;
|
||||
break;
|
||||
case mPERIPH_RUMBLE:
|
||||
gb->memory.rumble = periph;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t _GBCoreBusRead8(struct mCore* core, uint32_t address) {
|
||||
|
@ -495,8 +552,15 @@ static bool _GBCoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType t
|
|||
|
||||
static struct mDebuggerPlatform* _GBCoreDebuggerPlatform(struct mCore* core) {
|
||||
struct GBCore* gbcore = (struct GBCore*) core;
|
||||
struct GB* gb = core->board;
|
||||
if (!gbcore->debuggerPlatform) {
|
||||
gbcore->debuggerPlatform = LR35902DebuggerPlatformCreate();
|
||||
struct LR35902Debugger* platform = (struct LR35902Debugger*) LR35902DebuggerPlatformCreate();
|
||||
if (gb->model >= GB_MODEL_CGB) {
|
||||
platform->segments = _GBCSegments;
|
||||
} else {
|
||||
platform->segments = _GBSegments;
|
||||
}
|
||||
gbcore->debuggerPlatform = &platform->d;
|
||||
}
|
||||
return gbcore->debuggerPlatform;
|
||||
}
|
||||
|
@ -523,6 +587,19 @@ static void _GBCoreDetachDebugger(struct mCore* core) {
|
|||
cpu->components[CPU_COMPONENT_DEBUGGER] = NULL;
|
||||
core->debugger = NULL;
|
||||
}
|
||||
|
||||
static void _GBCoreLoadSymbols(struct mCore* core, struct VFile* vf) {
|
||||
core->symbolTable = mDebuggerSymbolTableCreate();
|
||||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||
if (!vf) {
|
||||
vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.base, ".sym", O_RDONLY);
|
||||
}
|
||||
#endif
|
||||
if (!vf) {
|
||||
return;
|
||||
}
|
||||
GBLoadSymbols(core->symbolTable, vf);
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct mCheatDevice* _GBCoreCheatDevice(struct mCore* core) {
|
||||
|
@ -569,6 +646,72 @@ static bool _GBCoreSavedataRestore(struct mCore* core, const void* sram, size_t
|
|||
return true;
|
||||
}
|
||||
|
||||
static size_t _GBCoreListVideoLayers(const struct mCore* core, const struct mCoreChannelInfo** info) {
|
||||
UNUSED(core);
|
||||
*info = _GBVideoLayers;
|
||||
return sizeof(_GBVideoLayers) / sizeof(*_GBVideoLayers);
|
||||
}
|
||||
|
||||
static size_t _GBCoreListAudioChannels(const struct mCore* core, const struct mCoreChannelInfo** info) {
|
||||
UNUSED(core);
|
||||
*info = _GBAudioChannels;
|
||||
return sizeof(_GBAudioChannels) / sizeof(*_GBAudioChannels);
|
||||
}
|
||||
|
||||
static void _GBCoreEnableVideoLayer(struct mCore* core, size_t id, bool enable) {
|
||||
struct GB* gb = core->board;
|
||||
switch (id) {
|
||||
case 0:
|
||||
gb->video.renderer->disableBG = !enable;
|
||||
break;
|
||||
case 1:
|
||||
gb->video.renderer->disableOBJ = !enable;
|
||||
break;
|
||||
case 2:
|
||||
gb->video.renderer->disableWIN = !enable;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void _GBCoreEnableAudioChannel(struct mCore* core, size_t id, bool enable) {
|
||||
struct GB* gb = core->board;
|
||||
switch (id) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
gb->audio.forceDisableCh[id] = !enable;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void _GBCoreStartVideoLog(struct mCore* core, struct mVideoLogContext* context) {
|
||||
struct GBCore* gbcore = (struct GBCore*) core;
|
||||
struct GB* gb = core->board;
|
||||
gbcore->logContext = context;
|
||||
|
||||
int channelId = mVideoLoggerAddChannel(context);
|
||||
gbcore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger));
|
||||
mVideoLoggerRendererCreate(gbcore->proxyRenderer.logger, false);
|
||||
mVideoLoggerAttachChannel(gbcore->proxyRenderer.logger, context, channelId);
|
||||
gbcore->proxyRenderer.logger->block = false;
|
||||
|
||||
GBVideoProxyRendererCreate(&gbcore->proxyRenderer, &gbcore->renderer.d);
|
||||
GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer);
|
||||
}
|
||||
|
||||
static void _GBCoreEndVideoLog(struct mCore* core) {
|
||||
struct GBCore* gbcore = (struct GBCore*) core;
|
||||
struct GB* gb = core->board;
|
||||
GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer);
|
||||
free(gbcore->proxyRenderer.logger);
|
||||
gbcore->proxyRenderer.logger = NULL;
|
||||
}
|
||||
|
||||
struct mCore* GBCoreCreate(void) {
|
||||
struct GBCore* gbcore = malloc(sizeof(*gbcore));
|
||||
struct mCore* core = &gbcore->d;
|
||||
|
@ -576,6 +719,7 @@ struct mCore* GBCoreCreate(void) {
|
|||
core->cpu = NULL;
|
||||
core->board = NULL;
|
||||
core->debugger = NULL;
|
||||
core->symbolTable = NULL;
|
||||
core->init = _GBCoreInit;
|
||||
core->deinit = _GBCoreDeinit;
|
||||
core->platform = _GBCorePlatform;
|
||||
|
@ -614,8 +758,7 @@ struct mCore* GBCoreCreate(void) {
|
|||
core->frequency = _GBCoreFrequency;
|
||||
core->getGameTitle = _GBCoreGetGameTitle;
|
||||
core->getGameCode = _GBCoreGetGameCode;
|
||||
core->setRotation = _GBCoreSetRotation;
|
||||
core->setRumble = _GBCoreSetRumble;
|
||||
core->setPeripheral = _GBCoreSetPeripheral;
|
||||
core->busRead8 = _GBCoreBusRead8;
|
||||
core->busRead16 = _GBCoreBusRead16;
|
||||
core->busRead32 = _GBCoreBusRead32;
|
||||
|
@ -634,9 +777,129 @@ struct mCore* GBCoreCreate(void) {
|
|||
core->cliDebuggerSystem = _GBCoreCliDebuggerSystem;
|
||||
core->attachDebugger = _GBCoreAttachDebugger;
|
||||
core->detachDebugger = _GBCoreDetachDebugger;
|
||||
core->loadSymbols = _GBCoreLoadSymbols;
|
||||
#endif
|
||||
core->cheatDevice = _GBCoreCheatDevice;
|
||||
core->savedataClone = _GBCoreSavedataClone;
|
||||
core->savedataRestore = _GBCoreSavedataRestore;
|
||||
core->listVideoLayers = _GBCoreListVideoLayers;
|
||||
core->listAudioChannels = _GBCoreListAudioChannels;
|
||||
core->enableVideoLayer = _GBCoreEnableVideoLayer;
|
||||
core->enableAudioChannel = _GBCoreEnableAudioChannel;
|
||||
#ifndef MINIMAL_CORE
|
||||
core->startVideoLog = _GBCoreStartVideoLog;
|
||||
core->endVideoLog = _GBCoreEndVideoLog;
|
||||
#endif
|
||||
return core;
|
||||
}
|
||||
|
||||
#ifndef MINIMAL_CORE
|
||||
static void _GBVLPStartFrameCallback(void *context) {
|
||||
struct mCore* core = context;
|
||||
struct GBCore* gbcore = (struct GBCore*) core;
|
||||
struct GB* gb = core->board;
|
||||
|
||||
if (!mVideoLoggerRendererRun(gbcore->proxyRenderer.logger, true)) {
|
||||
GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer);
|
||||
mVideoLogContextRewind(gbcore->logContext, core);
|
||||
GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer);
|
||||
}
|
||||
}
|
||||
|
||||
static bool _GBVLPInit(struct mCore* core) {
|
||||
struct GBCore* gbcore = (struct GBCore*) core;
|
||||
if (!_GBCoreInit(core)) {
|
||||
return false;
|
||||
}
|
||||
gbcore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger));
|
||||
mVideoLoggerRendererCreate(gbcore->proxyRenderer.logger, true);
|
||||
GBVideoProxyRendererCreate(&gbcore->proxyRenderer, NULL);
|
||||
memset(&gbcore->logCallbacks, 0, sizeof(gbcore->logCallbacks));
|
||||
gbcore->logCallbacks.videoFrameStarted = _GBVLPStartFrameCallback;
|
||||
gbcore->logCallbacks.context = core;
|
||||
core->addCoreCallbacks(core, &gbcore->logCallbacks);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void _GBVLPDeinit(struct mCore* core) {
|
||||
struct GBCore* gbcore = (struct GBCore*) core;
|
||||
if (gbcore->logContext) {
|
||||
mVideoLogContextDestroy(core, gbcore->logContext);
|
||||
}
|
||||
_GBCoreDeinit(core);
|
||||
}
|
||||
|
||||
static void _GBVLPReset(struct mCore* core) {
|
||||
struct GBCore* gbcore = (struct GBCore*) core;
|
||||
struct GB* gb = (struct GB*) core->board;
|
||||
if (gb->video.renderer == &gbcore->proxyRenderer.d) {
|
||||
GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer);
|
||||
} else if (gbcore->renderer.outputBuffer) {
|
||||
struct GBVideoRenderer* renderer = &gbcore->renderer.d;
|
||||
GBVideoAssociateRenderer(&gb->video, renderer);
|
||||
}
|
||||
|
||||
LR35902Reset(core->cpu);
|
||||
mVideoLogContextRewind(gbcore->logContext, core);
|
||||
GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer);
|
||||
|
||||
// Make sure CPU loop never spins
|
||||
GBHalt(gb->cpu);
|
||||
gb->memory.ie = 0;
|
||||
gb->memory.ime = false;
|
||||
}
|
||||
|
||||
static bool _GBVLPLoadROM(struct mCore* core, struct VFile* vf) {
|
||||
struct GBCore* gbcore = (struct GBCore*) core;
|
||||
gbcore->logContext = mVideoLogContextCreate(NULL);
|
||||
if (!mVideoLogContextLoad(gbcore->logContext, vf)) {
|
||||
mVideoLogContextDestroy(core, gbcore->logContext);
|
||||
gbcore->logContext = NULL;
|
||||
return false;
|
||||
}
|
||||
mVideoLoggerAttachChannel(gbcore->proxyRenderer.logger, gbcore->logContext, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _GBVLPLoadState(struct mCore* core, const void* buffer) {
|
||||
struct GB* gb = (struct GB*) core->board;
|
||||
const struct GBSerializedState* state = buffer;
|
||||
|
||||
gb->timing.root = NULL;
|
||||
gb->model = state->model;
|
||||
|
||||
gb->cpu->pc = GB_BASE_HRAM;
|
||||
gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc);
|
||||
|
||||
GBVideoDeserialize(&gb->video, state);
|
||||
GBIODeserialize(gb, state);
|
||||
GBAudioReset(&gb->audio);
|
||||
|
||||
// Make sure CPU loop never spins
|
||||
GBHalt(gb->cpu);
|
||||
gb->memory.ie = 0;
|
||||
gb->memory.ime = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _returnTrue(struct VFile* vf) {
|
||||
UNUSED(vf);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct mCore* GBVideoLogPlayerCreate(void) {
|
||||
struct mCore* core = GBCoreCreate();
|
||||
core->init = _GBVLPInit;
|
||||
core->deinit = _GBVLPDeinit;
|
||||
core->reset = _GBVLPReset;
|
||||
core->loadROM = _GBVLPLoadROM;
|
||||
core->loadState = _GBVLPLoadState;
|
||||
core->isROM = _returnTrue;
|
||||
return core;
|
||||
}
|
||||
#else
|
||||
struct mCore* GBVideoLogPlayerCreate(void) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
/* Copyright (c) 2013-2016 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include <mgba/internal/gb/debugger/symbols.h>
|
||||
|
||||
#include <mgba/internal/debugger/symbols.h>
|
||||
#include <mgba-util/string.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
void GBLoadSymbols(struct mDebuggerSymbols* st, struct VFile* vf) {
|
||||
char line[512];
|
||||
|
||||
while (true) {
|
||||
ssize_t bytesRead = vf->readline(vf, line, sizeof(line));
|
||||
if (bytesRead <= 0) {
|
||||
break;
|
||||
}
|
||||
if (line[bytesRead - 1] == '\n') {
|
||||
line[bytesRead - 1] = '\0';
|
||||
}
|
||||
int segment = -1;
|
||||
uint32_t address = 0;
|
||||
|
||||
uint8_t byte;
|
||||
const char* buf = line;
|
||||
while (buf) {
|
||||
buf = hex8(buf, &byte);
|
||||
if (!buf) {
|
||||
break;
|
||||
}
|
||||
address <<= 8;
|
||||
address += byte;
|
||||
|
||||
if (buf[0] == ':') {
|
||||
segment = address;
|
||||
address = 0;
|
||||
++buf;
|
||||
}
|
||||
if (isspace((int) buf[0])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!buf) {
|
||||
continue;
|
||||
}
|
||||
|
||||
while (isspace((int) buf[0])) {
|
||||
++buf;
|
||||
}
|
||||
|
||||
mDebuggerSymbolAdd(st, buf, address, segment);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,272 @@
|
|||
/* 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/gb/renderers/proxy.h>
|
||||
|
||||
#include <mgba/core/tile-cache.h>
|
||||
#include <mgba/internal/gb/gb.h>
|
||||
#include <mgba/internal/gb/io.h>
|
||||
|
||||
#define BUFFER_OAM 1
|
||||
|
||||
static void GBVideoProxyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model);
|
||||
static void GBVideoProxyRendererDeinit(struct GBVideoRenderer* renderer);
|
||||
static uint8_t GBVideoProxyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
|
||||
static void GBVideoProxyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address);
|
||||
static void GBVideoProxyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam);
|
||||
static void GBVideoProxyRendererWritePalette(struct GBVideoRenderer* renderer, int address, uint16_t value);
|
||||
static void GBVideoProxyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax);
|
||||
static void GBVideoProxyRendererFinishScanline(struct GBVideoRenderer* renderer, int y);
|
||||
static void GBVideoProxyRendererFinishFrame(struct GBVideoRenderer* renderer);
|
||||
static void GBVideoProxyRendererGetPixels(struct GBVideoRenderer* renderer, size_t* stride, const void** pixels);
|
||||
static void GBVideoProxyRendererPutPixels(struct GBVideoRenderer* renderer, size_t stride, const void* pixels);
|
||||
|
||||
static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* packet);
|
||||
static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address);
|
||||
|
||||
void GBVideoProxyRendererCreate(struct GBVideoProxyRenderer* renderer, struct GBVideoRenderer* backend) {
|
||||
renderer->d.init = GBVideoProxyRendererInit;
|
||||
renderer->d.deinit = GBVideoProxyRendererDeinit;
|
||||
renderer->d.writeVideoRegister = GBVideoProxyRendererWriteVideoRegister;
|
||||
renderer->d.writeVRAM = GBVideoProxyRendererWriteVRAM;
|
||||
renderer->d.writeOAM = GBVideoProxyRendererWriteOAM;
|
||||
renderer->d.writePalette = GBVideoProxyRendererWritePalette;
|
||||
renderer->d.drawRange = GBVideoProxyRendererDrawRange;
|
||||
renderer->d.finishScanline = GBVideoProxyRendererFinishScanline;
|
||||
renderer->d.finishFrame = GBVideoProxyRendererFinishFrame;
|
||||
renderer->d.getPixels = GBVideoProxyRendererGetPixels;
|
||||
renderer->d.putPixels = GBVideoProxyRendererPutPixels;
|
||||
|
||||
renderer->logger->context = renderer;
|
||||
renderer->logger->parsePacket = _parsePacket;
|
||||
renderer->logger->vramBlock = _vramBlock;
|
||||
renderer->logger->paletteSize = 0;
|
||||
renderer->logger->vramSize = GB_SIZE_VRAM;
|
||||
renderer->logger->oamSize = GB_SIZE_OAM;
|
||||
|
||||
renderer->backend = backend;
|
||||
}
|
||||
|
||||
static void _init(struct GBVideoProxyRenderer* proxyRenderer) {
|
||||
mVideoLoggerRendererInit(proxyRenderer->logger);
|
||||
|
||||
if (proxyRenderer->logger->block) {
|
||||
proxyRenderer->backend->vram = (uint8_t*) proxyRenderer->logger->vram;
|
||||
proxyRenderer->backend->oam = (union GBOAM*) proxyRenderer->logger->oam;
|
||||
proxyRenderer->backend->cache = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void _reset(struct GBVideoProxyRenderer* proxyRenderer, enum GBModel model) {
|
||||
memcpy(proxyRenderer->logger->oam, &proxyRenderer->d.oam->raw, GB_SIZE_OAM);
|
||||
memcpy(proxyRenderer->logger->vram, proxyRenderer->d.vram, GB_SIZE_VRAM);
|
||||
|
||||
proxyRenderer->oamMax = 0;
|
||||
|
||||
mVideoLoggerRendererReset(proxyRenderer->logger);
|
||||
}
|
||||
|
||||
void GBVideoProxyRendererShim(struct GBVideo* video, struct GBVideoProxyRenderer* renderer) {
|
||||
if ((renderer->backend && video->renderer != renderer->backend) || video->renderer == &renderer->d) {
|
||||
return;
|
||||
}
|
||||
renderer->backend = video->renderer;
|
||||
video->renderer = &renderer->d;
|
||||
renderer->d.cache = renderer->backend->cache;
|
||||
renderer->d.vram = video->vram;
|
||||
renderer->d.oam = &video->oam;
|
||||
_init(renderer);
|
||||
_reset(renderer, video->p->model);
|
||||
}
|
||||
|
||||
void GBVideoProxyRendererUnshim(struct GBVideo* video, struct GBVideoProxyRenderer* renderer) {
|
||||
if (video->renderer != &renderer->d) {
|
||||
return;
|
||||
}
|
||||
renderer->backend->cache = video->renderer->cache;
|
||||
video->renderer = renderer->backend;
|
||||
renderer->backend->vram = video->vram;
|
||||
renderer->backend->oam = &video->oam;
|
||||
|
||||
mVideoLoggerRendererDeinit(renderer->logger);
|
||||
}
|
||||
|
||||
void GBVideoProxyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model) {
|
||||
struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
|
||||
|
||||
_init(proxyRenderer);
|
||||
|
||||
proxyRenderer->backend->init(proxyRenderer->backend, model);
|
||||
}
|
||||
|
||||
void GBVideoProxyRendererDeinit(struct GBVideoRenderer* renderer) {
|
||||
struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
|
||||
|
||||
proxyRenderer->backend->deinit(proxyRenderer->backend);
|
||||
|
||||
mVideoLoggerRendererDeinit(proxyRenderer->logger);
|
||||
}
|
||||
|
||||
static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* item) {
|
||||
struct GBVideoProxyRenderer* proxyRenderer = logger->context;
|
||||
switch (item->type) {
|
||||
case DIRTY_REGISTER:
|
||||
proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value);
|
||||
break;
|
||||
case DIRTY_PALETTE:
|
||||
if (item->address < 64) {
|
||||
proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value);
|
||||
}
|
||||
break;
|
||||
case DIRTY_OAM:
|
||||
if (item->address < GB_SIZE_OAM) {
|
||||
logger->oam[item->address] = item->value;
|
||||
proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address);
|
||||
}
|
||||
break;
|
||||
case DIRTY_VRAM:
|
||||
if (item->address <= GB_SIZE_VRAM - 0x1000) {
|
||||
logger->readData(logger, &logger->vram[item->address >> 1], 0x1000, true);
|
||||
proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address);
|
||||
}
|
||||
break;
|
||||
case DIRTY_SCANLINE:
|
||||
if (item->address < GB_VIDEO_VERTICAL_PIXELS) {
|
||||
proxyRenderer->backend->finishScanline(proxyRenderer->backend, item->address);
|
||||
}
|
||||
break;
|
||||
case DIRTY_RANGE:
|
||||
if (item->value < item->value2 && item->value2 <= GB_VIDEO_HORIZONTAL_PIXELS && item->address < GB_VIDEO_VERTICAL_PIXELS) {
|
||||
proxyRenderer->backend->drawRange(proxyRenderer->backend, item->value, item->value2, item->address, proxyRenderer->objThisLine, proxyRenderer->oamMax);
|
||||
}
|
||||
break;
|
||||
case DIRTY_FRAME:
|
||||
proxyRenderer->backend->finishFrame(proxyRenderer->backend);
|
||||
break;
|
||||
case DIRTY_BUFFER:
|
||||
switch (item->address) {
|
||||
case BUFFER_OAM:
|
||||
proxyRenderer->oamMax = item->value2 / sizeof(struct GBObj);
|
||||
if (proxyRenderer->oamMax > 40) {
|
||||
proxyRenderer->oamMax = 0;
|
||||
return false;
|
||||
}
|
||||
logger->readData(logger, &proxyRenderer->objThisLine, item->value2, true);
|
||||
}
|
||||
break;
|
||||
case DIRTY_FLUSH:
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address) {
|
||||
struct GBVideoProxyRenderer* proxyRenderer = logger->context;
|
||||
return (uint16_t*) &proxyRenderer->d.vram[address];
|
||||
}
|
||||
|
||||
uint8_t GBVideoProxyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value) {
|
||||
struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
|
||||
|
||||
mVideoLoggerRendererWriteVideoRegister(proxyRenderer->logger, address, value);
|
||||
if (!proxyRenderer->logger->block) {
|
||||
proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, address, value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void GBVideoProxyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address) {
|
||||
struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
|
||||
mVideoLoggerRendererWriteVRAM(proxyRenderer->logger, address);
|
||||
if (!proxyRenderer->logger->block) {
|
||||
proxyRenderer->backend->writeVRAM(proxyRenderer->backend, address);
|
||||
}
|
||||
if (renderer->cache) {
|
||||
mTileCacheWriteVRAM(renderer->cache, address);
|
||||
}
|
||||
}
|
||||
|
||||
void GBVideoProxyRendererWritePalette(struct GBVideoRenderer* renderer, int address, uint16_t value) {
|
||||
struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
|
||||
mVideoLoggerRendererWritePalette(proxyRenderer->logger, address, value);
|
||||
if (!proxyRenderer->logger->block) {
|
||||
proxyRenderer->backend->writePalette(proxyRenderer->backend, address, value);
|
||||
}
|
||||
if (renderer->cache) {
|
||||
mTileCacheWritePalette(renderer->cache, address);
|
||||
}
|
||||
}
|
||||
|
||||
void GBVideoProxyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam) {
|
||||
struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
|
||||
if (!proxyRenderer->logger->block) {
|
||||
proxyRenderer->backend->writeOAM(proxyRenderer->backend, oam);
|
||||
}
|
||||
mVideoLoggerRendererWriteOAM(proxyRenderer->logger, oam, ((uint8_t*) proxyRenderer->d.oam->raw)[oam]);
|
||||
}
|
||||
|
||||
void GBVideoProxyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax) {
|
||||
struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
|
||||
if (!proxyRenderer->logger->block) {
|
||||
proxyRenderer->backend->drawRange(proxyRenderer->backend, startX, endX, y, obj, oamMax);
|
||||
}
|
||||
mVideoLoggerWriteBuffer(proxyRenderer->logger, BUFFER_OAM, 0, oamMax * sizeof(*obj), obj);
|
||||
mVideoLoggerRendererDrawRange(proxyRenderer->logger, startX, endX, y);
|
||||
}
|
||||
|
||||
void GBVideoProxyRendererFinishScanline(struct GBVideoRenderer* renderer, int y) {
|
||||
struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
|
||||
if (!proxyRenderer->logger->block) {
|
||||
proxyRenderer->backend->finishScanline(proxyRenderer->backend, y);
|
||||
}
|
||||
mVideoLoggerRendererDrawScanline(proxyRenderer->logger, y);
|
||||
if (proxyRenderer->logger->block && proxyRenderer->logger->wake) {
|
||||
proxyRenderer->logger->wake(proxyRenderer->logger, y);
|
||||
}
|
||||
}
|
||||
|
||||
void GBVideoProxyRendererFinishFrame(struct GBVideoRenderer* renderer) {
|
||||
struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
|
||||
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
|
||||
proxyRenderer->logger->lock(proxyRenderer->logger);
|
||||
proxyRenderer->logger->wait(proxyRenderer->logger);
|
||||
}
|
||||
proxyRenderer->backend->finishFrame(proxyRenderer->backend);
|
||||
mVideoLoggerRendererFinishFrame(proxyRenderer->logger);
|
||||
mVideoLoggerRendererFlush(proxyRenderer->logger);
|
||||
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
|
||||
proxyRenderer->logger->unlock(proxyRenderer->logger);
|
||||
}
|
||||
}
|
||||
|
||||
static void GBVideoProxyRendererGetPixels(struct GBVideoRenderer* renderer, size_t* stride, const void** pixels) {
|
||||
struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
|
||||
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
|
||||
proxyRenderer->logger->lock(proxyRenderer->logger);
|
||||
// Insert an extra item into the queue to make sure it gets flushed
|
||||
mVideoLoggerRendererFlush(proxyRenderer->logger);
|
||||
proxyRenderer->logger->wait(proxyRenderer->logger);
|
||||
}
|
||||
proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels);
|
||||
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
|
||||
proxyRenderer->logger->unlock(proxyRenderer->logger);
|
||||
}
|
||||
}
|
||||
|
||||
static void GBVideoProxyRendererPutPixels(struct GBVideoRenderer* renderer, size_t stride, const void* pixels) {
|
||||
struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
|
||||
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
|
||||
proxyRenderer->logger->lock(proxyRenderer->logger);
|
||||
// Insert an extra item into the queue to make sure it gets flushed
|
||||
mVideoLoggerRendererFlush(proxyRenderer->logger);
|
||||
proxyRenderer->logger->wait(proxyRenderer->logger);
|
||||
}
|
||||
proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels);
|
||||
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
|
||||
proxyRenderer->logger->unlock(proxyRenderer->logger);
|
||||
}
|
||||
}
|
21
src/gb/gb.c
21
src/gb/gb.c
|
@ -124,7 +124,7 @@ bool GBLoadROM(struct GB* gb, struct VFile* vf) {
|
|||
gb->memory.romBase = gb->memory.rom;
|
||||
gb->memory.romSize = gb->pristineRomSize;
|
||||
gb->romCrc32 = doCrc32(gb->memory.rom, gb->memory.romSize);
|
||||
GBMBCSwitchBank(gb, gb->memory.currentBank);
|
||||
GBMBCInit(gb);
|
||||
|
||||
if (gb->cpu) {
|
||||
struct LR35902Core* cpu = gb->cpu;
|
||||
|
@ -135,12 +135,6 @@ bool GBLoadROM(struct GB* gb, struct VFile* vf) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GBLoadSave(struct GB* gb, struct VFile* vf) {
|
||||
gb->sramVf = vf;
|
||||
gb->sramRealVf = vf;
|
||||
return vf;
|
||||
}
|
||||
|
||||
static void GBSramDeinit(struct GB* gb) {
|
||||
if (gb->sramVf) {
|
||||
gb->sramVf->unmap(gb->sramVf, gb->memory.sram, gb->sramSize);
|
||||
|
@ -154,6 +148,16 @@ static void GBSramDeinit(struct GB* gb) {
|
|||
gb->memory.sram = 0;
|
||||
}
|
||||
|
||||
bool GBLoadSave(struct GB* gb, struct VFile* vf) {
|
||||
GBSramDeinit(gb);
|
||||
gb->sramVf = vf;
|
||||
gb->sramRealVf = vf;
|
||||
if (gb->sramSize) {
|
||||
GBResizeSram(gb, gb->sramSize);
|
||||
}
|
||||
return vf;
|
||||
}
|
||||
|
||||
void GBResizeSram(struct GB* gb, size_t size) {
|
||||
if (gb->memory.sram && size <= gb->sramSize) {
|
||||
return;
|
||||
|
@ -280,6 +284,7 @@ void GBUnloadROM(struct GB* gb) {
|
|||
gb->romVf = NULL;
|
||||
}
|
||||
gb->memory.rom = NULL;
|
||||
gb->memory.mbcType = GB_MBC_AUTODETECT;
|
||||
gb->isPristine = false;
|
||||
|
||||
GBSavedataUnmask(gb);
|
||||
|
@ -385,7 +390,9 @@ bool GBIsBIOS(struct VFile* vf) {
|
|||
|
||||
void GBReset(struct LR35902Core* cpu) {
|
||||
struct GB* gb = (struct GB*) cpu->master;
|
||||
gb->memory.romBase = gb->memory.rom;
|
||||
GBDetectModel(gb);
|
||||
|
||||
if (gb->biosVf) {
|
||||
if (!GBIsBIOS(gb->biosVf)) {
|
||||
gb->biosVf->close(gb->biosVf);
|
||||
|
|
|
@ -382,7 +382,7 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) {
|
|||
value = gb->video.stat;
|
||||
break;
|
||||
case 0x50:
|
||||
if (gb->memory.romBase != gb->memory.rom) {
|
||||
if (gb->memory.romBase < gb->memory.rom || gb->memory.romBase > &gb->memory.rom[gb->memory.romSize - 1]) {
|
||||
free(gb->memory.romBase);
|
||||
gb->memory.romBase = gb->memory.rom;
|
||||
}
|
||||
|
|
53
src/gb/mbc.c
53
src/gb/mbc.c
|
@ -46,6 +46,33 @@ void GBMBCSwitchBank(struct GB* gb, int bank) {
|
|||
}
|
||||
}
|
||||
|
||||
static void _switchBank0(struct GB* gb, int bank) {
|
||||
size_t bankStart = bank * GB_SIZE_CART_BANK0 << gb->memory.mbcState.mbc1.multicartStride;
|
||||
if (bankStart + GB_SIZE_CART_BANK0 > gb->memory.romSize) {
|
||||
mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank);
|
||||
bankStart &= (gb->memory.romSize - 1);
|
||||
}
|
||||
gb->memory.romBase = &gb->memory.rom[bankStart];
|
||||
if (gb->cpu->pc < GB_SIZE_CART_BANK0) {
|
||||
gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc);
|
||||
}
|
||||
}
|
||||
|
||||
static bool _isMulticart(const uint8_t* mem) {
|
||||
bool success = true;
|
||||
struct VFile* vf;
|
||||
|
||||
vf = VFileFromConstMemory(&mem[GB_SIZE_CART_BANK0 * 0x10], 1024);
|
||||
success = success && GBIsROM(vf);
|
||||
vf->close(vf);
|
||||
|
||||
vf = VFileFromConstMemory(&mem[GB_SIZE_CART_BANK0 * 0x20], 1024);
|
||||
success = success && GBIsROM(vf);
|
||||
vf->close(vf);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void GBMBCSwitchSramBank(struct GB* gb, int bank) {
|
||||
size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM;
|
||||
GBResizeSram(gb, (bank + 1) * GB_SIZE_EXTERNAL_RAM);
|
||||
|
@ -83,6 +110,11 @@ void GBMBCInit(struct GB* gb) {
|
|||
case 2:
|
||||
case 3:
|
||||
gb->memory.mbcType = GB_MBC1;
|
||||
if (gb->memory.romSize >= GB_SIZE_CART_BANK0 * 0x31 && _isMulticart(gb->memory.rom)) {
|
||||
gb->memory.mbcState.mbc1.multicartStride = 4;
|
||||
} else {
|
||||
gb->memory.mbcState.mbc1.multicartStride = 5;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
|
@ -172,6 +204,14 @@ void GBMBCInit(struct GB* gb) {
|
|||
break;
|
||||
}
|
||||
|
||||
gb->memory.currentBank = 1;
|
||||
gb->memory.sramCurrentBank = 0;
|
||||
gb->memory.sramAccess = false;
|
||||
gb->memory.rtcAccess = false;
|
||||
gb->memory.activeRtcReg = 0;
|
||||
gb->memory.rtcLatched = false;
|
||||
memset(&gb->memory.rtcRegs, 0, sizeof(gb->memory.rtcRegs));
|
||||
|
||||
GBResizeSram(gb, gb->sramSize);
|
||||
|
||||
if (gb->memory.mbcType == GB_MBC3_RTC) {
|
||||
|
@ -233,6 +273,7 @@ static void _latchRtc(struct mRTCSource* rtc, uint8_t* rtcRegs, time_t* rtcLastL
|
|||
void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
int bank = value & 0x1F;
|
||||
int stride = 1 << memory->mbcState.mbc1.multicartStride;
|
||||
switch (address >> 13) {
|
||||
case 0x0:
|
||||
switch (value) {
|
||||
|
@ -253,21 +294,23 @@ void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
if (!bank) {
|
||||
++bank;
|
||||
}
|
||||
GBMBCSwitchBank(gb, bank | (memory->currentBank & 0x60));
|
||||
bank &= stride - 1;
|
||||
GBMBCSwitchBank(gb, bank | (memory->currentBank & (3 * stride)));
|
||||
break;
|
||||
case 0x2:
|
||||
bank &= 3;
|
||||
if (!memory->mbcState.mbc1.mode) {
|
||||
GBMBCSwitchBank(gb, (bank << 5) | (memory->currentBank & 0x1F));
|
||||
} else {
|
||||
if (memory->mbcState.mbc1.mode) {
|
||||
_switchBank0(gb, bank);
|
||||
GBMBCSwitchSramBank(gb, bank);
|
||||
}
|
||||
GBMBCSwitchBank(gb, (bank << memory->mbcState.mbc1.multicartStride) | (memory->currentBank & (stride - 1)));
|
||||
break;
|
||||
case 0x3:
|
||||
memory->mbcState.mbc1.mode = value & 1;
|
||||
if (memory->mbcState.mbc1.mode) {
|
||||
GBMBCSwitchBank(gb, memory->currentBank & 0x1F);
|
||||
_switchBank0(gb, memory->currentBank >> memory->mbcState.mbc1.multicartStride);
|
||||
} else {
|
||||
_switchBank0(gb, 0);
|
||||
GBMBCSwitchSramBank(gb, 0);
|
||||
}
|
||||
break;
|
||||
|
|
162
src/gb/memory.c
162
src/gb/memory.c
|
@ -16,6 +16,33 @@
|
|||
|
||||
mLOG_DEFINE_CATEGORY(GB_MEM, "GB Memory", "gb.memory");
|
||||
|
||||
struct OAMBlock {
|
||||
uint16_t low;
|
||||
uint16_t high;
|
||||
};
|
||||
|
||||
static const struct OAMBlock _oamBlockDMG[] = {
|
||||
{ 0xA000, 0xFE00 },
|
||||
{ 0xA000, 0xFE00 },
|
||||
{ 0xA000, 0xFE00 },
|
||||
{ 0xA000, 0xFE00 },
|
||||
{ 0x8000, 0xA000 },
|
||||
{ 0xA000, 0xFE00 },
|
||||
{ 0xA000, 0xFE00 },
|
||||
{ 0xA000, 0xFE00 },
|
||||
};
|
||||
|
||||
static const struct OAMBlock _oamBlockCGB[] = {
|
||||
{ 0xA000, 0xC000 },
|
||||
{ 0xA000, 0xC000 },
|
||||
{ 0xA000, 0xC000 },
|
||||
{ 0xA000, 0xC000 },
|
||||
{ 0x8000, 0xA000 },
|
||||
{ 0xA000, 0xC000 },
|
||||
{ 0xC000, 0xFE00 },
|
||||
{ 0xA000, 0xC000 },
|
||||
};
|
||||
|
||||
static void _pristineCow(struct GB* gba);
|
||||
|
||||
static uint8_t GBFastLoad8(struct LR35902Core* cpu, uint16_t address) {
|
||||
|
@ -62,6 +89,7 @@ void GBMemoryInit(struct GB* gb) {
|
|||
cpu->memory.cpuLoad8 = GBLoad8;
|
||||
cpu->memory.load8 = GBLoad8;
|
||||
cpu->memory.store8 = GBStore8;
|
||||
cpu->memory.currentSegment = GBCurrentSegment;
|
||||
cpu->memory.setActiveRegion = GBSetActiveRegion;
|
||||
|
||||
gb->memory.wram = 0;
|
||||
|
@ -130,14 +158,14 @@ void GBMemoryReset(struct GB* gb) {
|
|||
gb->memory.hdmaEvent.callback = _GBMemoryHDMAService;
|
||||
gb->memory.hdmaEvent.priority = 0x41;
|
||||
|
||||
gb->memory.sramAccess = false;
|
||||
gb->memory.rtcAccess = false;
|
||||
gb->memory.activeRtcReg = 0;
|
||||
gb->memory.rtcLatched = false;
|
||||
memset(&gb->memory.rtcRegs, 0, sizeof(gb->memory.rtcRegs));
|
||||
|
||||
memset(&gb->memory.hram, 0, sizeof(gb->memory.hram));
|
||||
memset(&gb->memory.mbcState, 0, sizeof(gb->memory.mbcState));
|
||||
switch (gb->memory.mbcType) {
|
||||
case GB_MBC1:
|
||||
gb->memory.mbcState.mbc1.mode = 0;
|
||||
break;
|
||||
default:
|
||||
memset(&gb->memory.mbcState, 0, sizeof(gb->memory.mbcState));
|
||||
}
|
||||
|
||||
GBMBCInit(gb);
|
||||
gb->memory.sramBank = gb->memory.sram;
|
||||
|
@ -159,6 +187,16 @@ void GBMemorySwitchWramBank(struct GBMemory* memory, int bank) {
|
|||
uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address) {
|
||||
struct GB* gb = (struct GB*) cpu->master;
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
if (gb->memory.dmaRemaining) {
|
||||
const struct OAMBlock* block = gb->model < GB_MODEL_CGB ? _oamBlockDMG : _oamBlockCGB;
|
||||
block = &block[memory->dmaSource >> 13];
|
||||
if (address >= block->low && address < block->high) {
|
||||
return 0xFF;
|
||||
}
|
||||
if (address >= GB_BASE_OAM && address < GB_BASE_UNUSABLE) {
|
||||
return 0xFF;
|
||||
}
|
||||
}
|
||||
switch (address >> 12) {
|
||||
case GB_REGION_CART_BANK0:
|
||||
case GB_REGION_CART_BANK0 + 1:
|
||||
|
@ -217,6 +255,16 @@ uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address) {
|
|||
void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) {
|
||||
struct GB* gb = (struct GB*) cpu->master;
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
if (gb->memory.dmaRemaining) {
|
||||
const struct OAMBlock* block = gb->model < GB_MODEL_CGB ? _oamBlockDMG : _oamBlockCGB;
|
||||
block = &block[memory->dmaSource >> 13];
|
||||
if (address >= block->low && address < block->high) {
|
||||
return;
|
||||
}
|
||||
if (address >= GB_BASE_OAM && address < GB_BASE_UNUSABLE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
switch (address >> 12) {
|
||||
case GB_REGION_CART_BANK0:
|
||||
case GB_REGION_CART_BANK0 + 1:
|
||||
|
@ -258,6 +306,7 @@ void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) {
|
|||
} else if (address < GB_BASE_UNUSABLE) {
|
||||
if (gb->video.mode < 2) {
|
||||
gb->video.oam.raw[address & 0xFF] = value;
|
||||
gb->video.renderer->writeOAM(gb->video.renderer, address & 0xFF);
|
||||
}
|
||||
} else if (address < GB_BASE_IO) {
|
||||
mLOG(GB_MEM, GAME_ERROR, "Attempt to write to unusable memory: %04X:%02X", address, value);
|
||||
|
@ -270,6 +319,37 @@ void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
int GBCurrentSegment(struct LR35902Core* cpu, uint16_t address) {
|
||||
struct GB* gb = (struct GB*) cpu->master;
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
switch (address >> 12) {
|
||||
case GB_REGION_CART_BANK0:
|
||||
case GB_REGION_CART_BANK0 + 1:
|
||||
case GB_REGION_CART_BANK0 + 2:
|
||||
case GB_REGION_CART_BANK0 + 3:
|
||||
return 0;
|
||||
case GB_REGION_CART_BANK1:
|
||||
case GB_REGION_CART_BANK1 + 1:
|
||||
case GB_REGION_CART_BANK1 + 2:
|
||||
case GB_REGION_CART_BANK1 + 3:
|
||||
return memory->currentBank;
|
||||
case GB_REGION_VRAM:
|
||||
case GB_REGION_VRAM + 1:
|
||||
return gb->video.vramCurrentBank;
|
||||
case GB_REGION_EXTERNAL_RAM:
|
||||
case GB_REGION_EXTERNAL_RAM + 1:
|
||||
return memory->sramCurrentBank;
|
||||
case GB_REGION_WORKING_RAM_BANK0:
|
||||
case GB_REGION_WORKING_RAM_BANK0 + 2:
|
||||
return 0;
|
||||
case GB_REGION_WORKING_RAM_BANK1:
|
||||
return memory->wramCurrentBank;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t GBView8(struct LR35902Core* cpu, uint16_t address, int segment) {
|
||||
struct GB* gb = (struct GB*) cpu->master;
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
|
@ -356,9 +436,6 @@ void GBMemoryDMA(struct GB* gb, uint16_t base) {
|
|||
if (base > 0xF100) {
|
||||
return;
|
||||
}
|
||||
gb->cpu->memory.store8 = GBDMAStore8;
|
||||
gb->cpu->memory.load8 = GBDMALoad8;
|
||||
gb->cpu->memory.cpuLoad8 = GBDMALoad8;
|
||||
mTimingSchedule(&gb->timing, &gb->memory.dmaEvent, 8);
|
||||
if (gb->cpu->cycles + 8 < gb->cpu->nextEvent) {
|
||||
gb->cpu->nextEvent = gb->cpu->cycles + 8;
|
||||
|
@ -392,17 +469,17 @@ void GBMemoryWriteHDMA5(struct GB* gb, uint8_t value) {
|
|||
|
||||
void _GBMemoryDMAService(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||
struct GB* gb = context;
|
||||
int dmaRemaining = gb->memory.dmaRemaining;
|
||||
gb->memory.dmaRemaining = 0;
|
||||
uint8_t b = GBLoad8(gb->cpu, gb->memory.dmaSource);
|
||||
// TODO: Can DMA write OAM during modes 2-3?
|
||||
gb->video.oam.raw[gb->memory.dmaDest] = b;
|
||||
gb->video.renderer->writeOAM(gb->video.renderer, gb->memory.dmaDest);
|
||||
++gb->memory.dmaSource;
|
||||
++gb->memory.dmaDest;
|
||||
--gb->memory.dmaRemaining;
|
||||
gb->memory.dmaRemaining = dmaRemaining - 1;
|
||||
if (gb->memory.dmaRemaining) {
|
||||
mTimingSchedule(timing, &gb->memory.dmaEvent, 4 - cyclesLate);
|
||||
} else {
|
||||
gb->cpu->memory.store8 = GBStore8;
|
||||
gb->cpu->memory.load8 = GBLoad8;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -434,61 +511,6 @@ void _GBMemoryHDMAService(struct mTiming* timing, void* context, uint32_t cycles
|
|||
}
|
||||
}
|
||||
|
||||
struct OAMBlock {
|
||||
uint16_t low;
|
||||
uint16_t high;
|
||||
};
|
||||
|
||||
static const struct OAMBlock _oamBlockDMG[] = {
|
||||
{ 0xA000, 0xFE00 },
|
||||
{ 0xA000, 0xFE00 },
|
||||
{ 0xA000, 0xFE00 },
|
||||
{ 0xA000, 0xFE00 },
|
||||
{ 0x8000, 0xA000 },
|
||||
{ 0xA000, 0xFE00 },
|
||||
{ 0xA000, 0xFE00 },
|
||||
{ 0xA000, 0xFE00 },
|
||||
};
|
||||
|
||||
static const struct OAMBlock _oamBlockCGB[] = {
|
||||
{ 0xA000, 0xC000 },
|
||||
{ 0xA000, 0xC000 },
|
||||
{ 0xA000, 0xC000 },
|
||||
{ 0xA000, 0xC000 },
|
||||
{ 0x8000, 0xA000 },
|
||||
{ 0xA000, 0xC000 },
|
||||
{ 0xC000, 0xFE00 },
|
||||
{ 0xA000, 0xC000 },
|
||||
};
|
||||
|
||||
uint8_t GBDMALoad8(struct LR35902Core* cpu, uint16_t address) {
|
||||
struct GB* gb = (struct GB*) cpu->master;
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
const struct OAMBlock* block = gb->model < GB_MODEL_CGB ? _oamBlockDMG : _oamBlockCGB;
|
||||
block = &block[memory->dmaSource >> 13];
|
||||
if (address >= block->low && address < block->high) {
|
||||
return 0xFF;
|
||||
}
|
||||
if (address >= GB_BASE_OAM && address < GB_BASE_UNUSABLE) {
|
||||
return 0xFF;
|
||||
}
|
||||
return GBLoad8(cpu, address);
|
||||
}
|
||||
|
||||
void GBDMAStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) {
|
||||
struct GB* gb = (struct GB*) cpu->master;
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
const struct OAMBlock* block = gb->model < GB_MODEL_CGB ? _oamBlockDMG : _oamBlockCGB;
|
||||
block = &block[memory->dmaSource >> 13];
|
||||
if (address >= block->low && address < block->high) {
|
||||
return;
|
||||
}
|
||||
if (address >= GB_BASE_OAM && address < GB_BASE_UNUSABLE) {
|
||||
return;
|
||||
}
|
||||
GBStore8(cpu, address, value);
|
||||
}
|
||||
|
||||
void GBPatch8(struct LR35902Core* cpu, uint16_t address, int8_t value, int8_t* old, int segment) {
|
||||
struct GB* gb = (struct GB*) cpu->master;
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
|
@ -559,6 +581,7 @@ void GBPatch8(struct LR35902Core* cpu, uint16_t address, int8_t value, int8_t* o
|
|||
} else if (address < GB_BASE_UNUSABLE) {
|
||||
oldValue = gb->video.oam.raw[address & 0xFF];
|
||||
gb->video.oam.raw[address & 0xFF] = value;
|
||||
gb->video.renderer->writeOAM(gb->video.renderer, address & 0xFF);
|
||||
} else if (address < GB_BASE_HRAM) {
|
||||
mLOG(GB_MEM, STUB, "Unimplemented memory Patch8: 0x%08X", address);
|
||||
return;
|
||||
|
@ -660,4 +683,5 @@ void _pristineCow(struct GB* gb) {
|
|||
}
|
||||
gb->memory.rom = newRom;
|
||||
GBMBCSwitchBank(gb, gb->memory.currentBank);
|
||||
gb->isPristine = false;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <mgba/internal/gb/overrides.h>
|
||||
|
||||
#include <mgba/internal/gb/gb.h>
|
||||
#include <mgba/internal/gb/mbc.h>
|
||||
|
||||
#include <mgba-util/configuration.h>
|
||||
#include <mgba-util/crc32.h>
|
||||
|
@ -102,6 +103,7 @@ void GBOverrideApply(struct GB* gb, const struct GBCartridgeOverride* override)
|
|||
|
||||
if (override->mbc != GB_MBC_AUTODETECT) {
|
||||
gb->memory.mbcType = override->mbc;
|
||||
GBMBCInit(gb);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer);
|
|||
static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
|
||||
static void GBVideoSoftwareRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value);
|
||||
static void GBVideoSoftwareRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address);
|
||||
static void GBVideoSoftwareRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam);
|
||||
static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax);
|
||||
static void GBVideoSoftwareRendererFinishScanline(struct GBVideoRenderer* renderer, int y);
|
||||
static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer);
|
||||
|
@ -24,26 +25,15 @@ static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer
|
|||
static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int startX, int endX, int y);
|
||||
|
||||
static void _clearScreen(struct GBVideoSoftwareRenderer* renderer) {
|
||||
// TODO: Dynamic from dmgPalette
|
||||
#ifdef COLOR_16_BIT
|
||||
#ifdef COLOR_5_6_5
|
||||
color_t palette0 = 0xFFDF;
|
||||
#else
|
||||
color_t palette0 = 0x7FFF;
|
||||
#endif
|
||||
#else
|
||||
color_t palette0 = 0xFFFFFF;
|
||||
#endif
|
||||
|
||||
int y;
|
||||
for (y = 0; y < GB_VIDEO_VERTICAL_PIXELS; ++y) {
|
||||
color_t* row = &renderer->outputBuffer[renderer->outputBufferStride * y];
|
||||
int x;
|
||||
for (x = 0; x < GB_VIDEO_HORIZONTAL_PIXELS; x += 4) {
|
||||
row[x + 0] = palette0;
|
||||
row[x + 1] = palette0;
|
||||
row[x + 2] = palette0;
|
||||
row[x + 3] = palette0;
|
||||
row[x + 0] = renderer->palette[0];
|
||||
row[x + 1] = renderer->palette[0];
|
||||
row[x + 2] = renderer->palette[0];
|
||||
row[x + 3] = renderer->palette[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,12 +44,17 @@ void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer* renderer) {
|
|||
renderer->d.writeVideoRegister = GBVideoSoftwareRendererWriteVideoRegister;
|
||||
renderer->d.writePalette = GBVideoSoftwareRendererWritePalette;
|
||||
renderer->d.writeVRAM = GBVideoSoftwareRendererWriteVRAM;
|
||||
renderer->d.writeOAM = GBVideoSoftwareRendererWriteOAM;
|
||||
renderer->d.drawRange = GBVideoSoftwareRendererDrawRange;
|
||||
renderer->d.finishScanline = GBVideoSoftwareRendererFinishScanline;
|
||||
renderer->d.finishFrame = GBVideoSoftwareRendererFinishFrame;
|
||||
renderer->d.getPixels = GBVideoSoftwareRendererGetPixels;
|
||||
renderer->d.putPixels = GBVideoSoftwareRendererPutPixels;
|
||||
|
||||
renderer->d.disableBG = false;
|
||||
renderer->d.disableOBJ = false;
|
||||
renderer->d.disableWIN = false;
|
||||
|
||||
renderer->temporaryBuffer = 0;
|
||||
}
|
||||
|
||||
|
@ -85,9 +80,6 @@ static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer*
|
|||
struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
|
||||
switch (address) {
|
||||
case REG_LCDC:
|
||||
if (GBRegisterLCDCIsEnable(softwareRenderer->lcdc) && !GBRegisterLCDCIsEnable(value)) {
|
||||
_clearScreen(softwareRenderer);
|
||||
}
|
||||
softwareRenderer->lcdc = value;
|
||||
break;
|
||||
case REG_SCY:
|
||||
|
@ -136,15 +128,24 @@ static void GBVideoSoftwareRendererWriteVRAM(struct GBVideoRenderer* renderer, u
|
|||
}
|
||||
}
|
||||
|
||||
static void GBVideoSoftwareRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam) {
|
||||
UNUSED(renderer);
|
||||
UNUSED(oam);
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax) {
|
||||
struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
|
||||
uint8_t* maps = &softwareRenderer->d.vram[GB_BASE_MAP];
|
||||
if (GBRegisterLCDCIsTileMap(softwareRenderer->lcdc)) {
|
||||
maps += GB_SIZE_MAP;
|
||||
}
|
||||
if (softwareRenderer->d.disableBG) {
|
||||
memset(&softwareRenderer->row[startX], 0, endX - startX);
|
||||
}
|
||||
if (GBRegisterLCDCIsBgEnable(softwareRenderer->lcdc) || softwareRenderer->model >= GB_MODEL_CGB) {
|
||||
if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && softwareRenderer->wy <= y && endX >= softwareRenderer->wx - 7) {
|
||||
if (softwareRenderer->wx - 7 > 0) {
|
||||
if (softwareRenderer->wx - 7 > 0 && !softwareRenderer->d.disableBG) {
|
||||
GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, softwareRenderer->wx - 7, softwareRenderer->scx, softwareRenderer->scy + y);
|
||||
}
|
||||
|
||||
|
@ -152,15 +153,17 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i
|
|||
if (GBRegisterLCDCIsWindowTileMap(softwareRenderer->lcdc)) {
|
||||
maps += GB_SIZE_MAP;
|
||||
}
|
||||
GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, softwareRenderer->wx - 7, endX, 7 - softwareRenderer->wx, softwareRenderer->currentWy);
|
||||
} else {
|
||||
if (!softwareRenderer->d.disableWIN) {
|
||||
GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, softwareRenderer->wx - 7, endX, 7 - softwareRenderer->wx, softwareRenderer->currentWy);
|
||||
}
|
||||
} else if (!softwareRenderer->d.disableBG) {
|
||||
GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, endX, softwareRenderer->scx, softwareRenderer->scy + y);
|
||||
}
|
||||
} else {
|
||||
} else if (!softwareRenderer->d.disableBG) {
|
||||
memset(&softwareRenderer->row[startX], 0, endX - startX);
|
||||
}
|
||||
|
||||
if (GBRegisterLCDCIsObjEnable(softwareRenderer->lcdc)) {
|
||||
if (GBRegisterLCDCIsObjEnable(softwareRenderer->lcdc) && !softwareRenderer->d.disableOBJ) {
|
||||
size_t i;
|
||||
for (i = 0; i < oamMax; ++i) {
|
||||
GBVideoSoftwareRendererDrawObj(softwareRenderer, &obj[i], startX, endX, y);
|
||||
|
@ -197,6 +200,9 @@ static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer)
|
|||
mappedMemoryFree(softwareRenderer->temporaryBuffer, GB_VIDEO_HORIZONTAL_PIXELS * GB_VIDEO_VERTICAL_PIXELS * 4);
|
||||
softwareRenderer->temporaryBuffer = 0;
|
||||
}
|
||||
if (!GBRegisterLCDCIsEnable(softwareRenderer->lcdc)) {
|
||||
_clearScreen(softwareRenderer);
|
||||
}
|
||||
softwareRenderer->currentWy = 0;
|
||||
}
|
||||
|
||||
|
|
158
src/gb/video.c
158
src/gb/video.c
|
@ -20,6 +20,7 @@ static void GBVideoDummyRendererDeinit(struct GBVideoRenderer* renderer);
|
|||
static uint8_t GBVideoDummyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
|
||||
static void GBVideoDummyRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value);
|
||||
static void GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address);
|
||||
static void GBVideoDummyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam);
|
||||
static void GBVideoDummyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax);
|
||||
static void GBVideoDummyRendererFinishScanline(struct GBVideoRenderer* renderer, int y);
|
||||
static void GBVideoDummyRendererFinishFrame(struct GBVideoRenderer* renderer);
|
||||
|
@ -39,6 +40,7 @@ static struct GBVideoRenderer dummyRenderer = {
|
|||
.deinit = GBVideoDummyRendererDeinit,
|
||||
.writeVideoRegister = GBVideoDummyRendererWriteVideoRegister,
|
||||
.writeVRAM = GBVideoDummyRendererWriteVRAM,
|
||||
.writeOAM = GBVideoDummyRendererWriteOAM,
|
||||
.writePalette = GBVideoDummyRendererWritePalette,
|
||||
.drawRange = GBVideoDummyRendererDrawRange,
|
||||
.finishScanline = GBVideoDummyRendererFinishScanline,
|
||||
|
@ -61,6 +63,11 @@ void GBVideoInit(struct GBVideo* video) {
|
|||
video->frameEvent.name = "GB Video Frame";
|
||||
video->frameEvent.callback = _updateFrameCount;
|
||||
video->frameEvent.priority = 9;
|
||||
|
||||
video->dmgPalette[0] = 0x7FFF;
|
||||
video->dmgPalette[1] = 0x56B5;
|
||||
video->dmgPalette[2] = 0x294A;
|
||||
video->dmgPalette[3] = 0x0000;
|
||||
}
|
||||
|
||||
void GBVideoReset(struct GBVideo* video) {
|
||||
|
@ -99,6 +106,33 @@ void GBVideoAssociateRenderer(struct GBVideo* video, struct GBVideoRenderer* ren
|
|||
video->renderer->init(video->renderer, video->p->model);
|
||||
}
|
||||
|
||||
static bool _statIRQAsserted(struct GBVideo* video, GBRegisterSTAT stat) {
|
||||
// TODO: variable for the IRQ line value?
|
||||
if (GBRegisterSTATIsLYCIRQ(stat) && GBRegisterSTATIsLYC(stat)) {
|
||||
return true;
|
||||
}
|
||||
switch (GBRegisterSTATGetMode(stat)) {
|
||||
case 0:
|
||||
if (GBRegisterSTATIsHblankIRQ(stat)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (GBRegisterSTATIsVblankIRQ(stat)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (GBRegisterSTATIsOAMIRQ(stat)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||
struct GBVideo* video = context;
|
||||
if (video->frameskipCounter <= 0) {
|
||||
|
@ -108,40 +142,30 @@ void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
|||
int32_t next;
|
||||
++video->ly;
|
||||
video->p->memory.io[REG_LY] = video->ly;
|
||||
GBRegisterSTAT oldStat = video->stat;
|
||||
video->stat = GBRegisterSTATSetLYC(video->stat, lyc == video->ly);
|
||||
if (video->ly < GB_VIDEO_VERTICAL_PIXELS) {
|
||||
// TODO: Cache SCX & 7 in case it changes during mode 2
|
||||
next = GB_VIDEO_MODE_2_LENGTH + (video->p->memory.io[REG_SCX] & 7);
|
||||
video->mode = 2;
|
||||
video->modeEvent.callback = _endMode2;
|
||||
if (!GBRegisterSTATIsHblankIRQ(video->stat) && GBRegisterSTATIsOAMIRQ(video->stat)) {
|
||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||
}
|
||||
} else {
|
||||
next = GB_VIDEO_HORIZONTAL_LENGTH;
|
||||
video->mode = 1;
|
||||
video->modeEvent.callback = _endMode1;
|
||||
|
||||
_updateFrameCount(timing, video, cyclesLate);
|
||||
mTimingSchedule(&video->p->timing, &video->frameEvent, -cyclesLate);
|
||||
|
||||
if (GBRegisterSTATIsVblankIRQ(video->stat) || GBRegisterSTATIsOAMIRQ(video->stat)) {
|
||||
if (!_statIRQAsserted(video, oldStat) && GBRegisterSTATIsOAMIRQ(video->stat)) {
|
||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||
}
|
||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_VBLANK);
|
||||
|
||||
size_t c;
|
||||
for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) {
|
||||
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&video->p->coreCallbacks, c);
|
||||
if (callbacks->videoFrameEnded) {
|
||||
callbacks->videoFrameEnded(callbacks->context);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!GBRegisterSTATIsHblankIRQ(video->stat) && GBRegisterSTATIsLYCIRQ(video->stat) && lyc == video->ly) {
|
||||
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
|
||||
if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
|
||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||
}
|
||||
GBUpdateIRQs(video->p);
|
||||
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
|
||||
video->p->memory.io[REG_STAT] = video->stat;
|
||||
mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate);
|
||||
}
|
||||
|
@ -161,11 +185,6 @@ void _endMode1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
|||
next = GB_VIDEO_MODE_2_LENGTH + (video->p->memory.io[REG_SCX] & 7);
|
||||
video->mode = 2;
|
||||
video->modeEvent.callback = _endMode2;
|
||||
if (GBRegisterSTATIsOAMIRQ(video->stat)) {
|
||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||
GBUpdateIRQs(video->p);
|
||||
}
|
||||
video->renderer->finishFrame(video->renderer);
|
||||
if (video->p->memory.mbcType == GB_MBC7 && video->p->memory.rotation && video->p->memory.rotation->sample) {
|
||||
video->p->memory.rotation->sample(video->p->memory.rotation);
|
||||
}
|
||||
|
@ -180,9 +199,10 @@ void _endMode1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
|||
next = GB_VIDEO_HORIZONTAL_LENGTH;
|
||||
}
|
||||
|
||||
GBRegisterSTAT oldStat = video->stat;
|
||||
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
|
||||
video->stat = GBRegisterSTATSetLYC(video->stat, lyc == video->p->memory.io[REG_LY]);
|
||||
if (video->ly && GBRegisterSTATIsLYCIRQ(video->stat) && lyc == video->p->memory.io[REG_LY]) {
|
||||
if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
|
||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||
GBUpdateIRQs(video->p);
|
||||
}
|
||||
|
@ -195,10 +215,15 @@ void _endMode2(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
|||
_cleanOAM(video, video->ly);
|
||||
video->x = 0;
|
||||
video->dotClock = timing->masterCycles - cyclesLate;
|
||||
int32_t next = GB_VIDEO_MODE_3_LENGTH_BASE + video->objMax * 11 - (video->p->memory.io[REG_SCX] & 7);
|
||||
int32_t next = GB_VIDEO_MODE_3_LENGTH_BASE + video->objMax * 6 - (video->p->memory.io[REG_SCX] & 7);
|
||||
video->mode = 3;
|
||||
video->modeEvent.callback = _endMode3;
|
||||
GBRegisterSTAT oldStat = video->stat;
|
||||
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
|
||||
if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
|
||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||
GBUpdateIRQs(video->p);
|
||||
}
|
||||
video->p->memory.io[REG_STAT] = video->stat;
|
||||
mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate);
|
||||
}
|
||||
|
@ -206,10 +231,6 @@ void _endMode2(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
|||
void _endMode3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||
struct GBVideo* video = context;
|
||||
GBVideoProcessDots(video);
|
||||
if (GBRegisterSTATIsHblankIRQ(video->stat)) {
|
||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||
GBUpdateIRQs(video->p);
|
||||
}
|
||||
if (video->ly < GB_VIDEO_VERTICAL_PIXELS && video->p->memory.isHdma && video->p->memory.io[REG_HDMA5] != 0xFF) {
|
||||
video->p->memory.hdmaRemaining = 0x10;
|
||||
mTimingDeschedule(timing, &video->p->memory.hdmaEvent);
|
||||
|
@ -217,9 +238,14 @@ void _endMode3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
|||
}
|
||||
video->mode = 0;
|
||||
video->modeEvent.callback = _endMode0;
|
||||
GBRegisterSTAT oldStat = video->stat;
|
||||
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
|
||||
if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
|
||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||
GBUpdateIRQs(video->p);
|
||||
}
|
||||
video->p->memory.io[REG_STAT] = video->stat;
|
||||
int32_t next = GB_VIDEO_MODE_0_LENGTH_BASE - video->objMax * 11;
|
||||
int32_t next = GB_VIDEO_MODE_0_LENGTH_BASE - video->objMax * 6;
|
||||
mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate);
|
||||
}
|
||||
|
||||
|
@ -231,9 +257,18 @@ void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLat
|
|||
return;
|
||||
}
|
||||
|
||||
size_t c;
|
||||
for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) {
|
||||
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&video->p->coreCallbacks, c);
|
||||
if (callbacks->videoFrameEnded) {
|
||||
callbacks->videoFrameEnded(callbacks->context);
|
||||
}
|
||||
}
|
||||
|
||||
GBFrameEnded(video->p);
|
||||
--video->frameskipCounter;
|
||||
if (video->frameskipCounter < 0) {
|
||||
video->renderer->finishFrame(video->renderer);
|
||||
mCoreSyncPostFrame(video->p->sync);
|
||||
video->frameskipCounter = video->frameskip;
|
||||
}
|
||||
|
@ -247,17 +282,16 @@ void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLat
|
|||
video->p->stream->postVideoFrame(video->p->stream, pixels, stride);
|
||||
}
|
||||
|
||||
size_t c;
|
||||
if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC])) {
|
||||
mTimingSchedule(timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH);
|
||||
}
|
||||
|
||||
for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) {
|
||||
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&video->p->coreCallbacks, c);
|
||||
if (callbacks->videoFrameStarted) {
|
||||
callbacks->videoFrameStarted(callbacks->context);
|
||||
}
|
||||
}
|
||||
|
||||
if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC])) {
|
||||
mTimingSchedule(timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
static void _cleanOAM(struct GBVideo* video, int y) {
|
||||
|
@ -312,13 +346,16 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
|
|||
video->ly = 0;
|
||||
video->p->memory.io[REG_LY] = 0;
|
||||
// TODO: Does this read as 0 for 4 T-cycles?
|
||||
GBRegisterSTAT oldStat = video->stat;
|
||||
video->stat = GBRegisterSTATSetMode(video->stat, 2);
|
||||
video->stat = GBRegisterSTATSetLYC(video->stat, video->ly == video->p->memory.io[REG_LYC]);
|
||||
if (GBRegisterSTATIsLYCIRQ(video->stat) && video->ly == video->p->memory.io[REG_LYC]) {
|
||||
if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
|
||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||
GBUpdateIRQs(video->p);
|
||||
}
|
||||
video->p->memory.io[REG_STAT] = video->stat;
|
||||
video->renderer->writePalette(video->renderer, 0, video->palette[0]);
|
||||
|
||||
mTimingDeschedule(&video->p->timing, &video->frameEvent);
|
||||
}
|
||||
if (GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) && !GBRegisterLCDCIsEnable(value)) {
|
||||
|
@ -327,6 +364,8 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
|
|||
video->p->memory.io[REG_STAT] = video->stat;
|
||||
video->ly = 0;
|
||||
video->p->memory.io[REG_LY] = 0;
|
||||
video->renderer->writePalette(video->renderer, 0, video->dmgPalette[0]);
|
||||
|
||||
mTimingDeschedule(&video->p->timing, &video->modeEvent);
|
||||
mTimingSchedule(&video->p->timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH);
|
||||
}
|
||||
|
@ -334,52 +373,52 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
|
|||
}
|
||||
|
||||
void GBVideoWriteSTAT(struct GBVideo* video, GBRegisterSTAT value) {
|
||||
GBRegisterSTAT oldStat = video->stat;
|
||||
video->stat = (video->stat & 0x7) | (value & 0x78);
|
||||
if (video->p->model == GB_MODEL_DMG && video->mode == 1) {
|
||||
if (video->p->model == GB_MODEL_DMG && !_statIRQAsserted(video, oldStat) && video->mode < 3) {
|
||||
// TODO: variable for the IRQ line value?
|
||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||
GBUpdateIRQs(video->p);
|
||||
}
|
||||
}
|
||||
|
||||
void GBVideoWriteLYC(struct GBVideo* video, uint8_t value) {
|
||||
if (video->mode == 2) {
|
||||
video->stat = GBRegisterSTATSetLYC(video->stat, value == video->ly);
|
||||
if (GBRegisterSTATIsLYCIRQ(video->stat) && value == video->ly) {
|
||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||
GBUpdateIRQs(video->p);
|
||||
}
|
||||
GBRegisterSTAT oldStat = video->stat;
|
||||
video->stat = GBRegisterSTATSetLYC(video->stat, value == video->ly);
|
||||
if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
|
||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||
GBUpdateIRQs(video->p);
|
||||
}
|
||||
}
|
||||
|
||||
void GBVideoWritePalette(struct GBVideo* video, uint16_t address, uint8_t value) {
|
||||
static const uint16_t dmgPalette[4] = { 0x7FFF, 0x56B5, 0x294A, 0x0000};
|
||||
if (video->p->model < GB_MODEL_CGB) {
|
||||
switch (address) {
|
||||
case REG_BGP:
|
||||
video->palette[0] = dmgPalette[value & 3];
|
||||
video->palette[1] = dmgPalette[(value >> 2) & 3];
|
||||
video->palette[2] = dmgPalette[(value >> 4) & 3];
|
||||
video->palette[3] = dmgPalette[(value >> 6) & 3];
|
||||
video->palette[0] = video->dmgPalette[value & 3];
|
||||
video->palette[1] = video->dmgPalette[(value >> 2) & 3];
|
||||
video->palette[2] = video->dmgPalette[(value >> 4) & 3];
|
||||
video->palette[3] = video->dmgPalette[(value >> 6) & 3];
|
||||
video->renderer->writePalette(video->renderer, 0, video->palette[0]);
|
||||
video->renderer->writePalette(video->renderer, 1, video->palette[1]);
|
||||
video->renderer->writePalette(video->renderer, 2, video->palette[2]);
|
||||
video->renderer->writePalette(video->renderer, 3, video->palette[3]);
|
||||
break;
|
||||
case REG_OBP0:
|
||||
video->palette[8 * 4 + 0] = dmgPalette[value & 3];
|
||||
video->palette[8 * 4 + 1] = dmgPalette[(value >> 2) & 3];
|
||||
video->palette[8 * 4 + 2] = dmgPalette[(value >> 4) & 3];
|
||||
video->palette[8 * 4 + 3] = dmgPalette[(value >> 6) & 3];
|
||||
video->palette[8 * 4 + 0] = video->dmgPalette[value & 3];
|
||||
video->palette[8 * 4 + 1] = video->dmgPalette[(value >> 2) & 3];
|
||||
video->palette[8 * 4 + 2] = video->dmgPalette[(value >> 4) & 3];
|
||||
video->palette[8 * 4 + 3] = video->dmgPalette[(value >> 6) & 3];
|
||||
video->renderer->writePalette(video->renderer, 8 * 4 + 0, video->palette[8 * 4 + 0]);
|
||||
video->renderer->writePalette(video->renderer, 8 * 4 + 1, video->palette[8 * 4 + 1]);
|
||||
video->renderer->writePalette(video->renderer, 8 * 4 + 2, video->palette[8 * 4 + 2]);
|
||||
video->renderer->writePalette(video->renderer, 8 * 4 + 3, video->palette[8 * 4 + 3]);
|
||||
break;
|
||||
case REG_OBP1:
|
||||
video->palette[9 * 4 + 0] = dmgPalette[value & 3];
|
||||
video->palette[9 * 4 + 1] = dmgPalette[(value >> 2) & 3];
|
||||
video->palette[9 * 4 + 2] = dmgPalette[(value >> 4) & 3];
|
||||
video->palette[9 * 4 + 3] = dmgPalette[(value >> 6) & 3];
|
||||
video->palette[9 * 4 + 0] = video->dmgPalette[value & 3];
|
||||
video->palette[9 * 4 + 1] = video->dmgPalette[(value >> 2) & 3];
|
||||
video->palette[9 * 4 + 2] = video->dmgPalette[(value >> 4) & 3];
|
||||
video->palette[9 * 4 + 3] = video->dmgPalette[(value >> 6) & 3];
|
||||
video->renderer->writePalette(video->renderer, 9 * 4 + 0, video->palette[9 * 4 + 0]);
|
||||
video->renderer->writePalette(video->renderer, 9 * 4 + 1, video->palette[9 * 4 + 1]);
|
||||
video->renderer->writePalette(video->renderer, 9 * 4 + 2, video->palette[9 * 4 + 2]);
|
||||
|
@ -432,6 +471,13 @@ void GBVideoSwitchBank(struct GBVideo* video, uint8_t value) {
|
|||
video->vramCurrentBank = value;
|
||||
}
|
||||
|
||||
void GBVideoSetPalette(struct GBVideo* video, unsigned index, uint16_t color) {
|
||||
if (index >= 4) {
|
||||
return;
|
||||
}
|
||||
video->dmgPalette[index] = color;
|
||||
}
|
||||
|
||||
static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model) {
|
||||
UNUSED(renderer);
|
||||
UNUSED(model);
|
||||
|
@ -455,6 +501,12 @@ static void GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint
|
|||
}
|
||||
}
|
||||
|
||||
static void GBVideoDummyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam) {
|
||||
UNUSED(renderer);
|
||||
UNUSED(oam);
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
static void GBVideoDummyRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value) {
|
||||
UNUSED(value);
|
||||
if (renderer->cache) {
|
||||
|
|
|
@ -297,8 +297,8 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
|||
audio->lastRight = sampleRight;
|
||||
audio->clock += audio->sampleInterval;
|
||||
if (audio->clock >= CLOCKS_PER_FRAME) {
|
||||
blip_end_frame(audio->psg.left, audio->clock);
|
||||
blip_end_frame(audio->psg.right, audio->clock);
|
||||
blip_end_frame(audio->psg.left, CLOCKS_PER_FRAME);
|
||||
blip_end_frame(audio->psg.right, CLOCKS_PER_FRAME);
|
||||
audio->clock -= CLOCKS_PER_FRAME;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -271,7 +271,7 @@ static void _Div(struct GBA* gba, int32_t num, int32_t denom) {
|
|||
}
|
||||
}
|
||||
|
||||
static int16_t _ArcTan(int16_t i) {
|
||||
static int16_t _ArcTan(int32_t i, int32_t* r1, int32_t* r3) {
|
||||
int32_t a = -((i * i) >> 14);
|
||||
int32_t b = ((0xA9 * a) >> 14) + 0x390;
|
||||
b = ((b * a) >> 14) + 0x91C;
|
||||
|
@ -280,10 +280,16 @@ static int16_t _ArcTan(int16_t i) {
|
|||
b = ((b * a) >> 14) + 0x2081;
|
||||
b = ((b * a) >> 14) + 0x3651;
|
||||
b = ((b * a) >> 14) + 0xA2F9;
|
||||
if (r1) {
|
||||
*r1 = a;
|
||||
}
|
||||
if (r3) {
|
||||
*r3 = b;
|
||||
}
|
||||
return (i * b) >> 16;
|
||||
}
|
||||
|
||||
static int16_t _ArcTan2(int16_t x, int16_t y) {
|
||||
static int16_t _ArcTan2(int32_t x, int32_t y, int32_t* r1) {
|
||||
if (!y) {
|
||||
if (x >= 0) {
|
||||
return 0;
|
||||
|
@ -299,21 +305,21 @@ static int16_t _ArcTan2(int16_t x, int16_t y) {
|
|||
if (y >= 0) {
|
||||
if (x >= 0) {
|
||||
if (x >= y) {
|
||||
return _ArcTan((y << 14)/ x);
|
||||
return _ArcTan((y << 14) / x, r1, NULL);
|
||||
}
|
||||
} else if (-x >= y) {
|
||||
return _ArcTan((y << 14) / x) + 0x8000;
|
||||
return _ArcTan((y << 14) / x, r1, NULL) + 0x8000;
|
||||
}
|
||||
return 0x4000 - _ArcTan((x << 14) / y);
|
||||
return 0x4000 - _ArcTan((x << 14) / y, r1, NULL);
|
||||
} else {
|
||||
if (x <= 0) {
|
||||
if (-x > -y) {
|
||||
return _ArcTan((y << 14) / x) + 0x8000;
|
||||
return _ArcTan((y << 14) / x, r1, NULL) + 0x8000;
|
||||
}
|
||||
} else if (x >= -y) {
|
||||
return _ArcTan((y << 14) / x) + 0x10000;
|
||||
return _ArcTan((y << 14) / x, r1, NULL) + 0x10000;
|
||||
}
|
||||
return 0xC000 - _ArcTan((x << 14) / y);
|
||||
return 0xC000 - _ArcTan((x << 14) / y, r1, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -356,10 +362,11 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
|
|||
cpu->gprs[0] = sqrt((uint32_t) cpu->gprs[0]);
|
||||
break;
|
||||
case 0x9:
|
||||
cpu->gprs[0] = (uint16_t) _ArcTan(cpu->gprs[0]);
|
||||
cpu->gprs[0] = _ArcTan(cpu->gprs[0], &cpu->gprs[1], &cpu->gprs[3]);
|
||||
break;
|
||||
case 0xA:
|
||||
cpu->gprs[0] = (uint16_t) _ArcTan2(cpu->gprs[0], cpu->gprs[1]);
|
||||
cpu->gprs[0] = (uint16_t) _ArcTan2(cpu->gprs[0], cpu->gprs[1], &cpu->gprs[1]);
|
||||
cpu->gprs[3] = 0x170;
|
||||
break;
|
||||
case 0xB:
|
||||
case 0xC:
|
||||
|
|
260
src/gba/core.c
260
src/gba/core.c
|
@ -10,11 +10,13 @@
|
|||
#include <mgba/internal/arm/debugger/debugger.h>
|
||||
#include <mgba/internal/gba/cheats.h>
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
#include <mgba/internal/gba/io.h>
|
||||
#include <mgba/internal/gba/extra/cli.h>
|
||||
#include <mgba/internal/gba/overrides.h>
|
||||
#ifndef DISABLE_THREADING
|
||||
#include <mgba/internal/gba/renderers/thread-proxy.h>
|
||||
#include <mgba/feature/thread-proxy.h>
|
||||
#endif
|
||||
#include <mgba/internal/gba/renderers/proxy.h>
|
||||
#include <mgba/internal/gba/renderers/video-software.h>
|
||||
#include <mgba/internal/gba/savedata.h>
|
||||
#include <mgba/internal/gba/serialize.h>
|
||||
|
@ -26,11 +28,32 @@
|
|||
#include <mgba/internal/gba/input.h>
|
||||
#endif
|
||||
|
||||
const static struct mCoreChannelInfo _GBAVideoLayers[] = {
|
||||
{ 0, "bg0", "Background 0", NULL },
|
||||
{ 1, "bg1", "Background 1", NULL },
|
||||
{ 2, "bg2", "Background 2", NULL },
|
||||
{ 3, "bg3", "Background 3", NULL },
|
||||
{ 4, "obj", "Objects", NULL },
|
||||
};
|
||||
|
||||
const static struct mCoreChannelInfo _GBAAudioChannels[] = {
|
||||
{ 0, "ch0", "PSG Channel 0", "Square/Sweep" },
|
||||
{ 1, "ch1", "PSG Channel 1", "Square" },
|
||||
{ 2, "ch2", "PSG Channel 2", "PCM" },
|
||||
{ 3, "ch3", "PSG Channel 3", "Noise" },
|
||||
{ 4, "chA", "FIFO Channel A", NULL },
|
||||
{ 5, "chB", "FIFO Channel B", NULL },
|
||||
};
|
||||
|
||||
struct mVideoLogContext;
|
||||
struct GBACore {
|
||||
struct mCore d;
|
||||
struct GBAVideoSoftwareRenderer renderer;
|
||||
struct GBAVideoProxyRenderer proxyRenderer;
|
||||
struct mVideoLogContext* logContext;
|
||||
struct mCoreCallbacks logCallbacks;
|
||||
#ifndef DISABLE_THREADING
|
||||
struct GBAVideoThreadProxyRenderer threadProxy;
|
||||
struct mVideoThreadProxy threadProxy;
|
||||
int threadedVideo;
|
||||
#endif
|
||||
int keys;
|
||||
|
@ -53,9 +76,11 @@ static bool _GBACoreInit(struct mCore* core) {
|
|||
core->cpu = cpu;
|
||||
core->board = gba;
|
||||
core->debugger = NULL;
|
||||
core->symbolTable = NULL;
|
||||
gbacore->overrides = NULL;
|
||||
gbacore->debuggerPlatform = NULL;
|
||||
gbacore->cheatDevice = NULL;
|
||||
gbacore->logContext = NULL;
|
||||
|
||||
GBACreate(gba);
|
||||
// TODO: Restore cheats
|
||||
|
@ -70,8 +95,9 @@ static bool _GBACoreInit(struct mCore* core) {
|
|||
|
||||
#ifndef DISABLE_THREADING
|
||||
gbacore->threadedVideo = false;
|
||||
GBAVideoThreadProxyRendererCreate(&gbacore->threadProxy, &gbacore->renderer.d);
|
||||
mVideoThreadProxyCreate(&gbacore->threadProxy);
|
||||
#endif
|
||||
gbacore->proxyRenderer.logger = NULL;
|
||||
|
||||
gbacore->keys = 0;
|
||||
gba->keySource = &gbacore->keys;
|
||||
|
@ -281,7 +307,9 @@ static void _GBACoreReset(struct mCore* core) {
|
|||
struct GBAVideoRenderer* renderer = &gbacore->renderer.d;
|
||||
#ifndef DISABLE_THREADING
|
||||
if (gbacore->threadedVideo) {
|
||||
renderer = &gbacore->threadProxy.d;
|
||||
gbacore->proxyRenderer.logger = &gbacore->threadProxy.d;
|
||||
GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, renderer);
|
||||
renderer = &gbacore->proxyRenderer.d;
|
||||
}
|
||||
#endif
|
||||
GBAVideoAssociateRenderer(&gba->video, renderer);
|
||||
|
@ -326,7 +354,7 @@ static void _GBACoreReset(struct mCore* core) {
|
|||
mCoreConfigDirectory(path, PATH_MAX);
|
||||
strncat(path, PATH_SEP "gba_bios.bin", PATH_MAX - strlen(path));
|
||||
bios = VFileOpen(path, O_RDONLY);
|
||||
if (bios && GBIsBIOS(bios)) {
|
||||
if (bios && GBAIsBIOS(bios)) {
|
||||
found = true;
|
||||
} else if (bios) {
|
||||
bios->close(bios);
|
||||
|
@ -378,16 +406,19 @@ static bool _GBACoreSaveState(struct mCore* core, void* state) {
|
|||
static void _GBACoreSetKeys(struct mCore* core, uint32_t keys) {
|
||||
struct GBACore* gbacore = (struct GBACore*) core;
|
||||
gbacore->keys = keys;
|
||||
GBATestKeypadIRQ(core->board);
|
||||
}
|
||||
|
||||
static void _GBACoreAddKeys(struct mCore* core, uint32_t keys) {
|
||||
struct GBACore* gbacore = (struct GBACore*) core;
|
||||
gbacore->keys |= keys;
|
||||
GBATestKeypadIRQ(core->board);
|
||||
}
|
||||
|
||||
static void _GBACoreClearKeys(struct mCore* core, uint32_t keys) {
|
||||
struct GBACore* gbacore = (struct GBACore*) core;
|
||||
gbacore->keys &= ~keys;
|
||||
GBATestKeypadIRQ(core->board);
|
||||
}
|
||||
|
||||
static int32_t _GBACoreFrameCounter(const struct mCore* core) {
|
||||
|
@ -413,14 +444,21 @@ static void _GBACoreGetGameCode(const struct mCore* core, char* title) {
|
|||
GBAGetGameCode(core->board, title);
|
||||
}
|
||||
|
||||
static void _GBACoreSetRotation(struct mCore* core, struct mRotationSource* rotation) {
|
||||
static void _GBACoreSetPeripheral(struct mCore* core, int type, void* periph) {
|
||||
struct GBA* gba = core->board;
|
||||
gba->rotationSource = rotation;
|
||||
}
|
||||
|
||||
static void _GBACoreSetRumble(struct mCore* core, struct mRumble* rumble) {
|
||||
struct GBA* gba = core->board;
|
||||
gba->rumble = rumble;
|
||||
switch (type) {
|
||||
case mPERIPH_ROTATION:
|
||||
gba->rotationSource = periph;
|
||||
break;
|
||||
case mPERIPH_RUMBLE:
|
||||
gba->rumble = periph;
|
||||
break;
|
||||
case mPERIPH_GBA_LUMINANCE:
|
||||
gba->luminanceSource = periph;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t _GBACoreBusRead8(struct mCore* core, uint32_t address) {
|
||||
|
@ -529,6 +567,10 @@ static void _GBACoreDetachDebugger(struct mCore* core) {
|
|||
GBADetachDebugger(core->board);
|
||||
core->debugger = NULL;
|
||||
}
|
||||
|
||||
static void _GBACoreLoadSymbols(struct mCore* core, struct VFile* vf) {
|
||||
// TODO
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct mCheatDevice* _GBACoreCheatDevice(struct mCore* core) {
|
||||
|
@ -582,6 +624,81 @@ static bool _GBACoreSavedataRestore(struct mCore* core, const void* sram, size_t
|
|||
return success;
|
||||
}
|
||||
|
||||
static size_t _GBACoreListVideoLayers(const struct mCore* core, const struct mCoreChannelInfo** info) {
|
||||
UNUSED(core);
|
||||
*info = _GBAVideoLayers;
|
||||
return sizeof(_GBAVideoLayers) / sizeof(*_GBAVideoLayers);
|
||||
}
|
||||
|
||||
static size_t _GBACoreListAudioChannels(const struct mCore* core, const struct mCoreChannelInfo** info) {
|
||||
UNUSED(core);
|
||||
*info = _GBAAudioChannels;
|
||||
return sizeof(_GBAAudioChannels) / sizeof(*_GBAAudioChannels);
|
||||
}
|
||||
|
||||
static void _GBACoreEnableVideoLayer(struct mCore* core, size_t id, bool enable) {
|
||||
struct GBA* gba = core->board;
|
||||
switch (id) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
gba->video.renderer->disableBG[id] = !enable;
|
||||
break;
|
||||
case 4:
|
||||
gba->video.renderer->disableOBJ = !enable;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void _GBACoreEnableAudioChannel(struct mCore* core, size_t id, bool enable) {
|
||||
struct GBA* gba = core->board;
|
||||
switch (id) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
gba->audio.psg.forceDisableCh[id] = !enable;
|
||||
break;
|
||||
case 4:
|
||||
gba->audio.forceDisableChA = !enable;
|
||||
case 5:
|
||||
gba->audio.forceDisableChB = !enable;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void _GBACoreStartVideoLog(struct mCore* core, struct mVideoLogContext* context) {
|
||||
struct GBACore* gbacore = (struct GBACore*) core;
|
||||
struct GBA* gba = core->board;
|
||||
gbacore->logContext = context;
|
||||
|
||||
struct GBASerializedState* state = mVideoLogContextInitialState(context, NULL);
|
||||
state->id = 0;
|
||||
state->cpu.gprs[ARM_PC] = BASE_WORKING_RAM;
|
||||
|
||||
int channelId = mVideoLoggerAddChannel(context);
|
||||
gbacore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger));
|
||||
mVideoLoggerRendererCreate(gbacore->proxyRenderer.logger, false);
|
||||
mVideoLoggerAttachChannel(gbacore->proxyRenderer.logger, context, channelId);
|
||||
gbacore->proxyRenderer.logger->block = false;
|
||||
|
||||
GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, &gbacore->renderer.d);
|
||||
GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer);
|
||||
}
|
||||
|
||||
static void _GBACoreEndVideoLog(struct mCore* core) {
|
||||
struct GBACore* gbacore = (struct GBACore*) core;
|
||||
struct GBA* gba = core->board;
|
||||
GBAVideoProxyRendererUnshim(&gba->video, &gbacore->proxyRenderer);
|
||||
free(gbacore->proxyRenderer.logger);
|
||||
gbacore->proxyRenderer.logger = NULL;
|
||||
}
|
||||
|
||||
struct mCore* GBACoreCreate(void) {
|
||||
struct GBACore* gbacore = malloc(sizeof(*gbacore));
|
||||
struct mCore* core = &gbacore->d;
|
||||
|
@ -627,8 +744,7 @@ struct mCore* GBACoreCreate(void) {
|
|||
core->frequency = _GBACoreFrequency;
|
||||
core->getGameTitle = _GBACoreGetGameTitle;
|
||||
core->getGameCode = _GBACoreGetGameCode;
|
||||
core->setRotation = _GBACoreSetRotation;
|
||||
core->setRumble = _GBACoreSetRumble;
|
||||
core->setPeripheral = _GBACoreSetPeripheral;
|
||||
core->busRead8 = _GBACoreBusRead8;
|
||||
core->busRead16 = _GBACoreBusRead16;
|
||||
core->busRead32 = _GBACoreBusRead32;
|
||||
|
@ -647,9 +763,125 @@ struct mCore* GBACoreCreate(void) {
|
|||
core->cliDebuggerSystem = _GBACoreCliDebuggerSystem;
|
||||
core->attachDebugger = _GBACoreAttachDebugger;
|
||||
core->detachDebugger = _GBACoreDetachDebugger;
|
||||
core->loadSymbols = _GBACoreLoadSymbols;
|
||||
#endif
|
||||
core->cheatDevice = _GBACoreCheatDevice;
|
||||
core->savedataClone = _GBACoreSavedataClone;
|
||||
core->savedataRestore = _GBACoreSavedataRestore;
|
||||
core->listVideoLayers = _GBACoreListVideoLayers;
|
||||
core->listAudioChannels = _GBACoreListAudioChannels;
|
||||
core->enableVideoLayer = _GBACoreEnableVideoLayer;
|
||||
core->enableAudioChannel = _GBACoreEnableAudioChannel;
|
||||
#ifndef MINIMAL_CORE
|
||||
core->startVideoLog = _GBACoreStartVideoLog;
|
||||
core->endVideoLog = _GBACoreEndVideoLog;
|
||||
#endif
|
||||
return core;
|
||||
}
|
||||
|
||||
#ifndef MINIMAL_CORE
|
||||
static void _GBAVLPStartFrameCallback(void *context) {
|
||||
struct mCore* core = context;
|
||||
struct GBACore* gbacore = (struct GBACore*) core;
|
||||
struct GBA* gba = core->board;
|
||||
|
||||
if (!mVideoLoggerRendererRun(gbacore->proxyRenderer.logger, true)) {
|
||||
GBAVideoProxyRendererUnshim(&gba->video, &gbacore->proxyRenderer);
|
||||
mVideoLogContextRewind(gbacore->logContext, core);
|
||||
GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer);
|
||||
}
|
||||
}
|
||||
|
||||
static bool _GBAVLPInit(struct mCore* core) {
|
||||
struct GBACore* gbacore = (struct GBACore*) core;
|
||||
if (!_GBACoreInit(core)) {
|
||||
return false;
|
||||
}
|
||||
gbacore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger));
|
||||
mVideoLoggerRendererCreate(gbacore->proxyRenderer.logger, true);
|
||||
GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, NULL);
|
||||
memset(&gbacore->logCallbacks, 0, sizeof(gbacore->logCallbacks));
|
||||
gbacore->logCallbacks.videoFrameStarted = _GBAVLPStartFrameCallback;
|
||||
gbacore->logCallbacks.context = core;
|
||||
core->addCoreCallbacks(core, &gbacore->logCallbacks);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void _GBAVLPDeinit(struct mCore* core) {
|
||||
struct GBACore* gbacore = (struct GBACore*) core;
|
||||
if (gbacore->logContext) {
|
||||
mVideoLogContextDestroy(core, gbacore->logContext);
|
||||
}
|
||||
_GBACoreDeinit(core);
|
||||
}
|
||||
|
||||
static void _GBAVLPReset(struct mCore* core) {
|
||||
struct GBACore* gbacore = (struct GBACore*) core;
|
||||
struct GBA* gba = (struct GBA*) core->board;
|
||||
if (gba->video.renderer == &gbacore->proxyRenderer.d) {
|
||||
GBAVideoProxyRendererUnshim(&gba->video, &gbacore->proxyRenderer);
|
||||
} else if (gbacore->renderer.outputBuffer) {
|
||||
struct GBAVideoRenderer* renderer = &gbacore->renderer.d;
|
||||
GBAVideoAssociateRenderer(&gba->video, renderer);
|
||||
}
|
||||
|
||||
ARMReset(core->cpu);
|
||||
mVideoLogContextRewind(gbacore->logContext, core);
|
||||
GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer);
|
||||
|
||||
// Make sure CPU loop never spins
|
||||
GBAHalt(gba);
|
||||
gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL);
|
||||
gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL);
|
||||
}
|
||||
|
||||
static bool _GBAVLPLoadROM(struct mCore* core, struct VFile* vf) {
|
||||
struct GBACore* gbacore = (struct GBACore*) core;
|
||||
gbacore->logContext = mVideoLogContextCreate(NULL);
|
||||
if (!mVideoLogContextLoad(gbacore->logContext, vf)) {
|
||||
mVideoLogContextDestroy(core, gbacore->logContext);
|
||||
gbacore->logContext = NULL;
|
||||
return false;
|
||||
}
|
||||
mVideoLoggerAttachChannel(gbacore->proxyRenderer.logger, gbacore->logContext, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _GBAVLPLoadState(struct mCore* core, const void* state) {
|
||||
struct GBA* gba = (struct GBA*) core->board;
|
||||
|
||||
gba->timing.root = NULL;
|
||||
gba->cpu->gprs[ARM_PC] = BASE_WORKING_RAM;
|
||||
gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
|
||||
|
||||
// Make sure CPU loop never spins
|
||||
GBAHalt(gba);
|
||||
gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL);
|
||||
gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL);
|
||||
GBAVideoDeserialize(&gba->video, state);
|
||||
GBAIODeserialize(gba, state);
|
||||
GBAAudioReset(&gba->audio);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _returnTrue(struct VFile* vf) {
|
||||
UNUSED(vf);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct mCore* GBAVideoLogPlayerCreate(void) {
|
||||
struct mCore* core = GBACoreCreate();
|
||||
core->init = _GBAVLPInit;
|
||||
core->deinit = _GBAVLPDeinit;
|
||||
core->reset = _GBAVLPReset;
|
||||
core->loadROM = _GBAVLPLoadROM;
|
||||
core->loadState = _GBAVLPLoadState;
|
||||
core->isROM = _returnTrue;
|
||||
return core;
|
||||
}
|
||||
#else
|
||||
struct mCore* GBAVideoLogPlayerCreate(void) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,283 @@
|
|||
/* 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/renderers/proxy.h>
|
||||
|
||||
#include <mgba/core/tile-cache.h>
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
#include <mgba/internal/gba/io.h>
|
||||
|
||||
static void GBAVideoProxyRendererInit(struct GBAVideoRenderer* renderer);
|
||||
static void GBAVideoProxyRendererReset(struct GBAVideoRenderer* renderer);
|
||||
static void GBAVideoProxyRendererDeinit(struct GBAVideoRenderer* renderer);
|
||||
static uint16_t GBAVideoProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
|
||||
static void GBAVideoProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address);
|
||||
static void GBAVideoProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
|
||||
static void GBAVideoProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
|
||||
static void GBAVideoProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
|
||||
static void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer);
|
||||
static void GBAVideoProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels);
|
||||
static void GBAVideoProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels);
|
||||
|
||||
static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* packet);
|
||||
static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address);
|
||||
|
||||
void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct GBAVideoRenderer* backend) {
|
||||
renderer->d.init = GBAVideoProxyRendererInit;
|
||||
renderer->d.reset = GBAVideoProxyRendererReset;
|
||||
renderer->d.deinit = GBAVideoProxyRendererDeinit;
|
||||
renderer->d.writeVideoRegister = GBAVideoProxyRendererWriteVideoRegister;
|
||||
renderer->d.writeVRAM = GBAVideoProxyRendererWriteVRAM;
|
||||
renderer->d.writeOAM = GBAVideoProxyRendererWriteOAM;
|
||||
renderer->d.writePalette = GBAVideoProxyRendererWritePalette;
|
||||
renderer->d.drawScanline = GBAVideoProxyRendererDrawScanline;
|
||||
renderer->d.finishFrame = GBAVideoProxyRendererFinishFrame;
|
||||
renderer->d.getPixels = GBAVideoProxyRendererGetPixels;
|
||||
renderer->d.putPixels = GBAVideoProxyRendererPutPixels;
|
||||
|
||||
renderer->d.disableBG[0] = false;
|
||||
renderer->d.disableBG[1] = false;
|
||||
renderer->d.disableBG[2] = false;
|
||||
renderer->d.disableBG[3] = false;
|
||||
renderer->d.disableOBJ = false;
|
||||
|
||||
renderer->logger->context = renderer;
|
||||
renderer->logger->parsePacket = _parsePacket;
|
||||
renderer->logger->vramBlock = _vramBlock;
|
||||
renderer->logger->paletteSize = SIZE_PALETTE_RAM;
|
||||
renderer->logger->vramSize = SIZE_VRAM;
|
||||
renderer->logger->oamSize = SIZE_OAM;
|
||||
|
||||
renderer->backend = backend;
|
||||
}
|
||||
|
||||
static void _init(struct GBAVideoProxyRenderer* proxyRenderer) {
|
||||
mVideoLoggerRendererInit(proxyRenderer->logger);
|
||||
|
||||
if (proxyRenderer->logger->block) {
|
||||
proxyRenderer->backend->palette = proxyRenderer->logger->palette;
|
||||
proxyRenderer->backend->vram = proxyRenderer->logger->vram;
|
||||
proxyRenderer->backend->oam = (union GBAOAM*) proxyRenderer->logger->oam;
|
||||
proxyRenderer->backend->cache = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void _reset(struct GBAVideoProxyRenderer* proxyRenderer) {
|
||||
memcpy(proxyRenderer->logger->oam, &proxyRenderer->d.oam->raw, SIZE_OAM);
|
||||
memcpy(proxyRenderer->logger->palette, proxyRenderer->d.palette, SIZE_PALETTE_RAM);
|
||||
memcpy(proxyRenderer->logger->vram, proxyRenderer->d.vram, SIZE_VRAM);
|
||||
|
||||
mVideoLoggerRendererReset(proxyRenderer->logger);
|
||||
}
|
||||
|
||||
void GBAVideoProxyRendererShim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer) {
|
||||
if ((renderer->backend && video->renderer != renderer->backend) || video->renderer == &renderer->d) {
|
||||
return;
|
||||
}
|
||||
renderer->backend = video->renderer;
|
||||
video->renderer = &renderer->d;
|
||||
renderer->d.cache = renderer->backend->cache;
|
||||
renderer->d.palette = video->palette;
|
||||
renderer->d.vram = video->vram;
|
||||
renderer->d.oam = &video->oam;
|
||||
_init(renderer);
|
||||
_reset(renderer);
|
||||
}
|
||||
|
||||
void GBAVideoProxyRendererUnshim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer) {
|
||||
if (video->renderer != &renderer->d) {
|
||||
return;
|
||||
}
|
||||
renderer->backend->cache = video->renderer->cache;
|
||||
video->renderer = renderer->backend;
|
||||
renderer->backend->palette = video->palette;
|
||||
renderer->backend->vram = video->vram;
|
||||
renderer->backend->oam = &video->oam;
|
||||
|
||||
mVideoLoggerRendererDeinit(renderer->logger);
|
||||
}
|
||||
|
||||
void GBAVideoProxyRendererInit(struct GBAVideoRenderer* renderer) {
|
||||
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
|
||||
|
||||
_init(proxyRenderer);
|
||||
|
||||
proxyRenderer->backend->init(proxyRenderer->backend);
|
||||
}
|
||||
|
||||
void GBAVideoProxyRendererReset(struct GBAVideoRenderer* renderer) {
|
||||
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
|
||||
|
||||
_reset(proxyRenderer);
|
||||
|
||||
proxyRenderer->backend->reset(proxyRenderer->backend);
|
||||
}
|
||||
|
||||
void GBAVideoProxyRendererDeinit(struct GBAVideoRenderer* renderer) {
|
||||
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
|
||||
|
||||
proxyRenderer->backend->deinit(proxyRenderer->backend);
|
||||
|
||||
mVideoLoggerRendererDeinit(proxyRenderer->logger);
|
||||
}
|
||||
|
||||
static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* item) {
|
||||
struct GBAVideoProxyRenderer* proxyRenderer = logger->context;
|
||||
switch (item->type) {
|
||||
case DIRTY_REGISTER:
|
||||
proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value);
|
||||
break;
|
||||
case DIRTY_PALETTE:
|
||||
if (item->address < SIZE_PALETTE_RAM) {
|
||||
logger->palette[item->address >> 1] = item->value;
|
||||
proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value);
|
||||
}
|
||||
break;
|
||||
case DIRTY_OAM:
|
||||
if (item->address < SIZE_PALETTE_RAM) {
|
||||
logger->oam[item->address] = item->value;
|
||||
proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address);
|
||||
}
|
||||
break;
|
||||
case DIRTY_VRAM:
|
||||
if (item->address <= SIZE_VRAM - 0x1000) {
|
||||
logger->readData(logger, &logger->vram[item->address >> 1], 0x1000, true);
|
||||
proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address);
|
||||
}
|
||||
break;
|
||||
case DIRTY_SCANLINE:
|
||||
if (item->address < VIDEO_VERTICAL_PIXELS) {
|
||||
proxyRenderer->backend->drawScanline(proxyRenderer->backend, item->address);
|
||||
}
|
||||
break;
|
||||
case DIRTY_FRAME:
|
||||
proxyRenderer->backend->finishFrame(proxyRenderer->backend);
|
||||
break;
|
||||
case DIRTY_FLUSH:
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address) {
|
||||
struct GBAVideoProxyRenderer* proxyRenderer = logger->context;
|
||||
return &proxyRenderer->d.vram[address >> 1];
|
||||
}
|
||||
|
||||
uint16_t GBAVideoProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
|
||||
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
|
||||
switch (address) {
|
||||
case REG_BG0CNT:
|
||||
case REG_BG1CNT:
|
||||
case REG_BG2CNT:
|
||||
case REG_BG3CNT:
|
||||
value &= 0xFFCF;
|
||||
break;
|
||||
case REG_BG0HOFS:
|
||||
case REG_BG0VOFS:
|
||||
case REG_BG1HOFS:
|
||||
case REG_BG1VOFS:
|
||||
case REG_BG2HOFS:
|
||||
case REG_BG2VOFS:
|
||||
case REG_BG3HOFS:
|
||||
case REG_BG3VOFS:
|
||||
value &= 0x01FF;
|
||||
break;
|
||||
}
|
||||
if (address > REG_BLDY) {
|
||||
return value;
|
||||
}
|
||||
|
||||
mVideoLoggerRendererWriteVideoRegister(proxyRenderer->logger, address, value);
|
||||
if (!proxyRenderer->logger->block) {
|
||||
proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, address, value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void GBAVideoProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
|
||||
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
|
||||
mVideoLoggerRendererWriteVRAM(proxyRenderer->logger, address);
|
||||
if (!proxyRenderer->logger->block) {
|
||||
proxyRenderer->backend->writeVRAM(proxyRenderer->backend, address);
|
||||
}
|
||||
if (renderer->cache) {
|
||||
mTileCacheWriteVRAM(renderer->cache, address);
|
||||
}
|
||||
}
|
||||
|
||||
void GBAVideoProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
|
||||
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
|
||||
mVideoLoggerRendererWritePalette(proxyRenderer->logger, address, value);
|
||||
if (!proxyRenderer->logger->block) {
|
||||
proxyRenderer->backend->writePalette(proxyRenderer->backend, address, value);
|
||||
}
|
||||
if (renderer->cache) {
|
||||
mTileCacheWritePalette(renderer->cache, address);
|
||||
}
|
||||
}
|
||||
|
||||
void GBAVideoProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
|
||||
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
|
||||
if (!proxyRenderer->logger->block) {
|
||||
proxyRenderer->backend->writeOAM(proxyRenderer->backend, oam);
|
||||
}
|
||||
mVideoLoggerRendererWriteOAM(proxyRenderer->logger, oam, proxyRenderer->d.oam->raw[oam]);
|
||||
}
|
||||
|
||||
void GBAVideoProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
|
||||
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
|
||||
if (!proxyRenderer->logger->block) {
|
||||
proxyRenderer->backend->drawScanline(proxyRenderer->backend, y);
|
||||
}
|
||||
mVideoLoggerRendererDrawScanline(proxyRenderer->logger, y);
|
||||
if (proxyRenderer->logger->block && proxyRenderer->logger->wake) {
|
||||
proxyRenderer->logger->wake(proxyRenderer->logger, y);
|
||||
}
|
||||
}
|
||||
|
||||
void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
|
||||
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
|
||||
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
|
||||
proxyRenderer->logger->lock(proxyRenderer->logger);
|
||||
proxyRenderer->logger->wait(proxyRenderer->logger);
|
||||
}
|
||||
proxyRenderer->backend->finishFrame(proxyRenderer->backend);
|
||||
mVideoLoggerRendererFinishFrame(proxyRenderer->logger);
|
||||
mVideoLoggerRendererFlush(proxyRenderer->logger);
|
||||
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
|
||||
proxyRenderer->logger->unlock(proxyRenderer->logger);
|
||||
}
|
||||
}
|
||||
|
||||
static void GBAVideoProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) {
|
||||
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
|
||||
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
|
||||
proxyRenderer->logger->lock(proxyRenderer->logger);
|
||||
// Insert an extra item into the queue to make sure it gets flushed
|
||||
mVideoLoggerRendererFlush(proxyRenderer->logger);
|
||||
proxyRenderer->logger->wait(proxyRenderer->logger);
|
||||
}
|
||||
proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels);
|
||||
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
|
||||
proxyRenderer->logger->unlock(proxyRenderer->logger);
|
||||
}
|
||||
}
|
||||
|
||||
static void GBAVideoProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) {
|
||||
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
|
||||
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
|
||||
proxyRenderer->logger->lock(proxyRenderer->logger);
|
||||
// Insert an extra item into the queue to make sure it gets flushed
|
||||
mVideoLoggerRendererFlush(proxyRenderer->logger);
|
||||
proxyRenderer->logger->wait(proxyRenderer->logger);
|
||||
}
|
||||
proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels);
|
||||
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
|
||||
proxyRenderer->logger->unlock(proxyRenderer->logger);
|
||||
}
|
||||
}
|
|
@ -76,6 +76,8 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
|
|||
gba->sio.p = gba;
|
||||
GBASIOInit(&gba->sio);
|
||||
|
||||
GBAHardwareInit(&gba->memory.hw, NULL);
|
||||
|
||||
gba->springIRQ = 0;
|
||||
gba->keySource = 0;
|
||||
gba->rotationSource = 0;
|
||||
|
@ -89,8 +91,6 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
|
|||
|
||||
gba->stream = NULL;
|
||||
gba->keyCallback = NULL;
|
||||
gba->stopCallback = NULL;
|
||||
gba->stopCallback = NULL;
|
||||
mCoreCallbacksListInit(&gba->coreCallbacks, 0);
|
||||
|
||||
gba->biosChecksum = GBAChecksum(gba->memory.bios, SIZE_BIOS);
|
||||
|
@ -270,6 +270,7 @@ static void GBAProcessEvents(struct ARMCore* cpu) {
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef USE_DEBUGGERS
|
||||
void GBAAttachDebugger(struct GBA* gba, struct mDebugger* debugger) {
|
||||
gba->debugger = (struct ARMDebugger*) debugger->platform;
|
||||
gba->debugger->setSoftwareBreakpoint = _setSoftwareBreakpoint;
|
||||
|
@ -279,10 +280,13 @@ void GBAAttachDebugger(struct GBA* gba, struct mDebugger* debugger) {
|
|||
}
|
||||
|
||||
void GBADetachDebugger(struct GBA* gba) {
|
||||
gba->debugger = 0;
|
||||
ARMHotplugDetach(gba->cpu, CPU_COMPONENT_DEBUGGER);
|
||||
gba->cpu->components[CPU_COMPONENT_DEBUGGER] = 0;
|
||||
if (gba->debugger) {
|
||||
ARMHotplugDetach(gba->cpu, CPU_COMPONENT_DEBUGGER);
|
||||
}
|
||||
gba->cpu->components[CPU_COMPONENT_DEBUGGER] = NULL;
|
||||
gba->debugger = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool GBALoadMB(struct GBA* gba, struct VFile* vf) {
|
||||
GBAUnloadROM(gba);
|
||||
|
@ -293,14 +297,9 @@ bool GBALoadMB(struct GBA* gba, struct VFile* vf) {
|
|||
gba->pristineRomSize = SIZE_WORKING_RAM;
|
||||
}
|
||||
gba->isPristine = true;
|
||||
#ifdef _3DS
|
||||
if (gba->pristineRomSize <= romBufferSize) {
|
||||
gba->memory.wram = romBuffer;
|
||||
vf->read(vf, romBuffer, gba->pristineRomSize);
|
||||
}
|
||||
#else
|
||||
gba->memory.wram = vf->map(vf, gba->pristineRomSize, MAP_READ);
|
||||
#endif
|
||||
gba->memory.wram = anonymousMemoryMap(SIZE_WORKING_RAM);
|
||||
memset(gba->memory.wram, 0, SIZE_WORKING_RAM);
|
||||
vf->read(vf, gba->memory.wram, gba->pristineRomSize);
|
||||
if (!gba->memory.wram) {
|
||||
mLOG(GBA, WARN, "Couldn't map ROM");
|
||||
return false;
|
||||
|
@ -410,10 +409,6 @@ void GBAApplyPatch(struct GBA* gba, struct Patch* patch) {
|
|||
}
|
||||
|
||||
void GBAWriteIE(struct GBA* gba, uint16_t value) {
|
||||
if (value & (1 << IRQ_KEYPAD)) {
|
||||
mLOG(GBA, STUB, "Keypad interrupts not implemented");
|
||||
}
|
||||
|
||||
if (gba->memory.io[REG_IME >> 1] && value & gba->memory.io[REG_IF >> 1]) {
|
||||
ARMRaiseIRQ(gba->cpu);
|
||||
}
|
||||
|
@ -450,11 +445,14 @@ void GBAHalt(struct GBA* gba) {
|
|||
}
|
||||
|
||||
void GBAStop(struct GBA* gba) {
|
||||
if (!gba->stopCallback) {
|
||||
return;
|
||||
size_t c;
|
||||
for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) {
|
||||
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gba->coreCallbacks, c);
|
||||
if (callbacks->sleep) {
|
||||
callbacks->sleep(callbacks->context);
|
||||
}
|
||||
}
|
||||
gba->cpu->nextEvent = gba->cpu->cycles;
|
||||
gba->stopCallback->stop(gba->stopCallback);
|
||||
}
|
||||
|
||||
void GBADebug(struct GBA* gba, uint16_t flags) {
|
||||
|
@ -635,7 +633,7 @@ void GBABreakpoint(struct ARMCore* cpu, int immediate) {
|
|||
}
|
||||
|
||||
void GBAFrameStarted(struct GBA* gba) {
|
||||
UNUSED(gba);
|
||||
GBATestKeypadIRQ(gba);
|
||||
|
||||
size_t c;
|
||||
for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) {
|
||||
|
@ -682,6 +680,29 @@ void GBAFrameEnded(struct GBA* gba) {
|
|||
}
|
||||
}
|
||||
|
||||
void GBATestKeypadIRQ(struct GBA* gba) {
|
||||
uint16_t keycnt = gba->memory.io[REG_KEYCNT >> 1];
|
||||
if (!(keycnt & 0x4000)) {
|
||||
return;
|
||||
}
|
||||
int isAnd = keycnt & 0x8000;
|
||||
uint16_t keyInput;
|
||||
|
||||
if (!gba->keySource) {
|
||||
// TODO?
|
||||
return;
|
||||
}
|
||||
|
||||
keycnt &= 0x3FF;
|
||||
keyInput = *gba->keySource;
|
||||
|
||||
if (isAnd && keycnt == keyInput) {
|
||||
GBARaiseIRQ(gba, IRQ_KEYPAD);
|
||||
} else if (!isAnd && keycnt & keyInput) {
|
||||
GBARaiseIRQ(gba, IRQ_KEYPAD);
|
||||
}
|
||||
}
|
||||
|
||||
void GBASetBreakpoint(struct GBA* gba, struct mCPUComponent* component, uint32_t address, enum ExecutionMode mode, uint32_t* opcode) {
|
||||
size_t immediate;
|
||||
for (immediate = 0; immediate < gba->cpu->numComponents; ++immediate) {
|
||||
|
|
|
@ -77,6 +77,9 @@ void GBAHardwareClear(struct GBACartridgeHardware* hw) {
|
|||
}
|
||||
|
||||
void GBAHardwareGPIOWrite(struct GBACartridgeHardware* hw, uint32_t address, uint16_t value) {
|
||||
if (!hw->gpioBase) {
|
||||
return;
|
||||
}
|
||||
switch (address) {
|
||||
case GPIO_REG_DATA:
|
||||
hw->pinState &= ~hw->direction;
|
||||
|
|
11
src/gba/io.c
11
src/gba/io.c
|
@ -537,6 +537,11 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
|
|||
break;
|
||||
|
||||
// Interrupts and misc
|
||||
case REG_KEYCNT:
|
||||
value &= 0xC3FF;
|
||||
gba->memory.io[address >> 1] = value;
|
||||
GBATestKeypadIRQ(gba);
|
||||
return;
|
||||
case REG_WAITCNT:
|
||||
value &= 0x5FFF;
|
||||
GBAAdjustWaitstates(gba, value);
|
||||
|
@ -689,6 +694,7 @@ bool GBAIOIsReadConstant(uint32_t address) {
|
|||
case REG_TM2CNT_HI:
|
||||
case REG_TM3CNT_HI:
|
||||
case REG_KEYINPUT:
|
||||
case REG_KEYCNT:
|
||||
case REG_IE:
|
||||
return true;
|
||||
}
|
||||
|
@ -721,6 +727,9 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
|
|||
uint16_t input = 0x3FF;
|
||||
if (gba->keyCallback) {
|
||||
input = gba->keyCallback->readKeys(gba->keyCallback);
|
||||
if (gba->keySource) {
|
||||
*gba->keySource = input;
|
||||
}
|
||||
} else if (gba->keySource) {
|
||||
input = *gba->keySource;
|
||||
}
|
||||
|
@ -814,7 +823,6 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
|
|||
break;
|
||||
|
||||
case REG_SOUNDBIAS:
|
||||
case REG_KEYCNT:
|
||||
case REG_POSTFLG:
|
||||
mLOG(GBA_IO, STUB, "Stub I/O register read: %03x", address);
|
||||
break;
|
||||
|
@ -863,6 +871,7 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
|
|||
case REG_TM1CNT_HI:
|
||||
case REG_TM2CNT_HI:
|
||||
case REG_TM3CNT_HI:
|
||||
case REG_KEYCNT:
|
||||
case REG_SIOMULTI0:
|
||||
case REG_SIOMULTI1:
|
||||
case REG_SIOMULTI2:
|
||||
|
|
|
@ -102,7 +102,7 @@ void GBAMemoryDeinit(struct GBA* gba) {
|
|||
}
|
||||
|
||||
void GBAMemoryReset(struct GBA* gba) {
|
||||
if (gba->memory.rom || gba->memory.fullBios) {
|
||||
if (gba->memory.rom || gba->memory.fullBios || !gba->memory.wram) {
|
||||
// Not multiboot
|
||||
if (gba->memory.wram) {
|
||||
mappedMemoryFree(gba->memory.wram, SIZE_WORKING_RAM);
|
||||
|
@ -274,10 +274,10 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
|
|||
break;
|
||||
case REGION_VRAM:
|
||||
if (address & 0x10000) {
|
||||
cpu->memory.activeRegion = (uint32_t*) &gba->video.renderer->vram[0x8000];
|
||||
cpu->memory.activeRegion = (uint32_t*) &gba->video.vram[0x8000];
|
||||
cpu->memory.activeMask = 0x00007FFF;
|
||||
} else {
|
||||
cpu->memory.activeRegion = (uint32_t*) gba->video.renderer->vram;
|
||||
cpu->memory.activeRegion = (uint32_t*) gba->video.vram;
|
||||
cpu->memory.activeMask = 0x0000FFFF;
|
||||
}
|
||||
break;
|
||||
|
@ -377,9 +377,9 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
|
|||
|
||||
#define LOAD_VRAM \
|
||||
if ((address & 0x0001FFFF) < SIZE_VRAM) { \
|
||||
LOAD_32(value, address & 0x0001FFFC, gba->video.renderer->vram); \
|
||||
LOAD_32(value, address & 0x0001FFFC, gba->video.vram); \
|
||||
} else { \
|
||||
LOAD_32(value, address & 0x00017FFC, gba->video.renderer->vram); \
|
||||
LOAD_32(value, address & 0x00017FFC, gba->video.vram); \
|
||||
} \
|
||||
wait += waitstatesRegion[REGION_VRAM];
|
||||
|
||||
|
@ -507,9 +507,9 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
|||
break;
|
||||
case REGION_VRAM:
|
||||
if ((address & 0x0001FFFF) < SIZE_VRAM) {
|
||||
LOAD_16(value, address & 0x0001FFFE, gba->video.renderer->vram);
|
||||
LOAD_16(value, address & 0x0001FFFE, gba->video.vram);
|
||||
} else {
|
||||
LOAD_16(value, address & 0x00017FFE, gba->video.renderer->vram);
|
||||
LOAD_16(value, address & 0x00017FFE, gba->video.vram);
|
||||
}
|
||||
break;
|
||||
case REGION_OAM:
|
||||
|
@ -608,9 +608,9 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
|||
break;
|
||||
case REGION_VRAM:
|
||||
if ((address & 0x0001FFFF) < SIZE_VRAM) {
|
||||
value = ((uint8_t*) gba->video.renderer->vram)[address & 0x0001FFFF];
|
||||
value = ((uint8_t*) gba->video.vram)[address & 0x0001FFFF];
|
||||
} else {
|
||||
value = ((uint8_t*) gba->video.renderer->vram)[address & 0x00017FFF];
|
||||
value = ((uint8_t*) gba->video.vram)[address & 0x00017FFF];
|
||||
}
|
||||
break;
|
||||
case REGION_OAM:
|
||||
|
@ -691,11 +691,11 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
|||
|
||||
#define STORE_VRAM \
|
||||
if ((address & 0x0001FFFF) < SIZE_VRAM) { \
|
||||
STORE_32(value, address & 0x0001FFFC, gba->video.renderer->vram); \
|
||||
STORE_32(value, address & 0x0001FFFC, gba->video.vram); \
|
||||
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC) + 2); \
|
||||
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC)); \
|
||||
} else { \
|
||||
STORE_32(value, address & 0x00017FFC, gba->video.renderer->vram); \
|
||||
STORE_32(value, address & 0x00017FFC, gba->video.vram); \
|
||||
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC) + 2); \
|
||||
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC)); \
|
||||
} \
|
||||
|
@ -796,10 +796,10 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
|
|||
break;
|
||||
case REGION_VRAM:
|
||||
if ((address & 0x0001FFFF) < SIZE_VRAM) {
|
||||
STORE_16(value, address & 0x0001FFFE, gba->video.renderer->vram);
|
||||
STORE_16(value, address & 0x0001FFFE, gba->video.vram);
|
||||
gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE);
|
||||
} else {
|
||||
STORE_16(value, address & 0x00017FFE, gba->video.renderer->vram);
|
||||
STORE_16(value, address & 0x00017FFE, gba->video.vram);
|
||||
gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x00017FFE);
|
||||
}
|
||||
break;
|
||||
|
@ -1052,11 +1052,11 @@ void GBAPatch32(struct ARMCore* cpu, uint32_t address, int32_t value, int32_t* o
|
|||
break;
|
||||
case REGION_VRAM:
|
||||
if ((address & 0x0001FFFF) < SIZE_VRAM) {
|
||||
LOAD_32(oldValue, address & 0x0001FFFC, gba->video.renderer->vram);
|
||||
STORE_32(value, address & 0x0001FFFC, gba->video.renderer->vram);
|
||||
LOAD_32(oldValue, address & 0x0001FFFC, gba->video.vram);
|
||||
STORE_32(value, address & 0x0001FFFC, gba->video.vram);
|
||||
} else {
|
||||
LOAD_32(oldValue, address & 0x00017FFC, gba->video.renderer->vram);
|
||||
STORE_32(value, address & 0x00017FFC, gba->video.renderer->vram);
|
||||
LOAD_32(oldValue, address & 0x00017FFC, gba->video.vram);
|
||||
STORE_32(value, address & 0x00017FFC, gba->video.vram);
|
||||
}
|
||||
break;
|
||||
case REGION_OAM:
|
||||
|
@ -1121,11 +1121,11 @@ void GBAPatch16(struct ARMCore* cpu, uint32_t address, int16_t value, int16_t* o
|
|||
break;
|
||||
case REGION_VRAM:
|
||||
if ((address & 0x0001FFFF) < SIZE_VRAM) {
|
||||
LOAD_16(oldValue, address & 0x0001FFFE, gba->video.renderer->vram);
|
||||
STORE_16(value, address & 0x0001FFFE, gba->video.renderer->vram);
|
||||
LOAD_16(oldValue, address & 0x0001FFFE, gba->video.vram);
|
||||
STORE_16(value, address & 0x0001FFFE, gba->video.vram);
|
||||
} else {
|
||||
LOAD_16(oldValue, address & 0x00017FFE, gba->video.renderer->vram);
|
||||
STORE_16(value, address & 0x00017FFE, gba->video.renderer->vram);
|
||||
LOAD_16(oldValue, address & 0x00017FFE, gba->video.vram);
|
||||
STORE_16(value, address & 0x00017FFE, gba->video.vram);
|
||||
}
|
||||
break;
|
||||
case REGION_OAM:
|
||||
|
@ -1552,6 +1552,9 @@ void _pristineCow(struct GBA* gba) {
|
|||
void* newRom = anonymousMemoryMap(SIZE_CART0);
|
||||
memcpy(newRom, gba->memory.rom, gba->memory.romSize);
|
||||
memset(((uint8_t*) newRom) + gba->memory.romSize, 0xFF, SIZE_CART0 - gba->memory.romSize);
|
||||
if (gba->cpu->memory.activeRegion == gba->memory.rom) {
|
||||
gba->cpu->memory.activeRegion = newRom;
|
||||
}
|
||||
if (gba->romVf) {
|
||||
#ifndef _3DS
|
||||
gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->memory.romSize);
|
||||
|
|
|
@ -1,390 +0,0 @@
|
|||
/* Copyright (c) 2013-2015 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include <mgba/internal/gba/renderers/thread-proxy.h>
|
||||
|
||||
#include <mgba/core/tile-cache.h>
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
#include <mgba/internal/gba/io.h>
|
||||
|
||||
#include <mgba-util/memory.h>
|
||||
|
||||
#ifndef DISABLE_THREADING
|
||||
|
||||
enum GBAVideoDirtyType {
|
||||
DIRTY_DUMMY = 0,
|
||||
DIRTY_REGISTER,
|
||||
DIRTY_OAM,
|
||||
DIRTY_PALETTE,
|
||||
DIRTY_VRAM,
|
||||
DIRTY_SCANLINE,
|
||||
DIRTY_FLUSH
|
||||
};
|
||||
|
||||
struct GBAVideoDirtyInfo {
|
||||
enum GBAVideoDirtyType type;
|
||||
uint32_t address;
|
||||
uint16_t value;
|
||||
uint32_t padding;
|
||||
};
|
||||
|
||||
static void GBAVideoThreadProxyRendererInit(struct GBAVideoRenderer* renderer);
|
||||
static void GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer);
|
||||
static void GBAVideoThreadProxyRendererDeinit(struct GBAVideoRenderer* renderer);
|
||||
static uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
|
||||
static void GBAVideoThreadProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address);
|
||||
static void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
|
||||
static void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
|
||||
static void GBAVideoThreadProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
|
||||
static void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer);
|
||||
static void GBAVideoThreadProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels);
|
||||
static void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels);
|
||||
|
||||
static THREAD_ENTRY _proxyThread(void* renderer);
|
||||
|
||||
void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend) {
|
||||
renderer->d.init = GBAVideoThreadProxyRendererInit;
|
||||
renderer->d.reset = GBAVideoThreadProxyRendererReset;
|
||||
renderer->d.deinit = GBAVideoThreadProxyRendererDeinit;
|
||||
renderer->d.writeVideoRegister = GBAVideoThreadProxyRendererWriteVideoRegister;
|
||||
renderer->d.writeVRAM = GBAVideoThreadProxyRendererWriteVRAM;
|
||||
renderer->d.writeOAM = GBAVideoThreadProxyRendererWriteOAM;
|
||||
renderer->d.writePalette = GBAVideoThreadProxyRendererWritePalette;
|
||||
renderer->d.drawScanline = GBAVideoThreadProxyRendererDrawScanline;
|
||||
renderer->d.finishFrame = GBAVideoThreadProxyRendererFinishFrame;
|
||||
renderer->d.getPixels = GBAVideoThreadProxyRendererGetPixels;
|
||||
renderer->d.putPixels = GBAVideoThreadProxyRendererPutPixels;
|
||||
|
||||
renderer->d.disableBG[0] = false;
|
||||
renderer->d.disableBG[1] = false;
|
||||
renderer->d.disableBG[2] = false;
|
||||
renderer->d.disableBG[3] = false;
|
||||
renderer->d.disableOBJ = false;
|
||||
|
||||
renderer->backend = backend;
|
||||
}
|
||||
|
||||
void GBAVideoThreadProxyRendererInit(struct GBAVideoRenderer* renderer) {
|
||||
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
|
||||
ConditionInit(&proxyRenderer->fromThreadCond);
|
||||
ConditionInit(&proxyRenderer->toThreadCond);
|
||||
MutexInit(&proxyRenderer->mutex);
|
||||
RingFIFOInit(&proxyRenderer->dirtyQueue, 0x40000);
|
||||
|
||||
proxyRenderer->vramProxy = anonymousMemoryMap(SIZE_VRAM);
|
||||
proxyRenderer->backend->palette = proxyRenderer->paletteProxy;
|
||||
proxyRenderer->backend->vram = proxyRenderer->vramProxy;
|
||||
proxyRenderer->backend->oam = &proxyRenderer->oamProxy;
|
||||
proxyRenderer->backend->cache = NULL;
|
||||
|
||||
proxyRenderer->backend->init(proxyRenderer->backend);
|
||||
|
||||
proxyRenderer->vramDirtyBitmap = 0;
|
||||
proxyRenderer->threadState = PROXY_THREAD_IDLE;
|
||||
ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
|
||||
}
|
||||
|
||||
void GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer) {
|
||||
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
|
||||
MutexLock(&proxyRenderer->mutex);
|
||||
while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
|
||||
ConditionWake(&proxyRenderer->toThreadCond);
|
||||
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
|
||||
}
|
||||
memcpy(&proxyRenderer->oamProxy.raw, &renderer->oam->raw, SIZE_OAM);
|
||||
memcpy(proxyRenderer->paletteProxy, renderer->palette, SIZE_PALETTE_RAM);
|
||||
memcpy(proxyRenderer->vramProxy, renderer->vram, SIZE_VRAM);
|
||||
proxyRenderer->backend->reset(proxyRenderer->backend);
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
}
|
||||
|
||||
void GBAVideoThreadProxyRendererDeinit(struct GBAVideoRenderer* renderer) {
|
||||
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
|
||||
bool waiting = false;
|
||||
MutexLock(&proxyRenderer->mutex);
|
||||
while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
|
||||
ConditionWake(&proxyRenderer->toThreadCond);
|
||||
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
|
||||
}
|
||||
if (proxyRenderer->threadState == PROXY_THREAD_IDLE) {
|
||||
proxyRenderer->threadState = PROXY_THREAD_STOPPED;
|
||||
ConditionWake(&proxyRenderer->toThreadCond);
|
||||
waiting = true;
|
||||
}
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
if (waiting) {
|
||||
ThreadJoin(proxyRenderer->thread);
|
||||
}
|
||||
ConditionDeinit(&proxyRenderer->fromThreadCond);
|
||||
ConditionDeinit(&proxyRenderer->toThreadCond);
|
||||
MutexDeinit(&proxyRenderer->mutex);
|
||||
proxyRenderer->backend->deinit(proxyRenderer->backend);
|
||||
|
||||
mappedMemoryFree(proxyRenderer->vramProxy, SIZE_VRAM);
|
||||
}
|
||||
|
||||
void _proxyThreadRecover(struct GBAVideoThreadProxyRenderer* proxyRenderer) {
|
||||
MutexLock(&proxyRenderer->mutex);
|
||||
if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
return;
|
||||
}
|
||||
RingFIFOClear(&proxyRenderer->dirtyQueue);
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
ThreadJoin(proxyRenderer->thread);
|
||||
proxyRenderer->threadState = PROXY_THREAD_IDLE;
|
||||
ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
|
||||
}
|
||||
|
||||
static bool _writeData(struct GBAVideoThreadProxyRenderer* proxyRenderer, void* data, size_t length) {
|
||||
while (!RingFIFOWrite(&proxyRenderer->dirtyQueue, data, length)) {
|
||||
mLOG(GBA_VIDEO, DEBUG, "Can't write %"PRIz"u bytes. Proxy thread asleep?", length);
|
||||
MutexLock(&proxyRenderer->mutex);
|
||||
if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
|
||||
mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!");
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
return false;
|
||||
}
|
||||
ConditionWake(&proxyRenderer->toThreadCond);
|
||||
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
|
||||
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
|
||||
switch (address) {
|
||||
case REG_BG0CNT:
|
||||
case REG_BG1CNT:
|
||||
case REG_BG2CNT:
|
||||
case REG_BG3CNT:
|
||||
value &= 0xFFCF;
|
||||
break;
|
||||
case REG_BG0HOFS:
|
||||
case REG_BG0VOFS:
|
||||
case REG_BG1HOFS:
|
||||
case REG_BG1VOFS:
|
||||
case REG_BG2HOFS:
|
||||
case REG_BG2VOFS:
|
||||
case REG_BG3HOFS:
|
||||
case REG_BG3VOFS:
|
||||
value &= 0x01FF;
|
||||
break;
|
||||
}
|
||||
if (address > REG_BLDY) {
|
||||
return value;
|
||||
}
|
||||
|
||||
struct GBAVideoDirtyInfo dirty = {
|
||||
DIRTY_REGISTER,
|
||||
address,
|
||||
value,
|
||||
0xDEADBEEF,
|
||||
};
|
||||
_writeData(proxyRenderer, &dirty, sizeof(dirty));
|
||||
return value;
|
||||
}
|
||||
|
||||
void GBAVideoThreadProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
|
||||
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
|
||||
int bit = 1 << (address >> 12);
|
||||
if (proxyRenderer->vramDirtyBitmap & bit) {
|
||||
return;
|
||||
}
|
||||
proxyRenderer->vramDirtyBitmap |= bit;
|
||||
if (renderer->cache) {
|
||||
mTileCacheWriteVRAM(renderer->cache, address);
|
||||
}
|
||||
}
|
||||
|
||||
void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
|
||||
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
|
||||
struct GBAVideoDirtyInfo dirty = {
|
||||
DIRTY_PALETTE,
|
||||
address,
|
||||
value,
|
||||
0xDEADBEEF,
|
||||
};
|
||||
_writeData(proxyRenderer, &dirty, sizeof(dirty));
|
||||
if (renderer->cache) {
|
||||
mTileCacheWritePalette(renderer->cache, address);
|
||||
}
|
||||
}
|
||||
|
||||
void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
|
||||
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
|
||||
struct GBAVideoDirtyInfo dirty = {
|
||||
DIRTY_OAM,
|
||||
oam,
|
||||
proxyRenderer->d.oam->raw[oam],
|
||||
0xDEADBEEF,
|
||||
};
|
||||
_writeData(proxyRenderer, &dirty, sizeof(dirty));
|
||||
}
|
||||
|
||||
void GBAVideoThreadProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
|
||||
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
|
||||
if (proxyRenderer->vramDirtyBitmap) {
|
||||
int bitmap = proxyRenderer->vramDirtyBitmap;
|
||||
proxyRenderer->vramDirtyBitmap = 0;
|
||||
int j;
|
||||
for (j = 0; j < 24; ++j) {
|
||||
if (!(bitmap & (1 << j))) {
|
||||
continue;
|
||||
}
|
||||
struct GBAVideoDirtyInfo dirty = {
|
||||
DIRTY_VRAM,
|
||||
j * 0x1000,
|
||||
0xABCD,
|
||||
0xDEADBEEF,
|
||||
};
|
||||
_writeData(proxyRenderer, &dirty, sizeof(dirty));
|
||||
_writeData(proxyRenderer, &proxyRenderer->d.vram[j * 0x800], 0x1000);
|
||||
}
|
||||
}
|
||||
struct GBAVideoDirtyInfo dirty = {
|
||||
DIRTY_SCANLINE,
|
||||
y,
|
||||
0,
|
||||
0xDEADBEEF,
|
||||
};
|
||||
_writeData(proxyRenderer, &dirty, sizeof(dirty));
|
||||
if ((y & 15) == 15) {
|
||||
ConditionWake(&proxyRenderer->toThreadCond);
|
||||
}
|
||||
}
|
||||
|
||||
void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
|
||||
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
|
||||
if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
|
||||
mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!");
|
||||
_proxyThreadRecover(proxyRenderer);
|
||||
return;
|
||||
}
|
||||
MutexLock(&proxyRenderer->mutex);
|
||||
// Insert an extra item into the queue to make sure it gets flushed
|
||||
struct GBAVideoDirtyInfo dirty = {
|
||||
DIRTY_FLUSH,
|
||||
0,
|
||||
0,
|
||||
0xDEADBEEF,
|
||||
};
|
||||
_writeData(proxyRenderer, &dirty, sizeof(dirty));
|
||||
do {
|
||||
ConditionWake(&proxyRenderer->toThreadCond);
|
||||
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
|
||||
} while (proxyRenderer->threadState == PROXY_THREAD_BUSY);
|
||||
proxyRenderer->backend->finishFrame(proxyRenderer->backend);
|
||||
proxyRenderer->vramDirtyBitmap = 0;
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
}
|
||||
|
||||
static void GBAVideoThreadProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) {
|
||||
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
|
||||
MutexLock(&proxyRenderer->mutex);
|
||||
// Insert an extra item into the queue to make sure it gets flushed
|
||||
struct GBAVideoDirtyInfo dirty = {
|
||||
DIRTY_FLUSH,
|
||||
0,
|
||||
0,
|
||||
0xDEADBEEF,
|
||||
};
|
||||
_writeData(proxyRenderer, &dirty, sizeof(dirty));
|
||||
while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
|
||||
ConditionWake(&proxyRenderer->toThreadCond);
|
||||
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
|
||||
}
|
||||
proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels);
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
}
|
||||
|
||||
static void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) {
|
||||
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
|
||||
MutexLock(&proxyRenderer->mutex);
|
||||
// Insert an extra item into the queue to make sure it gets flushed
|
||||
struct GBAVideoDirtyInfo dirty = {
|
||||
DIRTY_FLUSH,
|
||||
0,
|
||||
0,
|
||||
0xDEADBEEF,
|
||||
};
|
||||
_writeData(proxyRenderer, &dirty, sizeof(dirty));
|
||||
while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
|
||||
ConditionWake(&proxyRenderer->toThreadCond);
|
||||
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
|
||||
}
|
||||
proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels);
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
}
|
||||
|
||||
static THREAD_ENTRY _proxyThread(void* renderer) {
|
||||
struct GBAVideoThreadProxyRenderer* proxyRenderer = renderer;
|
||||
ThreadSetName("Proxy Renderer Thread");
|
||||
|
||||
MutexLock(&proxyRenderer->mutex);
|
||||
struct GBAVideoDirtyInfo item = {0};
|
||||
while (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
|
||||
ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
|
||||
if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
|
||||
break;
|
||||
}
|
||||
if (RingFIFORead(&proxyRenderer->dirtyQueue, &item, sizeof(item))) {
|
||||
proxyRenderer->threadState = PROXY_THREAD_BUSY;
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
do {
|
||||
switch (item.type) {
|
||||
case DIRTY_REGISTER:
|
||||
proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item.address, item.value);
|
||||
break;
|
||||
case DIRTY_PALETTE:
|
||||
proxyRenderer->paletteProxy[item.address >> 1] = item.value;
|
||||
proxyRenderer->backend->writePalette(proxyRenderer->backend, item.address, item.value);
|
||||
break;
|
||||
case DIRTY_OAM:
|
||||
proxyRenderer->oamProxy.raw[item.address] = item.value;
|
||||
proxyRenderer->backend->writeOAM(proxyRenderer->backend, item.address);
|
||||
break;
|
||||
case DIRTY_VRAM:
|
||||
while (!RingFIFORead(&proxyRenderer->dirtyQueue, &proxyRenderer->vramProxy[item.address >> 1], 0x1000)) {
|
||||
mLOG(GBA_VIDEO, DEBUG, "Proxy thread can't read VRAM. CPU thread asleep?");
|
||||
MutexLock(&proxyRenderer->mutex);
|
||||
ConditionWake(&proxyRenderer->fromThreadCond);
|
||||
ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
}
|
||||
proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item.address);
|
||||
break;
|
||||
case DIRTY_SCANLINE:
|
||||
proxyRenderer->backend->drawScanline(proxyRenderer->backend, item.address);
|
||||
break;
|
||||
case DIRTY_FLUSH:
|
||||
MutexLock(&proxyRenderer->mutex);
|
||||
goto out;
|
||||
default:
|
||||
// FIFO was corrupted
|
||||
MutexLock(&proxyRenderer->mutex);
|
||||
proxyRenderer->threadState = PROXY_THREAD_STOPPED;
|
||||
mLOG(GBA_VIDEO, ERROR, "Proxy thread queue got corrupted!");
|
||||
goto out;
|
||||
}
|
||||
} while (proxyRenderer->threadState == PROXY_THREAD_BUSY && RingFIFORead(&proxyRenderer->dirtyQueue, &item, sizeof(item)));
|
||||
MutexLock(&proxyRenderer->mutex);
|
||||
}
|
||||
out:
|
||||
ConditionWake(&proxyRenderer->fromThreadCond);
|
||||
if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
|
||||
proxyRenderer->threadState = PROXY_THREAD_IDLE;
|
||||
}
|
||||
}
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
|
||||
#ifdef _3DS
|
||||
svcExitThread();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -807,10 +807,12 @@ static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
|
|||
}
|
||||
}
|
||||
}
|
||||
renderer->bg[2].sx += renderer->bg[2].dmx;
|
||||
renderer->bg[2].sy += renderer->bg[2].dmy;
|
||||
renderer->bg[3].sx += renderer->bg[3].dmx;
|
||||
renderer->bg[3].sy += renderer->bg[3].dmy;
|
||||
if (GBARegisterDISPCNTGetMode(renderer->dispcnt) != 0) {
|
||||
renderer->bg[2].sx += renderer->bg[2].dmx;
|
||||
renderer->bg[2].sy += renderer->bg[2].dmy;
|
||||
renderer->bg[3].sx += renderer->bg[3].dmx;
|
||||
renderer->bg[3].sy += renderer->bg[3].dmy;
|
||||
}
|
||||
}
|
||||
|
||||
static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {
|
||||
|
|
|
@ -206,11 +206,3 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct GBASerializedState* GBAAllocateState(void) {
|
||||
return anonymousMemoryMap(sizeof(struct GBASerializedState));
|
||||
}
|
||||
|
||||
void GBADeallocateState(struct GBASerializedState* state) {
|
||||
mappedMemoryFree(state, sizeof(struct GBASerializedState));
|
||||
}
|
||||
|
|
|
@ -295,7 +295,7 @@ static void GBAVideoDummyRendererPutPixels(struct GBAVideoRenderer* renderer, si
|
|||
}
|
||||
|
||||
void GBAVideoSerialize(const struct GBAVideo* video, struct GBASerializedState* state) {
|
||||
memcpy(state->vram, video->renderer->vram, SIZE_VRAM);
|
||||
memcpy(state->vram, video->vram, SIZE_VRAM);
|
||||
memcpy(state->oam, video->oam.raw, SIZE_OAM);
|
||||
memcpy(state->pram, video->palette, SIZE_PALETTE_RAM);
|
||||
STORE_32(video->event.when - mTimingCurrentTime(&video->p->timing), 0, &state->video.nextEvent);
|
||||
|
@ -303,7 +303,7 @@ void GBAVideoSerialize(const struct GBAVideo* video, struct GBASerializedState*
|
|||
}
|
||||
|
||||
void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState* state) {
|
||||
memcpy(video->renderer->vram, state->vram, SIZE_VRAM);
|
||||
memcpy(video->vram, state->vram, SIZE_VRAM);
|
||||
uint16_t value;
|
||||
int i;
|
||||
for (i = 0; i < SIZE_OAM; i += 2) {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <mgba/core/core.h>
|
||||
#include <mgba/internal/debugger/cli-debugger.h>
|
||||
#include <mgba/internal/lr35902/decoder.h>
|
||||
#include <mgba/internal/lr35902/debugger/debugger.h>
|
||||
#include <mgba/internal/lr35902/lr35902.h>
|
||||
|
||||
static void _printStatus(struct CLIDebuggerSystem*);
|
||||
|
@ -31,11 +32,13 @@ static void _disassemble(struct CLIDebuggerSystem* debugger, struct CLIDebugVect
|
|||
struct LR35902Core* cpu = debugger->p->d.core->cpu;
|
||||
|
||||
uint16_t address;
|
||||
int segment = -1;
|
||||
size_t size;
|
||||
if (!dv || dv->type != CLIDV_INT_TYPE) {
|
||||
address = cpu->pc;
|
||||
} else {
|
||||
address = dv->intValue;
|
||||
segment = dv->segmentValue;
|
||||
dv = dv->next;
|
||||
}
|
||||
|
||||
|
@ -48,7 +51,7 @@ static void _disassemble(struct CLIDebuggerSystem* debugger, struct CLIDebugVect
|
|||
|
||||
size_t i;
|
||||
for (i = 0; i < size; ++i) {
|
||||
address = _printLine(debugger->p, address, -1);
|
||||
address = _printLine(debugger->p, address, segment);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,7 +61,7 @@ static inline uint16_t _printLine(struct CLIDebugger* debugger, uint16_t address
|
|||
char disassembly[48];
|
||||
char* disPtr = disassembly;
|
||||
if (segment >= 0) {
|
||||
be->printf(be, "%02X: ", segment);
|
||||
be->printf(be, "%02X:", segment);
|
||||
}
|
||||
be->printf(be, "%04X: ", address);
|
||||
uint8_t instruction;
|
||||
|
@ -84,8 +87,17 @@ static void _printStatus(struct CLIDebuggerSystem* debugger) {
|
|||
be->printf(be, "D: %02X E: %02X (DE: %04X)\n", cpu->d, cpu->e, cpu->de);
|
||||
be->printf(be, "H: %02X L: %02X (HL: %04X)\n", cpu->h, cpu->l, cpu->hl);
|
||||
be->printf(be, "PC: %04X SP: %04X\n", cpu->pc, cpu->sp);
|
||||
|
||||
struct LR35902Debugger* platDebugger = (struct LR35902Debugger*) debugger->p->d.platform;
|
||||
size_t i;
|
||||
for (i = 0; platDebugger->segments[i].name; ++i) {
|
||||
be->printf(be, "%s%s: %02X", i ? " " : "", platDebugger->segments[i].name, cpu->memory.currentSegment(cpu, platDebugger->segments[i].start));
|
||||
}
|
||||
if (i) {
|
||||
be->printf(be, "\n");
|
||||
}
|
||||
_printFlags(be, cpu->f);
|
||||
_printLine(debugger->p, cpu->pc, -1);
|
||||
_printLine(debugger->p, cpu->pc, cpu->memory.currentSegment(cpu, cpu->pc));
|
||||
}
|
||||
|
||||
static uint32_t _lookupPlatformIdentifier(struct CLIDebuggerSystem* debugger, const char* name, struct CLIDebugVector* dv) {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/internal/lr35902/lr35902.h>
|
||||
#include <mgba/internal/lr35902/debugger/memory-debugger.h>
|
||||
|
||||
DEFINE_VECTOR(LR35902DebugBreakpointList, struct LR35902DebugBreakpoint);
|
||||
DEFINE_VECTOR(LR35902DebugWatchpointList, struct LR35902DebugWatchpoint);
|
||||
|
@ -27,7 +28,9 @@ static void LR35902DebuggerCheckBreakpoints(struct mDebuggerPlatform* d) {
|
|||
if (!breakpoint) {
|
||||
return;
|
||||
}
|
||||
// TODO: Segments
|
||||
if (breakpoint->segment >= 0 && debugger->cpu->memory.currentSegment(debugger->cpu, breakpoint->address) != breakpoint->segment) {
|
||||
return;
|
||||
}
|
||||
struct mDebuggerEntryInfo info = {
|
||||
.address = breakpoint->address
|
||||
};
|
||||
|
@ -39,8 +42,10 @@ static void LR35902DebuggerDeinit(struct mDebuggerPlatform* platform);
|
|||
|
||||
static void LR35902DebuggerEnter(struct mDebuggerPlatform* d, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info);
|
||||
|
||||
static void LR35902DebuggerSetBreakpoint(struct mDebuggerPlatform*, uint32_t address);
|
||||
static void LR35902DebuggerClearBreakpoint(struct mDebuggerPlatform*, uint32_t address);
|
||||
static void LR35902DebuggerSetBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
static void LR35902DebuggerClearBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
static void LR35902DebuggerSetWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type);
|
||||
static void LR35902DebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
static void LR35902DebuggerCheckBreakpoints(struct mDebuggerPlatform*);
|
||||
static bool LR35902DebuggerHasBreakpoints(struct mDebuggerPlatform*);
|
||||
|
||||
|
@ -51,8 +56,8 @@ struct mDebuggerPlatform* LR35902DebuggerPlatformCreate(void) {
|
|||
platform->deinit = LR35902DebuggerDeinit;
|
||||
platform->setBreakpoint = LR35902DebuggerSetBreakpoint;
|
||||
platform->clearBreakpoint = LR35902DebuggerClearBreakpoint;
|
||||
platform->setWatchpoint = NULL;
|
||||
platform->clearWatchpoint = NULL;
|
||||
platform->setWatchpoint = LR35902DebuggerSetWatchpoint;
|
||||
platform->clearWatchpoint = LR35902DebuggerClearWatchpoint;
|
||||
platform->checkBreakpoints = LR35902DebuggerCheckBreakpoints;
|
||||
platform->hasBreakpoints = LR35902DebuggerHasBreakpoints;
|
||||
return platform;
|
||||
|
@ -77,21 +82,26 @@ static void LR35902DebuggerEnter(struct mDebuggerPlatform* platform, enum mDebug
|
|||
struct LR35902Debugger* debugger = (struct LR35902Debugger*) platform;
|
||||
struct LR35902Core* cpu = debugger->cpu;
|
||||
cpu->nextEvent = cpu->cycles;
|
||||
|
||||
if (debugger->d.p->entered) {
|
||||
debugger->d.p->entered(debugger->d.p, reason, info);
|
||||
}
|
||||
}
|
||||
|
||||
static void LR35902DebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t address) {
|
||||
static void LR35902DebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
|
||||
struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
|
||||
struct LR35902DebugBreakpoint* breakpoint = LR35902DebugBreakpointListAppend(&debugger->breakpoints);
|
||||
breakpoint->address = address;
|
||||
breakpoint->segment = -1;
|
||||
breakpoint->segment = segment;
|
||||
}
|
||||
|
||||
static void LR35902DebuggerClearBreakpoint(struct mDebuggerPlatform* d, uint32_t address) {
|
||||
static void LR35902DebuggerClearBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
|
||||
struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
|
||||
struct LR35902DebugBreakpointList* breakpoints = &debugger->breakpoints;
|
||||
size_t i;
|
||||
for (i = 0; i < LR35902DebugBreakpointListSize(breakpoints); ++i) {
|
||||
if (LR35902DebugBreakpointListGetPointer(breakpoints, i)->address == address) {
|
||||
struct LR35902DebugBreakpoint* breakpoint = LR35902DebugBreakpointListGetPointer(breakpoints, i);
|
||||
if (breakpoint->address == address && breakpoint->segment == segment) {
|
||||
LR35902DebugBreakpointListShift(breakpoints, i, 1);
|
||||
}
|
||||
}
|
||||
|
@ -101,3 +111,29 @@ static bool LR35902DebuggerHasBreakpoints(struct mDebuggerPlatform* d) {
|
|||
struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
|
||||
return LR35902DebugBreakpointListSize(&debugger->breakpoints) || LR35902DebugWatchpointListSize(&debugger->watchpoints);
|
||||
}
|
||||
|
||||
static void LR35902DebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type) {
|
||||
struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
|
||||
if (!LR35902DebugWatchpointListSize(&debugger->watchpoints)) {
|
||||
LR35902DebuggerInstallMemoryShim(debugger);
|
||||
}
|
||||
struct LR35902DebugWatchpoint* watchpoint = LR35902DebugWatchpointListAppend(&debugger->watchpoints);
|
||||
watchpoint->address = address;
|
||||
watchpoint->type = type;
|
||||
watchpoint->segment = segment;
|
||||
}
|
||||
|
||||
static void LR35902DebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
|
||||
struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
|
||||
struct LR35902DebugWatchpointList* watchpoints = &debugger->watchpoints;
|
||||
size_t i;
|
||||
for (i = 0; i < LR35902DebugWatchpointListSize(watchpoints); ++i) {
|
||||
struct LR35902DebugWatchpoint* watchpoint = LR35902DebugWatchpointListGetPointer(watchpoints, i);
|
||||
if (watchpoint->address == address && watchpoint->segment == segment) {
|
||||
LR35902DebugWatchpointListShift(watchpoints, i, 1);
|
||||
}
|
||||
}
|
||||
if (!LR35902DebugWatchpointListSize(&debugger->watchpoints)) {
|
||||
LR35902DebuggerRemoveMemoryShim(debugger);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/* 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/lr35902/debugger/memory-debugger.h>
|
||||
|
||||
#include <mgba/internal/lr35902/debugger/debugger.h>
|
||||
|
||||
#include <mgba-util/math.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static bool _checkWatchpoints(struct LR35902Debugger* debugger, uint16_t address, struct mDebuggerEntryInfo* info, enum mWatchpointType type, uint8_t newValue);
|
||||
|
||||
#define FIND_DEBUGGER(DEBUGGER, CPU) \
|
||||
do { \
|
||||
DEBUGGER = 0; \
|
||||
size_t i; \
|
||||
for (i = 0; i < CPU->numComponents; ++i) { \
|
||||
if (CPU->components[i]->id == DEBUGGER_ID) { \
|
||||
DEBUGGER = (struct LR35902Debugger*) ((struct mDebugger*) cpu->components[i])->platform; \
|
||||
goto debuggerFound; \
|
||||
} \
|
||||
} \
|
||||
abort(); \
|
||||
debuggerFound: break; \
|
||||
} while(0)
|
||||
|
||||
#define CREATE_WATCHPOINT_SHIM(NAME, RW, RETURN, TYPES, ...) \
|
||||
static RETURN DebuggerShim_ ## NAME TYPES { \
|
||||
struct LR35902Debugger* debugger; \
|
||||
FIND_DEBUGGER(debugger, cpu); \
|
||||
struct mDebuggerEntryInfo info; \
|
||||
if (_checkWatchpoints(debugger, address, &info, WATCHPOINT_ ## RW, 0)) { \
|
||||
mDebuggerEnter(debugger->d.p, DEBUGGER_ENTER_WATCHPOINT, &info); \
|
||||
} \
|
||||
return debugger->originalMemory.NAME(cpu, __VA_ARGS__); \
|
||||
}
|
||||
|
||||
CREATE_WATCHPOINT_SHIM(load8, READ, uint8_t, (struct LR35902Core* cpu, uint16_t address), address)
|
||||
CREATE_WATCHPOINT_SHIM(store8, WRITE, void, (struct LR35902Core* cpu, uint16_t address, int8_t value), address, value)
|
||||
|
||||
static bool _checkWatchpoints(struct LR35902Debugger* debugger, uint16_t address, struct mDebuggerEntryInfo* info, enum mWatchpointType type, uint8_t newValue) {
|
||||
struct LR35902DebugWatchpoint* watchpoint;
|
||||
size_t i;
|
||||
for (i = 0; i < LR35902DebugWatchpointListSize(&debugger->watchpoints); ++i) {
|
||||
watchpoint = LR35902DebugWatchpointListGetPointer(&debugger->watchpoints, i);
|
||||
if (watchpoint->address == address && (watchpoint->segment < 0 || watchpoint->segment == debugger->originalMemory.currentSegment(debugger->cpu, address)) && watchpoint->type & type) {
|
||||
info->oldValue = debugger->originalMemory.load8(debugger->cpu, address);
|
||||
info->newValue = newValue;
|
||||
info->address = address;
|
||||
info->watchType = watchpoint->type;
|
||||
info->accessType = type;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void LR35902DebuggerInstallMemoryShim(struct LR35902Debugger* debugger) {
|
||||
debugger->originalMemory = debugger->cpu->memory;
|
||||
debugger->cpu->memory.store8 = DebuggerShim_store8;
|
||||
debugger->cpu->memory.load8 = DebuggerShim_load8;
|
||||
}
|
||||
|
||||
void LR35902DebuggerRemoveMemoryShim(struct LR35902Debugger* debugger) {
|
||||
debugger->cpu->memory.store8 = debugger->originalMemory.store8;
|
||||
debugger->cpu->memory.load8 = debugger->originalMemory.load8;
|
||||
}
|
|
@ -66,8 +66,7 @@ DEFINE_DECODER_LR35902(NOP, info->mnemonic = LR35902_MN_NOP;)
|
|||
DEFINE_LD_DECODER_LR35902_MEM(NAME, HL) \
|
||||
DEFINE_LD_DECODER_LR35902_MEM_2(NAME, HL) \
|
||||
DEFINE_DECODER_LR35902(LD ## NAME ## _, info->mnemonic = LR35902_MN_LD; \
|
||||
info->op1.reg = LR35902_REG_A; \
|
||||
info->op1.flags = LR35902_OP_FLAG_IMPLICIT; \
|
||||
info->op1.reg = LR35902_REG_ ## NAME; \
|
||||
return 1;) \
|
||||
DEFINE_LD_DECODER_LR35902_NOHL(NAME)
|
||||
|
||||
|
@ -500,7 +499,10 @@ static int _decodeOperand(struct LR35902Operand op, char* buffer, int blen) {
|
|||
strncpy(buffer, "(", blen - 1);
|
||||
ADVANCE(1);
|
||||
}
|
||||
if (op.immediate) {
|
||||
if (op.reg) {
|
||||
int written = snprintf(buffer, blen - 1, "%s", _lr35902Registers[op.reg]);
|
||||
ADVANCE(written);
|
||||
} else {
|
||||
int written = snprintf(buffer, blen - 1, "$%02X", op.immediate);
|
||||
ADVANCE(written);
|
||||
if (op.reg) {
|
||||
|
@ -508,10 +510,6 @@ static int _decodeOperand(struct LR35902Operand op, char* buffer, int blen) {
|
|||
ADVANCE(1);
|
||||
}
|
||||
}
|
||||
if (op.reg) {
|
||||
int written = snprintf(buffer, blen - 1, "%s", _lr35902Registers[op.reg]);
|
||||
ADVANCE(written);
|
||||
}
|
||||
if (op.flags & LR35902_OP_FLAG_INCREMENT) {
|
||||
strncpy(buffer, "+", blen - 1);
|
||||
ADVANCE(1);
|
||||
|
@ -546,10 +544,12 @@ int LR35902Disassemble(struct LR35902InstructionInfo* info, char* buffer, int bl
|
|||
}
|
||||
}
|
||||
|
||||
written = _decodeOperand(info->op1, buffer, blen);
|
||||
ADVANCE(written);
|
||||
if (info->op1.reg || info->op1.immediate) {
|
||||
written = _decodeOperand(info->op1, buffer, blen);
|
||||
ADVANCE(written);
|
||||
}
|
||||
|
||||
if (info->op2.reg || info->op2.immediate) {
|
||||
if (info->op2.reg || (!info->op1.immediate && info->opcodeSize > 1 && info->opcode[0] != 0xCB)) {
|
||||
if (written) {
|
||||
strncpy(buffer, ", ", blen - 1);
|
||||
ADVANCE(2);
|
||||
|
|
|
@ -158,6 +158,10 @@ void LR35902Tick(struct LR35902Core* cpu) {
|
|||
void LR35902Run(struct LR35902Core* cpu) {
|
||||
bool running = true;
|
||||
while (running || cpu->executionState != LR35902_CORE_FETCH) {
|
||||
if (cpu->cycles >= cpu->nextEvent) {
|
||||
cpu->irqh.processEvents(cpu);
|
||||
break;
|
||||
}
|
||||
_LR35902Step(cpu);
|
||||
if (cpu->cycles + 2 >= cpu->nextEvent) {
|
||||
int32_t diff = cpu->nextEvent - cpu->cycles;
|
||||
|
@ -172,9 +176,5 @@ void LR35902Run(struct LR35902Core* cpu) {
|
|||
cpu->executionState = LR35902_CORE_FETCH;
|
||||
cpu->instruction(cpu);
|
||||
++cpu->cycles;
|
||||
if (cpu->cycles >= cpu->nextEvent) {
|
||||
cpu->irqh.processEvents(cpu);
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -242,7 +242,7 @@ static void _setup(struct mGUIRunner* runner) {
|
|||
mCoreLoadForeignConfig(runner->core, &runner->config);
|
||||
}
|
||||
|
||||
runner->core->setRotation(runner->core, &rotation.d);
|
||||
runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation.d);
|
||||
if (hasSound != NO_SOUND) {
|
||||
runner->core->setAVStream(runner->core, &stream);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// This source file is placed into the public domain.
|
||||
#include <mgba/core/core.h>
|
||||
#include "feature/commandline.h"
|
||||
#include <mgba/feature/commandline.h>
|
||||
#include <mgba-util/socket.h>
|
||||
|
||||
#define DEFAULT_PORT 13721
|
||||
|
|
|
@ -398,7 +398,7 @@ bool retro_load_game(const struct retro_game_info* game) {
|
|||
blip_set_rates(core->getAudioChannel(core, 0), core->frequency(core), 32768);
|
||||
blip_set_rates(core->getAudioChannel(core, 1), core->frequency(core), 32768);
|
||||
|
||||
core->setRumble(core, &rumble);
|
||||
core->setPeripheral(core, mPERIPH_RUMBLE, &rumble);
|
||||
|
||||
savedata = anonymousMemoryMap(SIZE_CART_FLASH1M);
|
||||
struct VFile* save = VFileFromMemory(savedata, SIZE_CART_FLASH1M);
|
||||
|
@ -409,8 +409,7 @@ bool retro_load_game(const struct retro_game_info* game) {
|
|||
|
||||
#ifdef M_CORE_GBA
|
||||
if (core->platform(core) == PLATFORM_GBA) {
|
||||
struct GBA* gba = core->board;
|
||||
gba->luminanceSource = &lux;
|
||||
core->setPeripheral(core, mPERIPH_GBA_LUMINANCE, &lux);
|
||||
|
||||
const char* sysDir = 0;
|
||||
if (core->opts.useBios && environCallback(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &sysDir)) {
|
||||
|
|
|
@ -65,6 +65,10 @@ static void mGLContextResized(struct VideoBackend* v, unsigned w, unsigned h) {
|
|||
drawH = w * v->height / v->width;
|
||||
}
|
||||
}
|
||||
if (v->lockIntegerScaling) {
|
||||
drawW -= drawW % v->width;
|
||||
drawH -= drawH % v->height;
|
||||
}
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
glClearColor(0, 0, 0, 0);
|
||||
|
|
|
@ -171,6 +171,10 @@ static void mGLES2ContextResized(struct VideoBackend* v, unsigned w, unsigned h)
|
|||
drawH = w * v->height / v->width;
|
||||
}
|
||||
}
|
||||
if (v->lockIntegerScaling) {
|
||||
drawW -= drawW % v->width;
|
||||
drawH -= drawH % v->height;
|
||||
}
|
||||
glViewport(0, 0, v->width, v->height);
|
||||
glClearColor(0.f, 0.f, 0.f, 1.f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include <psp2/display.h>
|
||||
#include <psp2/kernel/processmgr.h>
|
||||
#include <psp2/kernel/threadmgr.h>
|
||||
#include <psp2/moduleinfo.h>
|
||||
#include <psp2/power.h>
|
||||
#include <psp2/sysmodule.h>
|
||||
#include <psp2/touch.h>
|
||||
|
@ -164,6 +163,7 @@ int main() {
|
|||
mPSP2MapKey(&runner.params.keyMap, SCE_CTRL_RIGHT, GUI_INPUT_RIGHT);
|
||||
mPSP2MapKey(&runner.params.keyMap, SCE_CTRL_SQUARE, mGUI_INPUT_SCREEN_MODE);
|
||||
|
||||
scePowerSetArmClockFrequency(444);
|
||||
mGUIRunloop(&runner);
|
||||
|
||||
vita2d_fini();
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include <mgba-util/memory.h>
|
||||
#include <mgba-util/circle-buffer.h>
|
||||
#include <mgba-util/math.h>
|
||||
#include <mgba-util/ring-fifo.h>
|
||||
#include <mgba-util/threading.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
@ -31,7 +32,6 @@
|
|||
#include <psp2/gxm.h>
|
||||
#include <psp2/kernel/sysmem.h>
|
||||
#include <psp2/motion.h>
|
||||
#include <psp2/power.h>
|
||||
|
||||
#include <vita2d.h>
|
||||
|
||||
|
@ -63,8 +63,8 @@ bool frameLimiter = true;
|
|||
extern const uint8_t _binary_backdrop_png_start[];
|
||||
static vita2d_texture* backdrop = 0;
|
||||
|
||||
#define PSP2_SAMPLES 128
|
||||
#define PSP2_AUDIO_BUFFER_SIZE (PSP2_SAMPLES * 40)
|
||||
#define PSP2_SAMPLES 256
|
||||
#define PSP2_AUDIO_BUFFER_SIZE (PSP2_SAMPLES * 20)
|
||||
|
||||
static struct mPSP2AudioContext {
|
||||
struct RingFIFO buffer;
|
||||
|
@ -176,7 +176,6 @@ void mPSP2Setup(struct mGUIRunner* runner) {
|
|||
mCoreConfigSetDefaultIntValue(&runner->config, "threadedVideo", 1);
|
||||
mCoreLoadForeignConfig(runner->core, &runner->config);
|
||||
|
||||
scePowerSetArmClockFrequency(333);
|
||||
mPSP2MapKey(&runner->core->inputMap, SCE_CTRL_CROSS, GBA_KEY_A);
|
||||
mPSP2MapKey(&runner->core->inputMap, SCE_CTRL_CIRCLE, GBA_KEY_B);
|
||||
mPSP2MapKey(&runner->core->inputMap, SCE_CTRL_START, GBA_KEY_START);
|
||||
|
@ -193,8 +192,10 @@ void mPSP2Setup(struct mGUIRunner* runner) {
|
|||
desc = (struct mInputAxis) { GBA_KEY_RIGHT, GBA_KEY_LEFT, 192, 64 };
|
||||
mInputBindAxis(&runner->core->inputMap, PSP2_INPUT, 1, &desc);
|
||||
|
||||
tex = vita2d_create_empty_texture_format(256, 256, SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
|
||||
screenshot = vita2d_create_empty_texture_format(256, 256, SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
|
||||
unsigned width, height;
|
||||
runner->core->desiredVideoDimensions(runner->core, &width, &height);
|
||||
tex = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
|
||||
screenshot = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
|
||||
|
||||
outputBuffer = vita2d_texture_get_datap(tex);
|
||||
runner->core->setVideoBuffer(runner->core, outputBuffer, 256);
|
||||
|
@ -203,11 +204,11 @@ void mPSP2Setup(struct mGUIRunner* runner) {
|
|||
rotation.d.readTiltX = _readTiltX;
|
||||
rotation.d.readTiltY = _readTiltY;
|
||||
rotation.d.readGyroZ = _readGyroZ;
|
||||
runner->core->setRotation(runner->core, &rotation.d);
|
||||
runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation.d);
|
||||
|
||||
rumble.d.setRumble = _setRumble;
|
||||
CircleBufferInit(&rumble.history, RUMBLE_PWM);
|
||||
runner->core->setRumble(runner->core, &rumble.d);
|
||||
runner->core->setPeripheral(runner->core, mPERIPH_RUMBLE, &rumble.d);
|
||||
|
||||
frameLimiter = true;
|
||||
backdrop = vita2d_load_PNG_buffer(_binary_backdrop_png_start);
|
||||
|
@ -219,7 +220,6 @@ void mPSP2Setup(struct mGUIRunner* runner) {
|
|||
}
|
||||
|
||||
void mPSP2LoadROM(struct mGUIRunner* runner) {
|
||||
scePowerSetArmClockFrequency(444);
|
||||
float rate = 60.0f / 1.001f;
|
||||
sceDisplayGetRefreshRate(&rate);
|
||||
double ratio = GBAAudioCalculateRatio(1, rate, 1);
|
||||
|
@ -262,7 +262,6 @@ void mPSP2PrepareForFrame(struct mGUIRunner* runner) {
|
|||
blip_clear(runner->core->getAudioChannel(runner->core, 1));
|
||||
break;
|
||||
}
|
||||
sceKernelDelayThread(400);
|
||||
}
|
||||
blip_read_samples(runner->core->getAudioChannel(runner->core, 0), &samples[0].left, PSP2_SAMPLES, true);
|
||||
blip_read_samples(runner->core->getAudioChannel(runner->core, 1), &samples[0].right, PSP2_SAMPLES, true);
|
||||
|
@ -297,7 +296,6 @@ void mPSP2UnloadROM(struct mGUIRunner* runner) {
|
|||
default:
|
||||
break;
|
||||
}
|
||||
scePowerSetArmClockFrequency(333);
|
||||
}
|
||||
|
||||
void mPSP2Paused(struct mGUIRunner* runner) {
|
||||
|
|
|
@ -2,8 +2,13 @@ find_program(PYTHON python)
|
|||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in ${CMAKE_CURRENT_BINARY_DIR}/setup.py)
|
||||
|
||||
get_property(INCLUDE_DIRECTORIES DIRECTORY PROPERTY INCLUDE_DIRECTORIES)
|
||||
set(INCLUDE_FLAGS)
|
||||
foreach(DIR IN LISTS INCLUDE_DIRECTORIES)
|
||||
list(APPEND INCLUDE_FLAGS "-I${DIR}")
|
||||
endforeach()
|
||||
add_custom_command(OUTPUT build/lib/${BINARY_NAME}/__init__.py
|
||||
COMMAND BINDIR=${CMAKE_CURRENT_BINARY_DIR}/.. ${PYTHON} ${CMAKE_CURRENT_BINARY_DIR}/setup.py build --build-base ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND BINDIR=${CMAKE_CURRENT_BINARY_DIR}/.. CPPFLAGS="${INCLUDE_FLAGS}" ${PYTHON} ${CMAKE_CURRENT_BINARY_DIR}/setup.py build --build-base ${CMAKE_CURRENT_BINARY_DIR}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DEPENDS ${BINARY_NAME}
|
||||
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/setup.py
|
||||
|
|
|
@ -29,8 +29,12 @@ void free(void*);
|
|||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/core/tile-cache.h>
|
||||
#include "platform/python/vfs-py.h"
|
||||
|
||||
#define PYEXPORT extern "Python+C"
|
||||
#include "platform/python/log.h"
|
||||
#include "platform/python/sio.h"
|
||||
#include "platform/python/vfs-py.h"
|
||||
#undef PYEXPORT
|
||||
|
||||
#ifdef USE_PNG
|
||||
#include <mgba-util/png-io.h>
|
||||
|
|
|
@ -32,24 +32,16 @@ ffi.set_source("mgba._pylib", """
|
|||
#include <mgba-util/png-io.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
struct VFile* VFileFromPython(void* fileobj);
|
||||
|
||||
struct VFilePy {
|
||||
struct VFile d;
|
||||
void* fileobj;
|
||||
};
|
||||
|
||||
struct mLogger* mLoggerPythonCreate(void* pyobj);
|
||||
|
||||
struct mLoggerPy {
|
||||
struct mLogger d;
|
||||
void* pyobj;
|
||||
};
|
||||
#define PYEXPORT
|
||||
#include "platform/python/log.h"
|
||||
#include "platform/python/sio.h"
|
||||
#include "platform/python/vfs-py.h"
|
||||
#undef PYEXPORT
|
||||
""", include_dirs=[incdir, srcdir],
|
||||
extra_compile_args=cppflags,
|
||||
libraries=["mgba"],
|
||||
library_dirs=[bindir],
|
||||
sources=[os.path.join(pydir, path) for path in ["vfs-py.c", "log.c"]])
|
||||
sources=[os.path.join(pydir, path) for path in ["vfs-py.c", "log.c", "sio.c"]])
|
||||
|
||||
preprocessed = subprocess.check_output(cpp + ["-fno-inline", "-P"] + cppflags + [os.path.join(pydir, "_builder.h")], universal_newlines=True)
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ static void _pyLogShim(struct mLogger* logger, int category, enum mLogLevel leve
|
|||
struct mLogger* mLoggerPythonCreate(void* pyobj) {
|
||||
struct mLoggerPy* logger = malloc(sizeof(*logger));
|
||||
logger->d.log = _pyLogShim;
|
||||
logger->d.filter = NULL;
|
||||
logger->pyobj = pyobj;
|
||||
return &logger->d;
|
||||
}
|
||||
|
|
|
@ -12,4 +12,4 @@ struct mLoggerPy {
|
|||
|
||||
struct mLogger* mLoggerPythonCreate(void* pyobj);
|
||||
|
||||
extern "Python+C" void _pyLog(void* logger, int category, enum mLogLevel level, const char* message);
|
||||
PYEXPORT void _pyLog(void* logger, int category, enum mLogLevel level, const char* message);
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
# 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/.
|
||||
from ._pylib import ffi, lib
|
||||
|
||||
def createCallback(structName, cbName, funcName=None):
|
||||
funcName = funcName or "_py{}{}".format(structName, cbName[0].upper() + cbName[1:])
|
||||
fullStruct = "struct {}*".format(structName)
|
||||
def cb(handle, *args):
|
||||
h = ffi.cast(fullStruct, handle)
|
||||
return getattr(ffi.from_handle(h.pyobj), cbName)(*args)
|
||||
|
||||
return ffi.def_extern(name=funcName)(cb)
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright (c) 2013-2016 Jeffrey Pfau
|
||||
# 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
|
||||
|
@ -8,6 +8,7 @@ from .lr35902 import LR35902Core
|
|||
from .core import Core, needsReset
|
||||
from .memory import Memory
|
||||
from .tile import Sprite
|
||||
from . import createCallback
|
||||
|
||||
class GB(Core):
|
||||
KEY_A = lib.GBA_KEY_A
|
||||
|
@ -34,6 +35,48 @@ class GB(Core):
|
|||
self._native.video.renderer.cache = ffi.NULL
|
||||
lib.mTileCacheDeinit(cache)
|
||||
|
||||
def attachSIO(self, link):
|
||||
lib.GBSIOSetDriver(ffi.addressof(self._native.sio), link._native)
|
||||
|
||||
createCallback("GBSIOPythonDriver", "init")
|
||||
createCallback("GBSIOPythonDriver", "deinit")
|
||||
createCallback("GBSIOPythonDriver", "writeSB")
|
||||
createCallback("GBSIOPythonDriver", "writeSC")
|
||||
|
||||
class GBSIODriver(object):
|
||||
def __init__(self):
|
||||
self._handle = ffi.new_handle(self)
|
||||
self._native = ffi.gc(lib.GBSIOPythonDriverCreate(self._handle), lib.free)
|
||||
|
||||
def init(self):
|
||||
return True
|
||||
|
||||
def deinit(self):
|
||||
pass
|
||||
|
||||
def writeSB(self, value):
|
||||
pass
|
||||
|
||||
def writeSC(self, value):
|
||||
return value
|
||||
|
||||
class GBSIOSimpleDriver(GBSIODriver):
|
||||
def __init__(self):
|
||||
super(GBSIOSimpleDriver, self).__init__()
|
||||
self.tx = 0xFF
|
||||
self.rx = 0xFF
|
||||
|
||||
def writeSB(self, value):
|
||||
self.rx = value
|
||||
|
||||
def schedule(self, period=0x100, when=0):
|
||||
self._native.p.remainingBits = 8
|
||||
self._native.p.period = period
|
||||
self._native.p.pendingSB = self.tx
|
||||
self.tx = 0xFF
|
||||
lib.mTimingDeschedule(ffi.addressof(self._native.p.p.timing), ffi.addressof(self._native.p.event))
|
||||
lib.mTimingSchedule(ffi.addressof(self._native.p.p.timing), ffi.addressof(self._native.p.event), when)
|
||||
|
||||
class GBMemory(Memory):
|
||||
def __init__(self, core):
|
||||
super(GBMemory, self).__init__(core, 0x10000)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue