Merge branch 'master' (early part) into medusa
jump to
@@ -19,16 +19,25 @@ - DS Core: Add symbol loading
- 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 @@ - Partial support for MBC6, MMM01, TAMA and HuC-1 GB mappers
- GBA: ARMIPS/A22i-style and ELF symbol table support - Initial Switch port Bugfixes: + - ARM: Fix MSR when T bit is set + - Core: Fix audio sync breaking when interrupted + - Core: Fix ordering events when scheduling during events + - FFmpeg: Fix encoding audio/video queue issues + - 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 Audio: Fix channel 1, 2 and 4 reset timing + - 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 - - 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 + - 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 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 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 - - 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 Serialize: Fix loading states in Hblank - 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 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) - - 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 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 + - 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 - - GBA Hardware: Fix RTC handshake transition (fixes mgba.io/i/1134) - - GBA BIOS: Fix BitUnPack narrowing Misc: - - GBA Timer: Use global cycles for timers - - GBA: Extend oddly-sized ROMs to full address space (fixes mgba.io/i/722) + - 3DS: Remove deprecated CSND interface - 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 + - 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 + - FFmpeg: Support lossless VP9 encoding - GBA Cheats: Allow multiple ROM patches in the same slot - GB: Skip BIOS option now works - - Libretro: Add frameskip option + - 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 - - 3DS: Remove deprecated CSND interface + - 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 - - 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 - - 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 - - mGUI: Add fast forward toggle + - 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 @@ - Windows installer
- 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 - - Wii: Define _GNU_SOURCE (fixes mgba.io/i/1106) - - Wii: Expose stretch configuration in settings - - Wii: Stretch now sets pixel-accurate mode size cap + - Python: Minor API improvements - 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 + - Wii: Define _GNU_SOURCE (fixes mgba.io/i/1106) + - Wii: Expose stretch configuration in settings + - Wii: Stretch now sets pixel-accurate mode size cap 0.7 beta 1: (2018-09-24) - Initial beta for 0.7
@@ -194,15 +194,28 @@ #define FILL_BITS(SRC, START, END) ((SRC) | MAKE_MASK(START, END))
#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 mWatchpointType { WATCHPOINT_WRITE = 1, WATCHPOINT_READ = 2, - WATCHPOINT_RW = 3 + WATCHPOINT_RW = 3, + WATCHPOINT_WRITE_CHANGE = 4, }; enum mBreakpointType {@@ -69,6 +71,25 @@ } bp;
} 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 @@ void (*deinit)(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 @@
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 @@
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 @@ #include <mgba-util/vector.h>
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 ARMCore* cpu;
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,38 +13,22 @@
#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; uint16_t end; 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/arm/debugger/memory-debugger.h>
#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 @@ return 0;
} 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 @@ struct ARMDebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->breakpoints, debugger->cpu->gprs[ARM_PC] - instructionLength);
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 @@ platform->entered = ARMDebuggerEnter;
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 @@ // Clear the stack backwards in case any overlap
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 @@ _destroyBreakpoint(ARMDebugBreakpointListGetPointer(&debugger->breakpoints, i));
} 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 @@ struct ARMCore* cpu = debugger->cpu;
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 @@ debugger->d.p->entered(debugger->d.p, reason, info);
} } -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) { +static ssize_t ARMDebuggerSetBreakpoint(struct mDebuggerPlatform* d, const struct mBreakpoint* info) { 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); + struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->breakpoints); + 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 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 bool ARMDebuggerClearBreakpoint(struct mDebuggerPlatform* d, ssize_t id) { struct ARMDebugger* debugger = (struct ARMDebugger*) d; - struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->breakpoints); - breakpoint->condition = condition; - breakpoint->address = address; - breakpoint->isSw = false; -} + size_t i; -static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) { - UNUSED(segment); - struct ARMDebugger* debugger = (struct ARMDebugger*) d; struct ARMDebugBreakpointList* breakpoints = &debugger->breakpoints; - size_t i; 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 bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform* d) { +static void ARMDebuggerListBreakpoints(struct mDebuggerPlatform* d, struct mBreakpointList* list) { struct ARMDebugger* debugger = (struct ARMDebugger*) d; - return ARMDebugBreakpointListSize(&debugger->breakpoints) || ARMDebugWatchpointListSize(&debugger->watchpoints); + 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 void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type) { - ARMDebuggerSetConditionalWatchpoint(d, address, segment, type, NULL); +static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform* d) { + struct ARMDebugger* debugger = (struct ARMDebugger*) d; + return ARMDebugBreakpointListSize(&debugger->breakpoints) || mWatchpointListSize(&debugger->watchpoints); } -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), address)
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 @@ ++key;
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 @@ mLogFilterSet(filter, key, ivalue);
} 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); } +} +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(struct mLogFilter* filter, int category, enum mLogLevel level) { - int value = (intptr_t) TableLookup(&filter->levels, category); + +bool mLogFilterTest(const struct mLogFilter* filter, int category, enum mLogLevel level) { + int value = mLogFilterLevels(filter, category); if (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); - if (value) { - TableInsert(&filter->levels, category, (void*)(intptr_t) value); - return value & level; - } } - return level & filter->defaultLevels; + return value; } mLOG_DEFINE_CATEGORY(STATUS, "Status", "core.status")
@@ -305,31 +305,36 @@ size_t stateSize = core->stateSize(core);
if (flags & SAVESTATE_METADATA) { uint64_t* creationUsec = malloc(sizeof(*creationUsec)); + if (creationUsec) { #ifndef _MSC_VER - struct timeval tv; - if (!gettimeofday(&tv, 0)) { - uint64_t usec = tv.tv_usec; - usec += tv.tv_sec * 1000000LL; - STORE_64LE(usec, 0, creationUsec); - } + struct timeval tv; + if (!gettimeofday(&tv, 0)) { + uint64_t usec = tv.tv_usec; + usec += tv.tv_sec * 1000000LL; + STORE_64LE(usec, 0, creationUsec); + } #else - struct timespec ts; - if (timespec_get(&ts, TIME_UTC)) { - uint64_t usec = ts.tv_nsec / 1000; - usec += ts.tv_sec * 1000000LL; - STORE_64LE(usec, 0, creationUsec); - } + struct timespec ts; + if (timespec_get(&ts, TIME_UTC)) { + uint64_t usec = ts.tv_nsec / 1000; + usec += ts.tv_sec * 1000000LL; + STORE_64LE(usec, 0, creationUsec); + } #endif - else { - creationUsec = 0; + else { + free(creationUsec); + creationUsec = 0; + } } - struct mStateExtdataItem item = { - .size = sizeof(*creationUsec), - .data = creationUsec, - .clean = free - }; - mStateExtdataPut(&extdata, EXTDATA_META_TIME, &item); + if (creationUsec) { + struct mStateExtdataItem item = { + .size = sizeof(*creationUsec), + .data = creationUsec, + .clean = free + }; + mStateExtdataPut(&extdata, EXTDATA_META_TIME, &item); + } } if (flags & SAVESTATE_SAVEDATA) {
@@ -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 @@ { "h", _printHelp, "S", "Print help" },
{ "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 @@ { "r/2", _readHalfword, "I", "Read a halfword from a specified offset" },
{ "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 @@ if (!dv || dv->type != CLIDV_INT_TYPE) {
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 @@ if (!debugger->d.platform->setWatchpoint) {
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) { + _setWatchpoint(debugger, dv, WATCHPOINT_READ); +} + +static void _setWriteWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { + _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; } - 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); + 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, "%s\n", ERROR_INVALID_ARGS); + debugger->backend->printf(debugger->backend, "%" PRIz "i: 0x%X\n", breakpoint->id, breakpoint->address); } - } else { - debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_READ); } + mBreakpointListDeinit(&breakpoints); } -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); +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, "%s\n", ERROR_INVALID_ARGS); + debugger->backend->printf(debugger->backend, "%" PRIz "i: 0x%X\n", watchpoint->id, watchpoint->address); } - } else { - debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_WRITE); - }} - -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); - } + 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 @@ stub->d.state = DEBUGGER_RUNNING;
} return; } + // Fall through + case WATCHPOINT_WRITE_CHANGE: type = "watch"; break; case WATCHPOINT_READ:@@ -488,21 +490,32 @@ uint32_t address = _readHex(readAddress, &i);
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 @@ }
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 @@ break;
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 @@ char oolBuf[0x101];
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 @@ gba->isPristine = false;
} 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 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 @@ outX += mosaicH - (outX % mosaicH); \
} \ } \ 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 tileData; \
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; \@@ -237,9 +231,6 @@ } else {
charBase *= renderer->tileStride; } if (!renderer->d.vramOBJ[charBase >> VRAM_BLOCK_OFFSET]) { - return 0; - } - if (renderer->spriteCyclesRemaining <= 0) { return 0; }
@@ -955,7 +955,6 @@ }
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,30 +963,33 @@ }
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)) { + int i; + for (i = 0; i < renderer->oamMax; ++i) { + 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; } - int i; - int drawn; - - for (i = 0; i < renderer->oamMax; ++i) { - int localY = y; + for (w = 0; w < renderer->nWindows; ++w) { if (renderer->spriteCyclesRemaining <= 0) { break; } - struct GBAVideoSoftwareSprite* sprite = &renderer->sprites[i]; - if (GBAObjAttributesAIsMosaic(sprite->obj.a)) { - localY = mosaicY; - } - if ((localY < sprite->y && (sprite->endY - 256 < 0 || localY >= sprite->endY - 256)) || localY >= sprite->endY) { + 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; } - drawn = GBAVideoSoftwareRendererPreprocessSprite(renderer, &sprite->obj, localY); + + int drawn = GBAVideoSoftwareRendererPreprocessSprite(renderer, &sprite->obj, localY); spriteLayers |= drawn << GBAObjAttributesCGetPriority(sprite->obj.c); + } + if (renderer->spriteCyclesRemaining <= 0) { + break; } } }
@@ -11,27 +11,28 @@ #include <mgba/internal/lr35902/decoder.h>
#include <mgba/internal/lr35902/lr35902.h> #include <mgba/internal/lr35902/debugger/memory-debugger.h> -DEFINE_VECTOR(LR35902DebugBreakpointList, struct LR35902DebugBreakpoint); -DEFINE_VECTOR(LR35902DebugWatchpointList, struct LR35902DebugWatchpoint); - -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,11 +41,8 @@ }
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) {@@ -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 @@ platform->entered = LR35902DebuggerEnter;
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 @@ debugger->d.p->entered(debugger->d.p, reason, info);
} } -static void LR35902DebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) { - LR35902DebuggerSetConditionalBreakpoint(d, address, segment, NULL); -} - -static void LR35902DebuggerSetConditionalBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, struct ParseTree* condition) { +static ssize_t LR35902DebuggerSetBreakpoint(struct mDebuggerPlatform* d, const struct mBreakpoint* info) { struct LR35902Debugger* debugger = (struct LR35902Debugger*) d; - struct LR35902DebugBreakpoint* breakpoint = LR35902DebugBreakpointListAppend(&debugger->breakpoints); - breakpoint->address = address; - breakpoint->segment = segment; - breakpoint->condition = condition; + struct mBreakpoint* breakpoint = mBreakpointListAppend(&debugger->breakpoints); + *breakpoint = *info; + breakpoint->id = debugger->nextId; + ++debugger->nextId; + return breakpoint->id; + } -static void LR35902DebuggerClearBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) { +static bool LR35902DebuggerClearBreakpoint(struct mDebuggerPlatform* d, ssize_t id) { 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 ssize_t LR35902DebuggerSetWatchpoint(struct mDebuggerPlatform* d, const struct mWatchpoint* info) { + struct LR35902Debugger* debugger = (struct LR35902Debugger*) d; + if (!mWatchpointListSize(&debugger->watchpoints)) { + LR35902DebuggerInstallMemoryShim(debugger); + } + struct mWatchpoint* watchpoint = mWatchpointListAppend(&debugger->watchpoints); + *watchpoint = *info; + watchpoint->id = debugger->nextId; + ++debugger->nextId; + return watchpoint->id; } -static void LR35902DebuggerSetConditionalWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type, struct ParseTree* condition) { +static void LR35902DebuggerListBreakpoints(struct mDebuggerPlatform* d, struct mBreakpointList* list) { struct LR35902Debugger* debugger = (struct LR35902Debugger*) d; - if (!LR35902DebugWatchpointListSize(&debugger->watchpoints)) { - LR35902DebuggerInstallMemoryShim(debugger); - } - struct LR35902DebugWatchpoint* watchpoint = LR35902DebugWatchpointListAppend(&debugger->watchpoints); - watchpoint->address = address; - watchpoint->type = type; - watchpoint->segment = segment; - watchpoint->condition = condition; + mBreakpointListClear(list); + mBreakpointListCopy(list, &debugger->breakpoints); } -static void LR35902DebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) { +static void LR35902DebuggerListWatchpoints(struct mDebuggerPlatform* d, struct mWatchpointList* 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); - } + 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_t address), address)
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 @@
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 @@ IOViewer.cpp
KeyEditor.cpp LoadSaveState.cpp LogController.cpp + LogConfigModel.cpp LogView.cpp MapView.cpp MemoryModel.cpp@@ -99,9 +100,9 @@ ObjView.cpp
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 @@ TileView.ui
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 @@ void saveOverride(const Override&);
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 @@ }
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 @@ vf->close(vf);
#endif } -void CoreController::attachPrinter() { #ifdef M_CORE_GB +void CoreController::attachPrinter() { if (platform() != PLATFORM_GB) { return; }@@ -692,11 +691,9 @@ QMetaObject::invokeMethod(qPrinter->parent, "imagePrinted", Q_ARG(const QImage&, image));
}; 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 @@ Interrupter interrupter(this);
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 + +#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 @@ #ifdef M_CORE_GB
#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 @@
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();@@ -217,6 +229,10 @@ struct QGBPrinter {
GBPrinter d; CoreController* parent; } m_printer; +#endif + +#ifdef M_CORE_GBA + GBASIOBattlechipGate m_battlechip; #endif };
@@ -10,8 +10,9 @@ #include "CoreController.h"
#include "CoreManager.h" #include "ConfigController.h" #include "Display.h" +#include "LogController.h" +#include "VFileDevice.h" #include "Window.h" -#include "VFileDevice.h" #include <QFileInfo> #include <QFileOpenEvent>@@ -64,6 +65,8 @@
if (!m_configController->getQtOption("audioDriver").isNull()) { 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 @@ * 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 "LogController.h" +#include "ConfigController.h" + using namespace QGBA; LogController LogController::s_global(mLOG_ALL);@@ -19,9 +21,9 @@ m_filter.defaultLevels = levels;
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); }@@ -53,6 +81,46 @@
void LogController::disableLevels(int levels) { m_filter.defaultLevels &= ~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() {
@@ -11,8 +11,12 @@ #include <mgba/core/log.h>
#include <QObject> #include <QStringList> +#include <QTextStream> +#include <memory> namespace QGBA { + +class ConfigController; class LogController : public QObject { Q_OBJECT@@ -38,31 +42,51 @@ LogController(int levels, QObject* parent = nullptr);
~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 @@ this, &LogView::setMaxLines);
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 @@ m_ui.transform->setFont(font);
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 "ConfigController.h"
#include "Display.h" #include "GBAApp.h" #include "InputController.h" +#include "RotatedHeaderView.h" #include "ShaderSelector.h" #include "ShortcutView.h"@@ -23,10 +24,11 @@ #ifdef M_CORE_GB
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 @@ m_ui.languages->setCurrentIndex(m_ui.languages->count() - 1);
} } + 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 @@ saveSetting("cheatAutoload", m_ui.cheatAutoload);
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 @@ m_controller->setQtOption("language", language.bcp47Name());
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 @@ loadSetting("cheatAutoload", m_ui.cheatAutoload, true);
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());@@ -546,6 +571,8 @@ }
m_ui.saveStateScreenshot->setChecked(saveState & SAVESTATE_SCREENSHOT); 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");
@@ -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 @@ InputController* m_input;
ShortcutView* m_shortcutView; ShortcutView* m_keyView; ShaderSelector* m_shader = nullptr; + LogConfigModel m_logModel; #ifdef M_CORE_GB uint32_t m_gbColors[12]{};
@@ -74,6 +74,11 @@ </property>
</item> <item> <property name="text"> + <string>Logging</string> + </property> + </item> + <item> + <property name="text"> <string>Game Boy</string> </property> </item>@@ -82,7 +87,7 @@ </item>
<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">@@ -1282,6 +1287,52 @@ <property name="text">
<string>Same directory as the ROM</string> </property> </widget> + </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>
@@ -31,6 +31,7 @@ calculatedHeight -= calculatedHeight % m_size;
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 @@ void setTileMagnification(int mag);
signals: void indexPressed(int index); + void needsRedraw(); protected: void paintEvent(QPaintEvent*) override;
@@ -25,6 +25,9 @@ m_ui.setupUi(this);
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 @@ #endif
#include "AboutScreen.h" #include "AudioProcessor.h" +#include "BattleChipView.h" #include "CheatsView.h" #include "ConfigController.h" #include "CoreController.h"@@ -162,6 +163,7 @@ connect(&m_fpsTimer, &QTimer::timeout, this, &Window::showFPS);
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::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 @@ m_screenWidget->setDimensions(m_logo.width(), m_logo.height());
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); + 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); + m_display->setMinimumSize(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS); #endif + } setMouseTracking(false); m_videoLayers->clear();@@ -859,7 +863,6 @@ tr("The game has crashed with the following error:\n\n%1").arg(errorMessage),
QMessageBox::Ok, this, Qt::Sheet); crash->setAttribute(Qt::WA_DeleteOnClose); crash->show(); - m_controller->stop(); } void Window::gameFailed() {@@ -1411,6 +1414,31 @@ });
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) {@@ -1553,18 +1581,6 @@ m_controller->endVideoLog();
}); 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"));
@@ -100,63 +100,64 @@ SM_MAX
} screenMode = SM_PA; static bool initEgl() { - s_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (!s_display) { - goto _fail0; - } + s_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (!s_display) { + goto _fail0; + } - eglInitialize(s_display, NULL, NULL); + eglInitialize(s_display, NULL, NULL); - EGLConfig config; - EGLint numConfigs; - static const EGLint attributeList[] = { - EGL_RED_SIZE, 1, - EGL_GREEN_SIZE, 1, - EGL_BLUE_SIZE, 1, - EGL_NONE - }; - eglChooseConfig(s_display, attributeList, &config, 1, &numConfigs); - if (!numConfigs) { - goto _fail1; - } + EGLConfig config; + EGLint numConfigs; + static const EGLint attributeList[] = { + EGL_RED_SIZE, 1, + EGL_GREEN_SIZE, 1, + EGL_BLUE_SIZE, 1, + EGL_NONE + }; + eglChooseConfig(s_display, attributeList, &config, 1, &numConfigs); + if (!numConfigs) { + goto _fail1; + } - s_surface = eglCreateWindowSurface(s_display, config, nwindowGetDefault(), NULL); - if (!s_surface) { - goto _fail1; - } + s_surface = eglCreateWindowSurface(s_display, config, nwindowGetDefault(), NULL); + if (!s_surface) { + goto _fail1; + } EGLint contextAttributeList[] = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE }; - s_context = eglCreateContext(s_display, config, EGL_NO_CONTEXT, contextAttributeList); - if (!s_context) { - goto _fail2; - } + s_context = eglCreateContext(s_display, config, EGL_NO_CONTEXT, contextAttributeList); + if (!s_context) { + goto _fail2; + } - eglMakeCurrent(s_display, s_surface, s_surface, s_context); - return true; + eglMakeCurrent(s_display, s_surface, s_surface, s_context); + return true; _fail2: - eglDestroySurface(s_display, s_surface); - s_surface = NULL; + eglDestroySurface(s_display, s_surface); + s_surface = NULL; _fail1: - eglTerminate(s_display); - s_display = NULL; + eglTerminate(s_display); + s_display = NULL; _fail0: - return false; + return false; } static void deinitEgl() { - if (s_display) { - if (s_context) { - eglDestroyContext(s_display, s_context); - } - if (s_surface) { - eglDestroySurface(s_display, s_surface); - } - eglTerminate(s_display); - } + if (s_display) { + eglMakeCurrent(s_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (s_context) { + eglDestroyContext(s_display, s_context); + } + if (s_surface) { + eglDestroySurface(s_display, s_surface); + } + eglTerminate(s_display); + } } static void _mapKey(struct mInputMap* map, uint32_t binding, int nativeKey, enum GBAKey key) {@@ -726,8 +727,14 @@ } else {
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 @@ return 1;
} #elif defined(__SWITCH__) UNUSED(_mPerfShutdown); - gfxInitDefault(); consoleInit(NULL); #else signal(SIGINT, _mPerfShutdown);@@ -123,6 +122,9 @@
_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 @@ #ifdef _3DS
gfxExit(); acExit(); #elif defined(__SWITCH__) - gfxExit(); + consoleExit(NULL); #endif return didFail;@@ -221,6 +223,9 @@ }
} 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 @@ timeDiff += (currentTime.tv_usec - lastEcho.tv_usec) / 1000;
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) { - table->deinitializer(lookupResult->value); + if (table->deinitializer) { + table->deinitializer(lookupResult->value); + } lookupResult->value = value; } return;