mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'feature/input-revamp' into medusa
This commit is contained in:
commit
f45ff4d35f
21
CHANGES
21
CHANGES
|
@ -25,6 +25,13 @@ Features:
|
|||
- 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
|
||||
|
@ -58,6 +65,14 @@ Bugfixes:
|
|||
- 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
|
||||
|
@ -112,6 +127,12 @@ Misc:
|
|||
- 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
|
||||
|
||||
medusa alpha 2: (2017-04-26)
|
||||
Features:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
cmake_minimum_required(VERSION 2.6)
|
||||
project(medusa C)
|
||||
cmake_minimum_required(VERSION 2.8.11)
|
||||
project(medusa)
|
||||
set(BINARY_NAME medusa-emu CACHE INTERNAL "Name of output binaries")
|
||||
if(NOT MSVC)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-missing-field-initializers -std=c99")
|
||||
|
@ -56,12 +56,14 @@ 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 DS_RENDERER_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/ds/renderers/*.c ${CMAKE_CURRENT_SOURCE_DIR}/src/ds/gx/*.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})
|
||||
|
@ -306,6 +308,7 @@ else()
|
|||
endif()
|
||||
set(DISABLE_FRONTENDS ON)
|
||||
set(MINIMAL_CORE ON)
|
||||
set(ENABLE_EXTRA ON)
|
||||
endif()
|
||||
|
||||
check_function_exists(chmod HAVE_CHMOD)
|
||||
|
@ -397,6 +400,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)
|
||||
|
@ -412,7 +416,7 @@ if(USE_EDITLINE)
|
|||
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/debugger/parser.c)
|
||||
if(M_CORE_GBA)
|
||||
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/arm/debugger/cli-debugger.c)
|
||||
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/extra/cli.c)
|
||||
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/debugger/cli.c)
|
||||
endif()
|
||||
if(M_CORE_GB)
|
||||
list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/lr35902/debugger/cli-debugger.c)
|
||||
|
@ -618,7 +622,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})
|
||||
|
@ -635,7 +641,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})
|
||||
|
@ -693,10 +699,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)
|
||||
|
@ -704,8 +709,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)
|
||||
|
@ -802,7 +820,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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
#ifdef M_CORE_DS
|
||||
PLATFORM_DS,
|
||||
|
@ -42,11 +42,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;
|
||||
|
@ -141,6 +144,8 @@ 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*);
|
||||
|
@ -152,6 +157,11 @@ struct mCore {
|
|||
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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -175,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;
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -22,8 +22,11 @@
|
|||
#include <mgba/ds/core.h>
|
||||
#include <mgba/internal/ds/ds.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;
|
||||
|
@ -44,7 +47,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;
|
||||
|
@ -53,6 +56,9 @@ struct mCore* mCoreFindVF(struct VFile* vf) {
|
|||
if (filter->open) {
|
||||
return filter->open();
|
||||
}
|
||||
#ifndef MINIMAL_CORE
|
||||
return mVideoLogCoreFind(vf);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -60,7 +66,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;
|
||||
|
|
|
@ -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,27 +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) {
|
||||
nextState->truncate(nextState, size);
|
||||
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;
|
||||
|
@ -88,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
|
||||
|
||||
|
|
|
@ -168,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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -495,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';
|
||||
|
@ -524,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>
|
||||
|
|
|
@ -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;
|
||||
}
|
189
src/gb/core.c
189
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>
|
||||
|
@ -38,9 +42,27 @@ const static struct mCoreChannelInfo _GBAudioChannels[] = {
|
|||
{ 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;
|
||||
|
@ -93,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;
|
||||
|
@ -541,8 +566,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;
|
||||
}
|
||||
|
@ -569,6 +601,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) {
|
||||
|
@ -658,6 +703,29 @@ static void _GBCoreEnableAudioChannel(struct mCore* core, size_t id, bool enable
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -665,6 +733,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;
|
||||
|
@ -724,6 +793,7 @@ struct mCore* GBCoreCreate(void) {
|
|||
core->cliDebuggerSystem = _GBCoreCliDebuggerSystem;
|
||||
core->attachDebugger = _GBCoreAttachDebugger;
|
||||
core->detachDebugger = _GBCoreDetachDebugger;
|
||||
core->loadSymbols = _GBCoreLoadSymbols;
|
||||
#endif
|
||||
core->cheatDevice = _GBCoreCheatDevice;
|
||||
core->savedataClone = _GBCoreSavedataClone;
|
||||
|
@ -732,5 +802,120 @@ struct mCore* GBCoreCreate(void) {
|
|||
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);
|
||||
|
@ -43,6 +44,7 @@ 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;
|
||||
|
@ -126,6 +128,12 @@ 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];
|
||||
|
|
|
@ -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,
|
||||
|
@ -104,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) {
|
||||
|
@ -113,32 +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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
@ -158,10 +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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
@ -176,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);
|
||||
}
|
||||
|
@ -191,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);
|
||||
}
|
||||
|
@ -202,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);
|
||||
|
@ -213,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);
|
||||
}
|
||||
|
||||
|
@ -252,16 +282,16 @@ void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLat
|
|||
video->p->stream->postVideoFrame(video->p->stream, pixels, stride);
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -316,9 +346,10 @@ 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);
|
||||
}
|
||||
|
@ -342,20 +373,21 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -469,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) {
|
||||
|
|
167
src/gba/core.c
167
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>
|
||||
|
@ -43,11 +45,15 @@ const static struct mCoreChannelInfo _GBAAudioChannels[] = {
|
|||
{ 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;
|
||||
|
@ -70,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
|
||||
|
@ -87,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;
|
||||
|
@ -301,7 +310,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);
|
||||
|
@ -346,7 +357,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);
|
||||
|
@ -398,16 +409,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 void _GBACoreSetCursorLocation(struct mCore* core, int x, int y) {
|
||||
|
@ -567,6 +581,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) {
|
||||
|
@ -668,6 +686,33 @@ static void _GBACoreEnableAudioChannel(struct mCore* core, size_t id, bool enabl
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -734,6 +779,7 @@ struct mCore* GBACoreCreate(void) {
|
|||
core->cliDebuggerSystem = _GBACoreCliDebuggerSystem;
|
||||
core->attachDebugger = _GBACoreAttachDebugger;
|
||||
core->detachDebugger = _GBACoreDetachDebugger;
|
||||
core->loadSymbols = _GBACoreLoadSymbols;
|
||||
#endif
|
||||
core->cheatDevice = _GBACoreCheatDevice;
|
||||
core->savedataClone = _GBACoreSavedataClone;
|
||||
|
@ -742,5 +788,116 @@ struct mCore* GBACoreCreate(void) {
|
|||
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,301 @@
|
|||
/* 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;
|
||||
memset(proxyRenderer->backend->vramBG, 0, sizeof(proxyRenderer->backend->vramBG));
|
||||
proxyRenderer->backend->vramBG[0] = &proxyRenderer->logger->vram[0x0000];
|
||||
proxyRenderer->backend->vramBG[1] = &proxyRenderer->logger->vram[0x2000];
|
||||
proxyRenderer->backend->vramBG[2] = &proxyRenderer->logger->vram[0x4000];
|
||||
proxyRenderer->backend->vramBG[3] = &proxyRenderer->logger->vram[0x6000];
|
||||
memset(proxyRenderer->backend->vramOBJ, 0, sizeof(proxyRenderer->backend->vramOBJ));
|
||||
proxyRenderer->backend->vramOBJ[0] = &proxyRenderer->logger->vram[0x8000];
|
||||
proxyRenderer->backend->vramOBJ[1] = &proxyRenderer->logger->vram[0xA000];
|
||||
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[0x0000], proxyRenderer->d.vramBG[0], 0x4000);
|
||||
memcpy(&proxyRenderer->logger->vram[0x2000], proxyRenderer->d.vramBG[1], 0x4000);
|
||||
memcpy(&proxyRenderer->logger->vram[0x4000], proxyRenderer->d.vramBG[2], 0x4000);
|
||||
memcpy(&proxyRenderer->logger->vram[0x6000], proxyRenderer->d.vramBG[3], 0x4000);
|
||||
memcpy(&proxyRenderer->logger->vram[0x8000], proxyRenderer->d.vramOBJ[0], 0x4000);
|
||||
memcpy(&proxyRenderer->logger->vram[0xA000], proxyRenderer->d.vramOBJ[1], 0x4000);
|
||||
|
||||
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;
|
||||
memcpy(renderer->d.vramBG, renderer->backend->vramBG, sizeof(renderer->backend->vramBG));
|
||||
memcpy(renderer->d.vramOBJ, renderer->backend->vramOBJ, sizeof(renderer->backend->vramOBJ));
|
||||
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;
|
||||
memcpy(renderer->backend->vramBG, renderer->d.vramBG, sizeof(renderer->backend->vramBG));
|
||||
memcpy(renderer->backend->vramOBJ, renderer->d.vramOBJ, sizeof(renderer->backend->vramOBJ));
|
||||
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;
|
||||
if (address < 0x10000) {
|
||||
return &proxyRenderer->d.vramBG[address >> VRAM_BLOCK_OFFSET][(address & VRAM_BLOCK_MASK) >> 1];
|
||||
} else {
|
||||
return &proxyRenderer->d.vramOBJ[(address & 0x7FFF) >> VRAM_BLOCK_OFFSET][(address & VRAM_BLOCK_MASK) >> 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);
|
||||
}
|
||||
}
|
|
@ -409,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);
|
||||
}
|
||||
|
@ -636,7 +632,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) {
|
||||
|
@ -683,6 +679,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) {
|
||||
|
|
11
src/gba/io.c
11
src/gba/io.c
|
@ -541,6 +541,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);
|
||||
|
@ -693,6 +698,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;
|
||||
}
|
||||
|
@ -725,6 +731,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;
|
||||
}
|
||||
|
@ -818,7 +827,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;
|
||||
|
@ -867,6 +875,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:
|
||||
|
|
|
@ -98,7 +98,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);
|
||||
|
|
|
@ -1,397 +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;
|
||||
memset(renderer->vramBG, 0, sizeof(renderer->vramBG));
|
||||
proxyRenderer->backend->vramBG[0] = &proxyRenderer->vramProxy[0x0000];
|
||||
proxyRenderer->backend->vramBG[1] = &proxyRenderer->vramProxy[0x2000];
|
||||
proxyRenderer->backend->vramBG[2] = &proxyRenderer->vramProxy[0x4000];
|
||||
proxyRenderer->backend->vramBG[3] = &proxyRenderer->vramProxy[0x6000];
|
||||
memset(renderer->vramOBJ, 0, sizeof(renderer->vramOBJ));
|
||||
proxyRenderer->backend->vramOBJ[0] = &proxyRenderer->vramProxy[0x8000];
|
||||
proxyRenderer->backend->vramOBJ[1] = &proxyRenderer->vramProxy[0xA000];
|
||||
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->vramBG[0], 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.vramBG[0][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
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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=["medusa-emu"],
|
||||
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)
|
||||
|
|
|
@ -8,6 +8,7 @@ from .arm import ARMCore
|
|||
from .core import Core, needsReset
|
||||
from .tile import Sprite
|
||||
from .memory import Memory
|
||||
from . import createCallback
|
||||
|
||||
class GBA(Core):
|
||||
KEY_A = lib.GBA_KEY_A
|
||||
|
@ -21,6 +22,12 @@ class GBA(Core):
|
|||
KEY_L = lib.GBA_KEY_L
|
||||
KEY_R = lib.GBA_KEY_R
|
||||
|
||||
SIO_NORMAL_8 = lib.SIO_NORMAL_8
|
||||
SIO_NORMAL_32 = lib.SIO_NORMAL_32
|
||||
SIO_MULTI = lib.SIO_MULTI
|
||||
SIO_UART = lib.SIO_UART
|
||||
SIO_GPIO = lib.SIO_GPIO
|
||||
|
||||
def __init__(self, native):
|
||||
super(GBA, self).__init__(native)
|
||||
self._native = ffi.cast("struct GBA*", native.board)
|
||||
|
@ -40,6 +47,35 @@ class GBA(Core):
|
|||
super(GBA, self).reset()
|
||||
self.memory = GBAMemory(self._core, self._native.memory.romSize)
|
||||
|
||||
def attachSIO(self, link, mode=lib.SIO_MULTI):
|
||||
lib.GBASIOSetDriver(ffi.addressof(self._native.sio), link._native, mode)
|
||||
|
||||
createCallback("GBASIOPythonDriver", "init")
|
||||
createCallback("GBASIOPythonDriver", "deinit")
|
||||
createCallback("GBASIOPythonDriver", "load")
|
||||
createCallback("GBASIOPythonDriver", "unload")
|
||||
createCallback("GBASIOPythonDriver", "writeRegister")
|
||||
|
||||
class GBASIODriver(object):
|
||||
def __init__(self):
|
||||
self._handle = ffi.new_handle(self)
|
||||
self._native = ffi.gc(lib.GBASIOPythonDriverCreate(self._handle), lib.free)
|
||||
|
||||
def init(self):
|
||||
return True
|
||||
|
||||
def deinit(self):
|
||||
pass
|
||||
|
||||
def load(self):
|
||||
return True
|
||||
|
||||
def unload(self):
|
||||
return True
|
||||
|
||||
def writeRegister(self, address, value):
|
||||
return value
|
||||
|
||||
class GBAMemory(Memory):
|
||||
def __init__(self, core, romSize=lib.SIZE_CART0):
|
||||
super(GBAMemory, self).__init__(core, 0x100000000)
|
||||
|
|
|
@ -4,11 +4,9 @@
|
|||
# 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
|
||||
from . import createCallback
|
||||
|
||||
@ffi.def_extern()
|
||||
def _pyLog(logger, category, level, message):
|
||||
l = ffi.cast("struct mLoggerPy*", logger)
|
||||
ffi.from_handle(l.pyobj).log(category, level, ffi.string(message).decode('UTF-8'))
|
||||
createCallback("mLoggerPy", "log", "_pyLog")
|
||||
|
||||
def installDefault(logger):
|
||||
lib.mLogSetDefaultLogger(logger._native)
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
/* 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 "flags.h"
|
||||
|
||||
#define CREATE_SHIM(PLAT, NAME, RETURN) \
|
||||
RETURN _py ## PLAT ## SIOPythonDriver ## NAME (void* driver); \
|
||||
static RETURN _py ## PLAT ## SIOPythonDriver ## NAME ## Shim(struct PLAT ## SIODriver* driver) { \
|
||||
struct PLAT ## SIODriver* py = (struct PLAT ## SIODriver*) driver; \
|
||||
return _py ## PLAT ## SIOPythonDriver ## NAME(py); \
|
||||
}
|
||||
|
||||
#define CREATE_SHIM_ARGS(PLAT, NAME, RETURN, TYPES, ...) \
|
||||
RETURN _py ## PLAT ## SIOPythonDriver ## NAME TYPES; \
|
||||
static RETURN _py ## PLAT ## SIOPythonDriver ## NAME ## Shim TYPES { \
|
||||
struct PLAT ## SIODriver* py = (struct PLAT ## SIODriver*) driver; \
|
||||
return _py ## PLAT ## SIOPythonDriver ## NAME(py, __VA_ARGS__); \
|
||||
}
|
||||
|
||||
#ifdef M_CORE_GBA
|
||||
|
||||
#include <mgba/gba/interface.h>
|
||||
|
||||
struct GBASIOPythonDriver {
|
||||
struct GBASIODriver d;
|
||||
void* pyobj;
|
||||
};
|
||||
|
||||
CREATE_SHIM(GBA, Init, bool);
|
||||
CREATE_SHIM(GBA, Deinit, void);
|
||||
CREATE_SHIM(GBA, Load, bool);
|
||||
CREATE_SHIM(GBA, Unload, bool);
|
||||
CREATE_SHIM_ARGS(GBA, WriteRegister, uint16_t, (struct GBASIODriver* driver, uint32_t address, uint16_t value), address, value);
|
||||
|
||||
struct GBASIODriver* GBASIOPythonDriverCreate(void* pyobj) {
|
||||
struct GBASIOPythonDriver* driver = malloc(sizeof(*driver));
|
||||
driver->d.init = _pyGBASIOPythonDriverInitShim;
|
||||
driver->d.deinit = _pyGBASIOPythonDriverDeinitShim;
|
||||
driver->d.load = _pyGBASIOPythonDriverLoadShim;
|
||||
driver->d.unload = _pyGBASIOPythonDriverUnloadShim;
|
||||
driver->d.writeRegister = _pyGBASIOPythonDriverWriteRegisterShim;
|
||||
|
||||
driver->pyobj = pyobj;
|
||||
return &driver->d;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef M_CORE_GB
|
||||
|
||||
#include <mgba/gb/interface.h>
|
||||
|
||||
struct GBSIOPythonDriver {
|
||||
struct GBSIODriver d;
|
||||
void* pyobj;
|
||||
};
|
||||
|
||||
CREATE_SHIM(GB, Init, bool);
|
||||
CREATE_SHIM(GB, Deinit, void);
|
||||
CREATE_SHIM_ARGS(GB, WriteSB, void, (struct GBSIODriver* driver, uint8_t value), value);
|
||||
CREATE_SHIM_ARGS(GB, WriteSC, uint8_t, (struct GBSIODriver* driver, uint8_t value), value);
|
||||
|
||||
struct GBSIODriver* GBSIOPythonDriverCreate(void* pyobj) {
|
||||
struct GBSIOPythonDriver* driver = malloc(sizeof(*driver));
|
||||
driver->d.init = _pyGBSIOPythonDriverInitShim;
|
||||
driver->d.deinit = _pyGBSIOPythonDriverDeinitShim;
|
||||
driver->d.writeSB = _pyGBSIOPythonDriverWriteSBShim;
|
||||
driver->d.writeSC = _pyGBSIOPythonDriverWriteSCShim;
|
||||
|
||||
driver->pyobj = pyobj;
|
||||
return &driver->d;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,41 @@
|
|||
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifdef M_CORE_GBA
|
||||
|
||||
#include <mgba/gba/interface.h>
|
||||
|
||||
struct GBASIOPythonDriver {
|
||||
struct GBASIODriver d;
|
||||
void* pyobj;
|
||||
};
|
||||
|
||||
struct GBASIODriver* GBASIOPythonDriverCreate(void* pyobj);
|
||||
|
||||
PYEXPORT bool _pyGBASIOPythonDriverInit(void* driver);
|
||||
PYEXPORT void _pyGBASIOPythonDriverDeinit(void* driver);
|
||||
PYEXPORT bool _pyGBASIOPythonDriverLoad(void* driver);
|
||||
PYEXPORT bool _pyGBASIOPythonDriverUnload(void* driver);
|
||||
PYEXPORT uint16_t _pyGBASIOPythonDriverWriteRegister(void* driver, uint32_t address, uint16_t value);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef M_CORE_GB
|
||||
|
||||
#include <mgba/gb/interface.h>
|
||||
|
||||
struct GBSIOPythonDriver {
|
||||
struct GBSIODriver d;
|
||||
void* pyobj;
|
||||
};
|
||||
|
||||
struct GBSIODriver* GBSIOPythonDriverCreate(void* pyobj);
|
||||
|
||||
PYEXPORT bool _pyGBSIOPythonDriverInit(void* driver);
|
||||
PYEXPORT void _pyGBSIOPythonDriverDeinit(void* driver);
|
||||
PYEXPORT void _pyGBSIOPythonDriverWriteSB(void* driver, uint8_t value);
|
||||
PYEXPORT uint8_t _pyGBSIOPythonDriverWriteSC(void* driver, uint8_t value);
|
||||
|
||||
#endif
|
|
@ -13,16 +13,12 @@ struct VFilePy {
|
|||
|
||||
struct VFile* VFileFromPython(void* fileobj);
|
||||
|
||||
extern "Python+C" {
|
||||
|
||||
bool _vfpClose(struct VFile* vf);
|
||||
off_t _vfpSeek(struct VFile* vf, off_t offset, int whence);
|
||||
ssize_t _vfpRead(struct VFile* vf, void* buffer, size_t size);
|
||||
ssize_t _vfpWrite(struct VFile* vf, const void* buffer, size_t size);
|
||||
void* _vfpMap(struct VFile* vf, size_t size, int flags);
|
||||
void _vfpUnmap(struct VFile* vf, void* memory, size_t size);
|
||||
void _vfpTruncate(struct VFile* vf, size_t size);
|
||||
ssize_t _vfpSize(struct VFile* vf);
|
||||
bool _vfpSync(struct VFile* vf, const void* buffer, size_t size);
|
||||
|
||||
}
|
||||
PYEXPORT bool _vfpClose(struct VFile* vf);
|
||||
PYEXPORT off_t _vfpSeek(struct VFile* vf, off_t offset, int whence);
|
||||
PYEXPORT ssize_t _vfpRead(struct VFile* vf, void* buffer, size_t size);
|
||||
PYEXPORT ssize_t _vfpWrite(struct VFile* vf, const void* buffer, size_t size);
|
||||
PYEXPORT void* _vfpMap(struct VFile* vf, size_t size, int flags);
|
||||
PYEXPORT void _vfpUnmap(struct VFile* vf, void* memory, size_t size);
|
||||
PYEXPORT void _vfpTruncate(struct VFile* vf, size_t size);
|
||||
PYEXPORT ssize_t _vfpSize(struct VFile* vf);
|
||||
PYEXPORT bool _vfpSync(struct VFile* vf, const void* buffer, size_t size);
|
||||
|
|
|
@ -13,11 +13,12 @@ ArchiveInspector::ArchiveInspector(const QString& filename, QWidget* parent)
|
|||
: QDialog(parent)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
connect(m_ui.archiveView, &LibraryView::doneLoading, [this]() {
|
||||
connect(m_ui.archiveView, &LibraryController::doneLoading, [this]() {
|
||||
m_ui.loading->hide();
|
||||
});
|
||||
connect(m_ui.archiveView, SIGNAL(accepted()), this, SIGNAL(accepted()));
|
||||
m_ui.archiveView->setDirectory(filename);
|
||||
connect(m_ui.archiveView, &LibraryController::startGame, this, &ArchiveInspector::accepted);
|
||||
m_ui.archiveView->setViewStyle(LibraryStyle::STYLE_LIST);
|
||||
m_ui.archiveView->addDirectory(filename);
|
||||
}
|
||||
|
||||
VFile* ArchiveInspector::selectedVFile() const {
|
||||
|
|
|
@ -29,15 +29,15 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QGBA::LibraryView" name="archiveView" native="true"/>
|
||||
<widget class="QGBA::LibraryController" name="archiveView" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>QGBA::LibraryView</class>
|
||||
<class>QGBA::LibraryController</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>LibraryView.h</header>
|
||||
<header>library/LibraryController.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
|
|
|
@ -21,10 +21,6 @@ using namespace QGBA;
|
|||
|
||||
AssetTile::AssetTile(QWidget* parent)
|
||||
: QGroupBox(parent)
|
||||
, m_tileCache(nullptr)
|
||||
, m_paletteId(0)
|
||||
, m_paletteSet(0)
|
||||
, m_index(0)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
|
@ -32,7 +28,7 @@ AssetTile::AssetTile(QWidget* parent)
|
|||
m_ui.color->setDimensions(QSize(1, 1));
|
||||
m_ui.color->setSize(50);
|
||||
|
||||
connect(m_ui.preview, SIGNAL(indexPressed(int)), this, SLOT(selectColor(int)));
|
||||
connect(m_ui.preview, &Swatch::indexPressed, this, &AssetTile::selectColor);
|
||||
|
||||
const QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont);
|
||||
|
||||
|
|
|
@ -31,9 +31,9 @@ private:
|
|||
Ui::AssetTile m_ui;
|
||||
|
||||
std::shared_ptr<mTileCache> m_tileCache;
|
||||
int m_paletteId;
|
||||
int m_paletteSet;
|
||||
int m_index;
|
||||
int m_paletteId = 0;
|
||||
int m_paletteSet = 0;
|
||||
int m_index = 0;
|
||||
|
||||
int m_addressWidth;
|
||||
int m_addressBase;
|
||||
|
|
|
@ -13,16 +13,17 @@ using namespace QGBA;
|
|||
|
||||
AssetView::AssetView(GameController* controller, QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_controller(controller)
|
||||
, m_tileCache(controller->tileCache())
|
||||
, m_controller(controller)
|
||||
{
|
||||
m_updateTimer.setSingleShot(true);
|
||||
m_updateTimer.setInterval(1);
|
||||
connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateTiles()));
|
||||
|
||||
connect(m_controller, SIGNAL(frameAvailable(const uint32_t*)), &m_updateTimer, SLOT(start()));
|
||||
connect(m_controller, SIGNAL(gameStopped(mCoreThread*)), this, SLOT(close()));
|
||||
connect(m_controller, SIGNAL(gameStopped(mCoreThread*)), &m_updateTimer, SLOT(stop()));
|
||||
connect(m_controller, &GameController::frameAvailable, &m_updateTimer,
|
||||
static_cast<void(QTimer::*)()>(&QTimer::start));
|
||||
connect(m_controller, &GameController::gameStopped, this, &AssetView::close);
|
||||
connect(m_controller, &GameController::gameStopped, &m_updateTimer, &QTimer::stop);
|
||||
}
|
||||
|
||||
void AssetView::updateTiles(bool force) {
|
||||
|
|
|
@ -44,8 +44,6 @@ AudioProcessor* AudioProcessor::create() {
|
|||
|
||||
AudioProcessor::AudioProcessor(QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_context(nullptr)
|
||||
, m_samples(2048)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -47,8 +47,8 @@ protected:
|
|||
mCoreThread* input() { return m_context; }
|
||||
|
||||
private:
|
||||
mCoreThread* m_context;
|
||||
int m_samples;
|
||||
mCoreThread* m_context = nullptr;
|
||||
int m_samples = 2048;
|
||||
static Driver s_driver;
|
||||
};
|
||||
|
||||
|
|
|
@ -17,9 +17,6 @@ using namespace QGBA;
|
|||
|
||||
AudioProcessorQt::AudioProcessorQt(QObject* parent)
|
||||
: AudioProcessor(parent)
|
||||
, m_audioOutput(nullptr)
|
||||
, m_device(nullptr)
|
||||
, m_sampleRate(44100)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -32,9 +32,9 @@ public slots:
|
|||
virtual void requestSampleRate(unsigned) override;
|
||||
|
||||
private:
|
||||
QAudioOutput* m_audioOutput;
|
||||
AudioDevice* m_device;
|
||||
unsigned m_sampleRate;
|
||||
QAudioOutput* m_audioOutput = nullptr;
|
||||
AudioDevice* m_device = nullptr;
|
||||
unsigned m_sampleRate = 44100;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ using namespace QGBA;
|
|||
|
||||
AudioProcessorSDL::AudioProcessorSDL(QObject* parent)
|
||||
: AudioProcessor(parent)
|
||||
, m_audio{ 2048, 44100 }
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ public slots:
|
|||
virtual void requestSampleRate(unsigned) override;
|
||||
|
||||
private:
|
||||
mSDLAudio m_audio;
|
||||
mSDLAudio m_audio{2048, 44100};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
cmake_minimum_required(VERSION 2.8.11)
|
||||
enable_language(CXX)
|
||||
|
||||
if(NOT MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
endif()
|
||||
|
@ -35,12 +32,10 @@ find_package(Qt5OpenGL)
|
|||
find_package(Qt5Widgets)
|
||||
|
||||
if(NOT BUILD_GL AND NOT BUILD_GLES2)
|
||||
message(WARNING "OpenGL is required to build the Qt port")
|
||||
set(BUILD_QT OFF PARENT_SCOPE)
|
||||
return()
|
||||
message(WARNING "OpenGL is recommended to build the Qt port")
|
||||
endif()
|
||||
|
||||
if(NOT Qt5OpenGL_FOUND OR NOT Qt5Widgets_FOUND)
|
||||
if(NOT Qt5Widgets_FOUND)
|
||||
message(WARNING "Cannot find Qt modules")
|
||||
set(BUILD_QT OFF PARENT_SCOPE)
|
||||
return()
|
||||
|
@ -105,6 +100,7 @@ set(SOURCE_FILES
|
|||
Swatch.cpp
|
||||
TilePainter.cpp
|
||||
TileView.cpp
|
||||
utils.cpp
|
||||
Window.cpp
|
||||
VFileDevice.cpp
|
||||
VideoView.cpp)
|
||||
|
@ -117,7 +113,6 @@ set(UI_FILES
|
|||
DebuggerConsole.ui
|
||||
GIFView.ui
|
||||
IOViewer.ui
|
||||
LibraryView.ui
|
||||
LoadSaveState.ui
|
||||
LogView.ui
|
||||
MemoryView.ui
|
||||
|
@ -138,8 +133,6 @@ set(GBA_SRC
|
|||
set(GB_SRC
|
||||
GBOverride.cpp)
|
||||
|
||||
qt5_wrap_ui(UI_SRC ${UI_FILES})
|
||||
|
||||
set(QT_LIBRARIES)
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libqt5widgets5,libqt5opengl5")
|
||||
|
||||
|
@ -189,8 +182,9 @@ endif()
|
|||
if(USE_SQLITE3)
|
||||
list(APPEND SOURCE_FILES
|
||||
ArchiveInspector.cpp
|
||||
LibraryModel.cpp
|
||||
LibraryView.cpp)
|
||||
library/LibraryController.cpp
|
||||
library/LibraryGrid.cpp
|
||||
library/LibraryTree.cpp)
|
||||
endif()
|
||||
|
||||
qt5_add_resources(RESOURCES resources.qrc)
|
||||
|
@ -239,11 +233,16 @@ if(Qt5LinguistTools_FOUND)
|
|||
list(APPEND RESOURCES ${TRANSLATION_RESOURCES})
|
||||
endif()
|
||||
|
||||
qt5_wrap_ui(UI_SRC ${UI_FILES})
|
||||
|
||||
add_executable(${BINARY_NAME}-qt WIN32 MACOSX_BUNDLE main.cpp ${CMAKE_SOURCE_DIR}/res/${BINARY_NAME}.icns ${SOURCE_FILES} ${PLATFORM_SRC} ${UI_SRC} ${AUDIO_SRC} ${RESOURCES})
|
||||
set_target_properties(${BINARY_NAME}-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/res/info.plist.in COMPILE_DEFINITIONS "${FEATURE_DEFINES};${FUNCTION_DEFINES};${OS_DEFINES};${QT_DEFINES}")
|
||||
|
||||
list(APPEND QT_LIBRARIES Qt5::Widgets Qt5::OpenGL)
|
||||
target_link_libraries(${BINARY_NAME}-qt ${PLATFORM_LIBRARY} ${BINARY_NAME} ${QT_LIBRARIES} ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY})
|
||||
list(APPEND QT_LIBRARIES Qt5::Widgets)
|
||||
if(BUILD_GL OR BUILD_GLES2)
|
||||
list(APPEND QT_LIBRARIES Qt5::OpenGL ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY})
|
||||
endif()
|
||||
target_link_libraries(${BINARY_NAME}-qt ${PLATFORM_LIBRARY} ${BINARY_NAME} ${QT_LIBRARIES})
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS}" PARENT_SCOPE)
|
||||
|
||||
install(TARGETS ${BINARY_NAME}-qt
|
||||
|
@ -290,3 +289,7 @@ if(APPLE)
|
|||
install(CODE "execute_process(COMMAND \"${CMAKE_SOURCE_DIR}/tools/deploy-mac.py\" -v ${DEPLOY_OPTIONS} \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/Applications/${PROJECT_NAME}.app\")")
|
||||
endif()
|
||||
endif()
|
||||
if(WIN32 AND CMAKE_MAJOR_VERSION GREATER 2 AND CMAKE_MINOR_VERSION GREATER 7)
|
||||
# Work around CMake issue #16907
|
||||
set_target_properties(${BINARY_NAME}-qt PROPERTIES AUTORCC ON SKIP_AUTORCC ON)
|
||||
endif()
|
||||
|
|
|
@ -31,12 +31,12 @@ CheatsView::CheatsView(GameController* controller, QWidget* parent)
|
|||
m_ui.cheatList->installEventFilter(this);
|
||||
m_ui.cheatList->setModel(&m_model);
|
||||
|
||||
connect(m_ui.load, SIGNAL(clicked()), this, SLOT(load()));
|
||||
connect(m_ui.save, SIGNAL(clicked()), this, SLOT(save()));
|
||||
connect(m_ui.addSet, SIGNAL(clicked()), this, SLOT(addSet()));
|
||||
connect(m_ui.remove, SIGNAL(clicked()), this, SLOT(removeSet()));
|
||||
connect(controller, SIGNAL(gameStopped(mCoreThread*)), this, SLOT(close()));
|
||||
connect(controller, SIGNAL(stateLoaded(mCoreThread*)), &m_model, SLOT(invalidated()));
|
||||
connect(m_ui.load, &QPushButton::clicked, this, &CheatsView::load);
|
||||
connect(m_ui.save, &QPushButton::clicked, this, &CheatsView::save);
|
||||
connect(m_ui.addSet, &QPushButton::clicked, this, &CheatsView::addSet);
|
||||
connect(m_ui.remove, &QPushButton::clicked, this, &CheatsView::removeSet);
|
||||
connect(controller, &GameController::gameStopped, this, &CheatsView::close);
|
||||
connect(controller, &GameController::stateLoaded, &m_model, &CheatsModel::invalidated);
|
||||
|
||||
QPushButton* add;
|
||||
switch (controller->platform()) {
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include <QDir>
|
||||
#include <QMenu>
|
||||
|
||||
#include "feature/commandline.h"
|
||||
#include <mgba/feature/commandline.h>
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
|
@ -76,14 +76,12 @@ void ConfigOption::setValue(const char* value) {
|
|||
}
|
||||
|
||||
void ConfigOption::setValue(const QVariant& value) {
|
||||
QPair<QAction*, QVariant> action;
|
||||
foreach (action, m_actions) {
|
||||
for (QPair<QAction*, QVariant>& action : m_actions) {
|
||||
bool signalsEnabled = action.first->blockSignals(true);
|
||||
action.first->setChecked(value == action.second);
|
||||
action.first->blockSignals(signalsEnabled);
|
||||
}
|
||||
std::function<void(const QVariant&)> slot;
|
||||
foreach(slot, m_slots.values()) {
|
||||
for (std::function<void(const QVariant&)>& slot : m_slots.values()) {
|
||||
slot(value);
|
||||
}
|
||||
}
|
||||
|
@ -92,7 +90,6 @@ QString ConfigController::s_configDir;
|
|||
|
||||
ConfigController::ConfigController(QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_opts{}
|
||||
{
|
||||
QString fileName = configDir();
|
||||
fileName.append(QDir::separator());
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
#include <mgba/core/config.h>
|
||||
#include <mgba-util/configuration.h>
|
||||
#include "feature/commandline.h"
|
||||
#include <mgba/feature/commandline.h>
|
||||
|
||||
class QAction;
|
||||
class QMenu;
|
||||
|
@ -102,7 +102,7 @@ private:
|
|||
Configuration* defaults() { return &m_config.defaultsTable; }
|
||||
|
||||
mCoreConfig m_config;
|
||||
mCoreOptions m_opts;
|
||||
mCoreOptions m_opts{};
|
||||
|
||||
QMap<QString, ConfigOption*> m_optionSet;
|
||||
QSettings* m_settings;
|
||||
|
|
|
@ -17,10 +17,10 @@ DebuggerConsole::DebuggerConsole(DebuggerConsoleController* controller, QWidget*
|
|||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
connect(m_ui.prompt, SIGNAL(returnPressed()), this, SLOT(postLine()));
|
||||
connect(controller, SIGNAL(log(const QString&)), this, SLOT(log(const QString&)));
|
||||
connect(m_ui.breakpoint, SIGNAL(clicked()), controller, SLOT(attach()));
|
||||
connect(m_ui.breakpoint, SIGNAL(clicked()), controller, SLOT(breakInto()));
|
||||
connect(m_ui.prompt, &QLineEdit::returnPressed, this, &DebuggerConsole::postLine);
|
||||
connect(controller, &DebuggerConsoleController::log, this, &DebuggerConsole::log);
|
||||
connect(m_ui.breakpoint, &QAbstractButton::clicked, controller, &DebuggerController::attach);
|
||||
connect(m_ui.breakpoint, &QAbstractButton::clicked, controller, &DebuggerController::breakInto);
|
||||
}
|
||||
|
||||
void DebuggerConsole::log(const QString& line) {
|
||||
|
|
|
@ -11,8 +11,8 @@ using namespace QGBA;
|
|||
|
||||
DebuggerController::DebuggerController(GameController* controller, mDebugger* debugger, QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_gameController(controller)
|
||||
, m_debugger(debugger)
|
||||
, m_gameController(controller)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ void DebuggerController::attach() {
|
|||
mDebuggerEnter(m_debugger, DEBUGGER_ENTER_ATTACHED, 0);
|
||||
} else {
|
||||
QObject::disconnect(m_autoattach);
|
||||
m_autoattach = connect(m_gameController, SIGNAL(gameStarted(mCoreThread*, const QString&)), this, SLOT(attach()));
|
||||
m_autoattach = connect(m_gameController, &GameController::gameStarted, this, &DebuggerController::attach);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -53,9 +53,6 @@ Display* Display::create(QWidget* parent) {
|
|||
|
||||
Display::Display(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_lockAspectRatio(false)
|
||||
, m_lockIntegerScaling(false)
|
||||
, m_filter(false)
|
||||
{
|
||||
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
|
||||
#ifdef M_CORE_GB
|
||||
|
@ -63,7 +60,7 @@ Display::Display(QWidget* parent)
|
|||
#elif defined(M_CORE_GBA)
|
||||
setMinimumSize(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
|
||||
#endif
|
||||
connect(&m_mouseTimer, SIGNAL(timeout()), this, SIGNAL(hideCursor()));
|
||||
connect(&m_mouseTimer, &QTimer::timeout, this, &Display::hideCursor);
|
||||
m_mouseTimer.setSingleShot(true);
|
||||
m_mouseTimer.setInterval(MOUSE_DISAPPEAR_TIMER);
|
||||
setMouseTracking(true);
|
||||
|
|
|
@ -79,9 +79,9 @@ private:
|
|||
static const int MOUSE_DISAPPEAR_TIMER = 1000;
|
||||
|
||||
MessagePainter m_messagePainter;
|
||||
bool m_lockAspectRatio;
|
||||
bool m_lockIntegerScaling;
|
||||
bool m_filter;
|
||||
bool m_lockAspectRatio = false;
|
||||
bool m_lockIntegerScaling = false;
|
||||
bool m_filter = false;
|
||||
QTimer m_mouseTimer;
|
||||
int m_coreWidth;
|
||||
int m_coreHeight;
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "DisplayGL.h"
|
||||
|
||||
#if defined(BUILD_GL) || defined(BUILD_GLES)
|
||||
|
||||
#include <QApplication>
|
||||
#include <QResizeEvent>
|
||||
#include <QTimer>
|
||||
|
@ -25,10 +27,7 @@ using namespace QGBA;
|
|||
|
||||
DisplayGL::DisplayGL(const QGLFormat& format, QWidget* parent)
|
||||
: Display(parent)
|
||||
, m_isDrawing(false)
|
||||
, m_gl(new EmptyGLWidget(format, this))
|
||||
, m_drawThread(nullptr)
|
||||
, m_context(nullptr)
|
||||
{
|
||||
m_painter = new PainterGL(format.majorVersion() < 2 ? 1 : m_gl->format().majorVersion(), m_gl);
|
||||
m_gl->setMouseTracking(true);
|
||||
|
@ -68,7 +67,7 @@ void DisplayGL::startDrawing(mCoreThread* thread) {
|
|||
m_gl->context()->doneCurrent();
|
||||
m_gl->context()->moveToThread(m_drawThread);
|
||||
m_painter->moveToThread(m_drawThread);
|
||||
connect(m_drawThread, SIGNAL(started()), m_painter, SLOT(start()));
|
||||
connect(m_drawThread, &QThread::started, m_painter, &PainterGL::start);
|
||||
m_drawThread->start();
|
||||
mCoreSyncSetVideoSync(&m_context->sync, false);
|
||||
|
||||
|
@ -488,3 +487,5 @@ void PainterGL::clearShaders() {
|
|||
VideoShader* PainterGL::shaders() {
|
||||
return &m_shader;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#ifndef QGBA_DISPLAY_GL
|
||||
#define QGBA_DISPLAY_GL
|
||||
|
||||
#if defined(BUILD_GL) || defined(BUILD_GLES)
|
||||
|
||||
#include "Display.h"
|
||||
|
||||
#ifdef USE_EPOXY
|
||||
|
@ -68,11 +70,11 @@ protected:
|
|||
private:
|
||||
void resizePainter();
|
||||
|
||||
bool m_isDrawing;
|
||||
bool m_isDrawing = false;
|
||||
QGLWidget* m_gl;
|
||||
PainterGL* m_painter;
|
||||
QThread* m_drawThread;
|
||||
mCoreThread* m_context;
|
||||
QThread* m_drawThread = nullptr;
|
||||
mCoreThread* m_context = nullptr;
|
||||
};
|
||||
|
||||
class PainterGL : public QObject {
|
||||
|
@ -128,3 +130,5 @@ private:
|
|||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -14,8 +14,6 @@ using namespace QGBA;
|
|||
|
||||
DisplayQt::DisplayQt(QWidget* parent)
|
||||
: Display(parent)
|
||||
, m_isDrawing(false)
|
||||
, m_backing(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -40,10 +40,10 @@ protected:
|
|||
virtual void paintEvent(QPaintEvent*) override;
|
||||
|
||||
private:
|
||||
bool m_isDrawing;
|
||||
bool m_isDrawing = false;
|
||||
unsigned m_width;
|
||||
unsigned m_height;
|
||||
QImage m_backing;
|
||||
QImage m_backing{nullptr};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -33,7 +33,6 @@ mLOG_DEFINE_CATEGORY(QT, "Qt", "platform.qt");
|
|||
|
||||
GBAApp::GBAApp(int& argc, char* argv[])
|
||||
: QApplication(argc, argv)
|
||||
, m_db(nullptr)
|
||||
{
|
||||
g_app = this;
|
||||
|
||||
|
@ -127,6 +126,9 @@ Window* GBAApp::newWindow() {
|
|||
int windowId = m_multiplayer.attached();
|
||||
connect(w, &Window::destroyed, [this, w]() {
|
||||
m_windows.removeAll(w);
|
||||
for (Window* w : m_windows) {
|
||||
w->updateMultiplayerStatus(m_windows.count() < MAX_GBAS);
|
||||
}
|
||||
});
|
||||
m_windows.append(w);
|
||||
w->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
@ -134,6 +136,9 @@ Window* GBAApp::newWindow() {
|
|||
w->show();
|
||||
w->controller()->setMultiplayerController(&m_multiplayer);
|
||||
w->multiplayerChanged();
|
||||
for (Window* w : m_windows) {
|
||||
w->updateMultiplayerStatus(m_windows.count() < MAX_GBAS);
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue