Merge branch 'master' into feature/input-revamp

This commit is contained in:
Vicki Pfau 2017-06-03 17:24:05 -07:00
commit c784fad04a
213 changed files with 6967 additions and 2573 deletions

47
CHANGES
View File

@ -12,6 +12,17 @@ Features:
- Add option for whether rewinding restores save games
- Qt: German translation (by Lothar Serra Mari)
- Savestates now contain any RTC override data
- Command line ability to override configuration values
- Add option to allow preloading the entire ROM before running
- GB: Video/audio channel enabling/disabling
- Add option to lock video to integer scaling
- Video log recording for testing and bug reporting
- Library view
- Debugger: Segment/bank support
- GB: Symbol table support
- GB MBC: Add MBC1 multicart support
- GBA: Implement keypad interrupts
- LR35902: Watchpoints
Bugfixes:
- LR35902: Fix core never exiting with certain event patterns
- GB Timer: Improve DIV reset behavior
@ -31,6 +42,28 @@ Bugfixes:
- Qt: Fix linking after some windows have been closed
- GBA Video: Fix wrong palette on 256-color sprites in OBJWIN
- Windows: Fix VDir.rewind
- SDL: Fix game crash check
- SDL: Fix race condition with audio thread when starting
- GB: Fix flickering when screen is strobed quickly
- FFmpeg: Fix overflow and general issues with audio encoding
- Qt: Fix crash when changing audio settings after a game is closed
- GBA BIOS: Fix ArcTan sign in HLE BIOS
- GBA BIOS: Fix ArcTan2 sign in HLE BIOS (fixes mgba.io/i/689)
- GBA Video: Don't update background scanline params in mode 0 (fixes mgba.io/i/377)
- Qt: Ensure CLI backend is attached when submitting commands (fixes mgba.io/i/662)
- Core: Fix crash with rewind if savestates shrink
- Test: Fix crash when loading invalid file
- GBA Hardware: Fix crash if a savestate lies about game hardware
- Test: Fix crash when fuzzing fails to load a file
- GBA: Fix multiboot loading resulting in too small WRAM
- Test: Don't rely on core for frames elapsed
- Test: Fix crash when loading invalid file
- GBA Hardware: Fix crash if a savestate lies about game hardware
- Test: Fix crash when fuzzing fails to load a file
- Qt: Disable "New multiplayer window" when MAX_GBAS is reached (fixes mgba.io/i/107)
- LR35902: Fix decoding LD r, $imm and 0-valued immediates (fixes mgba.io/i/735)
- GB: Fix STAT blocking
- GB MBC: Fix swapping carts not detect new MBC
Misc:
- SDL: Remove scancode key input
- GBA Video: Clean up unused timers
@ -77,6 +110,20 @@ Misc:
- Qt: Remove audio thread
- Qt: Remove audio buffer sizing in AudioProcessorQt
- Qt: Re-enable QtMultimedia on Windows
- FFmpeg: Return false if a file fails to open
- FFmpeg: Force MP4 files to YUV420P
- Qt: Make "Mute" able to be bound to a key
- Core: Restore sleep callback
- Qt: Add .gb/.gbc files to the extension list in Info.plist
- Feature: Make -l option explicit
- Core: Ability to enumerate and modify video and audio channels
- Debugger: Make attaching a backend idempotent
- VFS: Optimize expanding in-memory files
- VFS: Add VFileFIFO for operating on circle buffers
- Core: Move rewind diffing to its own thread
- Util: Tune patch-fast extent sizes
- Qt: Relax hard dependency on OpenGL
- GB Video: Improved video timings
0.5.2: (2016-12-31)
Bugfixes:

View File

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 2.6)
project(mGBA C)
cmake_minimum_required(VERSION 2.8.11)
project(mGBA)
set(BINARY_NAME mgba CACHE INTERNAL "Name of output binaries")
if(NOT MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-missing-field-initializers -std=c99")
@ -54,11 +54,13 @@ file(GLOB UTIL_TEST_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/test/*.c)
file(GLOB GUI_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/gui/*.c ${CMAKE_CURRENT_SOURCE_DIR}/src/feature/gui/*.c)
file(GLOB GBA_RENDERER_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/renderers/*.c)
file(GLOB GBA_SIO_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/sio/lockstep.c)
file(GLOB GBA_EXTRA_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/extra/*.c)
file(GLOB GB_SIO_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/sio/lockstep.c)
file(GLOB GB_RENDERER_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/renderers/*.c)
file(GLOB GB_EXTRA_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/extra/*.c)
file(GLOB THIRD_PARTY_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/third-party/inih/*.c)
set(CLI_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/feature/commandline.c)
set(CORE_VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-mem.c)
file(GLOB EXTRA_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/feature/*.c)
set(CORE_VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-mem.c ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-fifo.c)
set(VFS_SRC)
source_group("ARM core" FILES ${ARM_SRC})
source_group("LR35902 core" FILES ${LR35902_SRC})
@ -301,6 +303,7 @@ else()
endif()
set(DISABLE_FRONTENDS ON)
set(MINIMAL_CORE ON)
set(ENABLE_EXTRA ON)
endif()
check_function_exists(chmod HAVE_CHMOD)
@ -392,6 +395,7 @@ find_feature(USE_SQLITE3 "sqlite3")
set(DEBUGGER_SRC
${CMAKE_CURRENT_SOURCE_DIR}/src/debugger/debugger.c
${CMAKE_CURRENT_SOURCE_DIR}/src/debugger/parser.c
${CMAKE_CURRENT_SOURCE_DIR}/src/debugger/symbols.c
${CMAKE_CURRENT_SOURCE_DIR}/src/debugger/cli-debugger.c)
set(FEATURE_SRC)
@ -600,7 +604,9 @@ if(M_CORE_GB)
list(APPEND DEBUGGER_SRC
${CMAKE_CURRENT_SOURCE_DIR}/src/lr35902/debugger/cli-debugger.c
${CMAKE_CURRENT_SOURCE_DIR}/src/lr35902/debugger/debugger.c
${CMAKE_CURRENT_SOURCE_DIR}/src/gb/extra/cli.c)
${CMAKE_CURRENT_SOURCE_DIR}/src/lr35902/debugger/memory-debugger.c
${CMAKE_CURRENT_SOURCE_DIR}/src/gb/debugger/cli.c
${CMAKE_CURRENT_SOURCE_DIR}/src/gb/debugger/symbols.c)
list(APPEND TEST_SRC
${LR35902_TEST_SRC}
${GB_TEST_SRC})
@ -617,7 +623,7 @@ if(M_CORE_GBA)
${CMAKE_CURRENT_SOURCE_DIR}/src/arm/debugger/cli-debugger.c
${CMAKE_CURRENT_SOURCE_DIR}/src/arm/debugger/debugger.c
${CMAKE_CURRENT_SOURCE_DIR}/src/arm/debugger/memory-debugger.c
${CMAKE_CURRENT_SOURCE_DIR}/src/gba/extra/cli.c)
${CMAKE_CURRENT_SOURCE_DIR}/src/gba/debugger/cli.c)
list(APPEND TEST_SRC
${ARM_TEST_SRC}
${GBA_TEST_SRC})
@ -662,10 +668,9 @@ list(APPEND TEST_SRC ${UTIL_TEST_SRC})
set(SRC ${CORE_SRC} ${VFS_SRC})
if(NOT MINIMAL_CORE)
set(ENABLE_EXTRA ON)
if(M_CORE_GBA)
list(APPEND SRC
${GBA_RR_SRC}
${GBA_EXTRA_SRC}
${GBA_SIO_SRC})
endif()
if(M_CORE_GB)
@ -673,8 +678,21 @@ if(NOT MINIMAL_CORE)
${GB_SIO_SRC})
endif()
list(APPEND SRC
${FEATURE_SRC}
${CLI_SRC})
${FEATURE_SRC})
endif()
if(ENABLE_EXTRA)
if(M_CORE_GBA)
list(APPEND SRC
${GBA_RR_SRC}
${GBA_EXTRA_SRC})
endif()
if(M_CORE_GB)
list(APPEND SRC
${GB_EXTRA_SRC})
endif()
list(APPEND SRC
${EXTRA_SRC})
endif()
if(NOT SKIP_LIBRARY)
@ -771,7 +789,7 @@ if(BUILD_QT)
endif()
if(BUILD_PERF)
set(PERF_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/test/perf-main.c ${CLI_SRC})
set(PERF_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/test/perf-main.c)
if(UNIX AND NOT APPLE)
list(APPEND PERF_LIB rt)
endif()

View File

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

View File

@ -56,6 +56,11 @@ typedef intptr_t ssize_t;
#include <sys/time.h>
#endif
#ifdef PSP2
// For PATH_MAX on modern toolchains
#include <sys/syslimits.h>
#endif
#ifndef SSIZE_MAX
#define SSIZE_MAX ((ssize_t) (SIZE_MAX >> 1))
#endif

View File

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

View File

@ -14,6 +14,8 @@ CXX_GUARD_START
#include <sys/time.h>
#if defined(__FreeBSD__) || defined(__OpenBSD__)
#include <pthread_np.h>
#elif defined(__HAIKU__)
#include <OS.h>
#endif
#define THREAD_ENTRY void*
@ -88,6 +90,9 @@ static inline int ThreadSetName(const char* name) {
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
pthread_set_name_np(pthread_self(), name);
return 0;
#elif defined(__HAIKU__)
rename_thread(find_thread(NULL), name);
return 0;
#elif !defined(BUILD_PANDORA) // Pandora's glibc is too old
return pthread_setname_np(pthread_self(), name);
#else

View File

@ -92,6 +92,8 @@ CXX_GUARD_START
dest->size = src->size; \
} \
DECLARE_VECTOR(StringList, char*);
CXX_GUARD_END
#endif

View File

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

View File

@ -48,7 +48,6 @@ struct mCheat {
mLOG_DECLARE_CATEGORY(CHEATS);
DECLARE_VECTOR(mCheatList, struct mCheat);
DECLARE_VECTOR(StringList, char*);
struct mCheatDevice;
struct mCheatSet {

View File

@ -42,6 +42,7 @@ struct mCoreOptions {
int width;
int height;
bool lockAspectRatio;
bool lockIntegerScaling;
bool resampleVideo;
bool suspendScreensaver;
char* shader;

View File

@ -26,10 +26,10 @@ CXX_GUARD_START
enum mPlatform {
PLATFORM_NONE = -1,
#ifdef M_CORE_GBA
PLATFORM_GBA,
PLATFORM_GBA = 0,
#endif
#ifdef M_CORE_GB
PLATFORM_GB,
PLATFORM_GB = 1,
#endif
};
@ -39,11 +39,14 @@ enum mCoreChecksumType {
struct mCoreConfig;
struct mCoreSync;
struct mDebuggerSymbols;
struct mStateExtdata;
struct mVideoLogContext;
struct mCore {
void* cpu;
void* board;
struct mDebugger* debugger;
struct mDebuggerSymbols* symbolTable;
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
struct mDirectorySet dirs;
@ -111,8 +114,7 @@ struct mCore {
void (*getGameTitle)(const struct mCore*, char* title);
void (*getGameCode)(const struct mCore*, char* title);
void (*setRotation)(struct mCore*, struct mRotationSource*);
void (*setRumble)(struct mCore*, struct mRumble*);
void (*setPeripheral)(struct mCore*, int type, void*);
uint32_t (*busRead8)(struct mCore*, uint32_t address);
uint32_t (*busRead16)(struct mCore*, uint32_t address);
@ -136,18 +138,33 @@ struct mCore {
struct CLIDebuggerSystem* (*cliDebuggerSystem)(struct mCore*);
void (*attachDebugger)(struct mCore*, struct mDebugger*);
void (*detachDebugger)(struct mCore*);
void (*loadSymbols)(struct mCore*, struct VFile*);
#endif
struct mCheatDevice* (*cheatDevice)(struct mCore*);
size_t (*savedataClone)(struct mCore*, void** sram);
bool (*savedataRestore)(struct mCore*, const void* sram, size_t size, bool writeback);
size_t (*listVideoLayers)(const struct mCore*, const struct mCoreChannelInfo**);
size_t (*listAudioChannels)(const struct mCore*, const struct mCoreChannelInfo**);
void (*enableVideoLayer)(struct mCore*, size_t id, bool enable);
void (*enableAudioChannel)(struct mCore*, size_t id, bool enable);
#ifndef MINIMAL_CORE
void (*startVideoLog)(struct mCore*, struct mVideoLogContext*);
void (*endVideoLog)(struct mCore*);
#endif
};
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
struct mCore* mCoreFind(const char* path);
bool mCoreLoadFile(struct mCore* core, const char* path);
bool mCorePreloadVF(struct mCore* core, struct VFile* vf);
bool mCorePreloadFile(struct mCore* core, const char* path);
bool mCoreAutoloadSave(struct mCore* core);
bool mCoreAutoloadPatch(struct mCore* core);

View File

@ -38,6 +38,7 @@ struct mCoreCallbacks {
void (*videoFrameStarted)(void* context);
void (*videoFrameEnded)(void* context);
void (*coreCrashed)(void* context);
void (*sleep)(void* context);
};
DECLARE_VECTOR(mCoreCallbacksList, struct mCoreCallbacks);
@ -53,8 +54,10 @@ struct mKeyCallback {
uint16_t (*readKeys)(struct mKeyCallback*);
};
struct mStopCallback {
void (*stop)(struct mStopCallback*);
enum mPeripheral {
mPERIPH_ROTATION = 1,
mPERIPH_RUMBLE,
mPERIPH_CUSTOM = 0x1000
};
struct mRotationSource {
@ -102,6 +105,13 @@ struct mRumble {
void (*setRumble)(struct mRumble*, int enable);
};
struct mCoreChannelInfo {
size_t id;
const char* internalName;
const char* visibleName;
const char* visibleType;
};
CXX_GUARD_END
#endif

View File

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

View File

@ -62,6 +62,7 @@ struct mCoreThread {
ThreadCallback resetCallback;
ThreadCallback cleanCallback;
ThreadCallback frameCallback;
ThreadCallback sleepCallback;
void* userData;
void (*run)(struct mCoreThread*);

View File

@ -10,6 +10,8 @@
CXX_GUARD_START
#include <mgba-util/table.h>
#include <mgba/internal/debugger/debugger.h>
struct mArguments {
@ -21,6 +23,8 @@ struct mArguments {
int logLevel;
int frameskip;
struct Table configOverrides;
enum mDebuggerType debuggerType;
bool debugAtStart;
bool showHelp;

View File

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

View File

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

View File

@ -12,6 +12,9 @@ CXX_GUARD_START
struct mCore;
struct mCore* GBCoreCreate(void);
#ifndef MINIMAL_CORE
struct mCore* GBVideoLogPlayerCreate(void);
#endif
CXX_GUARD_END

View File

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

View File

@ -12,6 +12,9 @@ CXX_GUARD_START
struct mCore;
struct mCore* GBACoreCreate(void);
#ifndef MINIMAL_CORE
struct mCore* GBAVideoLogPlayerCreate(void);
#endif
CXX_GUARD_END

View File

@ -28,6 +28,10 @@ struct GBAVideoRenderer;
extern const int GBA_LUX_LEVELS[10];
enum {
mPERIPH_GBA_LUMINANCE = 0x1000
};
struct GBALuminanceSource {
void (*sample)(struct GBALuminanceSource*);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -20,9 +20,9 @@ enum {
GB_VIDEO_VERTICAL_TOTAL_PIXELS = 154,
// TODO: Figure out exact lengths
GB_VIDEO_MODE_2_LENGTH = 76,
GB_VIDEO_MODE_3_LENGTH_BASE = 171,
GB_VIDEO_MODE_0_LENGTH_BASE = 209,
GB_VIDEO_MODE_2_LENGTH = 80,
GB_VIDEO_MODE_3_LENGTH_BASE = 172,
GB_VIDEO_MODE_0_LENGTH_BASE = 204,
GB_VIDEO_HORIZONTAL_LENGTH = 456,
@ -61,6 +61,7 @@ struct GBVideoRenderer {
uint8_t (*writeVideoRegister)(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
void (*writeVRAM)(struct GBVideoRenderer* renderer, uint16_t address);
void (*writePalette)(struct GBVideoRenderer* renderer, int index, uint16_t value);
void (*writeOAM)(struct GBVideoRenderer* renderer, uint16_t oam);
void (*drawRange)(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* objOnLine, size_t nObj);
void (*finishScanline)(struct GBVideoRenderer* renderer, int y);
void (*finishFrame)(struct GBVideoRenderer* renderer);
@ -71,6 +72,10 @@ struct GBVideoRenderer {
uint8_t* vram;
union GBOAM* oam;
struct mTileCache* cache;
bool disableBG;
bool disableOBJ;
bool disableWIN;
};
DECL_BITFIELD(GBRegisterLCDC, uint8_t);
@ -119,6 +124,7 @@ struct GBVideo {
int ocpIndex;
bool ocpIncrement;
uint16_t dmgPalette[4];
uint16_t palette[64];
int32_t frameCounter;
@ -138,6 +144,8 @@ void GBVideoWriteLYC(struct GBVideo* video, uint8_t value);
void GBVideoWritePalette(struct GBVideo* video, uint16_t address, uint8_t value);
void GBVideoSwitchBank(struct GBVideo* video, uint8_t value);
void GBVideoSetPalette(struct GBVideo* video, unsigned index, uint16_t color);
struct GBSerializedState;
void GBVideoSerialize(const struct GBVideo* video, struct GBSerializedState* state);
void GBVideoDeserialize(struct GBVideo* video, const struct GBSerializedState* state);

View File

@ -99,7 +99,6 @@ struct GBA {
struct mAVStream* stream;
struct mKeyCallback* keyCallback;
struct mStopCallback* stopCallback;
struct mCoreCallbacksList coreCallbacks;
enum GBAIdleLoopOptimization idleOptimization;
@ -176,6 +175,8 @@ bool GBAIsBIOS(struct VFile* vf);
void GBAGetGameCode(const struct GBA* gba, char* out);
void GBAGetGameTitle(const struct GBA* gba, char* out);
void GBATestKeypadIRQ(struct GBA* gba);
void GBAFrameStarted(struct GBA* gba);
void GBAFrameEnded(struct GBA* gba);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -44,6 +44,17 @@
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>gb</string>
<string>gbc</string>
</array>
<key>CFBundleTypeName</key>
<string>Game Boy ROM Image</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
</array>
</dict>
</plist>

View File

@ -1,7 +1,9 @@
Trey Boyer
Christopher Cole
Jaime J. Denizard
Fog
Reilly Grant
Philip Horton
Jordan Jorgensen
Joshua Minor
Rohit Nirmal
Rhys Powell
rootfather
Yuri Kunde Schlesner

View File

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

View File

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

View File

@ -0,0 +1,34 @@
varying vec2 texCoord;
uniform sampler2D tex;
uniform vec2 texSize;
uniform float darken_screen;
const float target_gamma = 2.2;
const float display_gamma = 2.5;
const float sat = 1.0;
const float lum = 0.99;
const float contrast = 1.0;
const vec3 bl = vec3(0.0, 0.0, 0.0);
const vec3 r = vec3(0.84, 0.09, 0.15);
const vec3 g = vec3(0.18, 0.67, 0.10);
const vec3 b = vec3(0.0, 0.26, 0.73);
void main() {
vec4 screen = pow(texture2D(tex, texCoord), vec4(target_gamma + darken_screen)).rgba;
vec4 avglum = vec4(0.5);
screen = mix(screen, avglum, (1.0 - contrast));
mat4 color = mat4( r.r, r.g, r.b, 0.0,
g.r, g.g, g.b, 0.0,
b.r, b.g, b.b, 0.0,
bl.r, bl.g, bl.b, 1.0);
mat4 adjust = mat4( (1.0 - sat) * 0.3086 + sat, (1.0 - sat) * 0.3086, (1.0 - sat) * 0.3086, 1.0,
(1.0 - sat) * 0.6094, (1.0 - sat) * 0.6094 + sat, (1.0 - sat) * 0.6094, 1.0,
(1.0 - sat) * 0.0820, (1.0 - sat) * 0.0820, (1.0 - sat) * 0.0820 + sat, 1.0,
0.0, 0.0, 0.0, 1.0);
color *= adjust;
screen = clamp(screen * lum, 0.0, 1.0);
screen = color * screen;
gl_FragColor = pow(screen, vec4(1.0 / display_gamma + (darken_screen * 0.125)));
}

View File

@ -0,0 +1,14 @@
[shader]
name=GBA Color
author=Pokefan531 and hunterk
description=Modifies the color output to simulate the GBA LCD characteristics.
passes=1
[pass.0]
fragmentShader=gba-color.fs
blend=1
[pass.0.uniform.darken_screen]
type=float
default=0.5
readableName=Darken Screen

View File

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

View File

@ -17,7 +17,6 @@ mLOG_DEFINE_CATEGORY(CHEATS, "Cheats", "core.cheats");
DEFINE_VECTOR(mCheatList, struct mCheat);
DEFINE_VECTOR(mCheatSets, struct mCheatSet*);
DEFINE_VECTOR(StringList, char*);
static int32_t _readMem(struct mCore* core, uint32_t address, int width) {
switch (width) {

View File

@ -349,6 +349,9 @@ void mCoreConfigMap(const struct mCoreConfig* config, struct mCoreOptions* opts)
if (_lookupIntValue(config, "lockAspectRatio", &fakeBool)) {
opts->lockAspectRatio = fakeBool;
}
if (_lookupIntValue(config, "lockIntegerScaling", &fakeBool)) {
opts->lockIntegerScaling = fakeBool;
}
if (_lookupIntValue(config, "resampleVideo", &fakeBool)) {
opts->resampleVideo = fakeBool;
}
@ -396,6 +399,7 @@ void mCoreConfigLoadDefaults(struct mCoreConfig* config, const struct mCoreOptio
ConfigurationSetIntValue(&config->defaultsTable, 0, "volume", opts->volume);
ConfigurationSetIntValue(&config->defaultsTable, 0, "mute", opts->mute);
ConfigurationSetIntValue(&config->defaultsTable, 0, "lockAspectRatio", opts->lockAspectRatio);
ConfigurationSetIntValue(&config->defaultsTable, 0, "lockIntegerScaling", opts->lockIntegerScaling);
ConfigurationSetIntValue(&config->defaultsTable, 0, "resampleVideo", opts->resampleVideo);
ConfigurationSetIntValue(&config->defaultsTable, 0, "suspendScreensaver", opts->suspendScreensaver);
}

View File

@ -18,8 +18,11 @@
#include <mgba/gba/core.h>
#include <mgba/internal/gba/gba.h>
#endif
#ifndef MINIMAL_CORE
#include <mgba/feature/video-logger.h>
#endif
static struct mCoreFilter {
const static struct mCoreFilter {
bool (*filter)(struct VFile*);
struct mCore* (*open)(void);
enum mPlatform platform;
@ -37,7 +40,7 @@ struct mCore* mCoreFindVF(struct VFile* vf) {
if (!vf) {
return NULL;
}
struct mCoreFilter* filter;
const struct mCoreFilter* filter;
for (filter = &_filters[0]; filter->filter; ++filter) {
if (filter->filter(vf)) {
break;
@ -46,6 +49,9 @@ struct mCore* mCoreFindVF(struct VFile* vf) {
if (filter->open) {
return filter->open();
}
#ifndef MINIMAL_CORE
return mVideoLogCoreFind(vf);
#endif
return NULL;
}
@ -53,7 +59,7 @@ enum mPlatform mCoreIsCompatible(struct VFile* vf) {
if (!vf) {
return false;
}
struct mCoreFilter* filter;
const struct mCoreFilter* filter;
for (filter = &_filters[0]; filter->filter; ++filter) {
if (filter->filter(vf)) {
return filter->platform;
@ -115,6 +121,35 @@ bool mCoreLoadFile(struct mCore* core, const char* path) {
return ret;
}
bool mCorePreloadVF(struct mCore* core, struct VFile* vf) {
struct VFile* vfm = VFileMemChunk(NULL, vf->size(vf));
uint8_t buffer[2048];
ssize_t read;
vf->seek(vf, 0, SEEK_SET);
while ((read = vf->read(vf, buffer, sizeof(buffer))) > 0) {
vfm->write(vfm, buffer, read);
}
vf->close(vf);
bool ret = core->loadROM(core, vfm);
if (!ret) {
vfm->close(vfm);
}
return ret;
}
bool mCorePreloadFile(struct mCore* core, const char* path) {
struct VFile* rom = mDirectorySetOpenPath(&core->dirs, path, core->isROM);
if (!rom) {
return false;
}
bool ret = mCorePreloadVF(core, rom);
if (!ret) {
rom->close(rom);
}
return ret;
}
bool mCoreAutoloadSave(struct mCore* core) {
return core->loadSave(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.save, ".sav", O_CREAT | O_RDWR));
}

View File

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

View File

@ -12,7 +12,13 @@
DEFINE_VECTOR(mCoreRewindPatches, struct PatchFast);
void mCoreRewindContextInit(struct mCoreRewindContext* context, size_t entries) {
void _rewindDiff(struct mCoreRewindContext* context);
#ifndef DISABLE_THREADING
THREAD_ENTRY _rewindThread(void* context);
#endif
void mCoreRewindContextInit(struct mCoreRewindContext* context, size_t entries, bool onThread) {
mCoreRewindPatchesInit(&context->patchMemory, entries);
size_t e;
for (e = 0; e < entries; ++e) {
@ -22,9 +28,30 @@ void mCoreRewindContextInit(struct mCoreRewindContext* context, size_t entries)
context->currentState = VFileMemChunk(0, 0);
context->size = 0;
context->stateFlags = SAVESTATE_SAVEDATA;
#ifndef DISABLE_THREADING
context->onThread = onThread;
if (onThread) {
MutexInit(&context->mutex);
ConditionInit(&context->cond);
ThreadCreate(&context->thread, _rewindThread, context);
}
#else
UNUSED(onThread);
#endif
}
void mCoreRewindContextDeinit(struct mCoreRewindContext* context) {
#ifndef DISABLE_THREADING
if (context->onThread) {
MutexLock(&context->mutex);
context->onThread = false;
MutexUnlock(&context->mutex);
ConditionWake(&context->cond);
ThreadJoin(context->thread);
MutexDeinit(&context->mutex);
ConditionDeinit(&context->cond);
}
#endif
context->previousState->close(context->previousState);
context->currentState->close(context->currentState);
size_t s;
@ -35,7 +62,26 @@ void mCoreRewindContextDeinit(struct mCoreRewindContext* context) {
}
void mCoreRewindAppend(struct mCoreRewindContext* context, struct mCore* core) {
#ifndef DISABLE_THREADING
if (context->onThread) {
MutexLock(&context->mutex);
}
#endif
struct VFile* nextState = context->previousState;
mCoreSaveStateNamed(core, nextState, context->stateFlags);
context->previousState = context->currentState;
context->currentState = nextState;
#ifndef DISABLE_THREADING
if (context->onThread) {
ConditionWake(&context->cond);
MutexUnlock(&context->mutex);
return;
}
#endif
_rewindDiff(context);
}
void _rewindDiff(struct mCoreRewindContext* context) {
++context->current;
if (context->size < mCoreRewindPatchesSize(&context->patchMemory)) {
++context->size;
@ -43,25 +89,34 @@ void mCoreRewindAppend(struct mCoreRewindContext* context, struct mCore* core) {
if (context->current >= mCoreRewindPatchesSize(&context->patchMemory)) {
context->current = 0;
}
mCoreSaveStateNamed(core, nextState, context->stateFlags);
struct PatchFast* patch = mCoreRewindPatchesGetPointer(&context->patchMemory, context->current);
size_t size2 = nextState->size(nextState);
size_t size = context->currentState->size(context->currentState);
size_t size2 = context->currentState->size(context->currentState);
size_t size = context->previousState->size(context->previousState);
if (size2 > size) {
context->currentState->truncate(context->currentState, size2);
context->previousState->truncate(context->previousState, size2);
size = size2;
} else if (size > size2) {
context->currentState->truncate(context->currentState, size);
}
void* current = context->currentState->map(context->currentState, size, MAP_READ);
void* next = nextState->map(nextState, size, MAP_READ);
void* current = context->previousState->map(context->previousState, size, MAP_READ);
void* next = context->currentState->map(context->currentState, size, MAP_READ);
diffPatchFast(patch, current, next, size);
context->currentState->unmap(context->currentState, current, size);
nextState->unmap(next, nextState, size);
context->previousState = context->currentState;
context->currentState = nextState;
context->previousState->unmap(context->previousState, current, size);
context->currentState->unmap(context->currentState, next, size);
}
bool mCoreRewindRestore(struct mCoreRewindContext* context, struct mCore* core) {
#ifndef DISABLE_THREADING
if (context->onThread) {
MutexLock(&context->mutex);
}
#endif
if (!context->size) {
#ifndef DISABLE_THREADING
if (context->onThread) {
MutexUnlock(&context->mutex);
}
#endif
return false;
}
--context->size;
@ -86,5 +141,29 @@ bool mCoreRewindRestore(struct mCoreRewindContext* context, struct mCore* core)
context->current = mCoreRewindPatchesSize(&context->patchMemory);
}
--context->current;
#ifndef DISABLE_THREADING
if (context->onThread) {
MutexUnlock(&context->mutex);
}
#endif
return true;
}
#ifndef DISABLE_THREADING
THREAD_ENTRY _rewindThread(void* context) {
struct mCoreRewindContext* rewindContext = context;
ThreadSetName("Rewind Diff Thread");
MutexLock(&rewindContext->mutex);
struct VFile* state = rewindContext->currentState;
while (rewindContext->onThread) {
if (rewindContext->currentState != state) {
_rewindDiff(rewindContext);
state = rewindContext->currentState;
}
ConditionWait(&rewindContext->cond, &rewindContext->mutex);
}
MutexUnlock(&rewindContext->mutex);
return 0;
}
#endif

View File

@ -120,6 +120,16 @@ void _crashed(void* context) {
_changeState(thread, THREAD_CRASHED, true);
}
void _coreSleep(void* context) {
struct mCoreThread* thread = context;
if (!thread) {
return;
}
if (thread->sleepCallback) {
thread->sleepCallback(thread);
}
}
static THREAD_ENTRY _mCoreThreadRun(void* context) {
struct mCoreThread* threadContext = context;
#ifdef USE_PTHREADS
@ -143,6 +153,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
.videoFrameStarted = _frameStarted,
.videoFrameEnded = _frameEnded,
.coreCrashed = _crashed,
.sleep = _coreSleep,
.context = threadContext
};
core->addCoreCallbacks(core, &callbacks);
@ -157,7 +168,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
}
if (core->opts.rewindEnable && core->opts.rewindBufferCapacity > 0) {
mCoreRewindContextInit(&threadContext->rewind, core->opts.rewindBufferCapacity);
mCoreRewindContextInit(&threadContext->rewind, core->opts.rewindBufferCapacity, true);
threadContext->rewind.stateFlags = core->opts.rewindSave ? SAVESTATE_SAVEDATA : 0;
}

View File

@ -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;
}
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;
}
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;
if (dv->segmentValue >= 0) {
debugger->d.core->rawWrite32(debugger->d.core, address, value, dv->segmentValue);
} else {
debugger->d.core->busWrite32(debugger->d.core, address, value);
}
}
static void _dumpByte(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
@ -306,7 +339,12 @@ static void _dumpByte(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
}
debugger->backend->printf(debugger->backend, "0x%08X:", address);
for (; line > 0; --line, ++address, --words) {
uint32_t value = debugger->d.core->busRead8(debugger->d.core, address);
uint32_t value;
if (dv->segmentValue >= 0) {
value = debugger->d.core->rawRead8(debugger->d.core, address, dv->segmentValue);
} else {
value = debugger->d.core->busRead8(debugger->d.core, address);
}
debugger->backend->printf(debugger->backend, " %02X", value);
}
debugger->backend->printf(debugger->backend, "\n");
@ -330,7 +368,12 @@ static void _dumpHalfword(struct CLIDebugger* debugger, struct CLIDebugVector* d
}
debugger->backend->printf(debugger->backend, "0x%08X:", address);
for (; line > 0; --line, address += 2, --words) {
uint32_t value = debugger->d.core->busRead16(debugger->d.core, address);
uint32_t value;
if (dv->segmentValue >= 0) {
value = debugger->d.core->rawRead16(debugger->d.core, address, dv->segmentValue);
} else {
value = debugger->d.core->busRead16(debugger->d.core, address);
}
debugger->backend->printf(debugger->backend, " %04X", value);
}
debugger->backend->printf(debugger->backend, "\n");
@ -354,7 +397,12 @@ static void _dumpWord(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
}
debugger->backend->printf(debugger->backend, "0x%08X:", address);
for (; line > 0; --line, address += 4, --words) {
uint32_t value = debugger->d.core->busRead32(debugger->d.core, address);
uint32_t value;
if (dv->segmentValue >= 0) {
value = debugger->d.core->rawRead32(debugger->d.core, address, dv->segmentValue);
} else {
value = debugger->d.core->busRead32(debugger->d.core, address);
}
debugger->backend->printf(debugger->backend, " %08X", value);
}
debugger->backend->printf(debugger->backend, "\n");
@ -367,7 +415,7 @@ static void _setBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector*
return;
}
uint32_t address = dv->intValue;
debugger->d.platform->setBreakpoint(debugger->d.platform, address);
debugger->d.platform->setBreakpoint(debugger->d.platform, address, dv->segmentValue);
}
static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
@ -380,7 +428,7 @@ static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector*
return;
}
uint32_t address = dv->intValue;
debugger->d.platform->setWatchpoint(debugger->d.platform, address, WATCHPOINT_RW);
debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_RW);
}
static void _setReadWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
@ -393,7 +441,7 @@ static void _setReadWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVect
return;
}
uint32_t address = dv->intValue;
debugger->d.platform->setWatchpoint(debugger->d.platform, address, WATCHPOINT_READ);
debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_READ);
}
static void _setWriteWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
@ -406,7 +454,7 @@ static void _setWriteWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVec
return;
}
uint32_t address = dv->intValue;
debugger->d.platform->setWatchpoint(debugger->d.platform, address, WATCHPOINT_WRITE);
debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_WRITE);
}
static void _clearBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
@ -415,9 +463,9 @@ static void _clearBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector
return;
}
uint32_t address = dv->intValue;
debugger->d.platform->clearBreakpoint(debugger->d.platform, address);
debugger->d.platform->clearBreakpoint(debugger->d.platform, address, dv->segmentValue);
if (debugger->d.platform->clearWatchpoint) {
debugger->d.platform->clearWatchpoint(debugger->d.platform, address);
debugger->d.platform->clearWatchpoint(debugger->d.platform, address, dv->segmentValue);
}
}
@ -455,7 +503,11 @@ static uint32_t _performOperation(enum Operation operation, uint32_t current, ui
static void _lookupIdentifier(struct mDebugger* debugger, const char* name, struct CLIDebugVector* dv) {
struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger;
if (cliDebugger->system) {
uint32_t value = cliDebugger->system->lookupPlatformIdentifier(cliDebugger->system, name, dv);
uint32_t value;
if (debugger->core->symbolTable && mDebuggerSymbolLookup(debugger->core->symbolTable, name, &dv->intValue, &dv->segmentValue)) {
return;
}
value = cliDebugger->system->lookupPlatformIdentifier(cliDebugger->system, name, dv);
if (dv->type != CLIDV_ERROR_TYPE) {
dv->intValue = value;
return;
@ -751,6 +803,9 @@ void CLIDebuggerAttachSystem(struct CLIDebugger* debugger, struct CLIDebuggerSys
}
void CLIDebuggerAttachBackend(struct CLIDebugger* debugger, struct CLIDebuggerBackend* backend) {
if (debugger->backend == backend) {
return;
}
if (debugger->backend && debugger->backend->deinit) {
debugger->backend->deinit(debugger->backend);
}

View File

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

View File

@ -321,13 +321,31 @@ static void _readGPRs(struct GDBStub* stub, const char* message) {
UNUSED(message);
int r;
int i = 0;
// General purpose registers
for (r = 0; r < ARM_PC; ++r) {
_int2hex32(cpu->gprs[r], &stub->outgoing[i]);
i += 8;
}
// Program counter
_int2hex32(cpu->gprs[ARM_PC] - (cpu->cpsr.t ? WORD_SIZE_THUMB : WORD_SIZE_ARM), &stub->outgoing[i]);
i += 8;
// Floating point registers, unused on the GBA (8 of them, 24 bits each)
for (r = 0; r < 8 * 3; ++r) {
_int2hex32(0, &stub->outgoing[i]);
i += 8;
}
// Floating point status, unused on the GBA (32 bits)
_int2hex32(0, &stub->outgoing[i]);
i += 8;
// CPU status
_int2hex32(cpu->cpsr.packed, &stub->outgoing[i]);
i += 8;
stub->outgoing[i] = 0;
_sendMessage(stub);
}
@ -477,16 +495,16 @@ static void _setBreakpoint(struct GDBStub* stub, const char* message) {
ARMDebuggerSetSoftwareBreakpoint(stub->d.platform, address, kind == 2 ? MODE_THUMB : MODE_ARM);
break;
case '1':
stub->d.platform->setBreakpoint(stub->d.platform, address);
stub->d.platform->setBreakpoint(stub->d.platform, address, -1);
break;
case '2':
stub->d.platform->setWatchpoint(stub->d.platform, address, WATCHPOINT_WRITE);
stub->d.platform->setWatchpoint(stub->d.platform, address, -1, WATCHPOINT_WRITE);
break;
case '3':
stub->d.platform->setWatchpoint(stub->d.platform, address, WATCHPOINT_READ);
stub->d.platform->setWatchpoint(stub->d.platform, address, -1, WATCHPOINT_READ);
break;
case '4':
stub->d.platform->setWatchpoint(stub->d.platform, address, WATCHPOINT_RW);
stub->d.platform->setWatchpoint(stub->d.platform, address, -1, WATCHPOINT_RW);
break;
default:
stub->outgoing[0] = '\0';
@ -506,12 +524,12 @@ static void _clearBreakpoint(struct GDBStub* stub, const char* message) {
ARMDebuggerClearSoftwareBreakpoint(stub->d.platform, address);
break;
case '1':
stub->d.platform->clearBreakpoint(stub->d.platform, address);
stub->d.platform->clearBreakpoint(stub->d.platform, address, -1);
break;
case '2':
case '3':
case '4':
stub->d.platform->clearWatchpoint(stub->d.platform, address);
stub->d.platform->clearWatchpoint(stub->d.platform, address, -1);
break;
default:
break;

49
src/debugger/symbols.c Normal file
View File

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

View File

@ -3,7 +3,7 @@
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "commandline.h"
#include <mgba/feature/commandline.h>
#include <mgba/core/config.h>
#include <mgba/core/version.h>
@ -38,6 +38,7 @@ static const struct option _options[] = {
{ "gdb", no_argument, 0, 'g' },
#endif
{ "help", no_argument, 0, 'h' },
{ "log-level", required_argument, 0, 'l' },
{ "movie", required_argument, 0, 'v' },
{ "patch", required_argument, 0, 'p' },
{ "version", no_argument, 0, '\0' },
@ -47,10 +48,27 @@ static const struct option _options[] = {
static bool _parseGraphicsArg(struct mSubParser* parser, int option, const char* arg);
static void _applyGraphicsArgs(struct mSubParser* parser, struct mCoreConfig* config);
static void _tableInsert(struct Table* table, const char* pair) {
char* eq = strchr(pair, '=');
if (eq) {
char option[128] = "";
strncpy(option, pair, eq - pair);
option[sizeof(option) - 1] = '\0';
HashTableInsert(table, option, strdup(&eq[1]));
} else {
HashTableInsert(table, pair, strdup("1"));
}
}
static void _tableApply(const char* key, void* value, void* user) {
struct mCoreConfig* config = user;
mCoreConfigSetOverrideValue(config, key, value);
}
bool parseArguments(struct mArguments* args, int argc, char* const* argv, struct mSubParser* subparser) {
int ch;
char options[64] =
"b:c:hl:p:s:v:"
"b:c:C:hl:p:s:v:"
#ifdef USE_EDITLINE
"d"
#endif
@ -61,6 +79,7 @@ bool parseArguments(struct mArguments* args, int argc, char* const* argv, struct
memset(args, 0, sizeof(*args));
args->frameskip = -1;
args->logLevel = INT_MIN;
HashTableInit(&args->configOverrides, 0, free);
if (subparser && subparser->extraOptions) {
// TODO: modularize options to subparsers
strncat(options, subparser->extraOptions, sizeof(options) - strlen(options) - 1);
@ -82,6 +101,9 @@ bool parseArguments(struct mArguments* args, int argc, char* const* argv, struct
case 'c':
args->cheatsFile = strdup(optarg);
break;
case 'C':
_tableInsert(&args->configOverrides, optarg);
break;
#ifdef USE_EDITLINE
case 'd':
if (args->debuggerType != DEBUGGER_NONE) {
@ -144,6 +166,7 @@ void applyArguments(const struct mArguments* args, struct mSubParser* subparser,
if (args->bios) {
mCoreConfigSetOverrideValue(config, "bios", args->bios);
}
HashTableEnumerate(&args->configOverrides, _tableApply, config);
if (subparser) {
subparser->apply(subparser, config);
}
@ -164,6 +187,8 @@ void freeArguments(struct mArguments* args) {
free(args->bios);
args->bios = 0;
HashTableDeinit(&args->configOverrides);
}
void initParserForGraphics(struct mSubParser* parser, struct mGraphicsOpts* opts) {
@ -211,12 +236,14 @@ void usage(const char* arg0, const char* extraOptions) {
puts("\nGeneric options:");
puts(" -b, --bios FILE GBA BIOS file to use");
puts(" -c, --cheats FILE Apply cheat codes from a file");
puts(" -C, --config OPTION=VALUE Override config value");
#ifdef USE_EDITLINE
puts(" -d, --debug Use command-line debugger");
#endif
#ifdef USE_GDB_STUB
puts(" -g, --gdb Start GDB session (default port 2345)");
#endif
puts(" -l, --log-level N Log level mask");
puts(" -v, --movie FILE Play back a movie of recorded input");
puts(" -p, --patch FILE Apply a specified patch file when running");
puts(" -s, --frameskip N Skip every N frames");

View File

@ -304,6 +304,14 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
}
av_opt_set(encoder->video->priv_data, "tune", "zerolatency", 0);
}
if (encoder->video->codec->id == AV_CODEC_ID_H264 &&
(strcasecmp(encoder->containerFormat, "mp4") ||
strcasecmp(encoder->containerFormat, "m4v") ||
strcasecmp(encoder->containerFormat, "mov"))) {
// QuickTime and a few other things require YUV420
encoder->video->pix_fmt = AV_PIX_FMT_YUV420P;
}
avcodec_open2(encoder->video, vcodec, 0);
#if LIBAVCODEC_VERSION_MAJOR >= 55
encoder->videoFrame = av_frame_alloc();
@ -320,7 +328,9 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
avcodec_parameters_from_context(encoder->videoStream->codecpar, encoder->video);
#endif
avio_open(&encoder->context->pb, outfile, AVIO_FLAG_WRITE);
if (avio_open(&encoder->context->pb, outfile, AVIO_FLAG_WRITE) < 0) {
return false;
}
return avformat_write_header(encoder->context, 0) >= 0;
}
@ -389,27 +399,27 @@ void _ffmpegPostAudioFrame(struct mAVStream* stream, int16_t left, int16_t right
encoder->audioBuffer[encoder->currentAudioSample * 2] = left;
encoder->audioBuffer[encoder->currentAudioSample * 2 + 1] = right;
++encoder->currentAudioFrame;
++encoder->currentAudioSample;
if ((encoder->currentAudioSample * 4) < encoder->audioBufferSize) {
if (encoder->currentAudioSample * 4 < encoder->audioBufferSize) {
return;
}
int channelSize = 2 * av_get_bytes_per_sample(encoder->audio->sample_fmt);
avresample_convert(encoder->resampleContext,
0, 0, 0,
avresample_convert(encoder->resampleContext, 0, 0, 0,
(uint8_t**) &encoder->audioBuffer, 0, encoder->audioBufferSize / 4);
encoder->currentAudioSample = 0;
if (avresample_available(encoder->resampleContext) < encoder->audioFrame->nb_samples) {
return;
}
#if LIBAVCODEC_VERSION_MAJOR >= 55
av_frame_make_writable(encoder->audioFrame);
#endif
avresample_read(encoder->resampleContext, encoder->audioFrame->data, encoder->postaudioBufferSize / channelSize);
int samples = avresample_read(encoder->resampleContext, encoder->audioFrame->data, encoder->postaudioBufferSize / channelSize);
encoder->audioFrame->pts = av_rescale_q(encoder->currentAudioFrame - encoder->currentAudioSample, encoder->audio->time_base, encoder->audioStream->time_base);
encoder->currentAudioSample = 0;
encoder->audioFrame->pts = av_rescale_q(encoder->currentAudioFrame, encoder->audio->time_base, encoder->audioStream->time_base);
encoder->currentAudioFrame += samples;
AVPacket packet;
av_init_packet(&packet);

View File

@ -298,7 +298,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
return;
}
if (runner->core->platform(runner->core) == PLATFORM_GBA) {
((struct GBA*) runner->core->board)->luminanceSource = &runner->luminanceSource.d;
runner->core->setPeripheral(runner->core, mPERIPH_GBA_LUMINANCE, &runner->luminanceSource.d);
}
mLOG(GUI_RUNNER, DEBUG, "Loading config...");
mCoreLoadForeignConfig(runner->core, &runner->config);

195
src/feature/thread-proxy.c Normal file
View File

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

951
src/feature/video-logger.c Normal file
View File

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

View File

@ -640,8 +640,8 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
audio->lastRight = sampleRight;
audio->clock += audio->sampleInterval;
if (audio->clock >= CLOCKS_PER_BLIP_FRAME) {
blip_end_frame(audio->left, audio->clock);
blip_end_frame(audio->right, audio->clock);
blip_end_frame(audio->left, CLOCKS_PER_BLIP_FRAME);
blip_end_frame(audio->right, CLOCKS_PER_BLIP_FRAME);
audio->clock -= CLOCKS_PER_BLIP_FRAME;
}
}

View File

@ -6,13 +6,17 @@
#include <mgba/gb/core.h>
#include <mgba/core/core.h>
#include <mgba/internal/debugger/symbols.h>
#include <mgba/internal/gb/cheats.h>
#include <mgba/internal/gb/debugger/symbols.h>
#include <mgba/internal/gb/extra/cli.h>
#include <mgba/internal/gb/io.h>
#include <mgba/internal/gb/gb.h>
#include <mgba/internal/gb/input.h>
#include <mgba/internal/gb/mbc.h>
#include <mgba/internal/gb/overrides.h>
#include <mgba/internal/gb/renderers/software.h>
#include <mgba/internal/gb/renderers/proxy.h>
#include <mgba/internal/gb/serialize.h>
#include <mgba/internal/lr35902/lr35902.h>
#include <mgba/internal/lr35902/debugger/debugger.h>
@ -25,9 +29,40 @@
#include <mgba/internal/gba/input.h>
#endif
const static struct mCoreChannelInfo _GBVideoLayers[] = {
{ 0, "bg", "Background", NULL },
{ 1, "obj", "Objects", NULL },
{ 2, "win", "Window", NULL },
};
const static struct mCoreChannelInfo _GBAudioChannels[] = {
{ 0, "ch0", "Channel 0", "Square/Sweep" },
{ 1, "ch1", "Channel 1", "Square" },
{ 2, "ch2", "Channel 2", "PCM" },
{ 3, "ch3", "Channel 3", "Noise" },
};
const static struct LR35902Segment _GBSegments[] = {
{ .name = "ROM", .start = GB_BASE_CART_BANK1, .end = GB_BASE_VRAM },
{ .name = "RAM", .start = GB_BASE_EXTERNAL_RAM, .end = GB_BASE_WORKING_RAM_BANK0 },
{ 0 }
};
const static struct LR35902Segment _GBCSegments[] = {
{ .name = "ROM", .start = GB_BASE_CART_BANK1, .end = GB_BASE_VRAM },
{ .name = "RAM", .start = GB_BASE_EXTERNAL_RAM, .end = GB_BASE_WORKING_RAM_BANK0 },
{ .name = "WRAM", .start = GB_BASE_WORKING_RAM_BANK1, .end = 0xE000 },
{ .name = "VRAM", .start = GB_BASE_VRAM, .end = GB_BASE_EXTERNAL_RAM },
{ 0 }
};
struct mVideoLogContext;
struct GBCore {
struct mCore d;
struct GBVideoSoftwareRenderer renderer;
struct GBVideoProxyRenderer proxyRenderer;
struct mVideoLogContext* logContext;
struct mCoreCallbacks logCallbacks;
uint8_t keys;
struct mCPUComponent* components[CPU_COMPONENT_MAX];
const struct Configuration* overrides;
@ -80,8 +115,11 @@ static void _GBCoreDeinit(struct mCore* core) {
GBDestroy(core->board);
mappedMemoryFree(core->cpu, sizeof(struct LR35902Core));
mappedMemoryFree(core->board, sizeof(struct GB));
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
#if defined USE_DEBUGGERS && (!defined(MINIMAL_CORE) || MINIMAL_CORE < 2)
mDirectorySetDeinit(&core->dirs);
if (core->symbolTable) {
mDebuggerSymbolTableDestroy(core->symbolTable);
}
#endif
struct GBCore* gbcore = (struct GBCore*) core;
@ -114,6 +152,21 @@ static void _GBCoreLoadConfig(struct mCore* core, const struct mCoreConfig* conf
gb->audio.masterVolume = core->opts.volume;
}
gb->video.frameskip = core->opts.frameskip;
int color;
if (mCoreConfigGetIntValue(&core->config, "gb.pal[0]", &color)) {
GBVideoSetPalette(&gb->video, 0, color);
}
if (mCoreConfigGetIntValue(&core->config, "gb.pal[1]", &color)) {
GBVideoSetPalette(&gb->video, 1, color);
}
if (mCoreConfigGetIntValue(&core->config, "gb.pal[2]", &color)) {
GBVideoSetPalette(&gb->video, 2, color);
}
if (mCoreConfigGetIntValue(&core->config, "gb.pal[3]", &color)) {
GBVideoSetPalette(&gb->video, 3, color);
}
mCoreConfigCopyValue(&core->config, config, "gb.bios");
mCoreConfigCopyValue(&core->config, config, "gbc.bios");
@ -402,14 +455,18 @@ static void _GBCoreGetGameCode(const struct mCore* core, char* title) {
GBGetGameCode(core->board, title);
}
static void _GBCoreSetRotation(struct mCore* core, struct mRotationSource* rotation) {
static void _GBCoreSetPeripheral(struct mCore* core, int type, void* periph) {
struct GB* gb = core->board;
gb->memory.rotation = rotation;
}
static void _GBCoreSetRumble(struct mCore* core, struct mRumble* rumble) {
struct GB* gb = core->board;
gb->memory.rumble = rumble;
switch (type) {
case mPERIPH_ROTATION:
gb->memory.rotation = periph;
break;
case mPERIPH_RUMBLE:
gb->memory.rumble = periph;
break;
default:
return;
}
}
static uint32_t _GBCoreBusRead8(struct mCore* core, uint32_t address) {
@ -495,8 +552,15 @@ static bool _GBCoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType t
static struct mDebuggerPlatform* _GBCoreDebuggerPlatform(struct mCore* core) {
struct GBCore* gbcore = (struct GBCore*) core;
struct GB* gb = core->board;
if (!gbcore->debuggerPlatform) {
gbcore->debuggerPlatform = LR35902DebuggerPlatformCreate();
struct LR35902Debugger* platform = (struct LR35902Debugger*) LR35902DebuggerPlatformCreate();
if (gb->model >= GB_MODEL_CGB) {
platform->segments = _GBCSegments;
} else {
platform->segments = _GBSegments;
}
gbcore->debuggerPlatform = &platform->d;
}
return gbcore->debuggerPlatform;
}
@ -523,6 +587,19 @@ static void _GBCoreDetachDebugger(struct mCore* core) {
cpu->components[CPU_COMPONENT_DEBUGGER] = NULL;
core->debugger = NULL;
}
static void _GBCoreLoadSymbols(struct mCore* core, struct VFile* vf) {
core->symbolTable = mDebuggerSymbolTableCreate();
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
if (!vf) {
vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.base, ".sym", O_RDONLY);
}
#endif
if (!vf) {
return;
}
GBLoadSymbols(core->symbolTable, vf);
}
#endif
static struct mCheatDevice* _GBCoreCheatDevice(struct mCore* core) {
@ -569,6 +646,72 @@ static bool _GBCoreSavedataRestore(struct mCore* core, const void* sram, size_t
return true;
}
static size_t _GBCoreListVideoLayers(const struct mCore* core, const struct mCoreChannelInfo** info) {
UNUSED(core);
*info = _GBVideoLayers;
return sizeof(_GBVideoLayers) / sizeof(*_GBVideoLayers);
}
static size_t _GBCoreListAudioChannels(const struct mCore* core, const struct mCoreChannelInfo** info) {
UNUSED(core);
*info = _GBAudioChannels;
return sizeof(_GBAudioChannels) / sizeof(*_GBAudioChannels);
}
static void _GBCoreEnableVideoLayer(struct mCore* core, size_t id, bool enable) {
struct GB* gb = core->board;
switch (id) {
case 0:
gb->video.renderer->disableBG = !enable;
break;
case 1:
gb->video.renderer->disableOBJ = !enable;
break;
case 2:
gb->video.renderer->disableWIN = !enable;
break;
default:
break;
}
}
static void _GBCoreEnableAudioChannel(struct mCore* core, size_t id, bool enable) {
struct GB* gb = core->board;
switch (id) {
case 0:
case 1:
case 2:
case 3:
gb->audio.forceDisableCh[id] = !enable;
break;
default:
break;
}
}
static void _GBCoreStartVideoLog(struct mCore* core, struct mVideoLogContext* context) {
struct GBCore* gbcore = (struct GBCore*) core;
struct GB* gb = core->board;
gbcore->logContext = context;
int channelId = mVideoLoggerAddChannel(context);
gbcore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger));
mVideoLoggerRendererCreate(gbcore->proxyRenderer.logger, false);
mVideoLoggerAttachChannel(gbcore->proxyRenderer.logger, context, channelId);
gbcore->proxyRenderer.logger->block = false;
GBVideoProxyRendererCreate(&gbcore->proxyRenderer, &gbcore->renderer.d);
GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer);
}
static void _GBCoreEndVideoLog(struct mCore* core) {
struct GBCore* gbcore = (struct GBCore*) core;
struct GB* gb = core->board;
GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer);
free(gbcore->proxyRenderer.logger);
gbcore->proxyRenderer.logger = NULL;
}
struct mCore* GBCoreCreate(void) {
struct GBCore* gbcore = malloc(sizeof(*gbcore));
struct mCore* core = &gbcore->d;
@ -576,6 +719,7 @@ struct mCore* GBCoreCreate(void) {
core->cpu = NULL;
core->board = NULL;
core->debugger = NULL;
core->symbolTable = NULL;
core->init = _GBCoreInit;
core->deinit = _GBCoreDeinit;
core->platform = _GBCorePlatform;
@ -614,8 +758,7 @@ struct mCore* GBCoreCreate(void) {
core->frequency = _GBCoreFrequency;
core->getGameTitle = _GBCoreGetGameTitle;
core->getGameCode = _GBCoreGetGameCode;
core->setRotation = _GBCoreSetRotation;
core->setRumble = _GBCoreSetRumble;
core->setPeripheral = _GBCoreSetPeripheral;
core->busRead8 = _GBCoreBusRead8;
core->busRead16 = _GBCoreBusRead16;
core->busRead32 = _GBCoreBusRead32;
@ -634,9 +777,129 @@ struct mCore* GBCoreCreate(void) {
core->cliDebuggerSystem = _GBCoreCliDebuggerSystem;
core->attachDebugger = _GBCoreAttachDebugger;
core->detachDebugger = _GBCoreDetachDebugger;
core->loadSymbols = _GBCoreLoadSymbols;
#endif
core->cheatDevice = _GBCoreCheatDevice;
core->savedataClone = _GBCoreSavedataClone;
core->savedataRestore = _GBCoreSavedataRestore;
core->listVideoLayers = _GBCoreListVideoLayers;
core->listAudioChannels = _GBCoreListAudioChannels;
core->enableVideoLayer = _GBCoreEnableVideoLayer;
core->enableAudioChannel = _GBCoreEnableAudioChannel;
#ifndef MINIMAL_CORE
core->startVideoLog = _GBCoreStartVideoLog;
core->endVideoLog = _GBCoreEndVideoLog;
#endif
return core;
}
#ifndef MINIMAL_CORE
static void _GBVLPStartFrameCallback(void *context) {
struct mCore* core = context;
struct GBCore* gbcore = (struct GBCore*) core;
struct GB* gb = core->board;
if (!mVideoLoggerRendererRun(gbcore->proxyRenderer.logger, true)) {
GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer);
mVideoLogContextRewind(gbcore->logContext, core);
GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer);
}
}
static bool _GBVLPInit(struct mCore* core) {
struct GBCore* gbcore = (struct GBCore*) core;
if (!_GBCoreInit(core)) {
return false;
}
gbcore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger));
mVideoLoggerRendererCreate(gbcore->proxyRenderer.logger, true);
GBVideoProxyRendererCreate(&gbcore->proxyRenderer, NULL);
memset(&gbcore->logCallbacks, 0, sizeof(gbcore->logCallbacks));
gbcore->logCallbacks.videoFrameStarted = _GBVLPStartFrameCallback;
gbcore->logCallbacks.context = core;
core->addCoreCallbacks(core, &gbcore->logCallbacks);
return true;
}
static void _GBVLPDeinit(struct mCore* core) {
struct GBCore* gbcore = (struct GBCore*) core;
if (gbcore->logContext) {
mVideoLogContextDestroy(core, gbcore->logContext);
}
_GBCoreDeinit(core);
}
static void _GBVLPReset(struct mCore* core) {
struct GBCore* gbcore = (struct GBCore*) core;
struct GB* gb = (struct GB*) core->board;
if (gb->video.renderer == &gbcore->proxyRenderer.d) {
GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer);
} else if (gbcore->renderer.outputBuffer) {
struct GBVideoRenderer* renderer = &gbcore->renderer.d;
GBVideoAssociateRenderer(&gb->video, renderer);
}
LR35902Reset(core->cpu);
mVideoLogContextRewind(gbcore->logContext, core);
GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer);
// Make sure CPU loop never spins
GBHalt(gb->cpu);
gb->memory.ie = 0;
gb->memory.ime = false;
}
static bool _GBVLPLoadROM(struct mCore* core, struct VFile* vf) {
struct GBCore* gbcore = (struct GBCore*) core;
gbcore->logContext = mVideoLogContextCreate(NULL);
if (!mVideoLogContextLoad(gbcore->logContext, vf)) {
mVideoLogContextDestroy(core, gbcore->logContext);
gbcore->logContext = NULL;
return false;
}
mVideoLoggerAttachChannel(gbcore->proxyRenderer.logger, gbcore->logContext, 0);
return true;
}
static bool _GBVLPLoadState(struct mCore* core, const void* buffer) {
struct GB* gb = (struct GB*) core->board;
const struct GBSerializedState* state = buffer;
gb->timing.root = NULL;
gb->model = state->model;
gb->cpu->pc = GB_BASE_HRAM;
gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc);
GBVideoDeserialize(&gb->video, state);
GBIODeserialize(gb, state);
GBAudioReset(&gb->audio);
// Make sure CPU loop never spins
GBHalt(gb->cpu);
gb->memory.ie = 0;
gb->memory.ime = false;
return true;
}
static bool _returnTrue(struct VFile* vf) {
UNUSED(vf);
return true;
}
struct mCore* GBVideoLogPlayerCreate(void) {
struct mCore* core = GBCoreCreate();
core->init = _GBVLPInit;
core->deinit = _GBVLPDeinit;
core->reset = _GBVLPReset;
core->loadROM = _GBVLPLoadROM;
core->loadState = _GBVLPLoadState;
core->isROM = _returnTrue;
return core;
}
#else
struct mCore* GBVideoLogPlayerCreate(void) {
return false;
}
#endif

55
src/gb/debugger/symbols.c Normal file
View File

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

272
src/gb/extra/proxy.c Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -14,6 +14,7 @@ static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer);
static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
static void GBVideoSoftwareRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value);
static void GBVideoSoftwareRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address);
static void GBVideoSoftwareRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam);
static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax);
static void GBVideoSoftwareRendererFinishScanline(struct GBVideoRenderer* renderer, int y);
static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer);
@ -24,26 +25,15 @@ static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer
static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int startX, int endX, int y);
static void _clearScreen(struct GBVideoSoftwareRenderer* renderer) {
// TODO: Dynamic from dmgPalette
#ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5
color_t palette0 = 0xFFDF;
#else
color_t palette0 = 0x7FFF;
#endif
#else
color_t palette0 = 0xFFFFFF;
#endif
int y;
for (y = 0; y < GB_VIDEO_VERTICAL_PIXELS; ++y) {
color_t* row = &renderer->outputBuffer[renderer->outputBufferStride * y];
int x;
for (x = 0; x < GB_VIDEO_HORIZONTAL_PIXELS; x += 4) {
row[x + 0] = palette0;
row[x + 1] = palette0;
row[x + 2] = palette0;
row[x + 3] = palette0;
row[x + 0] = renderer->palette[0];
row[x + 1] = renderer->palette[0];
row[x + 2] = renderer->palette[0];
row[x + 3] = renderer->palette[0];
}
}
}
@ -54,12 +44,17 @@ void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer* renderer) {
renderer->d.writeVideoRegister = GBVideoSoftwareRendererWriteVideoRegister;
renderer->d.writePalette = GBVideoSoftwareRendererWritePalette;
renderer->d.writeVRAM = GBVideoSoftwareRendererWriteVRAM;
renderer->d.writeOAM = GBVideoSoftwareRendererWriteOAM;
renderer->d.drawRange = GBVideoSoftwareRendererDrawRange;
renderer->d.finishScanline = GBVideoSoftwareRendererFinishScanline;
renderer->d.finishFrame = GBVideoSoftwareRendererFinishFrame;
renderer->d.getPixels = GBVideoSoftwareRendererGetPixels;
renderer->d.putPixels = GBVideoSoftwareRendererPutPixels;
renderer->d.disableBG = false;
renderer->d.disableOBJ = false;
renderer->d.disableWIN = false;
renderer->temporaryBuffer = 0;
}
@ -85,9 +80,6 @@ static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer*
struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
switch (address) {
case REG_LCDC:
if (GBRegisterLCDCIsEnable(softwareRenderer->lcdc) && !GBRegisterLCDCIsEnable(value)) {
_clearScreen(softwareRenderer);
}
softwareRenderer->lcdc = value;
break;
case REG_SCY:
@ -136,15 +128,24 @@ static void GBVideoSoftwareRendererWriteVRAM(struct GBVideoRenderer* renderer, u
}
}
static void GBVideoSoftwareRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam) {
UNUSED(renderer);
UNUSED(oam);
// Nothing to do
}
static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax) {
struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
uint8_t* maps = &softwareRenderer->d.vram[GB_BASE_MAP];
if (GBRegisterLCDCIsTileMap(softwareRenderer->lcdc)) {
maps += GB_SIZE_MAP;
}
if (softwareRenderer->d.disableBG) {
memset(&softwareRenderer->row[startX], 0, endX - startX);
}
if (GBRegisterLCDCIsBgEnable(softwareRenderer->lcdc) || softwareRenderer->model >= GB_MODEL_CGB) {
if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && softwareRenderer->wy <= y && endX >= softwareRenderer->wx - 7) {
if (softwareRenderer->wx - 7 > 0) {
if (softwareRenderer->wx - 7 > 0 && !softwareRenderer->d.disableBG) {
GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, softwareRenderer->wx - 7, softwareRenderer->scx, softwareRenderer->scy + y);
}
@ -152,15 +153,17 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i
if (GBRegisterLCDCIsWindowTileMap(softwareRenderer->lcdc)) {
maps += GB_SIZE_MAP;
}
if (!softwareRenderer->d.disableWIN) {
GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, softwareRenderer->wx - 7, endX, 7 - softwareRenderer->wx, softwareRenderer->currentWy);
} else {
}
} else if (!softwareRenderer->d.disableBG) {
GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, endX, softwareRenderer->scx, softwareRenderer->scy + y);
}
} else {
} else if (!softwareRenderer->d.disableBG) {
memset(&softwareRenderer->row[startX], 0, endX - startX);
}
if (GBRegisterLCDCIsObjEnable(softwareRenderer->lcdc)) {
if (GBRegisterLCDCIsObjEnable(softwareRenderer->lcdc) && !softwareRenderer->d.disableOBJ) {
size_t i;
for (i = 0; i < oamMax; ++i) {
GBVideoSoftwareRendererDrawObj(softwareRenderer, &obj[i], startX, endX, y);
@ -197,6 +200,9 @@ static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer)
mappedMemoryFree(softwareRenderer->temporaryBuffer, GB_VIDEO_HORIZONTAL_PIXELS * GB_VIDEO_VERTICAL_PIXELS * 4);
softwareRenderer->temporaryBuffer = 0;
}
if (!GBRegisterLCDCIsEnable(softwareRenderer->lcdc)) {
_clearScreen(softwareRenderer);
}
softwareRenderer->currentWy = 0;
}

View File

@ -20,6 +20,7 @@ static void GBVideoDummyRendererDeinit(struct GBVideoRenderer* renderer);
static uint8_t GBVideoDummyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
static void GBVideoDummyRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value);
static void GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address);
static void GBVideoDummyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam);
static void GBVideoDummyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax);
static void GBVideoDummyRendererFinishScanline(struct GBVideoRenderer* renderer, int y);
static void GBVideoDummyRendererFinishFrame(struct GBVideoRenderer* renderer);
@ -39,6 +40,7 @@ static struct GBVideoRenderer dummyRenderer = {
.deinit = GBVideoDummyRendererDeinit,
.writeVideoRegister = GBVideoDummyRendererWriteVideoRegister,
.writeVRAM = GBVideoDummyRendererWriteVRAM,
.writeOAM = GBVideoDummyRendererWriteOAM,
.writePalette = GBVideoDummyRendererWritePalette,
.drawRange = GBVideoDummyRendererDrawRange,
.finishScanline = GBVideoDummyRendererFinishScanline,
@ -61,6 +63,11 @@ void GBVideoInit(struct GBVideo* video) {
video->frameEvent.name = "GB Video Frame";
video->frameEvent.callback = _updateFrameCount;
video->frameEvent.priority = 9;
video->dmgPalette[0] = 0x7FFF;
video->dmgPalette[1] = 0x56B5;
video->dmgPalette[2] = 0x294A;
video->dmgPalette[3] = 0x0000;
}
void GBVideoReset(struct GBVideo* video) {
@ -99,6 +106,33 @@ void GBVideoAssociateRenderer(struct GBVideo* video, struct GBVideoRenderer* ren
video->renderer->init(video->renderer, video->p->model);
}
static bool _statIRQAsserted(struct GBVideo* video, GBRegisterSTAT stat) {
// TODO: variable for the IRQ line value?
if (GBRegisterSTATIsLYCIRQ(stat) && GBRegisterSTATIsLYC(stat)) {
return true;
}
switch (GBRegisterSTATGetMode(stat)) {
case 0:
if (GBRegisterSTATIsHblankIRQ(stat)) {
return true;
}
break;
case 1:
if (GBRegisterSTATIsVblankIRQ(stat)) {
return true;
}
break;
case 2:
if (GBRegisterSTATIsOAMIRQ(stat)) {
return true;
}
break;
case 3:
break;
}
return false;
}
void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct GBVideo* video = context;
if (video->frameskipCounter <= 0) {
@ -108,40 +142,30 @@ void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
int32_t next;
++video->ly;
video->p->memory.io[REG_LY] = video->ly;
GBRegisterSTAT oldStat = video->stat;
video->stat = GBRegisterSTATSetLYC(video->stat, lyc == video->ly);
if (video->ly < GB_VIDEO_VERTICAL_PIXELS) {
// TODO: Cache SCX & 7 in case it changes during mode 2
next = GB_VIDEO_MODE_2_LENGTH + (video->p->memory.io[REG_SCX] & 7);
video->mode = 2;
video->modeEvent.callback = _endMode2;
if (!GBRegisterSTATIsHblankIRQ(video->stat) && GBRegisterSTATIsOAMIRQ(video->stat)) {
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
}
} else {
next = GB_VIDEO_HORIZONTAL_LENGTH;
video->mode = 1;
video->modeEvent.callback = _endMode1;
_updateFrameCount(timing, video, cyclesLate);
mTimingSchedule(&video->p->timing, &video->frameEvent, -cyclesLate);
if (GBRegisterSTATIsVblankIRQ(video->stat) || GBRegisterSTATIsOAMIRQ(video->stat)) {
if (!_statIRQAsserted(video, oldStat) && GBRegisterSTATIsOAMIRQ(video->stat)) {
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
}
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_VBLANK);
size_t c;
for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) {
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&video->p->coreCallbacks, c);
if (callbacks->videoFrameEnded) {
callbacks->videoFrameEnded(callbacks->context);
}
}
}
if (!GBRegisterSTATIsHblankIRQ(video->stat) && GBRegisterSTATIsLYCIRQ(video->stat) && lyc == video->ly) {
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
}
GBUpdateIRQs(video->p);
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
video->p->memory.io[REG_STAT] = video->stat;
mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate);
}
@ -161,11 +185,6 @@ void _endMode1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
next = GB_VIDEO_MODE_2_LENGTH + (video->p->memory.io[REG_SCX] & 7);
video->mode = 2;
video->modeEvent.callback = _endMode2;
if (GBRegisterSTATIsOAMIRQ(video->stat)) {
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
GBUpdateIRQs(video->p);
}
video->renderer->finishFrame(video->renderer);
if (video->p->memory.mbcType == GB_MBC7 && video->p->memory.rotation && video->p->memory.rotation->sample) {
video->p->memory.rotation->sample(video->p->memory.rotation);
}
@ -180,9 +199,10 @@ void _endMode1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
next = GB_VIDEO_HORIZONTAL_LENGTH;
}
GBRegisterSTAT oldStat = video->stat;
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
video->stat = GBRegisterSTATSetLYC(video->stat, lyc == video->p->memory.io[REG_LY]);
if (video->ly && GBRegisterSTATIsLYCIRQ(video->stat) && lyc == video->p->memory.io[REG_LY]) {
if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
GBUpdateIRQs(video->p);
}
@ -195,10 +215,15 @@ void _endMode2(struct mTiming* timing, void* context, uint32_t cyclesLate) {
_cleanOAM(video, video->ly);
video->x = 0;
video->dotClock = timing->masterCycles - cyclesLate;
int32_t next = GB_VIDEO_MODE_3_LENGTH_BASE + video->objMax * 11 - (video->p->memory.io[REG_SCX] & 7);
int32_t next = GB_VIDEO_MODE_3_LENGTH_BASE + video->objMax * 6 - (video->p->memory.io[REG_SCX] & 7);
video->mode = 3;
video->modeEvent.callback = _endMode3;
GBRegisterSTAT oldStat = video->stat;
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
GBUpdateIRQs(video->p);
}
video->p->memory.io[REG_STAT] = video->stat;
mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate);
}
@ -206,10 +231,6 @@ void _endMode2(struct mTiming* timing, void* context, uint32_t cyclesLate) {
void _endMode3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct GBVideo* video = context;
GBVideoProcessDots(video);
if (GBRegisterSTATIsHblankIRQ(video->stat)) {
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
GBUpdateIRQs(video->p);
}
if (video->ly < GB_VIDEO_VERTICAL_PIXELS && video->p->memory.isHdma && video->p->memory.io[REG_HDMA5] != 0xFF) {
video->p->memory.hdmaRemaining = 0x10;
mTimingDeschedule(timing, &video->p->memory.hdmaEvent);
@ -217,9 +238,14 @@ void _endMode3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
}
video->mode = 0;
video->modeEvent.callback = _endMode0;
GBRegisterSTAT oldStat = video->stat;
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
GBUpdateIRQs(video->p);
}
video->p->memory.io[REG_STAT] = video->stat;
int32_t next = GB_VIDEO_MODE_0_LENGTH_BASE - video->objMax * 11;
int32_t next = GB_VIDEO_MODE_0_LENGTH_BASE - video->objMax * 6;
mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate);
}
@ -231,9 +257,18 @@ void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLat
return;
}
size_t c;
for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) {
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&video->p->coreCallbacks, c);
if (callbacks->videoFrameEnded) {
callbacks->videoFrameEnded(callbacks->context);
}
}
GBFrameEnded(video->p);
--video->frameskipCounter;
if (video->frameskipCounter < 0) {
video->renderer->finishFrame(video->renderer);
mCoreSyncPostFrame(video->p->sync);
video->frameskipCounter = video->frameskip;
}
@ -247,17 +282,16 @@ void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLat
video->p->stream->postVideoFrame(video->p->stream, pixels, stride);
}
size_t c;
if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC])) {
mTimingSchedule(timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH);
}
for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) {
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&video->p->coreCallbacks, c);
if (callbacks->videoFrameStarted) {
callbacks->videoFrameStarted(callbacks->context);
}
}
if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC])) {
mTimingSchedule(timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH);
}
}
static void _cleanOAM(struct GBVideo* video, int y) {
@ -312,13 +346,16 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
video->ly = 0;
video->p->memory.io[REG_LY] = 0;
// TODO: Does this read as 0 for 4 T-cycles?
GBRegisterSTAT oldStat = video->stat;
video->stat = GBRegisterSTATSetMode(video->stat, 2);
video->stat = GBRegisterSTATSetLYC(video->stat, video->ly == video->p->memory.io[REG_LYC]);
if (GBRegisterSTATIsLYCIRQ(video->stat) && video->ly == video->p->memory.io[REG_LYC]) {
if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
GBUpdateIRQs(video->p);
}
video->p->memory.io[REG_STAT] = video->stat;
video->renderer->writePalette(video->renderer, 0, video->palette[0]);
mTimingDeschedule(&video->p->timing, &video->frameEvent);
}
if (GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) && !GBRegisterLCDCIsEnable(value)) {
@ -327,6 +364,8 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
video->p->memory.io[REG_STAT] = video->stat;
video->ly = 0;
video->p->memory.io[REG_LY] = 0;
video->renderer->writePalette(video->renderer, 0, video->dmgPalette[0]);
mTimingDeschedule(&video->p->timing, &video->modeEvent);
mTimingSchedule(&video->p->timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH);
}
@ -334,52 +373,52 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
}
void GBVideoWriteSTAT(struct GBVideo* video, GBRegisterSTAT value) {
GBRegisterSTAT oldStat = video->stat;
video->stat = (video->stat & 0x7) | (value & 0x78);
if (video->p->model == GB_MODEL_DMG && video->mode == 1) {
if (video->p->model == GB_MODEL_DMG && !_statIRQAsserted(video, oldStat) && video->mode < 3) {
// TODO: variable for the IRQ line value?
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
GBUpdateIRQs(video->p);
}
}
void GBVideoWriteLYC(struct GBVideo* video, uint8_t value) {
if (video->mode == 2) {
GBRegisterSTAT oldStat = video->stat;
video->stat = GBRegisterSTATSetLYC(video->stat, value == video->ly);
if (GBRegisterSTATIsLYCIRQ(video->stat) && value == video->ly) {
if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) {
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
GBUpdateIRQs(video->p);
}
}
}
void GBVideoWritePalette(struct GBVideo* video, uint16_t address, uint8_t value) {
static const uint16_t dmgPalette[4] = { 0x7FFF, 0x56B5, 0x294A, 0x0000};
if (video->p->model < GB_MODEL_CGB) {
switch (address) {
case REG_BGP:
video->palette[0] = dmgPalette[value & 3];
video->palette[1] = dmgPalette[(value >> 2) & 3];
video->palette[2] = dmgPalette[(value >> 4) & 3];
video->palette[3] = dmgPalette[(value >> 6) & 3];
video->palette[0] = video->dmgPalette[value & 3];
video->palette[1] = video->dmgPalette[(value >> 2) & 3];
video->palette[2] = video->dmgPalette[(value >> 4) & 3];
video->palette[3] = video->dmgPalette[(value >> 6) & 3];
video->renderer->writePalette(video->renderer, 0, video->palette[0]);
video->renderer->writePalette(video->renderer, 1, video->palette[1]);
video->renderer->writePalette(video->renderer, 2, video->palette[2]);
video->renderer->writePalette(video->renderer, 3, video->palette[3]);
break;
case REG_OBP0:
video->palette[8 * 4 + 0] = dmgPalette[value & 3];
video->palette[8 * 4 + 1] = dmgPalette[(value >> 2) & 3];
video->palette[8 * 4 + 2] = dmgPalette[(value >> 4) & 3];
video->palette[8 * 4 + 3] = dmgPalette[(value >> 6) & 3];
video->palette[8 * 4 + 0] = video->dmgPalette[value & 3];
video->palette[8 * 4 + 1] = video->dmgPalette[(value >> 2) & 3];
video->palette[8 * 4 + 2] = video->dmgPalette[(value >> 4) & 3];
video->palette[8 * 4 + 3] = video->dmgPalette[(value >> 6) & 3];
video->renderer->writePalette(video->renderer, 8 * 4 + 0, video->palette[8 * 4 + 0]);
video->renderer->writePalette(video->renderer, 8 * 4 + 1, video->palette[8 * 4 + 1]);
video->renderer->writePalette(video->renderer, 8 * 4 + 2, video->palette[8 * 4 + 2]);
video->renderer->writePalette(video->renderer, 8 * 4 + 3, video->palette[8 * 4 + 3]);
break;
case REG_OBP1:
video->palette[9 * 4 + 0] = dmgPalette[value & 3];
video->palette[9 * 4 + 1] = dmgPalette[(value >> 2) & 3];
video->palette[9 * 4 + 2] = dmgPalette[(value >> 4) & 3];
video->palette[9 * 4 + 3] = dmgPalette[(value >> 6) & 3];
video->palette[9 * 4 + 0] = video->dmgPalette[value & 3];
video->palette[9 * 4 + 1] = video->dmgPalette[(value >> 2) & 3];
video->palette[9 * 4 + 2] = video->dmgPalette[(value >> 4) & 3];
video->palette[9 * 4 + 3] = video->dmgPalette[(value >> 6) & 3];
video->renderer->writePalette(video->renderer, 9 * 4 + 0, video->palette[9 * 4 + 0]);
video->renderer->writePalette(video->renderer, 9 * 4 + 1, video->palette[9 * 4 + 1]);
video->renderer->writePalette(video->renderer, 9 * 4 + 2, video->palette[9 * 4 + 2]);
@ -432,6 +471,13 @@ void GBVideoSwitchBank(struct GBVideo* video, uint8_t value) {
video->vramCurrentBank = value;
}
void GBVideoSetPalette(struct GBVideo* video, unsigned index, uint16_t color) {
if (index >= 4) {
return;
}
video->dmgPalette[index] = color;
}
static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model) {
UNUSED(renderer);
UNUSED(model);
@ -455,6 +501,12 @@ static void GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint
}
}
static void GBVideoDummyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam) {
UNUSED(renderer);
UNUSED(oam);
// Nothing to do
}
static void GBVideoDummyRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value) {
UNUSED(value);
if (renderer->cache) {

View File

@ -297,8 +297,8 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
audio->lastRight = sampleRight;
audio->clock += audio->sampleInterval;
if (audio->clock >= CLOCKS_PER_FRAME) {
blip_end_frame(audio->psg.left, audio->clock);
blip_end_frame(audio->psg.right, audio->clock);
blip_end_frame(audio->psg.left, CLOCKS_PER_FRAME);
blip_end_frame(audio->psg.right, CLOCKS_PER_FRAME);
audio->clock -= CLOCKS_PER_FRAME;
}
}

View File

@ -271,7 +271,7 @@ static void _Div(struct GBA* gba, int32_t num, int32_t denom) {
}
}
static int16_t _ArcTan(int16_t i) {
static int16_t _ArcTan(int32_t i, int32_t* r1, int32_t* r3) {
int32_t a = -((i * i) >> 14);
int32_t b = ((0xA9 * a) >> 14) + 0x390;
b = ((b * a) >> 14) + 0x91C;
@ -280,10 +280,16 @@ static int16_t _ArcTan(int16_t i) {
b = ((b * a) >> 14) + 0x2081;
b = ((b * a) >> 14) + 0x3651;
b = ((b * a) >> 14) + 0xA2F9;
if (r1) {
*r1 = a;
}
if (r3) {
*r3 = b;
}
return (i * b) >> 16;
}
static int16_t _ArcTan2(int16_t x, int16_t y) {
static int16_t _ArcTan2(int32_t x, int32_t y, int32_t* r1) {
if (!y) {
if (x >= 0) {
return 0;
@ -299,21 +305,21 @@ static int16_t _ArcTan2(int16_t x, int16_t y) {
if (y >= 0) {
if (x >= 0) {
if (x >= y) {
return _ArcTan((y << 14)/ x);
return _ArcTan((y << 14) / x, r1, NULL);
}
} else if (-x >= y) {
return _ArcTan((y << 14) / x) + 0x8000;
return _ArcTan((y << 14) / x, r1, NULL) + 0x8000;
}
return 0x4000 - _ArcTan((x << 14) / y);
return 0x4000 - _ArcTan((x << 14) / y, r1, NULL);
} else {
if (x <= 0) {
if (-x > -y) {
return _ArcTan((y << 14) / x) + 0x8000;
return _ArcTan((y << 14) / x, r1, NULL) + 0x8000;
}
} else if (x >= -y) {
return _ArcTan((y << 14) / x) + 0x10000;
return _ArcTan((y << 14) / x, r1, NULL) + 0x10000;
}
return 0xC000 - _ArcTan((x << 14) / y);
return 0xC000 - _ArcTan((x << 14) / y, r1, NULL);
}
}
@ -356,10 +362,11 @@ void GBASwi16(struct ARMCore* cpu, int immediate) {
cpu->gprs[0] = sqrt((uint32_t) cpu->gprs[0]);
break;
case 0x9:
cpu->gprs[0] = (uint16_t) _ArcTan(cpu->gprs[0]);
cpu->gprs[0] = _ArcTan(cpu->gprs[0], &cpu->gprs[1], &cpu->gprs[3]);
break;
case 0xA:
cpu->gprs[0] = (uint16_t) _ArcTan2(cpu->gprs[0], cpu->gprs[1]);
cpu->gprs[0] = (uint16_t) _ArcTan2(cpu->gprs[0], cpu->gprs[1], &cpu->gprs[1]);
cpu->gprs[3] = 0x170;
break;
case 0xB:
case 0xC:

View File

@ -10,11 +10,13 @@
#include <mgba/internal/arm/debugger/debugger.h>
#include <mgba/internal/gba/cheats.h>
#include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/io.h>
#include <mgba/internal/gba/extra/cli.h>
#include <mgba/internal/gba/overrides.h>
#ifndef DISABLE_THREADING
#include <mgba/internal/gba/renderers/thread-proxy.h>
#include <mgba/feature/thread-proxy.h>
#endif
#include <mgba/internal/gba/renderers/proxy.h>
#include <mgba/internal/gba/renderers/video-software.h>
#include <mgba/internal/gba/savedata.h>
#include <mgba/internal/gba/serialize.h>
@ -26,11 +28,32 @@
#include <mgba/internal/gba/input.h>
#endif
const static struct mCoreChannelInfo _GBAVideoLayers[] = {
{ 0, "bg0", "Background 0", NULL },
{ 1, "bg1", "Background 1", NULL },
{ 2, "bg2", "Background 2", NULL },
{ 3, "bg3", "Background 3", NULL },
{ 4, "obj", "Objects", NULL },
};
const static struct mCoreChannelInfo _GBAAudioChannels[] = {
{ 0, "ch0", "PSG Channel 0", "Square/Sweep" },
{ 1, "ch1", "PSG Channel 1", "Square" },
{ 2, "ch2", "PSG Channel 2", "PCM" },
{ 3, "ch3", "PSG Channel 3", "Noise" },
{ 4, "chA", "FIFO Channel A", NULL },
{ 5, "chB", "FIFO Channel B", NULL },
};
struct mVideoLogContext;
struct GBACore {
struct mCore d;
struct GBAVideoSoftwareRenderer renderer;
struct GBAVideoProxyRenderer proxyRenderer;
struct mVideoLogContext* logContext;
struct mCoreCallbacks logCallbacks;
#ifndef DISABLE_THREADING
struct GBAVideoThreadProxyRenderer threadProxy;
struct mVideoThreadProxy threadProxy;
int threadedVideo;
#endif
int keys;
@ -53,9 +76,11 @@ static bool _GBACoreInit(struct mCore* core) {
core->cpu = cpu;
core->board = gba;
core->debugger = NULL;
core->symbolTable = NULL;
gbacore->overrides = NULL;
gbacore->debuggerPlatform = NULL;
gbacore->cheatDevice = NULL;
gbacore->logContext = NULL;
GBACreate(gba);
// TODO: Restore cheats
@ -70,8 +95,9 @@ static bool _GBACoreInit(struct mCore* core) {
#ifndef DISABLE_THREADING
gbacore->threadedVideo = false;
GBAVideoThreadProxyRendererCreate(&gbacore->threadProxy, &gbacore->renderer.d);
mVideoThreadProxyCreate(&gbacore->threadProxy);
#endif
gbacore->proxyRenderer.logger = NULL;
gbacore->keys = 0;
gba->keySource = &gbacore->keys;
@ -281,7 +307,9 @@ static void _GBACoreReset(struct mCore* core) {
struct GBAVideoRenderer* renderer = &gbacore->renderer.d;
#ifndef DISABLE_THREADING
if (gbacore->threadedVideo) {
renderer = &gbacore->threadProxy.d;
gbacore->proxyRenderer.logger = &gbacore->threadProxy.d;
GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, renderer);
renderer = &gbacore->proxyRenderer.d;
}
#endif
GBAVideoAssociateRenderer(&gba->video, renderer);
@ -326,7 +354,7 @@ static void _GBACoreReset(struct mCore* core) {
mCoreConfigDirectory(path, PATH_MAX);
strncat(path, PATH_SEP "gba_bios.bin", PATH_MAX - strlen(path));
bios = VFileOpen(path, O_RDONLY);
if (bios && GBIsBIOS(bios)) {
if (bios && GBAIsBIOS(bios)) {
found = true;
} else if (bios) {
bios->close(bios);
@ -378,16 +406,19 @@ static bool _GBACoreSaveState(struct mCore* core, void* state) {
static void _GBACoreSetKeys(struct mCore* core, uint32_t keys) {
struct GBACore* gbacore = (struct GBACore*) core;
gbacore->keys = keys;
GBATestKeypadIRQ(core->board);
}
static void _GBACoreAddKeys(struct mCore* core, uint32_t keys) {
struct GBACore* gbacore = (struct GBACore*) core;
gbacore->keys |= keys;
GBATestKeypadIRQ(core->board);
}
static void _GBACoreClearKeys(struct mCore* core, uint32_t keys) {
struct GBACore* gbacore = (struct GBACore*) core;
gbacore->keys &= ~keys;
GBATestKeypadIRQ(core->board);
}
static int32_t _GBACoreFrameCounter(const struct mCore* core) {
@ -413,14 +444,21 @@ static void _GBACoreGetGameCode(const struct mCore* core, char* title) {
GBAGetGameCode(core->board, title);
}
static void _GBACoreSetRotation(struct mCore* core, struct mRotationSource* rotation) {
static void _GBACoreSetPeripheral(struct mCore* core, int type, void* periph) {
struct GBA* gba = core->board;
gba->rotationSource = rotation;
}
static void _GBACoreSetRumble(struct mCore* core, struct mRumble* rumble) {
struct GBA* gba = core->board;
gba->rumble = rumble;
switch (type) {
case mPERIPH_ROTATION:
gba->rotationSource = periph;
break;
case mPERIPH_RUMBLE:
gba->rumble = periph;
break;
case mPERIPH_GBA_LUMINANCE:
gba->luminanceSource = periph;
break;
default:
return;
}
}
static uint32_t _GBACoreBusRead8(struct mCore* core, uint32_t address) {
@ -529,6 +567,10 @@ static void _GBACoreDetachDebugger(struct mCore* core) {
GBADetachDebugger(core->board);
core->debugger = NULL;
}
static void _GBACoreLoadSymbols(struct mCore* core, struct VFile* vf) {
// TODO
}
#endif
static struct mCheatDevice* _GBACoreCheatDevice(struct mCore* core) {
@ -582,6 +624,81 @@ static bool _GBACoreSavedataRestore(struct mCore* core, const void* sram, size_t
return success;
}
static size_t _GBACoreListVideoLayers(const struct mCore* core, const struct mCoreChannelInfo** info) {
UNUSED(core);
*info = _GBAVideoLayers;
return sizeof(_GBAVideoLayers) / sizeof(*_GBAVideoLayers);
}
static size_t _GBACoreListAudioChannels(const struct mCore* core, const struct mCoreChannelInfo** info) {
UNUSED(core);
*info = _GBAAudioChannels;
return sizeof(_GBAAudioChannels) / sizeof(*_GBAAudioChannels);
}
static void _GBACoreEnableVideoLayer(struct mCore* core, size_t id, bool enable) {
struct GBA* gba = core->board;
switch (id) {
case 0:
case 1:
case 2:
case 3:
gba->video.renderer->disableBG[id] = !enable;
break;
case 4:
gba->video.renderer->disableOBJ = !enable;
break;
default:
break;
}
}
static void _GBACoreEnableAudioChannel(struct mCore* core, size_t id, bool enable) {
struct GBA* gba = core->board;
switch (id) {
case 0:
case 1:
case 2:
case 3:
gba->audio.psg.forceDisableCh[id] = !enable;
break;
case 4:
gba->audio.forceDisableChA = !enable;
case 5:
gba->audio.forceDisableChB = !enable;
break;
default:
break;
}
}
static void _GBACoreStartVideoLog(struct mCore* core, struct mVideoLogContext* context) {
struct GBACore* gbacore = (struct GBACore*) core;
struct GBA* gba = core->board;
gbacore->logContext = context;
struct GBASerializedState* state = mVideoLogContextInitialState(context, NULL);
state->id = 0;
state->cpu.gprs[ARM_PC] = BASE_WORKING_RAM;
int channelId = mVideoLoggerAddChannel(context);
gbacore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger));
mVideoLoggerRendererCreate(gbacore->proxyRenderer.logger, false);
mVideoLoggerAttachChannel(gbacore->proxyRenderer.logger, context, channelId);
gbacore->proxyRenderer.logger->block = false;
GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, &gbacore->renderer.d);
GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer);
}
static void _GBACoreEndVideoLog(struct mCore* core) {
struct GBACore* gbacore = (struct GBACore*) core;
struct GBA* gba = core->board;
GBAVideoProxyRendererUnshim(&gba->video, &gbacore->proxyRenderer);
free(gbacore->proxyRenderer.logger);
gbacore->proxyRenderer.logger = NULL;
}
struct mCore* GBACoreCreate(void) {
struct GBACore* gbacore = malloc(sizeof(*gbacore));
struct mCore* core = &gbacore->d;
@ -627,8 +744,7 @@ struct mCore* GBACoreCreate(void) {
core->frequency = _GBACoreFrequency;
core->getGameTitle = _GBACoreGetGameTitle;
core->getGameCode = _GBACoreGetGameCode;
core->setRotation = _GBACoreSetRotation;
core->setRumble = _GBACoreSetRumble;
core->setPeripheral = _GBACoreSetPeripheral;
core->busRead8 = _GBACoreBusRead8;
core->busRead16 = _GBACoreBusRead16;
core->busRead32 = _GBACoreBusRead32;
@ -647,9 +763,125 @@ struct mCore* GBACoreCreate(void) {
core->cliDebuggerSystem = _GBACoreCliDebuggerSystem;
core->attachDebugger = _GBACoreAttachDebugger;
core->detachDebugger = _GBACoreDetachDebugger;
core->loadSymbols = _GBACoreLoadSymbols;
#endif
core->cheatDevice = _GBACoreCheatDevice;
core->savedataClone = _GBACoreSavedataClone;
core->savedataRestore = _GBACoreSavedataRestore;
core->listVideoLayers = _GBACoreListVideoLayers;
core->listAudioChannels = _GBACoreListAudioChannels;
core->enableVideoLayer = _GBACoreEnableVideoLayer;
core->enableAudioChannel = _GBACoreEnableAudioChannel;
#ifndef MINIMAL_CORE
core->startVideoLog = _GBACoreStartVideoLog;
core->endVideoLog = _GBACoreEndVideoLog;
#endif
return core;
}
#ifndef MINIMAL_CORE
static void _GBAVLPStartFrameCallback(void *context) {
struct mCore* core = context;
struct GBACore* gbacore = (struct GBACore*) core;
struct GBA* gba = core->board;
if (!mVideoLoggerRendererRun(gbacore->proxyRenderer.logger, true)) {
GBAVideoProxyRendererUnshim(&gba->video, &gbacore->proxyRenderer);
mVideoLogContextRewind(gbacore->logContext, core);
GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer);
}
}
static bool _GBAVLPInit(struct mCore* core) {
struct GBACore* gbacore = (struct GBACore*) core;
if (!_GBACoreInit(core)) {
return false;
}
gbacore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger));
mVideoLoggerRendererCreate(gbacore->proxyRenderer.logger, true);
GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, NULL);
memset(&gbacore->logCallbacks, 0, sizeof(gbacore->logCallbacks));
gbacore->logCallbacks.videoFrameStarted = _GBAVLPStartFrameCallback;
gbacore->logCallbacks.context = core;
core->addCoreCallbacks(core, &gbacore->logCallbacks);
return true;
}
static void _GBAVLPDeinit(struct mCore* core) {
struct GBACore* gbacore = (struct GBACore*) core;
if (gbacore->logContext) {
mVideoLogContextDestroy(core, gbacore->logContext);
}
_GBACoreDeinit(core);
}
static void _GBAVLPReset(struct mCore* core) {
struct GBACore* gbacore = (struct GBACore*) core;
struct GBA* gba = (struct GBA*) core->board;
if (gba->video.renderer == &gbacore->proxyRenderer.d) {
GBAVideoProxyRendererUnshim(&gba->video, &gbacore->proxyRenderer);
} else if (gbacore->renderer.outputBuffer) {
struct GBAVideoRenderer* renderer = &gbacore->renderer.d;
GBAVideoAssociateRenderer(&gba->video, renderer);
}
ARMReset(core->cpu);
mVideoLogContextRewind(gbacore->logContext, core);
GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer);
// Make sure CPU loop never spins
GBAHalt(gba);
gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL);
gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL);
}
static bool _GBAVLPLoadROM(struct mCore* core, struct VFile* vf) {
struct GBACore* gbacore = (struct GBACore*) core;
gbacore->logContext = mVideoLogContextCreate(NULL);
if (!mVideoLogContextLoad(gbacore->logContext, vf)) {
mVideoLogContextDestroy(core, gbacore->logContext);
gbacore->logContext = NULL;
return false;
}
mVideoLoggerAttachChannel(gbacore->proxyRenderer.logger, gbacore->logContext, 0);
return true;
}
static bool _GBAVLPLoadState(struct mCore* core, const void* state) {
struct GBA* gba = (struct GBA*) core->board;
gba->timing.root = NULL;
gba->cpu->gprs[ARM_PC] = BASE_WORKING_RAM;
gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]);
// Make sure CPU loop never spins
GBAHalt(gba);
gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL);
gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL);
GBAVideoDeserialize(&gba->video, state);
GBAIODeserialize(gba, state);
GBAAudioReset(&gba->audio);
return true;
}
static bool _returnTrue(struct VFile* vf) {
UNUSED(vf);
return true;
}
struct mCore* GBAVideoLogPlayerCreate(void) {
struct mCore* core = GBACoreCreate();
core->init = _GBAVLPInit;
core->deinit = _GBAVLPDeinit;
core->reset = _GBAVLPReset;
core->loadROM = _GBAVLPLoadROM;
core->loadState = _GBAVLPLoadState;
core->isROM = _returnTrue;
return core;
}
#else
struct mCore* GBAVideoLogPlayerCreate(void) {
return false;
}
#endif

283
src/gba/extra/proxy.c Normal file
View File

@ -0,0 +1,283 @@
/* Copyright (c) 2013-2017 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/internal/gba/renderers/proxy.h>
#include <mgba/core/tile-cache.h>
#include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/io.h>
static void GBAVideoProxyRendererInit(struct GBAVideoRenderer* renderer);
static void GBAVideoProxyRendererReset(struct GBAVideoRenderer* renderer);
static void GBAVideoProxyRendererDeinit(struct GBAVideoRenderer* renderer);
static uint16_t GBAVideoProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
static void GBAVideoProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address);
static void GBAVideoProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
static void GBAVideoProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
static void GBAVideoProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
static void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer);
static void GBAVideoProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels);
static void GBAVideoProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels);
static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* packet);
static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address);
void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct GBAVideoRenderer* backend) {
renderer->d.init = GBAVideoProxyRendererInit;
renderer->d.reset = GBAVideoProxyRendererReset;
renderer->d.deinit = GBAVideoProxyRendererDeinit;
renderer->d.writeVideoRegister = GBAVideoProxyRendererWriteVideoRegister;
renderer->d.writeVRAM = GBAVideoProxyRendererWriteVRAM;
renderer->d.writeOAM = GBAVideoProxyRendererWriteOAM;
renderer->d.writePalette = GBAVideoProxyRendererWritePalette;
renderer->d.drawScanline = GBAVideoProxyRendererDrawScanline;
renderer->d.finishFrame = GBAVideoProxyRendererFinishFrame;
renderer->d.getPixels = GBAVideoProxyRendererGetPixels;
renderer->d.putPixels = GBAVideoProxyRendererPutPixels;
renderer->d.disableBG[0] = false;
renderer->d.disableBG[1] = false;
renderer->d.disableBG[2] = false;
renderer->d.disableBG[3] = false;
renderer->d.disableOBJ = false;
renderer->logger->context = renderer;
renderer->logger->parsePacket = _parsePacket;
renderer->logger->vramBlock = _vramBlock;
renderer->logger->paletteSize = SIZE_PALETTE_RAM;
renderer->logger->vramSize = SIZE_VRAM;
renderer->logger->oamSize = SIZE_OAM;
renderer->backend = backend;
}
static void _init(struct GBAVideoProxyRenderer* proxyRenderer) {
mVideoLoggerRendererInit(proxyRenderer->logger);
if (proxyRenderer->logger->block) {
proxyRenderer->backend->palette = proxyRenderer->logger->palette;
proxyRenderer->backend->vram = proxyRenderer->logger->vram;
proxyRenderer->backend->oam = (union GBAOAM*) proxyRenderer->logger->oam;
proxyRenderer->backend->cache = NULL;
}
}
static void _reset(struct GBAVideoProxyRenderer* proxyRenderer) {
memcpy(proxyRenderer->logger->oam, &proxyRenderer->d.oam->raw, SIZE_OAM);
memcpy(proxyRenderer->logger->palette, proxyRenderer->d.palette, SIZE_PALETTE_RAM);
memcpy(proxyRenderer->logger->vram, proxyRenderer->d.vram, SIZE_VRAM);
mVideoLoggerRendererReset(proxyRenderer->logger);
}
void GBAVideoProxyRendererShim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer) {
if ((renderer->backend && video->renderer != renderer->backend) || video->renderer == &renderer->d) {
return;
}
renderer->backend = video->renderer;
video->renderer = &renderer->d;
renderer->d.cache = renderer->backend->cache;
renderer->d.palette = video->palette;
renderer->d.vram = video->vram;
renderer->d.oam = &video->oam;
_init(renderer);
_reset(renderer);
}
void GBAVideoProxyRendererUnshim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer) {
if (video->renderer != &renderer->d) {
return;
}
renderer->backend->cache = video->renderer->cache;
video->renderer = renderer->backend;
renderer->backend->palette = video->palette;
renderer->backend->vram = video->vram;
renderer->backend->oam = &video->oam;
mVideoLoggerRendererDeinit(renderer->logger);
}
void GBAVideoProxyRendererInit(struct GBAVideoRenderer* renderer) {
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
_init(proxyRenderer);
proxyRenderer->backend->init(proxyRenderer->backend);
}
void GBAVideoProxyRendererReset(struct GBAVideoRenderer* renderer) {
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
_reset(proxyRenderer);
proxyRenderer->backend->reset(proxyRenderer->backend);
}
void GBAVideoProxyRendererDeinit(struct GBAVideoRenderer* renderer) {
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
proxyRenderer->backend->deinit(proxyRenderer->backend);
mVideoLoggerRendererDeinit(proxyRenderer->logger);
}
static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* item) {
struct GBAVideoProxyRenderer* proxyRenderer = logger->context;
switch (item->type) {
case DIRTY_REGISTER:
proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value);
break;
case DIRTY_PALETTE:
if (item->address < SIZE_PALETTE_RAM) {
logger->palette[item->address >> 1] = item->value;
proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value);
}
break;
case DIRTY_OAM:
if (item->address < SIZE_PALETTE_RAM) {
logger->oam[item->address] = item->value;
proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address);
}
break;
case DIRTY_VRAM:
if (item->address <= SIZE_VRAM - 0x1000) {
logger->readData(logger, &logger->vram[item->address >> 1], 0x1000, true);
proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address);
}
break;
case DIRTY_SCANLINE:
if (item->address < VIDEO_VERTICAL_PIXELS) {
proxyRenderer->backend->drawScanline(proxyRenderer->backend, item->address);
}
break;
case DIRTY_FRAME:
proxyRenderer->backend->finishFrame(proxyRenderer->backend);
break;
case DIRTY_FLUSH:
return false;
default:
return false;
}
return true;
}
static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address) {
struct GBAVideoProxyRenderer* proxyRenderer = logger->context;
return &proxyRenderer->d.vram[address >> 1];
}
uint16_t GBAVideoProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
switch (address) {
case REG_BG0CNT:
case REG_BG1CNT:
case REG_BG2CNT:
case REG_BG3CNT:
value &= 0xFFCF;
break;
case REG_BG0HOFS:
case REG_BG0VOFS:
case REG_BG1HOFS:
case REG_BG1VOFS:
case REG_BG2HOFS:
case REG_BG2VOFS:
case REG_BG3HOFS:
case REG_BG3VOFS:
value &= 0x01FF;
break;
}
if (address > REG_BLDY) {
return value;
}
mVideoLoggerRendererWriteVideoRegister(proxyRenderer->logger, address, value);
if (!proxyRenderer->logger->block) {
proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, address, value);
}
return value;
}
void GBAVideoProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
mVideoLoggerRendererWriteVRAM(proxyRenderer->logger, address);
if (!proxyRenderer->logger->block) {
proxyRenderer->backend->writeVRAM(proxyRenderer->backend, address);
}
if (renderer->cache) {
mTileCacheWriteVRAM(renderer->cache, address);
}
}
void GBAVideoProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
mVideoLoggerRendererWritePalette(proxyRenderer->logger, address, value);
if (!proxyRenderer->logger->block) {
proxyRenderer->backend->writePalette(proxyRenderer->backend, address, value);
}
if (renderer->cache) {
mTileCacheWritePalette(renderer->cache, address);
}
}
void GBAVideoProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
if (!proxyRenderer->logger->block) {
proxyRenderer->backend->writeOAM(proxyRenderer->backend, oam);
}
mVideoLoggerRendererWriteOAM(proxyRenderer->logger, oam, proxyRenderer->d.oam->raw[oam]);
}
void GBAVideoProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
if (!proxyRenderer->logger->block) {
proxyRenderer->backend->drawScanline(proxyRenderer->backend, y);
}
mVideoLoggerRendererDrawScanline(proxyRenderer->logger, y);
if (proxyRenderer->logger->block && proxyRenderer->logger->wake) {
proxyRenderer->logger->wake(proxyRenderer->logger, y);
}
}
void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
proxyRenderer->logger->lock(proxyRenderer->logger);
proxyRenderer->logger->wait(proxyRenderer->logger);
}
proxyRenderer->backend->finishFrame(proxyRenderer->backend);
mVideoLoggerRendererFinishFrame(proxyRenderer->logger);
mVideoLoggerRendererFlush(proxyRenderer->logger);
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
proxyRenderer->logger->unlock(proxyRenderer->logger);
}
}
static void GBAVideoProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) {
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
proxyRenderer->logger->lock(proxyRenderer->logger);
// Insert an extra item into the queue to make sure it gets flushed
mVideoLoggerRendererFlush(proxyRenderer->logger);
proxyRenderer->logger->wait(proxyRenderer->logger);
}
proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels);
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
proxyRenderer->logger->unlock(proxyRenderer->logger);
}
}
static void GBAVideoProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) {
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
proxyRenderer->logger->lock(proxyRenderer->logger);
// Insert an extra item into the queue to make sure it gets flushed
mVideoLoggerRendererFlush(proxyRenderer->logger);
proxyRenderer->logger->wait(proxyRenderer->logger);
}
proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels);
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
proxyRenderer->logger->unlock(proxyRenderer->logger);
}
}

View File

@ -76,6 +76,8 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
gba->sio.p = gba;
GBASIOInit(&gba->sio);
GBAHardwareInit(&gba->memory.hw, NULL);
gba->springIRQ = 0;
gba->keySource = 0;
gba->rotationSource = 0;
@ -89,8 +91,6 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
gba->stream = NULL;
gba->keyCallback = NULL;
gba->stopCallback = NULL;
gba->stopCallback = NULL;
mCoreCallbacksListInit(&gba->coreCallbacks, 0);
gba->biosChecksum = GBAChecksum(gba->memory.bios, SIZE_BIOS);
@ -270,6 +270,7 @@ static void GBAProcessEvents(struct ARMCore* cpu) {
}
}
#ifdef USE_DEBUGGERS
void GBAAttachDebugger(struct GBA* gba, struct mDebugger* debugger) {
gba->debugger = (struct ARMDebugger*) debugger->platform;
gba->debugger->setSoftwareBreakpoint = _setSoftwareBreakpoint;
@ -279,10 +280,13 @@ void GBAAttachDebugger(struct GBA* gba, struct mDebugger* debugger) {
}
void GBADetachDebugger(struct GBA* gba) {
gba->debugger = 0;
if (gba->debugger) {
ARMHotplugDetach(gba->cpu, CPU_COMPONENT_DEBUGGER);
gba->cpu->components[CPU_COMPONENT_DEBUGGER] = 0;
}
gba->cpu->components[CPU_COMPONENT_DEBUGGER] = NULL;
gba->debugger = NULL;
}
#endif
bool GBALoadMB(struct GBA* gba, struct VFile* vf) {
GBAUnloadROM(gba);
@ -293,14 +297,9 @@ bool GBALoadMB(struct GBA* gba, struct VFile* vf) {
gba->pristineRomSize = SIZE_WORKING_RAM;
}
gba->isPristine = true;
#ifdef _3DS
if (gba->pristineRomSize <= romBufferSize) {
gba->memory.wram = romBuffer;
vf->read(vf, romBuffer, gba->pristineRomSize);
}
#else
gba->memory.wram = vf->map(vf, gba->pristineRomSize, MAP_READ);
#endif
gba->memory.wram = anonymousMemoryMap(SIZE_WORKING_RAM);
memset(gba->memory.wram, 0, SIZE_WORKING_RAM);
vf->read(vf, gba->memory.wram, gba->pristineRomSize);
if (!gba->memory.wram) {
mLOG(GBA, WARN, "Couldn't map ROM");
return false;
@ -410,10 +409,6 @@ void GBAApplyPatch(struct GBA* gba, struct Patch* patch) {
}
void GBAWriteIE(struct GBA* gba, uint16_t value) {
if (value & (1 << IRQ_KEYPAD)) {
mLOG(GBA, STUB, "Keypad interrupts not implemented");
}
if (gba->memory.io[REG_IME >> 1] && value & gba->memory.io[REG_IF >> 1]) {
ARMRaiseIRQ(gba->cpu);
}
@ -450,11 +445,14 @@ void GBAHalt(struct GBA* gba) {
}
void GBAStop(struct GBA* gba) {
if (!gba->stopCallback) {
return;
size_t c;
for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) {
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gba->coreCallbacks, c);
if (callbacks->sleep) {
callbacks->sleep(callbacks->context);
}
}
gba->cpu->nextEvent = gba->cpu->cycles;
gba->stopCallback->stop(gba->stopCallback);
}
void GBADebug(struct GBA* gba, uint16_t flags) {
@ -635,7 +633,7 @@ void GBABreakpoint(struct ARMCore* cpu, int immediate) {
}
void GBAFrameStarted(struct GBA* gba) {
UNUSED(gba);
GBATestKeypadIRQ(gba);
size_t c;
for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) {
@ -682,6 +680,29 @@ void GBAFrameEnded(struct GBA* gba) {
}
}
void GBATestKeypadIRQ(struct GBA* gba) {
uint16_t keycnt = gba->memory.io[REG_KEYCNT >> 1];
if (!(keycnt & 0x4000)) {
return;
}
int isAnd = keycnt & 0x8000;
uint16_t keyInput;
if (!gba->keySource) {
// TODO?
return;
}
keycnt &= 0x3FF;
keyInput = *gba->keySource;
if (isAnd && keycnt == keyInput) {
GBARaiseIRQ(gba, IRQ_KEYPAD);
} else if (!isAnd && keycnt & keyInput) {
GBARaiseIRQ(gba, IRQ_KEYPAD);
}
}
void GBASetBreakpoint(struct GBA* gba, struct mCPUComponent* component, uint32_t address, enum ExecutionMode mode, uint32_t* opcode) {
size_t immediate;
for (immediate = 0; immediate < gba->cpu->numComponents; ++immediate) {

View File

@ -77,6 +77,9 @@ void GBAHardwareClear(struct GBACartridgeHardware* hw) {
}
void GBAHardwareGPIOWrite(struct GBACartridgeHardware* hw, uint32_t address, uint16_t value) {
if (!hw->gpioBase) {
return;
}
switch (address) {
case GPIO_REG_DATA:
hw->pinState &= ~hw->direction;

View File

@ -537,6 +537,11 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
break;
// Interrupts and misc
case REG_KEYCNT:
value &= 0xC3FF;
gba->memory.io[address >> 1] = value;
GBATestKeypadIRQ(gba);
return;
case REG_WAITCNT:
value &= 0x5FFF;
GBAAdjustWaitstates(gba, value);
@ -689,6 +694,7 @@ bool GBAIOIsReadConstant(uint32_t address) {
case REG_TM2CNT_HI:
case REG_TM3CNT_HI:
case REG_KEYINPUT:
case REG_KEYCNT:
case REG_IE:
return true;
}
@ -721,6 +727,9 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
uint16_t input = 0x3FF;
if (gba->keyCallback) {
input = gba->keyCallback->readKeys(gba->keyCallback);
if (gba->keySource) {
*gba->keySource = input;
}
} else if (gba->keySource) {
input = *gba->keySource;
}
@ -814,7 +823,6 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
break;
case REG_SOUNDBIAS:
case REG_KEYCNT:
case REG_POSTFLG:
mLOG(GBA_IO, STUB, "Stub I/O register read: %03x", address);
break;
@ -863,6 +871,7 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
case REG_TM1CNT_HI:
case REG_TM2CNT_HI:
case REG_TM3CNT_HI:
case REG_KEYCNT:
case REG_SIOMULTI0:
case REG_SIOMULTI1:
case REG_SIOMULTI2:

View File

@ -102,7 +102,7 @@ void GBAMemoryDeinit(struct GBA* gba) {
}
void GBAMemoryReset(struct GBA* gba) {
if (gba->memory.rom || gba->memory.fullBios) {
if (gba->memory.rom || gba->memory.fullBios || !gba->memory.wram) {
// Not multiboot
if (gba->memory.wram) {
mappedMemoryFree(gba->memory.wram, SIZE_WORKING_RAM);
@ -274,10 +274,10 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
break;
case REGION_VRAM:
if (address & 0x10000) {
cpu->memory.activeRegion = (uint32_t*) &gba->video.renderer->vram[0x8000];
cpu->memory.activeRegion = (uint32_t*) &gba->video.vram[0x8000];
cpu->memory.activeMask = 0x00007FFF;
} else {
cpu->memory.activeRegion = (uint32_t*) gba->video.renderer->vram;
cpu->memory.activeRegion = (uint32_t*) gba->video.vram;
cpu->memory.activeMask = 0x0000FFFF;
}
break;
@ -377,9 +377,9 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) {
#define LOAD_VRAM \
if ((address & 0x0001FFFF) < SIZE_VRAM) { \
LOAD_32(value, address & 0x0001FFFC, gba->video.renderer->vram); \
LOAD_32(value, address & 0x0001FFFC, gba->video.vram); \
} else { \
LOAD_32(value, address & 0x00017FFC, gba->video.renderer->vram); \
LOAD_32(value, address & 0x00017FFC, gba->video.vram); \
} \
wait += waitstatesRegion[REGION_VRAM];
@ -507,9 +507,9 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
break;
case REGION_VRAM:
if ((address & 0x0001FFFF) < SIZE_VRAM) {
LOAD_16(value, address & 0x0001FFFE, gba->video.renderer->vram);
LOAD_16(value, address & 0x0001FFFE, gba->video.vram);
} else {
LOAD_16(value, address & 0x00017FFE, gba->video.renderer->vram);
LOAD_16(value, address & 0x00017FFE, gba->video.vram);
}
break;
case REGION_OAM:
@ -608,9 +608,9 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
break;
case REGION_VRAM:
if ((address & 0x0001FFFF) < SIZE_VRAM) {
value = ((uint8_t*) gba->video.renderer->vram)[address & 0x0001FFFF];
value = ((uint8_t*) gba->video.vram)[address & 0x0001FFFF];
} else {
value = ((uint8_t*) gba->video.renderer->vram)[address & 0x00017FFF];
value = ((uint8_t*) gba->video.vram)[address & 0x00017FFF];
}
break;
case REGION_OAM:
@ -691,11 +691,11 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
#define STORE_VRAM \
if ((address & 0x0001FFFF) < SIZE_VRAM) { \
STORE_32(value, address & 0x0001FFFC, gba->video.renderer->vram); \
STORE_32(value, address & 0x0001FFFC, gba->video.vram); \
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC) + 2); \
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC)); \
} else { \
STORE_32(value, address & 0x00017FFC, gba->video.renderer->vram); \
STORE_32(value, address & 0x00017FFC, gba->video.vram); \
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC) + 2); \
gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC)); \
} \
@ -796,10 +796,10 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle
break;
case REGION_VRAM:
if ((address & 0x0001FFFF) < SIZE_VRAM) {
STORE_16(value, address & 0x0001FFFE, gba->video.renderer->vram);
STORE_16(value, address & 0x0001FFFE, gba->video.vram);
gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE);
} else {
STORE_16(value, address & 0x00017FFE, gba->video.renderer->vram);
STORE_16(value, address & 0x00017FFE, gba->video.vram);
gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x00017FFE);
}
break;
@ -1052,11 +1052,11 @@ void GBAPatch32(struct ARMCore* cpu, uint32_t address, int32_t value, int32_t* o
break;
case REGION_VRAM:
if ((address & 0x0001FFFF) < SIZE_VRAM) {
LOAD_32(oldValue, address & 0x0001FFFC, gba->video.renderer->vram);
STORE_32(value, address & 0x0001FFFC, gba->video.renderer->vram);
LOAD_32(oldValue, address & 0x0001FFFC, gba->video.vram);
STORE_32(value, address & 0x0001FFFC, gba->video.vram);
} else {
LOAD_32(oldValue, address & 0x00017FFC, gba->video.renderer->vram);
STORE_32(value, address & 0x00017FFC, gba->video.renderer->vram);
LOAD_32(oldValue, address & 0x00017FFC, gba->video.vram);
STORE_32(value, address & 0x00017FFC, gba->video.vram);
}
break;
case REGION_OAM:
@ -1121,11 +1121,11 @@ void GBAPatch16(struct ARMCore* cpu, uint32_t address, int16_t value, int16_t* o
break;
case REGION_VRAM:
if ((address & 0x0001FFFF) < SIZE_VRAM) {
LOAD_16(oldValue, address & 0x0001FFFE, gba->video.renderer->vram);
STORE_16(value, address & 0x0001FFFE, gba->video.renderer->vram);
LOAD_16(oldValue, address & 0x0001FFFE, gba->video.vram);
STORE_16(value, address & 0x0001FFFE, gba->video.vram);
} else {
LOAD_16(oldValue, address & 0x00017FFE, gba->video.renderer->vram);
STORE_16(value, address & 0x00017FFE, gba->video.renderer->vram);
LOAD_16(oldValue, address & 0x00017FFE, gba->video.vram);
STORE_16(value, address & 0x00017FFE, gba->video.vram);
}
break;
case REGION_OAM:
@ -1552,6 +1552,9 @@ void _pristineCow(struct GBA* gba) {
void* newRom = anonymousMemoryMap(SIZE_CART0);
memcpy(newRom, gba->memory.rom, gba->memory.romSize);
memset(((uint8_t*) newRom) + gba->memory.romSize, 0xFF, SIZE_CART0 - gba->memory.romSize);
if (gba->cpu->memory.activeRegion == gba->memory.rom) {
gba->cpu->memory.activeRegion = newRom;
}
if (gba->romVf) {
#ifndef _3DS
gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->memory.romSize);

View File

@ -1,390 +0,0 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/internal/gba/renderers/thread-proxy.h>
#include <mgba/core/tile-cache.h>
#include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/io.h>
#include <mgba-util/memory.h>
#ifndef DISABLE_THREADING
enum GBAVideoDirtyType {
DIRTY_DUMMY = 0,
DIRTY_REGISTER,
DIRTY_OAM,
DIRTY_PALETTE,
DIRTY_VRAM,
DIRTY_SCANLINE,
DIRTY_FLUSH
};
struct GBAVideoDirtyInfo {
enum GBAVideoDirtyType type;
uint32_t address;
uint16_t value;
uint32_t padding;
};
static void GBAVideoThreadProxyRendererInit(struct GBAVideoRenderer* renderer);
static void GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer);
static void GBAVideoThreadProxyRendererDeinit(struct GBAVideoRenderer* renderer);
static uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
static void GBAVideoThreadProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address);
static void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value);
static void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam);
static void GBAVideoThreadProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y);
static void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer);
static void GBAVideoThreadProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels);
static void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels);
static THREAD_ENTRY _proxyThread(void* renderer);
void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend) {
renderer->d.init = GBAVideoThreadProxyRendererInit;
renderer->d.reset = GBAVideoThreadProxyRendererReset;
renderer->d.deinit = GBAVideoThreadProxyRendererDeinit;
renderer->d.writeVideoRegister = GBAVideoThreadProxyRendererWriteVideoRegister;
renderer->d.writeVRAM = GBAVideoThreadProxyRendererWriteVRAM;
renderer->d.writeOAM = GBAVideoThreadProxyRendererWriteOAM;
renderer->d.writePalette = GBAVideoThreadProxyRendererWritePalette;
renderer->d.drawScanline = GBAVideoThreadProxyRendererDrawScanline;
renderer->d.finishFrame = GBAVideoThreadProxyRendererFinishFrame;
renderer->d.getPixels = GBAVideoThreadProxyRendererGetPixels;
renderer->d.putPixels = GBAVideoThreadProxyRendererPutPixels;
renderer->d.disableBG[0] = false;
renderer->d.disableBG[1] = false;
renderer->d.disableBG[2] = false;
renderer->d.disableBG[3] = false;
renderer->d.disableOBJ = false;
renderer->backend = backend;
}
void GBAVideoThreadProxyRendererInit(struct GBAVideoRenderer* renderer) {
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
ConditionInit(&proxyRenderer->fromThreadCond);
ConditionInit(&proxyRenderer->toThreadCond);
MutexInit(&proxyRenderer->mutex);
RingFIFOInit(&proxyRenderer->dirtyQueue, 0x40000);
proxyRenderer->vramProxy = anonymousMemoryMap(SIZE_VRAM);
proxyRenderer->backend->palette = proxyRenderer->paletteProxy;
proxyRenderer->backend->vram = proxyRenderer->vramProxy;
proxyRenderer->backend->oam = &proxyRenderer->oamProxy;
proxyRenderer->backend->cache = NULL;
proxyRenderer->backend->init(proxyRenderer->backend);
proxyRenderer->vramDirtyBitmap = 0;
proxyRenderer->threadState = PROXY_THREAD_IDLE;
ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
}
void GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer) {
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
MutexLock(&proxyRenderer->mutex);
while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
ConditionWake(&proxyRenderer->toThreadCond);
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
}
memcpy(&proxyRenderer->oamProxy.raw, &renderer->oam->raw, SIZE_OAM);
memcpy(proxyRenderer->paletteProxy, renderer->palette, SIZE_PALETTE_RAM);
memcpy(proxyRenderer->vramProxy, renderer->vram, SIZE_VRAM);
proxyRenderer->backend->reset(proxyRenderer->backend);
MutexUnlock(&proxyRenderer->mutex);
}
void GBAVideoThreadProxyRendererDeinit(struct GBAVideoRenderer* renderer) {
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
bool waiting = false;
MutexLock(&proxyRenderer->mutex);
while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
ConditionWake(&proxyRenderer->toThreadCond);
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
}
if (proxyRenderer->threadState == PROXY_THREAD_IDLE) {
proxyRenderer->threadState = PROXY_THREAD_STOPPED;
ConditionWake(&proxyRenderer->toThreadCond);
waiting = true;
}
MutexUnlock(&proxyRenderer->mutex);
if (waiting) {
ThreadJoin(proxyRenderer->thread);
}
ConditionDeinit(&proxyRenderer->fromThreadCond);
ConditionDeinit(&proxyRenderer->toThreadCond);
MutexDeinit(&proxyRenderer->mutex);
proxyRenderer->backend->deinit(proxyRenderer->backend);
mappedMemoryFree(proxyRenderer->vramProxy, SIZE_VRAM);
}
void _proxyThreadRecover(struct GBAVideoThreadProxyRenderer* proxyRenderer) {
MutexLock(&proxyRenderer->mutex);
if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
MutexUnlock(&proxyRenderer->mutex);
return;
}
RingFIFOClear(&proxyRenderer->dirtyQueue);
MutexUnlock(&proxyRenderer->mutex);
ThreadJoin(proxyRenderer->thread);
proxyRenderer->threadState = PROXY_THREAD_IDLE;
ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer);
}
static bool _writeData(struct GBAVideoThreadProxyRenderer* proxyRenderer, void* data, size_t length) {
while (!RingFIFOWrite(&proxyRenderer->dirtyQueue, data, length)) {
mLOG(GBA_VIDEO, DEBUG, "Can't write %"PRIz"u bytes. Proxy thread asleep?", length);
MutexLock(&proxyRenderer->mutex);
if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!");
MutexUnlock(&proxyRenderer->mutex);
return false;
}
ConditionWake(&proxyRenderer->toThreadCond);
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
MutexUnlock(&proxyRenderer->mutex);
}
return true;
}
uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
switch (address) {
case REG_BG0CNT:
case REG_BG1CNT:
case REG_BG2CNT:
case REG_BG3CNT:
value &= 0xFFCF;
break;
case REG_BG0HOFS:
case REG_BG0VOFS:
case REG_BG1HOFS:
case REG_BG1VOFS:
case REG_BG2HOFS:
case REG_BG2VOFS:
case REG_BG3HOFS:
case REG_BG3VOFS:
value &= 0x01FF;
break;
}
if (address > REG_BLDY) {
return value;
}
struct GBAVideoDirtyInfo dirty = {
DIRTY_REGISTER,
address,
value,
0xDEADBEEF,
};
_writeData(proxyRenderer, &dirty, sizeof(dirty));
return value;
}
void GBAVideoThreadProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) {
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
int bit = 1 << (address >> 12);
if (proxyRenderer->vramDirtyBitmap & bit) {
return;
}
proxyRenderer->vramDirtyBitmap |= bit;
if (renderer->cache) {
mTileCacheWriteVRAM(renderer->cache, address);
}
}
void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) {
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
struct GBAVideoDirtyInfo dirty = {
DIRTY_PALETTE,
address,
value,
0xDEADBEEF,
};
_writeData(proxyRenderer, &dirty, sizeof(dirty));
if (renderer->cache) {
mTileCacheWritePalette(renderer->cache, address);
}
}
void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) {
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
struct GBAVideoDirtyInfo dirty = {
DIRTY_OAM,
oam,
proxyRenderer->d.oam->raw[oam],
0xDEADBEEF,
};
_writeData(proxyRenderer, &dirty, sizeof(dirty));
}
void GBAVideoThreadProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
if (proxyRenderer->vramDirtyBitmap) {
int bitmap = proxyRenderer->vramDirtyBitmap;
proxyRenderer->vramDirtyBitmap = 0;
int j;
for (j = 0; j < 24; ++j) {
if (!(bitmap & (1 << j))) {
continue;
}
struct GBAVideoDirtyInfo dirty = {
DIRTY_VRAM,
j * 0x1000,
0xABCD,
0xDEADBEEF,
};
_writeData(proxyRenderer, &dirty, sizeof(dirty));
_writeData(proxyRenderer, &proxyRenderer->d.vram[j * 0x800], 0x1000);
}
}
struct GBAVideoDirtyInfo dirty = {
DIRTY_SCANLINE,
y,
0,
0xDEADBEEF,
};
_writeData(proxyRenderer, &dirty, sizeof(dirty));
if ((y & 15) == 15) {
ConditionWake(&proxyRenderer->toThreadCond);
}
}
void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!");
_proxyThreadRecover(proxyRenderer);
return;
}
MutexLock(&proxyRenderer->mutex);
// Insert an extra item into the queue to make sure it gets flushed
struct GBAVideoDirtyInfo dirty = {
DIRTY_FLUSH,
0,
0,
0xDEADBEEF,
};
_writeData(proxyRenderer, &dirty, sizeof(dirty));
do {
ConditionWake(&proxyRenderer->toThreadCond);
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
} while (proxyRenderer->threadState == PROXY_THREAD_BUSY);
proxyRenderer->backend->finishFrame(proxyRenderer->backend);
proxyRenderer->vramDirtyBitmap = 0;
MutexUnlock(&proxyRenderer->mutex);
}
static void GBAVideoThreadProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) {
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
MutexLock(&proxyRenderer->mutex);
// Insert an extra item into the queue to make sure it gets flushed
struct GBAVideoDirtyInfo dirty = {
DIRTY_FLUSH,
0,
0,
0xDEADBEEF,
};
_writeData(proxyRenderer, &dirty, sizeof(dirty));
while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
ConditionWake(&proxyRenderer->toThreadCond);
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
}
proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels);
MutexUnlock(&proxyRenderer->mutex);
}
static void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) {
struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer;
MutexLock(&proxyRenderer->mutex);
// Insert an extra item into the queue to make sure it gets flushed
struct GBAVideoDirtyInfo dirty = {
DIRTY_FLUSH,
0,
0,
0xDEADBEEF,
};
_writeData(proxyRenderer, &dirty, sizeof(dirty));
while (proxyRenderer->threadState == PROXY_THREAD_BUSY) {
ConditionWake(&proxyRenderer->toThreadCond);
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
}
proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels);
MutexUnlock(&proxyRenderer->mutex);
}
static THREAD_ENTRY _proxyThread(void* renderer) {
struct GBAVideoThreadProxyRenderer* proxyRenderer = renderer;
ThreadSetName("Proxy Renderer Thread");
MutexLock(&proxyRenderer->mutex);
struct GBAVideoDirtyInfo item = {0};
while (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {
break;
}
if (RingFIFORead(&proxyRenderer->dirtyQueue, &item, sizeof(item))) {
proxyRenderer->threadState = PROXY_THREAD_BUSY;
MutexUnlock(&proxyRenderer->mutex);
do {
switch (item.type) {
case DIRTY_REGISTER:
proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item.address, item.value);
break;
case DIRTY_PALETTE:
proxyRenderer->paletteProxy[item.address >> 1] = item.value;
proxyRenderer->backend->writePalette(proxyRenderer->backend, item.address, item.value);
break;
case DIRTY_OAM:
proxyRenderer->oamProxy.raw[item.address] = item.value;
proxyRenderer->backend->writeOAM(proxyRenderer->backend, item.address);
break;
case DIRTY_VRAM:
while (!RingFIFORead(&proxyRenderer->dirtyQueue, &proxyRenderer->vramProxy[item.address >> 1], 0x1000)) {
mLOG(GBA_VIDEO, DEBUG, "Proxy thread can't read VRAM. CPU thread asleep?");
MutexLock(&proxyRenderer->mutex);
ConditionWake(&proxyRenderer->fromThreadCond);
ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
MutexUnlock(&proxyRenderer->mutex);
}
proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item.address);
break;
case DIRTY_SCANLINE:
proxyRenderer->backend->drawScanline(proxyRenderer->backend, item.address);
break;
case DIRTY_FLUSH:
MutexLock(&proxyRenderer->mutex);
goto out;
default:
// FIFO was corrupted
MutexLock(&proxyRenderer->mutex);
proxyRenderer->threadState = PROXY_THREAD_STOPPED;
mLOG(GBA_VIDEO, ERROR, "Proxy thread queue got corrupted!");
goto out;
}
} while (proxyRenderer->threadState == PROXY_THREAD_BUSY && RingFIFORead(&proxyRenderer->dirtyQueue, &item, sizeof(item)));
MutexLock(&proxyRenderer->mutex);
}
out:
ConditionWake(&proxyRenderer->fromThreadCond);
if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
proxyRenderer->threadState = PROXY_THREAD_IDLE;
}
}
MutexUnlock(&proxyRenderer->mutex);
#ifdef _3DS
svcExitThread();
#endif
return 0;
}
#endif

View File

@ -807,10 +807,12 @@ static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) {
}
}
}
if (GBARegisterDISPCNTGetMode(renderer->dispcnt) != 0) {
renderer->bg[2].sx += renderer->bg[2].dmx;
renderer->bg[2].sy += renderer->bg[2].dmy;
renderer->bg[3].sx += renderer->bg[3].dmx;
renderer->bg[3].sy += renderer->bg[3].dmy;
}
}
static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) {

View File

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

View File

@ -295,7 +295,7 @@ static void GBAVideoDummyRendererPutPixels(struct GBAVideoRenderer* renderer, si
}
void GBAVideoSerialize(const struct GBAVideo* video, struct GBASerializedState* state) {
memcpy(state->vram, video->renderer->vram, SIZE_VRAM);
memcpy(state->vram, video->vram, SIZE_VRAM);
memcpy(state->oam, video->oam.raw, SIZE_OAM);
memcpy(state->pram, video->palette, SIZE_PALETTE_RAM);
STORE_32(video->event.when - mTimingCurrentTime(&video->p->timing), 0, &state->video.nextEvent);
@ -303,7 +303,7 @@ void GBAVideoSerialize(const struct GBAVideo* video, struct GBASerializedState*
}
void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState* state) {
memcpy(video->renderer->vram, state->vram, SIZE_VRAM);
memcpy(video->vram, state->vram, SIZE_VRAM);
uint16_t value;
int i;
for (i = 0; i < SIZE_OAM; i += 2) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -242,7 +242,7 @@ static void _setup(struct mGUIRunner* runner) {
mCoreLoadForeignConfig(runner->core, &runner->config);
}
runner->core->setRotation(runner->core, &rotation.d);
runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation.d);
if (hasSound != NO_SOUND) {
runner->core->setAVStream(runner->core, &stream);
}

View File

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

View File

@ -398,7 +398,7 @@ bool retro_load_game(const struct retro_game_info* game) {
blip_set_rates(core->getAudioChannel(core, 0), core->frequency(core), 32768);
blip_set_rates(core->getAudioChannel(core, 1), core->frequency(core), 32768);
core->setRumble(core, &rumble);
core->setPeripheral(core, mPERIPH_RUMBLE, &rumble);
savedata = anonymousMemoryMap(SIZE_CART_FLASH1M);
struct VFile* save = VFileFromMemory(savedata, SIZE_CART_FLASH1M);
@ -409,8 +409,7 @@ bool retro_load_game(const struct retro_game_info* game) {
#ifdef M_CORE_GBA
if (core->platform(core) == PLATFORM_GBA) {
struct GBA* gba = core->board;
gba->luminanceSource = &lux;
core->setPeripheral(core, mPERIPH_GBA_LUMINANCE, &lux);
const char* sysDir = 0;
if (core->opts.useBios && environCallback(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &sysDir)) {

View File

@ -65,6 +65,10 @@ static void mGLContextResized(struct VideoBackend* v, unsigned w, unsigned h) {
drawH = w * v->height / v->width;
}
}
if (v->lockIntegerScaling) {
drawW -= drawW % v->width;
drawH -= drawH % v->height;
}
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClearColor(0, 0, 0, 0);

View File

@ -171,6 +171,10 @@ static void mGLES2ContextResized(struct VideoBackend* v, unsigned w, unsigned h)
drawH = w * v->height / v->width;
}
}
if (v->lockIntegerScaling) {
drawW -= drawW % v->width;
drawH -= drawH % v->height;
}
glViewport(0, 0, v->width, v->height);
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);

View File

@ -16,7 +16,6 @@
#include <psp2/display.h>
#include <psp2/kernel/processmgr.h>
#include <psp2/kernel/threadmgr.h>
#include <psp2/moduleinfo.h>
#include <psp2/power.h>
#include <psp2/sysmodule.h>
#include <psp2/touch.h>
@ -164,6 +163,7 @@ int main() {
mPSP2MapKey(&runner.params.keyMap, SCE_CTRL_RIGHT, GUI_INPUT_RIGHT);
mPSP2MapKey(&runner.params.keyMap, SCE_CTRL_SQUARE, mGUI_INPUT_SCREEN_MODE);
scePowerSetArmClockFrequency(444);
mGUIRunloop(&runner);
vita2d_fini();

View File

@ -20,6 +20,7 @@
#include <mgba-util/memory.h>
#include <mgba-util/circle-buffer.h>
#include <mgba-util/math.h>
#include <mgba-util/ring-fifo.h>
#include <mgba-util/threading.h>
#include <mgba-util/vfs.h>
@ -31,7 +32,6 @@
#include <psp2/gxm.h>
#include <psp2/kernel/sysmem.h>
#include <psp2/motion.h>
#include <psp2/power.h>
#include <vita2d.h>
@ -63,8 +63,8 @@ bool frameLimiter = true;
extern const uint8_t _binary_backdrop_png_start[];
static vita2d_texture* backdrop = 0;
#define PSP2_SAMPLES 128
#define PSP2_AUDIO_BUFFER_SIZE (PSP2_SAMPLES * 40)
#define PSP2_SAMPLES 256
#define PSP2_AUDIO_BUFFER_SIZE (PSP2_SAMPLES * 20)
static struct mPSP2AudioContext {
struct RingFIFO buffer;
@ -176,7 +176,6 @@ void mPSP2Setup(struct mGUIRunner* runner) {
mCoreConfigSetDefaultIntValue(&runner->config, "threadedVideo", 1);
mCoreLoadForeignConfig(runner->core, &runner->config);
scePowerSetArmClockFrequency(333);
mPSP2MapKey(&runner->core->inputMap, SCE_CTRL_CROSS, GBA_KEY_A);
mPSP2MapKey(&runner->core->inputMap, SCE_CTRL_CIRCLE, GBA_KEY_B);
mPSP2MapKey(&runner->core->inputMap, SCE_CTRL_START, GBA_KEY_START);
@ -193,8 +192,10 @@ void mPSP2Setup(struct mGUIRunner* runner) {
desc = (struct mInputAxis) { GBA_KEY_RIGHT, GBA_KEY_LEFT, 192, 64 };
mInputBindAxis(&runner->core->inputMap, PSP2_INPUT, 1, &desc);
tex = vita2d_create_empty_texture_format(256, 256, SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
screenshot = vita2d_create_empty_texture_format(256, 256, SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
unsigned width, height;
runner->core->desiredVideoDimensions(runner->core, &width, &height);
tex = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
screenshot = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR);
outputBuffer = vita2d_texture_get_datap(tex);
runner->core->setVideoBuffer(runner->core, outputBuffer, 256);
@ -203,11 +204,11 @@ void mPSP2Setup(struct mGUIRunner* runner) {
rotation.d.readTiltX = _readTiltX;
rotation.d.readTiltY = _readTiltY;
rotation.d.readGyroZ = _readGyroZ;
runner->core->setRotation(runner->core, &rotation.d);
runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation.d);
rumble.d.setRumble = _setRumble;
CircleBufferInit(&rumble.history, RUMBLE_PWM);
runner->core->setRumble(runner->core, &rumble.d);
runner->core->setPeripheral(runner->core, mPERIPH_RUMBLE, &rumble.d);
frameLimiter = true;
backdrop = vita2d_load_PNG_buffer(_binary_backdrop_png_start);
@ -219,7 +220,6 @@ void mPSP2Setup(struct mGUIRunner* runner) {
}
void mPSP2LoadROM(struct mGUIRunner* runner) {
scePowerSetArmClockFrequency(444);
float rate = 60.0f / 1.001f;
sceDisplayGetRefreshRate(&rate);
double ratio = GBAAudioCalculateRatio(1, rate, 1);
@ -262,7 +262,6 @@ void mPSP2PrepareForFrame(struct mGUIRunner* runner) {
blip_clear(runner->core->getAudioChannel(runner->core, 1));
break;
}
sceKernelDelayThread(400);
}
blip_read_samples(runner->core->getAudioChannel(runner->core, 0), &samples[0].left, PSP2_SAMPLES, true);
blip_read_samples(runner->core->getAudioChannel(runner->core, 1), &samples[0].right, PSP2_SAMPLES, true);
@ -297,7 +296,6 @@ void mPSP2UnloadROM(struct mGUIRunner* runner) {
default:
break;
}
scePowerSetArmClockFrequency(333);
}
void mPSP2Paused(struct mGUIRunner* runner) {

View File

@ -2,8 +2,13 @@ find_program(PYTHON python)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in ${CMAKE_CURRENT_BINARY_DIR}/setup.py)
get_property(INCLUDE_DIRECTORIES DIRECTORY PROPERTY INCLUDE_DIRECTORIES)
set(INCLUDE_FLAGS)
foreach(DIR IN LISTS INCLUDE_DIRECTORIES)
list(APPEND INCLUDE_FLAGS "-I${DIR}")
endforeach()
add_custom_command(OUTPUT build/lib/${BINARY_NAME}/__init__.py
COMMAND BINDIR=${CMAKE_CURRENT_BINARY_DIR}/.. ${PYTHON} ${CMAKE_CURRENT_BINARY_DIR}/setup.py build --build-base ${CMAKE_CURRENT_BINARY_DIR}
COMMAND BINDIR=${CMAKE_CURRENT_BINARY_DIR}/.. CPPFLAGS="${INCLUDE_FLAGS}" ${PYTHON} ${CMAKE_CURRENT_BINARY_DIR}/setup.py build --build-base ${CMAKE_CURRENT_BINARY_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ${BINARY_NAME}
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/setup.py

View File

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

View File

@ -32,24 +32,16 @@ ffi.set_source("mgba._pylib", """
#include <mgba-util/png-io.h>
#include <mgba-util/vfs.h>
struct VFile* VFileFromPython(void* fileobj);
struct VFilePy {
struct VFile d;
void* fileobj;
};
struct mLogger* mLoggerPythonCreate(void* pyobj);
struct mLoggerPy {
struct mLogger d;
void* pyobj;
};
#define PYEXPORT
#include "platform/python/log.h"
#include "platform/python/sio.h"
#include "platform/python/vfs-py.h"
#undef PYEXPORT
""", include_dirs=[incdir, srcdir],
extra_compile_args=cppflags,
libraries=["mgba"],
library_dirs=[bindir],
sources=[os.path.join(pydir, path) for path in ["vfs-py.c", "log.c"]])
sources=[os.path.join(pydir, path) for path in ["vfs-py.c", "log.c", "sio.c"]])
preprocessed = subprocess.check_output(cpp + ["-fno-inline", "-P"] + cppflags + [os.path.join(pydir, "_builder.h")], universal_newlines=True)

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
# Copyright (c) 2013-2016 Jeffrey Pfau
# Copyright (c) 2013-2017 Jeffrey Pfau
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
@ -8,6 +8,7 @@ from .lr35902 import LR35902Core
from .core import Core, needsReset
from .memory import Memory
from .tile import Sprite
from . import createCallback
class GB(Core):
KEY_A = lib.GBA_KEY_A
@ -34,6 +35,48 @@ class GB(Core):
self._native.video.renderer.cache = ffi.NULL
lib.mTileCacheDeinit(cache)
def attachSIO(self, link):
lib.GBSIOSetDriver(ffi.addressof(self._native.sio), link._native)
createCallback("GBSIOPythonDriver", "init")
createCallback("GBSIOPythonDriver", "deinit")
createCallback("GBSIOPythonDriver", "writeSB")
createCallback("GBSIOPythonDriver", "writeSC")
class GBSIODriver(object):
def __init__(self):
self._handle = ffi.new_handle(self)
self._native = ffi.gc(lib.GBSIOPythonDriverCreate(self._handle), lib.free)
def init(self):
return True
def deinit(self):
pass
def writeSB(self, value):
pass
def writeSC(self, value):
return value
class GBSIOSimpleDriver(GBSIODriver):
def __init__(self):
super(GBSIOSimpleDriver, self).__init__()
self.tx = 0xFF
self.rx = 0xFF
def writeSB(self, value):
self.rx = value
def schedule(self, period=0x100, when=0):
self._native.p.remainingBits = 8
self._native.p.period = period
self._native.p.pendingSB = self.tx
self.tx = 0xFF
lib.mTimingDeschedule(ffi.addressof(self._native.p.p.timing), ffi.addressof(self._native.p.event))
lib.mTimingSchedule(ffi.addressof(self._native.p.p.timing), ffi.addressof(self._native.p.event), when)
class GBMemory(Memory):
def __init__(self, core):
super(GBMemory, self).__init__(core, 0x10000)

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