mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' into medusa
This commit is contained in:
commit
48cb8abc21
11
CHANGES
11
CHANGES
|
@ -30,8 +30,10 @@ Features:
|
|||
- Debugger: Segment/bank support
|
||||
- GB: Symbol table support
|
||||
- GB MBC: Add MBC1 multicart support
|
||||
- GBA: Implement keypad interrupts
|
||||
- Implement keypad interrupts
|
||||
- LR35902: Watchpoints
|
||||
- Memory search
|
||||
- Debugger: Execution tracing
|
||||
Bugfixes:
|
||||
- LR35902: Fix core never exiting with certain event patterns
|
||||
- GB Timer: Improve DIV reset behavior
|
||||
|
@ -73,6 +75,9 @@ Bugfixes:
|
|||
- LR35902: Fix decoding LD r, $imm and 0-valued immediates (fixes mgba.io/i/735)
|
||||
- GB: Fix STAT blocking
|
||||
- GB MBC: Fix swapping carts not detect new MBC
|
||||
- GB Timer: Fix DIV batching if TAC changes
|
||||
- GB Video: Reset renderer when loading state
|
||||
- GBA BIOS: Fix INT_MIN/-1 crash
|
||||
Misc:
|
||||
- SDL: Remove scancode key input
|
||||
- GBA Video: Clean up unused timers
|
||||
|
@ -133,6 +138,10 @@ Misc:
|
|||
- Util: Tune patch-fast extent sizes
|
||||
- Qt: Relax hard dependency on OpenGL
|
||||
- GB Video: Improved video timings
|
||||
- Core: List memory segments in the core
|
||||
- Core: Move savestate creation time to extdata
|
||||
- Debugger: Add mDebuggerRunFrame convenience function
|
||||
- GBA Memory: Remove unused prefetch cruft
|
||||
|
||||
medusa alpha 2: (2017-04-26)
|
||||
Features:
|
||||
|
|
|
@ -160,8 +160,6 @@ add_custom_target(version-info ALL
|
|||
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/version.cmake)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/core/version.c.in ${CMAKE_CURRENT_BINARY_DIR}/version.c)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/core/flags.h.in ${CMAKE_CURRENT_BINARY_DIR}/flags.h)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/flags.h DESTINATION include/mgba COMPONENT lib${BINARY_NAME})
|
||||
|
||||
list(APPEND UTIL_SRC ${CMAKE_CURRENT_BINARY_DIR}/version.c)
|
||||
source_group("Generated sources" FILES ${CMAKE_CURRENT_BINARY_DIR}/version.c)
|
||||
|
@ -205,6 +203,7 @@ if(WIN32)
|
|||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
|
||||
endif()
|
||||
elseif(UNIX)
|
||||
set(USE_PTHREADS ON)
|
||||
add_definitions(-DUSE_PTHREADS)
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
|
@ -875,6 +874,10 @@ if(BUILD_EXAMPLE)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
message(STATUS ${USE_PTHREADS})
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/core/flags.h.in ${CMAKE_CURRENT_BINARY_DIR}/flags.h)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/flags.h DESTINATION include/mgba COMPONENT lib${BINARY_NAME})
|
||||
|
||||
# Packaging
|
||||
set(CPACK_PACKAGE_VERSION ${VERSION_STRING})
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR ${LIB_VERSION_MAJOR})
|
||||
|
|
|
@ -30,7 +30,6 @@ size_t CircleBufferWrite(struct CircleBuffer* buffer, const void* input, size_t
|
|||
int CircleBufferRead8(struct CircleBuffer* buffer, int8_t* value);
|
||||
int CircleBufferRead16(struct CircleBuffer* buffer, int16_t* value);
|
||||
int CircleBufferRead32(struct CircleBuffer* buffer, int32_t* value);
|
||||
size_t CircleBufferWrite(struct CircleBuffer* buffer, const void* input, size_t length);
|
||||
size_t CircleBufferRead(struct CircleBuffer* buffer, void* output, size_t length);
|
||||
size_t CircleBufferDump(const struct CircleBuffer* buffer, void* output, size_t length);
|
||||
|
||||
|
|
|
@ -19,8 +19,7 @@ CXX_GUARD_START
|
|||
#endif
|
||||
#include <mgba/core/interface.h>
|
||||
#ifdef USE_DEBUGGERS
|
||||
// TODO: Fix layering violation
|
||||
#include <mgba/internal/debugger/debugger.h>
|
||||
#include <mgba/debugger/debugger.h>
|
||||
#endif
|
||||
|
||||
enum mPlatform {
|
||||
|
@ -138,6 +137,9 @@ struct mCore {
|
|||
void (*rawWrite16)(struct mCore*, uint32_t address, int segment, uint16_t);
|
||||
void (*rawWrite32)(struct mCore*, uint32_t address, int segment, uint32_t);
|
||||
|
||||
size_t (*listMemoryBlocks)(const struct mCore*, const struct mCoreMemoryBlock**);
|
||||
void* (*getMemoryBlock)(struct mCore*, size_t id, size_t* sizeOut);
|
||||
|
||||
#ifdef USE_DEBUGGERS
|
||||
bool (*supportsDebuggerType)(struct mCore*, enum mDebuggerType);
|
||||
struct mDebuggerPlatform* (*debuggerPlatform)(struct mCore*);
|
||||
|
|
|
@ -113,6 +113,27 @@ struct mCoreChannelInfo {
|
|||
const char* visibleType;
|
||||
};
|
||||
|
||||
enum mCoreMemoryBlockFlags {
|
||||
mCORE_MEMORY_READ = 0x01,
|
||||
mCORE_MEMORY_WRITE = 0x02,
|
||||
mCORE_MEMORY_RW = 0x03,
|
||||
mCORE_MEMORY_MAPPED = 0x10,
|
||||
mCORE_MEMORY_VIRTUAL = 0x20,
|
||||
};
|
||||
|
||||
struct mCoreMemoryBlock {
|
||||
size_t id;
|
||||
const char* internalName;
|
||||
const char* shortName;
|
||||
const char* longName;
|
||||
uint32_t start;
|
||||
uint32_t end;
|
||||
uint32_t size;
|
||||
uint32_t flags;
|
||||
uint16_t maxSegment;
|
||||
uint32_t segmentStart;
|
||||
};
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef CORE_MEM_SEARCH_H
|
||||
#define CORE_MEM_SEARCH_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba-util/vector.h>
|
||||
|
||||
enum mCoreMemorySearchType {
|
||||
mCORE_MEMORY_SEARCH_32,
|
||||
mCORE_MEMORY_SEARCH_16,
|
||||
mCORE_MEMORY_SEARCH_8,
|
||||
mCORE_MEMORY_SEARCH_STRING,
|
||||
mCORE_MEMORY_SEARCH_GUESS,
|
||||
};
|
||||
|
||||
struct mCoreMemorySearchParams {
|
||||
int memoryFlags;
|
||||
enum mCoreMemorySearchType type;
|
||||
union {
|
||||
const char* valueStr;
|
||||
uint32_t value32;
|
||||
uint32_t value16;
|
||||
uint32_t value8;
|
||||
};
|
||||
};
|
||||
|
||||
struct mCoreMemorySearchResult {
|
||||
uint32_t address;
|
||||
int segment;
|
||||
uint64_t guessDivisor;
|
||||
enum mCoreMemorySearchType type;
|
||||
};
|
||||
|
||||
DECLARE_VECTOR(mCoreMemorySearchResults, struct mCoreMemorySearchResult);
|
||||
|
||||
struct mCore;
|
||||
void mCoreMemorySearch(struct mCore* core, const struct mCoreMemorySearchParams* params, struct mCoreMemorySearchResults* out, size_t limit);
|
||||
void mCoreMemorySearchRepeat(struct mCore* core, const struct mCoreMemorySearchParams* params, struct mCoreMemorySearchResults* inout);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -16,6 +16,7 @@ enum mStateExtdataTag {
|
|||
EXTDATA_SAVEDATA = 2,
|
||||
EXTDATA_CHEATS = 3,
|
||||
EXTDATA_RTC = 4,
|
||||
EXTDATA_META_TIME = 0x101,
|
||||
EXTDATA_MAX
|
||||
};
|
||||
|
||||
|
@ -23,6 +24,7 @@ enum mStateExtdataTag {
|
|||
#define SAVESTATE_SAVEDATA 2
|
||||
#define SAVESTATE_CHEATS 4
|
||||
#define SAVESTATE_RTC 8
|
||||
#define SAVESTATE_METADATA 16
|
||||
|
||||
struct mStateExtdataItem {
|
||||
int32_t size;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2013-2014 Jeffrey Pfau
|
||||
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
@ -12,7 +12,6 @@ CXX_GUARD_START
|
|||
|
||||
#include <mgba/core/cpu.h>
|
||||
#include <mgba/core/log.h>
|
||||
#include <mgba-util/vector.h>
|
||||
|
||||
mLOG_DECLARE_CATEGORY(DEBUGGER);
|
||||
|
||||
|
@ -53,14 +52,6 @@ enum mDebuggerEntryReason {
|
|||
DEBUGGER_ENTER_ILLEGAL_OP
|
||||
};
|
||||
|
||||
struct mDebugWatchpoint {
|
||||
uint32_t address;
|
||||
enum mWatchpointType type;
|
||||
};
|
||||
|
||||
extern const char* ERROR_MISSING_ARGS;
|
||||
extern const char* ERROR_OVERFLOW;
|
||||
|
||||
struct mDebuggerEntryInfo {
|
||||
uint32_t address;
|
||||
union {
|
||||
|
@ -92,6 +83,7 @@ struct mDebuggerPlatform {
|
|||
void (*setWatchpoint)(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type);
|
||||
void (*clearWatchpoint)(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
void (*checkBreakpoints)(struct mDebuggerPlatform*);
|
||||
void (*trace)(struct mDebuggerPlatform*, char* out, size_t* length);
|
||||
};
|
||||
|
||||
struct mDebuggerSymbols;
|
||||
|
@ -112,6 +104,7 @@ struct mDebugger {
|
|||
struct mDebugger* mDebuggerCreate(enum mDebuggerType type, struct mCore*);
|
||||
void mDebuggerAttach(struct mDebugger*, struct mCore*);
|
||||
void mDebuggerRun(struct mDebugger*);
|
||||
void mDebuggerRunFrame(struct mDebugger*);
|
||||
void mDebuggerEnter(struct mDebugger*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*);
|
||||
|
||||
CXX_GUARD_END
|
|
@ -12,7 +12,7 @@ CXX_GUARD_START
|
|||
|
||||
#include <mgba-util/table.h>
|
||||
|
||||
#include <mgba/internal/debugger/debugger.h>
|
||||
#include <mgba/debugger/debugger.h>
|
||||
|
||||
struct mArguments {
|
||||
char* fname;
|
||||
|
|
|
@ -30,6 +30,7 @@ enum GBMemoryBankControllerType {
|
|||
GB_MMM01 = 0x10,
|
||||
GB_HuC1 = 0x11,
|
||||
GB_HuC3 = 0x12,
|
||||
GB_POCKETCAM = 0x13,
|
||||
GB_MBC3_RTC = 0x103,
|
||||
GB_MBC5_RUMBLE = 0x105
|
||||
};
|
||||
|
|
|
@ -10,9 +10,10 @@
|
|||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba/internal/debugger/debugger.h>
|
||||
#include <mgba/debugger/debugger.h>
|
||||
|
||||
#include <mgba/internal/arm/arm.h>
|
||||
#include <mgba-util/vector.h>
|
||||
|
||||
struct ARMDebugBreakpoint {
|
||||
uint32_t address;
|
||||
|
|
|
@ -10,7 +10,10 @@
|
|||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba/internal/debugger/debugger.h>
|
||||
#include <mgba/debugger/debugger.h>
|
||||
|
||||
extern const char* ERROR_MISSING_ARGS;
|
||||
extern const char* ERROR_OVERFLOW;
|
||||
|
||||
struct CLIDebugger;
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba/internal/debugger/debugger.h>
|
||||
#include <mgba/debugger/debugger.h>
|
||||
|
||||
#include <mgba-util/socket.h>
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba/internal/debugger/debugger.h>
|
||||
#include <mgba/debugger/debugger.h>
|
||||
|
||||
enum LexState {
|
||||
LEX_ERROR = -1,
|
||||
|
|
|
@ -139,6 +139,8 @@ bool GBIsROM(struct VFile* vf);
|
|||
void GBGetGameTitle(const struct GB* gba, char* out);
|
||||
void GBGetGameCode(const struct GB* gba, char* out);
|
||||
|
||||
void GBTestKeypadIRQ(struct GB* gb);
|
||||
|
||||
void GBFrameEnded(struct GB* gb);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
|
|
@ -36,7 +36,6 @@ struct GBMBCRTCSaveBuffer {
|
|||
void GBMBCRTCRead(struct GB* gb);
|
||||
void GBMBCRTCWrite(struct GB* gb);
|
||||
|
||||
uint8_t GBMBC7Read(struct GBMemory*, uint16_t address);
|
||||
void GBMBC7Write(struct GBMemory*, uint16_t address, uint8_t value);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
|
|
@ -63,7 +63,8 @@ enum {
|
|||
};
|
||||
|
||||
struct GBMemory;
|
||||
typedef void (*GBMemoryBankController)(struct GB*, uint16_t address, uint8_t value);
|
||||
typedef void (*GBMemoryBankControllerWrite)(struct GB*, uint16_t address, uint8_t value);
|
||||
typedef uint8_t (*GBMemoryBankControllerRead)(struct GBMemory*, uint16_t address);
|
||||
|
||||
DECL_BITFIELD(GBMBC7Field, uint8_t);
|
||||
DECL_BIT(GBMBC7Field, SK, 6);
|
||||
|
@ -98,9 +99,14 @@ struct GBMBC7State {
|
|||
GBMBC7Field field;
|
||||
};
|
||||
|
||||
struct GBPocketCamState {
|
||||
bool registersActive;
|
||||
};
|
||||
|
||||
union GBMBCState {
|
||||
struct GBMBC1State mbc1;
|
||||
struct GBMBC7State mbc7;
|
||||
struct GBPocketCamState pocketCam;
|
||||
};
|
||||
|
||||
struct mRotationSource;
|
||||
|
@ -109,7 +115,8 @@ struct GBMemory {
|
|||
uint8_t* romBase;
|
||||
uint8_t* romBank;
|
||||
enum GBMemoryBankControllerType mbcType;
|
||||
GBMemoryBankController mbc;
|
||||
GBMemoryBankControllerWrite mbcWrite;
|
||||
GBMemoryBankControllerRead mbcRead;
|
||||
union GBMBCState mbcState;
|
||||
int currentBank;
|
||||
|
||||
|
|
|
@ -154,8 +154,7 @@ mLOG_DECLARE_CATEGORY(GB_STATE);
|
|||
* | bit 4: Is HDMA active?
|
||||
* | bits 5 - 7: Active RTC register
|
||||
* | 0x00196 - 0x00197: Reserved (leave zero)
|
||||
* 0x00198 - 0x0019F: Savestate creation time (usec since 1970)
|
||||
* 0x001A0 - 0x0025F: Reserved (leave zero)
|
||||
* 0x00198 - 0x0025F: Reserved (leave zero)
|
||||
* 0x00260 - 0x002FF: OAM
|
||||
* 0x00300 - 0x0037F: I/O memory
|
||||
* 0x00380 - 0x003FE: HRAM
|
||||
|
@ -354,9 +353,7 @@ struct GBSerializedState {
|
|||
uint16_t reserved;
|
||||
} memory;
|
||||
|
||||
uint64_t creationUsec;
|
||||
|
||||
uint32_t reserved[48];
|
||||
uint32_t reserved[50];
|
||||
|
||||
uint8_t oam[GB_SIZE_OAM];
|
||||
|
||||
|
|
|
@ -97,10 +97,6 @@ struct GBAMemory {
|
|||
char waitstatesSeq16[256];
|
||||
char waitstatesNonseq32[256];
|
||||
char waitstatesNonseq16[256];
|
||||
char waitstatesPrefetchSeq32[16];
|
||||
char waitstatesPrefetchSeq16[16];
|
||||
char waitstatesPrefetchNonseq32[16];
|
||||
char waitstatesPrefetchNonseq16[16];
|
||||
int activeRegion;
|
||||
bool prefetch;
|
||||
uint32_t lastPrefetchedPc;
|
||||
|
|
|
@ -190,8 +190,7 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
|
|||
* | 0x002F8 - 0x002FB: CPU prefecth (decode slot)
|
||||
* | 0x002FC - 0x002FF: CPU prefetch (fetch slot)
|
||||
* 0x00300 - 0x00303: Associated movie stream ID for record/replay (or 0 if no stream)
|
||||
* 0x00304 - 0x0030F: Reserved (leave zero)
|
||||
* 0x00310 - 0x00317: Savestate creation time (usec since 1970)
|
||||
* 0x00304 - 0x00317: Savestate creation time (usec since 1970)
|
||||
* 0x00318 - 0x0031B: Last prefetched program counter
|
||||
* 0x0031C - 0x0031F: Miscellaneous flags
|
||||
* | bit 0: Is CPU halted?
|
||||
|
@ -312,9 +311,7 @@ struct GBASerializedState {
|
|||
uint32_t cpuPrefetch[2];
|
||||
|
||||
uint32_t associatedStreamId;
|
||||
uint32_t reservedRr[3];
|
||||
|
||||
uint64_t creationUsec;
|
||||
uint32_t reservedRr[5];
|
||||
|
||||
uint32_t lastPrefetchedPc;
|
||||
GBASerializedMiscFlags miscFlags;
|
||||
|
|
|
@ -10,9 +10,10 @@
|
|||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba/internal/debugger/debugger.h>
|
||||
#include <mgba/debugger/debugger.h>
|
||||
|
||||
#include <mgba/internal/lr35902/lr35902.h>
|
||||
#include <mgba-util/vector.h>
|
||||
|
||||
|
||||
struct LR35902DebugBreakpoint {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/internal/arm/arm.h>
|
||||
#include <mgba/internal/arm/decoder.h>
|
||||
#include <mgba/internal/arm/isa-inlines.h>
|
||||
#include <mgba/internal/arm/debugger/memory-debugger.h>
|
||||
|
||||
|
@ -54,6 +55,7 @@ static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform*, uint32_t address
|
|||
static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform*);
|
||||
static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform*);
|
||||
static void ARMDebuggerTrace(struct mDebuggerPlatform*, char* out, size_t* length);
|
||||
|
||||
struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) {
|
||||
struct mDebuggerPlatform* platform = (struct mDebuggerPlatform*) malloc(sizeof(struct ARMDebugger));
|
||||
|
@ -66,6 +68,7 @@ struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) {
|
|||
platform->clearWatchpoint = ARMDebuggerClearWatchpoint;
|
||||
platform->checkBreakpoints = ARMDebuggerCheckBreakpoints;
|
||||
platform->hasBreakpoints = ARMDebuggerHasBreakpoints;
|
||||
platform->trace = ARMDebuggerTrace;
|
||||
return platform;
|
||||
}
|
||||
|
||||
|
@ -207,3 +210,39 @@ static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t add
|
|||
ARMDebuggerRemoveMemoryShim(debugger);
|
||||
}
|
||||
}
|
||||
|
||||
static void ARMDebuggerTrace(struct mDebuggerPlatform* d, char* out, size_t* length) {
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
struct ARMCore* cpu = debugger->cpu;
|
||||
|
||||
char disassembly[64];
|
||||
|
||||
struct ARMInstructionInfo info;
|
||||
if (cpu->executionMode == MODE_ARM) {
|
||||
uint32_t instruction = cpu->prefetch[0];
|
||||
sprintf(disassembly, "%08X: ", instruction);
|
||||
ARMDecodeARM(instruction, &info);
|
||||
ARMDisassemble(&info, cpu->gprs[ARM_PC], disassembly + strlen("00000000: "), sizeof(disassembly) - strlen("00000000: "));
|
||||
} else {
|
||||
struct ARMInstructionInfo info2;
|
||||
struct ARMInstructionInfo combined;
|
||||
uint16_t instruction = cpu->prefetch[0];
|
||||
uint16_t instruction2 = cpu->prefetch[1];
|
||||
ARMDecodeThumb(instruction, &info);
|
||||
ARMDecodeThumb(instruction2, &info2);
|
||||
if (ARMDecodeThumbCombine(&info, &info2, &combined)) {
|
||||
sprintf(disassembly, "%04X%04X: ", instruction, instruction2);
|
||||
ARMDisassemble(&combined, cpu->gprs[ARM_PC], disassembly + strlen("00000000: "), sizeof(disassembly) - strlen("00000000: "));
|
||||
} else {
|
||||
sprintf(disassembly, " %04X: ", instruction);
|
||||
ARMDisassemble(&info, cpu->gprs[ARM_PC], disassembly + strlen("00000000: "), sizeof(disassembly) - strlen("00000000: "));
|
||||
}
|
||||
}
|
||||
|
||||
*length = snprintf(out, *length, "%08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X cpsr: %08X | %s",
|
||||
cpu->gprs[0], cpu->gprs[1], cpu->gprs[2], cpu->gprs[3],
|
||||
cpu->gprs[4], cpu->gprs[5], cpu->gprs[6], cpu->gprs[7],
|
||||
cpu->gprs[8], cpu->gprs[9], cpu->gprs[10], cpu->gprs[11],
|
||||
cpu->gprs[12], cpu->gprs[13], cpu->gprs[14], cpu->gprs[15],
|
||||
cpu->cpsr.packed, disassembly);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,496 @@
|
|||
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include <mgba/core/mem-search.h>
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/core/interface.h>
|
||||
|
||||
DEFINE_VECTOR(mCoreMemorySearchResults, struct mCoreMemorySearchResult);
|
||||
|
||||
static size_t _search32(const void* mem, size_t size, const struct mCoreMemoryBlock* block, uint32_t value32, struct mCoreMemorySearchResults* out, size_t limit) {
|
||||
const uint32_t* mem32 = mem;
|
||||
size_t found = 0;
|
||||
uint32_t start = block->start;
|
||||
uint32_t end = size; // TODO: Segments
|
||||
size_t i;
|
||||
// TODO: Big endian
|
||||
for (i = 0; (!limit || found < limit) && i < end; i += 16) {
|
||||
int mask = 0;
|
||||
mask |= (mem32[(i >> 2) + 0] == value32) << 0;
|
||||
mask |= (mem32[(i >> 2) + 1] == value32) << 1;
|
||||
mask |= (mem32[(i >> 2) + 2] == value32) << 2;
|
||||
mask |= (mem32[(i >> 2) + 3] == value32) << 3;
|
||||
if (!mask) {
|
||||
continue;
|
||||
}
|
||||
if ((mask & 1) && (!limit || found < limit)) {
|
||||
struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out);
|
||||
res->address = start + i;
|
||||
res->type = mCORE_MEMORY_SEARCH_32;
|
||||
res->segment = -1; // TODO
|
||||
res->guessDivisor = 1;
|
||||
++found;
|
||||
}
|
||||
if ((mask & 2) && (!limit || found < limit)) {
|
||||
struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out);
|
||||
res->address = start + i + 4;
|
||||
res->type = mCORE_MEMORY_SEARCH_32;
|
||||
res->segment = -1; // TODO
|
||||
res->guessDivisor = 1;
|
||||
++found;
|
||||
}
|
||||
if ((mask & 4) && (!limit || found < limit)) {
|
||||
struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out);
|
||||
res->address = start + i + 8;
|
||||
res->type = mCORE_MEMORY_SEARCH_32;
|
||||
res->segment = -1; // TODO
|
||||
res->guessDivisor = 1;
|
||||
++found;
|
||||
}
|
||||
if ((mask & 8) && (!limit || found < limit)) {
|
||||
struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out);
|
||||
res->address = start + i + 12;
|
||||
res->type = mCORE_MEMORY_SEARCH_32;
|
||||
res->segment = -1; // TODO
|
||||
res->guessDivisor = 1;
|
||||
++found;
|
||||
}
|
||||
}
|
||||
// TODO: last 12 bytes
|
||||
return found;
|
||||
}
|
||||
|
||||
static size_t _search16(const void* mem, size_t size, const struct mCoreMemoryBlock* block, uint16_t value16, struct mCoreMemorySearchResults* out, size_t limit) {
|
||||
const uint16_t* mem16 = mem;
|
||||
size_t found = 0;
|
||||
uint32_t start = block->start;
|
||||
uint32_t end = size; // TODO: Segments
|
||||
size_t i;
|
||||
// TODO: Big endian
|
||||
for (i = 0; (!limit || found < limit) && i < end; i += 16) {
|
||||
int mask = 0;
|
||||
mask |= (mem16[(i >> 1) + 0] == value16) << 0;
|
||||
mask |= (mem16[(i >> 1) + 1] == value16) << 1;
|
||||
mask |= (mem16[(i >> 1) + 2] == value16) << 2;
|
||||
mask |= (mem16[(i >> 1) + 3] == value16) << 3;
|
||||
mask |= (mem16[(i >> 1) + 4] == value16) << 4;
|
||||
mask |= (mem16[(i >> 1) + 5] == value16) << 5;
|
||||
mask |= (mem16[(i >> 1) + 6] == value16) << 6;
|
||||
mask |= (mem16[(i >> 1) + 7] == value16) << 7;
|
||||
if (!mask) {
|
||||
continue;
|
||||
}
|
||||
if ((mask & 1) && (!limit || found < limit)) {
|
||||
struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out);
|
||||
res->address = start + i;
|
||||
res->type = mCORE_MEMORY_SEARCH_16;
|
||||
res->segment = -1; // TODO
|
||||
res->guessDivisor = 1;
|
||||
++found;
|
||||
}
|
||||
if ((mask & 2) && (!limit || found < limit)) {
|
||||
struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out);
|
||||
res->address = start + i + 2;
|
||||
res->type = mCORE_MEMORY_SEARCH_16;
|
||||
res->segment = -1; // TODO
|
||||
res->guessDivisor = 1;
|
||||
++found;
|
||||
}
|
||||
if ((mask & 4) && (!limit || found < limit)) {
|
||||
struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out);
|
||||
res->address = start + i + 4;
|
||||
res->type = mCORE_MEMORY_SEARCH_16;
|
||||
res->segment = -1; // TODO
|
||||
res->guessDivisor = 1;
|
||||
++found;
|
||||
}
|
||||
if ((mask & 8) && (!limit || found < limit)) {
|
||||
struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out);
|
||||
res->address = start + i + 6;
|
||||
res->type = mCORE_MEMORY_SEARCH_16;
|
||||
res->segment = -1; // TODO
|
||||
res->guessDivisor = 1;
|
||||
++found;
|
||||
}
|
||||
if ((mask & 16) && (!limit || found < limit)) {
|
||||
struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out);
|
||||
res->address = start + i + 8;
|
||||
res->type = mCORE_MEMORY_SEARCH_16;
|
||||
res->segment = -1; // TODO
|
||||
res->guessDivisor = 1;
|
||||
++found;
|
||||
}
|
||||
if ((mask & 32) && (!limit || found < limit)) {
|
||||
struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out);
|
||||
res->address = start + i + 10;
|
||||
res->type = mCORE_MEMORY_SEARCH_16;
|
||||
res->segment = -1; // TODO
|
||||
res->guessDivisor = 1;
|
||||
++found;
|
||||
}
|
||||
if ((mask & 64) && (!limit || found < limit)) {
|
||||
struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out);
|
||||
res->address = start + i + 12;
|
||||
res->type = mCORE_MEMORY_SEARCH_16;
|
||||
res->segment = -1; // TODO
|
||||
res->guessDivisor = 1;
|
||||
++found;
|
||||
}
|
||||
if ((mask & 128) && (!limit || found < limit)) {
|
||||
struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out);
|
||||
res->address = start + i + 14;
|
||||
res->type = mCORE_MEMORY_SEARCH_16;
|
||||
res->segment = -1; // TODO
|
||||
res->guessDivisor = 1;
|
||||
++found;
|
||||
}
|
||||
}
|
||||
// TODO: last 14 bytes
|
||||
return found;
|
||||
}
|
||||
|
||||
static size_t _search8(const void* mem, size_t size, const struct mCoreMemoryBlock* block, uint8_t value8, struct mCoreMemorySearchResults* out, size_t limit) {
|
||||
const uint8_t* mem8 = mem;
|
||||
size_t found = 0;
|
||||
uint32_t start = block->start;
|
||||
uint32_t end = size; // TODO: Segments
|
||||
size_t i;
|
||||
for (i = 0; (!limit || found < limit) && i < end; i += 8) {
|
||||
int mask = 0;
|
||||
mask |= (mem8[i + 0] == value8) << 0;
|
||||
mask |= (mem8[i + 1] == value8) << 1;
|
||||
mask |= (mem8[i + 2] == value8) << 2;
|
||||
mask |= (mem8[i + 3] == value8) << 3;
|
||||
mask |= (mem8[i + 4] == value8) << 4;
|
||||
mask |= (mem8[i + 5] == value8) << 5;
|
||||
mask |= (mem8[i + 6] == value8) << 6;
|
||||
mask |= (mem8[i + 7] == value8) << 7;
|
||||
if (!mask) {
|
||||
continue;
|
||||
}
|
||||
if ((mask & 1) && (!limit || found < limit)) {
|
||||
struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out);
|
||||
res->address = start + i;
|
||||
res->type = mCORE_MEMORY_SEARCH_8;
|
||||
res->segment = -1; // TODO
|
||||
res->guessDivisor = 1;
|
||||
++found;
|
||||
}
|
||||
if ((mask & 2) && (!limit || found < limit)) {
|
||||
struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out);
|
||||
res->address = start + i + 1;
|
||||
res->type = mCORE_MEMORY_SEARCH_8;
|
||||
res->segment = -1; // TODO
|
||||
res->guessDivisor = 1;
|
||||
++found;
|
||||
}
|
||||
if ((mask & 4) && (!limit || found < limit)) {
|
||||
struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out);
|
||||
res->address = start + i + 2;
|
||||
res->type = mCORE_MEMORY_SEARCH_8;
|
||||
res->segment = -1; // TODO
|
||||
res->guessDivisor = 1;
|
||||
++found;
|
||||
}
|
||||
if ((mask & 8) && (!limit || found < limit)) {
|
||||
struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out);
|
||||
res->address = start + i + 3;
|
||||
res->type = mCORE_MEMORY_SEARCH_8;
|
||||
res->segment = -1; // TODO
|
||||
res->guessDivisor = 1;
|
||||
++found;
|
||||
}
|
||||
if ((mask & 16) && (!limit || found < limit)) {
|
||||
struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out);
|
||||
res->address = start + i + 4;
|
||||
res->type = mCORE_MEMORY_SEARCH_8;
|
||||
res->segment = -1; // TODO
|
||||
res->guessDivisor = 1;
|
||||
++found;
|
||||
}
|
||||
if ((mask & 32) && (!limit || found < limit)) {
|
||||
struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out);
|
||||
res->address = start + i + 5;
|
||||
res->type = mCORE_MEMORY_SEARCH_8;
|
||||
res->segment = -1; // TODO
|
||||
res->guessDivisor = 1;
|
||||
++found;
|
||||
}
|
||||
if ((mask & 64) && (!limit || found < limit)) {
|
||||
struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out);
|
||||
res->address = start + i + 6;
|
||||
res->type = mCORE_MEMORY_SEARCH_8;
|
||||
res->segment = -1; // TODO
|
||||
res->guessDivisor = 1;
|
||||
++found;
|
||||
}
|
||||
if ((mask & 128) && (!limit || found < limit)) {
|
||||
struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out);
|
||||
res->address = start + i + 7;
|
||||
res->type = mCORE_MEMORY_SEARCH_8;
|
||||
res->segment = -1; // TODO
|
||||
res->guessDivisor = 1;
|
||||
++found;
|
||||
}
|
||||
}
|
||||
// TODO: last 7 bytes
|
||||
return found;
|
||||
}
|
||||
|
||||
static size_t _searchStr(const void* mem, size_t size, const struct mCoreMemoryBlock* block, const char* valueStr, struct mCoreMemorySearchResults* out, size_t limit) {
|
||||
const char* memStr = mem;
|
||||
size_t found = 0;
|
||||
size_t len = strlen(valueStr);
|
||||
uint32_t start = block->start;
|
||||
uint32_t end = size; // TODO: Segments
|
||||
size_t i;
|
||||
for (i = 0; (!limit || found < limit) && i < end - len; ++i) {
|
||||
if (!strncmp(valueStr, &memStr[i], len)) {
|
||||
struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsAppend(out);
|
||||
res->address = start + i;
|
||||
res->type = mCORE_MEMORY_SEARCH_STRING;
|
||||
res->segment = -1; // TODO
|
||||
++found;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
static size_t _searchGuess(const void* mem, size_t size, const struct mCoreMemoryBlock* block, const char* valueStr, struct mCoreMemorySearchResults* out, size_t limit) {
|
||||
// TODO: As str
|
||||
|
||||
char* end;
|
||||
uint64_t value;
|
||||
|
||||
size_t found = 0;
|
||||
|
||||
struct mCoreMemorySearchResults tmp;
|
||||
mCoreMemorySearchResultsInit(&tmp, 0);
|
||||
|
||||
// Decimal:
|
||||
value = strtoull(valueStr, &end, 10);
|
||||
if (end) {
|
||||
if (value > 0x10000) {
|
||||
found += _search32(mem, size, block, value, out, limit ? limit - found : 0);
|
||||
} else if (value > 0x100) {
|
||||
found += _search16(mem, size, block, value, out, limit ? limit - found : 0);
|
||||
} else {
|
||||
found += _search8(mem, size, block, value, out, limit ? limit - found : 0);
|
||||
}
|
||||
|
||||
uint32_t divisor = 1;
|
||||
while (value && !(value % 10)) {
|
||||
mCoreMemorySearchResultsClear(&tmp);
|
||||
value /= 10;
|
||||
divisor *= 10;
|
||||
|
||||
if (value > 0x10000) {
|
||||
found += _search32(mem, size, block, value, &tmp, limit ? limit - found : 0);
|
||||
} else if (value > 0x100) {
|
||||
found += _search16(mem, size, block, value, &tmp, limit ? limit - found : 0);
|
||||
} else {
|
||||
found += _search8(mem, size, block, value, &tmp, limit ? limit - found : 0);
|
||||
}
|
||||
size_t i;
|
||||
for (i = 0; i < mCoreMemorySearchResultsSize(&tmp); ++i) {
|
||||
struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsGetPointer(&tmp, i);
|
||||
res->guessDivisor = divisor;
|
||||
*mCoreMemorySearchResultsAppend(out) = *res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hex:
|
||||
value = strtoull(valueStr, &end, 16);
|
||||
if (end) {
|
||||
if (value > 0x10000) {
|
||||
found += _search32(mem, size, block, value, out, limit ? limit - found : 0);
|
||||
} else if (value > 0x100) {
|
||||
found += _search16(mem, size, block, value, out, limit ? limit - found : 0);
|
||||
} else {
|
||||
found += _search8(mem, size, block, value, out, limit ? limit - found : 0);
|
||||
}
|
||||
|
||||
uint32_t divisor = 1;
|
||||
while (value && !(value & 0xF)) {
|
||||
mCoreMemorySearchResultsClear(&tmp);
|
||||
value >>= 4;
|
||||
divisor <<= 4;
|
||||
|
||||
if (value > 0x10000) {
|
||||
found += _search32(mem, size, block, value, &tmp, limit ? limit - found : 0);
|
||||
} else if (value > 0x100) {
|
||||
found += _search16(mem, size, block, value, &tmp, limit ? limit - found : 0);
|
||||
} else {
|
||||
found += _search8(mem, size, block, value, &tmp, limit ? limit - found : 0);
|
||||
}
|
||||
size_t i;
|
||||
for (i = 0; i < mCoreMemorySearchResultsSize(&tmp); ++i) {
|
||||
struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsGetPointer(&tmp, i);
|
||||
res->guessDivisor = divisor;
|
||||
*mCoreMemorySearchResultsAppend(out) = *res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mCoreMemorySearchResultsDeinit(&tmp);
|
||||
return found;
|
||||
}
|
||||
|
||||
static size_t _search(const void* mem, size_t size, const struct mCoreMemoryBlock* block, const struct mCoreMemorySearchParams* params, struct mCoreMemorySearchResults* out, size_t limit) {
|
||||
switch (params->type) {
|
||||
case mCORE_MEMORY_SEARCH_32:
|
||||
return _search32(mem, size, block, params->value32, out, limit);
|
||||
case mCORE_MEMORY_SEARCH_16:
|
||||
return _search16(mem, size, block, params->value16, out, limit);
|
||||
case mCORE_MEMORY_SEARCH_8:
|
||||
return _search8(mem, size, block, params->value8, out, limit);
|
||||
case mCORE_MEMORY_SEARCH_STRING:
|
||||
return _searchStr(mem, size, block, params->valueStr, out, limit);
|
||||
case mCORE_MEMORY_SEARCH_GUESS:
|
||||
return _searchGuess(mem, size, block, params->valueStr, out, limit);
|
||||
}
|
||||
}
|
||||
|
||||
void mCoreMemorySearch(struct mCore* core, const struct mCoreMemorySearchParams* params, struct mCoreMemorySearchResults* out, size_t limit) {
|
||||
const struct mCoreMemoryBlock* blocks;
|
||||
size_t nBlocks = core->listMemoryBlocks(core, &blocks);
|
||||
size_t found = 0;
|
||||
|
||||
size_t b;
|
||||
for (b = 0; (!limit || found < limit) && b < nBlocks; ++b) {
|
||||
size_t size;
|
||||
const struct mCoreMemoryBlock* block = &blocks[b];
|
||||
if (!(block->flags & params->memoryFlags)) {
|
||||
continue;
|
||||
}
|
||||
void* mem = core->getMemoryBlock(core, block->id, &size);
|
||||
if (!mem) {
|
||||
continue;
|
||||
}
|
||||
if (size > block->end - block->start) {
|
||||
size = block->end - block->start; // TOOD: Segments
|
||||
}
|
||||
found += _search(mem, size, block, params, out, limit ? limit - found : 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool _testGuess(struct mCore* core, const struct mCoreMemorySearchResult* res, const struct mCoreMemorySearchParams* params) {
|
||||
uint64_t value;
|
||||
char* end;
|
||||
|
||||
value = strtoull(params->valueStr, &end, 10);
|
||||
if (end) {
|
||||
if (core->rawRead8(core, res->address, res->segment) * res->guessDivisor == value) {
|
||||
return true;
|
||||
}
|
||||
if (!(res->address & 1) && core->rawRead16(core, res->address, res->segment) * res->guessDivisor == value) {
|
||||
return true;
|
||||
}
|
||||
if (!(res->address & 3) && core->rawRead32(core, res->address, res->segment) * res->guessDivisor == value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
value = strtoull(params->valueStr, &end, 16);
|
||||
if (end) {
|
||||
if (core->rawRead8(core, res->address, res->segment) * res->guessDivisor == value) {
|
||||
return true;
|
||||
}
|
||||
if (!(res->address & 1) && core->rawRead16(core, res->address, res->segment) * res->guessDivisor == value) {
|
||||
return true;
|
||||
}
|
||||
if (!(res->address & 3) && core->rawRead32(core, res->address, res->segment) * res->guessDivisor == value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void mCoreMemorySearchRepeat(struct mCore* core, const struct mCoreMemorySearchParams* params, struct mCoreMemorySearchResults* inout) {
|
||||
size_t i;
|
||||
for (i = 0; i < mCoreMemorySearchResultsSize(inout); ++i) {
|
||||
struct mCoreMemorySearchResult* res = mCoreMemorySearchResultsGetPointer(inout, i);
|
||||
switch (res->type) {
|
||||
case mCORE_MEMORY_SEARCH_8:
|
||||
switch (params->type) {
|
||||
case mCORE_MEMORY_SEARCH_8:
|
||||
if (core->rawRead8(core, res->address, res->segment) != params->value8) {
|
||||
mCoreMemorySearchResultsShift(inout, i, 1);
|
||||
--i;
|
||||
}
|
||||
break;
|
||||
case mCORE_MEMORY_SEARCH_16:
|
||||
if (core->rawRead8(core, res->address, res->segment) != params->value16) {
|
||||
mCoreMemorySearchResultsShift(inout, i, 1);
|
||||
--i;
|
||||
}
|
||||
break;
|
||||
case mCORE_MEMORY_SEARCH_32:
|
||||
if (core->rawRead32(core, res->address, res->segment) != params->value32) {
|
||||
mCoreMemorySearchResultsShift(inout, i, 1);
|
||||
--i;
|
||||
}
|
||||
break;
|
||||
case mCORE_MEMORY_SEARCH_GUESS:
|
||||
if (!_testGuess(core, res, params)) {
|
||||
mCoreMemorySearchResultsShift(inout, i, 1);
|
||||
--i;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case mCORE_MEMORY_SEARCH_16:
|
||||
switch (params->type) {
|
||||
case mCORE_MEMORY_SEARCH_16:
|
||||
if (core->rawRead16(core, res->address, res->segment) != params->value16) {
|
||||
mCoreMemorySearchResultsShift(inout, i, 1);
|
||||
--i;
|
||||
}
|
||||
break;
|
||||
case mCORE_MEMORY_SEARCH_32:
|
||||
if (core->rawRead32(core, res->address, res->segment) != params->value32) {
|
||||
mCoreMemorySearchResultsShift(inout, i, 1);
|
||||
--i;
|
||||
}
|
||||
break;
|
||||
case mCORE_MEMORY_SEARCH_GUESS:
|
||||
if (!_testGuess(core, res, params)) {
|
||||
mCoreMemorySearchResultsShift(inout, i, 1);
|
||||
--i;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case mCORE_MEMORY_SEARCH_32:
|
||||
switch (params->type) {
|
||||
case mCORE_MEMORY_SEARCH_32:
|
||||
if (core->rawRead32(core, res->address, res->segment) != params->value32) {
|
||||
mCoreMemorySearchResultsShift(inout, i, 1);
|
||||
--i;
|
||||
}
|
||||
break;
|
||||
case mCORE_MEMORY_SEARCH_GUESS:
|
||||
if (!_testGuess(core, res, params)) {
|
||||
mCoreMemorySearchResultsShift(inout, i, 1);
|
||||
--i;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case mCORE_MEMORY_SEARCH_STRING:
|
||||
case mCORE_MEMORY_SEARCH_GUESS:
|
||||
// TOOD
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -302,6 +302,36 @@ bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags) {
|
|||
struct mStateExtdata extdata;
|
||||
mStateExtdataInit(&extdata);
|
||||
size_t stateSize = core->stateSize(core);
|
||||
|
||||
if (flags & SAVESTATE_METADATA) {
|
||||
uint64_t 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);
|
||||
}
|
||||
#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);
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
creationUsec = 0;
|
||||
}
|
||||
|
||||
struct mStateExtdataItem item = {
|
||||
.size = sizeof(creationUsec),
|
||||
.data = &creationUsec,
|
||||
.clean = NULL
|
||||
};
|
||||
mStateExtdataPut(&extdata, EXTDATA_META_TIME, &item);
|
||||
}
|
||||
|
||||
if (flags & SAVESTATE_SAVEDATA) {
|
||||
void* sram = NULL;
|
||||
size_t size = core->savedataClone(core, &sram);
|
||||
|
|
|
@ -44,6 +44,7 @@ static void _clearBreakpoint(struct CLIDebugger*, struct CLIDebugVector*);
|
|||
static void _setWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _setReadWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _setWriteWatchpoint(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*);
|
||||
static void _writeWord(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
|
@ -80,6 +81,7 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = {
|
|||
{ "r/2", _readHalfword, CLIDVParse, "Read a halfword from a specified offset" },
|
||||
{ "r/4", _readWord, CLIDVParse, "Read a word from a specified offset" },
|
||||
{ "status", _printStatus, 0, "Print the current status" },
|
||||
{ "trace", _trace, CLIDVParse, "Trace a fixed number of instructions" },
|
||||
{ "w", _setWatchpoint, CLIDVParse, "Set a watchpoint" },
|
||||
{ "w/1", _writeByte, CLIDVParse, "Write a byte at a specified offset" },
|
||||
{ "w/2", _writeHalfword, CLIDVParse, "Write a halfword at a specified offset" },
|
||||
|
@ -469,6 +471,27 @@ static void _clearBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector
|
|||
}
|
||||
}
|
||||
|
||||
static void _trace(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
if (!dv || dv->type != CLIDV_INT_TYPE) {
|
||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS);
|
||||
return;
|
||||
}
|
||||
|
||||
char trace[1024];
|
||||
trace[sizeof(trace) - 1] = '\0';
|
||||
|
||||
int i;
|
||||
for (i = 0; i < dv->intValue; ++i) {
|
||||
debugger->d.core->step(debugger->d.core);
|
||||
size_t traceSize = sizeof(trace) - 1;
|
||||
debugger->d.platform->trace(debugger->d.platform, trace, &traceSize);
|
||||
if (traceSize + 1 < sizeof(trace)) {
|
||||
trace[traceSize + 1] = '\0';
|
||||
}
|
||||
debugger->backend->printf(debugger->backend, "%s\n", trace);
|
||||
}
|
||||
}
|
||||
|
||||
static void _printStatus(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
UNUSED(dv);
|
||||
debugger->system->printStatus(debugger->system);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include <mgba/internal/debugger/debugger.h>
|
||||
#include <mgba/debugger/debugger.h>
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
|
||||
|
@ -97,6 +97,13 @@ void mDebuggerRun(struct mDebugger* debugger) {
|
|||
}
|
||||
}
|
||||
|
||||
void mDebuggerRunFrame(struct mDebugger* debugger) {
|
||||
int32_t frame = debugger->core->frameCounter(debugger->core);
|
||||
do {
|
||||
mDebuggerRun(debugger);
|
||||
} while (debugger->core->frameCounter(debugger->core) == frame);
|
||||
}
|
||||
|
||||
void mDebuggerEnter(struct mDebugger* debugger, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
|
||||
debugger->state = DEBUGGER_PAUSED;
|
||||
if (debugger->platform->entered) {
|
||||
|
|
|
@ -431,7 +431,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
|
|||
runner->core->reset(runner->core);
|
||||
break;
|
||||
case RUNNER_SAVE_STATE:
|
||||
mCoreSaveState(runner->core, ((int) item->data) >> 16, SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA);
|
||||
mCoreSaveState(runner->core, ((int) item->data) >> 16, SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_RTC | SAVESTATE_METADATA);
|
||||
break;
|
||||
case RUNNER_LOAD_STATE:
|
||||
mCoreLoadState(runner->core, ((int) item->data) >> 16, SAVESTATE_SCREENSHOT | SAVESTATE_RTC);
|
||||
|
|
|
@ -337,6 +337,7 @@ void GBAudioWriteNR34(struct GBAudio* audio, uint8_t value) {
|
|||
}
|
||||
audio->ch3.window = 0;
|
||||
}
|
||||
mTimingDeschedule(audio->timing, &audio->ch3Fade);
|
||||
mTimingDeschedule(audio->timing, &audio->ch3Event);
|
||||
if (audio->playingCh3) {
|
||||
audio->ch3.readable = audio->style != GB_AUDIO_DMG;
|
||||
|
@ -863,6 +864,7 @@ static void _updateChannel3(struct mTiming* timing, void* user, uint32_t cyclesL
|
|||
ch->sample *= volume * 4;
|
||||
audio->ch3.readable = true;
|
||||
if (audio->style == GB_AUDIO_DMG) {
|
||||
mTimingDeschedule(audio->timing, &audio->ch3Fade);
|
||||
mTimingSchedule(timing, &audio->ch3Fade, 2 - cyclesLate);
|
||||
}
|
||||
int cycles = 2 * (2048 - ch->rate);
|
||||
|
|
|
@ -56,6 +56,28 @@ const static struct LR35902Segment _GBCSegments[] = {
|
|||
{ 0 }
|
||||
};
|
||||
|
||||
const static struct mCoreMemoryBlock _GBMemoryBlocks[] = {
|
||||
{ -1, "mem", "All", "All", 0, 0x10000, 0x10000, mCORE_MEMORY_VIRTUAL },
|
||||
{ GB_REGION_CART_BANK0, "cart0", "ROM Bank", "Game Pak (32kiB)", GB_BASE_CART_BANK0, GB_SIZE_CART_BANK0 * 2, 0x800000, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED, 511 },
|
||||
{ GB_REGION_VRAM, "vram", "VRAM", "Video RAM (8kiB)", GB_BASE_VRAM, GB_BASE_VRAM + GB_SIZE_VRAM, GB_SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GB_REGION_EXTERNAL_RAM, "sram", "SRAM", "External RAM (8kiB)", GB_BASE_EXTERNAL_RAM, GB_BASE_EXTERNAL_RAM + GB_SIZE_EXTERNAL_RAM, GB_SIZE_EXTERNAL_RAM * 4, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 3 },
|
||||
{ GB_REGION_WORKING_RAM_BANK0, "wram", "WRAM", "Working RAM (8kiB)", GB_BASE_WORKING_RAM_BANK0, GB_BASE_WORKING_RAM_BANK0 + GB_SIZE_WORKING_RAM_BANK0 * 2 , GB_SIZE_WORKING_RAM_BANK0 * 2, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GB_BASE_OAM, "oam", "OAM", "OBJ Attribute Memory", GB_BASE_OAM, GB_BASE_OAM + GB_SIZE_OAM, GB_SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GB_BASE_IO, "io", "MMIO", "Memory-Mapped I/O", GB_BASE_IO, GB_BASE_IO + GB_SIZE_IO, GB_SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GB_BASE_HRAM, "hram", "HRAM", "High RAM", GB_BASE_HRAM, GB_BASE_HRAM + GB_SIZE_HRAM, GB_SIZE_HRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
};
|
||||
|
||||
const static struct mCoreMemoryBlock _GBCMemoryBlocks[] = {
|
||||
{ -1, "mem", "All", "All", 0, 0x10000, 0x10000, mCORE_MEMORY_VIRTUAL },
|
||||
{ GB_REGION_CART_BANK0, "cart0", "ROM Bank", "Game Pak (32kiB)", GB_BASE_CART_BANK0, GB_SIZE_CART_BANK0 * 2, 0x800000, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED, 511 },
|
||||
{ GB_REGION_VRAM, "vram", "VRAM", "Video RAM (8kiB)", GB_BASE_VRAM, GB_BASE_VRAM + GB_SIZE_VRAM, GB_SIZE_VRAM * 2, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 1 },
|
||||
{ GB_REGION_EXTERNAL_RAM, "sram", "SRAM", "External RAM (8kiB)", GB_BASE_EXTERNAL_RAM, GB_BASE_EXTERNAL_RAM + GB_SIZE_EXTERNAL_RAM, GB_SIZE_EXTERNAL_RAM * 4, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 3 },
|
||||
{ GB_REGION_WORKING_RAM_BANK0, "wram", "WRAM", "Working RAM (8kiB)", GB_BASE_WORKING_RAM_BANK0, GB_BASE_WORKING_RAM_BANK0 + GB_SIZE_WORKING_RAM_BANK0 * 2, GB_SIZE_WORKING_RAM_BANK0 * 8, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 7 },
|
||||
{ GB_BASE_OAM, "oam", "OAM", "OBJ Attribute Memory", GB_BASE_OAM, GB_BASE_OAM + GB_SIZE_OAM, GB_SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GB_BASE_IO, "io", "MMIO", "Memory-Mapped I/O", GB_BASE_IO, GB_BASE_IO + GB_SIZE_IO, GB_SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ GB_BASE_HRAM, "hram", "HRAM", "High RAM", GB_BASE_HRAM, GB_BASE_HRAM + GB_SIZE_HRAM, GB_SIZE_HRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
};
|
||||
|
||||
struct mVideoLogContext;
|
||||
struct GBCore {
|
||||
struct mCore d;
|
||||
|
@ -422,11 +444,13 @@ static bool _GBCoreSaveState(struct mCore* core, void* state) {
|
|||
static void _GBCoreSetKeys(struct mCore* core, uint32_t keys) {
|
||||
struct GBCore* gbcore = (struct GBCore*) core;
|
||||
gbcore->keys = keys;
|
||||
GBTestKeypadIRQ(core->board);
|
||||
}
|
||||
|
||||
static void _GBCoreAddKeys(struct mCore* core, uint32_t keys) {
|
||||
struct GBCore* gbcore = (struct GBCore*) core;
|
||||
gbcore->keys |= keys;
|
||||
GBTestKeypadIRQ(core->board);
|
||||
}
|
||||
|
||||
static void _GBCoreClearKeys(struct mCore* core, uint32_t keys) {
|
||||
|
@ -553,6 +577,48 @@ static void _GBCoreRawWrite32(struct mCore* core, uint32_t address, int segment,
|
|||
GBPatch8(cpu, address + 3, value >> 24, NULL, segment);
|
||||
}
|
||||
|
||||
size_t _GBListMemoryBlocks(const struct mCore* core, const struct mCoreMemoryBlock** blocks) {
|
||||
const struct GB* gb = core->board;
|
||||
switch (gb->model) {
|
||||
case GB_MODEL_DMG:
|
||||
case GB_MODEL_SGB:
|
||||
default:
|
||||
*blocks = _GBMemoryBlocks;
|
||||
return sizeof(_GBMemoryBlocks) / sizeof(*_GBMemoryBlocks);
|
||||
case GB_MODEL_CGB:
|
||||
case GB_MODEL_AGB:
|
||||
*blocks = _GBCMemoryBlocks;
|
||||
return sizeof(_GBCMemoryBlocks) / sizeof(*_GBCMemoryBlocks);
|
||||
}
|
||||
}
|
||||
|
||||
void* _GBGetMemoryBlock(struct mCore* core, size_t id, size_t* sizeOut) {
|
||||
struct GB* gb = core->board;
|
||||
bool isCgb = gb->model >= GB_MODEL_CGB;
|
||||
switch (id) {
|
||||
default:
|
||||
return NULL;
|
||||
case GB_REGION_CART_BANK0:
|
||||
*sizeOut = gb->memory.romSize;
|
||||
return gb->memory.rom;
|
||||
case GB_REGION_VRAM:
|
||||
*sizeOut = GB_SIZE_WORKING_RAM_BANK0 * (isCgb ? 1 : 2);
|
||||
return gb->video.vram;
|
||||
case GB_REGION_EXTERNAL_RAM:
|
||||
*sizeOut = gb->sramSize;
|
||||
return gb->memory.sram;
|
||||
case GB_REGION_WORKING_RAM_BANK0:
|
||||
*sizeOut = GB_SIZE_VRAM * (isCgb ? 8 : 2);
|
||||
return gb->memory.wram;
|
||||
case GB_BASE_OAM:
|
||||
*sizeOut = GB_SIZE_OAM;
|
||||
return gb->video.oam.raw;
|
||||
case GB_BASE_HRAM:
|
||||
*sizeOut = GB_SIZE_HRAM;
|
||||
return gb->memory.hram;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_DEBUGGERS
|
||||
static bool _GBCoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) {
|
||||
UNUSED(core);
|
||||
|
@ -787,6 +853,8 @@ struct mCore* GBCoreCreate(void) {
|
|||
core->rawWrite8 = _GBCoreRawWrite8;
|
||||
core->rawWrite16 = _GBCoreRawWrite16;
|
||||
core->rawWrite32 = _GBCoreRawWrite32;
|
||||
core->listMemoryBlocks = _GBListMemoryBlocks;
|
||||
core->getMemoryBlock = _GBGetMemoryBlock;
|
||||
#ifdef USE_DEBUGGERS
|
||||
core->supportsDebuggerType = _GBCoreSupportsDebuggerType;
|
||||
core->debuggerPlatform = _GBCoreDebuggerPlatform;
|
||||
|
|
|
@ -118,5 +118,5 @@ static void _save(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
|||
|
||||
struct GBCLIDebugger* gbDebugger = (struct GBCLIDebugger*) debugger->system;
|
||||
|
||||
mCoreSaveState(gbDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC);
|
||||
mCoreSaveState(gbDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC | SAVESTATE_METADATA);
|
||||
}
|
||||
|
|
|
@ -710,4 +710,6 @@ void GBFrameEnded(struct GB* gb) {
|
|||
mCheatRefresh(device, cheats);
|
||||
}
|
||||
}
|
||||
|
||||
GBTestKeypadIRQ(gb);
|
||||
}
|
||||
|
|
|
@ -564,6 +564,13 @@ uint8_t GBIORead(struct GB* gb, unsigned address) {
|
|||
return gb->memory.io[address] | _registerMask[address];
|
||||
}
|
||||
|
||||
void GBTestKeypadIRQ(struct GB* gb) {
|
||||
if (_readKeys(gb)) {
|
||||
gb->memory.io[REG_IF] |= (1 << GB_IRQ_KEYPAD);
|
||||
GBUpdateIRQs(gb);
|
||||
}
|
||||
}
|
||||
|
||||
struct GBSerializedState;
|
||||
void GBIOSerialize(const struct GB* gb, struct GBSerializedState* state) {
|
||||
memcpy(state->io, gb->memory.io, GB_SIZE_IO);
|
||||
|
|
91
src/gb/mbc.c
91
src/gb/mbc.c
|
@ -28,6 +28,10 @@ static void _GBMBC5(struct GB*, uint16_t address, uint8_t value);
|
|||
static void _GBMBC6(struct GB*, uint16_t address, uint8_t value);
|
||||
static void _GBMBC7(struct GB*, uint16_t address, uint8_t value);
|
||||
static void _GBHuC3(struct GB*, uint16_t address, uint8_t value);
|
||||
static void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value);
|
||||
|
||||
static uint8_t _GBMBC7Read(struct GBMemory*, uint16_t address);
|
||||
static uint8_t _GBPocketCamRead(struct GBMemory*, uint16_t address);
|
||||
|
||||
void GBMBCSwitchBank(struct GB* gb, int bank) {
|
||||
size_t bankStart = bank * GB_SIZE_CART_BANK0;
|
||||
|
@ -148,6 +152,12 @@ void GBMBCInit(struct GB* gb) {
|
|||
case 0x22:
|
||||
gb->memory.mbcType = GB_MBC7;
|
||||
break;
|
||||
case 0xFC:
|
||||
gb->memory.mbcType = GB_POCKETCAM;
|
||||
break;
|
||||
case 0xFD:
|
||||
gb->memory.mbcType = GB_HuC1;
|
||||
break;
|
||||
case 0xFE:
|
||||
gb->memory.mbcType = GB_HuC3;
|
||||
break;
|
||||
|
@ -156,51 +166,57 @@ void GBMBCInit(struct GB* gb) {
|
|||
} else {
|
||||
gb->memory.mbcType = GB_MBC_NONE;
|
||||
}
|
||||
gb->memory.mbcRead = NULL;
|
||||
switch (gb->memory.mbcType) {
|
||||
case GB_MBC_NONE:
|
||||
gb->memory.mbc = _GBMBCNone;
|
||||
gb->memory.mbcWrite = _GBMBCNone;
|
||||
break;
|
||||
case GB_MBC1:
|
||||
gb->memory.mbc = _GBMBC1;
|
||||
gb->memory.mbcWrite = _GBMBC1;
|
||||
break;
|
||||
case GB_MBC2:
|
||||
gb->memory.mbc = _GBMBC2;
|
||||
gb->memory.mbcWrite = _GBMBC2;
|
||||
gb->sramSize = 0x200;
|
||||
break;
|
||||
case GB_MBC3:
|
||||
gb->memory.mbc = _GBMBC3;
|
||||
gb->memory.mbcWrite = _GBMBC3;
|
||||
break;
|
||||
default:
|
||||
mLOG(GB_MBC, WARN, "Unknown MBC type: %02X", cart->type);
|
||||
// Fall through
|
||||
case GB_MBC5:
|
||||
gb->memory.mbc = _GBMBC5;
|
||||
gb->memory.mbcWrite = _GBMBC5;
|
||||
break;
|
||||
case GB_MBC6:
|
||||
mLOG(GB_MBC, WARN, "unimplemented MBC: MBC6");
|
||||
gb->memory.mbc = _GBMBC6;
|
||||
gb->memory.mbcWrite = _GBMBC6;
|
||||
break;
|
||||
case GB_MBC7:
|
||||
gb->memory.mbc = _GBMBC7;
|
||||
gb->memory.mbcWrite = _GBMBC7;
|
||||
gb->memory.mbcRead = _GBMBC7Read;
|
||||
gb->sramSize = GB_SIZE_EXTERNAL_RAM;
|
||||
break;
|
||||
case GB_MMM01:
|
||||
mLOG(GB_MBC, WARN, "unimplemented MBC: MMM01");
|
||||
gb->memory.mbc = _GBMBC1;
|
||||
gb->memory.mbcWrite = _GBMBC1;
|
||||
break;
|
||||
case GB_HuC1:
|
||||
mLOG(GB_MBC, WARN, "unimplemented MBC: HuC-1");
|
||||
gb->memory.mbc = _GBMBC1;
|
||||
gb->memory.mbcWrite = _GBMBC1;
|
||||
break;
|
||||
case GB_HuC3:
|
||||
gb->memory.mbc = _GBHuC3;
|
||||
gb->memory.mbcWrite = _GBHuC3;
|
||||
break;
|
||||
case GB_MBC3_RTC:
|
||||
memset(gb->memory.rtcRegs, 0, sizeof(gb->memory.rtcRegs));
|
||||
gb->memory.mbc = _GBMBC3;
|
||||
gb->memory.mbcWrite = _GBMBC3;
|
||||
break;
|
||||
case GB_MBC5_RUMBLE:
|
||||
gb->memory.mbc = _GBMBC5;
|
||||
gb->memory.mbcWrite = _GBMBC5;
|
||||
break;
|
||||
case GB_POCKETCAM:
|
||||
gb->memory.mbcWrite = _GBPocketCam;
|
||||
gb->memory.mbcRead = _GBPocketCamRead;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -350,7 +366,8 @@ void _GBMBC2(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
// TODO
|
||||
mLOG(GB_MBC, STUB, "MBC2 unknown address: %04X:%02X", address, value);
|
||||
break;
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
void _GBMBC3(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
|
@ -466,7 +483,7 @@ void _GBMBC7(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
}
|
||||
}
|
||||
|
||||
uint8_t GBMBC7Read(struct GBMemory* memory, uint16_t address) {
|
||||
uint8_t _GBMBC7Read(struct GBMemory* memory, uint16_t address) {
|
||||
struct GBMBC7State* mbc7 = &memory->mbcState.mbc7;
|
||||
switch (address & 0xF0) {
|
||||
case 0x00:
|
||||
|
@ -674,6 +691,52 @@ void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) {
|
|||
}
|
||||
}
|
||||
|
||||
void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
int bank = value & 0x3F;
|
||||
switch (address >> 13) {
|
||||
case 0x0:
|
||||
switch (value) {
|
||||
case 0:
|
||||
memory->sramAccess = false;
|
||||
break;
|
||||
case 0xA:
|
||||
memory->sramAccess = true;
|
||||
GBMBCSwitchSramBank(gb, memory->sramCurrentBank);
|
||||
break;
|
||||
default:
|
||||
// TODO
|
||||
mLOG(GB_MBC, STUB, "Pocket Cam unknown value %02X", value);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x1:
|
||||
GBMBCSwitchBank(gb, bank);
|
||||
break;
|
||||
case 0x2:
|
||||
if (value < 0x10) {
|
||||
GBMBCSwitchSramBank(gb, value);
|
||||
memory->mbcState.pocketCam.registersActive = false;
|
||||
} else {
|
||||
memory->mbcState.pocketCam.registersActive = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
mLOG(GB_MBC, STUB, "Pocket Cam unknown address: %04X:%02X", address, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t _GBPocketCamRead(struct GBMemory* memory, uint16_t address) {
|
||||
if (memory->mbcState.pocketCam.registersActive) {
|
||||
return 0;
|
||||
}
|
||||
if (!memory->sramAccess) {
|
||||
return 0xFF;
|
||||
}
|
||||
return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
|
||||
}
|
||||
|
||||
void GBMBCRTCRead(struct GB* gb) {
|
||||
struct GBMBCRTCSaveBuffer rtcBuffer;
|
||||
struct VFile* vf = gb->sramVf;
|
||||
|
|
|
@ -99,7 +99,8 @@ void GBMemoryInit(struct GB* gb) {
|
|||
gb->memory.romSize = 0;
|
||||
gb->memory.sram = 0;
|
||||
gb->memory.mbcType = GB_MBC_AUTODETECT;
|
||||
gb->memory.mbc = 0;
|
||||
gb->memory.mbcRead = NULL;
|
||||
gb->memory.mbcWrite = NULL;
|
||||
|
||||
gb->memory.rtc = NULL;
|
||||
|
||||
|
@ -215,10 +216,10 @@ uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address) {
|
|||
case GB_REGION_EXTERNAL_RAM + 1:
|
||||
if (memory->rtcAccess) {
|
||||
return memory->rtcRegs[memory->activeRtcReg];
|
||||
} else if (memory->mbcRead) {
|
||||
return memory->mbcRead(memory, address);
|
||||
} else if (memory->sramAccess) {
|
||||
return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
|
||||
} else if (memory->mbcType == GB_MBC7) {
|
||||
return GBMBC7Read(memory, address);
|
||||
} else if (memory->mbcType == GB_HuC3) {
|
||||
return 0x01; // TODO: Is this supposed to be the current SRAM bank?
|
||||
}
|
||||
|
@ -274,7 +275,7 @@ void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) {
|
|||
case GB_REGION_CART_BANK1 + 1:
|
||||
case GB_REGION_CART_BANK1 + 2:
|
||||
case GB_REGION_CART_BANK1 + 3:
|
||||
memory->mbc(gb, address, value);
|
||||
memory->mbcWrite(gb, address, value);
|
||||
cpu->memory.setActiveRegion(cpu, cpu->pc);
|
||||
return;
|
||||
case GB_REGION_VRAM:
|
||||
|
@ -391,8 +392,8 @@ uint8_t GBView8(struct LR35902Core* cpu, uint16_t address, int segment) {
|
|||
} else {
|
||||
return 0xFF;
|
||||
}
|
||||
} else if (memory->mbcType == GB_MBC7) {
|
||||
return GBMBC7Read(memory, address);
|
||||
} else if (memory->mbcRead) {
|
||||
return memory->mbcRead(memory, address);
|
||||
} else if (memory->mbcType == GB_HuC3) {
|
||||
return 0x01; // TODO: Is this supposed to be the current SRAM bank?
|
||||
}
|
||||
|
|
|
@ -59,25 +59,6 @@ void GBSerialize(struct GB* gb, struct GBSerializedState* state) {
|
|||
GBVideoSerialize(&gb->video, state);
|
||||
GBTimerSerialize(&gb->timer, state);
|
||||
GBAudioSerialize(&gb->audio, state);
|
||||
|
||||
#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, &state->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, &state->creationUsec);
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
state->creationUsec = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include <mgba/internal/gb/io.h>
|
||||
#include <mgba/internal/gb/serialize.h>
|
||||
|
||||
static void _GBTimerUpdateDIV(struct GBTimer* timer, uint32_t cyclesLate);
|
||||
|
||||
void _GBTimerIRQ(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||
UNUSED(timing);
|
||||
UNUSED(cyclesLate);
|
||||
|
@ -34,8 +36,12 @@ void _GBTimerIncrement(struct mTiming* timing, void* context, uint32_t cyclesLat
|
|||
++timer->internalDiv;
|
||||
timer->p->memory.io[REG_DIV] = timer->internalDiv >> 4;
|
||||
}
|
||||
_GBTimerUpdateDIV(timer, cyclesLate);
|
||||
}
|
||||
|
||||
void _GBTimerUpdateDIV(struct GBTimer* timer, uint32_t cyclesLate) {
|
||||
// Batch div increments
|
||||
int divsToGo = 16 - (timer->internalDiv & 15);
|
||||
int divsToGo = 16 - (timer->internalDiv & 15) + (timer->nextDiv / GB_DMG_DIV_PERIOD);
|
||||
int timaToGo = INT_MAX;
|
||||
if (timer->timaPeriod) {
|
||||
timaToGo = timer->timaPeriod - (timer->internalDiv & (timer->timaPeriod - 1));
|
||||
|
@ -43,8 +49,12 @@ void _GBTimerIncrement(struct mTiming* timing, void* context, uint32_t cyclesLat
|
|||
if (timaToGo < divsToGo) {
|
||||
divsToGo = timaToGo;
|
||||
}
|
||||
timer->nextDiv = GB_DMG_DIV_PERIOD * divsToGo;
|
||||
mTimingSchedule(timing, &timer->event, timer->nextDiv - cyclesLate);
|
||||
if (divsToGo > 16) {
|
||||
divsToGo = 16;
|
||||
}
|
||||
timer->nextDiv &= GB_DMG_DIV_PERIOD - 1;
|
||||
timer->nextDiv += GB_DMG_DIV_PERIOD * divsToGo;
|
||||
mTimingSchedule(&timer->p->timing, &timer->event, timer->nextDiv - cyclesLate);
|
||||
}
|
||||
|
||||
void GBTimerReset(struct GBTimer* timer) {
|
||||
|
@ -89,6 +99,8 @@ uint8_t GBTimerUpdateTAC(struct GBTimer* timer, GBRegisterTAC tac) {
|
|||
} else {
|
||||
timer->timaPeriod = 0;
|
||||
}
|
||||
mTimingDeschedule(&timer->p->timing, &timer->event);
|
||||
_GBTimerUpdateDIV(timer, 0);
|
||||
return tac;
|
||||
}
|
||||
|
||||
|
|
|
@ -628,4 +628,7 @@ void GBVideoDeserialize(struct GBVideo* video, const struct GBSerializedState* s
|
|||
|
||||
_cleanOAM(video, video->ly);
|
||||
GBVideoSwitchBank(video, video->vramCurrentBank);
|
||||
|
||||
video->renderer->deinit(video->renderer);
|
||||
video->renderer->init(video->renderer, video->p->model);
|
||||
}
|
||||
|
|
|
@ -256,18 +256,23 @@ static void _MidiKey2Freq(struct GBA* gba) {
|
|||
|
||||
static void _Div(struct GBA* gba, int32_t num, int32_t denom) {
|
||||
struct ARMCore* cpu = gba->cpu;
|
||||
if (denom != 0) {
|
||||
if (denom != 0 && (denom != -1 || num != INT32_MIN)) {
|
||||
div_t result = div(num, denom);
|
||||
cpu->gprs[0] = result.quot;
|
||||
cpu->gprs[1] = result.rem;
|
||||
cpu->gprs[3] = abs(result.quot);
|
||||
} else {
|
||||
} else if (denom == 0) {
|
||||
mLOG(GBA_BIOS, GAME_ERROR, "Attempting to divide %i by zero!", num);
|
||||
// If abs(num) > 1, this should hang, but that would be painful to
|
||||
// emulate in HLE, and no game will get into a state where it hangs...
|
||||
cpu->gprs[0] = (num < 0) ? -1 : 1;
|
||||
cpu->gprs[1] = num;
|
||||
cpu->gprs[3] = 1;
|
||||
} else {
|
||||
mLOG(GBA_BIOS, GAME_ERROR, "Attempting to divide INT_MIN by -1!");
|
||||
cpu->gprs[0] = INT32_MIN;
|
||||
cpu->gprs[1] = 0;
|
||||
cpu->gprs[3] = INT32_MIN;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
137
src/gba/core.c
137
src/gba/core.c
|
@ -45,6 +45,80 @@ const static struct mCoreChannelInfo _GBAAudioChannels[] = {
|
|||
{ 5, "chB", "FIFO Channel B", NULL },
|
||||
};
|
||||
|
||||
const static struct mCoreMemoryBlock _GBAMemoryBlocks[] = {
|
||||
{ -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL },
|
||||
{ REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_WORKING_IRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, BASE_WORKING_IRAM + SIZE_WORKING_IRAM, SIZE_WORKING_IRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_IO, "io", "MMIO", "Memory-Mapped I/O", BASE_IO, BASE_IO + SIZE_IO, SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
};
|
||||
|
||||
const static struct mCoreMemoryBlock _GBAMemoryBlocksSRAM[] = {
|
||||
{ -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL },
|
||||
{ REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_WORKING_IRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, BASE_WORKING_IRAM + SIZE_WORKING_IRAM, SIZE_WORKING_IRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_IO, "io", "MMIO", "Memory-Mapped I/O", BASE_IO, BASE_IO + SIZE_IO, SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART_SRAM, "sram", "SRAM", "Static RAM (64kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_SRAM, SIZE_CART_SRAM, true },
|
||||
};
|
||||
|
||||
const static struct mCoreMemoryBlock _GBAMemoryBlocksFlash512[] = {
|
||||
{ -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL },
|
||||
{ REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_WORKING_IRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, BASE_WORKING_IRAM + SIZE_WORKING_IRAM, SIZE_WORKING_IRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_IO, "io", "MMIO", "Memory-Mapped I/O", BASE_IO, BASE_IO + SIZE_IO, SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART_SRAM, "sram", "Flash", "Flash Memory (64kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_FLASH512, SIZE_CART_FLASH512, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
};
|
||||
|
||||
const static struct mCoreMemoryBlock _GBAMemoryBlocksFlash1M[] = {
|
||||
{ -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL },
|
||||
{ REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_WORKING_IRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, BASE_WORKING_IRAM + SIZE_WORKING_IRAM, SIZE_WORKING_IRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_IO, "io", "MMIO", "Memory-Mapped I/O", BASE_IO, BASE_IO + SIZE_IO, SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART_SRAM, "sram", "Flash", "Flash Memory (64kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_FLASH512, SIZE_CART_FLASH1M, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 1 },
|
||||
};
|
||||
|
||||
const static struct mCoreMemoryBlock _GBAMemoryBlocksEEPROM[] = {
|
||||
{ -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL },
|
||||
{ REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_WORKING_IRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, BASE_WORKING_IRAM + SIZE_WORKING_IRAM, SIZE_WORKING_IRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_IO, "io", "MMIO", "Memory-Mapped I/O", BASE_IO, BASE_IO + SIZE_IO, SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED },
|
||||
{ REGION_CART_SRAM_MIRROR, "eeprom", "EEPROM", "EEPROM (8kiB)", 0, SIZE_CART_EEPROM, SIZE_CART_EEPROM, mCORE_MEMORY_RW },
|
||||
};
|
||||
|
||||
struct mVideoLogContext;
|
||||
struct GBACore {
|
||||
struct mCore d;
|
||||
|
@ -542,6 +616,67 @@ static void _GBACoreRawWrite32(struct mCore* core, uint32_t address, int segment
|
|||
GBAPatch32(cpu, address, value, NULL);
|
||||
}
|
||||
|
||||
size_t _GBAListMemoryBlocks(const struct mCore* core, const struct mCoreMemoryBlock** blocks) {
|
||||
const struct GBA* gba = core->board;
|
||||
switch (gba->memory.savedata.type) {
|
||||
case SAVEDATA_SRAM:
|
||||
*blocks = _GBAMemoryBlocksSRAM;
|
||||
return sizeof(_GBAMemoryBlocksSRAM) / sizeof(*_GBAMemoryBlocksSRAM);
|
||||
case SAVEDATA_FLASH512:
|
||||
*blocks = _GBAMemoryBlocksFlash512;
|
||||
return sizeof(_GBAMemoryBlocksFlash512) / sizeof(*_GBAMemoryBlocksFlash512);
|
||||
case SAVEDATA_FLASH1M:
|
||||
*blocks = _GBAMemoryBlocksFlash1M;
|
||||
return sizeof(_GBAMemoryBlocksFlash1M) / sizeof(*_GBAMemoryBlocksFlash1M);
|
||||
case SAVEDATA_EEPROM:
|
||||
*blocks = _GBAMemoryBlocksEEPROM;
|
||||
return sizeof(_GBAMemoryBlocksEEPROM) / sizeof(*_GBAMemoryBlocksEEPROM);
|
||||
default:
|
||||
*blocks = _GBAMemoryBlocks;
|
||||
return sizeof(_GBAMemoryBlocks) / sizeof(*_GBAMemoryBlocks);
|
||||
}
|
||||
}
|
||||
|
||||
void* _GBAGetMemoryBlock(struct mCore* core, size_t id, size_t* sizeOut) {
|
||||
struct GBA* gba = core->board;
|
||||
switch (id) {
|
||||
default:
|
||||
return NULL;
|
||||
case REGION_BIOS:
|
||||
*sizeOut = SIZE_BIOS;
|
||||
return gba->memory.bios;
|
||||
case REGION_WORKING_RAM:
|
||||
*sizeOut = SIZE_WORKING_RAM;
|
||||
return gba->memory.wram;
|
||||
case REGION_WORKING_IRAM:
|
||||
*sizeOut = SIZE_WORKING_IRAM;
|
||||
return gba->memory.iwram;
|
||||
case REGION_PALETTE_RAM:
|
||||
*sizeOut = SIZE_PALETTE_RAM;
|
||||
return gba->video.palette;
|
||||
case REGION_VRAM:
|
||||
*sizeOut = SIZE_VRAM;
|
||||
return gba->video.vram;
|
||||
case REGION_OAM:
|
||||
*sizeOut = SIZE_OAM;
|
||||
return gba->video.oam.raw;
|
||||
case REGION_CART0:
|
||||
case REGION_CART1:
|
||||
case REGION_CART2:
|
||||
*sizeOut = gba->memory.romSize;
|
||||
return gba->memory.rom;
|
||||
case REGION_CART_SRAM:
|
||||
if (gba->memory.savedata.type == SAVEDATA_FLASH1M) {
|
||||
*sizeOut = SIZE_CART_FLASH1M;
|
||||
return gba->memory.savedata.currentBank;
|
||||
}
|
||||
// Fall through
|
||||
case REGION_CART_SRAM_MIRROR:
|
||||
*sizeOut = GBASavedataSize(&gba->memory.savedata);
|
||||
return gba->memory.savedata.data;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_DEBUGGERS
|
||||
static bool _GBACoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) {
|
||||
UNUSED(core);
|
||||
|
@ -773,6 +908,8 @@ struct mCore* GBACoreCreate(void) {
|
|||
core->rawWrite8 = _GBACoreRawWrite8;
|
||||
core->rawWrite16 = _GBACoreRawWrite16;
|
||||
core->rawWrite32 = _GBACoreRawWrite32;
|
||||
core->listMemoryBlocks = _GBAListMemoryBlocks;
|
||||
core->getMemoryBlock = _GBAGetMemoryBlock;
|
||||
#ifdef USE_DEBUGGERS
|
||||
core->supportsDebuggerType = _GBACoreSupportsDebuggerType;
|
||||
core->debuggerPlatform = _GBACoreDebuggerPlatform;
|
||||
|
|
|
@ -119,5 +119,5 @@ static void _save(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
|||
|
||||
struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system;
|
||||
|
||||
mCoreSaveState(gbaDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC);
|
||||
mCoreSaveState(gbaDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC | SAVESTATE_METADATA);
|
||||
}
|
||||
|
|
|
@ -1501,33 +1501,32 @@ int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) {
|
|||
previousLoads = dist;
|
||||
}
|
||||
|
||||
int32_t s = cpu->memory.activeSeqCycles16 + 1;
|
||||
int32_t n2s = cpu->memory.activeNonseqCycles16 - cpu->memory.activeSeqCycles16 + 1;
|
||||
int32_t s = cpu->memory.activeSeqCycles16;
|
||||
int32_t n2s = cpu->memory.activeNonseqCycles16 - cpu->memory.activeSeqCycles16;
|
||||
|
||||
// Figure out how many sequential loads we can jam in
|
||||
int32_t stall = s;
|
||||
int32_t loads = 1;
|
||||
|
||||
if (stall > wait && !previousLoads) {
|
||||
// We might need to stall a bit extra if we haven't finished the first S cycle
|
||||
wait = stall;
|
||||
} else {
|
||||
while (stall < wait) {
|
||||
if (stall < wait) {
|
||||
int32_t maxLoads = 8 - previousLoads;
|
||||
while (stall < wait && loads < maxLoads) {
|
||||
stall += s;
|
||||
++loads;
|
||||
}
|
||||
if (loads + previousLoads > 8) {
|
||||
loads = 8 - previousLoads;
|
||||
}
|
||||
}
|
||||
if (stall > wait) {
|
||||
// The wait cannot take less time than the prefetch stalls
|
||||
wait = stall;
|
||||
}
|
||||
|
||||
// This instruction used to have an N, convert it to an S.
|
||||
wait -= n2s;
|
||||
|
||||
// TODO: Invalidate prefetch on branch
|
||||
memory->lastPrefetchedPc = cpu->gprs[ARM_PC] + WORD_SIZE_THUMB * loads;
|
||||
memory->lastPrefetchedPc = cpu->gprs[ARM_PC] + WORD_SIZE_THUMB * (loads + previousLoads - 1);
|
||||
|
||||
// The next |loads|S waitstates disappear entirely, so long as they're all in a row
|
||||
cpu->cycles -= (s - 1) * loads;
|
||||
cpu->cycles -= stall;
|
||||
return wait;
|
||||
}
|
||||
|
||||
|
|
|
@ -69,25 +69,6 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) {
|
|||
GBAAudioSerialize(&gba->audio, state);
|
||||
GBASavedataSerialize(&gba->memory.savedata, state);
|
||||
|
||||
|
||||
#ifndef _MSC_VER
|
||||
struct timeval tv;
|
||||
if (!gettimeofday(&tv, 0)) {
|
||||
uint64_t usec = tv.tv_usec;
|
||||
usec += tv.tv_sec * 1000000LL;
|
||||
STORE_64(usec, 0, &state->creationUsec);
|
||||
}
|
||||
#else
|
||||
struct timespec ts;
|
||||
if (timespec_get(&ts, TIME_UTC)) {
|
||||
uint64_t usec = ts.tv_nsec / 1000;
|
||||
usec += ts.tv_sec * 1000000LL;
|
||||
STORE_64(usec, 0, &state->creationUsec);
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
state->creationUsec = 0;
|
||||
}
|
||||
state->associatedStreamId = 0;
|
||||
if (gba->rr) {
|
||||
gba->rr->stateSaved(gba->rr, state);
|
||||
|
|
|
@ -105,18 +105,18 @@ void GBATimerInit(struct GBA* gba) {
|
|||
void GBATimerUpdateRegister(struct GBA* gba, int timer) {
|
||||
struct GBATimer* currentTimer = &gba->timers[timer];
|
||||
if (GBATimerFlagsIsEnable(currentTimer->flags) && !GBATimerFlagsIsCountUp(currentTimer->flags)) {
|
||||
int32_t prefetchSkew = 0;
|
||||
if (gba->memory.lastPrefetchedPc >= (uint32_t) gba->cpu->gprs[ARM_PC]) {
|
||||
prefetchSkew = (gba->memory.lastPrefetchedPc - gba->cpu->gprs[ARM_PC]) * (gba->cpu->memory.activeSeqCycles16 + 1) / WORD_SIZE_THUMB;
|
||||
// Reading this takes two cycles (1N+1I), so let's remove them preemptively
|
||||
int32_t prefetchSkew = -2;
|
||||
if (gba->memory.lastPrefetchedPc > (uint32_t) gba->cpu->gprs[ARM_PC]) {
|
||||
prefetchSkew += ((gba->memory.lastPrefetchedPc - gba->cpu->gprs[ARM_PC]) * gba->cpu->memory.activeSeqCycles16) / WORD_SIZE_THUMB;
|
||||
}
|
||||
GBATimerUpdateRegisterInternal(currentTimer, &gba->timing, gba->cpu, &gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1], prefetchSkew);
|
||||
}
|
||||
}
|
||||
|
||||
void GBATimerUpdateRegisterInternal(struct GBATimer* timer, struct mTiming* timing, struct ARMCore* cpu, uint16_t* io, int32_t skew) {
|
||||
// Reading this takes two cycles (1N+1I), so let's remove them preemptively
|
||||
int32_t diff = cpu->cycles - (timer->lastEvent - timing->masterCycles);
|
||||
*io = timer->oldReload + ((diff - 2 + skew) >> GBATimerFlagsGetPrescaleBits(timer->flags));
|
||||
*io = timer->oldReload + ((diff + skew) >> GBATimerFlagsGetPrescaleBits(timer->flags));
|
||||
}
|
||||
|
||||
void GBATimerWriteTMCNT_LO(struct GBATimer* timer, uint16_t reload) {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <mgba/internal/lr35902/debugger/debugger.h>
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/internal/lr35902/decoder.h>
|
||||
#include <mgba/internal/lr35902/lr35902.h>
|
||||
#include <mgba/internal/lr35902/debugger/memory-debugger.h>
|
||||
|
||||
|
@ -48,6 +49,7 @@ static void LR35902DebuggerSetWatchpoint(struct mDebuggerPlatform*, uint32_t add
|
|||
static void LR35902DebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
|
||||
static void LR35902DebuggerCheckBreakpoints(struct mDebuggerPlatform*);
|
||||
static bool LR35902DebuggerHasBreakpoints(struct mDebuggerPlatform*);
|
||||
static void LR35902DebuggerTrace(struct mDebuggerPlatform*, char* out, size_t* length);
|
||||
|
||||
struct mDebuggerPlatform* LR35902DebuggerPlatformCreate(void) {
|
||||
struct mDebuggerPlatform* platform = (struct mDebuggerPlatform*) malloc(sizeof(struct LR35902Debugger));
|
||||
|
@ -60,6 +62,7 @@ struct mDebuggerPlatform* LR35902DebuggerPlatformCreate(void) {
|
|||
platform->clearWatchpoint = LR35902DebuggerClearWatchpoint;
|
||||
platform->checkBreakpoints = LR35902DebuggerCheckBreakpoints;
|
||||
platform->hasBreakpoints = LR35902DebuggerHasBreakpoints;
|
||||
platform->trace = LR35902DebuggerTrace;
|
||||
return platform;
|
||||
}
|
||||
|
||||
|
@ -137,3 +140,31 @@ static void LR35902DebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t
|
|||
LR35902DebuggerRemoveMemoryShim(debugger);
|
||||
}
|
||||
}
|
||||
|
||||
static void LR35902DebuggerTrace(struct mDebuggerPlatform* d, char* out, size_t* length) {
|
||||
struct LR35902Debugger* debugger = (struct LR35902Debugger*) d;
|
||||
struct LR35902Core* cpu = debugger->cpu;
|
||||
|
||||
char disassembly[64];
|
||||
|
||||
struct LR35902InstructionInfo info = {{0}};
|
||||
char* disPtr = disassembly;
|
||||
uint8_t instruction;
|
||||
uint16_t address = cpu->pc;
|
||||
size_t bytesRemaining = 1;
|
||||
for (bytesRemaining = 1; bytesRemaining; --bytesRemaining) {
|
||||
instruction = debugger->d.p->core->rawRead8(debugger->d.p->core, address, -1);
|
||||
disPtr += snprintf(disPtr, sizeof(disassembly) - (disPtr - disassembly), "%02X", instruction);
|
||||
++address;
|
||||
bytesRemaining += LR35902Decode(instruction, &info);
|
||||
};
|
||||
disPtr[0] = ':';
|
||||
disPtr[1] = ' ';
|
||||
disPtr += 2;
|
||||
LR35902Disassemble(&info, disPtr, sizeof(disassembly) - (disPtr - disassembly));
|
||||
|
||||
*length = snprintf(out, *length, "A: %02X F: %02X B: %02X C: %02X D: %02X E: %02X H: %02X L: %02X SP: %04X PC: %04X | %s",
|
||||
cpu->a, cpu->f.packed, cpu->b, cpu->c,
|
||||
cpu->d, cpu->e, cpu->h, cpu->l,
|
||||
cpu->sp, cpu->pc, disassembly);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
find_program(PYTHON python)
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in ${CMAKE_CURRENT_BINARY_DIR}/setup.py)
|
||||
|
||||
get_property(INCLUDE_DIRECTORIES DIRECTORY PROPERTY INCLUDE_DIRECTORIES)
|
||||
set(INCLUDE_FLAGS)
|
||||
foreach(DIR IN LISTS INCLUDE_DIRECTORIES)
|
||||
list(APPEND INCLUDE_FLAGS "-I${DIR}")
|
||||
endforeach()
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in ${CMAKE_CURRENT_BINARY_DIR}/setup.py)
|
||||
add_custom_command(OUTPUT build/lib/${BINARY_NAME}/__init__.py
|
||||
COMMAND BINDIR=${CMAKE_CURRENT_BINARY_DIR}/.. CPPFLAGS="${INCLUDE_FLAGS}" ${PYTHON} ${CMAKE_CURRENT_BINARY_DIR}/setup.py build --build-base ${CMAKE_CURRENT_BINARY_DIR}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
|
|
|
@ -28,6 +28,7 @@ void free(void*);
|
|||
#include "flags.h"
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/core/mem-search.h>
|
||||
#include <mgba/core/tile-cache.h>
|
||||
|
||||
#define PYEXPORT extern "Python+C"
|
||||
|
|
|
@ -21,6 +21,7 @@ ffi.set_source("mgba._pylib", """
|
|||
#include <mgba-util/common.h>
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba/core/log.h>
|
||||
#include <mgba/core/mem-search.h>
|
||||
#include <mgba/core/tile-cache.h>
|
||||
#include <mgba/internal/arm/arm.h>
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
|
|
|
@ -67,10 +67,53 @@ class MemoryView(object):
|
|||
self._addrCheck(address)
|
||||
self._rawWrite(self._core, self._base + address, segment, value & self._mask)
|
||||
|
||||
|
||||
class MemorySearchResult(object):
|
||||
def __init__(self, memory, result):
|
||||
self.address = result.address
|
||||
self.segment = result.segment
|
||||
self.guessDivisor = result.guessDivisor
|
||||
self.type = result.type
|
||||
|
||||
if result.type == Memory.SEARCH_8:
|
||||
self._memory = memory.u8
|
||||
elif result.type == Memory.SEARCH_16:
|
||||
self._memory = memory.u16
|
||||
elif result.type == Memory.SEARCH_32:
|
||||
self._memory = memory.u32
|
||||
elif result.type == Memory.SEARCH_STRING:
|
||||
self._memory = memory.u8
|
||||
else:
|
||||
raise ValueError("Unknown type: %X" % result.type)
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
if self.type == Memory.SEARCH_STRING:
|
||||
raise ValueError
|
||||
return self._memory[self.address] * self.guessDivisor
|
||||
|
||||
@value.setter
|
||||
def value(self, v):
|
||||
if self.type == Memory.SEARCH_STRING:
|
||||
raise IndexError
|
||||
self._memory[self.address] = v // self.guessDivisor
|
||||
|
||||
|
||||
class Memory(object):
|
||||
SEARCH_32 = lib.mCORE_MEMORY_SEARCH_32
|
||||
SEARCH_16 = lib.mCORE_MEMORY_SEARCH_16
|
||||
SEARCH_8 = lib.mCORE_MEMORY_SEARCH_8
|
||||
SEARCH_STRING = lib.mCORE_MEMORY_SEARCH_STRING
|
||||
SEARCH_GUESS = lib.mCORE_MEMORY_SEARCH_GUESS
|
||||
|
||||
READ = lib.mCORE_MEMORY_READ
|
||||
WRITE = lib.mCORE_MEMORY_READ
|
||||
RW = lib.mCORE_MEMORY_RW
|
||||
|
||||
def __init__(self, core, size, base=0):
|
||||
self.size = size
|
||||
self.base = base
|
||||
self._core = core
|
||||
|
||||
self.u8 = MemoryView(core, 1, size, base, "u")
|
||||
self.u16 = MemoryView(core, 2, size, base, "u")
|
||||
|
@ -81,3 +124,32 @@ class Memory(object):
|
|||
|
||||
def __len__(self):
|
||||
return self._size
|
||||
|
||||
def search(self, value, type=SEARCH_GUESS, flags=RW, limit=10000, old_results=[]):
|
||||
results = ffi.new("struct mCoreMemorySearchResults*")
|
||||
lib.mCoreMemorySearchResultsInit(results, len(old_results))
|
||||
params = ffi.new("struct mCoreMemorySearchParams*")
|
||||
params.memoryFlags = flags
|
||||
params.type = type
|
||||
if type == self.SEARCH_8:
|
||||
params.value8 = int(value)
|
||||
elif type == self.SEARCH_16:
|
||||
params.value16 = int(value)
|
||||
elif type == self.SEARCH_32:
|
||||
params.value32 = int(value)
|
||||
else:
|
||||
params.valueStr = ffi.new("char[]", str(value).encode("ascii"))
|
||||
|
||||
for result in old_results:
|
||||
r = lib.mCoreMemorySearchResultsAppend(results)
|
||||
r.address = result.address
|
||||
r.segment = result.segment
|
||||
r.guessDivisor = result.guessDivisor
|
||||
r.type = result.type
|
||||
if old_results:
|
||||
lib.mCoreMemorySearchRepeat(self._core, params, results)
|
||||
else:
|
||||
lib.mCoreMemorySearch(self._core, params, results, limit)
|
||||
new_results = [MemorySearchResult(self, lib.mCoreMemorySearchResultsGetPointer(results, i)) for i in range(lib.mCoreMemorySearchResultsSize(results))]
|
||||
lib.mCoreMemorySearchResultsDeinit(results)
|
||||
return new_results
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
from setuptools import setup
|
||||
import re
|
||||
import os
|
||||
|
||||
os.environ["BINDIR"] = "${CMAKE_BINARY_DIR}"
|
||||
os.environ["CPPFLAGS"] = " ".join([d for d in "${INCLUDE_FLAGS}".split(";") if d])
|
||||
os.chdir("${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
classifiers = [
|
||||
"Programming Language :: C",
|
||||
|
|
|
@ -85,6 +85,7 @@ set(SOURCE_FILES
|
|||
LogController.cpp
|
||||
LogView.cpp
|
||||
MemoryModel.cpp
|
||||
MemorySearch.cpp
|
||||
MemoryView.cpp
|
||||
MessagePainter.cpp
|
||||
MultiplayerController.cpp
|
||||
|
@ -115,6 +116,7 @@ set(UI_FILES
|
|||
IOViewer.ui
|
||||
LoadSaveState.ui
|
||||
LogView.ui
|
||||
MemorySearch.ui
|
||||
MemoryView.ui
|
||||
ObjView.ui
|
||||
OverrideView.ui
|
||||
|
|
|
@ -40,7 +40,7 @@ using namespace std;
|
|||
GameController::GameController(QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_audioProcessor(AudioProcessor::create())
|
||||
, m_saveStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS | SAVESTATE_RTC)
|
||||
, m_saveStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS | SAVESTATE_RTC | SAVESTATE_METADATA)
|
||||
, m_loadStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_RTC)
|
||||
{
|
||||
#ifdef M_CORE_GBA
|
||||
|
|
|
@ -188,7 +188,7 @@ void LoadSaveState::loadState(int slot) {
|
|||
return;
|
||||
}
|
||||
|
||||
QDateTime creation/*(QDateTime::fromMSecsSinceEpoch(state->creationUsec / 1000LL))*/; // TODO
|
||||
QDateTime creation;
|
||||
QImage stateImage;
|
||||
|
||||
unsigned width, height;
|
||||
|
@ -198,6 +198,12 @@ void LoadSaveState::loadState(int slot) {
|
|||
stateImage = QImage((uchar*) item.data, width, height, QImage::Format_ARGB32).rgbSwapped();
|
||||
}
|
||||
|
||||
if (mStateExtdataGet(&extdata, EXTDATA_META_TIME, &item) && item.size == sizeof(uint64_t)) {
|
||||
uint64_t creationUsec;
|
||||
LOAD_64LE(creationUsec, 0, item.data);
|
||||
creation = QDateTime::fromMSecsSinceEpoch(creationUsec / 1000LL);
|
||||
}
|
||||
|
||||
if (!stateImage.isNull()) {
|
||||
QPixmap statePixmap;
|
||||
statePixmap.convertFromImage(stateImage);
|
||||
|
|
|
@ -0,0 +1,187 @@
|
|||
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "MemorySearch.h"
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
|
||||
#include "GameController.h"
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
MemorySearch::MemorySearch(GameController* controller, QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_controller(controller)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
mCoreMemorySearchResultsInit(&m_results, 0);
|
||||
connect(m_ui.search, &QPushButton::clicked, this, &MemorySearch::search);
|
||||
connect(m_ui.searchWithin, &QPushButton::clicked, this, &MemorySearch::searchWithin);
|
||||
connect(m_ui.refresh, &QPushButton::clicked, this, &MemorySearch::refresh);
|
||||
connect(m_ui.numHex, &QPushButton::clicked, this, &MemorySearch::refresh);
|
||||
connect(m_ui.numDec, &QPushButton::clicked, this, &MemorySearch::refresh);
|
||||
}
|
||||
|
||||
MemorySearch::~MemorySearch() {
|
||||
mCoreMemorySearchResultsDeinit(&m_results);
|
||||
}
|
||||
|
||||
bool MemorySearch::createParams(mCoreMemorySearchParams* params) {
|
||||
params->memoryFlags = mCORE_MEMORY_RW;
|
||||
mCore* core = m_controller->thread()->core;
|
||||
|
||||
QByteArray string;
|
||||
bool ok = false;
|
||||
if (m_ui.typeNum->isChecked()) {
|
||||
if (m_ui.bits8->isChecked()) {
|
||||
params->type = mCORE_MEMORY_SEARCH_8;
|
||||
}
|
||||
if (m_ui.bits16->isChecked()) {
|
||||
params->type = mCORE_MEMORY_SEARCH_16;
|
||||
}
|
||||
if (m_ui.bits32->isChecked()) {
|
||||
params->type = mCORE_MEMORY_SEARCH_32;
|
||||
}
|
||||
if (m_ui.numHex->isChecked()) {
|
||||
bool ok;
|
||||
uint32_t v = m_ui.value->text().toUInt(&ok, 16);
|
||||
if (ok) {
|
||||
switch (params->type) {
|
||||
case mCORE_MEMORY_SEARCH_8:
|
||||
ok = v < 0x100;
|
||||
params->value8 = v;
|
||||
break;
|
||||
case mCORE_MEMORY_SEARCH_16:
|
||||
ok = v < 0x10000;
|
||||
params->value16 = v;
|
||||
break;
|
||||
case mCORE_MEMORY_SEARCH_32:
|
||||
params->value32 = v;
|
||||
break;
|
||||
default:
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_ui.numDec->isChecked()) {
|
||||
uint32_t v = m_ui.value->text().toUInt(&ok, 10);
|
||||
if (ok) {
|
||||
switch (params->type) {
|
||||
case mCORE_MEMORY_SEARCH_8:
|
||||
ok = v < 0x100;
|
||||
params->value8 = v;
|
||||
break;
|
||||
case mCORE_MEMORY_SEARCH_16:
|
||||
ok = v < 0x10000;
|
||||
params->value16 = v;
|
||||
break;
|
||||
case mCORE_MEMORY_SEARCH_32:
|
||||
params->value32 = v;
|
||||
break;
|
||||
default:
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_ui.numGuess->isChecked()) {
|
||||
params->type = mCORE_MEMORY_SEARCH_GUESS;
|
||||
m_string = m_ui.value->text().toLocal8Bit();
|
||||
params->valueStr = m_string.constData();
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
if (m_ui.typeStr->isChecked()) {
|
||||
params->type = mCORE_MEMORY_SEARCH_STRING;
|
||||
m_string = m_ui.value->text().toLocal8Bit();
|
||||
params->valueStr = m_string.constData();
|
||||
ok = true;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
void MemorySearch::search() {
|
||||
mCoreMemorySearchResultsClear(&m_results);
|
||||
|
||||
mCoreMemorySearchParams params;
|
||||
|
||||
GameController::Interrupter interrupter(m_controller);
|
||||
if (!m_controller->isLoaded()) {
|
||||
return;
|
||||
}
|
||||
mCore* core = m_controller->thread()->core;
|
||||
|
||||
if (createParams(¶ms)) {
|
||||
mCoreMemorySearch(core, ¶ms, &m_results, LIMIT);
|
||||
}
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
void MemorySearch::searchWithin() {
|
||||
mCoreMemorySearchParams params;
|
||||
|
||||
GameController::Interrupter interrupter(m_controller);
|
||||
if (!m_controller->isLoaded()) {
|
||||
return;
|
||||
}
|
||||
mCore* core = m_controller->thread()->core;
|
||||
|
||||
if (createParams(¶ms)) {
|
||||
mCoreMemorySearchRepeat(core, ¶ms, &m_results);
|
||||
}
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
void MemorySearch::refresh() {
|
||||
GameController::Interrupter interrupter(m_controller);
|
||||
if (!m_controller->isLoaded()) {
|
||||
return;
|
||||
}
|
||||
mCore* core = m_controller->thread()->core;
|
||||
|
||||
m_ui.results->clearContents();
|
||||
m_ui.results->setRowCount(mCoreMemorySearchResultsSize(&m_results));
|
||||
for (size_t i = 0; i < mCoreMemorySearchResultsSize(&m_results); ++i) {
|
||||
mCoreMemorySearchResult* result = mCoreMemorySearchResultsGetPointer(&m_results, i);
|
||||
QTableWidgetItem* item = new QTableWidgetItem(QString("%1").arg(result->address, 8, 16, QChar('0')));
|
||||
m_ui.results->setItem(i, 0, item);
|
||||
if (m_ui.numHex->isChecked()) {
|
||||
switch (result->type) {
|
||||
case mCORE_MEMORY_SEARCH_8:
|
||||
item = new QTableWidgetItem(QString("%1").arg(core->rawRead8(core, result->address, result->segment), 2, 16, QChar('0')));
|
||||
break;
|
||||
case mCORE_MEMORY_SEARCH_16:
|
||||
item = new QTableWidgetItem(QString("%1").arg(core->rawRead16(core, result->address, result->segment), 4, 16, QChar('0')));
|
||||
break;
|
||||
case mCORE_MEMORY_SEARCH_GUESS:
|
||||
case mCORE_MEMORY_SEARCH_32:
|
||||
item = new QTableWidgetItem(QString("%1").arg(core->rawRead32(core, result->address, result->segment), 8, 16, QChar('0')));
|
||||
break;
|
||||
case mCORE_MEMORY_SEARCH_STRING:
|
||||
item = new QTableWidgetItem("?"); // TODO
|
||||
}
|
||||
} else {
|
||||
switch (result->type) {
|
||||
case mCORE_MEMORY_SEARCH_8:
|
||||
item = new QTableWidgetItem(QString::number(core->rawRead8(core, result->address, result->segment)));
|
||||
break;
|
||||
case mCORE_MEMORY_SEARCH_16:
|
||||
item = new QTableWidgetItem(QString::number(core->rawRead16(core, result->address, result->segment)));
|
||||
break;
|
||||
case mCORE_MEMORY_SEARCH_GUESS:
|
||||
case mCORE_MEMORY_SEARCH_32:
|
||||
item = new QTableWidgetItem(QString::number(core->rawRead32(core, result->address, result->segment)));
|
||||
break;
|
||||
case mCORE_MEMORY_SEARCH_STRING:
|
||||
item = new QTableWidgetItem("?"); // TODO
|
||||
}
|
||||
}
|
||||
m_ui.results->setItem(i, 1, item);
|
||||
}
|
||||
m_ui.results->sortItems(0);
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef QGBA_MEMORY_SEARCH
|
||||
#define QGBA_MEMORY_SEARCH
|
||||
|
||||
#include "ui_MemorySearch.h"
|
||||
|
||||
#include <mgba/core/mem-search.h>
|
||||
|
||||
namespace QGBA {
|
||||
|
||||
class GameController;
|
||||
|
||||
class MemorySearch : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static constexpr size_t LIMIT = 10000;
|
||||
|
||||
MemorySearch(GameController* controller, QWidget* parent = nullptr);
|
||||
~MemorySearch();
|
||||
|
||||
public slots:
|
||||
void refresh();
|
||||
void search();
|
||||
void searchWithin();
|
||||
|
||||
private:
|
||||
bool createParams(mCoreMemorySearchParams*);
|
||||
|
||||
Ui::MemorySearch m_ui;
|
||||
|
||||
GameController* m_controller;
|
||||
|
||||
mCoreMemorySearchResults m_results;
|
||||
QByteArray m_string;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,223 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MemorySearch</class>
|
||||
<widget class="QWidget" name="MemorySearch">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>631</width>
|
||||
<height>378</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>540</width>
|
||||
<height>241</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="1">
|
||||
<widget class="QTableWidget" name="results">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="showGrid">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Address</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Current Value</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Value</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="value"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QRadioButton" name="typeNum">
|
||||
<property name="text">
|
||||
<string>Numeric</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">type</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QRadioButton" name="typeStr">
|
||||
<property name="text">
|
||||
<string>Text</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">type</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Width</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QRadioButton" name="bits8">
|
||||
<property name="text">
|
||||
<string>1 Byte (8-bit)</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">width</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QRadioButton" name="bits16">
|
||||
<property name="text">
|
||||
<string>2 Bytes (16-bit)</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">width</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QRadioButton" name="bits32">
|
||||
<property name="text">
|
||||
<string>4 Bytes (32-bit)</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">width</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Number type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QRadioButton" name="numHex">
|
||||
<property name="text">
|
||||
<string>Hexadecimal</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QRadioButton" name="numDec">
|
||||
<property name="text">
|
||||
<string>Decimal</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1">
|
||||
<widget class="QRadioButton" name="numGuess">
|
||||
<property name="text">
|
||||
<string>Guess</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Close</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="search">
|
||||
<property name="text">
|
||||
<string>Search</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="searchWithin">
|
||||
<property name="text">
|
||||
<string>Search Within</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="viewMem">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>View in Memory View</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="refresh">
|
||||
<property name="text">
|
||||
<string>Refresh</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
<buttongroups>
|
||||
<buttongroup name="type"/>
|
||||
<buttongroup name="width"/>
|
||||
<buttongroup name="numType"/>
|
||||
</buttongroups>
|
||||
</ui>
|
|
@ -9,53 +9,9 @@
|
|||
#include "GameController.h"
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
#ifdef M_CORE_GBA
|
||||
#include <mgba/internal/gba/memory.h>
|
||||
#endif
|
||||
#ifdef M_CORE_GB
|
||||
#include <mgba/internal/gb/memory.h>
|
||||
#endif
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
struct IndexInfo {
|
||||
const char* name;
|
||||
const char* longName;
|
||||
uint32_t base;
|
||||
uint32_t size;
|
||||
int maxSegment;
|
||||
};
|
||||
#ifdef M_CORE_GBA
|
||||
const static struct IndexInfo indexInfoGBA[] = {
|
||||
{ "All", "All", 0, 0x10000000 },
|
||||
{ "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS },
|
||||
{ "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, SIZE_WORKING_RAM },
|
||||
{ "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, SIZE_WORKING_IRAM },
|
||||
{ "MMIO", "Memory-Mapped I/O", BASE_IO, SIZE_IO },
|
||||
{ "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, SIZE_PALETTE_RAM },
|
||||
{ "VRAM", "Video RAM (96kiB)", BASE_VRAM, SIZE_VRAM },
|
||||
{ "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, SIZE_OAM },
|
||||
{ "ROM", "Game Pak (32MiB)", BASE_CART0, SIZE_CART0 },
|
||||
{ "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, SIZE_CART1 },
|
||||
{ "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, SIZE_CART2 },
|
||||
{ "SRAM", "Static RAM (64kiB)", BASE_CART_SRAM, SIZE_CART_SRAM },
|
||||
{ nullptr, nullptr, 0, 0, 0 }
|
||||
};
|
||||
#endif
|
||||
#ifdef M_CORE_GB
|
||||
const static struct IndexInfo indexInfoGB[] = {
|
||||
{ "All", "All", 0, 0x10000 },
|
||||
{ "ROM", "Game Pak (32kiB)", GB_BASE_CART_BANK0, GB_SIZE_CART_BANK0 * 2, 511 },
|
||||
{ "VRAM", "Video RAM (8kiB)", GB_BASE_VRAM, GB_SIZE_VRAM, 1 },
|
||||
{ "SRAM", "External RAM (8kiB)", GB_BASE_EXTERNAL_RAM, GB_SIZE_EXTERNAL_RAM, 3 },
|
||||
{ "WRAM", "Working RAM (8kiB)", GB_BASE_WORKING_RAM_BANK0, GB_SIZE_WORKING_RAM_BANK0 * 2, 7 },
|
||||
{ "OAM", "OBJ Attribute Memory", GB_BASE_OAM, GB_SIZE_OAM },
|
||||
{ "IO", "Memory-Mapped I/O", GB_BASE_IO, GB_SIZE_IO },
|
||||
{ "HRAM", "High RAM", GB_BASE_HRAM, GB_SIZE_HRAM },
|
||||
{ nullptr, nullptr, 0, 0, 0 }
|
||||
};
|
||||
#endif
|
||||
|
||||
MemoryView::MemoryView(GameController* controller, QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_controller(controller)
|
||||
|
@ -65,21 +21,8 @@ MemoryView::MemoryView(GameController* controller, QWidget* parent)
|
|||
m_ui.hexfield->setController(controller);
|
||||
|
||||
mCore* core = m_controller->thread()->core;
|
||||
const IndexInfo* info = nullptr;
|
||||
switch (core->platform(core)) {
|
||||
#ifdef M_CORE_GBA
|
||||
case PLATFORM_GBA:
|
||||
info = indexInfoGBA;
|
||||
break;
|
||||
#endif
|
||||
#ifdef M_CORE_GB
|
||||
case PLATFORM_GB:
|
||||
info = indexInfoGB;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
const mCoreMemoryBlock* info;
|
||||
size_t nBlocks = core->listMemoryBlocks(core, &info);
|
||||
|
||||
connect(m_ui.regions, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
this, &MemoryView::setIndex);
|
||||
|
@ -87,7 +30,10 @@ MemoryView::MemoryView(GameController* controller, QWidget* parent)
|
|||
this, &MemoryView::setSegment);
|
||||
|
||||
if (info) {
|
||||
for (size_t i = 0; info[i].name; ++i) {
|
||||
for (size_t i = 0; i < nBlocks; ++i) {
|
||||
if (!(info[i].flags & (mCORE_MEMORY_MAPPED | mCORE_MEMORY_VIRTUAL))) {
|
||||
continue;
|
||||
}
|
||||
m_ui.regions->addItem(tr(info[i].longName));
|
||||
}
|
||||
}
|
||||
|
@ -116,44 +62,22 @@ MemoryView::MemoryView(GameController* controller, QWidget* parent)
|
|||
|
||||
void MemoryView::setIndex(int index) {
|
||||
mCore* core = m_controller->thread()->core;
|
||||
IndexInfo info;
|
||||
switch (core->platform(core)) {
|
||||
#ifdef M_CORE_GBA
|
||||
case PLATFORM_GBA:
|
||||
info = indexInfoGBA[index];
|
||||
break;
|
||||
#endif
|
||||
#ifdef M_CORE_GB
|
||||
case PLATFORM_GB:
|
||||
info = indexInfoGB[index];
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return;
|
||||
}
|
||||
const mCoreMemoryBlock* blocks;
|
||||
size_t nBlocks = core->listMemoryBlocks(core, &blocks);
|
||||
const mCoreMemoryBlock& info = blocks[index];
|
||||
|
||||
m_ui.segments->setValue(-1);
|
||||
m_ui.segments->setVisible(info.maxSegment > 0);
|
||||
m_ui.segments->setMaximum(info.maxSegment);
|
||||
m_ui.hexfield->setRegion(info.base, info.size, info.name);
|
||||
m_ui.hexfield->setRegion(info.start, info.end - info.start, info.shortName);
|
||||
}
|
||||
|
||||
void MemoryView::setSegment(int segment) {
|
||||
mCore* core = m_controller->thread()->core;
|
||||
IndexInfo info;
|
||||
switch (core->platform(core)) {
|
||||
#ifdef M_CORE_GBA
|
||||
case PLATFORM_GBA:
|
||||
info = indexInfoGBA[m_ui.regions->currentIndex()];
|
||||
break;
|
||||
#endif
|
||||
#ifdef M_CORE_GB
|
||||
case PLATFORM_GB:
|
||||
info = indexInfoGB[m_ui.regions->currentIndex()];
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return;
|
||||
}
|
||||
const mCoreMemoryBlock* blocks;
|
||||
size_t nBlocks = core->listMemoryBlocks(core, &blocks);
|
||||
const mCoreMemoryBlock& info = blocks[m_ui.regions->currentIndex()];
|
||||
|
||||
m_ui.hexfield->setSegment(info.maxSegment < segment ? info.maxSegment : segment);
|
||||
}
|
||||
|
||||
|
|
|
@ -224,7 +224,7 @@ void SettingsView::updateConfig() {
|
|||
loadState |= m_ui.loadStateCheats->isChecked() ? SAVESTATE_CHEATS : 0;
|
||||
saveSetting("loadStateExtdata", loadState);
|
||||
|
||||
int saveState = SAVESTATE_RTC;
|
||||
int saveState = SAVESTATE_RTC | SAVESTATE_METADATA;
|
||||
saveState |= m_ui.saveStateScreenshot->isChecked() ? SAVESTATE_SCREENSHOT : 0;
|
||||
saveState |= m_ui.saveStateSave->isChecked() ? SAVESTATE_SAVEDATA : 0;
|
||||
saveState |= m_ui.saveStateCheats->isChecked() ? SAVESTATE_CHEATS : 0;
|
||||
|
@ -314,7 +314,7 @@ void SettingsView::reloadConfig() {
|
|||
|
||||
int saveState = loadSetting("saveStateExtdata").toInt(&ok);
|
||||
if (!ok) {
|
||||
saveState = SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS | SAVESTATE_RTC;
|
||||
saveState = SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS | SAVESTATE_RTC | SAVESTATE_METADATA;
|
||||
}
|
||||
m_ui.saveStateScreenshot->setChecked(saveState & SAVESTATE_SCREENSHOT);
|
||||
m_ui.saveStateSave->setChecked(saveState & SAVESTATE_SAVEDATA);
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "LoadSaveState.h"
|
||||
#include "LogView.h"
|
||||
#include "MultiplayerController.h"
|
||||
#include "MemorySearch.h"
|
||||
#include "MemoryView.h"
|
||||
#include "OverrideView.h"
|
||||
#include "ObjView.h"
|
||||
|
@ -1501,6 +1502,11 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
m_gameActions.append(memoryView);
|
||||
addControlledAction(toolsMenu, memoryView, "memoryView");
|
||||
|
||||
QAction* memorySearch = new QAction(tr("Search memory..."), toolsMenu);
|
||||
connect(memorySearch, &QAction::triggered, openTView<MemorySearch>());
|
||||
m_gameActions.append(memorySearch);
|
||||
addControlledAction(toolsMenu, memorySearch, "memorySearch");
|
||||
|
||||
#ifdef M_CORE_GBA
|
||||
QAction* ioViewer = new QAction(tr("View &I/O registers..."), toolsMenu);
|
||||
connect(ioViewer, &QAction::triggered, openTView<IOViewer>());
|
||||
|
|
|
@ -181,7 +181,7 @@ void LibraryController::refresh() {
|
|||
}
|
||||
|
||||
void LibraryController::selectLastBootedGame() {
|
||||
if (!m_config) {
|
||||
if (!m_config || m_config->getMRU().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
const QString lastfile = m_config->getMRU().first();
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include <mgba/core/input.h>
|
||||
#include <mgba/core/serialize.h>
|
||||
#include <mgba/core/thread.h>
|
||||
#include <mgba/internal/debugger/debugger.h>
|
||||
#include <mgba/debugger/debugger.h>
|
||||
#include <mgba/internal/gba/input.h>
|
||||
#include <mgba-util/configuration.h>
|
||||
#include <mgba-util/formatting.h>
|
||||
|
|
Loading…
Reference in New Issue