mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' (early part) into medusa
This commit is contained in:
commit
7f912c686b
200
CHANGES
200
CHANGES
|
@ -19,16 +19,25 @@ Misc:
|
|||
- DS Video: Simplify VRAM mapping
|
||||
|
||||
0.8.0: (Future)
|
||||
Features:
|
||||
- Improved logging configuration
|
||||
Bugfixes:
|
||||
- GBA: All IRQs have 7 cycle delay (fixes mgba.io/i/539, mgba.io/i/1208)
|
||||
- GBA: Reset now reloads multiboot ROMs
|
||||
- GBA BIOS: Fix multiboot entry point (fixes Magic Floor)
|
||||
- Switch: Fix final cleanup (fixes mgba.io/i/1283)
|
||||
- Qt: Fix tile and sprite views not always displaying at first
|
||||
- GBA Memory: Fix a few AGBPrint crashes
|
||||
- GBA Memory: Fix OOB ROM reads showing up as AGBPrint memory
|
||||
Misc:
|
||||
- GBA Savedata: EEPROM performance fixes
|
||||
- GBA Savedata: Automatically map 1Mbit Flash files as 1Mbit Flash
|
||||
- GB Memory: Support running from blocked memory
|
||||
- Qt: Don't unload ROM immediately if it crashes
|
||||
- GBA Video: Improve sprite cycle counting (fixes mgba.io/i/1274)
|
||||
- Debugger: Add breakpoint and watchpoint listing
|
||||
|
||||
0.7.0: (Future)
|
||||
0.7.0: (2019-01-26)
|
||||
Features:
|
||||
- ELF support
|
||||
- Game Boy Camera support
|
||||
|
@ -49,87 +58,87 @@ Features:
|
|||
- GBA: ARMIPS/A22i-style and ELF symbol table support
|
||||
- Initial Switch port
|
||||
Bugfixes:
|
||||
- GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749)
|
||||
- GB Serialize: Fix audio state loading
|
||||
- GB Video: Fix dot clock timing being slightly wrong
|
||||
- Qt: Fix GL display when loading a game from CLI (fixes mgba.io/i/843)
|
||||
- ARM: Fix MSR when T bit is set
|
||||
- GB Serialize: Fix game title check
|
||||
- GB: Revamp IRQ handling based on new information
|
||||
- GBA Video: Don't mask out high bits of BLDY (fixes mgba.io/i/899)
|
||||
- GB Video: Fix loading states while in mode 3
|
||||
- GBA DMA: Fix invalid DMA reads (fixes mgba.io/i/142)
|
||||
- GBA Video: Add delay when enabling BGs (fixes mgba.io/i/744, mgba.io/i/752)
|
||||
- GB Timer: Minor accuracy improvements
|
||||
- GB Audio: Clock frame events on DIV
|
||||
- GBA Timer: Fix timers sometimes being late (fixes mgba.io/i/1012)
|
||||
- GBA Hardware: Fix RTC overriding light sensor (fixes mgba.io/i/1069)
|
||||
- GBA Savedata: Fix savedata modified time updating when read-only
|
||||
- GB Video: Fix enabling window when LY > WY (fixes mgba.io/i/409)
|
||||
- GBA Video: Start timing mid-scanline when skipping BIOS
|
||||
- Core: Fix audio sync breaking when interrupted
|
||||
- Qt: Improve FPS timer stability
|
||||
- GBA Serialize: Fix loading channel 3 volume (fixes mgba.io/i/1107)
|
||||
- GBA SIO: Fix unconnected SIOCNT for multi mode (fixes mgba.io/i/1105)
|
||||
- GBA BIOS: Fix BitUnPack final byte
|
||||
- GB I/O: DMA register is R/W
|
||||
- GB Video: Fix SCX timing
|
||||
- GBA Video: Improve sprite cycle counting (fixes mgba.io/i/1126)
|
||||
- GB, GBA Savedata: Fix savestate loading overwriting saves on reset
|
||||
- GBA Video: Make layer disabling work consistently
|
||||
- GB: Fix IRQ disabling on the same T-cycle as an assert
|
||||
- Core: Fix ordering events when scheduling during events
|
||||
- GBA: Reset WAITCNT properly
|
||||
- GBA Serialize: Fix loading states in Hblank
|
||||
- PSP2: Fix more issues causing poor audio
|
||||
- GBA Memory: Fix Vast Fame support (taizou) (fixes mgba.io/i/1170)
|
||||
- GB, GBA Savedata: Fix unmasking savedata crash
|
||||
- GBA DMA: Fix temporal sorting of DMAs of different priorities
|
||||
- FFmpeg: Fix encoding audio/video queue issues
|
||||
- GB Serialize: Fix IRQ pending/EI pending confusion
|
||||
- GB MBC: Improve multicart detection heuristic (fixes mgba.io/i/1177)
|
||||
- GB: Revamp IRQ handling based on new information
|
||||
- GB: Fix IRQ disabling on the same T-cycle as an assert
|
||||
- GB Audio: Make audio unsigned with bias (fixes mgba.io/i/749)
|
||||
- GB Audio: Clock frame events on DIV
|
||||
- GB Audio: Fix channel 3 reset value
|
||||
- GB Audio: Fix channel 4 initial LFSR
|
||||
- GB, GBA Video: Don't call finishFrame twice in thread proxy
|
||||
- GB Audio: Fix channel 1, 2 and 4 reset timing
|
||||
- Util: Fix wrapping edge cases in RingFIFO
|
||||
- GBA Hardware: Fix RTC handshake transition (fixes mgba.io/i/1134)
|
||||
- GB I/O: DMA register is R/W
|
||||
- GB MBC: Improve multicart detection heuristic (fixes mgba.io/i/1177)
|
||||
- GB, GBA Savedata: Fix savestate loading overwriting saves on reset
|
||||
- GB, GBA Savedata: Fix unmasking savedata crash
|
||||
- GB Serialize: Fix audio state loading
|
||||
- GB Serialize: Fix game title check
|
||||
- GB Serialize: Fix IRQ pending/EI pending confusion
|
||||
- GB Timer: Minor accuracy improvements
|
||||
- GB Video: Fix dot clock timing being slightly wrong
|
||||
- GB Video: Fix loading states while in mode 3
|
||||
- GB Video: Fix enabling window when LY > WY (fixes mgba.io/i/409)
|
||||
- GB Video: Fix SCX timing
|
||||
- GB, GBA Video: Don't call finishFrame twice in thread proxy
|
||||
- GBA: Reset WAITCNT properly
|
||||
- GBA BIOS: Fix BitUnPack final byte
|
||||
- GBA BIOS: Fix BitUnPack narrowing
|
||||
- GBA DMA: Fix invalid DMA reads (fixes mgba.io/i/142)
|
||||
- GBA DMA: Fix temporal sorting of DMAs of different priorities
|
||||
- GBA Hardware: Fix RTC overriding light sensor (fixes mgba.io/i/1069)
|
||||
- GBA Hardware: Fix RTC handshake transition (fixes mgba.io/i/1134)
|
||||
- GBA Memory: Fix Vast Fame support (taizou) (fixes mgba.io/i/1170)
|
||||
- GBA Savedata: Fix savedata modified time updating when read-only
|
||||
- GBA Serialize: Fix loading channel 3 volume (fixes mgba.io/i/1107)
|
||||
- GBA Serialize: Fix loading states in Hblank
|
||||
- GBA SIO: Fix unconnected SIOCNT for multi mode (fixes mgba.io/i/1105)
|
||||
- GBA Timer: Fix timers sometimes being late (fixes mgba.io/i/1012)
|
||||
- GBA Video: Don't mask out high bits of BLDY (fixes mgba.io/i/899)
|
||||
- GBA Video: Add delay when enabling BGs (fixes mgba.io/i/744, mgba.io/i/752)
|
||||
- GBA Video: Start timing mid-scanline when skipping BIOS
|
||||
- GBA Video: Improve sprite cycle counting (fixes mgba.io/i/1126)
|
||||
- GBA Video: Make layer disabling work consistently
|
||||
- PSP2: Fix more issues causing poor audio
|
||||
- Qt: Fix GL display when loading a game from CLI (fixes mgba.io/i/843)
|
||||
- Qt: Improve FPS timer stability
|
||||
- Util: Fix wrapping edge cases in RingFIFO
|
||||
Misc:
|
||||
- GBA Timer: Use global cycles for timers
|
||||
- GBA: Extend oddly-sized ROMs to full address space (fixes mgba.io/i/722)
|
||||
- All: Make FIXED_ROM_BUFFER an option instead of 3DS-only
|
||||
- Qt: Redo GameController into multiple classes
|
||||
- Test: Restructure test suite into multiple executables
|
||||
- Python: Integrate tests from cinema test suite
|
||||
- Util: Don't build crc32 if the function already exists
|
||||
- GBA: Implement display start DMAs
|
||||
- Qt: Prevent window from being created off-screen
|
||||
- Qt: Add option to disable FPS display
|
||||
- GBA: Improve multiboot image detection
|
||||
- GB MBC: Remove erroneous bank 0 wrapping
|
||||
- GBA Cheats: Allow multiple ROM patches in the same slot
|
||||
- GB: Skip BIOS option now works
|
||||
- Libretro: Add frameskip option
|
||||
- GBA Memory: 64 MiB GBA Video cartridge support
|
||||
- PSP2: Use system enter key by default
|
||||
- 3DS: Remove deprecated CSND interface
|
||||
- Qt: Options to mess around with layer placement
|
||||
- GBA Savedata: Remove ability to disable realistic timing
|
||||
- Qt: Add load alternate save option
|
||||
- GB Audio: Improved audio quality
|
||||
- GB, GBA Audio: Increase max audio volume
|
||||
- GB: Fix VRAM/palette locking (fixes mgba.io/i/1109)
|
||||
- GB Video: Darken colors in GBA mode
|
||||
- All: Make FIXED_ROM_BUFFER an option instead of 3DS-only
|
||||
- Core: Remove broken option for whether rewinding restores save games
|
||||
- Feature: Added loading savestates from command line
|
||||
- FFmpeg: Support libswresample (fixes mgba.io/i/1120, mgba.io/b/123)
|
||||
- FFmpeg: Support lossless h.264 encoding
|
||||
- Feature: Added loading savestates from command line
|
||||
- Qt: Allow pausing game at load (fixes mgba.io/i/1129)
|
||||
- Wii: Move audio handling to callbacks (fixes mgba.io/i/803)
|
||||
- Qt: Clean up FPS target UI (fixes mgba.io/i/436)
|
||||
- Core: Remove broken option for whether rewinding restores save games
|
||||
- FFmpeg: Support lossless VP9 encoding
|
||||
- GBA Cheats: Allow multiple ROM patches in the same slot
|
||||
- GB: Skip BIOS option now works
|
||||
- GB: Fix VRAM/palette locking (fixes mgba.io/i/1109)
|
||||
- GB Audio: Improved audio quality
|
||||
- GB, GBA Audio: Increase max audio volume
|
||||
- GB MBC: Remove erroneous bank 0 wrapping
|
||||
- GB Video: Darken colors in GBA mode
|
||||
- GBA: Extend oddly-sized ROMs to full address space (fixes mgba.io/i/722)
|
||||
- GBA: Implement display start DMAs
|
||||
- GBA: Improve multiboot image detection
|
||||
- GBA Memory: 64 MiB GBA Video cartridge support
|
||||
- GBA Savedata: Remove ability to disable realistic timing
|
||||
- GBA Timer: Use global cycles for timers
|
||||
- Libretro: Add frameskip option
|
||||
- mGUI: Add fast forward toggle
|
||||
- PSP2: Use system enter key by default
|
||||
- Python: Integrate tests from cinema test suite
|
||||
- Qt: Redo GameController into multiple classes
|
||||
- Qt: Prevent window from being created off-screen
|
||||
- Qt: Add option to disable FPS display
|
||||
- Qt: Options to mess around with layer placement
|
||||
- Qt: Add load alternate save option
|
||||
- Qt: Allow pausing game at load (fixes mgba.io/i/1129)
|
||||
- Qt: Clean up FPS target UI (fixes mgba.io/i/436)
|
||||
- Test: Restructure test suite into multiple executables
|
||||
- Util: Don't build crc32 if the function already exists
|
||||
- Wii: Move audio handling to callbacks (fixes mgba.io/i/803)
|
||||
Changes from beta 1:
|
||||
Features:
|
||||
- Libretro: Add Game Boy cheat support
|
||||
|
@ -142,48 +151,49 @@ Features:
|
|||
- Tile viewer now has adjustable width
|
||||
- Python: Experimental audio API
|
||||
Bugfixes:
|
||||
- 3DS: Fix unused screens not clearing (fixes mgba.io/i/1184)
|
||||
- Core: Remember to deinit proxy ring FIFO
|
||||
- Core: Reroot timing list when (de)scheduling
|
||||
- GB, GBA: Fix broken opposing button filter (fixes mgba.io/i/1191)
|
||||
- GB MBC: Fix MBC30 SRAM
|
||||
- GB, GBA Savedata: Fix leaks when loading masked save (fixes mgba.io/i/1197)
|
||||
- GB Video: Fix SGB border hole size
|
||||
- GB Video: Changing LYC while LCDC off doesn't affect STAT (fixes mgba.io/i/1224)
|
||||
- GBA: Fix GB Player features
|
||||
- GBA I/O: SOUNDCNT_HI is readable when sound is off
|
||||
- GBA Savedata: Fix EEPROM writing codepath when savetype is not EEPROM
|
||||
- GBA Video: Fix caching with background toggling (fixes mgba.io/i/1118)
|
||||
- Libretro: Fix adding codes with hooks
|
||||
- PSP2: Fix audio crackling after fast forward
|
||||
- PSP2: Fix audio crackling when buffer is full
|
||||
- 3DS: Fix unused screens not clearing (fixes mgba.io/i/1184)
|
||||
- GBA Video: Fix caching with background toggling (fixes mgba.io/i/1118)
|
||||
- Wii: Fix drawing caching regression (fixes mgba.io/i/1185)
|
||||
- Switch: Fix incorrect mapping for fast forward cap
|
||||
- GB, GBA: Fix broken opposing button filter (fixes mgba.io/i/1191)
|
||||
- Qt: Fix jumbled background when paused
|
||||
- Qt: Fix FPS counter on Windows
|
||||
- GB, GBA Savedata: Fix leaks when loading masked save (fixes mgba.io/i/1197)
|
||||
- Qt: Fix focus issues with load/save state overlay
|
||||
- GB Video: Fix SGB border hole size
|
||||
- Switch: Fix incorrect mapping for fast forward cap
|
||||
- Wii: Fix drawing caching regression (fixes mgba.io/i/1185)
|
||||
- PSP2: Fix tearing issues (fixes mgba.io/i/1211)
|
||||
- Qt: Fix mapping analog triggers (fixes mgba.io/i/495)
|
||||
- Qt: Grab focus when game starts (fixes mgba.io/i/804)
|
||||
- Core: Remember to deinit proxy ring FIFO
|
||||
- GBA Savedata: Fix EEPROM writing codepath when savetype is not EEPROM
|
||||
- Core: Reroot timing list when (de)scheduling
|
||||
- GB Video: Changing LYC while LCDC off doesn't affect STAT (fixes mgba.io/i/1224)
|
||||
- GBA I/O: SOUNDCNT_HI is readable when sound is off
|
||||
- SDL: Fix handling of invalid gamepads (fixes mgba.io/i/1239)
|
||||
- Libretro: Fix adding codes with hooks
|
||||
- GBA: Fix GB Player features
|
||||
- Qt: Ensure FATAL logs reach log view
|
||||
- SDL: Fix handling of invalid gamepads (fixes mgba.io/i/1239)
|
||||
Misc:
|
||||
- CMake: Fix libswresample version dependencies (fixes mgba.io/i/1229)
|
||||
- Debugger: Minor text fixes
|
||||
- Debugger: Readability improvements (fixes mgba.io/i/1238)
|
||||
- GB: Improved SGB2 support
|
||||
- GB Audio: Skip frame if enabled when clock is high
|
||||
- Libretro: Reduce rumble callbacks
|
||||
- mGUI: Add SGB border configuration option
|
||||
- mGUI: Add support for different settings types
|
||||
- Python: Minor API improvements
|
||||
- Qt: Ensure camera image is valid
|
||||
- Qt: Debugger console history
|
||||
- Qt: Detect presence of GL_ARB_framebuffer_object
|
||||
- Qt: Minor memory view tweaks
|
||||
- Res: Improve modeling of AGB/AGS screen in shaders
|
||||
- Wii: Define _GNU_SOURCE (fixes mgba.io/i/1106)
|
||||
- Wii: Expose stretch configuration in settings
|
||||
- Wii: Stretch now sets pixel-accurate mode size cap
|
||||
- Qt: Ensure camera image is valid
|
||||
- GB: Improved SGB2 support
|
||||
- Libretro: Reduce rumble callbacks
|
||||
- Debugger: Minor text fixes
|
||||
- Qt: Debugger console history
|
||||
- Qt: Detect presence of GL_ARB_framebuffer_object
|
||||
- Python: Minor API improvements
|
||||
- Qt: Minor memory view tweaks
|
||||
- CMake: Fix libswresample version dependencies (fixes mgba.io/i/1229)
|
||||
- Debugger: Readability improvements (fixes mgba.io/i/1238)
|
||||
- GB Audio: Skip frame if enabled when clock is high
|
||||
- Res: Improve modeling of AGB/AGS screen in shaders
|
||||
|
||||
0.7 beta 1: (2018-09-24)
|
||||
- Initial beta for 0.7
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
|
@ -194,15 +194,28 @@ typedef intptr_t ssize_t;
|
|||
#define TEST_FILL_BITS(SRC, START, END, TEST) ((TEST) ? (FILL_BITS(SRC, START, END)) : (CLEAR_BITS(SRC, START, END)))
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma section(".CRT$XCU",read)
|
||||
#define ATTRIBUTE_UNUSED
|
||||
#define ATTRIBUTE_ALIGN(X)
|
||||
#define ATTRIBUTE_FORMAT(X, Y, Z)
|
||||
#define ATTRIBUTE_NOINLINE
|
||||
// Adapted from https://stackoverflow.com/a/2390626
|
||||
#define _CONSTRUCTOR(FN, PRE) \
|
||||
static void FN(void); \
|
||||
__declspec(allocate(".CRT$XCU")) void (*_CONSTRUCTOR_ ## FN)(void) = FN; \
|
||||
__pragma(comment(linker,"/include:" PRE "_CONSTRUCTOR_" #FN)) \
|
||||
static void FN(void)
|
||||
#ifdef _WIN64
|
||||
#define CONSTRUCTOR(FN) _CONSTRUCTOR(FN, "")
|
||||
#else
|
||||
#define CONSTRUCTOR(FN) _CONSTRUCTOR(FN, "_")
|
||||
#endif
|
||||
#else
|
||||
#define ATTRIBUTE_UNUSED __attribute__((unused))
|
||||
#define ATTRIBUTE_ALIGN(X) __attribute__((aligned(X)))
|
||||
#define ATTRIBUTE_FORMAT(X, Y, Z) __attribute__((format(X, Y, Z)))
|
||||
#define ATTRIBUTE_NOINLINE __attribute__((noinline))
|
||||
#define CONSTRUCTOR(FN) static __attribute__((constructor)) void FN(void)
|
||||
#endif
|
||||
|
||||
#define DECL_BITFIELD(NAME, TYPE) typedef TYPE NAME
|
||||
|
|
|
@ -47,24 +47,23 @@ struct mCoreConfig;
|
|||
void mLogFilterInit(struct mLogFilter*);
|
||||
void mLogFilterDeinit(struct mLogFilter*);
|
||||
void mLogFilterLoad(struct mLogFilter*, const struct mCoreConfig*);
|
||||
void mLogFilterSave(const struct mLogFilter*, struct mCoreConfig*);
|
||||
void mLogFilterSet(struct mLogFilter*, const char* category, int levels);
|
||||
bool mLogFilterTest(struct mLogFilter*, int category, enum mLogLevel level);
|
||||
void mLogFilterReset(struct mLogFilter*, const char* category);
|
||||
bool mLogFilterTest(const struct mLogFilter*, int category, enum mLogLevel level);
|
||||
int mLogFilterLevels(const struct mLogFilter*, int category);
|
||||
|
||||
ATTRIBUTE_FORMAT(printf, 3, 4)
|
||||
void mLog(int category, enum mLogLevel level, const char* format, ...);
|
||||
|
||||
#define mLOG(CATEGORY, LEVEL, ...) mLog(_mLOG_CAT_ ## CATEGORY (), mLOG_ ## LEVEL, __VA_ARGS__)
|
||||
#define mLOG(CATEGORY, LEVEL, ...) mLog(_mLOG_CAT_ ## CATEGORY, mLOG_ ## LEVEL, __VA_ARGS__)
|
||||
|
||||
#define mLOG_DECLARE_CATEGORY(CATEGORY) int _mLOG_CAT_ ## CATEGORY (void); extern const char* _mLOG_CAT_ ## CATEGORY ## _ID;
|
||||
#define mLOG_DECLARE_CATEGORY(CATEGORY) extern int _mLOG_CAT_ ## CATEGORY;
|
||||
#define mLOG_DEFINE_CATEGORY(CATEGORY, NAME, ID) \
|
||||
int _mLOG_CAT_ ## CATEGORY (void) { \
|
||||
static int category = 0; \
|
||||
if (!category) { \
|
||||
category = mLogGenerateCategory(NAME, ID); \
|
||||
} \
|
||||
return category; \
|
||||
} \
|
||||
const char* _mLOG_CAT_ ## CATEGORY ## _ID = ID;
|
||||
int _mLOG_CAT_ ## CATEGORY; \
|
||||
CONSTRUCTOR(_mLOG_CAT_ ## CATEGORY ## _INIT) { \
|
||||
_mLOG_CAT_ ## CATEGORY = mLogGenerateCategory(NAME, ID); \
|
||||
}
|
||||
|
||||
mLOG_DECLARE_CATEGORY(STATUS)
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||
/* Copyright (c) 2013-2019 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
|
||||
|
@ -12,6 +12,7 @@ CXX_GUARD_START
|
|||
|
||||
#include <mgba/core/cpu.h>
|
||||
#include <mgba/core/log.h>
|
||||
#include <mgba-util/vector.h>
|
||||
|
||||
mLOG_DECLARE_CATEGORY(DEBUGGER);
|
||||
|
||||
|
@ -36,7 +37,8 @@ enum mDebuggerState {
|
|||
enum mWatchpointType {
|
||||
WATCHPOINT_WRITE = 1,
|
||||
WATCHPOINT_READ = 2,
|
||||
WATCHPOINT_RW = 3
|
||||
WATCHPOINT_RW = 3,
|
||||
WATCHPOINT_WRITE_CHANGE = 4,
|
||||
};
|
||||
|
||||
enum mBreakpointType {
|
||||
|
@ -69,6 +71,25 @@ struct mDebuggerEntryInfo {
|
|||
} type;
|
||||
};
|
||||
|
||||
struct mBreakpoint {
|
||||
ssize_t id;
|
||||
uint32_t address;
|
||||
int segment;
|
||||
enum mBreakpointType type;
|
||||
struct ParseTree* condition;
|
||||
};
|
||||
|
||||
struct mWatchpoint {
|
||||
ssize_t id;
|
||||
uint32_t address;
|
||||
int segment;
|
||||
enum mWatchpointType type;
|
||||
struct ParseTree* condition;
|
||||
};
|
||||
|
||||
DECLARE_VECTOR(mBreakpointList, struct mBreakpoint);
|
||||
DECLARE_VECTOR(mWatchpointList, struct mWatchpoint);
|
||||
|
||||
struct mDebugger;
|
||||
struct ParseTree;
|
||||
struct mDebuggerPlatform {
|
||||
|
@ -79,13 +100,15 @@ struct mDebuggerPlatform {
|
|||
void (*entered)(struct mDebuggerPlatform*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*);
|
||||
|
||||
bool (*hasBreakpoints)(struct mDebuggerPlatform*);
|
||||
void (*setBreakpoint)(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
void (*setConditionalBreakpoint)(struct mDebuggerPlatform*, uint32_t address, int segment, struct ParseTree* condition);
|
||||
void (*clearBreakpoint)(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
void (*setWatchpoint)(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type);
|
||||
void (*setConditionalWatchpoint)(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type, struct ParseTree* condition);
|
||||
void (*clearWatchpoint)(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
void (*checkBreakpoints)(struct mDebuggerPlatform*);
|
||||
bool (*clearBreakpoint)(struct mDebuggerPlatform*, ssize_t id);
|
||||
|
||||
ssize_t (*setBreakpoint)(struct mDebuggerPlatform*, const struct mBreakpoint*);
|
||||
void (*listBreakpoints)(struct mDebuggerPlatform*, struct mBreakpointList*);
|
||||
|
||||
ssize_t (*setWatchpoint)(struct mDebuggerPlatform*, const struct mWatchpoint*);
|
||||
void (*listWatchpoints)(struct mDebuggerPlatform*, struct mWatchpointList*);
|
||||
|
||||
void (*trace)(struct mDebuggerPlatform*, char* out, size_t* length);
|
||||
|
||||
bool (*getRegister)(struct mDebuggerPlatform*, const char* name, int32_t* value);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
CXX_GUARD_START
|
||||
|
||||
#include <mgba/core/interface.h>
|
||||
#include <mgba/core/timing.h>
|
||||
|
||||
enum GBASIOMode {
|
||||
SIO_NORMAL_8 = 0,
|
||||
|
@ -36,7 +37,8 @@ struct GBAVideoRenderer;
|
|||
extern const int GBA_LUX_LEVELS[10];
|
||||
|
||||
enum {
|
||||
mPERIPH_GBA_LUMINANCE = 0x1000
|
||||
mPERIPH_GBA_LUMINANCE = 0x1000,
|
||||
mPERIPH_GBA_BATTLECHIP_GATE,
|
||||
};
|
||||
|
||||
struct GBALuminanceSource {
|
||||
|
@ -58,6 +60,14 @@ struct GBASIODriver {
|
|||
void GBASIOJOYCreate(struct GBASIODriver* sio);
|
||||
int GBASIOJOYSendCommand(struct GBASIODriver* sio, enum GBASIOJOYCommand command, uint8_t* data);
|
||||
|
||||
struct GBASIOBattlechipGate {
|
||||
struct GBASIODriver d;
|
||||
struct mTimingEvent event;
|
||||
uint16_t chipId;
|
||||
int32_t index;
|
||||
};
|
||||
|
||||
void GBASIOBattlechipGateCreate(struct GBASIOBattlechipGate*);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
|
|
|
@ -17,23 +17,14 @@ CXX_GUARD_START
|
|||
|
||||
struct ParseTree;
|
||||
struct ARMDebugBreakpoint {
|
||||
uint32_t address;
|
||||
struct ParseTree* condition;
|
||||
bool isSw;
|
||||
struct mBreakpoint d;
|
||||
struct {
|
||||
uint32_t opcode;
|
||||
enum ExecutionMode mode;
|
||||
} sw;
|
||||
};
|
||||
|
||||
struct ARMDebugWatchpoint {
|
||||
uint32_t address;
|
||||
enum mWatchpointType type;
|
||||
struct ParseTree* condition;
|
||||
};
|
||||
|
||||
DECLARE_VECTOR(ARMDebugBreakpointList, struct ARMDebugBreakpoint);
|
||||
DECLARE_VECTOR(ARMDebugWatchpointList, struct ARMDebugWatchpoint);
|
||||
|
||||
struct ARMDebugger {
|
||||
struct mDebuggerPlatform d;
|
||||
|
@ -41,18 +32,19 @@ struct ARMDebugger {
|
|||
|
||||
struct ARMDebugBreakpointList breakpoints;
|
||||
struct ARMDebugBreakpointList swBreakpoints;
|
||||
struct ARMDebugWatchpointList watchpoints;
|
||||
struct mWatchpointList watchpoints;
|
||||
struct ARMMemory originalMemory;
|
||||
|
||||
ssize_t nextId;
|
||||
|
||||
void (*entered)(struct mDebugger*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*);
|
||||
|
||||
bool (*setSoftwareBreakpoint)(struct ARMDebugger*, uint32_t address, enum ExecutionMode mode, uint32_t* opcode);
|
||||
bool (*clearSoftwareBreakpoint)(struct ARMDebugger*, uint32_t address, enum ExecutionMode mode, uint32_t opcode);
|
||||
ssize_t (*setSoftwareBreakpoint)(struct ARMDebugger*, uint32_t address, enum ExecutionMode mode, uint32_t* opcode);
|
||||
void (*clearSoftwareBreakpoint)(struct ARMDebugger*, const struct ARMDebugBreakpoint*);
|
||||
};
|
||||
|
||||
struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void);
|
||||
bool ARMDebuggerSetSoftwareBreakpoint(struct mDebuggerPlatform* debugger, uint32_t address, enum ExecutionMode mode);
|
||||
void ARMDebuggerClearSoftwareBreakpoint(struct mDebuggerPlatform* debugger, uint32_t address);
|
||||
ssize_t ARMDebuggerSetSoftwareBreakpoint(struct mDebuggerPlatform* debugger, uint32_t address, enum ExecutionMode mode);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
|
|
|
@ -13,21 +13,6 @@ CXX_GUARD_START
|
|||
#include <mgba/debugger/debugger.h>
|
||||
|
||||
#include <mgba/internal/lr35902/lr35902.h>
|
||||
#include <mgba-util/vector.h>
|
||||
|
||||
struct ParseTree;
|
||||
struct LR35902DebugBreakpoint {
|
||||
uint16_t address;
|
||||
int segment;
|
||||
struct ParseTree* condition;
|
||||
};
|
||||
|
||||
struct LR35902DebugWatchpoint {
|
||||
uint16_t address;
|
||||
int segment;
|
||||
enum mWatchpointType type;
|
||||
struct ParseTree* condition;
|
||||
};
|
||||
|
||||
struct LR35902Segment {
|
||||
uint16_t start;
|
||||
|
@ -35,17 +20,16 @@ struct LR35902Segment {
|
|||
const char* name;
|
||||
};
|
||||
|
||||
DECLARE_VECTOR(LR35902DebugBreakpointList, struct LR35902DebugBreakpoint);
|
||||
DECLARE_VECTOR(LR35902DebugWatchpointList, struct LR35902DebugWatchpoint);
|
||||
|
||||
struct LR35902Debugger {
|
||||
struct mDebuggerPlatform d;
|
||||
struct LR35902Core* cpu;
|
||||
|
||||
struct LR35902DebugBreakpointList breakpoints;
|
||||
struct LR35902DebugWatchpointList watchpoints;
|
||||
struct mBreakpointList breakpoints;
|
||||
struct mWatchpointList watchpoints;
|
||||
struct LR35902Memory originalMemory;
|
||||
|
||||
ssize_t nextId;
|
||||
|
||||
const struct LR35902Segment* segments;
|
||||
};
|
||||
|
||||
|
|
|
@ -13,12 +13,11 @@
|
|||
#include <mgba/internal/debugger/parser.h>
|
||||
|
||||
DEFINE_VECTOR(ARMDebugBreakpointList, struct ARMDebugBreakpoint);
|
||||
DEFINE_VECTOR(ARMDebugWatchpointList, struct ARMDebugWatchpoint);
|
||||
|
||||
static struct ARMDebugBreakpoint* _lookupBreakpoint(struct ARMDebugBreakpointList* breakpoints, uint32_t address) {
|
||||
size_t i;
|
||||
for (i = 0; i < ARMDebugBreakpointListSize(breakpoints); ++i) {
|
||||
if (ARMDebugBreakpointListGetPointer(breakpoints, i)->address == address) {
|
||||
if (ARMDebugBreakpointListGetPointer(breakpoints, i)->d.address == address) {
|
||||
return ARMDebugBreakpointListGetPointer(breakpoints, i);
|
||||
}
|
||||
}
|
||||
|
@ -26,13 +25,13 @@ static struct ARMDebugBreakpoint* _lookupBreakpoint(struct ARMDebugBreakpointLis
|
|||
}
|
||||
|
||||
static void _destroyBreakpoint(struct ARMDebugBreakpoint* breakpoint) {
|
||||
if (breakpoint->condition) {
|
||||
parseFree(breakpoint->condition);
|
||||
free(breakpoint->condition);
|
||||
if (breakpoint->d.condition) {
|
||||
parseFree(breakpoint->d.condition);
|
||||
free(breakpoint->d.condition);
|
||||
}
|
||||
}
|
||||
|
||||
static void _destroyWatchpoint(struct ARMDebugWatchpoint* watchpoint) {
|
||||
static void _destroyWatchpoint(struct mWatchpoint* watchpoint) {
|
||||
if (watchpoint->condition) {
|
||||
parseFree(watchpoint->condition);
|
||||
free(watchpoint->condition);
|
||||
|
@ -52,15 +51,15 @@ static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform* d) {
|
|||
if (!breakpoint) {
|
||||
return;
|
||||
}
|
||||
if (breakpoint->condition) {
|
||||
if (breakpoint->d.condition) {
|
||||
int32_t value;
|
||||
int segment;
|
||||
if (!mDebuggerEvaluateParseTree(d->p, breakpoint->condition, &value, &segment) || !(value || segment >= 0)) {
|
||||
if (!mDebuggerEvaluateParseTree(d->p, breakpoint->d.condition, &value, &segment) || !(value || segment >= 0)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
struct mDebuggerEntryInfo info = {
|
||||
.address = breakpoint->address,
|
||||
.address = breakpoint->d.address,
|
||||
.type.bp.breakType = BREAKPOINT_HARDWARE
|
||||
};
|
||||
mDebuggerEnter(d->p, DEBUGGER_ENTER_BREAKPOINT, &info);
|
||||
|
@ -71,12 +70,11 @@ 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, int segment);
|
||||
static void ARMDebuggerSetConditionalBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment, struct ParseTree* condition);
|
||||
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 ARMDebuggerSetConditionalWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type, struct ParseTree* condition);
|
||||
static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
static ssize_t ARMDebuggerSetBreakpoint(struct mDebuggerPlatform*, const struct mBreakpoint*);
|
||||
static bool ARMDebuggerClearBreakpoint(struct mDebuggerPlatform*, ssize_t id);
|
||||
static void ARMDebuggerListBreakpoints(struct mDebuggerPlatform*, struct mBreakpointList*);
|
||||
static ssize_t ARMDebuggerSetWatchpoint(struct mDebuggerPlatform*, const struct mWatchpoint*);
|
||||
static void ARMDebuggerListWatchpoints(struct mDebuggerPlatform*, struct mWatchpointList*);
|
||||
static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform*);
|
||||
static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform*);
|
||||
static void ARMDebuggerTrace(struct mDebuggerPlatform*, char* out, size_t* length);
|
||||
|
@ -89,11 +87,10 @@ struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) {
|
|||
platform->init = ARMDebuggerInit;
|
||||
platform->deinit = ARMDebuggerDeinit;
|
||||
platform->setBreakpoint = ARMDebuggerSetBreakpoint;
|
||||
platform->setConditionalBreakpoint = ARMDebuggerSetConditionalBreakpoint;
|
||||
platform->listBreakpoints = ARMDebuggerListBreakpoints;
|
||||
platform->clearBreakpoint = ARMDebuggerClearBreakpoint;
|
||||
platform->setWatchpoint = ARMDebuggerSetWatchpoint;
|
||||
platform->setConditionalWatchpoint = ARMDebuggerSetConditionalWatchpoint;
|
||||
platform->clearWatchpoint = ARMDebuggerClearWatchpoint;
|
||||
platform->listWatchpoints = ARMDebuggerListWatchpoints;
|
||||
platform->checkBreakpoints = ARMDebuggerCheckBreakpoints;
|
||||
platform->hasBreakpoints = ARMDebuggerHasBreakpoints;
|
||||
platform->trace = ARMDebuggerTrace;
|
||||
|
@ -106,9 +103,10 @@ void ARMDebuggerInit(void* cpu, struct mDebuggerPlatform* platform) {
|
|||
struct ARMDebugger* debugger = (struct ARMDebugger*) platform;
|
||||
debugger->cpu = cpu;
|
||||
debugger->originalMemory = debugger->cpu->memory;
|
||||
debugger->nextId = 1;
|
||||
ARMDebugBreakpointListInit(&debugger->breakpoints, 0);
|
||||
ARMDebugBreakpointListInit(&debugger->swBreakpoints, 0);
|
||||
ARMDebugWatchpointListInit(&debugger->watchpoints, 0);
|
||||
mWatchpointListInit(&debugger->watchpoints, 0);
|
||||
}
|
||||
|
||||
void ARMDebuggerDeinit(struct mDebuggerPlatform* platform) {
|
||||
|
@ -118,7 +116,7 @@ void ARMDebuggerDeinit(struct mDebuggerPlatform* platform) {
|
|||
size_t b;
|
||||
for (b = ARMDebugBreakpointListSize(&debugger->swBreakpoints); b; --b) {
|
||||
struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListGetPointer(&debugger->swBreakpoints, b - 1);
|
||||
debugger->clearSoftwareBreakpoint(debugger, breakpoint->address, breakpoint->sw.mode, breakpoint->sw.opcode);
|
||||
debugger->clearSoftwareBreakpoint(debugger, breakpoint);
|
||||
}
|
||||
}
|
||||
ARMDebuggerRemoveMemoryShim(debugger);
|
||||
|
@ -129,11 +127,11 @@ void ARMDebuggerDeinit(struct mDebuggerPlatform* platform) {
|
|||
}
|
||||
ARMDebugBreakpointListDeinit(&debugger->breakpoints);
|
||||
|
||||
for (i = 0; i < ARMDebugWatchpointListSize(&debugger->watchpoints); ++i) {
|
||||
_destroyWatchpoint(ARMDebugWatchpointListGetPointer(&debugger->watchpoints, i));
|
||||
for (i = 0; i < mWatchpointListSize(&debugger->watchpoints); ++i) {
|
||||
_destroyWatchpoint(mWatchpointListGetPointer(&debugger->watchpoints, i));
|
||||
}
|
||||
ARMDebugBreakpointListDeinit(&debugger->swBreakpoints);
|
||||
ARMDebugWatchpointListDeinit(&debugger->watchpoints);
|
||||
mWatchpointListDeinit(&debugger->watchpoints);
|
||||
}
|
||||
|
||||
static void ARMDebuggerEnter(struct mDebuggerPlatform* platform, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
|
||||
|
@ -142,16 +140,16 @@ static void ARMDebuggerEnter(struct mDebuggerPlatform* platform, enum mDebuggerE
|
|||
cpu->nextEvent = cpu->cycles;
|
||||
if (reason == DEBUGGER_ENTER_BREAKPOINT) {
|
||||
struct ARMDebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->swBreakpoints, _ARMPCAddress(cpu));
|
||||
if (breakpoint && breakpoint->isSw) {
|
||||
info->address = breakpoint->address;
|
||||
if (breakpoint && breakpoint->d.type == BREAKPOINT_SOFTWARE) {
|
||||
info->address = breakpoint->d.address;
|
||||
if (debugger->clearSoftwareBreakpoint) {
|
||||
debugger->clearSoftwareBreakpoint(debugger, breakpoint->address, breakpoint->sw.mode, breakpoint->sw.opcode);
|
||||
debugger->clearSoftwareBreakpoint(debugger, breakpoint);
|
||||
}
|
||||
|
||||
ARMRunFake(cpu, breakpoint->sw.opcode);
|
||||
|
||||
if (debugger->setSoftwareBreakpoint) {
|
||||
debugger->setSoftwareBreakpoint(debugger, breakpoint->address, breakpoint->sw.mode, &breakpoint->sw.opcode);
|
||||
debugger->setSoftwareBreakpoint(debugger, breakpoint->d.address, breakpoint->sw.mode, &breakpoint->sw.opcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -160,105 +158,135 @@ static void ARMDebuggerEnter(struct mDebuggerPlatform* platform, enum mDebuggerE
|
|||
}
|
||||
}
|
||||
|
||||
bool ARMDebuggerSetSoftwareBreakpoint(struct mDebuggerPlatform* d, uint32_t address, enum ExecutionMode mode) {
|
||||
ssize_t ARMDebuggerSetSoftwareBreakpoint(struct mDebuggerPlatform* d, uint32_t address, enum ExecutionMode mode) {
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
uint32_t opcode;
|
||||
if (!debugger->setSoftwareBreakpoint || !debugger->setSoftwareBreakpoint(debugger, address, mode, &opcode)) {
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->swBreakpoints);
|
||||
breakpoint->address = address;
|
||||
breakpoint->isSw = true;
|
||||
ssize_t id = debugger->nextId;
|
||||
++debugger->nextId;
|
||||
breakpoint->d.id = id;
|
||||
breakpoint->d.address = address;
|
||||
breakpoint->d.segment = -1;
|
||||
breakpoint->d.condition = NULL;
|
||||
breakpoint->d.type = BREAKPOINT_SOFTWARE;
|
||||
breakpoint->sw.opcode = opcode;
|
||||
breakpoint->sw.mode = mode;
|
||||
|
||||
return true;
|
||||
return id;
|
||||
}
|
||||
|
||||
void ARMDebuggerClearSoftwareBreakpoint(struct mDebuggerPlatform* d, uint32_t address) {
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
if (!debugger->clearSoftwareBreakpoint) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct ARMDebugBreakpoint* breakpoint = NULL;
|
||||
// Clear the stack backwards in case any overlap
|
||||
size_t b;
|
||||
for (b = ARMDebugBreakpointListSize(&debugger->swBreakpoints); b; --b) {
|
||||
breakpoint = ARMDebugBreakpointListGetPointer(&debugger->swBreakpoints, b - 1);
|
||||
if (breakpoint->address == address) {
|
||||
break;
|
||||
}
|
||||
breakpoint = NULL;
|
||||
}
|
||||
|
||||
if (breakpoint) {
|
||||
debugger->clearSoftwareBreakpoint(debugger, address, breakpoint->sw.mode, breakpoint->sw.opcode);
|
||||
}
|
||||
}
|
||||
|
||||
static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
|
||||
ARMDebuggerSetConditionalBreakpoint(d, address, segment, NULL);
|
||||
}
|
||||
|
||||
static void ARMDebuggerSetConditionalBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, struct ParseTree* condition) {
|
||||
UNUSED(segment);
|
||||
static ssize_t ARMDebuggerSetBreakpoint(struct mDebuggerPlatform* d, const struct mBreakpoint* info) {
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->breakpoints);
|
||||
breakpoint->condition = condition;
|
||||
breakpoint->address = address;
|
||||
breakpoint->isSw = false;
|
||||
ssize_t id = debugger->nextId;
|
||||
++debugger->nextId;
|
||||
breakpoint->d = *info;
|
||||
breakpoint->d.id = id;
|
||||
if (info->type == BREAKPOINT_SOFTWARE) {
|
||||
// TODO
|
||||
abort();
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
|
||||
UNUSED(segment);
|
||||
static bool ARMDebuggerClearBreakpoint(struct mDebuggerPlatform* d, ssize_t id) {
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
struct ARMDebugBreakpointList* breakpoints = &debugger->breakpoints;
|
||||
size_t i;
|
||||
|
||||
struct ARMDebugBreakpointList* breakpoints = &debugger->breakpoints;
|
||||
for (i = 0; i < ARMDebugBreakpointListSize(breakpoints); ++i) {
|
||||
if (ARMDebugBreakpointListGetPointer(breakpoints, i)->address == address) {
|
||||
if (ARMDebugBreakpointListGetPointer(breakpoints, i)->d.id == id) {
|
||||
_destroyBreakpoint(ARMDebugBreakpointListGetPointer(breakpoints, i));
|
||||
ARMDebugBreakpointListShift(breakpoints, i, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
struct ARMDebugBreakpointList* swBreakpoints = &debugger->swBreakpoints;
|
||||
if (debugger->clearSoftwareBreakpoint) {
|
||||
for (i = 0; i < ARMDebugBreakpointListSize(swBreakpoints); ++i) {
|
||||
if (ARMDebugBreakpointListGetPointer(swBreakpoints, i)->d.id == id) {
|
||||
debugger->clearSoftwareBreakpoint(debugger, ARMDebugBreakpointListGetPointer(swBreakpoints, i));
|
||||
ARMDebugBreakpointListShift(swBreakpoints, i, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct mWatchpointList* watchpoints = &debugger->watchpoints;
|
||||
for (i = 0; i < mWatchpointListSize(watchpoints); ++i) {
|
||||
if (mWatchpointListGetPointer(watchpoints, i)->id == id) {
|
||||
_destroyWatchpoint(mWatchpointListGetPointer(watchpoints, i));
|
||||
mWatchpointListShift(watchpoints, i, 1);
|
||||
if (!mWatchpointListSize(&debugger->watchpoints)) {
|
||||
ARMDebuggerRemoveMemoryShim(debugger);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ARMDebuggerListBreakpoints(struct mDebuggerPlatform* d, struct mBreakpointList* list) {
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
mBreakpointListClear(list);
|
||||
size_t i, s;
|
||||
for (i = 0, s = 0; i < ARMDebugBreakpointListSize(&debugger->breakpoints) || s < ARMDebugBreakpointListSize(&debugger->swBreakpoints);) {
|
||||
struct ARMDebugBreakpoint* hw = NULL;
|
||||
struct ARMDebugBreakpoint* sw = NULL;
|
||||
if (i < ARMDebugBreakpointListSize(&debugger->breakpoints)) {
|
||||
hw = ARMDebugBreakpointListGetPointer(&debugger->breakpoints, i);
|
||||
}
|
||||
if (s < ARMDebugBreakpointListSize(&debugger->swBreakpoints)) {
|
||||
sw = ARMDebugBreakpointListGetPointer(&debugger->swBreakpoints, s);
|
||||
}
|
||||
struct mBreakpoint* b = mBreakpointListAppend(list);
|
||||
if (hw && sw) {
|
||||
if (hw->d.id < sw->d.id) {
|
||||
*b = hw->d;
|
||||
++i;
|
||||
} else {
|
||||
*b = sw->d;
|
||||
++s;
|
||||
}
|
||||
} else if (hw) {
|
||||
*b = hw->d;
|
||||
++i;
|
||||
} else if (sw) {
|
||||
*b = sw->d;
|
||||
++s;
|
||||
} else {
|
||||
abort(); // Should be unreachable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform* d) {
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
return ARMDebugBreakpointListSize(&debugger->breakpoints) || ARMDebugWatchpointListSize(&debugger->watchpoints);
|
||||
return ARMDebugBreakpointListSize(&debugger->breakpoints) || mWatchpointListSize(&debugger->watchpoints);
|
||||
}
|
||||
|
||||
static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type) {
|
||||
ARMDebuggerSetConditionalWatchpoint(d, address, segment, type, NULL);
|
||||
}
|
||||
|
||||
static void ARMDebuggerSetConditionalWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type, struct ParseTree* condition) {
|
||||
UNUSED(segment);
|
||||
static ssize_t ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, const struct mWatchpoint* info) {
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
if (!ARMDebugWatchpointListSize(&debugger->watchpoints)) {
|
||||
if (!mWatchpointListSize(&debugger->watchpoints)) {
|
||||
ARMDebuggerInstallMemoryShim(debugger);
|
||||
}
|
||||
struct ARMDebugWatchpoint* watchpoint = ARMDebugWatchpointListAppend(&debugger->watchpoints);
|
||||
watchpoint->address = address;
|
||||
watchpoint->type = type;
|
||||
watchpoint->condition = condition;
|
||||
struct mWatchpoint* watchpoint = mWatchpointListAppend(&debugger->watchpoints);
|
||||
ssize_t id = debugger->nextId;
|
||||
++debugger->nextId;
|
||||
*watchpoint = *info;
|
||||
watchpoint->id = id;
|
||||
return id;
|
||||
}
|
||||
|
||||
static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
|
||||
UNUSED(segment);
|
||||
static void ARMDebuggerListWatchpoints(struct mDebuggerPlatform* d, struct mWatchpointList* list) {
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
struct ARMDebugWatchpointList* watchpoints = &debugger->watchpoints;
|
||||
size_t i;
|
||||
for (i = 0; i < ARMDebugWatchpointListSize(watchpoints); ++i) {
|
||||
if (ARMDebugWatchpointListGetPointer(watchpoints, i)->address == address) {
|
||||
_destroyWatchpoint(ARMDebugWatchpointListGetPointer(watchpoints, i));
|
||||
ARMDebugWatchpointListShift(watchpoints, i, 1);
|
||||
}
|
||||
}
|
||||
if (!ARMDebugWatchpointListSize(&debugger->watchpoints)) {
|
||||
ARMDebuggerRemoveMemoryShim(debugger);
|
||||
}
|
||||
mWatchpointListClear(list);
|
||||
mWatchpointListCopy(list, &debugger->watchpoints);
|
||||
}
|
||||
|
||||
static void ARMDebuggerTrace(struct mDebuggerPlatform* d, char* out, size_t* length) {
|
||||
|
|
|
@ -93,10 +93,10 @@ CREATE_SHIM(setActiveRegion, void, (struct ARMCore* cpu, uint32_t address), addr
|
|||
|
||||
static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, struct mDebuggerEntryInfo* info, enum mWatchpointType type, uint32_t newValue, int width) {
|
||||
--width;
|
||||
struct ARMDebugWatchpoint* watchpoint;
|
||||
struct mWatchpoint* watchpoint;
|
||||
size_t i;
|
||||
for (i = 0; i < ARMDebugWatchpointListSize(&debugger->watchpoints); ++i) {
|
||||
watchpoint = ARMDebugWatchpointListGetPointer(&debugger->watchpoints, i);
|
||||
for (i = 0; i < mWatchpointListSize(&debugger->watchpoints); ++i) {
|
||||
watchpoint = mWatchpointListGetPointer(&debugger->watchpoints, i);
|
||||
if (!((watchpoint->address ^ address) & ~width) && watchpoint->type & type) {
|
||||
if (watchpoint->condition) {
|
||||
int32_t value;
|
||||
|
|
|
@ -104,7 +104,7 @@ static void _setFilterLevel(const char* key, const char* value, enum mCoreConfig
|
|||
char* end;
|
||||
int ivalue = strtol(value, &end, 10);
|
||||
if (ivalue == 0) {
|
||||
ivalue = INT_MIN; // Zero is reserved
|
||||
ivalue = 0x80; // Zero is reserved
|
||||
}
|
||||
if (!end) {
|
||||
return;
|
||||
|
@ -113,34 +113,66 @@ static void _setFilterLevel(const char* key, const char* value, enum mCoreConfig
|
|||
}
|
||||
|
||||
void mLogFilterLoad(struct mLogFilter* filter, const struct mCoreConfig* config) {
|
||||
HashTableClear(&filter->categories);
|
||||
TableClear(&filter->levels);
|
||||
|
||||
mCoreConfigEnumerate(config, "logLevel.", _setFilterLevel, filter);
|
||||
filter->defaultLevels = mLOG_ALL;
|
||||
mCoreConfigGetIntValue(config, "logLevel", &filter->defaultLevels);
|
||||
}
|
||||
|
||||
void mLogFilterSave(const struct mLogFilter* filter, struct mCoreConfig* config) {
|
||||
mCoreConfigSetIntValue(config, "logLevel", filter->defaultLevels);
|
||||
int i;
|
||||
for (i = 0; i < _category; ++i) {
|
||||
char configName[128] = {0};
|
||||
snprintf(configName, sizeof(configName) - 1, "logLevel.%s", mLogCategoryId(i));
|
||||
int levels = mLogFilterLevels(filter, i);
|
||||
if (levels) {
|
||||
mCoreConfigSetIntValue(config, configName, levels & ~0x80);
|
||||
} else {
|
||||
mCoreConfigSetValue(config, configName, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mLogFilterSet(struct mLogFilter* filter, const char* category, int levels) {
|
||||
levels |= 0x80;
|
||||
HashTableInsert(&filter->categories, category, (void*)(intptr_t) levels);
|
||||
// Can't do this eagerly because not all categories are initialized immediately
|
||||
int cat = mLogCategoryById(category);
|
||||
if (cat >= 0) {
|
||||
TableInsert(&filter->levels, cat, (void*)(intptr_t) levels);
|
||||
}
|
||||
|
||||
}
|
||||
bool mLogFilterTest(struct mLogFilter* filter, int category, enum mLogLevel level) {
|
||||
int value = (intptr_t) TableLookup(&filter->levels, category);
|
||||
|
||||
void mLogFilterReset(struct mLogFilter* filter, const char* category) {
|
||||
HashTableRemove(&filter->categories, category);
|
||||
// Can't do this eagerly because not all categories are initialized immediately
|
||||
int cat = mLogCategoryById(category);
|
||||
if (cat >= 0) {
|
||||
TableRemove(&filter->levels, cat);
|
||||
}
|
||||
}
|
||||
|
||||
bool mLogFilterTest(const struct mLogFilter* filter, int category, enum mLogLevel level) {
|
||||
int value = mLogFilterLevels(filter, category);
|
||||
if (value) {
|
||||
return value & level;
|
||||
}
|
||||
const char* cat = mLogCategoryId(category);
|
||||
if (cat) {
|
||||
value = (intptr_t) HashTableLookup(&filter->categories, cat);
|
||||
if (value) {
|
||||
TableInsert(&filter->levels, category, (void*)(intptr_t) value);
|
||||
return value & level;
|
||||
}
|
||||
}
|
||||
return level & filter->defaultLevels;
|
||||
}
|
||||
|
||||
int mLogFilterLevels(const struct mLogFilter* filter , int category) {
|
||||
int value = (intptr_t) TableLookup(&filter->levels, category);
|
||||
if (value) {
|
||||
return value;
|
||||
}
|
||||
const char* cat = mLogCategoryId(category);
|
||||
if (cat) {
|
||||
value = (intptr_t) HashTableLookup(&filter->categories, cat);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
mLOG_DEFINE_CATEGORY(STATUS, "Status", "core.status")
|
||||
|
|
|
@ -305,6 +305,7 @@ bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags) {
|
|||
|
||||
if (flags & SAVESTATE_METADATA) {
|
||||
uint64_t* creationUsec = malloc(sizeof(*creationUsec));
|
||||
if (creationUsec) {
|
||||
#ifndef _MSC_VER
|
||||
struct timeval tv;
|
||||
if (!gettimeofday(&tv, 0)) {
|
||||
|
@ -321,9 +322,12 @@ bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags) {
|
|||
}
|
||||
#endif
|
||||
else {
|
||||
free(creationUsec);
|
||||
creationUsec = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (creationUsec) {
|
||||
struct mStateExtdataItem item = {
|
||||
.size = sizeof(*creationUsec),
|
||||
.data = creationUsec,
|
||||
|
@ -331,6 +335,7 @@ bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags) {
|
|||
};
|
||||
mStateExtdataPut(&extdata, EXTDATA_META_TIME, &item);
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & SAVESTATE_SAVEDATA) {
|
||||
void* sram = NULL;
|
||||
|
|
|
@ -46,9 +46,12 @@ static void _readHalfword(struct CLIDebugger*, struct CLIDebugVector*);
|
|||
static void _readWord(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _setBreakpoint(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _clearBreakpoint(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _setWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _listBreakpoints(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _setReadWriteWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _setReadWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _setWriteWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _setWriteChangedWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _listWatchpoints(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _trace(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _writeByte(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _writeHalfword(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
|
@ -75,6 +78,10 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = {
|
|||
{ "help", _printHelp, "S", "Print help" },
|
||||
{ "i", _printStatus, "", "Print the current status" },
|
||||
{ "info", _printStatus, "", "Print the current status" },
|
||||
{ "lb", _listBreakpoints, "", "List breakpoints" },
|
||||
{ "listb", _listBreakpoints, "", "List breakpoints" },
|
||||
{ "lw", _listWatchpoints, "", "List watchpoints" },
|
||||
{ "listw", _listWatchpoints, "", "List watchpoints" },
|
||||
{ "n", _next, "", "Execute next instruction" },
|
||||
{ "next", _next, "", "Execute next instruction" },
|
||||
{ "p", _print, "I", "Print a value" },
|
||||
|
@ -91,12 +98,13 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = {
|
|||
{ "r/4", _readWord, "I", "Read a word from a specified offset" },
|
||||
{ "status", _printStatus, "", "Print the current status" },
|
||||
{ "trace", _trace, "I", "Trace a fixed number of instructions" },
|
||||
{ "w", _setWatchpoint, "Is", "Set a watchpoint" },
|
||||
{ "w", _setReadWriteWatchpoint, "Is", "Set a watchpoint" },
|
||||
{ "w/1", _writeByte, "II", "Write a byte at a specified offset" },
|
||||
{ "w/2", _writeHalfword, "II", "Write a halfword at a specified offset" },
|
||||
{ "w/r", _writeRegister, "SI", "Write a register" },
|
||||
{ "w/4", _writeWord, "II", "Write a word at a specified offset" },
|
||||
{ "watch", _setWatchpoint, "Is", "Set a watchpoint" },
|
||||
{ "watch", _setReadWriteWatchpoint, "Is", "Set a watchpoint" },
|
||||
{ "watch/c", _setWriteChangedWatchpoint, "Is", "Set a change watchpoint" },
|
||||
{ "watch/r", _setReadWatchpoint, "Is", "Set a read watchpoint" },
|
||||
{ "watch/w", _setWriteWatchpoint, "Is", "Set a write watchpoint" },
|
||||
{ "x/1", _dumpByte, "Ii", "Examine bytes at a specified offset" },
|
||||
|
@ -488,20 +496,24 @@ static void _setBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector*
|
|||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
|
||||
return;
|
||||
}
|
||||
uint32_t address = dv->intValue;
|
||||
struct mBreakpoint breakpoint = {
|
||||
.address = dv->intValue,
|
||||
.segment = dv->segmentValue,
|
||||
.type = BREAKPOINT_HARDWARE
|
||||
};
|
||||
if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) {
|
||||
struct ParseTree* tree = _parseTree(dv->next->charValue);
|
||||
if (tree) {
|
||||
debugger->d.platform->setConditionalBreakpoint(debugger->d.platform, address, dv->segmentValue, tree);
|
||||
breakpoint.condition = tree;
|
||||
} else {
|
||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
debugger->d.platform->setBreakpoint(debugger->d.platform, address, dv->segmentValue);
|
||||
}
|
||||
debugger->d.platform->setBreakpoint(debugger->d.platform, &breakpoint);
|
||||
}
|
||||
|
||||
static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv, enum mWatchpointType type) {
|
||||
if (!dv || dv->type != CLIDV_INT_TYPE) {
|
||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
|
||||
return;
|
||||
|
@ -510,72 +522,80 @@ static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector*
|
|||
debugger->backend->printf(debugger->backend, "Watchpoints are not supported by this platform.\n");
|
||||
return;
|
||||
}
|
||||
uint32_t address = dv->intValue;
|
||||
struct mWatchpoint watchpoint = {
|
||||
.address = dv->intValue,
|
||||
.segment = dv->segmentValue,
|
||||
.type = type
|
||||
};
|
||||
if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) {
|
||||
struct ParseTree* tree = _parseTree(dv->next->charValue);
|
||||
if (tree) {
|
||||
debugger->d.platform->setConditionalWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_RW, tree);
|
||||
watchpoint.condition = tree;
|
||||
} else {
|
||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_RW);
|
||||
}
|
||||
debugger->d.platform->setWatchpoint(debugger->d.platform, &watchpoint);
|
||||
}
|
||||
|
||||
static void _setReadWriteWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
_setWatchpoint(debugger, dv, WATCHPOINT_RW);
|
||||
}
|
||||
|
||||
static void _setReadWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
if (!dv || dv->type != CLIDV_INT_TYPE) {
|
||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
|
||||
return;
|
||||
}
|
||||
if (!debugger->d.platform->setWatchpoint) {
|
||||
debugger->backend->printf(debugger->backend, "Watchpoints are not supported by this platform.\n");
|
||||
return;
|
||||
}
|
||||
uint32_t address = dv->intValue;
|
||||
if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) {
|
||||
struct ParseTree* tree = _parseTree(dv->next->charValue);
|
||||
if (tree) {
|
||||
debugger->d.platform->setConditionalWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_READ, tree);
|
||||
} else {
|
||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
|
||||
}
|
||||
} else {
|
||||
debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_READ);
|
||||
}
|
||||
_setWatchpoint(debugger, dv, WATCHPOINT_READ);
|
||||
}
|
||||
|
||||
static void _setWriteWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
if (!dv || dv->type != CLIDV_INT_TYPE) {
|
||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
|
||||
return;
|
||||
}
|
||||
if (!debugger->d.platform->setWatchpoint) {
|
||||
debugger->backend->printf(debugger->backend, "Watchpoints are not supported by this platform.\n");
|
||||
return;
|
||||
}
|
||||
uint32_t address = dv->intValue;
|
||||
if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) {
|
||||
struct ParseTree* tree = _parseTree(dv->next->charValue);
|
||||
if (tree) {
|
||||
debugger->d.platform->setConditionalWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_WRITE, tree);
|
||||
} else {
|
||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
|
||||
}
|
||||
} else {
|
||||
debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_WRITE);
|
||||
}}
|
||||
_setWatchpoint(debugger, dv, WATCHPOINT_WRITE);
|
||||
}
|
||||
|
||||
static void _setWriteChangedWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
_setWatchpoint(debugger, dv, WATCHPOINT_WRITE_CHANGE);
|
||||
}
|
||||
|
||||
static void _clearBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
if (!dv || dv->type != CLIDV_INT_TYPE) {
|
||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
|
||||
return;
|
||||
}
|
||||
uint32_t address = dv->intValue;
|
||||
debugger->d.platform->clearBreakpoint(debugger->d.platform, address, dv->segmentValue);
|
||||
if (debugger->d.platform->clearWatchpoint) {
|
||||
debugger->d.platform->clearWatchpoint(debugger->d.platform, address, dv->segmentValue);
|
||||
uint64_t id = dv->intValue;
|
||||
debugger->d.platform->clearBreakpoint(debugger->d.platform, id);
|
||||
}
|
||||
|
||||
static void _listBreakpoints(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
UNUSED(dv);
|
||||
struct mBreakpointList breakpoints;
|
||||
mBreakpointListInit(&breakpoints, 0);
|
||||
debugger->d.platform->listBreakpoints(debugger->d.platform, &breakpoints);
|
||||
size_t i;
|
||||
for (i = 0; i < mBreakpointListSize(&breakpoints); ++i) {
|
||||
struct mBreakpoint* breakpoint = mBreakpointListGetPointer(&breakpoints, i);
|
||||
if (breakpoint->segment >= 0) {
|
||||
debugger->backend->printf(debugger->backend, "%" PRIz "i: %02X:%X\n", breakpoint->id, breakpoint->segment, breakpoint->address);
|
||||
} else {
|
||||
debugger->backend->printf(debugger->backend, "%" PRIz "i: 0x%X\n", breakpoint->id, breakpoint->address);
|
||||
}
|
||||
}
|
||||
mBreakpointListDeinit(&breakpoints);
|
||||
}
|
||||
|
||||
static void _listWatchpoints(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
UNUSED(dv);
|
||||
struct mWatchpointList watchpoints;
|
||||
mWatchpointListInit(&watchpoints, 0);
|
||||
debugger->d.platform->listWatchpoints(debugger->d.platform, &watchpoints);
|
||||
size_t i;
|
||||
for (i = 0; i < mWatchpointListSize(&watchpoints); ++i) {
|
||||
struct mWatchpoint* watchpoint = mWatchpointListGetPointer(&watchpoints, i);
|
||||
if (watchpoint->segment >= 0) {
|
||||
debugger->backend->printf(debugger->backend, "%" PRIz "i: %02X:%X\n", watchpoint->id, watchpoint->segment, watchpoint->address);
|
||||
} else {
|
||||
debugger->backend->printf(debugger->backend, "%" PRIz "i: 0x%X\n", watchpoint->id, watchpoint->address);
|
||||
}
|
||||
}
|
||||
mWatchpointListDeinit(&watchpoints);
|
||||
}
|
||||
|
||||
static void _trace(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
|
|
|
@ -22,6 +22,9 @@ const uint32_t DEBUGGER_ID = 0xDEADBEEF;
|
|||
|
||||
mLOG_DEFINE_CATEGORY(DEBUGGER, "Debugger", "core.debugger");
|
||||
|
||||
DEFINE_VECTOR(mBreakpointList, struct mBreakpoint);
|
||||
DEFINE_VECTOR(mWatchpointList, struct mWatchpoint);
|
||||
|
||||
static void mDebuggerInit(void* cpu, struct mCPUComponent* component);
|
||||
static void mDebuggerDeinit(struct mCPUComponent* component);
|
||||
|
||||
|
|
|
@ -62,6 +62,8 @@ static void _gdbStubEntered(struct mDebugger* debugger, enum mDebuggerEntryReaso
|
|||
}
|
||||
return;
|
||||
}
|
||||
// Fall through
|
||||
case WATCHPOINT_WRITE_CHANGE:
|
||||
type = "watch";
|
||||
break;
|
||||
case WATCHPOINT_READ:
|
||||
|
@ -488,21 +490,32 @@ static void _setBreakpoint(struct GDBStub* stub, const char* message) {
|
|||
readAddress += i + 1;
|
||||
uint32_t kind = _readHex(readAddress, &i);
|
||||
|
||||
struct mBreakpoint breakpoint = {
|
||||
.address = address,
|
||||
.type = BREAKPOINT_HARDWARE
|
||||
};
|
||||
struct mWatchpoint watchpoint = {
|
||||
.address = address
|
||||
};
|
||||
|
||||
switch (message[0]) {
|
||||
case '0':
|
||||
ARMDebuggerSetSoftwareBreakpoint(stub->d.platform, address, kind == 2 ? MODE_THUMB : MODE_ARM);
|
||||
break;
|
||||
case '1':
|
||||
stub->d.platform->setBreakpoint(stub->d.platform, address, -1);
|
||||
stub->d.platform->setBreakpoint(stub->d.platform, &breakpoint);
|
||||
break;
|
||||
case '2':
|
||||
stub->d.platform->setWatchpoint(stub->d.platform, address, -1, WATCHPOINT_WRITE);
|
||||
watchpoint.type = WATCHPOINT_WRITE_CHANGE;
|
||||
stub->d.platform->setWatchpoint(stub->d.platform, &watchpoint);
|
||||
break;
|
||||
case '3':
|
||||
stub->d.platform->setWatchpoint(stub->d.platform, address, -1, WATCHPOINT_READ);
|
||||
watchpoint.type = WATCHPOINT_READ;
|
||||
stub->d.platform->setWatchpoint(stub->d.platform, &watchpoint);
|
||||
break;
|
||||
case '4':
|
||||
stub->d.platform->setWatchpoint(stub->d.platform, address, -1, WATCHPOINT_RW);
|
||||
watchpoint.type = WATCHPOINT_RW;
|
||||
stub->d.platform->setWatchpoint(stub->d.platform, &watchpoint);
|
||||
break;
|
||||
default:
|
||||
stub->outgoing[0] = '\0';
|
||||
|
@ -517,17 +530,35 @@ static void _clearBreakpoint(struct GDBStub* stub, const char* message) {
|
|||
const char* readAddress = &message[2];
|
||||
unsigned i = 0;
|
||||
uint32_t address = _readHex(readAddress, &i);
|
||||
struct mBreakpointList breakpoints;
|
||||
struct mWatchpointList watchpoints;
|
||||
|
||||
size_t index;
|
||||
switch (message[0]) {
|
||||
case '0':
|
||||
ARMDebuggerClearSoftwareBreakpoint(stub->d.platform, address);
|
||||
break;
|
||||
case '1':
|
||||
stub->d.platform->clearBreakpoint(stub->d.platform, address, -1);
|
||||
mBreakpointListInit(&breakpoints, 0);
|
||||
stub->d.platform->listBreakpoints(stub->d.platform, &breakpoints);
|
||||
for (index = 0; index < mBreakpointListSize(&breakpoints); ++index) {
|
||||
if (mBreakpointListGetPointer(&breakpoints, index)->address != address) {
|
||||
continue;
|
||||
}
|
||||
stub->d.platform->clearBreakpoint(stub->d.platform, mBreakpointListGetPointer(&breakpoints, index)->id);
|
||||
}
|
||||
mBreakpointListDeinit(&breakpoints);
|
||||
break;
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
stub->d.platform->clearWatchpoint(stub->d.platform, address, -1);
|
||||
mWatchpointListInit(&watchpoints, 0);
|
||||
stub->d.platform->listWatchpoints(stub->d.platform, &watchpoints);
|
||||
for (index = 0; index < mWatchpointListSize(&watchpoints); ++index) {
|
||||
if (mWatchpointListGetPointer(&watchpoints, index)->address != address) {
|
||||
continue;
|
||||
}
|
||||
stub->d.platform->clearBreakpoint(stub->d.platform, mWatchpointListGetPointer(&watchpoints, index)->id);
|
||||
}
|
||||
mWatchpointListDeinit(&watchpoints);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
|
@ -510,10 +510,10 @@ void _GBMBC3(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
GBMBCSwitchBank(gb, bank);
|
||||
break;
|
||||
case 0x2:
|
||||
if (value < 4) {
|
||||
if (value < 8) {
|
||||
GBMBCSwitchSramBank(gb, value);
|
||||
memory->rtcAccess = false;
|
||||
} else if (value >= 8 && value <= 0xC) {
|
||||
} else if (value <= 0xC) {
|
||||
memory->activeRtcReg = value - 8;
|
||||
memory->rtcAccess = true;
|
||||
}
|
||||
|
|
|
@ -555,6 +555,9 @@ static void _GBACoreSetPeripheral(struct mCore* core, int type, void* periph) {
|
|||
case mPERIPH_GBA_LUMINANCE:
|
||||
gba->luminanceSource = periph;
|
||||
break;
|
||||
case mPERIPH_GBA_BATTLECHIP_GATE:
|
||||
GBASIOSetDriver(&gba->sio, periph, SIO_MULTI);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
/* Copyright (c) 2013-2018 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/gba/interface.h>
|
||||
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
#include <mgba/internal/gba/io.h>
|
||||
#include <mgba/internal/gba/sio.h>
|
||||
|
||||
mLOG_DECLARE_CATEGORY(GBA_BATTLECHIP);
|
||||
mLOG_DEFINE_CATEGORY(GBA_BATTLECHIP, "GBA BattleChip Gate", "gba.battlechip");
|
||||
|
||||
enum {
|
||||
BATTLECHIP_INDEX_HANDSHAKE_0 = 0,
|
||||
BATTLECHIP_INDEX_HANDSHAKE_1 = 1,
|
||||
BATTLECHIP_INDEX_ID = 2,
|
||||
BATTLECHIP_INDEX_END = 6
|
||||
};
|
||||
|
||||
enum {
|
||||
BATTLECHIP_OK = 0xFFC6,
|
||||
BATTLECHIP_CONTINUE = 0xFFFF,
|
||||
};
|
||||
|
||||
static bool GBASIOBattlechipGateInit(struct GBASIODriver* driver);
|
||||
static bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver);
|
||||
static uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value);
|
||||
|
||||
static void _battlechipTransfer(struct GBASIOBattlechipGate* gate);
|
||||
static void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate);
|
||||
|
||||
void GBASIOBattlechipGateCreate(struct GBASIOBattlechipGate* gate) {
|
||||
gate->d.init = GBASIOBattlechipGateInit;
|
||||
gate->d.deinit = NULL;
|
||||
gate->d.load = GBASIOBattlechipGateLoad;
|
||||
gate->d.unload = NULL;
|
||||
gate->d.writeRegister = GBASIOBattlechipGateWriteRegister;
|
||||
|
||||
gate->event.context = gate;
|
||||
gate->event.callback = _battlechipTransferEvent;
|
||||
gate->event.priority = 0x80;
|
||||
}
|
||||
|
||||
bool GBASIOBattlechipGateInit(struct GBASIODriver* driver) {
|
||||
struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
|
||||
gate->chipId = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GBASIOBattlechipGateLoad(struct GBASIODriver* driver) {
|
||||
struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
|
||||
gate->index = BATTLECHIP_INDEX_END;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t GBASIOBattlechipGateWriteRegister(struct GBASIODriver* driver, uint32_t address, uint16_t value) {
|
||||
struct GBASIOBattlechipGate* gate = (struct GBASIOBattlechipGate*) driver;
|
||||
switch (address) {
|
||||
case REG_SIOCNT:
|
||||
value &= ~0xC;
|
||||
value |= 0x8;
|
||||
if (value & 0x80) {
|
||||
_battlechipTransfer(gate);
|
||||
}
|
||||
break;
|
||||
case REG_SIOMLT_SEND:
|
||||
break;
|
||||
case REG_RCNT:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void _battlechipTransfer(struct GBASIOBattlechipGate* gate) {
|
||||
int32_t cycles = GBASIOCyclesPerTransfer[gate->d.p->multiplayerControl.baud][1];
|
||||
mTimingSchedule(&gate->d.p->p->timing, &gate->event, cycles);
|
||||
}
|
||||
|
||||
void _battlechipTransferEvent(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
||||
struct GBASIOBattlechipGate* gate = user;
|
||||
|
||||
uint16_t cmd = gate->d.p->p->memory.io[REG_SIOMLT_SEND >> 1];
|
||||
uint16_t reply = 0xFFFF;
|
||||
gate->d.p->p->memory.io[REG_SIOMULTI0 >> 1] = cmd;
|
||||
gate->d.p->p->memory.io[REG_SIOMULTI2 >> 1] = 0xFFFF;
|
||||
gate->d.p->p->memory.io[REG_SIOMULTI3 >> 1] = 0xFFFF;
|
||||
gate->d.p->multiplayerControl.busy = 0;
|
||||
gate->d.p->multiplayerControl.id = 0;
|
||||
|
||||
mLOG(GBA_BATTLECHIP, DEBUG, "> %04x", cmd);
|
||||
|
||||
switch (cmd) {
|
||||
case 0x4000:
|
||||
gate->index = 0;
|
||||
// Fall through
|
||||
case 0:
|
||||
switch (gate->index) {
|
||||
case BATTLECHIP_INDEX_HANDSHAKE_0:
|
||||
reply = 0x00FE;
|
||||
break;
|
||||
case BATTLECHIP_INDEX_HANDSHAKE_1:
|
||||
reply = 0xFFFE;
|
||||
break;
|
||||
case BATTLECHIP_INDEX_ID:
|
||||
reply = gate->chipId;
|
||||
break;
|
||||
default:
|
||||
if (gate->index >= BATTLECHIP_INDEX_END) {
|
||||
reply = BATTLECHIP_OK;
|
||||
} else if (gate->index < 0) {
|
||||
reply = BATTLECHIP_CONTINUE;
|
||||
} else {
|
||||
reply = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
++gate->index;
|
||||
break;
|
||||
case 0x8FFF:
|
||||
gate->index = -2;
|
||||
// Fall through
|
||||
default:
|
||||
case 0xA3D0:
|
||||
reply = BATTLECHIP_OK;
|
||||
break;
|
||||
case 0x4234:
|
||||
case 0x574A:
|
||||
reply = BATTLECHIP_CONTINUE;
|
||||
break;
|
||||
}
|
||||
|
||||
mLOG(GBA_BATTLECHIP, DEBUG, "< %04x", reply);
|
||||
|
||||
gate->d.p->p->memory.io[REG_SIOMULTI1 >> 1] = reply;
|
||||
|
||||
if (gate->d.p->multiplayerControl.irq) {
|
||||
GBARaiseIRQ(gate->d.p->p, IRQ_SIO);
|
||||
}
|
||||
}
|
|
@ -523,7 +523,7 @@ void GBADebug(struct GBA* gba, uint16_t flags) {
|
|||
strncpy(oolBuf, gba->debugString, sizeof(oolBuf) - 1);
|
||||
memset(gba->debugString, 0, sizeof(gba->debugString));
|
||||
oolBuf[0x100] = '\0';
|
||||
mLog(_mLOG_CAT_GBA_DEBUG(), level, "%s", oolBuf);
|
||||
mLog(_mLOG_CAT_GBA_DEBUG, level, "%s", oolBuf);
|
||||
}
|
||||
gba->debugFlags = GBADebugFlagsClearSend(gba->debugFlags);
|
||||
}
|
||||
|
|
|
@ -1647,6 +1647,10 @@ void _pristineCow(struct GBA* gba) {
|
|||
}
|
||||
|
||||
void GBAPrintFlush(struct GBA* gba) {
|
||||
if (!gba->memory.agbPrintBuffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
char oolBuf[0x101];
|
||||
size_t i;
|
||||
for (i = 0; gba->memory.agbPrintCtx.get != gba->memory.agbPrintCtx.put && i < 0x100; ++i) {
|
||||
|
@ -1688,8 +1692,8 @@ static void _agbPrintStore(struct GBA* gba, uint32_t address, int16_t value) {
|
|||
|
||||
static int16_t _agbPrintLoad(struct GBA* gba, uint32_t address) {
|
||||
struct GBAMemory* memory = &gba->memory;
|
||||
int16_t value = 0xFFFF;
|
||||
if (address < AGB_PRINT_TOP) {
|
||||
int16_t value = address >> 1;
|
||||
if (address < AGB_PRINT_TOP && memory->agbPrintBuffer) {
|
||||
LOAD_16(value, address & (SIZE_AGB_PRINT - 1), memory->agbPrintBuffer);
|
||||
} else if ((address & 0x00FFFFF8) == (AGB_PRINT_STRUCT & 0x00FFFFF8)) {
|
||||
value = (&memory->agbPrintCtx.request)[(address & 7) >> 1];
|
||||
|
|
|
@ -27,9 +27,6 @@
|
|||
} \
|
||||
} \
|
||||
for (; outX < condition; ++outX, inX += xOffset) { \
|
||||
if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
|
||||
continue; \
|
||||
} \
|
||||
int localX = inX - xOffset * (outX % mosaicH); \
|
||||
if (localX < 0 || localX > width - 1) { \
|
||||
continue; \
|
||||
|
@ -43,9 +40,6 @@
|
|||
unsigned widthMask = ~(width - 1); \
|
||||
unsigned heightMask = ~(height - 1); \
|
||||
for (; outX < condition; ++outX, ++inX) { \
|
||||
if (!(renderer->row[outX] & FLAG_UNWRITTEN)) { \
|
||||
continue; \
|
||||
} \
|
||||
renderer->spriteCyclesRemaining -= 2; \
|
||||
xAccum += mat.a; \
|
||||
yAccum += mat.c; \
|
||||
|
@ -239,9 +233,6 @@ int GBAVideoSoftwareRendererPreprocessSprite(struct GBAVideoSoftwareRenderer* re
|
|||
if (!renderer->d.vramOBJ[charBase >> VRAM_BLOCK_OFFSET]) {
|
||||
return 0;
|
||||
}
|
||||
if (renderer->spriteCyclesRemaining <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int objwinSlowPath = GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt) && GBAWindowControlGetBlendEnable(renderer->objwin.packed) != GBAWindowControlIsBlendEnable(renderer->currentWindow.packed);
|
||||
int variant = renderer->target1Obj &&
|
||||
|
|
|
@ -955,7 +955,6 @@ void GBAVideoSoftwareRendererPostprocessBuffer(struct GBAVideoSoftwareRenderer*
|
|||
|
||||
int GBAVideoSoftwareRendererPreprocessSpriteLayer(struct GBAVideoSoftwareRenderer* renderer, int y) {
|
||||
int w;
|
||||
renderer->end = 0;
|
||||
int spriteLayers = 0;
|
||||
if (GBARegisterDISPCNTIsObjEnable(renderer->dispcnt) && !renderer->d.disableOBJ) {
|
||||
if (renderer->oamDirty) {
|
||||
|
@ -964,31 +963,34 @@ int GBAVideoSoftwareRendererPreprocessSpriteLayer(struct GBAVideoSoftwareRendere
|
|||
renderer->spriteCyclesRemaining = GBARegisterDISPCNTIsHblankIntervalFree(renderer->dispcnt) ? OBJ_HBLANK_FREE_LENGTH : OBJ_LENGTH;
|
||||
int mosaicV = GBAMosaicControlGetObjV(renderer->mosaic) + 1;
|
||||
int mosaicY = y - (y % mosaicV);
|
||||
for (w = 0; w < renderer->nWindows; ++w) {
|
||||
renderer->start = renderer->end;
|
||||
renderer->end = renderer->windows[w].endX;
|
||||
renderer->currentWindow = renderer->windows[w].control;
|
||||
if (!GBAWindowControlIsObjEnable(renderer->currentWindow.packed) && !GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt)) {
|
||||
continue;
|
||||
}
|
||||
int i;
|
||||
int drawn;
|
||||
|
||||
for (i = 0; i < renderer->oamMax; ++i) {
|
||||
int localY = y;
|
||||
if (renderer->spriteCyclesRemaining <= 0) {
|
||||
break;
|
||||
}
|
||||
struct GBAVideoSoftwareSprite* sprite = &renderer->sprites[i];
|
||||
int localY = y;
|
||||
renderer->end = 0;
|
||||
if (GBAObjAttributesAIsMosaic(sprite->obj.a)) {
|
||||
localY = mosaicY;
|
||||
}
|
||||
if ((localY < sprite->y && (sprite->endY - 256 < 0 || localY >= sprite->endY - 256)) || localY >= sprite->endY) {
|
||||
continue;
|
||||
}
|
||||
drawn = GBAVideoSoftwareRendererPreprocessSprite(renderer, &sprite->obj, localY);
|
||||
for (w = 0; w < renderer->nWindows; ++w) {
|
||||
if (renderer->spriteCyclesRemaining <= 0) {
|
||||
break;
|
||||
}
|
||||
renderer->currentWindow = renderer->windows[w].control;
|
||||
renderer->start = renderer->end;
|
||||
renderer->end = renderer->windows[w].endX;
|
||||
if (!GBAWindowControlIsObjEnable(renderer->currentWindow.packed) && !GBARegisterDISPCNTIsObjwinEnable(renderer->dispcnt)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int drawn = GBAVideoSoftwareRendererPreprocessSprite(renderer, &sprite->obj, localY);
|
||||
spriteLayers |= drawn << GBAObjAttributesCGetPriority(sprite->obj.c);
|
||||
}
|
||||
if (renderer->spriteCyclesRemaining <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return spriteLayers;
|
||||
|
|
|
@ -11,27 +11,28 @@
|
|||
#include <mgba/internal/lr35902/lr35902.h>
|
||||
#include <mgba/internal/lr35902/debugger/memory-debugger.h>
|
||||
|
||||
DEFINE_VECTOR(LR35902DebugBreakpointList, struct LR35902DebugBreakpoint);
|
||||
DEFINE_VECTOR(LR35902DebugWatchpointList, struct LR35902DebugWatchpoint);
|
||||
|
||||
static struct LR35902DebugBreakpoint* _lookupBreakpoint(struct LR35902DebugBreakpointList* breakpoints, uint16_t address) {
|
||||
static struct mBreakpoint* _lookupBreakpoint(struct mBreakpointList* breakpoints, struct LR35902Core* cpu) {
|
||||
size_t i;
|
||||
for (i = 0; i < LR35902DebugBreakpointListSize(breakpoints); ++i) {
|
||||
if (LR35902DebugBreakpointListGetPointer(breakpoints, i)->address == address) {
|
||||
return LR35902DebugBreakpointListGetPointer(breakpoints, i);
|
||||
for (i = 0; i < mBreakpointListSize(breakpoints); ++i) {
|
||||
struct mBreakpoint* breakpoint = mBreakpointListGetPointer(breakpoints, i);
|
||||
if (breakpoint->address != cpu->pc) {
|
||||
continue;
|
||||
}
|
||||
if (breakpoint->segment < 0 || breakpoint->segment == cpu->memory.currentSegment(cpu, breakpoint->address)) {
|
||||
return breakpoint;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void _destroyBreakpoint(struct LR35902DebugBreakpoint* breakpoint) {
|
||||
static void _destroyBreakpoint(struct mBreakpoint* breakpoint) {
|
||||
if (breakpoint->condition) {
|
||||
parseFree(breakpoint->condition);
|
||||
free(breakpoint->condition);
|
||||
}
|
||||
}
|
||||
|
||||
static void _destroyWatchpoint(struct LR35902DebugWatchpoint* watchpoint) {
|
||||
static void _destroyWatchpoint(struct mWatchpoint* watchpoint) {
|
||||
if (watchpoint->condition) {
|
||||
parseFree(watchpoint->condition);
|
||||
free(watchpoint->condition);
|
||||
|
@ -40,13 +41,10 @@ static void _destroyWatchpoint(struct LR35902DebugWatchpoint* watchpoint) {
|
|||
|
||||
static void LR35902DebuggerCheckBreakpoints(struct mDebuggerPlatform* d) {
|
||||
struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
|
||||
struct LR35902DebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->breakpoints, debugger->cpu->pc);
|
||||
struct mBreakpoint* breakpoint = _lookupBreakpoint(&debugger->breakpoints, debugger->cpu);
|
||||
if (!breakpoint) {
|
||||
return;
|
||||
}
|
||||
if (breakpoint->segment >= 0 && debugger->cpu->memory.currentSegment(debugger->cpu, breakpoint->address) != breakpoint->segment) {
|
||||
return;
|
||||
}
|
||||
if (breakpoint->condition) {
|
||||
int32_t value;
|
||||
int segment;
|
||||
|
@ -65,12 +63,11 @@ 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, int segment);
|
||||
static void LR35902DebuggerSetConditionalBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment, struct ParseTree* condition);
|
||||
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 LR35902DebuggerSetConditionalWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type, struct ParseTree* condition);
|
||||
static void LR35902DebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
static ssize_t LR35902DebuggerSetBreakpoint(struct mDebuggerPlatform*, const struct mBreakpoint*);
|
||||
static void LR35902DebuggerListBreakpoints(struct mDebuggerPlatform*, struct mBreakpointList*);
|
||||
static bool LR35902DebuggerClearBreakpoint(struct mDebuggerPlatform*, ssize_t id);
|
||||
static ssize_t LR35902DebuggerSetWatchpoint(struct mDebuggerPlatform*, const struct mWatchpoint*);
|
||||
static void LR35902DebuggerListWatchpoints(struct mDebuggerPlatform*, struct mWatchpointList*);
|
||||
static void LR35902DebuggerCheckBreakpoints(struct mDebuggerPlatform*);
|
||||
static bool LR35902DebuggerHasBreakpoints(struct mDebuggerPlatform*);
|
||||
static void LR35902DebuggerTrace(struct mDebuggerPlatform*, char* out, size_t* length);
|
||||
|
@ -83,11 +80,10 @@ struct mDebuggerPlatform* LR35902DebuggerPlatformCreate(void) {
|
|||
platform->init = LR35902DebuggerInit;
|
||||
platform->deinit = LR35902DebuggerDeinit;
|
||||
platform->setBreakpoint = LR35902DebuggerSetBreakpoint;
|
||||
platform->setConditionalBreakpoint = LR35902DebuggerSetConditionalBreakpoint;
|
||||
platform->listBreakpoints = LR35902DebuggerListBreakpoints;
|
||||
platform->clearBreakpoint = LR35902DebuggerClearBreakpoint;
|
||||
platform->setWatchpoint = LR35902DebuggerSetWatchpoint;
|
||||
platform->setConditionalWatchpoint = LR35902DebuggerSetConditionalWatchpoint;
|
||||
platform->clearWatchpoint = LR35902DebuggerClearWatchpoint;
|
||||
platform->listWatchpoints = LR35902DebuggerListWatchpoints;
|
||||
platform->checkBreakpoints = LR35902DebuggerCheckBreakpoints;
|
||||
platform->hasBreakpoints = LR35902DebuggerHasBreakpoints;
|
||||
platform->trace = LR35902DebuggerTrace;
|
||||
|
@ -100,22 +96,23 @@ void LR35902DebuggerInit(void* cpu, struct mDebuggerPlatform* platform) {
|
|||
struct LR35902Debugger* debugger = (struct LR35902Debugger*) platform;
|
||||
debugger->cpu = cpu;
|
||||
debugger->originalMemory = debugger->cpu->memory;
|
||||
LR35902DebugBreakpointListInit(&debugger->breakpoints, 0);
|
||||
LR35902DebugWatchpointListInit(&debugger->watchpoints, 0);
|
||||
mBreakpointListInit(&debugger->breakpoints, 0);
|
||||
mWatchpointListInit(&debugger->watchpoints, 0);
|
||||
debugger->nextId = 1;
|
||||
}
|
||||
|
||||
void LR35902DebuggerDeinit(struct mDebuggerPlatform* platform) {
|
||||
struct LR35902Debugger* debugger = (struct LR35902Debugger*) platform;
|
||||
size_t i;
|
||||
for (i = 0; i < LR35902DebugBreakpointListSize(&debugger->breakpoints); ++i) {
|
||||
_destroyBreakpoint(LR35902DebugBreakpointListGetPointer(&debugger->breakpoints, i));
|
||||
for (i = 0; i < mBreakpointListSize(&debugger->breakpoints); ++i) {
|
||||
_destroyBreakpoint(mBreakpointListGetPointer(&debugger->breakpoints, i));
|
||||
}
|
||||
LR35902DebugBreakpointListDeinit(&debugger->breakpoints);
|
||||
mBreakpointListDeinit(&debugger->breakpoints);
|
||||
|
||||
for (i = 0; i < LR35902DebugWatchpointListSize(&debugger->watchpoints); ++i) {
|
||||
_destroyWatchpoint(LR35902DebugWatchpointListGetPointer(&debugger->watchpoints, i));
|
||||
for (i = 0; i < mWatchpointListSize(&debugger->watchpoints); ++i) {
|
||||
_destroyWatchpoint(mWatchpointListGetPointer(&debugger->watchpoints, i));
|
||||
}
|
||||
LR35902DebugWatchpointListDeinit(&debugger->watchpoints);
|
||||
mWatchpointListDeinit(&debugger->watchpoints);
|
||||
}
|
||||
|
||||
static void LR35902DebuggerEnter(struct mDebuggerPlatform* platform, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
|
||||
|
@ -130,65 +127,72 @@ static void LR35902DebuggerEnter(struct mDebuggerPlatform* platform, enum mDebug
|
|||
}
|
||||
}
|
||||
|
||||
static void LR35902DebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
|
||||
LR35902DebuggerSetConditionalBreakpoint(d, address, segment, NULL);
|
||||
static ssize_t LR35902DebuggerSetBreakpoint(struct mDebuggerPlatform* d, const struct mBreakpoint* info) {
|
||||
struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
|
||||
struct mBreakpoint* breakpoint = mBreakpointListAppend(&debugger->breakpoints);
|
||||
*breakpoint = *info;
|
||||
breakpoint->id = debugger->nextId;
|
||||
++debugger->nextId;
|
||||
return breakpoint->id;
|
||||
|
||||
}
|
||||
|
||||
static void LR35902DebuggerSetConditionalBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, struct ParseTree* condition) {
|
||||
static bool LR35902DebuggerClearBreakpoint(struct mDebuggerPlatform* d, ssize_t id) {
|
||||
struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
|
||||
struct LR35902DebugBreakpoint* breakpoint = LR35902DebugBreakpointListAppend(&debugger->breakpoints);
|
||||
breakpoint->address = address;
|
||||
breakpoint->segment = segment;
|
||||
breakpoint->condition = condition;
|
||||
}
|
||||
|
||||
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) {
|
||||
struct LR35902DebugBreakpoint* breakpoint = LR35902DebugBreakpointListGetPointer(breakpoints, i);
|
||||
if (breakpoint->address == address && breakpoint->segment == segment) {
|
||||
_destroyBreakpoint(LR35902DebugBreakpointListGetPointer(breakpoints, i));
|
||||
LR35902DebugBreakpointListShift(breakpoints, i, 1);
|
||||
|
||||
struct mBreakpointList* breakpoints = &debugger->breakpoints;
|
||||
for (i = 0; i < mBreakpointListSize(breakpoints); ++i) {
|
||||
struct mBreakpoint* breakpoint = mBreakpointListGetPointer(breakpoints, i);
|
||||
if (breakpoint->id == id) {
|
||||
_destroyBreakpoint(breakpoint);
|
||||
mBreakpointListShift(breakpoints, i, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
struct mWatchpointList* watchpoints = &debugger->watchpoints;
|
||||
for (i = 0; i < mWatchpointListSize(watchpoints); ++i) {
|
||||
struct mWatchpoint* watchpoint = mWatchpointListGetPointer(watchpoints, i);
|
||||
if (watchpoint->id == id) {
|
||||
_destroyWatchpoint(watchpoint);
|
||||
mWatchpointListShift(watchpoints, i, 1);
|
||||
if (!mWatchpointListSize(&debugger->watchpoints)) {
|
||||
LR35902DebuggerRemoveMemoryShim(debugger);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool LR35902DebuggerHasBreakpoints(struct mDebuggerPlatform* d) {
|
||||
struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
|
||||
return LR35902DebugBreakpointListSize(&debugger->breakpoints) || LR35902DebugWatchpointListSize(&debugger->watchpoints);
|
||||
return mBreakpointListSize(&debugger->breakpoints) || mWatchpointListSize(&debugger->watchpoints);
|
||||
}
|
||||
|
||||
static void LR35902DebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type) {
|
||||
LR35902DebuggerSetConditionalWatchpoint(d, address, segment, type, NULL);
|
||||
}
|
||||
|
||||
static void LR35902DebuggerSetConditionalWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type, struct ParseTree* condition) {
|
||||
static ssize_t LR35902DebuggerSetWatchpoint(struct mDebuggerPlatform* d, const struct mWatchpoint* info) {
|
||||
struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
|
||||
if (!LR35902DebugWatchpointListSize(&debugger->watchpoints)) {
|
||||
if (!mWatchpointListSize(&debugger->watchpoints)) {
|
||||
LR35902DebuggerInstallMemoryShim(debugger);
|
||||
}
|
||||
struct LR35902DebugWatchpoint* watchpoint = LR35902DebugWatchpointListAppend(&debugger->watchpoints);
|
||||
watchpoint->address = address;
|
||||
watchpoint->type = type;
|
||||
watchpoint->segment = segment;
|
||||
watchpoint->condition = condition;
|
||||
struct mWatchpoint* watchpoint = mWatchpointListAppend(&debugger->watchpoints);
|
||||
*watchpoint = *info;
|
||||
watchpoint->id = debugger->nextId;
|
||||
++debugger->nextId;
|
||||
return watchpoint->id;
|
||||
}
|
||||
|
||||
static void LR35902DebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) {
|
||||
static void LR35902DebuggerListBreakpoints(struct mDebuggerPlatform* d, struct mBreakpointList* list) {
|
||||
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);
|
||||
}
|
||||
mBreakpointListClear(list);
|
||||
mBreakpointListCopy(list, &debugger->breakpoints);
|
||||
}
|
||||
|
||||
static void LR35902DebuggerListWatchpoints(struct mDebuggerPlatform* d, struct mWatchpointList* list) {
|
||||
struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
|
||||
mWatchpointListClear(list);
|
||||
mWatchpointListCopy(list, &debugger->watchpoints);
|
||||
}
|
||||
|
||||
static void LR35902DebuggerTrace(struct mDebuggerPlatform* d, char* out, size_t* length) {
|
||||
|
|
|
@ -43,10 +43,10 @@ CREATE_WATCHPOINT_SHIM(load8, READ, 0, uint8_t, (struct LR35902Core* cpu, uint16
|
|||
CREATE_WATCHPOINT_SHIM(store8, WRITE, value, 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;
|
||||
struct mWatchpoint* watchpoint;
|
||||
size_t i;
|
||||
for (i = 0; i < LR35902DebugWatchpointListSize(&debugger->watchpoints); ++i) {
|
||||
watchpoint = LR35902DebugWatchpointListGetPointer(&debugger->watchpoints, i);
|
||||
for (i = 0; i < mWatchpointListSize(&debugger->watchpoints); ++i) {
|
||||
watchpoint = mWatchpointListGetPointer(&debugger->watchpoints, i);
|
||||
if (watchpoint->address == address && (watchpoint->segment < 0 || watchpoint->segment == debugger->originalMemory.currentSegment(debugger->cpu, address)) && watchpoint->type & type) {
|
||||
if (watchpoint->condition) {
|
||||
int32_t value;
|
||||
|
|
|
@ -50,7 +50,7 @@ public slots:
|
|||
virtual void requestSampleRate(unsigned) = 0;
|
||||
|
||||
protected:
|
||||
mCoreThread* input() { return m_context->thread(); }
|
||||
mCoreThread* input() { return m_context ? m_context->thread() : nullptr; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<CoreController> m_context;
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/* Copyright (c) 2013-2019 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 "BattleChipView.h"
|
||||
|
||||
#include "CoreController.h"
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
BattleChipView::BattleChipView(std::shared_ptr<CoreController> controller, QWidget* parent)
|
||||
: QDialog(parent)
|
||||
, m_controller(controller)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
connect(m_ui.chipId, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), m_ui.inserted, [this]() {
|
||||
m_ui.inserted->setChecked(Qt::Checked);
|
||||
insertChip(true);
|
||||
});
|
||||
connect(m_ui.inserted, &QAbstractButton::toggled, this, &BattleChipView::insertChip);
|
||||
connect(controller.get(), &CoreController::stopping, this, &QWidget::close);
|
||||
}
|
||||
|
||||
BattleChipView::~BattleChipView() {
|
||||
m_controller->detachBattleChipGate();
|
||||
}
|
||||
|
||||
void BattleChipView::insertChip(bool inserted) {
|
||||
if (inserted) {
|
||||
m_controller->setBattleChipId(m_ui.chipId->value());
|
||||
} else {
|
||||
m_controller->setBattleChipId(0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/* Copyright (c) 2013-2019 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/. */
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <mgba/core/interface.h>
|
||||
|
||||
#include "ui_BattleChipView.h"
|
||||
|
||||
namespace QGBA {
|
||||
|
||||
class CoreController;
|
||||
|
||||
class BattleChipView : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BattleChipView(std::shared_ptr<CoreController> controller, QWidget* parent = nullptr);
|
||||
~BattleChipView();
|
||||
|
||||
public slots:
|
||||
void insertChip(bool);
|
||||
|
||||
private:
|
||||
Ui::BattleChipView m_ui;
|
||||
|
||||
std::shared_ptr<CoreController> m_controller;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>BattleChipView</class>
|
||||
<widget class="QDialog" name="BattleChipView">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>217</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>BattleChip Gate</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="inserted">
|
||||
<property name="text">
|
||||
<string>Inserted</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Chip ID</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="chipId">
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>65535</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -88,6 +88,7 @@ set(SOURCE_FILES
|
|||
KeyEditor.cpp
|
||||
LoadSaveState.cpp
|
||||
LogController.cpp
|
||||
LogConfigModel.cpp
|
||||
LogView.cpp
|
||||
MapView.cpp
|
||||
MemoryModel.cpp
|
||||
|
@ -99,9 +100,9 @@ set(SOURCE_FILES
|
|||
OverrideView.cpp
|
||||
PaletteView.cpp
|
||||
PlacementControl.cpp
|
||||
PrinterView.cpp
|
||||
RegisterView.cpp
|
||||
ROMInfo.cpp
|
||||
RotatedHeaderView.cpp
|
||||
SavestateButton.cpp
|
||||
SensorView.cpp
|
||||
SettingsView.cpp
|
||||
|
@ -124,6 +125,7 @@ set(UI_FILES
|
|||
AboutScreen.ui
|
||||
ArchiveInspector.ui
|
||||
AssetTile.ui
|
||||
BattleChipView.ui
|
||||
CheatsView.ui
|
||||
DebuggerConsole.ui
|
||||
GIFView.ui
|
||||
|
@ -147,10 +149,12 @@ set(UI_FILES
|
|||
VideoView.ui)
|
||||
|
||||
set(GBA_SRC
|
||||
BattleChipView.cpp
|
||||
GBAOverride.cpp)
|
||||
|
||||
set(GB_SRC
|
||||
GBOverride.cpp)
|
||||
GBOverride.cpp
|
||||
PrinterView.cpp)
|
||||
|
||||
set(QT_LIBRARIES)
|
||||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libqt5widgets5,libqt5opengl5")
|
||||
|
|
|
@ -82,7 +82,8 @@ public:
|
|||
|
||||
Configuration* input() { return mCoreConfigGetInput(&m_config); }
|
||||
|
||||
const mCoreConfig* config() { return &m_config; }
|
||||
const mCoreConfig* config() const { return &m_config; }
|
||||
mCoreConfig* config() { return &m_config; }
|
||||
|
||||
static const QString& configDir();
|
||||
|
||||
|
|
|
@ -188,7 +188,6 @@ CoreController::CoreController(mCore* core, QObject* parent)
|
|||
message = QString().vsprintf(format, args);
|
||||
QMetaObject::invokeMethod(controller, "logPosted", Q_ARG(int, level), Q_ARG(int, category), Q_ARG(const QString&, message));
|
||||
if (level == mLOG_FATAL) {
|
||||
mCoreThreadMarkCrashed(controller->thread());
|
||||
QMetaObject::invokeMethod(controller, "crashed", Q_ARG(const QString&, QString().vsprintf(format, args)));
|
||||
}
|
||||
};
|
||||
|
@ -661,8 +660,8 @@ void CoreController::exportSharkport(const QString& path) {
|
|||
#endif
|
||||
}
|
||||
|
||||
void CoreController::attachPrinter() {
|
||||
#ifdef M_CORE_GB
|
||||
void CoreController::attachPrinter() {
|
||||
if (platform() != PLATFORM_GB) {
|
||||
return;
|
||||
}
|
||||
|
@ -692,11 +691,9 @@ void CoreController::attachPrinter() {
|
|||
};
|
||||
Interrupter interrupter(this);
|
||||
GBSIOSetDriver(&gb->sio, &m_printer.d.d);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CoreController::detachPrinter() {
|
||||
#ifdef M_CORE_GB
|
||||
if (platform() != PLATFORM_GB) {
|
||||
return;
|
||||
}
|
||||
|
@ -704,18 +701,44 @@ void CoreController::detachPrinter() {
|
|||
GB* gb = static_cast<GB*>(m_threadContext.core->board);
|
||||
GBPrinterDonePrinting(&m_printer.d);
|
||||
GBSIOSetDriver(&gb->sio, nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CoreController::endPrint() {
|
||||
#ifdef M_CORE_GB
|
||||
if (platform() != PLATFORM_GB) {
|
||||
return;
|
||||
}
|
||||
Interrupter interrupter(this);
|
||||
GBPrinterDonePrinting(&m_printer.d);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef M_CORE_GBA
|
||||
void CoreController::attachBattleChipGate() {
|
||||
if (platform() != PLATFORM_GBA) {
|
||||
return;
|
||||
}
|
||||
Interrupter interrupter(this);
|
||||
clearMultiplayerController();
|
||||
GBASIOBattlechipGateCreate(&m_battlechip);
|
||||
m_threadContext.core->setPeripheral(m_threadContext.core, mPERIPH_GBA_BATTLECHIP_GATE, &m_battlechip);
|
||||
}
|
||||
|
||||
void CoreController::detachBattleChipGate() {
|
||||
if (platform() != PLATFORM_GBA) {
|
||||
return;
|
||||
}
|
||||
Interrupter interrupter(this);
|
||||
m_threadContext.core->setPeripheral(m_threadContext.core, mPERIPH_GBA_BATTLECHIP_GATE, nullptr);
|
||||
}
|
||||
|
||||
void CoreController::setBattleChipId(uint16_t id) {
|
||||
if (platform() != PLATFORM_GBA) {
|
||||
return;
|
||||
}
|
||||
Interrupter interrupter(this);
|
||||
m_battlechip.chipId = id;
|
||||
}
|
||||
#endif
|
||||
|
||||
void CoreController::setAVStream(mAVStream* stream) {
|
||||
Interrupter interrupter(this);
|
||||
|
|
|
@ -25,6 +25,10 @@
|
|||
#include <mgba/internal/gb/sio/printer.h>
|
||||
#endif
|
||||
|
||||
#ifdef M_CORE_GBA
|
||||
#include <mgba/gba/interface.h>
|
||||
#endif
|
||||
|
||||
struct mCore;
|
||||
|
||||
namespace QGBA {
|
||||
|
@ -126,9 +130,17 @@ public slots:
|
|||
void importSharkport(const QString& path);
|
||||
void exportSharkport(const QString& path);
|
||||
|
||||
#ifdef M_CORE_GB
|
||||
void attachPrinter();
|
||||
void detachPrinter();
|
||||
void endPrint();
|
||||
#endif
|
||||
|
||||
#ifdef M_CORE_GBA
|
||||
void attachBattleChipGate();
|
||||
void detachBattleChipGate();
|
||||
void setBattleChipId(uint16_t id);
|
||||
#endif
|
||||
|
||||
void setAVStream(mAVStream*);
|
||||
void clearAVStream();
|
||||
|
@ -218,6 +230,10 @@ private:
|
|||
CoreController* parent;
|
||||
} m_printer;
|
||||
#endif
|
||||
|
||||
#ifdef M_CORE_GBA
|
||||
GBASIOBattlechipGate m_battlechip;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -10,8 +10,9 @@
|
|||
#include "CoreManager.h"
|
||||
#include "ConfigController.h"
|
||||
#include "Display.h"
|
||||
#include "Window.h"
|
||||
#include "LogController.h"
|
||||
#include "VFileDevice.h"
|
||||
#include "Window.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QFileOpenEvent>
|
||||
|
@ -65,6 +66,8 @@ GBAApp::GBAApp(int& argc, char* argv[], ConfigController* config)
|
|||
AudioProcessor::setDriver(static_cast<AudioProcessor::Driver>(m_configController->getQtOption("audioDriver").toInt()));
|
||||
}
|
||||
|
||||
LogController::global()->load(m_configController);
|
||||
|
||||
connect(this, &GBAApp::aboutToQuit, this, &GBAApp::cleanup);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
/* Copyright (c) 2013-2019 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 "LogConfigModel.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
LogConfigModel::LogConfigModel(LogController* controller, QObject* parent)
|
||||
: QAbstractItemModel(parent)
|
||||
, m_controller(controller)
|
||||
{
|
||||
for (int i = 0; mLogCategoryId(i); ++i) {
|
||||
int levels = controller->levels(i);
|
||||
m_cache.append({ i, mLogCategoryName(i), mLogCategoryId(i), levels ? levels : -1 });
|
||||
}
|
||||
std::sort(m_cache.begin(), m_cache.end());
|
||||
m_levels = m_controller->levels();
|
||||
}
|
||||
|
||||
QVariant LogConfigModel::data(const QModelIndex& index, int role) const {
|
||||
if (role != Qt::CheckStateRole) {
|
||||
return QVariant();
|
||||
}
|
||||
int levels;
|
||||
if (index.row() == 0) {
|
||||
levels = m_levels;
|
||||
} else {
|
||||
levels = m_cache[index.row() - 1].levels;
|
||||
}
|
||||
if (index.column() == 0) {
|
||||
return levels < 0 ? Qt::Checked : Qt::Unchecked;
|
||||
} else if (levels < 0 && index.row() > 0) {
|
||||
return (m_levels >> (index.column() - 1)) & 1 ? Qt::PartiallyChecked : Qt::Unchecked;
|
||||
} else {
|
||||
return (levels >> (index.column() - 1)) & 1 ? Qt::Checked : Qt::Unchecked;
|
||||
}
|
||||
}
|
||||
|
||||
bool LogConfigModel::setData(const QModelIndex& index, const QVariant& value, int role) {
|
||||
if (role != Qt::CheckStateRole) {
|
||||
return false;
|
||||
}
|
||||
int levels;
|
||||
if (index.row() == 0) {
|
||||
levels = m_levels;
|
||||
} else {
|
||||
levels = m_cache[index.row() - 1].levels;
|
||||
}
|
||||
if (index.column() == 0) {
|
||||
levels = -1;
|
||||
} else {
|
||||
if (levels < 0) {
|
||||
levels = m_levels;
|
||||
}
|
||||
levels ^= 1 << (index.column() - 1);
|
||||
}
|
||||
if (index.row() == 0) {
|
||||
beginResetModel();
|
||||
m_levels = levels;
|
||||
endResetModel();
|
||||
} else {
|
||||
m_cache[index.row() - 1].levels = levels;
|
||||
emit dataChanged(createIndex(0, index.row(), nullptr), createIndex(8, index.row(), nullptr));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QVariant LogConfigModel::headerData(int section, Qt::Orientation orientation, int role) const {
|
||||
if (role != Qt::DisplayRole) {
|
||||
return QVariant();
|
||||
}
|
||||
if (orientation == Qt::Horizontal) {
|
||||
switch (section) {
|
||||
case 0:
|
||||
return tr("Default");
|
||||
case 1:
|
||||
return tr("Fatal");
|
||||
case 2:
|
||||
return tr("Error");
|
||||
case 3:
|
||||
return tr("Warning");
|
||||
case 4:
|
||||
return tr("Info");
|
||||
case 5:
|
||||
return tr("Debug");
|
||||
case 6:
|
||||
return tr("Stub");
|
||||
case 7:
|
||||
return tr("Game Error");
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
} else if (section) {
|
||||
return m_cache[section - 1].name;
|
||||
} else {
|
||||
return tr("Default");
|
||||
}
|
||||
}
|
||||
|
||||
QModelIndex LogConfigModel::index(int row, int column, const QModelIndex& parent) const {
|
||||
return createIndex(row, column, nullptr);
|
||||
}
|
||||
|
||||
QModelIndex LogConfigModel::parent(const QModelIndex& index) const {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
int LogConfigModel::columnCount(const QModelIndex& parent) const {
|
||||
return 8;
|
||||
}
|
||||
|
||||
int LogConfigModel::rowCount(const QModelIndex& parent) const {
|
||||
return m_cache.size() + 1;
|
||||
}
|
||||
|
||||
Qt::ItemFlags LogConfigModel::flags(const QModelIndex& index) const {
|
||||
if (!index.isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return Qt::ItemIsUserCheckable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
|
||||
}
|
||||
|
||||
void LogConfigModel::reset() {
|
||||
beginResetModel();
|
||||
for (auto& row : m_cache) {
|
||||
row.levels = m_controller->levels(row.index);
|
||||
if (!row.levels) {
|
||||
row.levels = -1;
|
||||
}
|
||||
}
|
||||
m_levels = m_controller->levels();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void LogConfigModel::save(ConfigController* config) {
|
||||
for (auto& row : m_cache) {
|
||||
if (row.levels < 0) {
|
||||
m_controller->clearLevels(row.index);
|
||||
} else {
|
||||
m_controller->setLevels(row.levels, row.index);
|
||||
}
|
||||
}
|
||||
m_controller->setLevels(m_levels);
|
||||
m_controller->save(config);
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/* Copyright (c) 2013-2019 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/. */
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
|
||||
#include "LogController.h"
|
||||
|
||||
namespace QGBA {
|
||||
|
||||
class ConfigController;
|
||||
|
||||
class LogConfigModel : public QAbstractItemModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
LogConfigModel(LogController*, QObject* parent = nullptr);
|
||||
|
||||
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||
virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
|
||||
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||
|
||||
virtual QModelIndex index(int row, int column, const QModelIndex& parent) const override;
|
||||
virtual QModelIndex parent(const QModelIndex& index) const override;
|
||||
|
||||
virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
virtual Qt::ItemFlags flags(const QModelIndex& index) const override;
|
||||
|
||||
LogController* logger() { return m_controller; }
|
||||
|
||||
public slots:
|
||||
void reset();
|
||||
void save(ConfigController*);
|
||||
|
||||
private:
|
||||
struct ConfigSetting {
|
||||
int index;
|
||||
QString name;
|
||||
const char* id;
|
||||
int levels;
|
||||
|
||||
bool operator<(const ConfigSetting& other) const {
|
||||
return name < other.name;
|
||||
}
|
||||
};
|
||||
|
||||
LogController* m_controller;
|
||||
|
||||
QList<ConfigSetting> m_cache;
|
||||
int m_levels;
|
||||
};
|
||||
|
||||
}
|
|
@ -5,6 +5,8 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "LogController.h"
|
||||
|
||||
#include "ConfigController.h"
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
LogController LogController::s_global(mLOG_ALL);
|
||||
|
@ -19,9 +21,9 @@ LogController::LogController(int levels, QObject* parent)
|
|||
|
||||
if (this != &s_global) {
|
||||
connect(&s_global, &LogController::logPosted, this, &LogController::postLog);
|
||||
connect(this, &LogController::levelsSet, &s_global, &LogController::setLevels);
|
||||
connect(this, &LogController::levelsEnabled, &s_global, &LogController::enableLevels);
|
||||
connect(this, &LogController::levelsDisabled, &s_global, &LogController::disableLevels);
|
||||
connect(this, static_cast<void (LogController::*)(int)>(&LogController::levelsSet), &s_global, static_cast<void (LogController::*)(int)>(&LogController::setLevels));
|
||||
connect(this, static_cast<void (LogController::*)(int)>(&LogController::levelsEnabled), &s_global, static_cast<void (LogController::*)(int)>(&LogController::enableLevels));
|
||||
connect(this, static_cast<void (LogController::*)(int)>(&LogController::levelsDisabled), &s_global, static_cast<void (LogController::*)(int)>(&LogController::disableLevels));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,14 +31,40 @@ LogController::~LogController() {
|
|||
mLogFilterDeinit(&m_filter);
|
||||
}
|
||||
|
||||
int LogController::levels(int category) const {
|
||||
return mLogFilterLevels(&m_filter, category);
|
||||
}
|
||||
|
||||
LogController::Stream LogController::operator()(int category, int level) {
|
||||
return Stream(this, category, level);
|
||||
}
|
||||
|
||||
void LogController::load(const ConfigController* config) {
|
||||
mLogFilterLoad(&m_filter, config->config());
|
||||
setLogFile(config->getOption("logFile"));
|
||||
logToStdout(config->getOption("logToStdout").toInt());
|
||||
logToFile(config->getOption("logToFile").toInt());
|
||||
}
|
||||
|
||||
void LogController::save(ConfigController* config) const {
|
||||
mLogFilterSave(&m_filter, config->config());
|
||||
}
|
||||
|
||||
void LogController::postLog(int level, int category, const QString& string) {
|
||||
if (!mLogFilterTest(&m_filter, category, static_cast<mLogLevel>(level))) {
|
||||
return;
|
||||
}
|
||||
if (m_logToStdout || m_logToFile) {
|
||||
QString line = tr("[%1] %2: %3").arg(LogController::toString(level)).arg(mLogCategoryName(category)).arg(string);
|
||||
|
||||
if (m_logToStdout) {
|
||||
QTextStream out(stdout);
|
||||
out << line << endl;
|
||||
}
|
||||
if (m_logToFile && m_logStream) {
|
||||
*m_logStream << line << endl;
|
||||
}
|
||||
}
|
||||
emit logPosted(level, category, string);
|
||||
}
|
||||
|
||||
|
@ -55,6 +83,46 @@ void LogController::disableLevels(int levels) {
|
|||
emit levelsDisabled(levels);
|
||||
}
|
||||
|
||||
void LogController::setLevels(int levels, int category) {
|
||||
auto id = mLogCategoryId(category);
|
||||
mLogFilterSet(&m_filter, id, levels);
|
||||
emit levelsSet(levels, category);
|
||||
}
|
||||
|
||||
void LogController::enableLevels(int levels, int category) {
|
||||
auto id = mLogCategoryId(category);
|
||||
int newLevels = mLogFilterLevels(&m_filter, category) | levels;
|
||||
mLogFilterSet(&m_filter, id, newLevels);
|
||||
emit levelsEnabled(levels, category);
|
||||
}
|
||||
|
||||
void LogController::disableLevels(int levels, int category) {
|
||||
auto id = mLogCategoryId(category);
|
||||
int newLevels = mLogFilterLevels(&m_filter, category) & ~levels;
|
||||
mLogFilterSet(&m_filter, id, newLevels);
|
||||
emit levelsDisabled(levels, category);
|
||||
}
|
||||
|
||||
void LogController::clearLevels(int category) {
|
||||
auto id = mLogCategoryId(category);
|
||||
mLogFilterReset (&m_filter, id);
|
||||
}
|
||||
|
||||
void LogController::logToFile(bool log) {
|
||||
m_logToFile = log;
|
||||
}
|
||||
|
||||
void LogController::logToStdout(bool log) {
|
||||
m_logToStdout = log;
|
||||
}
|
||||
|
||||
void LogController::setLogFile(const QString& file) {
|
||||
m_logStream.reset();
|
||||
m_logFile = std::make_unique<QFile>(file);
|
||||
m_logFile->open(QIODevice::Append | QIODevice::Text);
|
||||
m_logStream = std::make_unique<QTextStream>(m_logFile.get());
|
||||
}
|
||||
|
||||
LogController* LogController::global() {
|
||||
return &s_global;
|
||||
}
|
||||
|
|
|
@ -11,9 +11,13 @@
|
|||
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
#include <QTextStream>
|
||||
#include <memory>
|
||||
|
||||
namespace QGBA {
|
||||
|
||||
class ConfigController;
|
||||
|
||||
class LogController : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -38,31 +42,51 @@ public:
|
|||
~LogController();
|
||||
|
||||
int levels() const { return m_filter.defaultLevels; }
|
||||
int levels(int category) const;
|
||||
mLogFilter* filter() { return &m_filter; }
|
||||
|
||||
Stream operator()(int category, int level);
|
||||
|
||||
static LogController* global();
|
||||
static QString toString(int level);
|
||||
static int categoryId(const char*);
|
||||
|
||||
void load(const ConfigController*);
|
||||
void save(ConfigController*) const;
|
||||
|
||||
signals:
|
||||
void logPosted(int level, int category, const QString& log);
|
||||
void levelsSet(int levels);
|
||||
void levelsEnabled(int levels);
|
||||
void levelsDisabled(int levels);
|
||||
void levelsSet(int levels, int category);
|
||||
void levelsEnabled(int levels, int category);
|
||||
void levelsDisabled(int levels, int category);
|
||||
|
||||
public slots:
|
||||
void postLog(int level, int category, const QString& string);
|
||||
void setLevels(int levels);
|
||||
void enableLevels(int levels);
|
||||
void disableLevels(int levels);
|
||||
void setLevels(int levels, int category);
|
||||
void enableLevels(int levels, int category);
|
||||
void disableLevels(int levels, int category);
|
||||
void clearLevels(int category);
|
||||
|
||||
void logToFile(bool);
|
||||
void logToStdout(bool);
|
||||
void setLogFile(const QString&);
|
||||
|
||||
private:
|
||||
mLogFilter m_filter;
|
||||
bool m_logToFile;
|
||||
bool m_logToStdout;
|
||||
std::unique_ptr<QFile> m_logFile;
|
||||
std::unique_ptr<QTextStream> m_logStream;
|
||||
|
||||
static LogController s_global;
|
||||
};
|
||||
|
||||
#define LOG(C, L) (*LogController::global())(mLOG_ ## L, _mLOG_CAT_ ## C ())
|
||||
#define LOG(C, L) (*LogController::global())(mLOG_ ## L, _mLOG_CAT_ ## C)
|
||||
|
||||
}
|
||||
|
|
|
@ -43,19 +43,19 @@ LogView::LogView(LogController* log, QWidget* parent)
|
|||
m_ui.maxLines->setValue(DEFAULT_LINE_LIMIT);
|
||||
|
||||
connect(log, &LogController::logPosted, this, &LogView::postLog);
|
||||
connect(log, &LogController::levelsSet, this, &LogView::setLevels);
|
||||
connect(log, &LogController::levelsEnabled, [this](int level) {
|
||||
connect(log, static_cast<void (LogController::*)(int)>(&LogController::levelsSet), this, &LogView::setLevels);
|
||||
connect(log, static_cast<void (LogController::*)(int)>(&LogController::levelsEnabled), [this](int level) {
|
||||
bool s = blockSignals(true);
|
||||
setLevel(level, true);
|
||||
blockSignals(s);
|
||||
});
|
||||
connect(log, &LogController::levelsDisabled, [this](int level) {
|
||||
connect(log, static_cast<void (LogController::*)(int)>(&LogController::levelsDisabled), [this](int level) {
|
||||
bool s = blockSignals(true);
|
||||
setLevel(level, false);
|
||||
blockSignals(s);
|
||||
});
|
||||
connect(this, &LogView::levelsEnabled, log, &LogController::enableLevels);
|
||||
connect(this, &LogView::levelsDisabled, log, &LogController::disableLevels);
|
||||
connect(this, &LogView::levelsEnabled, log, static_cast<void (LogController::*)(int)>(&LogController::enableLevels));
|
||||
connect(this, &LogView::levelsDisabled, log, static_cast<void (LogController::*)(int)>(&LogController::disableLevels));
|
||||
}
|
||||
|
||||
void LogView::postLog(int level, int category, const QString& log) {
|
||||
|
|
|
@ -46,6 +46,9 @@ ObjView::ObjView(std::shared_ptr<CoreController> controller, QWidget* parent)
|
|||
m_ui.mode->setFont(font);
|
||||
|
||||
connect(m_ui.tiles, &TilePainter::indexPressed, this, &ObjView::translateIndex);
|
||||
connect(m_ui.tiles, &TilePainter::needsRedraw, this, [this]() {
|
||||
updateTiles(true);
|
||||
});
|
||||
connect(m_ui.objId, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &ObjView::selectObj);
|
||||
connect(m_ui.magnification, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [this]() {
|
||||
updateTiles(true);
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/* Copyright (c) 2013-2019 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 "RotatedHeaderView.h"
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
RotatedHeaderView::RotatedHeaderView(Qt::Orientation orientation, QWidget* parent)
|
||||
: QHeaderView(orientation, parent)
|
||||
{
|
||||
}
|
||||
|
||||
void RotatedHeaderView::paintSection(QPainter* painter, const QRect& rect, int logicalIndex) const {
|
||||
painter->save();
|
||||
painter->translate(rect.x() + rect.width(), rect.y());
|
||||
painter->rotate(90);
|
||||
QHeaderView::paintSection(painter, QRect(0, 0, rect.height(), rect.width()), logicalIndex);
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
QSize RotatedHeaderView::sectionSizeFromContents(int logicalIndex) const {
|
||||
return QHeaderView::sectionSizeFromContents(logicalIndex).transposed();
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/* Copyright (c) 2013-2019 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/. */
|
||||
#pragma once
|
||||
|
||||
#include <QHeaderView>
|
||||
|
||||
namespace QGBA {
|
||||
|
||||
class RotatedHeaderView : public QHeaderView {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RotatedHeaderView(Qt::Orientation orientation, QWidget* parent = nullptr);
|
||||
|
||||
protected:
|
||||
void paintSection(QPainter* painter, const QRect& rect, int logicalIndex) const;
|
||||
virtual QSize sectionSizeFromContents(int logicalIndex) const override;
|
||||
};
|
||||
|
||||
}
|
|
@ -10,6 +10,7 @@
|
|||
#include "Display.h"
|
||||
#include "GBAApp.h"
|
||||
#include "InputController.h"
|
||||
#include "RotatedHeaderView.h"
|
||||
#include "ShaderSelector.h"
|
||||
#include "ShortcutView.h"
|
||||
|
||||
|
@ -23,10 +24,11 @@ using namespace QGBA;
|
|||
QList<enum GBModel> SettingsView::s_gbModelList;
|
||||
#endif
|
||||
|
||||
SettingsView::SettingsView(ConfigController* controller, InputController* inputController, QWidget* parent)
|
||||
SettingsView::SettingsView(ConfigController* controller, InputController* inputController, LogController* logController, QWidget* parent)
|
||||
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
|
||||
, m_controller(controller)
|
||||
, m_input(inputController)
|
||||
, m_logModel(logController)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
|
@ -290,6 +292,18 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
|
|||
}
|
||||
}
|
||||
|
||||
m_ui.loggingView->setModel(&m_logModel);
|
||||
m_ui.loggingView->setHorizontalHeader(new RotatedHeaderView(Qt::Horizontal));
|
||||
m_ui.loggingView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
m_ui.loggingView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
|
||||
connect(m_ui.logFileBrowse, &QAbstractButton::pressed, [this] () {
|
||||
QString path = GBAApp::app()->getSaveFileName(this, "Select log file");
|
||||
if (!path.isNull()) {
|
||||
m_ui.logFile->setText(path);
|
||||
}
|
||||
});
|
||||
|
||||
m_keyView = new ShortcutView();
|
||||
m_keyView->setModel(inputController->keyIndex());
|
||||
m_keyView->setInputController(inputController);
|
||||
|
@ -377,6 +391,9 @@ void SettingsView::updateConfig() {
|
|||
saveSetting("cheatAutosave", m_ui.cheatAutosave);
|
||||
saveSetting("autoload", m_ui.autoload);
|
||||
saveSetting("autosave", m_ui.autosave);
|
||||
saveSetting("logToFile", m_ui.logToFile);
|
||||
saveSetting("logToStdout", m_ui.logToStdout);
|
||||
saveSetting("logFile", m_ui.logFile);
|
||||
|
||||
if (m_ui.fastForwardUnbounded->isChecked()) {
|
||||
saveSetting("fastForwardRatio", "-1");
|
||||
|
@ -435,6 +452,11 @@ void SettingsView::updateConfig() {
|
|||
emit languageChanged();
|
||||
}
|
||||
|
||||
m_logModel.save(m_controller);
|
||||
m_logModel.logger()->setLogFile(m_ui.logFile->text());
|
||||
m_logModel.logger()->logToFile(m_ui.logToFile->isChecked());
|
||||
m_logModel.logger()->logToStdout(m_ui.logToStdout->isChecked());
|
||||
|
||||
#ifdef M_CORE_GB
|
||||
GBModel modelGB = s_gbModelList[m_ui.gbModel->currentIndex()];
|
||||
m_controller->setOption("gb.model", GBModelToName(modelGB));
|
||||
|
@ -508,6 +530,9 @@ void SettingsView::reloadConfig() {
|
|||
loadSetting("cheatAutosave", m_ui.cheatAutosave, true);
|
||||
loadSetting("autoload", m_ui.autoload, true);
|
||||
loadSetting("autosave", m_ui.autosave, false);
|
||||
loadSetting("logToFile", m_ui.logToFile);
|
||||
loadSetting("logToStdout", m_ui.logToStdout);
|
||||
loadSetting("logFile", m_ui.logFile);
|
||||
|
||||
m_ui.libraryStyle->setCurrentIndex(loadSetting("libraryStyle").toInt());
|
||||
|
||||
|
@ -547,6 +572,8 @@ void SettingsView::reloadConfig() {
|
|||
m_ui.saveStateSave->setChecked(saveState & SAVESTATE_SAVEDATA);
|
||||
m_ui.saveStateCheats->setChecked(saveState & SAVESTATE_CHEATS);
|
||||
|
||||
m_logModel.reset();
|
||||
|
||||
#ifdef M_CORE_GB
|
||||
QString modelGB = m_controller->getOption("gb.model");
|
||||
if (!modelGB.isNull()) {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <QDialog>
|
||||
|
||||
#include "ColorPicker.h"
|
||||
#include "LogConfigModel.h"
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
|
||||
|
@ -29,7 +30,7 @@ class SettingsView : public QDialog {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SettingsView(ConfigController* controller, InputController* inputController, QWidget* parent = nullptr);
|
||||
SettingsView(ConfigController* controller, InputController* inputController, LogController* logController, QWidget* parent = nullptr);
|
||||
~SettingsView();
|
||||
|
||||
void setShaderSelector(ShaderSelector* shaderSelector);
|
||||
|
@ -56,6 +57,7 @@ private:
|
|||
ShortcutView* m_shortcutView;
|
||||
ShortcutView* m_keyView;
|
||||
ShaderSelector* m_shader = nullptr;
|
||||
LogConfigModel m_logModel;
|
||||
|
||||
#ifdef M_CORE_GB
|
||||
uint32_t m_gbColors[12]{};
|
||||
|
|
|
@ -72,6 +72,11 @@
|
|||
<string>Paths</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Logging</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Game Boy</string>
|
||||
|
@ -82,7 +87,7 @@
|
|||
<item row="1" column="1">
|
||||
<widget class="QStackedWidget" name="stackedWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
<number>5</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="av">
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
|
@ -1285,6 +1290,52 @@
|
|||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="logging">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTableView" name="loggingView">
|
||||
<attribute name="horizontalHeaderDefaultSectionSize">
|
||||
<number>0</number>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderMinimumSectionSize">
|
||||
<number>0</number>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_19">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="logToFile">
|
||||
<property name="text">
|
||||
<string>Log to file</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="logToStdout">
|
||||
<property name="text">
|
||||
<string>Log to console</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_20">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="logFile"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="logFileBrowse">
|
||||
<property name="text">
|
||||
<string>Select Log File</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="gb">
|
||||
<layout class="QFormLayout" name="formLayout_1">
|
||||
<item row="0" column="0">
|
||||
|
|
|
@ -31,6 +31,7 @@ void TilePainter::resizeEvent(QResizeEvent* event) {
|
|||
if (width() / m_size != m_backing.width() / m_size || m_backing.height() != calculatedHeight) {
|
||||
m_backing = QPixmap(width(), calculatedHeight);
|
||||
m_backing.fill(Qt::transparent);
|
||||
emit needsRedraw();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ public slots:
|
|||
|
||||
signals:
|
||||
void indexPressed(int index);
|
||||
void needsRedraw();
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent*) override;
|
||||
|
|
|
@ -25,6 +25,9 @@ TileView::TileView(std::shared_ptr<CoreController> controller, QWidget* parent)
|
|||
m_ui.tile->setController(controller);
|
||||
|
||||
connect(m_ui.tiles, &TilePainter::indexPressed, m_ui.tile, &AssetTile::selectIndex);
|
||||
connect(m_ui.tiles, &TilePainter::needsRedraw, this, [this]() {
|
||||
updateTiles(true);
|
||||
});
|
||||
connect(m_ui.paletteId, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &TileView::updatePalette);
|
||||
|
||||
switch (m_controller->platform()) {
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include "AboutScreen.h"
|
||||
#include "AudioProcessor.h"
|
||||
#include "BattleChipView.h"
|
||||
#include "CheatsView.h"
|
||||
#include "ConfigController.h"
|
||||
#include "CoreController.h"
|
||||
|
@ -162,6 +163,7 @@ Window::Window(CoreManager* manager, ConfigController* config, int playerId, QWi
|
|||
connect(&m_focusCheck, &QTimer::timeout, this, &Window::focusCheck);
|
||||
|
||||
m_log.setLevels(mLOG_WARN | mLOG_ERROR | mLOG_FATAL);
|
||||
m_log.load(m_config);
|
||||
m_fpsTimer.setInterval(FPS_TIMER_INTERVAL);
|
||||
m_focusCheck.setInterval(200);
|
||||
|
||||
|
@ -478,8 +480,8 @@ void Window::exportSharkport() {
|
|||
}
|
||||
|
||||
void Window::openSettingsWindow() {
|
||||
SettingsView* settingsWindow = new SettingsView(m_config, &m_inputController);
|
||||
#if defined(BUILD_GL) || defined(BUILD_GLES)
|
||||
SettingsView* settingsWindow = new SettingsView(m_config, &m_inputController, &m_log);
|
||||
#if defined(BUILD_GL) || defined(BUILD_GLES2)
|
||||
if (m_display->supportsShaders()) {
|
||||
settingsWindow->setShaderSelector(m_shaderView.get());
|
||||
}
|
||||
|
@ -837,11 +839,13 @@ void Window::gameStopped() {
|
|||
m_screenWidget->setLockIntegerScaling(false);
|
||||
m_screenWidget->setPixmap(m_logo);
|
||||
m_screenWidget->unsetCursor();
|
||||
if (m_display) {
|
||||
#ifdef M_CORE_GB
|
||||
m_display->setMinimumSize(GB_VIDEO_HORIZONTAL_PIXELS, GB_VIDEO_VERTICAL_PIXELS);
|
||||
#elif defined(M_CORE_GBA)
|
||||
m_display->setMinimumSize(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
|
||||
#endif
|
||||
}
|
||||
|
||||
setMouseTracking(false);
|
||||
m_videoLayers->clear();
|
||||
|
@ -859,7 +863,6 @@ void Window::gameCrashed(const QString& errorMessage) {
|
|||
QMessageBox::Ok, this, Qt::Sheet);
|
||||
crash->setAttribute(Qt::WA_DeleteOnClose);
|
||||
crash->show();
|
||||
m_controller->stop();
|
||||
}
|
||||
|
||||
void Window::gameFailed() {
|
||||
|
@ -1411,6 +1414,31 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
addControlledAction(solarMenu, setSolar, QString("luminanceLevel.%1").arg(QString::number(i)));
|
||||
}
|
||||
|
||||
#ifdef M_CORE_GB
|
||||
QAction* gbPrint = new QAction(tr("Game Boy Printer..."), emulationMenu);
|
||||
connect(gbPrint, &QAction::triggered, [this]() {
|
||||
PrinterView* view = new PrinterView(m_controller);
|
||||
openView(view);
|
||||
m_controller->attachPrinter();
|
||||
|
||||
});
|
||||
addControlledAction(emulationMenu, gbPrint, "gbPrint");
|
||||
m_gameActions.append(gbPrint);
|
||||
#endif
|
||||
|
||||
#ifdef M_CORE_GBA
|
||||
QAction* bcGate = new QAction(tr("BattleChip Gate..."), emulationMenu);
|
||||
connect(bcGate, &QAction::triggered, [this]() {
|
||||
BattleChipView* view = new BattleChipView(m_controller);
|
||||
openView(view);
|
||||
m_controller->attachBattleChipGate();
|
||||
|
||||
});
|
||||
addControlledAction(emulationMenu, bcGate, "bcGate");
|
||||
m_platformActions.append(qMakePair(bcGate, SUPPORT_GBA));
|
||||
m_gameActions.append(bcGate);
|
||||
#endif
|
||||
|
||||
QMenu* avMenu = menubar->addMenu(tr("Audio/&Video"));
|
||||
QMenu* frameMenu = avMenu->addMenu(tr("Frame size"));
|
||||
for (int i = 1; i <= 6; ++i) {
|
||||
|
@ -1554,18 +1582,6 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
addControlledAction(avMenu, stopVL, "stopVL");
|
||||
m_gameActions.append(stopVL);
|
||||
|
||||
#ifdef M_CORE_GB
|
||||
QAction* gbPrint = new QAction(tr("Game Boy Printer..."), avMenu);
|
||||
connect(gbPrint, &QAction::triggered, [this]() {
|
||||
PrinterView* view = new PrinterView(m_controller);
|
||||
openView(view);
|
||||
m_controller->attachPrinter();
|
||||
|
||||
});
|
||||
addControlledAction(avMenu, gbPrint, "gbPrint");
|
||||
m_gameActions.append(gbPrint);
|
||||
#endif
|
||||
|
||||
avMenu->addSeparator();
|
||||
m_videoLayers = avMenu->addMenu(tr("Video layers"));
|
||||
m_audioChannels = avMenu->addMenu(tr("Audio channels"));
|
||||
|
|
|
@ -149,6 +149,7 @@ _fail0:
|
|||
|
||||
static void deinitEgl() {
|
||||
if (s_display) {
|
||||
eglMakeCurrent(s_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
if (s_context) {
|
||||
eglDestroyContext(s_display, s_context);
|
||||
}
|
||||
|
@ -726,8 +727,14 @@ int main(int argc, char* argv[]) {
|
|||
mGUIRunloop(&runner);
|
||||
}
|
||||
|
||||
mGUIDeinit(&runner);
|
||||
|
||||
audoutStopAudioOut();
|
||||
GUIFontDestroy(font);
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
|
||||
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
glDeleteBuffers(1, &pbo);
|
||||
|
||||
glDeleteTextures(1, &tex);
|
||||
|
|
|
@ -81,7 +81,6 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
#elif defined(__SWITCH__)
|
||||
UNUSED(_mPerfShutdown);
|
||||
gfxInitDefault();
|
||||
consoleInit(NULL);
|
||||
#else
|
||||
signal(SIGINT, _mPerfShutdown);
|
||||
|
@ -123,6 +122,9 @@ int main(int argc, char** argv) {
|
|||
_outputBuffer = malloc(256 * 384 * 4);
|
||||
if (perfOpts.csv) {
|
||||
puts("game_code,frames,duration,renderer");
|
||||
#ifdef __SWITCH__
|
||||
consoleUpdate(NULL);
|
||||
#endif
|
||||
}
|
||||
if (perfOpts.server) {
|
||||
didFail = !_mPerfRunServer(args.fname, &args, &perfOpts);
|
||||
|
@ -141,7 +143,7 @@ int main(int argc, char** argv) {
|
|||
gfxExit();
|
||||
acExit();
|
||||
#elif defined(__SWITCH__)
|
||||
gfxExit();
|
||||
consoleExit(NULL);
|
||||
#endif
|
||||
|
||||
return didFail;
|
||||
|
@ -221,6 +223,9 @@ bool _mPerfRunCore(const char* fname, const struct mArguments* args, const struc
|
|||
} else {
|
||||
printf("%u frames in %" PRIu64 " microseconds: %g fps (%gx)\n", frames, duration, scaledFrames / duration, scaledFrames / (duration * 60.f));
|
||||
}
|
||||
#ifdef __SWITCH__
|
||||
consoleUpdate(NULL);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -245,6 +250,9 @@ static void _mPerfRunloop(struct mCore* core, int* frames, bool quiet) {
|
|||
if (timeDiff >= 1000) {
|
||||
printf("\033[2K\rCurrent FPS: %g (%gx)", lastFrames / (timeDiff / 1000.0f), lastFrames / (float) (60 * (timeDiff / 1000.0f)));
|
||||
fflush(stdout);
|
||||
#ifdef __SWITCH__
|
||||
consoleUpdate(NULL);
|
||||
#endif
|
||||
lastEcho = currentTime;
|
||||
lastFrames = 0;
|
||||
}
|
||||
|
|
|
@ -111,7 +111,9 @@ void TableInsert(struct Table* table, uint32_t key, void* value) {
|
|||
struct TableList* list;
|
||||
TABLE_LOOKUP_START(TABLE_COMPARATOR, list, key) {
|
||||
if (value != lookupResult->value) {
|
||||
if (table->deinitializer) {
|
||||
table->deinitializer(lookupResult->value);
|
||||
}
|
||||
lookupResult->value = value;
|
||||
}
|
||||
return;
|
||||
|
|
Loading…
Reference in New Issue