Merge branch 'master' into feature/input-revamp

This commit is contained in:
Vicki Pfau 2017-06-25 17:40:23 -07:00
commit ab07c280fe
99 changed files with 2601 additions and 475 deletions

20
CHANGES
View File

@ -21,8 +21,10 @@ Features:
- Debugger: Segment/bank support - Debugger: Segment/bank support
- GB: Symbol table support - GB: Symbol table support
- GB MBC: Add MBC1 multicart support - GB MBC: Add MBC1 multicart support
- GBA: Implement keypad interrupts - Implement keypad interrupts
- LR35902: Watchpoints - LR35902: Watchpoints
- Memory search
- Debugger: Execution tracing
Bugfixes: Bugfixes:
- LR35902: Fix core never exiting with certain event patterns - LR35902: Fix core never exiting with certain event patterns
- GB Timer: Improve DIV reset behavior - GB Timer: Improve DIV reset behavior
@ -64,6 +66,13 @@ Bugfixes:
- LR35902: Fix decoding LD r, $imm and 0-valued immediates (fixes mgba.io/i/735) - LR35902: Fix decoding LD r, $imm and 0-valued immediates (fixes mgba.io/i/735)
- GB: Fix STAT blocking - GB: Fix STAT blocking
- GB MBC: Fix swapping carts not detect new MBC - 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
- GBA Savedata: Update and fix Sharkport importing (fixes mgba.io/i/658)
- OpenGL: Fix some shaders causing offset graphics
- Qt: Fix game unpausing after frame advancing and refocusing
- GB Timer: Fix sub-M-cycle DIV reset timing and edge triggering
Misc: Misc:
- SDL: Remove scancode key input - SDL: Remove scancode key input
- GBA Video: Clean up unused timers - GBA Video: Clean up unused timers
@ -124,6 +133,15 @@ Misc:
- Util: Tune patch-fast extent sizes - Util: Tune patch-fast extent sizes
- Qt: Relax hard dependency on OpenGL - Qt: Relax hard dependency on OpenGL
- GB Video: Improved video timings - 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
- GB: Trust ROM header for number of SRAM banks (fixes mgba.io/i/726)
- Core: Config values can now be hexadecimal
- GB: Reset with initial state of DIV register
- GB MBC: New MBC7 implementation
- Qt: Better highlight active key in control binding
0.5.2: (2016-12-31) 0.5.2: (2016-12-31)
Bugfixes: Bugfixes:

View File

@ -155,8 +155,6 @@ add_custom_target(version-info ALL
include(${CMAKE_CURRENT_SOURCE_DIR}/version.cmake) 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/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) list(APPEND UTIL_SRC ${CMAKE_CURRENT_BINARY_DIR}/version.c)
source_group("Generated sources" FILES ${CMAKE_CURRENT_BINARY_DIR}/version.c) source_group("Generated sources" FILES ${CMAKE_CURRENT_BINARY_DIR}/version.c)
@ -200,6 +198,7 @@ if(WIN32)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
endif() endif()
elseif(UNIX) elseif(UNIX)
set(USE_PTHREADS ON)
add_definitions(-DUSE_PTHREADS) add_definitions(-DUSE_PTHREADS)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux") if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
@ -844,6 +843,10 @@ if(BUILD_EXAMPLE)
endif() endif()
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 # Packaging
set(CPACK_PACKAGE_VERSION ${VERSION_STRING}) set(CPACK_PACKAGE_VERSION ${VERSION_STRING})
set(CPACK_PACKAGE_VERSION_MAJOR ${LIB_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MAJOR ${LIB_VERSION_MAJOR})

View File

@ -29,7 +29,7 @@ Features
- Remappable controls for both keyboards and gamepads. - Remappable controls for both keyboards and gamepads.
- Loading from ZIP and 7z files. - Loading from ZIP and 7z files.
- IPS, UPS and BPS patch support. - IPS, UPS and BPS patch support.
- Game debugging via a command-line interface (not available with Qt port) and GDB remote support, compatible with IDA Pro. - Game debugging via a command-line interface and GDB remote support, compatible with IDA Pro.
- Configurable emulation rewinding. - Configurable emulation rewinding.
- Support for loading and exporting GameShark and Action Replay snapshots. - Support for loading and exporting GameShark and Action Replay snapshots.
- Cores available for RetroArch/Libretro and OpenEmu. - Cores available for RetroArch/Libretro and OpenEmu.
@ -94,6 +94,16 @@ Compiling requires using CMake 2.8.11 or newer. GCC and Clang are both known to
This will build and install mGBA into `/usr/bin` and `/usr/lib`. Dependencies that are installed will be automatically detected, and features that are disabled if the dependencies are not found will be shown after running the `cmake` command after warnings about being unable to find them. This will build and install mGBA into `/usr/bin` and `/usr/lib`. Dependencies that are installed will be automatically detected, and features that are disabled if the dependencies are not found will be shown after running the `cmake` command after warnings about being unable to find them.
If you are on macOS, the steps are a little different. Assuming you are using the homebrew package manager, the recommended commands to obtain the dependencies and build are:
brew install cmake ffmpeg imagemagick libzip qt5 sdl2 libedit
mkdir build
cd build
cmake -DCMAKE_PREFIX_PATH=`brew --prefix qt5` ..
make
Note that you should not do a `make install` on macOS, as it will not work properly.
#### Windows developer building #### Windows developer building
To build on Windows for development, using MSYS2 is recommended. Follow the installation steps found on their [website](https://msys2.github.io). Make sure you're running the 32-bit version ("MSYS2 MinGW 32-bit") (or the 64-bit version "MSYS2 MinGW 64-bit" if you want to build for x86_64) and run this additional command (including the braces) to install the needed dependencies (please note that this involves downloading over 500MiB of packages, so it will take a long time): To build on Windows for development, using MSYS2 is recommended. Follow the installation steps found on their [website](https://msys2.github.io). Make sure you're running the 32-bit version ("MSYS2 MinGW 32-bit") (or the 64-bit version "MSYS2 MinGW 64-bit" if you want to build for x86_64) and run this additional command (including the braces) to install the needed dependencies (please note that this involves downloading over 500MiB of packages, so it will take a long time):
@ -133,7 +143,7 @@ mGBA has no hard dependencies, however, the following optional dependencies are
- ImageMagick: for GIF recording. - ImageMagick: for GIF recording.
- SQLite3: for game databases. - SQLite3: for game databases.
Both libpng and zlib are included with the emulator, so they do not need to be externally compiled first. SQLite3, libpng, and zlib are included with the emulator, so they do not need to be externally compiled first.
Footnotes Footnotes
--------- ---------
@ -145,7 +155,7 @@ Footnotes
<a name="flashdetect">[2]</a> Flash memory size detection does not work in some cases. These can be configured at runtime, but filing a bug is recommended if such a case is encountered. <a name="flashdetect">[2]</a> Flash memory size detection does not work in some cases. These can be configured at runtime, but filing a bug is recommended if such a case is encountered.
<a name="osxver">[3]</a> 10.7 is only needed for the Qt port. The SDL port is known to work on 10.6, and may work on older. <a name="osxver">[3]</a> 10.7 is only needed for the Qt port. The SDL port is known to work on 10.5, and may work on older.
[downloads]: http://mgba.io/downloads.html [downloads]: http://mgba.io/downloads.html
[source]: https://github.com/mgba-emu/mgba/ [source]: https://github.com/mgba-emu/mgba/
@ -153,7 +163,7 @@ Footnotes
Copyright Copyright
--------- ---------
mGBA is Copyright © 2013 2016 Jeffrey Pfau. It is distributed under the [Mozilla Public License version 2.0](https://www.mozilla.org/MPL/2.0/). A copy of the license is available in the distributed LICENSE file. mGBA is Copyright © 2013 2017 Jeffrey Pfau. It is distributed under the [Mozilla Public License version 2.0](https://www.mozilla.org/MPL/2.0/). A copy of the license is available in the distributed LICENSE file.
mGBA contains the following third-party libraries: mGBA contains the following third-party libraries:

View File

@ -19,8 +19,7 @@ CXX_GUARD_START
#endif #endif
#include <mgba/core/interface.h> #include <mgba/core/interface.h>
#ifdef USE_DEBUGGERS #ifdef USE_DEBUGGERS
// TODO: Fix layering violation #include <mgba/debugger/debugger.h>
#include <mgba/internal/debugger/debugger.h>
#endif #endif
enum mPlatform { enum mPlatform {
@ -132,6 +131,9 @@ struct mCore {
void (*rawWrite16)(struct mCore*, uint32_t address, int segment, uint16_t); void (*rawWrite16)(struct mCore*, uint32_t address, int segment, uint16_t);
void (*rawWrite32)(struct mCore*, uint32_t address, int segment, uint32_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 #ifdef USE_DEBUGGERS
bool (*supportsDebuggerType)(struct mCore*, enum mDebuggerType); bool (*supportsDebuggerType)(struct mCore*, enum mDebuggerType);
struct mDebuggerPlatform* (*debuggerPlatform)(struct mCore*); struct mDebuggerPlatform* (*debuggerPlatform)(struct mCore*);

View File

@ -31,6 +31,10 @@ typedef uint32_t color_t;
#define M_G8(X) (((((X) >> 2) & 0xF8) * 0x21) >> 5) #define M_G8(X) (((((X) >> 2) & 0xF8) * 0x21) >> 5)
#define M_B8(X) (((((X) >> 7) & 0xF8) * 0x21) >> 5) #define M_B8(X) (((((X) >> 7) & 0xF8) * 0x21) >> 5)
#define M_RGB5_TO_BGR8(X) ((M_R5(X) << 3) | (M_G5(X) << 11) | (M_B5(X) << 19))
#define M_RGB8_TO_BGR5(X) ((((X) & 0xF8) >> 3) | (((X) & 0xF800) >> 6) | (((X) & 0xF80000) >> 9))
#define M_RGB8_TO_RGB5(X) ((((X) & 0xF8) << 7) | (((X) & 0xF800) >> 6) | (((X) & 0xF80000) >> 19))
struct blip_t; struct blip_t;
struct mCoreCallbacks { struct mCoreCallbacks {
@ -112,6 +116,27 @@ struct mCoreChannelInfo {
const char* visibleType; 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 CXX_GUARD_END
#endif #endif

View File

@ -36,6 +36,7 @@ void mLibraryDestroy(struct mLibrary*);
struct VDir; struct VDir;
struct VFile; struct VFile;
void mLibraryLoadDirectory(struct mLibrary* library, const char* base); void mLibraryLoadDirectory(struct mLibrary* library, const char* base);
void mLibraryClear(struct mLibrary* library);
size_t mLibraryCount(struct mLibrary* library, const struct mLibraryEntry* constraints); size_t mLibraryCount(struct mLibrary* library, const struct mLibraryEntry* constraints);
size_t mLibraryGetEntries(struct mLibrary* library, struct mLibraryListing* out, size_t numEntries, size_t offset, const struct mLibraryEntry* constraints); size_t mLibraryGetEntries(struct mLibrary* library, struct mLibraryListing* out, size_t numEntries, size_t offset, const struct mLibraryEntry* constraints);

View File

@ -0,0 +1,49 @@
/* Copyright (c) 2013-2017 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#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

View File

@ -16,6 +16,7 @@ enum mStateExtdataTag {
EXTDATA_SAVEDATA = 2, EXTDATA_SAVEDATA = 2,
EXTDATA_CHEATS = 3, EXTDATA_CHEATS = 3,
EXTDATA_RTC = 4, EXTDATA_RTC = 4,
EXTDATA_META_TIME = 0x101,
EXTDATA_MAX EXTDATA_MAX
}; };
@ -23,6 +24,7 @@ enum mStateExtdataTag {
#define SAVESTATE_SAVEDATA 2 #define SAVESTATE_SAVEDATA 2
#define SAVESTATE_CHEATS 4 #define SAVESTATE_CHEATS 4
#define SAVESTATE_RTC 8 #define SAVESTATE_RTC 8
#define SAVESTATE_METADATA 16
struct mStateExtdataItem { struct mStateExtdataItem {
int32_t size; int32_t size;

View File

@ -38,6 +38,7 @@ bool mTimingIsScheduled(const struct mTiming* timing, const struct mTimingEvent*
int32_t mTimingTick(struct mTiming* timing, int32_t cycles); int32_t mTimingTick(struct mTiming* timing, int32_t cycles);
int32_t mTimingCurrentTime(const struct mTiming* timing); int32_t mTimingCurrentTime(const struct mTiming* timing);
int32_t mTimingNextEvent(struct mTiming* timing); int32_t mTimingNextEvent(struct mTiming* timing);
int32_t mTimingUntil(const struct mTiming* timing, const struct mTimingEvent*);
CXX_GUARD_END CXX_GUARD_END

View File

@ -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 * 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 * 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/cpu.h>
#include <mgba/core/log.h> #include <mgba/core/log.h>
#include <mgba-util/vector.h>
mLOG_DECLARE_CATEGORY(DEBUGGER); mLOG_DECLARE_CATEGORY(DEBUGGER);
@ -53,14 +52,6 @@ enum mDebuggerEntryReason {
DEBUGGER_ENTER_ILLEGAL_OP 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 { struct mDebuggerEntryInfo {
uint32_t address; uint32_t address;
union { union {
@ -92,6 +83,7 @@ struct mDebuggerPlatform {
void (*setWatchpoint)(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type); void (*setWatchpoint)(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type);
void (*clearWatchpoint)(struct mDebuggerPlatform*, uint32_t address, int segment); void (*clearWatchpoint)(struct mDebuggerPlatform*, uint32_t address, int segment);
void (*checkBreakpoints)(struct mDebuggerPlatform*); void (*checkBreakpoints)(struct mDebuggerPlatform*);
void (*trace)(struct mDebuggerPlatform*, char* out, size_t* length);
}; };
struct mDebuggerSymbols; struct mDebuggerSymbols;
@ -112,6 +104,7 @@ struct mDebugger {
struct mDebugger* mDebuggerCreate(enum mDebuggerType type, struct mCore*); struct mDebugger* mDebuggerCreate(enum mDebuggerType type, struct mCore*);
void mDebuggerAttach(struct mDebugger*, struct mCore*); void mDebuggerAttach(struct mDebugger*, struct mCore*);
void mDebuggerRun(struct mDebugger*); void mDebuggerRun(struct mDebugger*);
void mDebuggerRunFrame(struct mDebugger*);
void mDebuggerEnter(struct mDebugger*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*); void mDebuggerEnter(struct mDebugger*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*);
CXX_GUARD_END CXX_GUARD_END

View File

@ -12,7 +12,7 @@ CXX_GUARD_START
#include <mgba-util/table.h> #include <mgba-util/table.h>
#include <mgba/internal/debugger/debugger.h> #include <mgba/debugger/debugger.h>
struct mArguments { struct mArguments {
char* fname; char* fname;

View File

@ -30,6 +30,7 @@ enum GBMemoryBankControllerType {
GB_MMM01 = 0x10, GB_MMM01 = 0x10,
GB_HuC1 = 0x11, GB_HuC1 = 0x11,
GB_HuC3 = 0x12, GB_HuC3 = 0x12,
GB_POCKETCAM = 0x13,
GB_MBC3_RTC = 0x103, GB_MBC3_RTC = 0x103,
GB_MBC5_RUMBLE = 0x105 GB_MBC5_RUMBLE = 0x105
}; };

View File

@ -10,9 +10,10 @@
CXX_GUARD_START CXX_GUARD_START
#include <mgba/internal/debugger/debugger.h> #include <mgba/debugger/debugger.h>
#include <mgba/internal/arm/arm.h> #include <mgba/internal/arm/arm.h>
#include <mgba-util/vector.h>
struct ARMDebugBreakpoint { struct ARMDebugBreakpoint {
uint32_t address; uint32_t address;

View File

@ -10,7 +10,10 @@
CXX_GUARD_START 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; struct CLIDebugger;

View File

@ -10,7 +10,7 @@
CXX_GUARD_START CXX_GUARD_START
#include <mgba/internal/debugger/debugger.h> #include <mgba/debugger/debugger.h>
#include <mgba-util/socket.h> #include <mgba-util/socket.h>

View File

@ -10,7 +10,7 @@
CXX_GUARD_START CXX_GUARD_START
#include <mgba/internal/debugger/debugger.h> #include <mgba/debugger/debugger.h>
enum LexState { enum LexState {
LEX_ERROR = -1, LEX_ERROR = -1,

View File

@ -139,6 +139,8 @@ bool GBIsROM(struct VFile* vf);
void GBGetGameTitle(const struct GB* gba, char* out); void GBGetGameTitle(const struct GB* gba, char* out);
void GBGetGameCode(const struct GB* gba, char* out); void GBGetGameCode(const struct GB* gba, char* out);
void GBTestKeypadIRQ(struct GB* gb);
void GBFrameEnded(struct GB* gb); void GBFrameEnded(struct GB* gb);
CXX_GUARD_END CXX_GUARD_END

View File

@ -36,7 +36,6 @@ struct GBMBCRTCSaveBuffer {
void GBMBCRTCRead(struct GB* gb); void GBMBCRTCRead(struct GB* gb);
void GBMBCRTCWrite(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); void GBMBC7Write(struct GBMemory*, uint16_t address, uint8_t value);
CXX_GUARD_END CXX_GUARD_END

View File

@ -63,24 +63,27 @@ enum {
}; };
struct GBMemory; 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_BITFIELD(GBMBC7Field, uint8_t);
DECL_BIT(GBMBC7Field, SK, 6);
DECL_BIT(GBMBC7Field, CS, 7); DECL_BIT(GBMBC7Field, CS, 7);
DECL_BIT(GBMBC7Field, IO, 1); DECL_BIT(GBMBC7Field, CLK, 6);
DECL_BIT(GBMBC7Field, DI, 1);
DECL_BIT(GBMBC7Field, DO, 0);
enum GBMBC7MachineState { enum GBMBC7MachineState {
GBMBC7_STATE_NULL = -1,
GBMBC7_STATE_IDLE = 0, GBMBC7_STATE_IDLE = 0,
GBMBC7_STATE_READ_COMMAND = 1, GBMBC7_STATE_READ_COMMAND = 1,
GBMBC7_STATE_READ_ADDRESS = 2, GBMBC7_STATE_DO = 2,
GBMBC7_STATE_COMMAND_0 = 3,
GBMBC7_STATE_COMMAND_SR_WRITE = 4, GBMBC7_STATE_EEPROM_EWDS = 0x10,
GBMBC7_STATE_COMMAND_SR_READ = 5, GBMBC7_STATE_EEPROM_WRAL = 0x11,
GBMBC7_STATE_COMMAND_SR_FILL = 6, GBMBC7_STATE_EEPROM_ERAL = 0x12,
GBMBC7_STATE_READ = 7, GBMBC7_STATE_EEPROM_EWEN = 0x13,
GBMBC7_STATE_WRITE = 8, GBMBC7_STATE_EEPROM_WRITE = 0x14,
GBMBC7_STATE_EEPROM_READ = 0x18,
GBMBC7_STATE_EEPROM_ERASE = 0x1C,
}; };
struct GBMBC1State { struct GBMBC1State {
@ -90,17 +93,23 @@ struct GBMBC1State {
struct GBMBC7State { struct GBMBC7State {
enum GBMBC7MachineState state; enum GBMBC7MachineState state;
uint32_t sr; uint16_t sr;
uint8_t address; uint8_t address;
bool writable; bool writable;
int srBits; int srBits;
int command; uint8_t access;
GBMBC7Field field; uint8_t latch;
GBMBC7Field eeprom;
};
struct GBPocketCamState {
bool registersActive;
}; };
union GBMBCState { union GBMBCState {
struct GBMBC1State mbc1; struct GBMBC1State mbc1;
struct GBMBC7State mbc7; struct GBMBC7State mbc7;
struct GBPocketCamState pocketCam;
}; };
struct mRotationSource; struct mRotationSource;
@ -109,7 +118,8 @@ struct GBMemory {
uint8_t* romBase; uint8_t* romBase;
uint8_t* romBank; uint8_t* romBank;
enum GBMemoryBankControllerType mbcType; enum GBMemoryBankControllerType mbcType;
GBMemoryBankController mbc; GBMemoryBankControllerWrite mbcWrite;
GBMemoryBankControllerRead mbcRead;
union GBMBCState mbcState; union GBMBCState mbcState;
int currentBank; int currentBank;

View File

@ -16,6 +16,8 @@ struct GBCartridgeOverride {
int headerCrc32; int headerCrc32;
enum GBModel model; enum GBModel model;
enum GBMemoryBankControllerType mbc; enum GBMemoryBankControllerType mbc;
uint32_t gbColors[4];
}; };
struct Configuration; struct Configuration;

View File

@ -154,8 +154,7 @@ mLOG_DECLARE_CATEGORY(GB_STATE);
* | bit 4: Is HDMA active? * | bit 4: Is HDMA active?
* | bits 5 - 7: Active RTC register * | bits 5 - 7: Active RTC register
* | 0x00196 - 0x00197: Reserved (leave zero) * | 0x00196 - 0x00197: Reserved (leave zero)
* 0x00198 - 0x0019F: Savestate creation time (usec since 1970) * 0x00198 - 0x0025F: Reserved (leave zero)
* 0x001A0 - 0x0025F: Reserved (leave zero)
* 0x00260 - 0x002FF: OAM * 0x00260 - 0x002FF: OAM
* 0x00300 - 0x0037F: I/O memory * 0x00300 - 0x0037F: I/O memory
* 0x00380 - 0x003FE: HRAM * 0x00380 - 0x003FE: HRAM
@ -354,9 +353,7 @@ struct GBSerializedState {
uint16_t reserved; uint16_t reserved;
} memory; } memory;
uint64_t creationUsec; uint32_t reserved[50];
uint32_t reserved[48];
uint8_t oam[GB_SIZE_OAM]; uint8_t oam[GB_SIZE_OAM];

View File

@ -144,7 +144,7 @@ void GBVideoWriteLYC(struct GBVideo* video, uint8_t value);
void GBVideoWritePalette(struct GBVideo* video, uint16_t address, uint8_t value); void GBVideoWritePalette(struct GBVideo* video, uint16_t address, uint8_t value);
void GBVideoSwitchBank(struct GBVideo* video, uint8_t value); void GBVideoSwitchBank(struct GBVideo* video, uint8_t value);
void GBVideoSetPalette(struct GBVideo* video, unsigned index, uint16_t color); void GBVideoSetPalette(struct GBVideo* video, unsigned index, uint32_t color);
struct GBSerializedState; struct GBSerializedState;
void GBVideoSerialize(const struct GBVideo* video, struct GBSerializedState* state); void GBVideoSerialize(const struct GBVideo* video, struct GBSerializedState* state);

View File

@ -132,10 +132,6 @@ struct GBAMemory {
char waitstatesSeq16[256]; char waitstatesSeq16[256];
char waitstatesNonseq32[256]; char waitstatesNonseq32[256];
char waitstatesNonseq16[256]; char waitstatesNonseq16[256];
char waitstatesPrefetchSeq32[16];
char waitstatesPrefetchSeq16[16];
char waitstatesPrefetchNonseq32[16];
char waitstatesPrefetchNonseq16[16];
int activeRegion; int activeRegion;
bool prefetch; bool prefetch;
uint32_t lastPrefetchedPc; uint32_t lastPrefetchedPc;

View File

@ -190,8 +190,7 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* | 0x002F8 - 0x002FB: CPU prefecth (decode slot) * | 0x002F8 - 0x002FB: CPU prefecth (decode slot)
* | 0x002FC - 0x002FF: CPU prefetch (fetch slot) * | 0x002FC - 0x002FF: CPU prefetch (fetch slot)
* 0x00300 - 0x00303: Associated movie stream ID for record/replay (or 0 if no stream) * 0x00300 - 0x00303: Associated movie stream ID for record/replay (or 0 if no stream)
* 0x00304 - 0x0030F: Reserved (leave zero) * 0x00304 - 0x00317: Savestate creation time (usec since 1970)
* 0x00310 - 0x00317: Savestate creation time (usec since 1970)
* 0x00318 - 0x0031B: Last prefetched program counter * 0x00318 - 0x0031B: Last prefetched program counter
* 0x0031C - 0x0031F: Miscellaneous flags * 0x0031C - 0x0031F: Miscellaneous flags
* | bit 0: Is CPU halted? * | bit 0: Is CPU halted?
@ -312,9 +311,7 @@ struct GBASerializedState {
uint32_t cpuPrefetch[2]; uint32_t cpuPrefetch[2];
uint32_t associatedStreamId; uint32_t associatedStreamId;
uint32_t reservedRr[3]; uint32_t reservedRr[5];
uint64_t creationUsec;
uint32_t lastPrefetchedPc; uint32_t lastPrefetchedPc;
GBASerializedMiscFlags miscFlags; GBASerializedMiscFlags miscFlags;

View File

@ -10,9 +10,10 @@
CXX_GUARD_START CXX_GUARD_START
#include <mgba/internal/debugger/debugger.h> #include <mgba/debugger/debugger.h>
#include <mgba/internal/lr35902/lr35902.h> #include <mgba/internal/lr35902/lr35902.h>
#include <mgba-util/vector.h>
struct LR35902DebugBreakpoint { struct LR35902DebugBreakpoint {

View File

@ -7,5 +7,5 @@ passes=1
[pass.0] [pass.0]
fragmentShader=agb001.fs fragmentShader=agb001.fs
blend=1 blend=1
width=960 width=-4
height=640 height=-4

View File

@ -7,13 +7,13 @@ passes=2
[pass.0] [pass.0]
fragmentShader=ags001.fs fragmentShader=ags001.fs
blend=1 blend=1
width=960 width=-4
height=640 height=-4
[pass.1] [pass.1]
fragmentShader=ags001-light.fs fragmentShader=ags001-light.fs
width=960 width=-4
height=640 height=-4
[pass.1.uniform.lightBrightness] [pass.1.uniform.lightBrightness]
type=float type=float

View File

@ -23,19 +23,15 @@
THE SOFTWARE. THE SOFTWARE.
*/ */
precision highp float;
varying vec2 texCoord; varying vec2 texCoord;
uniform sampler2D tex; uniform sampler2D tex;
uniform vec2 texSize; uniform vec2 texSize;
uniform float similarity_threshold; uniform float similarity_threshold;
#define screen_res 240,160
vec4 texel_fetch(sampler2D t, ivec2 c) // because GLSL TexelFetch is not supported vec4 texel_fetch(sampler2D t, ivec2 c) // because GLSL TexelFetch is not supported
{ {
return texture2D(tex, (2 * vec2(c) + vec2(1,1)) / (2 * vec2(screen_res)) ); return texture2D(tex, (2 * vec2(c) + vec2(1,1)) / (2 * texSize) );
} }
float pixel_brightness(vec4 pixel) float pixel_brightness(vec4 pixel)
@ -140,7 +136,7 @@ vec4 interpolate_diagonal(vec4 a, vec4 b, vec4 c, vec4 d)
void main() void main()
{ {
ivec2 pixel_coords2 = ivec2(texCoord * vec2(screen_res) * 2); ivec2 pixel_coords2 = ivec2(texCoord * texSize * 2);
ivec2 pixel_coords = pixel_coords2 / 2; ivec2 pixel_coords = pixel_coords2 / 2;
bool x_even = mod(pixel_coords2.x,2) == 0; bool x_even = mod(pixel_coords2.x,2) == 0;

View File

@ -0,0 +1,40 @@
/*
LCD Shader
Copyright (C) 2017 Dominus Iniquitatis - zerosaiko@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
uniform sampler2D tex;
uniform vec2 texSize;
varying vec2 texCoord;
uniform float boundBrightness;
void main()
{
vec4 color = texture2D(tex, texCoord);
if (int(mod(texCoord.s * texSize.x * 3.0, 3.0)) == 0 ||
int(mod(texCoord.t * texSize.y * 3.0, 3.0)) == 0)
{
color.rgb *= vec3(1.0, 1.0, 1.0) * boundBrightness;
}
gl_FragColor = color;
}

View File

@ -0,0 +1,18 @@
[shader]
name=LCD
author=Dominus Iniquitatis
description=Simple LCD emulation.
passes=1
[pass.0]
fragmentShader=lcd.fs
blend=1
width=-3
height=-3
[pass.0.uniform.boundBrightness]
type=float
readableName=Bound brightness
default=0.9
min=0.0
max=1.0

View File

@ -0,0 +1,18 @@
[shader]
name=Motion Blur
author=Dominus Iniquitatis
description=Simple motion blur.
passes=1
[pass.0]
fragmentShader=motion_blur.fs
blend=1
width=-1
height=-1
[pass.0.uniform.amount]
type=float
readableName=Amount
default=0.3
min=0.0
max=1.0

View File

@ -0,0 +1,35 @@
/*
Motion Blur Shader
Copyright (C) 2017 Dominus Iniquitatis - zerosaiko@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
uniform sampler2D tex;
uniform vec2 texSize;
varying vec2 texCoord;
uniform float amount;
void main()
{
vec4 color = texture2D(tex, texCoord);
color.a = 1.0 - amount;
gl_FragColor = color;
}

View File

@ -0,0 +1,18 @@
[shader]
name=Scanlines
author=Dominus Iniquitatis
description=Simple scanlines.
passes=1
[pass.0]
fragmentShader=scanlines.fs
blend=1
width=-2
height=-2
[pass.0.uniform.lineBrightness]
type=float
readableName=Line brightness
default=0.5
min=0.0
max=1.0

View File

@ -0,0 +1,39 @@
/*
Scanlines Shader
Copyright (C) 2017 Dominus Iniquitatis - zerosaiko@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
uniform sampler2D tex;
uniform vec2 texSize;
varying vec2 texCoord;
uniform float lineBrightness;
void main()
{
vec4 color = texture2D(tex, texCoord);
if (int(mod(texCoord.t * texSize.y * 2.0, 2.0)) == 0)
{
color.rgb *= vec3(1.0, 1.0, 1.0) * lineBrightness;
}
gl_FragColor = color;
}

View File

@ -0,0 +1,18 @@
[shader]
name=Soften
author=Dominus Iniquitatis
description=Soft image blurring.
passes=1
[pass.0]
fragmentShader=soften.fs
blend=1
width=-1
height=-1
[pass.0.uniform.amount]
type=float
readableName=Amount
default=0.5
min=0.0
max=1.0

View File

@ -0,0 +1,64 @@
/*
Soften Shader
Copyright (C) 2017 Dominus Iniquitatis - zerosaiko@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
uniform sampler2D tex;
uniform vec2 texSize;
varying vec2 texCoord;
uniform float amount;
vec2 GetTexelSize()
{
return vec2(1.0 / texSize.x, 1.0 / texSize.y);
}
void main()
{
vec4 color = texture2D(tex, texCoord);
vec4 northColor = texture2D(tex, texCoord + vec2(0.0, GetTexelSize().y));
vec4 southColor = texture2D(tex, texCoord - vec2(0.0, GetTexelSize().y));
vec4 eastColor = texture2D(tex, texCoord + vec2(GetTexelSize().x, 0.0));
vec4 westColor = texture2D(tex, texCoord - vec2(GetTexelSize().x, 0.0));
if (abs(length(color) - length(northColor)) > 0.0)
{
color = mix(color, northColor, amount / 4.0);
}
if (abs(length(color) - length(southColor)) > 0.0)
{
color = mix(color, southColor, amount / 4.0);
}
if (abs(length(color) - length(eastColor)) > 0.0)
{
color = mix(color, eastColor, amount / 4.0);
}
if (abs(length(color) - length(westColor)) > 0.0)
{
color = mix(color, westColor, amount / 4.0);
}
gl_FragColor = color;
}

View File

@ -0,0 +1,18 @@
[shader]
name=VBA Pixelate
author=Dominus Iniquitatis
description=VisualBoyAdvance-style pixelation.
passes=1
[pass.0]
fragmentShader=vba_pixelate.fs
blend=1
width=-2
height=-2
[pass.0.uniform.boundBrightness]
type=float
readableName=Bound brightness
default=0.5
min=0.0
max=1.0

View File

@ -0,0 +1,40 @@
/*
VBA Pixelate Shader
Copyright (C) 2017 Dominus Iniquitatis - zerosaiko@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
uniform sampler2D tex;
uniform vec2 texSize;
varying vec2 texCoord;
uniform float boundBrightness;
void main()
{
vec4 color = texture2D(tex, texCoord);
if (int(mod(texCoord.s * texSize.x * 2.0, 2.0)) == 0 ||
int(mod(texCoord.t * texSize.y * 2.0, 2.0)) == 0)
{
color.rgb *= vec3(1.0, 1.0, 1.0) * boundBrightness;
}
gl_FragColor = color;
}

View File

@ -0,0 +1,18 @@
[shader]
name=Vignette
author=Dominus Iniquitatis
description=Configurable vignette effect.
passes=1
[pass.0]
fragmentShader=vignette.fs
blend=1
width=-1
height=-1
[pass.0.uniform.intensity]
type=float
readableName=Intensity
default=1.0
min=0.0
max=1.0

View File

@ -0,0 +1,35 @@
/*
Vignette Shader
Copyright (C) 2017 Dominus Iniquitatis - zerosaiko@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
uniform sampler2D tex;
uniform vec2 texSize;
varying vec2 texCoord;
uniform float intensity;
void main()
{
vec4 color = texture2D(tex, texCoord);
color = mix(color, vec4(0.0, 0.0, 0.0, 1.0), length(texCoord - 0.5) * intensity);
gl_FragColor = color;
}

View File

@ -7,5 +7,5 @@ passes=1
[pass.0] [pass.0]
fragmentShader=wiiu.fs fragmentShader=wiiu.fs
blend=1 blend=1
width=960 width=-4
height=640 height=-4

View File

@ -7,6 +7,7 @@
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba/internal/arm/arm.h> #include <mgba/internal/arm/arm.h>
#include <mgba/internal/arm/decoder.h>
#include <mgba/internal/arm/isa-inlines.h> #include <mgba/internal/arm/isa-inlines.h>
#include <mgba/internal/arm/debugger/memory-debugger.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 ARMDebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform*); static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform*);
static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform*); static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform*);
static void ARMDebuggerTrace(struct mDebuggerPlatform*, char* out, size_t* length);
struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) { struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) {
struct mDebuggerPlatform* platform = (struct mDebuggerPlatform*) malloc(sizeof(struct ARMDebugger)); struct mDebuggerPlatform* platform = (struct mDebuggerPlatform*) malloc(sizeof(struct ARMDebugger));
@ -66,6 +68,7 @@ struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) {
platform->clearWatchpoint = ARMDebuggerClearWatchpoint; platform->clearWatchpoint = ARMDebuggerClearWatchpoint;
platform->checkBreakpoints = ARMDebuggerCheckBreakpoints; platform->checkBreakpoints = ARMDebuggerCheckBreakpoints;
platform->hasBreakpoints = ARMDebuggerHasBreakpoints; platform->hasBreakpoints = ARMDebuggerHasBreakpoints;
platform->trace = ARMDebuggerTrace;
return platform; return platform;
} }
@ -207,3 +210,39 @@ static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t add
ARMDebuggerRemoveMemoryShim(debugger); 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);
}

View File

@ -86,6 +86,9 @@ static bool _lookupIntValue(const struct mCoreConfig* config, const char* key, i
} }
char* end; char* end;
long value = strtol(charValue, &end, 10); long value = strtol(charValue, &end, 10);
if (end == &charValue[1] && *end == 'x') {
value = strtol(charValue, &end, 16);
}
if (*end) { if (*end) {
return false; return false;
} }

View File

@ -339,6 +339,16 @@ static void _mLibraryDeleteEntry(struct mLibrary* library, struct mLibraryEntry*
sqlite3_step(library->insertPath); sqlite3_step(library->insertPath);
} }
void mLibraryClear(struct mLibrary* library) {
int result = sqlite3_exec(library->db,
" BEGIN TRANSACTION;"
"\n DELETE FROM roots;"
"\n DELETE FROM roms;"
"\n DELETE FROM paths;"
"\n COMMIT;"
"\n VACUUM;", NULL, NULL, NULL);
}
size_t mLibraryCount(struct mLibrary* library, const struct mLibraryEntry* constraints) { size_t mLibraryCount(struct mLibrary* library, const struct mLibraryEntry* constraints) {
sqlite3_clear_bindings(library->count); sqlite3_clear_bindings(library->count);
sqlite3_reset(library->count); sqlite3_reset(library->count);

496
src/core/mem-search.c Normal file
View File

@ -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 && !end[0]) {
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 && !end[0]) {
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;
}
}
}

View File

@ -303,6 +303,36 @@ bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags) {
struct mStateExtdata extdata; struct mStateExtdata extdata;
mStateExtdataInit(&extdata); mStateExtdataInit(&extdata);
size_t stateSize = core->stateSize(core); 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) { if (flags & SAVESTATE_SAVEDATA) {
void* sram = NULL; void* sram = NULL;
size_t size = core->savedataClone(core, &sram); size_t size = core->savedataClone(core, &sram);

View File

@ -89,5 +89,9 @@ int32_t mTimingNextEvent(struct mTiming* timing) {
if (!next) { if (!next) {
return INT_MAX; return INT_MAX;
} }
return next->when - timing->masterCycles; return next->when - timing->masterCycles - *timing->relativeCycles;
}
int32_t mTimingUntil(const struct mTiming* timing, const struct mTimingEvent* event) {
return event->when - timing->masterCycles - *timing->relativeCycles;
} }

View File

@ -44,6 +44,7 @@ static void _clearBreakpoint(struct CLIDebugger*, struct CLIDebugVector*);
static void _setWatchpoint(struct CLIDebugger*, struct CLIDebugVector*); static void _setWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
static void _setReadWatchpoint(struct CLIDebugger*, struct CLIDebugVector*); static void _setReadWatchpoint(struct CLIDebugger*, struct CLIDebugVector*);
static void _setWriteWatchpoint(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 _writeByte(struct CLIDebugger*, struct CLIDebugVector*);
static void _writeHalfword(struct CLIDebugger*, struct CLIDebugVector*); static void _writeHalfword(struct CLIDebugger*, struct CLIDebugVector*);
static void _writeWord(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/2", _readHalfword, CLIDVParse, "Read a halfword from a specified offset" },
{ "r/4", _readWord, CLIDVParse, "Read a word from a specified offset" }, { "r/4", _readWord, CLIDVParse, "Read a word from a specified offset" },
{ "status", _printStatus, 0, "Print the current status" }, { "status", _printStatus, 0, "Print the current status" },
{ "trace", _trace, CLIDVParse, "Trace a fixed number of instructions" },
{ "w", _setWatchpoint, CLIDVParse, "Set a watchpoint" }, { "w", _setWatchpoint, CLIDVParse, "Set a watchpoint" },
{ "w/1", _writeByte, CLIDVParse, "Write a byte at a specified offset" }, { "w/1", _writeByte, CLIDVParse, "Write a byte at a specified offset" },
{ "w/2", _writeHalfword, CLIDVParse, "Write a halfword 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) { static void _printStatus(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
UNUSED(dv); UNUSED(dv);
debugger->system->printStatus(debugger->system); debugger->system->printStatus(debugger->system);

View File

@ -3,7 +3,7 @@
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * 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/. */ * 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> #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) { void mDebuggerEnter(struct mDebugger* debugger, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
debugger->state = DEBUGGER_PAUSED; debugger->state = DEBUGGER_PAUSED;
if (debugger->platform->entered) { if (debugger->platform->entered) {

View File

@ -431,7 +431,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
runner->core->reset(runner->core); runner->core->reset(runner->core);
break; break;
case RUNNER_SAVE_STATE: 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; break;
case RUNNER_LOAD_STATE: case RUNNER_LOAD_STATE:
mCoreLoadState(runner->core, ((int) item->data) >> 16, SAVESTATE_SCREENSHOT | SAVESTATE_RTC); mCoreLoadState(runner->core, ((int) item->data) >> 16, SAVESTATE_SCREENSHOT | SAVESTATE_RTC);

View File

@ -337,6 +337,7 @@ void GBAudioWriteNR34(struct GBAudio* audio, uint8_t value) {
} }
audio->ch3.window = 0; audio->ch3.window = 0;
} }
mTimingDeschedule(audio->timing, &audio->ch3Fade);
mTimingDeschedule(audio->timing, &audio->ch3Event); mTimingDeschedule(audio->timing, &audio->ch3Event);
if (audio->playingCh3) { if (audio->playingCh3) {
audio->ch3.readable = audio->style != GB_AUDIO_DMG; audio->ch3.readable = audio->style != GB_AUDIO_DMG;
@ -554,7 +555,7 @@ void _updateFrame(struct mTiming* timing, void* user, uint32_t cyclesLate) {
if (audio->ch2.envelope.dead == 2) { if (audio->ch2.envelope.dead == 2) {
mTimingDeschedule(timing, &audio->ch2Event); mTimingDeschedule(timing, &audio->ch2Event);
} }
_updateSquareSample(&audio->ch1); _updateSquareSample(&audio->ch2);
} }
} }
@ -708,7 +709,7 @@ bool _writeEnvelope(struct GBAudioEnvelope* envelope, uint8_t value) {
} }
static void _updateSquareSample(struct GBAudioSquareChannel* ch) { static void _updateSquareSample(struct GBAudioSquareChannel* ch) {
ch->sample = (ch->control.hi * ch->envelope.currentVolume - 8) * 0x10; ch->sample = (ch->control.hi * 2 - 1) * ch->envelope.currentVolume * 0x8;
} }
static int32_t _updateSquareChannel(struct GBAudioSquareChannel* ch) { static int32_t _updateSquareChannel(struct GBAudioSquareChannel* ch) {
@ -863,6 +864,7 @@ static void _updateChannel3(struct mTiming* timing, void* user, uint32_t cyclesL
ch->sample *= volume * 4; ch->sample *= volume * 4;
audio->ch3.readable = true; audio->ch3.readable = true;
if (audio->style == GB_AUDIO_DMG) { if (audio->style == GB_AUDIO_DMG) {
mTimingDeschedule(audio->timing, &audio->ch3Fade);
mTimingSchedule(timing, &audio->ch3Fade, 2 - cyclesLate); mTimingSchedule(timing, &audio->ch3Fade, 2 - cyclesLate);
} }
int cycles = 2 * (2048 - ch->rate); int cycles = 2 * (2048 - ch->rate);

View File

@ -56,6 +56,28 @@ const static struct LR35902Segment _GBCSegments[] = {
{ 0 } { 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 mVideoLogContext;
struct GBCore { struct GBCore {
struct mCore d; struct mCore d;
@ -419,11 +441,13 @@ static bool _GBCoreSaveState(struct mCore* core, void* state) {
static void _GBCoreSetKeys(struct mCore* core, uint32_t keys) { static void _GBCoreSetKeys(struct mCore* core, uint32_t keys) {
struct GBCore* gbcore = (struct GBCore*) core; struct GBCore* gbcore = (struct GBCore*) core;
gbcore->keys = keys; gbcore->keys = keys;
GBTestKeypadIRQ(core->board);
} }
static void _GBCoreAddKeys(struct mCore* core, uint32_t keys) { static void _GBCoreAddKeys(struct mCore* core, uint32_t keys) {
struct GBCore* gbcore = (struct GBCore*) core; struct GBCore* gbcore = (struct GBCore*) core;
gbcore->keys |= keys; gbcore->keys |= keys;
GBTestKeypadIRQ(core->board);
} }
static void _GBCoreClearKeys(struct mCore* core, uint32_t keys) { static void _GBCoreClearKeys(struct mCore* core, uint32_t keys) {
@ -539,6 +563,48 @@ static void _GBCoreRawWrite32(struct mCore* core, uint32_t address, int segment,
GBPatch8(cpu, address + 3, value >> 24, NULL, 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 #ifdef USE_DEBUGGERS
static bool _GBCoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) { static bool _GBCoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) {
UNUSED(core); UNUSED(core);
@ -771,6 +837,8 @@ struct mCore* GBCoreCreate(void) {
core->rawWrite8 = _GBCoreRawWrite8; core->rawWrite8 = _GBCoreRawWrite8;
core->rawWrite16 = _GBCoreRawWrite16; core->rawWrite16 = _GBCoreRawWrite16;
core->rawWrite32 = _GBCoreRawWrite32; core->rawWrite32 = _GBCoreRawWrite32;
core->listMemoryBlocks = _GBListMemoryBlocks;
core->getMemoryBlock = _GBGetMemoryBlock;
#ifdef USE_DEBUGGERS #ifdef USE_DEBUGGERS
core->supportsDebuggerType = _GBCoreSupportsDebuggerType; core->supportsDebuggerType = _GBCoreSupportsDebuggerType;
core->debuggerPlatform = _GBCoreDebuggerPlatform; core->debuggerPlatform = _GBCoreDebuggerPlatform;

View File

@ -118,5 +118,5 @@ static void _save(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
struct GBCLIDebugger* gbDebugger = (struct GBCLIDebugger*) debugger->system; 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);
} }

View File

@ -433,6 +433,7 @@ void GBReset(struct LR35902Core* cpu) {
cpu->e = 0xD8; cpu->e = 0xD8;
cpu->h = 1; cpu->h = 1;
cpu->l = 0x4D; cpu->l = 0x4D;
gb->timer.internalDiv = 0x2AF3;
break; break;
case GB_MODEL_AGB: case GB_MODEL_AGB:
cpu->b = 1; cpu->b = 1;
@ -444,6 +445,7 @@ void GBReset(struct LR35902Core* cpu) {
cpu->e = 0x08; cpu->e = 0x08;
cpu->h = 0; cpu->h = 0;
cpu->l = 0x7C; cpu->l = 0x7C;
gb->timer.internalDiv = 0x7A8;
break; break;
} }
@ -710,4 +712,6 @@ void GBFrameEnded(struct GB* gb) {
mCheatRefresh(device, cheats); mCheatRefresh(device, cheats);
} }
} }
GBTestKeypadIRQ(gb);
} }

View File

@ -564,6 +564,13 @@ uint8_t GBIORead(struct GB* gb, unsigned address) {
return gb->memory.io[address] | _registerMask[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; struct GBSerializedState;
void GBIOSerialize(const struct GB* gb, struct GBSerializedState* state) { void GBIOSerialize(const struct GB* gb, struct GBSerializedState* state) {
memcpy(state->io, gb->memory.io, GB_SIZE_IO); memcpy(state->io, gb->memory.io, GB_SIZE_IO);

View File

@ -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 _GBMBC6(struct GB*, uint16_t address, uint8_t value);
static void _GBMBC7(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 _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) { void GBMBCSwitchBank(struct GB* gb, int bank) {
size_t bankStart = bank * GB_SIZE_CART_BANK0; size_t bankStart = bank * GB_SIZE_CART_BANK0;
@ -75,7 +79,11 @@ static bool _isMulticart(const uint8_t* mem) {
void GBMBCSwitchSramBank(struct GB* gb, int bank) { void GBMBCSwitchSramBank(struct GB* gb, int bank) {
size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM; size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM;
GBResizeSram(gb, (bank + 1) * GB_SIZE_EXTERNAL_RAM); if (bankStart + GB_SIZE_EXTERNAL_RAM > gb->sramSize) {
mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid RAM bank: %0X", bank);
bankStart &= (gb->sramSize - 1);
bank = bankStart / GB_SIZE_EXTERNAL_RAM;
}
gb->memory.sramBank = &gb->memory.sram[bankStart]; gb->memory.sramBank = &gb->memory.sram[bankStart];
gb->memory.sramCurrentBank = bank; gb->memory.sramCurrentBank = bank;
} }
@ -148,6 +156,12 @@ void GBMBCInit(struct GB* gb) {
case 0x22: case 0x22:
gb->memory.mbcType = GB_MBC7; gb->memory.mbcType = GB_MBC7;
break; break;
case 0xFC:
gb->memory.mbcType = GB_POCKETCAM;
break;
case 0xFD:
gb->memory.mbcType = GB_HuC1;
break;
case 0xFE: case 0xFE:
gb->memory.mbcType = GB_HuC3; gb->memory.mbcType = GB_HuC3;
break; break;
@ -156,51 +170,57 @@ void GBMBCInit(struct GB* gb) {
} else { } else {
gb->memory.mbcType = GB_MBC_NONE; gb->memory.mbcType = GB_MBC_NONE;
} }
gb->memory.mbcRead = NULL;
switch (gb->memory.mbcType) { switch (gb->memory.mbcType) {
case GB_MBC_NONE: case GB_MBC_NONE:
gb->memory.mbc = _GBMBCNone; gb->memory.mbcWrite = _GBMBCNone;
break; break;
case GB_MBC1: case GB_MBC1:
gb->memory.mbc = _GBMBC1; gb->memory.mbcWrite = _GBMBC1;
break; break;
case GB_MBC2: case GB_MBC2:
gb->memory.mbc = _GBMBC2; gb->memory.mbcWrite = _GBMBC2;
gb->sramSize = 0x200; gb->sramSize = 0x200;
break; break;
case GB_MBC3: case GB_MBC3:
gb->memory.mbc = _GBMBC3; gb->memory.mbcWrite = _GBMBC3;
break; break;
default: default:
mLOG(GB_MBC, WARN, "Unknown MBC type: %02X", cart->type); mLOG(GB_MBC, WARN, "Unknown MBC type: %02X", cart->type);
// Fall through // Fall through
case GB_MBC5: case GB_MBC5:
gb->memory.mbc = _GBMBC5; gb->memory.mbcWrite = _GBMBC5;
break; break;
case GB_MBC6: case GB_MBC6:
mLOG(GB_MBC, WARN, "unimplemented MBC: MBC6"); mLOG(GB_MBC, WARN, "unimplemented MBC: MBC6");
gb->memory.mbc = _GBMBC6; gb->memory.mbcWrite = _GBMBC6;
break; break;
case GB_MBC7: case GB_MBC7:
gb->memory.mbc = _GBMBC7; gb->memory.mbcWrite = _GBMBC7;
gb->sramSize = GB_SIZE_EXTERNAL_RAM; gb->memory.mbcRead = _GBMBC7Read;
gb->sramSize = 0x100;
break; break;
case GB_MMM01: case GB_MMM01:
mLOG(GB_MBC, WARN, "unimplemented MBC: MMM01"); mLOG(GB_MBC, WARN, "unimplemented MBC: MMM01");
gb->memory.mbc = _GBMBC1; gb->memory.mbcWrite = _GBMBC1;
break; break;
case GB_HuC1: case GB_HuC1:
mLOG(GB_MBC, WARN, "unimplemented MBC: HuC-1"); mLOG(GB_MBC, WARN, "unimplemented MBC: HuC-1");
gb->memory.mbc = _GBMBC1; gb->memory.mbcWrite = _GBMBC1;
break; break;
case GB_HuC3: case GB_HuC3:
gb->memory.mbc = _GBHuC3; gb->memory.mbcWrite = _GBHuC3;
break; break;
case GB_MBC3_RTC: case GB_MBC3_RTC:
memset(gb->memory.rtcRegs, 0, sizeof(gb->memory.rtcRegs)); memset(gb->memory.rtcRegs, 0, sizeof(gb->memory.rtcRegs));
gb->memory.mbc = _GBMBC3; gb->memory.mbcWrite = _GBMBC3;
break; break;
case GB_MBC5_RUMBLE: 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; break;
} }
@ -350,7 +370,8 @@ void _GBMBC2(struct GB* gb, uint16_t address, uint8_t value) {
// TODO // TODO
mLOG(GB_MBC, STUB, "MBC2 unknown address: %04X:%02X", address, value); mLOG(GB_MBC, STUB, "MBC2 unknown address: %04X:%02X", address, value);
break; break;
}} }
}
void _GBMBC3(struct GB* gb, uint16_t address, uint8_t value) { void _GBMBC3(struct GB* gb, uint16_t address, uint8_t value) {
struct GBMemory* memory = &gb->memory; struct GBMemory* memory = &gb->memory;
@ -451,12 +472,25 @@ void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) {
void _GBMBC7(struct GB* gb, uint16_t address, uint8_t value) { void _GBMBC7(struct GB* gb, uint16_t address, uint8_t value) {
int bank = value & 0x7F; int bank = value & 0x7F;
switch (address >> 13) { switch (address >> 13) {
case 0x0:
switch (value) {
default:
case 0:
gb->memory.mbcState.mbc7.access = 0;
break;
case 0xA:
gb->memory.mbcState.mbc7.access |= 1;
break;
}
break;
case 0x1: case 0x1:
GBMBCSwitchBank(gb, bank); GBMBCSwitchBank(gb, bank);
break; break;
case 0x2: case 0x2:
if (value < 0x10) { if (value == 0x40) {
GBMBCSwitchSramBank(gb, value); gb->memory.mbcState.mbc7.access |= 2;
} else {
gb->memory.mbcState.mbc7.access &= ~2;
} }
break; break;
default: default:
@ -466,19 +500,17 @@ 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; struct GBMBC7State* mbc7 = &memory->mbcState.mbc7;
if (mbc7->access != 3) {
return 0xFF;
}
switch (address & 0xF0) { switch (address & 0xF0) {
case 0x00:
case 0x10:
case 0x60:
case 0x70:
return 0;
case 0x20: case 0x20:
if (memory->rotation && memory->rotation->readTiltX) { if (memory->rotation && memory->rotation->readTiltX) {
int32_t x = -memory->rotation->readTiltX(memory->rotation); int32_t x = -memory->rotation->readTiltX(memory->rotation);
x >>= 21; x >>= 21;
x += 2047; x += 0x81D0;
return x; return x;
} }
return 0xFF; return 0xFF;
@ -486,7 +518,7 @@ uint8_t GBMBC7Read(struct GBMemory* memory, uint16_t address) {
if (memory->rotation && memory->rotation->readTiltX) { if (memory->rotation && memory->rotation->readTiltX) {
int32_t x = -memory->rotation->readTiltX(memory->rotation); int32_t x = -memory->rotation->readTiltX(memory->rotation);
x >>= 21; x >>= 21;
x += 2047; x += 0x81D0;
return x >> 8; return x >> 8;
} }
return 7; return 7;
@ -494,7 +526,7 @@ uint8_t GBMBC7Read(struct GBMemory* memory, uint16_t address) {
if (memory->rotation && memory->rotation->readTiltY) { if (memory->rotation && memory->rotation->readTiltY) {
int32_t y = -memory->rotation->readTiltY(memory->rotation); int32_t y = -memory->rotation->readTiltY(memory->rotation);
y >>= 21; y >>= 21;
y += 2047; y += 0x81D0;
return y; return y;
} }
return 0xFF; return 0xFF;
@ -502,144 +534,142 @@ uint8_t GBMBC7Read(struct GBMemory* memory, uint16_t address) {
if (memory->rotation && memory->rotation->readTiltY) { if (memory->rotation && memory->rotation->readTiltY) {
int32_t y = -memory->rotation->readTiltY(memory->rotation); int32_t y = -memory->rotation->readTiltY(memory->rotation);
y >>= 21; y >>= 21;
y += 2047; y += 0x81D0;
return y >> 8; return y >> 8;
} }
return 7; return 7;
case 0x60:
return 0;
case 0x80: case 0x80:
return (mbc7->sr >> 16) & 1; return mbc7->eeprom;
default: default:
return 0xFF; return 0xFF;
} }
} }
void GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value) { void GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value) {
if ((address & 0xF0) != 0x80) { struct GBMBC7State* mbc7 = &memory->mbcState.mbc7;
if (mbc7->access != 3) {
return; return;
} }
struct GBMBC7State* mbc7 = &memory->mbcState.mbc7; switch (address & 0xF0) {
GBMBC7Field old = memory->mbcState.mbc7.field; case 0x00:
mbc7->field = GBMBC7FieldClearIO(value); mbc7->latch = (value & 0x55) == 0x55;
if (!GBMBC7FieldIsCS(old) && GBMBC7FieldIsCS(value)) { return;
if (mbc7->state == GBMBC7_STATE_WRITE) { case 0x10:
if (mbc7->writable) { mbc7->latch |= (value & 0xAA);
memory->sramBank[mbc7->address * 2] = mbc7->sr >> 8; if (mbc7->latch == 0xFF && memory->rotation && memory->rotation->sample) {
memory->sramBank[mbc7->address * 2 + 1] = mbc7->sr; memory->rotation->sample(memory->rotation);
}
mbc7->sr = 0x1FFFF;
mbc7->state = GBMBC7_STATE_NULL;
} else {
mbc7->state = GBMBC7_STATE_IDLE;
} }
mbc7->latch = 0;
return;
default:
mLOG(GB_MBC, STUB, "MBC7 unknown register: %04X:%02X", address, value);
return;
case 0x80:
break;
} }
if (!GBMBC7FieldIsSK(old) && GBMBC7FieldIsSK(value)) { GBMBC7Field old = memory->mbcState.mbc7.eeprom;
if (mbc7->state > GBMBC7_STATE_IDLE && mbc7->state != GBMBC7_STATE_READ) { value = GBMBC7FieldFillDO(value); // Hi-Z
if (!GBMBC7FieldIsCS(old) && GBMBC7FieldIsCS(value)) {
mbc7->state = GBMBC7_STATE_IDLE;
}
if (!GBMBC7FieldIsCLK(old) && GBMBC7FieldIsCLK(value)) {
if (mbc7->state == GBMBC7_STATE_READ_COMMAND || mbc7->state == GBMBC7_STATE_EEPROM_WRITE || mbc7->state == GBMBC7_STATE_EEPROM_WRAL) {
mbc7->sr <<= 1; mbc7->sr <<= 1;
mbc7->sr |= GBMBC7FieldGetIO(value); mbc7->sr |= GBMBC7FieldGetDI(value);
++mbc7->srBits; ++mbc7->srBits;
} }
switch (mbc7->state) { switch (mbc7->state) {
case GBMBC7_STATE_IDLE: case GBMBC7_STATE_IDLE:
if (GBMBC7FieldIsIO(value)) { if (GBMBC7FieldIsDI(value)) {
mbc7->state = GBMBC7_STATE_READ_COMMAND; mbc7->state = GBMBC7_STATE_READ_COMMAND;
mbc7->srBits = 0; mbc7->srBits = 0;
mbc7->sr = 0; mbc7->sr = 0;
} }
break; break;
case GBMBC7_STATE_READ_COMMAND: case GBMBC7_STATE_READ_COMMAND:
if (mbc7->srBits == 2) { if (mbc7->srBits == 10) {
mbc7->state = GBMBC7_STATE_READ_ADDRESS; mbc7->state = 0x10 | (mbc7->sr >> 6);
mbc7->srBits = 0; if (mbc7->state & 0xC) {
mbc7->command = mbc7->sr; mbc7->state &= ~0x3;
}
break;
case GBMBC7_STATE_READ_ADDRESS:
if (mbc7->srBits == 8) {
mbc7->state = GBMBC7_STATE_COMMAND_0 + mbc7->command;
mbc7->srBits = 0;
mbc7->address = mbc7->sr;
if (mbc7->state == GBMBC7_STATE_COMMAND_0) {
switch (mbc7->address >> 6) {
case 0:
mbc7->writable = false;
mbc7->state = GBMBC7_STATE_NULL;
break;
case 3:
mbc7->writable = true;
mbc7->state = GBMBC7_STATE_NULL;
break;
}
} }
}
break;
case GBMBC7_STATE_COMMAND_0:
if (mbc7->srBits == 16) {
switch (mbc7->address >> 6) {
case 0:
mbc7->writable = false;
mbc7->state = GBMBC7_STATE_NULL;
break;
case 1:
mbc7->state = GBMBC7_STATE_WRITE;
if (mbc7->writable) {
int i;
for (i = 0; i < 256; ++i) {
memory->sramBank[i * 2] = mbc7->sr >> 8;
memory->sramBank[i * 2 + 1] = mbc7->sr;
}
}
break;
case 2:
mbc7->state = GBMBC7_STATE_WRITE;
if (mbc7->writable) {
int i;
for (i = 0; i < 256; ++i) {
memory->sramBank[i * 2] = 0xFF;
memory->sramBank[i * 2 + 1] = 0xFF;
}
}
break;
case 3:
mbc7->writable = true;
mbc7->state = GBMBC7_STATE_NULL;
break;
}
}
break;
case GBMBC7_STATE_COMMAND_SR_WRITE:
if (mbc7->srBits == 16) {
mbc7->srBits = 0; mbc7->srBits = 0;
mbc7->state = GBMBC7_STATE_WRITE; mbc7->address = mbc7->sr & 0x7F;
} }
break; break;
case GBMBC7_STATE_COMMAND_SR_READ: case GBMBC7_STATE_DO:
if (mbc7->srBits == 1) { value = GBMBC7FieldSetDO(value, mbc7->sr >> 15);
mbc7->sr = memory->sramBank[mbc7->address * 2] << 8; mbc7->sr <<= 1;
mbc7->sr |= memory->sramBank[mbc7->address * 2 + 1]; --mbc7->srBits;
mbc7->srBits = 0; if (!mbc7->srBits) {
mbc7->state = GBMBC7_STATE_READ; mbc7->state = GBMBC7_STATE_IDLE;
}
break;
case GBMBC7_STATE_COMMAND_SR_FILL:
if (mbc7->srBits == 16) {
mbc7->sr = 0xFFFF;
mbc7->srBits = 0;
mbc7->state = GBMBC7_STATE_WRITE;
} }
break; break;
default: default:
break; break;
} }
} else if (GBMBC7FieldIsSK(old) && !GBMBC7FieldIsSK(value)) { switch (mbc7->state) {
if (mbc7->state == GBMBC7_STATE_READ) { case GBMBC7_STATE_EEPROM_EWEN:
mbc7->sr <<= 1; mbc7->writable = true;
++mbc7->srBits; mbc7->state = GBMBC7_STATE_IDLE;
break;
case GBMBC7_STATE_EEPROM_EWDS:
mbc7->writable = false;
mbc7->state = GBMBC7_STATE_IDLE;
break;
case GBMBC7_STATE_EEPROM_WRITE:
if (mbc7->srBits == 16) { if (mbc7->srBits == 16) {
mbc7->srBits = 0; if (mbc7->writable) {
mbc7->state = GBMBC7_STATE_NULL; memory->sram[mbc7->address * 2] = mbc7->sr >> 8;
memory->sram[mbc7->address * 2 + 1] = mbc7->sr;
}
mbc7->state = GBMBC7_STATE_IDLE;
} }
break;
case GBMBC7_STATE_EEPROM_ERASE:
if (mbc7->writable) {
memory->sram[mbc7->address * 2] = 0xFF;
memory->sram[mbc7->address * 2 + 1] = 0xFF;
}
mbc7->state = GBMBC7_STATE_IDLE;
break;
case GBMBC7_STATE_EEPROM_READ:
mbc7->srBits = 16;
mbc7->sr = memory->sram[mbc7->address * 2] << 8;
mbc7->sr |= memory->sram[mbc7->address * 2 + 1];
mbc7->state = GBMBC7_STATE_DO;
value = GBMBC7FieldClearDO(value);
break;
case GBMBC7_STATE_EEPROM_WRAL:
if (mbc7->srBits == 16) {
if (mbc7->writable) {
int i;
for (i = 0; i < 128; ++i) {
memory->sram[i * 2] = mbc7->sr >> 8;
memory->sram[i * 2 + 1] = mbc7->sr;
}
}
mbc7->state = GBMBC7_STATE_IDLE;
}
break;
case GBMBC7_STATE_EEPROM_ERAL:
if (mbc7->writable) {
int i;
for (i = 0; i < 128; ++i) {
memory->sram[i * 2] = 0xFF;
memory->sram[i * 2 + 1] = 0xFF;
}
}
mbc7->state = GBMBC7_STATE_IDLE;
break;
default:
break;
} }
} else if (GBMBC7FieldIsCS(value) && GBMBC7FieldIsCLK(old) && !GBMBC7FieldIsCLK(value)) {
value = GBMBC7FieldSetDO(value, GBMBC7FieldGetDO(old));
} }
mbc7->eeprom = value;
} }
void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) { void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) {
@ -674,6 +704,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) { void GBMBCRTCRead(struct GB* gb) {
struct GBMBCRTCSaveBuffer rtcBuffer; struct GBMBCRTCSaveBuffer rtcBuffer;
struct VFile* vf = gb->sramVf; struct VFile* vf = gb->sramVf;

View File

@ -99,7 +99,8 @@ void GBMemoryInit(struct GB* gb) {
gb->memory.romSize = 0; gb->memory.romSize = 0;
gb->memory.sram = 0; gb->memory.sram = 0;
gb->memory.mbcType = GB_MBC_AUTODETECT; gb->memory.mbcType = GB_MBC_AUTODETECT;
gb->memory.mbc = 0; gb->memory.mbcRead = NULL;
gb->memory.mbcWrite = NULL;
gb->memory.rtc = NULL; gb->memory.rtc = NULL;
@ -215,10 +216,10 @@ uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address) {
case GB_REGION_EXTERNAL_RAM + 1: case GB_REGION_EXTERNAL_RAM + 1:
if (memory->rtcAccess) { if (memory->rtcAccess) {
return memory->rtcRegs[memory->activeRtcReg]; return memory->rtcRegs[memory->activeRtcReg];
} else if (memory->mbcRead) {
return memory->mbcRead(memory, address);
} else if (memory->sramAccess) { } else if (memory->sramAccess) {
return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)]; 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) { } else if (memory->mbcType == GB_HuC3) {
return 0x01; // TODO: Is this supposed to be the current SRAM bank? 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 + 1:
case GB_REGION_CART_BANK1 + 2: case GB_REGION_CART_BANK1 + 2:
case GB_REGION_CART_BANK1 + 3: case GB_REGION_CART_BANK1 + 3:
memory->mbc(gb, address, value); memory->mbcWrite(gb, address, value);
cpu->memory.setActiveRegion(cpu, cpu->pc); cpu->memory.setActiveRegion(cpu, cpu->pc);
return; return;
case GB_REGION_VRAM: case GB_REGION_VRAM:
@ -391,8 +392,8 @@ uint8_t GBView8(struct LR35902Core* cpu, uint16_t address, int segment) {
} else { } else {
return 0xFF; return 0xFF;
} }
} else if (memory->mbcType == GB_MBC7) { } else if (memory->mbcRead) {
return GBMBC7Read(memory, address); return memory->mbcRead(memory, address);
} else if (memory->mbcType == GB_HuC3) { } else if (memory->mbcType == GB_HuC3) {
return 0x01; // TODO: Is this supposed to be the current SRAM bank? return 0x01; // TODO: Is this supposed to be the current SRAM bank?
} }
@ -436,6 +437,7 @@ void GBMemoryDMA(struct GB* gb, uint16_t base) {
if (base > 0xF100) { if (base > 0xF100) {
return; return;
} }
mTimingDeschedule(&gb->timing, &gb->memory.dmaEvent);
mTimingSchedule(&gb->timing, &gb->memory.dmaEvent, 8); mTimingSchedule(&gb->timing, &gb->memory.dmaEvent, 8);
if (gb->cpu->cycles + 8 < gb->cpu->nextEvent) { if (gb->cpu->cycles + 8 < gb->cpu->nextEvent) {
gb->cpu->nextEvent = gb->cpu->cycles + 8; gb->cpu->nextEvent = gb->cpu->cycles + 8;

View File

@ -13,7 +13,7 @@
static const struct GBCartridgeOverride _overrides[] = { static const struct GBCartridgeOverride _overrides[] = {
// None yet // None yet
{ 0, 0, 0 } { 0, 0, 0, { 0 } }
}; };
bool GBOverrideFind(const struct Configuration* config, struct GBCartridgeOverride* override) { bool GBOverrideFind(const struct Configuration* config, struct GBCartridgeOverride* override) {
@ -35,6 +35,12 @@ bool GBOverrideFind(const struct Configuration* config, struct GBCartridgeOverri
snprintf(sectionName, sizeof(sectionName), "gb.override.%08X", override->headerCrc32); snprintf(sectionName, sizeof(sectionName), "gb.override.%08X", override->headerCrc32);
const char* model = ConfigurationGetValue(config, sectionName, "model"); const char* model = ConfigurationGetValue(config, sectionName, "model");
const char* mbc = ConfigurationGetValue(config, sectionName, "mbc"); const char* mbc = ConfigurationGetValue(config, sectionName, "mbc");
const char* pal[4] = {
ConfigurationGetValue(config, sectionName, "pal[0]"),
ConfigurationGetValue(config, sectionName, "pal[1]"),
ConfigurationGetValue(config, sectionName, "pal[2]"),
ConfigurationGetValue(config, sectionName, "pal[3]")
};
if (model) { if (model) {
if (strcasecmp(model, "DMG") == 0) { if (strcasecmp(model, "DMG") == 0) {
@ -63,6 +69,21 @@ bool GBOverrideFind(const struct Configuration* config, struct GBCartridgeOverri
found = true; found = true;
} }
} }
if (pal[0] && pal[1] && pal[2] && pal[3]) {
int i;
for (i = 0; i < 4; ++i) {
char* end;
unsigned long value = strtoul(pal[i], &end, 10);
if (end == &pal[i][1] && *end == 'x') {
value = strtoul(pal[i], &end, 16);
}
if (*end) {
continue;
}
override->gbColors[i] = value;
}
}
} }
return found; return found;
} }
@ -89,6 +110,12 @@ void GBOverrideSave(struct Configuration* config, const struct GBCartridgeOverri
} }
ConfigurationSetValue(config, sectionName, "model", model); ConfigurationSetValue(config, sectionName, "model", model);
if (override->gbColors[0] | override->gbColors[1] | override->gbColors[2] | override->gbColors[3]) {
ConfigurationSetIntValue(config, sectionName, "pal[0]", override->gbColors[0]);
ConfigurationSetIntValue(config, sectionName, "pal[1]", override->gbColors[1]);
ConfigurationSetIntValue(config, sectionName, "pal[2]", override->gbColors[2]);
ConfigurationSetIntValue(config, sectionName, "pal[3]", override->gbColors[3]);
}
if (override->mbc != GB_MBC_AUTODETECT) { if (override->mbc != GB_MBC_AUTODETECT) {
ConfigurationSetIntValue(config, sectionName, "mbc", override->mbc); ConfigurationSetIntValue(config, sectionName, "mbc", override->mbc);
} else { } else {
@ -105,6 +132,13 @@ void GBOverrideApply(struct GB* gb, const struct GBCartridgeOverride* override)
gb->memory.mbcType = override->mbc; gb->memory.mbcType = override->mbc;
GBMBCInit(gb); GBMBCInit(gb);
} }
if (override->gbColors[0] | override->gbColors[1] | override->gbColors[2] | override->gbColors[3]) {
GBVideoSetPalette(&gb->video, 0, override->gbColors[0]);
GBVideoSetPalette(&gb->video, 1, override->gbColors[1]);
GBVideoSetPalette(&gb->video, 2, override->gbColors[2]);
GBVideoSetPalette(&gb->video, 3, override->gbColors[3]);
}
} }
void GBOverrideApplyDefaults(struct GB* gb) { void GBOverrideApplyDefaults(struct GB* gb) {

View File

@ -67,8 +67,6 @@ static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum G
softwareRenderer->currentWy = 0; softwareRenderer->currentWy = 0;
softwareRenderer->wx = 0; softwareRenderer->wx = 0;
softwareRenderer->model = model; softwareRenderer->model = model;
_clearScreen(softwareRenderer);
} }
static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer) { static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer) {

View File

@ -59,25 +59,6 @@ void GBSerialize(struct GB* gb, struct GBSerializedState* state) {
GBVideoSerialize(&gb->video, state); GBVideoSerialize(&gb->video, state);
GBTimerSerialize(&gb->timer, state); GBTimerSerialize(&gb->timer, state);
GBAudioSerialize(&gb->audio, 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) { bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
@ -188,8 +169,8 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
} }
GBMemoryDeserialize(gb, state); GBMemoryDeserialize(gb, state);
GBIODeserialize(gb, state);
GBVideoDeserialize(&gb->video, state); GBVideoDeserialize(&gb->video, state);
GBIODeserialize(gb, state);
GBTimerDeserialize(&gb->timer, state); GBTimerDeserialize(&gb->timer, state);
GBAudioDeserialize(&gb->audio, state); GBAudioDeserialize(&gb->audio, state);

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/internal/gb/timer.h> #include <mgba/internal/gb/timer.h>
#include <mgba/internal/lr35902/lr35902.h>
#include <mgba/internal/gb/gb.h> #include <mgba/internal/gb/gb.h>
#include <mgba/internal/gb/io.h> #include <mgba/internal/gb/io.h>
#include <mgba/internal/gb/serialize.h> #include <mgba/internal/gb/serialize.h>
@ -18,22 +19,26 @@ void _GBTimerIRQ(struct mTiming* timing, void* context, uint32_t cyclesLate) {
GBUpdateIRQs(timer->p); GBUpdateIRQs(timer->p);
} }
void _GBTimerIncrement(struct mTiming* timing, void* context, uint32_t cyclesLate) { static void _GBTimerDivIncrement(struct GBTimer* timer, uint32_t cyclesLate) {
struct GBTimer* timer = context; while (timer->nextDiv >= GB_DMG_DIV_PERIOD) {
timer->nextDiv += cyclesLate;
while (timer->nextDiv > 0) {
timer->nextDiv -= GB_DMG_DIV_PERIOD; timer->nextDiv -= GB_DMG_DIV_PERIOD;
// Make sure to trigger when the correct bit is a falling edge // Make sure to trigger when the correct bit is a falling edge
if (timer->timaPeriod > 0 && (timer->internalDiv & (timer->timaPeriod - 1)) == timer->timaPeriod - 1) { if (timer->timaPeriod > 0 && (timer->internalDiv & (timer->timaPeriod - 1)) == timer->timaPeriod - 1) {
++timer->p->memory.io[REG_TIMA]; ++timer->p->memory.io[REG_TIMA];
if (!timer->p->memory.io[REG_TIMA]) { if (!timer->p->memory.io[REG_TIMA]) {
mTimingSchedule(timing, &timer->irq, 4 - cyclesLate); mTimingSchedule(&timer->p->timing, &timer->irq, 4 - cyclesLate);
} }
} }
++timer->internalDiv; ++timer->internalDiv;
timer->p->memory.io[REG_DIV] = timer->internalDiv >> 4; timer->p->memory.io[REG_DIV] = timer->internalDiv >> 4;
} }
}
void _GBTimerUpdate(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct GBTimer* timer = context;
timer->nextDiv += cyclesLate;
_GBTimerDivIncrement(timer, cyclesLate);
// Batch div increments // Batch div increments
int divsToGo = 16 - (timer->internalDiv & 15); int divsToGo = 16 - (timer->internalDiv & 15);
int timaToGo = INT_MAX; int timaToGo = INT_MAX;
@ -50,7 +55,7 @@ void _GBTimerIncrement(struct mTiming* timing, void* context, uint32_t cyclesLat
void GBTimerReset(struct GBTimer* timer) { void GBTimerReset(struct GBTimer* timer) {
timer->event.context = timer; timer->event.context = timer;
timer->event.name = "GB Timer"; timer->event.name = "GB Timer";
timer->event.callback = _GBTimerIncrement; timer->event.callback = _GBTimerUpdate;
timer->event.priority = 0x20; timer->event.priority = 0x20;
timer->irq.context = timer; timer->irq.context = timer;
timer->irq.name = "GB Timer IRQ"; timer->irq.name = "GB Timer IRQ";
@ -63,11 +68,19 @@ void GBTimerReset(struct GBTimer* timer) {
} }
void GBTimerDivReset(struct GBTimer* timer) { void GBTimerDivReset(struct GBTimer* timer) {
timer->nextDiv -= mTimingUntil(&timer->p->timing, &timer->event);
mTimingDeschedule(&timer->p->timing, &timer->event);
_GBTimerDivIncrement(timer, (timer->p->cpu->executionState + 1) & 3);
if (timer->internalDiv & (timer->timaPeriod >> 1)) {
++timer->p->memory.io[REG_TIMA];
if (!timer->p->memory.io[REG_TIMA]) {
mTimingSchedule(&timer->p->timing, &timer->irq, 4 - (timer->p->cpu->executionState + 1) & 3);
}
}
timer->p->memory.io[REG_DIV] = 0; timer->p->memory.io[REG_DIV] = 0;
timer->internalDiv = 0; timer->internalDiv = 0;
timer->nextDiv = GB_DMG_DIV_PERIOD; timer->nextDiv = GB_DMG_DIV_PERIOD;
mTimingDeschedule(&timer->p->timing, &timer->event); mTimingSchedule(&timer->p->timing, &timer->event, timer->nextDiv - ((timer->p->cpu->executionState + 1) & 3));
mTimingSchedule(&timer->p->timing, &timer->event, timer->nextDiv);
} }
uint8_t GBTimerUpdateTAC(struct GBTimer* timer, GBRegisterTAC tac) { uint8_t GBTimerUpdateTAC(struct GBTimer* timer, GBRegisterTAC tac) {
@ -86,6 +99,12 @@ uint8_t GBTimerUpdateTAC(struct GBTimer* timer, GBRegisterTAC tac) {
timer->timaPeriod = 256 >> 4; timer->timaPeriod = 256 >> 4;
break; break;
} }
timer->nextDiv -= mTimingUntil(&timer->p->timing, &timer->event);
mTimingDeschedule(&timer->p->timing, &timer->event);
_GBTimerDivIncrement(timer, (timer->p->cpu->executionState + 1) & 3);
timer->nextDiv += GB_DMG_DIV_PERIOD;
mTimingSchedule(&timer->p->timing, &timer->event, timer->nextDiv);
} else { } else {
timer->timaPeriod = 0; timer->timaPeriod = 0;
} }

View File

@ -185,9 +185,6 @@ void _endMode1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
next = GB_VIDEO_MODE_2_LENGTH + (video->p->memory.io[REG_SCX] & 7); next = GB_VIDEO_MODE_2_LENGTH + (video->p->memory.io[REG_SCX] & 7);
video->mode = 2; video->mode = 2;
video->modeEvent.callback = _endMode2; video->modeEvent.callback = _endMode2;
if (video->p->memory.mbcType == GB_MBC7 && video->p->memory.rotation && video->p->memory.rotation->sample) {
video->p->memory.rotation->sample(video->p->memory.rotation);
}
} else if (video->ly == GB_VIDEO_VERTICAL_TOTAL_PIXELS) { } else if (video->ly == GB_VIDEO_VERTICAL_TOTAL_PIXELS) {
video->p->memory.io[REG_LY] = 0; video->p->memory.io[REG_LY] = 0;
next = GB_VIDEO_HORIZONTAL_LENGTH - 8; next = GB_VIDEO_HORIZONTAL_LENGTH - 8;
@ -471,11 +468,11 @@ void GBVideoSwitchBank(struct GBVideo* video, uint8_t value) {
video->vramCurrentBank = value; video->vramCurrentBank = value;
} }
void GBVideoSetPalette(struct GBVideo* video, unsigned index, uint16_t color) { void GBVideoSetPalette(struct GBVideo* video, unsigned index, uint32_t color) {
if (index >= 4) { if (index >= 4) {
return; return;
} }
video->dmgPalette[index] = color; video->dmgPalette[index] = M_RGB8_TO_RGB5(color);
} }
static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model) { static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model) {
@ -628,4 +625,7 @@ void GBVideoDeserialize(struct GBVideo* video, const struct GBSerializedState* s
_cleanOAM(video, video->ly); _cleanOAM(video, video->ly);
GBVideoSwitchBank(video, video->vramCurrentBank); GBVideoSwitchBank(video, video->vramCurrentBank);
video->renderer->deinit(video->renderer);
video->renderer->init(video->renderer, video->p->model);
} }

View File

@ -256,18 +256,23 @@ static void _MidiKey2Freq(struct GBA* gba) {
static void _Div(struct GBA* gba, int32_t num, int32_t denom) { static void _Div(struct GBA* gba, int32_t num, int32_t denom) {
struct ARMCore* cpu = gba->cpu; struct ARMCore* cpu = gba->cpu;
if (denom != 0) { if (denom != 0 && (denom != -1 || num != INT32_MIN)) {
div_t result = div(num, denom); div_t result = div(num, denom);
cpu->gprs[0] = result.quot; cpu->gprs[0] = result.quot;
cpu->gprs[1] = result.rem; cpu->gprs[1] = result.rem;
cpu->gprs[3] = abs(result.quot); cpu->gprs[3] = abs(result.quot);
} else { } else if (denom == 0) {
mLOG(GBA_BIOS, GAME_ERROR, "Attempting to divide %i by zero!", num); 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 // 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... // emulate in HLE, and no game will get into a state where it hangs...
cpu->gprs[0] = (num < 0) ? -1 : 1; cpu->gprs[0] = (num < 0) ? -1 : 1;
cpu->gprs[1] = num; cpu->gprs[1] = num;
cpu->gprs[3] = 1; 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;
} }
} }

View File

@ -45,6 +45,80 @@ const static struct mCoreChannelInfo _GBAAudioChannels[] = {
{ 5, "chB", "FIFO Channel B", NULL }, { 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 mVideoLogContext;
struct GBACore { struct GBACore {
struct mCore d; struct mCore d;
@ -528,6 +602,67 @@ static void _GBACoreRawWrite32(struct mCore* core, uint32_t address, int segment
GBAPatch32(cpu, address, value, NULL); 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 #ifdef USE_DEBUGGERS
static bool _GBACoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) { static bool _GBACoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType type) {
UNUSED(core); UNUSED(core);
@ -757,6 +892,8 @@ struct mCore* GBACoreCreate(void) {
core->rawWrite8 = _GBACoreRawWrite8; core->rawWrite8 = _GBACoreRawWrite8;
core->rawWrite16 = _GBACoreRawWrite16; core->rawWrite16 = _GBACoreRawWrite16;
core->rawWrite32 = _GBACoreRawWrite32; core->rawWrite32 = _GBACoreRawWrite32;
core->listMemoryBlocks = _GBAListMemoryBlocks;
core->getMemoryBlock = _GBAGetMemoryBlock;
#ifdef USE_DEBUGGERS #ifdef USE_DEBUGGERS
core->supportsDebuggerType = _GBACoreSupportsDebuggerType; core->supportsDebuggerType = _GBACoreSupportsDebuggerType;
core->debuggerPlatform = _GBACoreDebuggerPlatform; core->debuggerPlatform = _GBACoreDebuggerPlatform;

View File

@ -119,5 +119,5 @@ static void _save(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
struct GBACLIDebugger* gbaDebugger = (struct GBACLIDebugger*) debugger->system; 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);
} }

View File

@ -59,12 +59,8 @@ void GBAMemoryInit(struct GBA* gba) {
for (i = 0; i < 16; ++i) { for (i = 0; i < 16; ++i) {
gba->memory.waitstatesNonseq16[i] = GBA_BASE_WAITSTATES[i]; gba->memory.waitstatesNonseq16[i] = GBA_BASE_WAITSTATES[i];
gba->memory.waitstatesSeq16[i] = GBA_BASE_WAITSTATES_SEQ[i]; gba->memory.waitstatesSeq16[i] = GBA_BASE_WAITSTATES_SEQ[i];
gba->memory.waitstatesPrefetchNonseq16[i] = GBA_BASE_WAITSTATES[i];
gba->memory.waitstatesPrefetchSeq16[i] = GBA_BASE_WAITSTATES_SEQ[i];
gba->memory.waitstatesNonseq32[i] = GBA_BASE_WAITSTATES_32[i]; gba->memory.waitstatesNonseq32[i] = GBA_BASE_WAITSTATES_32[i];
gba->memory.waitstatesSeq32[i] = GBA_BASE_WAITSTATES_SEQ_32[i]; gba->memory.waitstatesSeq32[i] = GBA_BASE_WAITSTATES_SEQ_32[i];
gba->memory.waitstatesPrefetchNonseq32[i] = GBA_BASE_WAITSTATES_32[i];
gba->memory.waitstatesPrefetchSeq32[i] = GBA_BASE_WAITSTATES_SEQ_32[i];
} }
for (; i < 256; ++i) { for (; i < 256; ++i) {
gba->memory.waitstatesNonseq16[i] = 0; gba->memory.waitstatesNonseq16[i] = 0;
@ -1505,33 +1501,32 @@ int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) {
previousLoads = dist; previousLoads = dist;
} }
int32_t s = cpu->memory.activeSeqCycles16 + 1; int32_t s = cpu->memory.activeSeqCycles16;
int32_t n2s = cpu->memory.activeNonseqCycles16 - cpu->memory.activeSeqCycles16 + 1; int32_t n2s = cpu->memory.activeNonseqCycles16 - cpu->memory.activeSeqCycles16;
// Figure out how many sequential loads we can jam in // Figure out how many sequential loads we can jam in
int32_t stall = s; int32_t stall = s;
int32_t loads = 1; int32_t loads = 1;
if (stall > wait && !previousLoads) { if (stall < wait) {
// We might need to stall a bit extra if we haven't finished the first S cycle int32_t maxLoads = 8 - previousLoads;
wait = stall; while (stall < wait && loads < maxLoads) {
} else {
while (stall < wait) {
stall += s; stall += s;
++loads; ++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. // This instruction used to have an N, convert it to an S.
wait -= n2s; wait -= n2s;
// TODO: Invalidate prefetch on branch memory->lastPrefetchedPc = cpu->gprs[ARM_PC] + WORD_SIZE_THUMB * (loads + previousLoads - 1);
memory->lastPrefetchedPc = cpu->gprs[ARM_PC] + WORD_SIZE_THUMB * loads;
// The next |loads|S waitstates disappear entirely, so long as they're all in a row // 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; return wait;
} }

View File

@ -69,25 +69,6 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) {
GBAAudioSerialize(&gba->audio, state); GBAAudioSerialize(&gba->audio, state);
GBASavedataSerialize(&gba->memory.savedata, 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; state->associatedStreamId = 0;
if (gba->rr) { if (gba->rr) {
gba->rr->stateSaved(gba->rr, state); gba->rr->stateSaved(gba->rr, state);

View File

@ -115,24 +115,14 @@ bool GBASavedataImportSharkPort(struct GBA* gba, struct VFile* vf, bool testChec
uint32_t copySize = size - 0x1C; uint32_t copySize = size - 0x1C;
switch (gba->memory.savedata.type) { switch (gba->memory.savedata.type) {
case SAVEDATA_SRAM:
if (copySize > SIZE_CART_SRAM) {
copySize = SIZE_CART_SRAM;
}
break;
case SAVEDATA_FLASH512: case SAVEDATA_FLASH512:
if (copySize > SIZE_CART_FLASH512) { if (copySize > SIZE_CART_FLASH512) {
GBASavedataForceType(&gba->memory.savedata, SAVEDATA_FLASH1M, gba->memory.savedata.realisticTiming); GBASavedataForceType(&gba->memory.savedata, SAVEDATA_FLASH1M, gba->memory.savedata.realisticTiming);
} }
// Fall through // Fall through
case SAVEDATA_FLASH1M: default:
if (copySize > SIZE_CART_FLASH1M) { if (copySize > GBASavedataSize(&gba->memory.savedata)) {
copySize = SIZE_CART_FLASH1M; copySize = GBASavedataSize(&gba->memory.savedata);
}
break;
case SAVEDATA_EEPROM:
if (copySize > SIZE_CART_EEPROM) {
copySize = SAVEDATA_EEPROM;
} }
break; break;
case SAVEDATA_FORCE_NONE: case SAVEDATA_FORCE_NONE:
@ -141,6 +131,7 @@ bool GBASavedataImportSharkPort(struct GBA* gba, struct VFile* vf, bool testChec
} }
memcpy(gba->memory.savedata.data, &payload[0x1C], copySize); memcpy(gba->memory.savedata.data, &payload[0x1C], copySize);
gba->memory.savedata.vf && gba->memory.savedata.vf->sync(gba->memory.savedata.vf, gba->memory.savedata.data, size);
free(payload); free(payload);
return true; return true;

View File

@ -83,13 +83,13 @@ void GBATimerInit(struct GBA* gba) {
void GBATimerUpdateRegister(struct GBA* gba, int timer) { void GBATimerUpdateRegister(struct GBA* gba, int timer) {
struct GBATimer* currentTimer = &gba->timers[timer]; struct GBATimer* currentTimer = &gba->timers[timer];
if (GBATimerFlagsIsEnable(currentTimer->flags) && !GBATimerFlagsIsCountUp(currentTimer->flags)) { if (GBATimerFlagsIsEnable(currentTimer->flags) && !GBATimerFlagsIsCountUp(currentTimer->flags)) {
int32_t prefetchSkew = 0; int32_t prefetchSkew = -2;
if (gba->memory.lastPrefetchedPc >= (uint32_t) gba->cpu->gprs[ARM_PC]) { 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; prefetchSkew += ((gba->memory.lastPrefetchedPc - gba->cpu->gprs[ARM_PC]) * gba->cpu->memory.activeSeqCycles16) / WORD_SIZE_THUMB;
} }
// Reading this takes two cycles (1N+1I), so let's remove them preemptively // Reading this takes two cycles (1N+1I), so let's remove them preemptively
int32_t diff = gba->cpu->cycles - (currentTimer->lastEvent - gba->timing.masterCycles); int32_t diff = gba->cpu->cycles - (currentTimer->lastEvent - gba->timing.masterCycles);
gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((diff - 2 + prefetchSkew) >> GBATimerFlagsGetPrescaleBits(currentTimer->flags)); gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((diff + prefetchSkew) >> GBATimerFlagsGetPrescaleBits(currentTimer->flags));
} }
} }

View File

@ -6,6 +6,7 @@
#include <mgba/internal/lr35902/debugger/debugger.h> #include <mgba/internal/lr35902/debugger/debugger.h>
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba/internal/lr35902/decoder.h>
#include <mgba/internal/lr35902/lr35902.h> #include <mgba/internal/lr35902/lr35902.h>
#include <mgba/internal/lr35902/debugger/memory-debugger.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 LR35902DebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment);
static void LR35902DebuggerCheckBreakpoints(struct mDebuggerPlatform*); static void LR35902DebuggerCheckBreakpoints(struct mDebuggerPlatform*);
static bool LR35902DebuggerHasBreakpoints(struct mDebuggerPlatform*); static bool LR35902DebuggerHasBreakpoints(struct mDebuggerPlatform*);
static void LR35902DebuggerTrace(struct mDebuggerPlatform*, char* out, size_t* length);
struct mDebuggerPlatform* LR35902DebuggerPlatformCreate(void) { struct mDebuggerPlatform* LR35902DebuggerPlatformCreate(void) {
struct mDebuggerPlatform* platform = (struct mDebuggerPlatform*) malloc(sizeof(struct LR35902Debugger)); struct mDebuggerPlatform* platform = (struct mDebuggerPlatform*) malloc(sizeof(struct LR35902Debugger));
@ -60,6 +62,7 @@ struct mDebuggerPlatform* LR35902DebuggerPlatformCreate(void) {
platform->clearWatchpoint = LR35902DebuggerClearWatchpoint; platform->clearWatchpoint = LR35902DebuggerClearWatchpoint;
platform->checkBreakpoints = LR35902DebuggerCheckBreakpoints; platform->checkBreakpoints = LR35902DebuggerCheckBreakpoints;
platform->hasBreakpoints = LR35902DebuggerHasBreakpoints; platform->hasBreakpoints = LR35902DebuggerHasBreakpoints;
platform->trace = LR35902DebuggerTrace;
return platform; return platform;
} }
@ -137,3 +140,31 @@ static void LR35902DebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t
LR35902DebuggerRemoveMemoryShim(debugger); 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);
}

View File

@ -175,7 +175,7 @@ static void mGLES2ContextResized(struct VideoBackend* v, unsigned w, unsigned h)
drawW -= drawW % v->width; drawW -= drawW % v->width;
drawH -= drawH % v->height; drawH -= drawH % v->height;
} }
glViewport(0, 0, v->width, v->height); glViewport(0, 0, w, h);
glClearColor(0.f, 0.f, 0.f, 1.f); glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
glViewport((w - drawW) / 2, (h - drawH) / 2, drawW, drawH); glViewport((w - drawW) / 2, (h - drawH) / 2, drawW, drawH);
@ -203,13 +203,13 @@ void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
int drawH = shader->height; int drawH = shader->height;
int padW = 0; int padW = 0;
int padH = 0; int padH = 0;
if (!shader->width) { if (!drawW) {
drawW = viewport[2]; drawW = viewport[2];
padW = viewport[0]; padW = viewport[0];
} else if (shader->width < 0) { } else if (shader->width < 0) {
drawW = context->d.width * -shader->width; drawW = context->d.width * -shader->width;
} }
if (!shader->height) { if (!drawH) {
drawH = viewport[3]; drawH = viewport[3];
padH = viewport[1]; padH = viewport[1];
} else if (shader->height < 0) { } else if (shader->height < 0) {
@ -234,7 +234,7 @@ void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST);
glUseProgram(shader->program); glUseProgram(shader->program);
glUniform1i(shader->texLocation, 0); glUniform1i(shader->texLocation, 0);
glUniform2f(shader->texSizeLocation, context->d.width, context->d.height); glUniform2f(shader->texSizeLocation, context->d.width - padW, context->d.height - padH);
glVertexAttribPointer(shader->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, _vertices); glVertexAttribPointer(shader->positionLocation, 2, GL_FLOAT, GL_FALSE, 0, _vertices);
glEnableVertexAttribArray(shader->positionLocation); glEnableVertexAttribArray(shader->positionLocation);
size_t u; size_t u;
@ -290,7 +290,6 @@ void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
} }
glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBindTexture(GL_TEXTURE_2D, shader->tex); glBindTexture(GL_TEXTURE_2D, shader->tex);
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
} }
void mGLES2ContextDrawFrame(struct VideoBackend* v) { void mGLES2ContextDrawFrame(struct VideoBackend* v) {
@ -298,12 +297,17 @@ void mGLES2ContextDrawFrame(struct VideoBackend* v) {
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, context->tex); glBindTexture(GL_TEXTURE_2D, context->tex);
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
context->finalShader.filter = v->filter; context->finalShader.filter = v->filter;
_drawShader(context, &context->initialShader); _drawShader(context, &context->initialShader);
size_t n; size_t n;
for (n = 0; n < context->nShaders; ++n) { for (n = 0; n < context->nShaders; ++n) {
glViewport(0, 0, viewport[2], viewport[3]);
_drawShader(context, &context->shaders[n]); _drawShader(context, &context->shaders[n]);
} }
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
_drawShader(context, &context->finalShader); _drawShader(context, &context->finalShader);
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
glUseProgram(0); glUseProgram(0);

View File

@ -1,12 +1,12 @@
find_program(PYTHON python) 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) get_property(INCLUDE_DIRECTORIES DIRECTORY PROPERTY INCLUDE_DIRECTORIES)
set(INCLUDE_FLAGS) set(INCLUDE_FLAGS)
foreach(DIR IN LISTS INCLUDE_DIRECTORIES) foreach(DIR IN LISTS INCLUDE_DIRECTORIES)
list(APPEND INCLUDE_FLAGS "-I${DIR}") list(APPEND INCLUDE_FLAGS "-I${DIR}")
endforeach() 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 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} 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} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}

View File

@ -28,7 +28,9 @@ void free(void*);
#include "flags.h" #include "flags.h"
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba/core/mem-search.h>
#include <mgba/core/tile-cache.h> #include <mgba/core/tile-cache.h>
#include <mgba/core/version.h>
#define PYEXPORT extern "Python+C" #define PYEXPORT extern "Python+C"
#include "platform/python/log.h" #include "platform/python/log.h"

View File

@ -21,7 +21,9 @@ ffi.set_source("mgba._pylib", """
#include <mgba-util/common.h> #include <mgba-util/common.h>
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba/core/log.h> #include <mgba/core/log.h>
#include <mgba/core/mem-search.h>
#include <mgba/core/tile-cache.h> #include <mgba/core/tile-cache.h>
#include <mgba/core/version.h>
#include <mgba/internal/arm/arm.h> #include <mgba/internal/arm/arm.h>
#include <mgba/internal/gba/gba.h> #include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/input.h> #include <mgba/internal/gba/input.h>

View File

@ -5,6 +5,8 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
from ._pylib import ffi, lib from ._pylib import ffi, lib
from collections import namedtuple
def createCallback(structName, cbName, funcName=None): def createCallback(structName, cbName, funcName=None):
funcName = funcName or "_py{}{}".format(structName, cbName[0].upper() + cbName[1:]) funcName = funcName or "_py{}{}".format(structName, cbName[0].upper() + cbName[1:])
fullStruct = "struct {}*".format(structName) fullStruct = "struct {}*".format(structName)
@ -13,3 +15,19 @@ def createCallback(structName, cbName, funcName=None):
return getattr(ffi.from_handle(h.pyobj), cbName)(*args) return getattr(ffi.from_handle(h.pyobj), cbName)(*args)
return ffi.def_extern(name=funcName)(cb) return ffi.def_extern(name=funcName)(cb)
version = ffi.string(lib.projectVersion).decode('utf-8')
GitInfo = namedtuple("GitInfo", "commit commitShort branch revision")
git = {}
if lib.gitCommit and lib.gitCommit != "(unknown)":
git['commit'] = ffi.string(lib.gitCommit).decode('utf-8')
if lib.gitCommitShort and lib.gitCommitShort != "(unknown)":
git['commitShort'] = ffi.string(lib.gitCommitShort).decode('utf-8')
if lib.gitBranch and lib.gitBranch != "(unknown)":
git['branch'] = ffi.string(lib.gitBranch).decode('utf-8')
if lib.gitRevision > 0:
git['revision'] = lib.gitRevision
git = GitInfo(**git)

View File

@ -6,64 +6,74 @@
from ._pylib import ffi, lib from ._pylib import ffi, lib
from . import png from . import png
try:
import PIL.Image as PImage
except ImportError:
pass
class Image: class Image:
def __init__(self, width, height, stride=0): def __init__(self, width, height, stride=0):
self.width = width self.width = width
self.height = height self.height = height
self.stride = stride self.stride = stride
self.constitute() self.constitute()
def constitute(self): def constitute(self):
if self.stride <= 0: if self.stride <= 0:
self.stride = self.width self.stride = self.width
self.buffer = ffi.new("color_t[{}]".format(self.stride * self.height)) self.buffer = ffi.new("color_t[{}]".format(self.stride * self.height))
def savePNG(self, f): def savePNG(self, f):
p = png.PNG(f) p = png.PNG(f)
success = p.writeHeader(self) success = p.writeHeader(self)
success = success and p.writePixels(self) success = success and p.writePixels(self)
p.writeClose() p.writeClose()
return success return success
if 'PImage' in globals():
def toPIL(self):
return PImage.frombytes("RGBX", (self.width, self.height), ffi.buffer(self.buffer), "raw",
"RGBX", self.stride * 4)
def u16ToU32(c): def u16ToU32(c):
r = c & 0x1F r = c & 0x1F
g = (c >> 5) & 0x1F g = (c >> 5) & 0x1F
b = (c >> 10) & 0x1F b = (c >> 10) & 0x1F
a = (c >> 15) & 1 a = (c >> 15) & 1
abgr = r << 3 abgr = r << 3
abgr |= g << 11 abgr |= g << 11
abgr |= b << 19 abgr |= b << 19
abgr |= (a * 0xFF) << 24 abgr |= (a * 0xFF) << 24
return abgr return abgr
def u32ToU16(c): def u32ToU16(c):
r = (c >> 3) & 0x1F r = (c >> 3) & 0x1F
g = (c >> 11) & 0x1F g = (c >> 11) & 0x1F
b = (c >> 19) & 0x1F b = (c >> 19) & 0x1F
a = c >> 31 a = c >> 31
abgr = r abgr = r
abgr |= g << 5 abgr |= g << 5
abgr |= b << 10 abgr |= b << 10
abgr |= a << 15 abgr |= a << 15
return abgr return abgr
if ffi.sizeof("color_t") == 2: if ffi.sizeof("color_t") == 2:
def colorToU16(c): def colorToU16(c):
return c return c
colorToU32 = u16ToU32 colorToU32 = u16ToU32
def u16ToColor(c): def u16ToColor(c):
return c return c
u32ToColor = u32ToU16 u32ToColor = u32ToU16
else: else:
def colorToU32(c): def colorToU32(c):
return c return c
colorToU16 = u32ToU16 colorToU16 = u32ToU16
def u32ToColor(c): def u32ToColor(c):
return c return c
u16ToColor = u16ToU32 u16ToColor = u16ToU32

View File

@ -8,29 +8,33 @@ from . import createCallback
createCallback("mLoggerPy", "log", "_pyLog") createCallback("mLoggerPy", "log", "_pyLog")
defaultLogger = None
def installDefault(logger): def installDefault(logger):
lib.mLogSetDefaultLogger(logger._native) global defaultLogger
defaultLogger = logger
lib.mLogSetDefaultLogger(logger._native)
class Logger(object): class Logger(object):
FATAL = lib.mLOG_FATAL FATAL = lib.mLOG_FATAL
DEBUG = lib.mLOG_DEBUG DEBUG = lib.mLOG_DEBUG
INFO = lib.mLOG_INFO INFO = lib.mLOG_INFO
WARN = lib.mLOG_WARN WARN = lib.mLOG_WARN
ERROR = lib.mLOG_ERROR ERROR = lib.mLOG_ERROR
STUB = lib.mLOG_STUB STUB = lib.mLOG_STUB
GAME_ERROR = lib.mLOG_GAME_ERROR GAME_ERROR = lib.mLOG_GAME_ERROR
def __init__(self): def __init__(self):
self._handle = ffi.new_handle(self) self._handle = ffi.new_handle(self)
self._native = ffi.gc(lib.mLoggerPythonCreate(self._handle), lib.free) self._native = ffi.gc(lib.mLoggerPythonCreate(self._handle), lib.free)
@staticmethod @staticmethod
def categoryName(category): def categoryName(category):
return ffi.string(lib.mLogCategoryName(category)).decode('UTF-8') return ffi.string(lib.mLogCategoryName(category)).decode('UTF-8')
def log(self, category, level, message): def log(self, category, level, message):
print("{}: {}".format(self.categoryName(category), message)) print("{}: {}".format(self.categoryName(category), message))
class NullLogger(Logger): class NullLogger(Logger):
def log(self, category, level, message): def log(self, category, level, message):
pass pass

View File

@ -67,10 +67,53 @@ class MemoryView(object):
self._addrCheck(address) self._addrCheck(address)
self._rawWrite(self._core, self._base + address, segment, value & self._mask) 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): 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): def __init__(self, core, size, base=0):
self.size = size self.size = size
self.base = base self.base = base
self._core = core
self.u8 = MemoryView(core, 1, size, base, "u") self.u8 = MemoryView(core, 1, size, base, "u")
self.u16 = MemoryView(core, 2, size, base, "u") self.u16 = MemoryView(core, 2, size, base, "u")
@ -81,3 +124,32 @@ class Memory(object):
def __len__(self): def __len__(self):
return self._size 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

View File

@ -1,5 +1,10 @@
from setuptools import setup from setuptools import setup
import re 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 = [ classifiers = [
"Programming Language :: C", "Programming Language :: C",
@ -18,6 +23,7 @@ setup(name="${BINARY_NAME}",
packages=["mgba"], packages=["mgba"],
setup_requires=['cffi>=1.6'], setup_requires=['cffi>=1.6'],
install_requires=['cffi>=1.6', 'cached-property'], install_requires=['cffi>=1.6', 'cached-property'],
extras_require={'pil': ['Pillow>=2.3']},
cffi_modules=["_builder.py:ffi"], cffi_modules=["_builder.py:ffi"],
license="MPL 2.0", license="MPL 2.0",
classifiers=classifiers classifiers=classifiers

View File

@ -85,6 +85,7 @@ set(SOURCE_FILES
LogController.cpp LogController.cpp
LogView.cpp LogView.cpp
MemoryModel.cpp MemoryModel.cpp
MemorySearch.cpp
MemoryView.cpp MemoryView.cpp
MessagePainter.cpp MessagePainter.cpp
MultiplayerController.cpp MultiplayerController.cpp
@ -115,6 +116,7 @@ set(UI_FILES
IOViewer.ui IOViewer.ui
LoadSaveState.ui LoadSaveState.ui
LogView.ui LogView.ui
MemorySearch.ui
MemoryView.ui MemoryView.ui
ObjView.ui ObjView.ui
OverrideView.ui OverrideView.ui

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GBAKeyEditor.h" #include "GBAKeyEditor.h"
#include <QApplication>
#include <QComboBox> #include <QComboBox>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QPaintEvent> #include <QPaintEvent>
@ -182,10 +183,20 @@ bool GBAKeyEditor::event(QEvent* event) {
} }
bool GBAKeyEditor::eventFilter(QObject* obj, QEvent* event) { bool GBAKeyEditor::eventFilter(QObject* obj, QEvent* event) {
KeyEditor* keyEditor = static_cast<KeyEditor*>(obj);
if (event->type() == QEvent::FocusOut) {
keyEditor->setPalette(QApplication::palette(keyEditor));
}
if (event->type() != QEvent::FocusIn) { if (event->type() != QEvent::FocusIn) {
return false; return false;
} }
findFocus(static_cast<KeyEditor*>(obj));
QPalette palette = keyEditor->palette();
palette.setBrush(keyEditor->backgroundRole(), palette.highlight());
palette.setBrush(keyEditor->foregroundRole(), palette.highlightedText());
keyEditor->setPalette(palette);
findFocus(keyEditor);
return true; return true;
} }

View File

@ -40,7 +40,7 @@ using namespace std;
GameController::GameController(QObject* parent) GameController::GameController(QObject* parent)
: QObject(parent) : QObject(parent)
, m_audioProcessor(AudioProcessor::create()) , 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) , m_loadStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_RTC)
{ {
#ifdef M_CORE_GBA #ifdef M_CORE_GBA
@ -162,6 +162,7 @@ GameController::GameController(QObject* parent)
default: default:
break; break;
} }
mTileCacheDeinit(controller->m_tileCache.get());
controller->m_tileCache.reset(); controller->m_tileCache.reset();
} }
@ -696,6 +697,7 @@ void GameController::threadContinue() {
void GameController::frameAdvance() { void GameController::frameAdvance() {
if (m_pauseAfterFrame.testAndSetRelaxed(false, true)) { if (m_pauseAfterFrame.testAndSetRelaxed(false, true)) {
setPaused(false); setPaused(false);
m_wasPaused = true;
} }
} }

View File

@ -188,7 +188,7 @@ void LoadSaveState::loadState(int slot) {
return; return;
} }
QDateTime creation/*(QDateTime::fromMSecsSinceEpoch(state->creationUsec / 1000LL))*/; // TODO QDateTime creation;
QImage stateImage; QImage stateImage;
unsigned width, height; unsigned width, height;
@ -198,6 +198,12 @@ void LoadSaveState::loadState(int slot) {
stateImage = QImage((uchar*) item.data, width, height, QImage::Format_ARGB32).rgbSwapped(); 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()) { if (!stateImage.isNull()) {
QPixmap statePixmap; QPixmap statePixmap;
statePixmap.convertFromImage(stateImage); statePixmap.convertFromImage(stateImage);

View File

@ -0,0 +1,204 @@
/* 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"
#include "MemoryView.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);
connect(m_ui.viewMem, &QPushButton::clicked, this, &MemorySearch::openMemory);
}
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()) {
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(&params)) {
mCoreMemorySearch(core, &params, &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(&params)) {
mCoreMemorySearchRepeat(core, &params, &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);
}
void MemorySearch::openMemory() {
auto items = m_ui.results->selectedItems();
if (items.empty()) {
return;
}
QTableWidgetItem* item = items[0];
uint32_t address = item->text().toUInt(nullptr, 16);
MemoryView* memView = new MemoryView(m_controller);
memView->jumpToAddress(address);
connect(m_controller, &GameController::gameStopped, memView, &QWidget::close);
memView->setAttribute(Qt::WA_DeleteOnClose);
memView->show();
}

View File

@ -0,0 +1,47 @@
/* 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 slots:
void openMemory();
private:
bool createParams(mCoreMemorySearchParams*);
Ui::MemorySearch m_ui;
GameController* m_controller;
mCoreMemorySearchResults m_results;
QByteArray m_string;
};
}
#endif

View File

@ -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="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</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="text">
<string>Open in Memory Viewer</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>

View File

@ -9,53 +9,9 @@
#include "GameController.h" #include "GameController.h"
#include <mgba/core/core.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; 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) MemoryView::MemoryView(GameController* controller, QWidget* parent)
: QWidget(parent) : QWidget(parent)
, m_controller(controller) , m_controller(controller)
@ -65,21 +21,8 @@ MemoryView::MemoryView(GameController* controller, QWidget* parent)
m_ui.hexfield->setController(controller); m_ui.hexfield->setController(controller);
mCore* core = m_controller->thread()->core; mCore* core = m_controller->thread()->core;
const IndexInfo* info = nullptr; const mCoreMemoryBlock* info;
switch (core->platform(core)) { size_t nBlocks = core->listMemoryBlocks(core, &info);
#ifdef M_CORE_GBA
case PLATFORM_GBA:
info = indexInfoGBA;
break;
#endif
#ifdef M_CORE_GB
case PLATFORM_GB:
info = indexInfoGB;
break;
#endif
default:
break;
}
connect(m_ui.regions, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), connect(m_ui.regions, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
this, &MemoryView::setIndex); this, &MemoryView::setIndex);
@ -87,7 +30,10 @@ MemoryView::MemoryView(GameController* controller, QWidget* parent)
this, &MemoryView::setSegment); this, &MemoryView::setSegment);
if (info) { 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)); m_ui.regions->addItem(tr(info[i].longName));
} }
} }
@ -116,44 +62,22 @@ MemoryView::MemoryView(GameController* controller, QWidget* parent)
void MemoryView::setIndex(int index) { void MemoryView::setIndex(int index) {
mCore* core = m_controller->thread()->core; mCore* core = m_controller->thread()->core;
IndexInfo info; const mCoreMemoryBlock* blocks;
switch (core->platform(core)) { size_t nBlocks = core->listMemoryBlocks(core, &blocks);
#ifdef M_CORE_GBA const mCoreMemoryBlock& info = blocks[index];
case PLATFORM_GBA:
info = indexInfoGBA[index];
break;
#endif
#ifdef M_CORE_GB
case PLATFORM_GB:
info = indexInfoGB[index];
break;
#endif
default:
return;
}
m_ui.segments->setValue(-1); m_ui.segments->setValue(-1);
m_ui.segments->setVisible(info.maxSegment > 0); m_ui.segments->setVisible(info.maxSegment > 0);
m_ui.segments->setMaximum(info.maxSegment); 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) { void MemoryView::setSegment(int segment) {
mCore* core = m_controller->thread()->core; mCore* core = m_controller->thread()->core;
IndexInfo info; const mCoreMemoryBlock* blocks;
switch (core->platform(core)) { size_t nBlocks = core->listMemoryBlocks(core, &blocks);
#ifdef M_CORE_GBA const mCoreMemoryBlock& info = blocks[m_ui.regions->currentIndex()];
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;
}
m_ui.hexfield->setSegment(info.maxSegment < segment ? info.maxSegment : segment); m_ui.hexfield->setSegment(info.maxSegment < segment ? info.maxSegment : segment);
} }

View File

@ -22,6 +22,7 @@ public:
public slots: public slots:
void update(); void update();
void jumpToAddress(uint32_t address) { m_ui.hexfield->jumpToAddress(address); }
private slots: private slots:
void setIndex(int); void setIndex(int);

View File

@ -68,6 +68,7 @@ void ObjView::translateIndex(int index) {
#ifdef M_CORE_GBA #ifdef M_CORE_GBA
void ObjView::updateTilesGBA(bool force) { void ObjView::updateTilesGBA(bool force) {
m_ui.objId->setMaximum(127);
const GBA* gba = static_cast<const GBA*>(m_controller->thread()->core->board); const GBA* gba = static_cast<const GBA*>(m_controller->thread()->core->board);
const GBAObj* obj = &gba->video.oam.obj[m_objId]; const GBAObj* obj = &gba->video.oam.obj[m_objId];
@ -172,6 +173,7 @@ void ObjView::updateTilesGBA(bool force) {
#ifdef M_CORE_GB #ifdef M_CORE_GB
void ObjView::updateTilesGB(bool force) { void ObjView::updateTilesGB(bool force) {
m_ui.objId->setMaximum(39);
const GB* gb = static_cast<const GB*>(m_controller->thread()->core->board); const GB* gb = static_cast<const GB*>(m_controller->thread()->core->board);
const GBObj* obj = &gb->video.oam.obj[m_objId]; const GBObj* obj = &gb->video.oam.obj[m_objId];

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "OverrideView.h" #include "OverrideView.h"
#include <QColorDialog>
#include <QPushButton> #include <QPushButton>
#include "ConfigController.h" #include "ConfigController.h"
@ -79,6 +80,21 @@ OverrideView::OverrideView(GameController* controller, ConfigController* config,
connect(m_ui.gbModel, &QComboBox::currentTextChanged, this, &OverrideView::updateOverrides); connect(m_ui.gbModel, &QComboBox::currentTextChanged, this, &OverrideView::updateOverrides);
connect(m_ui.mbc, &QComboBox::currentTextChanged, this, &OverrideView::updateOverrides); connect(m_ui.mbc, &QComboBox::currentTextChanged, this, &OverrideView::updateOverrides);
QPalette palette = m_ui.color0->palette();
palette.setColor(backgroundRole(), QColor(0xF8, 0xF8, 0xF8));
m_ui.color0->setPalette(palette);
palette.setColor(backgroundRole(), QColor(0xA8, 0xA8, 0xA8));
m_ui.color1->setPalette(palette);
palette.setColor(backgroundRole(), QColor(0x50, 0x50, 0x50));
m_ui.color2->setPalette(palette);
palette.setColor(backgroundRole(), QColor(0x00, 0x00, 0x00));
m_ui.color3->setPalette(palette);
m_ui.color0->installEventFilter(this);
m_ui.color1->installEventFilter(this);
m_ui.color2->installEventFilter(this);
m_ui.color3->installEventFilter(this);
connect(m_ui.tabWidget, &QTabWidget::currentChanged, this, &OverrideView::updateOverrides); connect(m_ui.tabWidget, &QTabWidget::currentChanged, this, &OverrideView::updateOverrides);
#ifndef M_CORE_GBA #ifndef M_CORE_GBA
m_ui.tabWidget->removeTab(m_ui.tabWidget->indexOf(m_ui.tabGBA)); m_ui.tabWidget->removeTab(m_ui.tabWidget->indexOf(m_ui.tabGBA));
@ -96,6 +112,42 @@ OverrideView::OverrideView(GameController* controller, ConfigController* config,
} }
} }
bool OverrideView::eventFilter(QObject* obj, QEvent* event) {
#ifdef M_CORE_GB
if (event->type() != QEvent::MouseButtonRelease) {
return false;
}
int colorId;
if (obj == m_ui.color0) {
colorId = 0;
} else if (obj == m_ui.color1) {
colorId = 1;
} else if (obj == m_ui.color2) {
colorId = 2;
} else if (obj == m_ui.color3) {
colorId = 3;
} else {
return false;
}
QWidget* swatch = static_cast<QWidget*>(obj);
QColorDialog* colorPicker = new QColorDialog;
colorPicker->setAttribute(Qt::WA_DeleteOnClose);
colorPicker->open();
connect(colorPicker, &QColorDialog::colorSelected, [this, swatch, colorId](const QColor& color) {
QPalette palette = swatch->palette();
palette.setColor(backgroundRole(), color);
swatch->setPalette(palette);
m_gbColors[colorId] = color.rgb();
updateOverrides();
});
return true;
#else
return false;
#endif
}
void OverrideView::saveOverride() { void OverrideView::saveOverride() {
if (!m_config) { if (!m_config) {
return; return;
@ -155,7 +207,13 @@ void OverrideView::updateOverrides() {
GBOverride* gb = new GBOverride; GBOverride* gb = new GBOverride;
gb->override.mbc = s_mbcList[m_ui.mbc->currentIndex()]; gb->override.mbc = s_mbcList[m_ui.mbc->currentIndex()];
gb->override.model = s_gbModelList[m_ui.gbModel->currentIndex()]; gb->override.model = s_gbModelList[m_ui.gbModel->currentIndex()];
if (gb->override.mbc != GB_MBC_AUTODETECT || gb->override.model != GB_MODEL_AUTODETECT) { gb->override.gbColors[0] = m_gbColors[0];
gb->override.gbColors[1] = m_gbColors[1];
gb->override.gbColors[2] = m_gbColors[2];
gb->override.gbColors[3] = m_gbColors[3];
bool hasOverride = gb->override.mbc != GB_MBC_AUTODETECT || gb->override.model != GB_MODEL_AUTODETECT;
hasOverride = hasOverride || (m_gbColors[0] | m_gbColors[1] | m_gbColors[2] | m_gbColors[3]);
if (hasOverride) {
m_controller->setOverride(gb); m_controller->setOverride(gb);
} else { } else {
m_controller->clearOverride(); m_controller->clearOverride();

View File

@ -36,6 +36,9 @@ private slots:
void gameStarted(mCoreThread*); void gameStarted(mCoreThread*);
void gameStopped(); void gameStopped();
protected:
bool eventFilter(QObject* obj, QEvent* event) override;
private: private:
Ui::OverrideView m_ui; Ui::OverrideView m_ui;
@ -43,6 +46,8 @@ private:
ConfigController* m_config; ConfigController* m_config;
#ifdef M_CORE_GB #ifdef M_CORE_GB
uint32_t m_gbColors[4]{};
static QList<enum GBModel> s_gbModelList; static QList<enum GBModel> s_gbModelList;
static QList<enum GBMemoryBankControllerType> s_mbcList; static QList<enum GBMemoryBankControllerType> s_mbcList;
#endif #endif

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>443</width> <width>444</width>
<height>282</height> <height>284</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -326,6 +326,93 @@
</item> </item>
</widget> </widget>
</item> </item>
<item row="2" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Colors</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QFrame" name="color0">
<property name="minimumSize">
<size>
<width>30</width>
<height>30</height>
</size>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="color1">
<property name="minimumSize">
<size>
<width>30</width>
<height>30</height>
</size>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="color2">
<property name="minimumSize">
<size>
<width>30</width>
<height>30</height>
</size>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="color3">
<property name="minimumSize">
<size>
<width>30</width>
<height>30</height>
</size>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
</layout>
</item>
</layout> </layout>
</widget> </widget>
</widget> </widget>

View File

@ -88,6 +88,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
m_ui.patchPath->setText(path); m_ui.patchPath->setText(path);
} }
}); });
connect(m_ui.clearCache, &QAbstractButton::pressed, this, &SettingsView::libraryCleared);
// TODO: Move to reloadConfig() // TODO: Move to reloadConfig()
QVariant audioDriver = m_controller->getQtOption("audioDriver"); QVariant audioDriver = m_controller->getQtOption("audioDriver");
@ -212,7 +213,7 @@ void SettingsView::updateConfig() {
loadState |= m_ui.loadStateCheats->isChecked() ? SAVESTATE_CHEATS : 0; loadState |= m_ui.loadStateCheats->isChecked() ? SAVESTATE_CHEATS : 0;
saveSetting("loadStateExtdata", loadState); saveSetting("loadStateExtdata", loadState);
int saveState = SAVESTATE_RTC; int saveState = SAVESTATE_RTC | SAVESTATE_METADATA;
saveState |= m_ui.saveStateScreenshot->isChecked() ? SAVESTATE_SCREENSHOT : 0; saveState |= m_ui.saveStateScreenshot->isChecked() ? SAVESTATE_SCREENSHOT : 0;
saveState |= m_ui.saveStateSave->isChecked() ? SAVESTATE_SAVEDATA : 0; saveState |= m_ui.saveStateSave->isChecked() ? SAVESTATE_SAVEDATA : 0;
saveState |= m_ui.saveStateCheats->isChecked() ? SAVESTATE_CHEATS : 0; saveState |= m_ui.saveStateCheats->isChecked() ? SAVESTATE_CHEATS : 0;
@ -299,7 +300,7 @@ void SettingsView::reloadConfig() {
int saveState = loadSetting("saveStateExtdata").toInt(&ok); int saveState = loadSetting("saveStateExtdata").toInt(&ok);
if (!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.saveStateScreenshot->setChecked(saveState & SAVESTATE_SCREENSHOT);
m_ui.saveStateSave->setChecked(saveState & SAVESTATE_SAVEDATA); m_ui.saveStateSave->setChecked(saveState & SAVESTATE_SAVEDATA);

View File

@ -29,6 +29,7 @@ signals:
void audioDriverChanged(); void audioDriverChanged();
void displayDriverChanged(); void displayDriverChanged();
void pathsChanged(); void pathsChanged();
void libraryCleared();
private slots: private slots:
void selectBios(QLineEdit*); void selectBios(QLineEdit*);

View File

@ -431,9 +431,6 @@
</item> </item>
<item row="2" column="1"> <item row="2" column="1">
<widget class="QPushButton" name="clearCache"> <widget class="QPushButton" name="clearCache">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text"> <property name="text">
<string>Clear cache</string> <string>Clear cache</string>
</property> </property>

View File

@ -35,6 +35,7 @@
#include "LoadSaveState.h" #include "LoadSaveState.h"
#include "LogView.h" #include "LogView.h"
#include "MultiplayerController.h" #include "MultiplayerController.h"
#include "MemorySearch.h"
#include "MemoryView.h" #include "MemoryView.h"
#include "OverrideView.h" #include "OverrideView.h"
#include "ObjView.h" #include "ObjView.h"
@ -468,6 +469,7 @@ void Window::openSettingsWindow() {
connect(settingsWindow, &SettingsView::audioDriverChanged, m_controller, &GameController::reloadAudioDriver); connect(settingsWindow, &SettingsView::audioDriverChanged, m_controller, &GameController::reloadAudioDriver);
connect(settingsWindow, &SettingsView::displayDriverChanged, this, &Window::mustRestart); connect(settingsWindow, &SettingsView::displayDriverChanged, this, &Window::mustRestart);
connect(settingsWindow, &SettingsView::pathsChanged, this, &Window::reloadConfig); connect(settingsWindow, &SettingsView::pathsChanged, this, &Window::reloadConfig);
connect(settingsWindow, &SettingsView::libraryCleared, m_libraryView, &LibraryController::clear);
openView(settingsWindow); openView(settingsWindow);
} }
@ -1405,6 +1407,11 @@ void Window::setupMenu(QMenuBar* menubar) {
m_gameActions.append(memoryView); m_gameActions.append(memoryView);
addControlledAction(toolsMenu, memoryView, "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 #ifdef M_CORE_GBA
QAction* ioViewer = new QAction(tr("View &I/O registers..."), toolsMenu); QAction* ioViewer = new QAction(tr("View &I/O registers..."), toolsMenu);
connect(ioViewer, &QAction::triggered, openTView<IOViewer>()); connect(ioViewer, &QAction::triggered, openTView<IOViewer>());

View File

@ -24,7 +24,7 @@ void AbstractGameList::addEntries(QList<LibraryEntryRef> items) {
} }
void AbstractGameList::removeEntries(QList<LibraryEntryRef> items) { void AbstractGameList::removeEntries(QList<LibraryEntryRef> items) {
for (LibraryEntryRef o : items) { for (LibraryEntryRef o : items) {
addEntry(o); removeEntry(o);
} }
} }
@ -130,6 +130,20 @@ void LibraryController::addDirectory(const QString& dir) {
m_loaderThread.start(); m_loaderThread.start();
} }
void LibraryController::clear() {
if (!m_library) {
if (!m_loaderThread.isRunning() && m_loaderThread.m_library) {
m_library = m_loaderThread.m_library;
m_loaderThread.m_library = nullptr;
} else {
return;
}
}
mLibraryClear(m_library);
refresh();
}
void LibraryController::refresh() { void LibraryController::refresh() {
if (!m_library) { if (!m_library) {
if (!m_loaderThread.isRunning() && m_loaderThread.m_library) { if (!m_loaderThread.isRunning() && m_loaderThread.m_library) {
@ -181,7 +195,7 @@ void LibraryController::refresh() {
} }
void LibraryController::selectLastBootedGame() { void LibraryController::selectLastBootedGame() {
if (!m_config) { if (!m_config || m_config->getMRU().isEmpty()) {
return; return;
} }
const QString lastfile = m_config->getMRU().first(); const QString lastfile = m_config->getMRU().first();

View File

@ -100,6 +100,9 @@ public:
void addDirectory(const QString& dir); void addDirectory(const QString& dir);
public slots:
void clear();
signals: signals:
void startGame(); void startGame();
void doneLoading(); void doneLoading();

View File

@ -9,7 +9,7 @@
#include <mgba/core/input.h> #include <mgba/core/input.h>
#include <mgba/core/serialize.h> #include <mgba/core/serialize.h>
#include <mgba/core/thread.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/internal/gba/input.h>
#include <mgba-util/configuration.h> #include <mgba-util/configuration.h>
#include <mgba-util/formatting.h> #include <mgba-util/formatting.h>