mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' into feature/input-revamp
This commit is contained in:
commit
97e2004fd3
33
CHANGES
33
CHANGES
|
@ -1,3 +1,7 @@
|
|||
0.7.0: (Future)
|
||||
Misc:
|
||||
- GBA Timer: Use global cycles for timers
|
||||
|
||||
0.6.0: (Future)
|
||||
Features:
|
||||
- GBA: Support printing debug strings from inside a game
|
||||
|
@ -25,6 +29,7 @@ Features:
|
|||
- LR35902: Watchpoints
|
||||
- Memory search
|
||||
- Debugger: Execution tracing
|
||||
- Qt: Italian translation (by theheroGAC)
|
||||
Bugfixes:
|
||||
- LR35902: Fix core never exiting with certain event patterns
|
||||
- GB Timer: Improve DIV reset behavior
|
||||
|
@ -73,6 +78,10 @@ Bugfixes:
|
|||
- 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
|
||||
- Core: Fix interrupting a thread while on the thread (fixes mgba.io/i/692)
|
||||
- Core: Fix directory sets crashing on close if base isn't properly detached
|
||||
- Qt: Fix window icon being stretched
|
||||
- Qt: Fix data directory path
|
||||
Misc:
|
||||
- SDL: Remove scancode key input
|
||||
- GBA Video: Clean up unused timers
|
||||
|
@ -142,6 +151,30 @@ Misc:
|
|||
- GB: Reset with initial state of DIV register
|
||||
- GB MBC: New MBC7 implementation
|
||||
- Qt: Better highlight active key in control binding
|
||||
- Core: Improved threading interrupted detection
|
||||
- GBA Timer: Improve accuracy of timers
|
||||
|
||||
0.6 beta 2: (Future)
|
||||
Features:
|
||||
- Qt: Italian translation (by theheroGAC)
|
||||
- Qt: Updated German translation
|
||||
Bugfixes:
|
||||
- Qt: Fix memory search close button (fixes mgba.io/i/769)
|
||||
- Qt: Fix window icon being stretched
|
||||
- Qt: Fix initial window size (fixes mgba.io/i/766)
|
||||
- Qt: Fix data directory path
|
||||
- Qt: Fix controls not saving on non-SDL builds
|
||||
- GB Video: Fix LYC regression
|
||||
- Qt: Fix translation initialization (fixes mgba.io/i/776)
|
||||
- PSP2: Use custom localtime_r since newlib version is broken (fixes mgba.io/i/560)
|
||||
Misc:
|
||||
- Qt: Add language selector
|
||||
- GBA Timer: Improve accuracy of timers
|
||||
- Qt: Minor test fixes
|
||||
- PSP2: Update toolchain to use vita.cmake
|
||||
|
||||
0.6 beta 1: (2017-06-29)
|
||||
- Initial beta for 0.6
|
||||
|
||||
0.5.2: (2016-12-31)
|
||||
Bugfixes:
|
||||
|
|
|
@ -275,7 +275,9 @@ endif()
|
|||
include(CheckFunctionExists)
|
||||
check_function_exists(strdup HAVE_STRDUP)
|
||||
check_function_exists(strndup HAVE_STRNDUP)
|
||||
check_function_exists(localtime_r HAVE_LOCALTIME_R)
|
||||
if(NOT DEFINED PSP2)
|
||||
check_function_exists(localtime_r HAVE_LOCALTIME_R)
|
||||
endif()
|
||||
if(NOT CMAKE_SYSTEM_NAME STREQUAL "Generic")
|
||||
check_function_exists(snprintf_l HAVE_SNPRINTF_L)
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
|
@ -843,7 +845,6 @@ if(BUILD_EXAMPLE)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
message(STATUS ${USE_PTHREADS})
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/core/flags.h.in ${CMAKE_CURRENT_BINARY_DIR}/flags.h)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/flags.h DESTINATION include/mgba COMPONENT lib${BINARY_NAME})
|
||||
|
||||
|
@ -897,6 +898,7 @@ else()
|
|||
endif()
|
||||
|
||||
if(NOT QUIET)
|
||||
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
|
||||
message(STATUS "Platforms:")
|
||||
message(STATUS " Game Boy Advance: ${M_CORE_GBA}")
|
||||
message(STATUS " Game Boy: ${M_CORE_GB}")
|
||||
|
|
|
@ -264,10 +264,10 @@ struct GBASerializedState {
|
|||
|
||||
struct {
|
||||
uint16_t reload;
|
||||
uint16_t oldReload;
|
||||
uint16_t reserved;
|
||||
uint32_t lastEvent;
|
||||
uint32_t nextEvent;
|
||||
int32_t overflowInterval;
|
||||
uint32_t nextIrq;
|
||||
GBATimerFlags flags;
|
||||
} timers[4];
|
||||
|
||||
|
|
|
@ -17,19 +17,19 @@ DECL_BITS(GBATimerFlags, PrescaleBits, 0, 4);
|
|||
DECL_BIT(GBATimerFlags, CountUp, 4);
|
||||
DECL_BIT(GBATimerFlags, DoIrq, 5);
|
||||
DECL_BIT(GBATimerFlags, Enable, 6);
|
||||
DECL_BIT(GBATimerFlags, IrqPending, 7);
|
||||
|
||||
struct GBA;
|
||||
struct GBATimer {
|
||||
uint16_t reload;
|
||||
uint16_t oldReload;
|
||||
uint32_t lastEvent;
|
||||
int32_t lastEvent;
|
||||
struct mTimingEvent event;
|
||||
int32_t overflowInterval;
|
||||
struct mTimingEvent irq;
|
||||
GBATimerFlags flags;
|
||||
};
|
||||
|
||||
void GBATimerInit(struct GBA* gba);
|
||||
void GBATimerUpdateRegister(struct GBA* gba, int timer);
|
||||
void GBATimerUpdateRegister(struct GBA* gba, int timer, int32_t cyclesLate);
|
||||
void GBATimerWriteTMCNT_LO(struct GBA* gba, int timer, uint16_t value);
|
||||
void GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t value);
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ Fog
|
|||
Reilly Grant
|
||||
Philip Horton
|
||||
Jordan Jorgensen
|
||||
mars
|
||||
Rohit Nirmal
|
||||
Rhys Powell
|
||||
rootfather
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include <mgba/feature/video-logger.h>
|
||||
#endif
|
||||
|
||||
const static struct mCoreFilter {
|
||||
static const struct mCoreFilter {
|
||||
bool (*filter)(struct VFile*);
|
||||
struct mCore* (*open)(void);
|
||||
enum mPlatform platform;
|
||||
|
|
|
@ -22,28 +22,58 @@ void mDirectorySetDeinit(struct mDirectorySet* dirs) {
|
|||
mDirectorySetDetachBase(dirs);
|
||||
|
||||
if (dirs->archive) {
|
||||
if (dirs->archive == dirs->save) {
|
||||
dirs->save = NULL;
|
||||
}
|
||||
if (dirs->archive == dirs->patch) {
|
||||
dirs->patch = NULL;
|
||||
}
|
||||
if (dirs->archive == dirs->state) {
|
||||
dirs->state = NULL;
|
||||
}
|
||||
if (dirs->archive == dirs->screenshot) {
|
||||
dirs->screenshot = NULL;
|
||||
}
|
||||
dirs->archive->close(dirs->archive);
|
||||
dirs->archive = 0;
|
||||
dirs->archive = NULL;
|
||||
}
|
||||
|
||||
if (dirs->save) {
|
||||
if (dirs->save == dirs->patch) {
|
||||
dirs->patch = NULL;
|
||||
}
|
||||
if (dirs->save == dirs->state) {
|
||||
dirs->state = NULL;
|
||||
}
|
||||
if (dirs->save == dirs->screenshot) {
|
||||
dirs->screenshot = NULL;
|
||||
}
|
||||
dirs->save->close(dirs->save);
|
||||
dirs->save = 0;
|
||||
dirs->save = NULL;
|
||||
}
|
||||
|
||||
if (dirs->patch) {
|
||||
if (dirs->patch == dirs->state) {
|
||||
dirs->state = NULL;
|
||||
}
|
||||
if (dirs->patch == dirs->screenshot) {
|
||||
dirs->screenshot = NULL;
|
||||
}
|
||||
dirs->patch->close(dirs->patch);
|
||||
dirs->patch = 0;
|
||||
dirs->patch = NULL;
|
||||
}
|
||||
|
||||
if (dirs->state) {
|
||||
if (dirs->state == dirs->screenshot) {
|
||||
dirs->state = NULL;
|
||||
}
|
||||
dirs->state->close(dirs->state);
|
||||
dirs->state = 0;
|
||||
dirs->state = NULL;
|
||||
}
|
||||
|
||||
if (dirs->screenshot) {
|
||||
dirs->screenshot->close(dirs->screenshot);
|
||||
dirs->screenshot = 0;
|
||||
dirs->screenshot = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,21 +95,21 @@ void mDirectorySetAttachBase(struct mDirectorySet* dirs, struct VDir* base) {
|
|||
|
||||
void mDirectorySetDetachBase(struct mDirectorySet* dirs) {
|
||||
if (dirs->save == dirs->base) {
|
||||
dirs->save = 0;
|
||||
dirs->save = NULL;
|
||||
}
|
||||
if (dirs->patch == dirs->base) {
|
||||
dirs->patch = 0;
|
||||
dirs->patch = NULL;
|
||||
}
|
||||
if (dirs->state == dirs->base) {
|
||||
dirs->state = 0;
|
||||
dirs->state = NULL;
|
||||
}
|
||||
if (dirs->screenshot == dirs->base) {
|
||||
dirs->screenshot = 0;
|
||||
dirs->screenshot = NULL;
|
||||
}
|
||||
|
||||
if (dirs->base) {
|
||||
dirs->base->close(dirs->base);
|
||||
dirs->base = 0;
|
||||
dirs->base = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -401,11 +401,14 @@ void mCoreThreadInterruptFromThread(struct mCoreThread* threadContext) {
|
|||
MutexLock(&threadContext->stateMutex);
|
||||
++threadContext->interruptDepth;
|
||||
if (threadContext->interruptDepth > 1 || !mCoreThreadIsActive(threadContext)) {
|
||||
if (threadContext->state == THREAD_INTERRUPTING) {
|
||||
threadContext->state = THREAD_INTERRUPTED;
|
||||
}
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
return;
|
||||
}
|
||||
threadContext->savedState = threadContext->state;
|
||||
threadContext->state = THREAD_INTERRUPTING;
|
||||
threadContext->state = THREAD_INTERRUPTED;
|
||||
ConditionWake(&threadContext->stateCond);
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
}
|
||||
|
@ -465,7 +468,7 @@ void mCoreThreadUnpause(struct mCoreThread* threadContext) {
|
|||
bool mCoreThreadIsPaused(struct mCoreThread* threadContext) {
|
||||
bool isPaused;
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
if (threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) {
|
||||
if (threadContext->interruptDepth) {
|
||||
isPaused = threadContext->savedState == THREAD_PAUSED;
|
||||
} else {
|
||||
isPaused = threadContext->state == THREAD_PAUSED;
|
||||
|
@ -495,7 +498,7 @@ void mCoreThreadTogglePause(struct mCoreThread* threadContext) {
|
|||
void mCoreThreadPauseFromThread(struct mCoreThread* threadContext) {
|
||||
bool frameOn = true;
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
if (threadContext->state == THREAD_RUNNING || (threadContext->state == THREAD_INTERRUPTING && threadContext->savedState == THREAD_RUNNING)) {
|
||||
if (threadContext->state == THREAD_RUNNING || (threadContext->interruptDepth && threadContext->savedState == THREAD_RUNNING)) {
|
||||
threadContext->state = THREAD_PAUSING;
|
||||
frameOn = false;
|
||||
}
|
||||
|
@ -506,11 +509,11 @@ void mCoreThreadPauseFromThread(struct mCoreThread* threadContext) {
|
|||
|
||||
void mCoreThreadSetRewinding(struct mCoreThread* threadContext, bool rewinding) {
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
if (rewinding && (threadContext->state == THREAD_REWINDING || (threadContext->state == THREAD_INTERRUPTING && threadContext->savedState == THREAD_REWINDING))) {
|
||||
if (rewinding && (threadContext->state == THREAD_REWINDING || (threadContext->interruptDepth && threadContext->savedState == THREAD_REWINDING))) {
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
return;
|
||||
}
|
||||
if (!rewinding && (threadContext->state == THREAD_RUNNING || (threadContext->state == THREAD_INTERRUPTING && threadContext->savedState == THREAD_RUNNING))) {
|
||||
if (!rewinding && ((!threadContext->interruptDepth && threadContext->state != THREAD_REWINDING) || (threadContext->interruptDepth && threadContext->savedState != THREAD_REWINDING))) {
|
||||
MutexUnlock(&threadContext->stateMutex);
|
||||
return;
|
||||
}
|
||||
|
@ -526,7 +529,7 @@ void mCoreThreadSetRewinding(struct mCoreThread* threadContext, bool rewinding)
|
|||
|
||||
void mCoreThreadWaitFromThread(struct mCoreThread* threadContext) {
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
if ((threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) && threadContext->savedState == THREAD_RUNNING) {
|
||||
if (threadContext->interruptDepth && threadContext->savedState == THREAD_RUNNING) {
|
||||
threadContext->savedState = THREAD_WAITING;
|
||||
} else if (threadContext->state == THREAD_RUNNING) {
|
||||
threadContext->state = THREAD_WAITING;
|
||||
|
@ -536,7 +539,7 @@ void mCoreThreadWaitFromThread(struct mCoreThread* threadContext) {
|
|||
|
||||
void mCoreThreadStopWaiting(struct mCoreThread* threadContext) {
|
||||
MutexLock(&threadContext->stateMutex);
|
||||
if ((threadContext->state == THREAD_INTERRUPTED || threadContext->state == THREAD_INTERRUPTING) && threadContext->savedState == THREAD_WAITING) {
|
||||
if (threadContext->interruptDepth && threadContext->savedState == THREAD_WAITING) {
|
||||
threadContext->savedState = THREAD_RUNNING;
|
||||
} else if (threadContext->state == THREAD_WAITING) {
|
||||
threadContext->state = THREAD_RUNNING;
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
const char mVL_MAGIC[] = "mVL\0";
|
||||
|
||||
const static struct mVLDescriptor {
|
||||
static const struct mVLDescriptor {
|
||||
enum mPlatform platform;
|
||||
struct mCore* (*open)(void);
|
||||
} _descriptors[] = {
|
||||
|
|
|
@ -29,26 +29,26 @@
|
|||
#include <mgba/internal/gba/input.h>
|
||||
#endif
|
||||
|
||||
const static struct mCoreChannelInfo _GBVideoLayers[] = {
|
||||
static const struct mCoreChannelInfo _GBVideoLayers[] = {
|
||||
{ 0, "bg", "Background", NULL },
|
||||
{ 1, "obj", "Objects", NULL },
|
||||
{ 2, "win", "Window", NULL },
|
||||
};
|
||||
|
||||
const static struct mCoreChannelInfo _GBAudioChannels[] = {
|
||||
{ 0, "ch0", "Channel 0", "Square/Sweep" },
|
||||
{ 1, "ch1", "Channel 1", "Square" },
|
||||
{ 2, "ch2", "Channel 2", "PCM" },
|
||||
{ 3, "ch3", "Channel 3", "Noise" },
|
||||
static const struct mCoreChannelInfo _GBAudioChannels[] = {
|
||||
{ 0, "ch1", "Channel 1", "Square/Sweep" },
|
||||
{ 1, "ch2", "Channel 2", "Square" },
|
||||
{ 2, "ch3", "Channel 3", "PCM" },
|
||||
{ 3, "ch4", "Channel 4", "Noise" },
|
||||
};
|
||||
|
||||
const static struct LR35902Segment _GBSegments[] = {
|
||||
static const struct LR35902Segment _GBSegments[] = {
|
||||
{ .name = "ROM", .start = GB_BASE_CART_BANK1, .end = GB_BASE_VRAM },
|
||||
{ .name = "RAM", .start = GB_BASE_EXTERNAL_RAM, .end = GB_BASE_WORKING_RAM_BANK0 },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
const static struct LR35902Segment _GBCSegments[] = {
|
||||
static const struct LR35902Segment _GBCSegments[] = {
|
||||
{ .name = "ROM", .start = GB_BASE_CART_BANK1, .end = GB_BASE_VRAM },
|
||||
{ .name = "RAM", .start = GB_BASE_EXTERNAL_RAM, .end = GB_BASE_WORKING_RAM_BANK0 },
|
||||
{ .name = "WRAM", .start = GB_BASE_WORKING_RAM_BANK1, .end = 0xE000 },
|
||||
|
@ -56,7 +56,7 @@ const static struct LR35902Segment _GBCSegments[] = {
|
|||
{ 0 }
|
||||
};
|
||||
|
||||
const static struct mCoreMemoryBlock _GBMemoryBlocks[] = {
|
||||
static const 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 },
|
||||
|
@ -67,7 +67,7 @@ const static struct mCoreMemoryBlock _GBMemoryBlocks[] = {
|
|||
{ 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[] = {
|
||||
static const 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 },
|
||||
|
|
|
@ -558,7 +558,7 @@ void GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value) {
|
|||
return;
|
||||
case 0x10:
|
||||
mbc7->latch |= (value & 0xAA);
|
||||
if (mbc7->latch == 0xFF && memory->rotation && memory->rotation->sample) {
|
||||
if (mbc7->latch == 0xAB && memory->rotation && memory->rotation->sample) {
|
||||
memory->rotation->sample(memory->rotation);
|
||||
}
|
||||
mbc7->latch = 0;
|
||||
|
|
|
@ -74,7 +74,7 @@ void GBTimerDivReset(struct GBTimer* timer) {
|
|||
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);
|
||||
mTimingSchedule(&timer->p->timing, &timer->irq, 4 - ((timer->p->cpu->executionState + 1) & 3));
|
||||
}
|
||||
}
|
||||
timer->p->memory.io[REG_DIV] = 0;
|
||||
|
|
|
@ -386,6 +386,7 @@ void GBVideoWriteLYC(struct GBVideo* video, uint8_t value) {
|
|||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||
GBUpdateIRQs(video->p);
|
||||
}
|
||||
video->p->memory.io[REG_STAT] = video->stat;
|
||||
}
|
||||
|
||||
void GBVideoWritePalette(struct GBVideo* video, uint16_t address, uint8_t value) {
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include <mgba/internal/gba/input.h>
|
||||
#endif
|
||||
|
||||
const static struct mCoreChannelInfo _GBAVideoLayers[] = {
|
||||
static const struct mCoreChannelInfo _GBAVideoLayers[] = {
|
||||
{ 0, "bg0", "Background 0", NULL },
|
||||
{ 1, "bg1", "Background 1", NULL },
|
||||
{ 2, "bg2", "Background 2", NULL },
|
||||
|
@ -36,16 +36,16 @@ const static struct mCoreChannelInfo _GBAVideoLayers[] = {
|
|||
{ 4, "obj", "Objects", NULL },
|
||||
};
|
||||
|
||||
const static struct mCoreChannelInfo _GBAAudioChannels[] = {
|
||||
{ 0, "ch0", "PSG Channel 0", "Square/Sweep" },
|
||||
{ 1, "ch1", "PSG Channel 1", "Square" },
|
||||
{ 2, "ch2", "PSG Channel 2", "PCM" },
|
||||
{ 3, "ch3", "PSG Channel 3", "Noise" },
|
||||
static const struct mCoreChannelInfo _GBAAudioChannels[] = {
|
||||
{ 0, "ch1", "PSG Channel 1", "Square/Sweep" },
|
||||
{ 1, "ch2", "PSG Channel 2", "Square" },
|
||||
{ 2, "ch3", "PSG Channel 3", "PCM" },
|
||||
{ 3, "ch4", "PSG Channel 4", "Noise" },
|
||||
{ 4, "chA", "FIFO Channel A", NULL },
|
||||
{ 5, "chB", "FIFO Channel B", NULL },
|
||||
};
|
||||
|
||||
const static struct mCoreMemoryBlock _GBAMemoryBlocks[] = {
|
||||
static const 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 },
|
||||
|
@ -59,7 +59,7 @@ const static struct mCoreMemoryBlock _GBAMemoryBlocks[] = {
|
|||
{ 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[] = {
|
||||
static const 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 },
|
||||
|
@ -74,7 +74,7 @@ const static struct mCoreMemoryBlock _GBAMemoryBlocksSRAM[] = {
|
|||
{ 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[] = {
|
||||
static const 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 },
|
||||
|
@ -89,7 +89,7 @@ const static struct mCoreMemoryBlock _GBAMemoryBlocksFlash512[] = {
|
|||
{ 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[] = {
|
||||
static const 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 },
|
||||
|
@ -104,7 +104,7 @@ const static struct mCoreMemoryBlock _GBAMemoryBlocksFlash1M[] = {
|
|||
{ 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[] = {
|
||||
static const 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 },
|
||||
|
|
18
src/gba/io.c
18
src/gba/io.c
|
@ -707,17 +707,18 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
|
|||
}
|
||||
|
||||
switch (address) {
|
||||
// Reading this takes two cycles (1N+1I), so let's remove them preemptively
|
||||
case REG_TM0CNT_LO:
|
||||
GBATimerUpdateRegister(gba, 0);
|
||||
GBATimerUpdateRegister(gba, 0, 2);
|
||||
break;
|
||||
case REG_TM1CNT_LO:
|
||||
GBATimerUpdateRegister(gba, 1);
|
||||
GBATimerUpdateRegister(gba, 1, 2);
|
||||
break;
|
||||
case REG_TM2CNT_LO:
|
||||
GBATimerUpdateRegister(gba, 2);
|
||||
GBATimerUpdateRegister(gba, 2, 2);
|
||||
break;
|
||||
case REG_TM3CNT_LO:
|
||||
GBATimerUpdateRegister(gba, 3);
|
||||
GBATimerUpdateRegister(gba, 3, 2);
|
||||
break;
|
||||
|
||||
case REG_KEYINPUT:
|
||||
|
@ -925,10 +926,9 @@ void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) {
|
|||
for (i = 0; i < 4; ++i) {
|
||||
STORE_16(gba->memory.io[(REG_DMA0CNT_LO + i * 12) >> 1], (REG_DMA0CNT_LO + i * 12), state->io);
|
||||
STORE_16(gba->timers[i].reload, 0, &state->timers[i].reload);
|
||||
STORE_16(gba->timers[i].oldReload, 0, &state->timers[i].oldReload);
|
||||
STORE_32(gba->timers[i].lastEvent - mTimingCurrentTime(&gba->timing), 0, &state->timers[i].lastEvent);
|
||||
STORE_32(gba->timers[i].event.when - mTimingCurrentTime(&gba->timing), 0, &state->timers[i].nextEvent);
|
||||
STORE_32(gba->timers[i].overflowInterval, 0, &state->timers[i].overflowInterval);
|
||||
STORE_32(gba->timers[i].irq.when - mTimingCurrentTime(&gba->timing), 0, &state->timers[i].nextIrq);
|
||||
STORE_32(gba->timers[i].flags, 0, &state->timers[i].flags);
|
||||
STORE_32(gba->memory.dma[i].nextSource, 0, &state->dma[i].nextSource);
|
||||
STORE_32(gba->memory.dma[i].nextDest, 0, &state->dma[i].nextDest);
|
||||
|
@ -954,8 +954,6 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
|||
uint32_t when;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
LOAD_16(gba->timers[i].reload, 0, &state->timers[i].reload);
|
||||
LOAD_16(gba->timers[i].oldReload, 0, &state->timers[i].oldReload);
|
||||
LOAD_32(gba->timers[i].overflowInterval, 0, &state->timers[i].overflowInterval);
|
||||
LOAD_32(gba->timers[i].flags, 0, &state->timers[i].flags);
|
||||
if (i > 0 && GBATimerFlagsIsCountUp(gba->timers[i].flags)) {
|
||||
// Overwrite invalid values in savestate
|
||||
|
@ -968,6 +966,10 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
|||
if (GBATimerFlagsIsEnable(gba->timers[i].flags)) {
|
||||
mTimingSchedule(&gba->timing, &gba->timers[i].event, when);
|
||||
}
|
||||
LOAD_32(when, 0, &state->timers[i].nextIrq);
|
||||
if (GBATimerFlagsIsIrqPending(gba->timers[i].flags)) {
|
||||
mTimingSchedule(&gba->timing, &gba->timers[i].irq, when);
|
||||
}
|
||||
|
||||
LOAD_16(gba->memory.dma[i].reg, (REG_DMA0CNT_HI + i * 12), state->io);
|
||||
LOAD_32(gba->memory.dma[i].nextSource, 0, &state->dma[i].nextSource);
|
||||
|
|
142
src/gba/timer.c
142
src/gba/timer.c
|
@ -8,14 +8,56 @@
|
|||
#include <mgba/internal/gba/gba.h>
|
||||
#include <mgba/internal/gba/io.h>
|
||||
|
||||
static void GBATimerUpdate(struct mTiming* timing, struct GBA* gba, int timerId, uint32_t cyclesLate) {
|
||||
#define TIMER_IRQ_DELAY 7
|
||||
#define TIMER_RELOAD_DELAY 0
|
||||
#define TIMER_STARTUP_DELAY 2
|
||||
|
||||
static void GBATimerIrq(struct GBA* gba, int timerId) {
|
||||
struct GBATimer* timer = &gba->timers[timerId];
|
||||
if (GBATimerFlagsIsIrqPending(timer->flags)) {
|
||||
timer->flags = GBATimerFlagsClearIrqPending(timer->flags);
|
||||
GBARaiseIRQ(gba, IRQ_TIMER0 + timerId);
|
||||
}
|
||||
}
|
||||
|
||||
static void GBATimerIrq0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||
UNUSED(timing);
|
||||
UNUSED(cyclesLate);
|
||||
GBATimerIrq(context, 0);
|
||||
}
|
||||
|
||||
static void GBATimerIrq1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||
UNUSED(timing);
|
||||
UNUSED(cyclesLate);
|
||||
GBATimerIrq(context, 1);
|
||||
}
|
||||
|
||||
static void GBATimerIrq2(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||
UNUSED(timing);
|
||||
UNUSED(cyclesLate);
|
||||
GBATimerIrq(context, 2);
|
||||
}
|
||||
|
||||
static void GBATimerIrq3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||
UNUSED(timing);
|
||||
UNUSED(cyclesLate);
|
||||
GBATimerIrq(context, 3);
|
||||
}
|
||||
|
||||
static void GBATimerUpdate(struct GBA* gba, int timerId, uint32_t cyclesLate) {
|
||||
struct GBATimer* timer = &gba->timers[timerId];
|
||||
gba->memory.io[(REG_TM0CNT_LO >> 1) + (timerId << 1)] = timer->reload;
|
||||
timer->oldReload = timer->reload;
|
||||
timer->lastEvent = timing->masterCycles - cyclesLate;
|
||||
int32_t currentTime = mTimingCurrentTime(&gba->timing) - cyclesLate;
|
||||
int32_t tickMask = (1 << GBATimerFlagsGetPrescaleBits(timer->flags)) - 1;
|
||||
currentTime &= ~tickMask;
|
||||
timer->lastEvent = currentTime;
|
||||
GBATimerUpdateRegister(gba, timerId, 0);
|
||||
|
||||
if (GBATimerFlagsIsDoIrq(timer->flags)) {
|
||||
GBARaiseIRQ(gba, IRQ_TIMER0 + timerId);
|
||||
timer->flags = GBATimerFlagsFillIrqPending(timer->flags);
|
||||
if (!mTimingIsScheduled(&gba->timing, &timer->irq)) {
|
||||
mTimingSchedule(&gba->timing, &timer->irq, TIMER_IRQ_DELAY - cyclesLate);
|
||||
}
|
||||
}
|
||||
|
||||
if (gba->audio.enable && timerId < 2) {
|
||||
|
@ -33,31 +75,30 @@ static void GBATimerUpdate(struct mTiming* timing, struct GBA* gba, int timerId,
|
|||
if (GBATimerFlagsIsCountUp(nextTimer->flags)) { // TODO: Does this increment while disabled?
|
||||
++gba->memory.io[(REG_TM1CNT_LO >> 1) + (timerId << 1)];
|
||||
if (!gba->memory.io[(REG_TM1CNT_LO >> 1) + (timerId << 1)] && GBATimerFlagsIsEnable(nextTimer->flags)) {
|
||||
mTimingSchedule(timing, &nextTimer->event, -cyclesLate);
|
||||
GBATimerUpdate(gba, timerId + 1, cyclesLate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!GBATimerFlagsIsCountUp(timer->flags)) {
|
||||
uint32_t nextEvent = timer->overflowInterval - cyclesLate;
|
||||
mTimingSchedule(timing, &timer->event, nextEvent);
|
||||
}
|
||||
}
|
||||
|
||||
static void GBATimerUpdate0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||
GBATimerUpdate(timing, context, 0, cyclesLate);
|
||||
UNUSED(timing);
|
||||
GBATimerUpdate(context, 0, cyclesLate);
|
||||
}
|
||||
|
||||
static void GBATimerUpdate1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||
GBATimerUpdate(timing, context, 1, cyclesLate);
|
||||
UNUSED(timing);
|
||||
GBATimerUpdate(context, 1, cyclesLate);
|
||||
}
|
||||
|
||||
static void GBATimerUpdate2(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||
GBATimerUpdate(timing, context, 2, cyclesLate);
|
||||
UNUSED(timing);
|
||||
GBATimerUpdate(context, 2, cyclesLate);
|
||||
}
|
||||
|
||||
static void GBATimerUpdate3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||
GBATimerUpdate(timing, context, 3, cyclesLate);
|
||||
UNUSED(timing);
|
||||
GBATimerUpdate(context, 3, cyclesLate);
|
||||
}
|
||||
|
||||
void GBATimerInit(struct GBA* gba) {
|
||||
|
@ -78,62 +119,91 @@ void GBATimerInit(struct GBA* gba) {
|
|||
gba->timers[3].event.callback = GBATimerUpdate3;
|
||||
gba->timers[3].event.context = gba;
|
||||
gba->timers[3].event.priority = 0x23;
|
||||
gba->timers[0].irq.name = "GBA Timer 0 IRQ";
|
||||
gba->timers[0].irq.callback = GBATimerIrq0;
|
||||
gba->timers[0].irq.context = gba;
|
||||
gba->timers[0].irq.priority = 0x28;
|
||||
gba->timers[1].irq.name = "GBA Timer 1 IRQ";
|
||||
gba->timers[1].irq.callback = GBATimerIrq1;
|
||||
gba->timers[1].irq.context = gba;
|
||||
gba->timers[1].irq.priority = 0x29;
|
||||
gba->timers[2].irq.name = "GBA Timer 2 IRQ";
|
||||
gba->timers[2].irq.callback = GBATimerIrq2;
|
||||
gba->timers[2].irq.context = gba;
|
||||
gba->timers[2].irq.priority = 0x2A;
|
||||
gba->timers[3].irq.name = "GBA Timer 3 IRQ";
|
||||
gba->timers[3].irq.callback = GBATimerIrq3;
|
||||
gba->timers[3].irq.context = gba;
|
||||
gba->timers[3].irq.priority = 0x2B;
|
||||
}
|
||||
|
||||
void GBATimerUpdateRegister(struct GBA* gba, int timer) {
|
||||
void GBATimerUpdateRegister(struct GBA* gba, int timer, int32_t cyclesLate) {
|
||||
struct GBATimer* currentTimer = &gba->timers[timer];
|
||||
if (GBATimerFlagsIsEnable(currentTimer->flags) && !GBATimerFlagsIsCountUp(currentTimer->flags)) {
|
||||
int32_t prefetchSkew = -2;
|
||||
if (gba->memory.lastPrefetchedPc > (uint32_t) gba->cpu->gprs[ARM_PC]) {
|
||||
prefetchSkew += ((gba->memory.lastPrefetchedPc - gba->cpu->gprs[ARM_PC]) * gba->cpu->memory.activeSeqCycles16) / WORD_SIZE_THUMB;
|
||||
}
|
||||
// Reading this takes two cycles (1N+1I), so let's remove them preemptively
|
||||
int32_t diff = gba->cpu->cycles - (currentTimer->lastEvent - gba->timing.masterCycles);
|
||||
gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((diff + prefetchSkew) >> GBATimerFlagsGetPrescaleBits(currentTimer->flags));
|
||||
if (!GBATimerFlagsIsEnable(currentTimer->flags) || GBATimerFlagsIsCountUp(currentTimer->flags)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gba->memory.lastPrefetchedPc > (uint32_t) gba->cpu->gprs[ARM_PC]) {
|
||||
cyclesLate -= ((gba->memory.lastPrefetchedPc - gba->cpu->gprs[ARM_PC]) * gba->cpu->memory.activeSeqCycles16) / WORD_SIZE_THUMB;
|
||||
}
|
||||
|
||||
int prescaleBits = GBATimerFlagsGetPrescaleBits(currentTimer->flags);
|
||||
int32_t currentTime = mTimingCurrentTime(&gba->timing) - cyclesLate;
|
||||
int32_t tickMask = (1 << prescaleBits) - 1;
|
||||
currentTime &= ~tickMask;
|
||||
int32_t tickIncrement = currentTime - currentTimer->lastEvent;
|
||||
currentTimer->lastEvent = currentTime;
|
||||
tickIncrement >>= prescaleBits;
|
||||
tickIncrement += gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1];
|
||||
gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = tickIncrement;
|
||||
if (!mTimingIsScheduled(&gba->timing, ¤tTimer->event)) {
|
||||
tickIncrement = (0x10000 - tickIncrement) << prescaleBits;
|
||||
currentTime -= mTimingCurrentTime(&gba->timing) - cyclesLate;
|
||||
mTimingSchedule(&gba->timing, ¤tTimer->event, TIMER_RELOAD_DELAY + tickIncrement + currentTime);
|
||||
}
|
||||
}
|
||||
|
||||
void GBATimerWriteTMCNT_LO(struct GBA* gba, int timer, uint16_t reload) {
|
||||
gba->timers[timer].reload = reload;
|
||||
gba->timers[timer].overflowInterval = (0x10000 - gba->timers[timer].reload) << GBATimerFlagsGetPrescaleBits(gba->timers[timer].flags);
|
||||
}
|
||||
|
||||
void GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t control) {
|
||||
struct GBATimer* currentTimer = &gba->timers[timer];
|
||||
GBATimerUpdateRegister(gba, timer);
|
||||
GBATimerUpdateRegister(gba, timer, 0);
|
||||
|
||||
unsigned oldPrescale = GBATimerFlagsGetPrescaleBits(currentTimer->flags);
|
||||
unsigned prescaleBits;
|
||||
switch (control & 0x0003) {
|
||||
case 0x0000:
|
||||
currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 0);
|
||||
prescaleBits = 0;
|
||||
break;
|
||||
case 0x0001:
|
||||
currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 6);
|
||||
prescaleBits = 6;
|
||||
break;
|
||||
case 0x0002:
|
||||
currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 8);
|
||||
prescaleBits = 8;
|
||||
break;
|
||||
case 0x0003:
|
||||
currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 10);
|
||||
prescaleBits = 10;
|
||||
break;
|
||||
}
|
||||
currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, prescaleBits);
|
||||
currentTimer->flags = GBATimerFlagsTestFillCountUp(currentTimer->flags, timer > 0 && (control & 0x0004));
|
||||
currentTimer->flags = GBATimerFlagsTestFillDoIrq(currentTimer->flags, control & 0x0040);
|
||||
currentTimer->overflowInterval = (0x10000 - currentTimer->reload) << GBATimerFlagsGetPrescaleBits(currentTimer->flags);
|
||||
bool wasEnabled = GBATimerFlagsIsEnable(currentTimer->flags);
|
||||
currentTimer->flags = GBATimerFlagsTestFillEnable(currentTimer->flags, control & 0x0080);
|
||||
if (!wasEnabled && GBATimerFlagsIsEnable(currentTimer->flags)) {
|
||||
mTimingDeschedule(&gba->timing, ¤tTimer->event);
|
||||
if (!GBATimerFlagsIsCountUp(currentTimer->flags)) {
|
||||
mTimingSchedule(&gba->timing, ¤tTimer->event, currentTimer->overflowInterval);
|
||||
}
|
||||
gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->reload;
|
||||
currentTimer->oldReload = currentTimer->reload;
|
||||
currentTimer->lastEvent = gba->timing.masterCycles + gba->cpu->cycles;
|
||||
int32_t tickMask = (1 << prescaleBits) - 1;
|
||||
currentTimer->lastEvent = (mTimingCurrentTime(&gba->timing) - TIMER_STARTUP_DELAY) & ~tickMask;
|
||||
GBATimerUpdateRegister(gba, timer, TIMER_STARTUP_DELAY);
|
||||
} else if (wasEnabled && !GBATimerFlagsIsEnable(currentTimer->flags)) {
|
||||
mTimingDeschedule(&gba->timing, ¤tTimer->event);
|
||||
} else if (GBATimerFlagsIsEnable(currentTimer->flags) && GBATimerFlagsGetPrescaleBits(currentTimer->flags) != oldPrescale && !GBATimerFlagsIsCountUp(currentTimer->flags)) {
|
||||
mTimingDeschedule(&gba->timing, ¤tTimer->event);
|
||||
mTimingSchedule(&gba->timing, ¤tTimer->event, currentTimer->overflowInterval - currentTimer->lastEvent);
|
||||
int32_t tickMask = (1 << prescaleBits) - 1;
|
||||
currentTimer->lastEvent = (mTimingCurrentTime(&gba->timing) - TIMER_STARTUP_DELAY) & ~tickMask;
|
||||
GBATimerUpdateRegister(gba, timer, TIMER_STARTUP_DELAY);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -315,24 +315,24 @@ DEFINE_POPPUSH_DECODER_LR35902(HL);
|
|||
DEFINE_POPPUSH_DECODER_LR35902(AF);
|
||||
|
||||
#define DEFINE_CB_2_DECODER_LR35902(NAME, BODY) \
|
||||
DEFINE_DECODER_LR35902(NAME ## B, info->op1.reg = LR35902_REG_B; BODY) \
|
||||
DEFINE_DECODER_LR35902(NAME ## C, info->op1.reg = LR35902_REG_C; BODY) \
|
||||
DEFINE_DECODER_LR35902(NAME ## D, info->op1.reg = LR35902_REG_D; BODY) \
|
||||
DEFINE_DECODER_LR35902(NAME ## E, info->op1.reg = LR35902_REG_E; BODY) \
|
||||
DEFINE_DECODER_LR35902(NAME ## H, info->op1.reg = LR35902_REG_H; BODY) \
|
||||
DEFINE_DECODER_LR35902(NAME ## L, info->op1.reg = LR35902_REG_L; BODY) \
|
||||
DEFINE_DECODER_LR35902(NAME ## HL, info->op1.reg = LR35902_REG_HL; BODY) \
|
||||
DEFINE_DECODER_LR35902(NAME ## A, info->op1.reg = LR35902_REG_A; BODY)
|
||||
DEFINE_DECODER_LR35902(NAME ## B, info->op2.reg = LR35902_REG_B; BODY) \
|
||||
DEFINE_DECODER_LR35902(NAME ## C, info->op2.reg = LR35902_REG_C; BODY) \
|
||||
DEFINE_DECODER_LR35902(NAME ## D, info->op2.reg = LR35902_REG_D; BODY) \
|
||||
DEFINE_DECODER_LR35902(NAME ## E, info->op2.reg = LR35902_REG_E; BODY) \
|
||||
DEFINE_DECODER_LR35902(NAME ## H, info->op2.reg = LR35902_REG_H; BODY) \
|
||||
DEFINE_DECODER_LR35902(NAME ## L, info->op2.reg = LR35902_REG_L; BODY) \
|
||||
DEFINE_DECODER_LR35902(NAME ## HL, info->op2.reg = LR35902_REG_HL; info->op2.flags = LR35902_OP_FLAG_MEMORY; BODY) \
|
||||
DEFINE_DECODER_LR35902(NAME ## A, info->op2.reg = LR35902_REG_A; BODY)
|
||||
|
||||
#define DEFINE_CB_DECODER_LR35902(NAME, BODY) \
|
||||
DEFINE_CB_2_DECODER_LR35902(NAME ## 0, info->op2.immediate = 1; BODY) \
|
||||
DEFINE_CB_2_DECODER_LR35902(NAME ## 1, info->op2.immediate = 2; BODY) \
|
||||
DEFINE_CB_2_DECODER_LR35902(NAME ## 2, info->op2.immediate = 4; BODY) \
|
||||
DEFINE_CB_2_DECODER_LR35902(NAME ## 3, info->op2.immediate = 8; BODY) \
|
||||
DEFINE_CB_2_DECODER_LR35902(NAME ## 4, info->op2.immediate = 16; BODY) \
|
||||
DEFINE_CB_2_DECODER_LR35902(NAME ## 5, info->op2.immediate = 32; BODY) \
|
||||
DEFINE_CB_2_DECODER_LR35902(NAME ## 6, info->op2.immediate = 64; BODY) \
|
||||
DEFINE_CB_2_DECODER_LR35902(NAME ## 7, info->op2.immediate = 128; BODY)
|
||||
DEFINE_CB_2_DECODER_LR35902(NAME ## 0, info->op1.immediate = 0; BODY) \
|
||||
DEFINE_CB_2_DECODER_LR35902(NAME ## 1, info->op1.immediate = 1; BODY) \
|
||||
DEFINE_CB_2_DECODER_LR35902(NAME ## 2, info->op1.immediate = 2; BODY) \
|
||||
DEFINE_CB_2_DECODER_LR35902(NAME ## 3, info->op1.immediate = 3; BODY) \
|
||||
DEFINE_CB_2_DECODER_LR35902(NAME ## 4, info->op1.immediate = 4; BODY) \
|
||||
DEFINE_CB_2_DECODER_LR35902(NAME ## 5, info->op1.immediate = 5; BODY) \
|
||||
DEFINE_CB_2_DECODER_LR35902(NAME ## 6, info->op1.immediate = 6; BODY) \
|
||||
DEFINE_CB_2_DECODER_LR35902(NAME ## 7, info->op1.immediate = 7; BODY)
|
||||
|
||||
DEFINE_CB_DECODER_LR35902(BIT, info->mnemonic = LR35902_MN_BIT)
|
||||
DEFINE_CB_DECODER_LR35902(RES, info->mnemonic = LR35902_MN_RES)
|
||||
|
@ -496,7 +496,7 @@ static int _decodeOperand(struct LR35902Operand op, char* buffer, int blen) {
|
|||
}
|
||||
|
||||
if (op.flags & LR35902_OP_FLAG_MEMORY) {
|
||||
strncpy(buffer, "(", blen - 1);
|
||||
strncpy(buffer, "[", blen - 1);
|
||||
ADVANCE(1);
|
||||
}
|
||||
if (op.reg) {
|
||||
|
@ -519,7 +519,7 @@ static int _decodeOperand(struct LR35902Operand op, char* buffer, int blen) {
|
|||
ADVANCE(1);
|
||||
}
|
||||
if (op.flags & LR35902_OP_FLAG_MEMORY) {
|
||||
strncpy(buffer, ")", blen - 1);
|
||||
strncpy(buffer, "]", blen - 1);
|
||||
ADVANCE(1);
|
||||
}
|
||||
return total;
|
||||
|
@ -544,7 +544,7 @@ int LR35902Disassemble(struct LR35902InstructionInfo* info, char* buffer, int bl
|
|||
}
|
||||
}
|
||||
|
||||
if (info->op1.reg || info->op1.immediate) {
|
||||
if (info->op1.reg || info->op1.immediate || info->op2.reg || info->op2.immediate) {
|
||||
written = _decodeOperand(info->op1, buffer, blen);
|
||||
ADVANCE(written);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
find_program(FIXUP vita-elf-create)
|
||||
find_program(MAKE_FSELF vita-make-fself)
|
||||
find_program(MAKE_SFO vita-mksfoex)
|
||||
include("${VITASDK}/share/vita.cmake" REQUIRED)
|
||||
|
||||
find_program(OBJCOPY ${cross_prefix}objcopy)
|
||||
find_file(NIDDB db.json PATHS ${VITASDK} ${VITASDK}/bin ${VITASDK}/share)
|
||||
find_program(STRIP ${cross_prefix}strip)
|
||||
|
||||
set(OS_DEFINES IOAPI_NO_64)
|
||||
set(OS_DEFINES ${OS_DEFINES} PARENT_SCOPE)
|
||||
|
@ -37,34 +35,16 @@ add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/backdrop.o
|
|||
COMMAND ${OBJCOPY_CMD} backdrop.png ${CMAKE_CURRENT_BINARY_DIR}/backdrop.o
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
add_custom_target(${BINARY_NAME}.velf ALL
|
||||
${STRIP} --strip-unneeded -go ${BINARY_NAME}-stripped.elf ${BINARY_NAME}.elf
|
||||
COMMAND ${FIXUP} ${BINARY_NAME}-stripped.elf ${BINARY_NAME}.velf ${NIDDB}
|
||||
DEPENDS ${BINARY_NAME}.elf)
|
||||
|
||||
add_custom_target(sce_sys ${CMAKE_COMMAND} -E make_directory sce_sys)
|
||||
|
||||
add_custom_target(param.sfo
|
||||
${MAKE_SFO} ${PROJECT_NAME} -s TITLE_ID=MGBA00001 sce_sys/param.sfo
|
||||
DEPENDS sce_sys)
|
||||
|
||||
add_custom_target(eboot.bin
|
||||
${MAKE_FSELF} -s ${BINARY_NAME}.velf eboot.bin
|
||||
DEPENDS ${BINARY_NAME}.velf)
|
||||
vita_create_self(${BINARY_NAME}.self ${BINARY_NAME}.elf)
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/template.xml.in ${CMAKE_CURRENT_BINARY_DIR}/template.xml)
|
||||
|
||||
add_custom_target(livearea
|
||||
${CMAKE_COMMAND} -E make_directory sce_sys/livearea/contents
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/icon0.png sce_sys
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/pic0.png sce_sys
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/template.xml sce_sys/livearea/contents
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/bg.png sce_sys/livearea/contents
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/startup.png sce_sys/livearea/contents
|
||||
DEPENDS sce_sys)
|
||||
|
||||
add_custom_target(${BINARY_NAME}.vpk ALL
|
||||
zip -qr ${BINARY_NAME}.vpk sce_sys eboot.bin
|
||||
DEPENDS livearea eboot.bin param.sfo)
|
||||
vita_create_vpk(${BINARY_NAME}.vpk MGBA00001 ${BINARY_NAME}.self
|
||||
NAME ${PROJECT_NAME}
|
||||
FILE ${CMAKE_CURRENT_SOURCE_DIR}/icon0.png sce_sys/icon0.png
|
||||
FILE ${CMAKE_CURRENT_SOURCE_DIR}/pic0.png sce_sys/pic0.png
|
||||
FILE ${CMAKE_CURRENT_SOURCE_DIR}/bg.png sce_sys/livearea/contents/bg.png
|
||||
FILE ${CMAKE_CURRENT_SOURCE_DIR}/startup.png sce_sys/livearea/contents/startup.png
|
||||
FILE ${CMAKE_CURRENT_BINARY_DIR}/template.xml sce_sys/livearea/contents/template.xml)
|
||||
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${BINARY_NAME}.vpk DESTINATION . COMPONENT ${BINARY_NAME}-psp2)
|
||||
|
|
|
@ -14,7 +14,7 @@ set(CMAKE_PROGRAM_PATH ${toolchain_dir}/bin)
|
|||
|
||||
set(cross_prefix arm-vita-eabi-)
|
||||
set(inc_flags -I${toolchain_dir}/include)
|
||||
set(link_flags "-L${toolchain_dir}/lib -Wl,-q")
|
||||
set(link_flags "-L${toolchain_dir}/lib -Wl,-z,nocopyreloc")
|
||||
|
||||
set(CMAKE_SYSTEM_NAME Generic CACHE INTERNAL "system name")
|
||||
set(CMAKE_SYSTEM_PROCESSOR armv7-a CACHE INTERNAL "processor")
|
||||
|
@ -26,9 +26,9 @@ find_program(CMAKE_C_COMPILER ${cross_prefix}gcc${extension})
|
|||
find_program(CMAKE_CXX_COMPILER ${cross_prefix}g++${extension})
|
||||
find_program(CMAKE_ASM_COMPILER ${cross_prefix}gcc${extension})
|
||||
find_program(CMAKE_LINKER ${cross_prefix}ld${extension})
|
||||
set(CMAKE_C_FLAGS ${inc_flags} CACHE INTERNAL "c compiler flags")
|
||||
set(CMAKE_C_FLAGS "${inc_flags} -Wl,-q" CACHE INTERNAL "c compiler flags")
|
||||
set(CMAKE_ASM_FLAGS ${inc_flags} CACHE INTERNAL "assembler flags")
|
||||
set(CMAKE_CXX_FLAGS ${inc_flags} CACHE INTERNAL "cxx compiler flags")
|
||||
set(CMAKE_CXX_FLAGS "${inc_flags} -Wl,-q" CACHE INTERNAL "cxx compiler flags")
|
||||
|
||||
set(CMAKE_EXE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "exe link flags")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS ${link_flags} CACHE INTERNAL "module link flags")
|
||||
|
@ -41,6 +41,13 @@ set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY CACHE INTERNAL "")
|
|||
set(ENV{PKG_CONFIG_PATH} ${VITASDK}/arm-vita-eabi/lib/pkgconfig)
|
||||
set(ENV{PKG_CONFIG_LIBDIR} ${VITASDK}/arm-vita-eabi/lib/pkgconfig)
|
||||
|
||||
set(VITA_ELF_CREATE "${VITASDK}/bin/vita-elf-create${TOOL_OS_SUFFIX}" CACHE PATH "vita-elf-create")
|
||||
set(VITA_ELF_EXPORT "${VITASDK}/bin/vita-elf-export${TOOL_OS_SUFFIX}" CACHE PATH "vita-elf-export")
|
||||
set(VITA_LIBS_GEN "${VITASDK}/bin/vita-libs-gen${TOOL_OS_SUFFIX}" CACHE PATH "vita-libs-gen")
|
||||
set(VITA_MAKE_FSELF "${VITASDK}/bin/vita-make-fself${TOOL_OS_SUFFIX}" CACHE PATH "vita-make-fself")
|
||||
set(VITA_MKSFOEX "${VITASDK}/bin/vita-mksfoex${TOOL_OS_SUFFIX}" CACHE PATH "vita-mksfoex")
|
||||
set(VITA_PACK_VPK "${VITASDK}/bin/vita-pack-vpk${TOOL_OS_SUFFIX}" CACHE PATH "vita-pack-vpk")
|
||||
|
||||
set(PSP2 ON)
|
||||
add_definitions(-DPSP2)
|
||||
set(M_LIBRARY m)
|
||||
|
|
|
@ -35,6 +35,10 @@ class GB(Core):
|
|||
self._native.video.renderer.cache = ffi.NULL
|
||||
lib.mTileCacheDeinit(cache)
|
||||
|
||||
def reset(self):
|
||||
super(GB, self).reset()
|
||||
self.memory = GBMemory(self._core)
|
||||
|
||||
def attachSIO(self, link):
|
||||
lib.GBSIOSetDriver(ffi.addressof(self._native.sio), link._native)
|
||||
|
||||
|
@ -61,21 +65,46 @@ class GBSIODriver(object):
|
|||
return value
|
||||
|
||||
class GBSIOSimpleDriver(GBSIODriver):
|
||||
def __init__(self):
|
||||
def __init__(self, period=0x100):
|
||||
super(GBSIOSimpleDriver, self).__init__()
|
||||
self.tx = 0xFF
|
||||
self.rx = 0xFF
|
||||
self.rx = 0x00
|
||||
self._period = period
|
||||
|
||||
def init(self):
|
||||
self._native.p.period = self._period
|
||||
return True
|
||||
|
||||
def writeSB(self, value):
|
||||
self.rx = value
|
||||
|
||||
def schedule(self, period=0x100, when=0):
|
||||
def writeSC(self, value):
|
||||
self._native.p.period = self._period
|
||||
if value & 0x80:
|
||||
lib.mTimingDeschedule(ffi.addressof(self._native.p.p.timing), ffi.addressof(self._native.p.event))
|
||||
lib.mTimingSchedule(ffi.addressof(self._native.p.p.timing), ffi.addressof(self._native.p.event), self._native.p.period)
|
||||
return value
|
||||
|
||||
def isReady(self):
|
||||
return not self._native.p.remainingBits
|
||||
|
||||
@property
|
||||
def tx(self):
|
||||
self._native.p.pendingSB
|
||||
|
||||
@property
|
||||
def period(self):
|
||||
return self._native.p.period
|
||||
|
||||
@tx.setter
|
||||
def tx(self, newTx):
|
||||
self._native.p.pendingSB = newTx
|
||||
self._native.p.remainingBits = 8
|
||||
self._native.p.period = period
|
||||
self._native.p.pendingSB = self.tx
|
||||
self.tx = 0xFF
|
||||
lib.mTimingDeschedule(ffi.addressof(self._native.p.p.timing), ffi.addressof(self._native.p.event))
|
||||
lib.mTimingSchedule(ffi.addressof(self._native.p.p.timing), ffi.addressof(self._native.p.event), when)
|
||||
|
||||
@period.setter
|
||||
def period(self, newPeriod):
|
||||
self._period = newPeriod
|
||||
if self._native.p:
|
||||
self._native.p.period = newPeriod
|
||||
|
||||
class GBMemory(Memory):
|
||||
def __init__(self, core):
|
||||
|
|
|
@ -83,7 +83,7 @@
|
|||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>© 2013 – 2016 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0
|
||||
<string>© 2013 – 2017 Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0
|
||||
Game Boy Advance is a registered trademark of Nintendo Co., Ltd.</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
|
|
|
@ -217,7 +217,7 @@ endif()
|
|||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/res/shaders DESTINATION ${DATADIR} COMPONENT ${BINARY_NAME}-qt)
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/res/nointro.dat DESTINATION ${DATADIR} COMPONENT ${BINARY_NAME}-qt)
|
||||
if(NOT WIN32 AND NOT APPLE)
|
||||
list(APPEND QT_DEFINES DATADIR="${DATADIR}")
|
||||
list(APPEND QT_DEFINES DATADIR="${CMAKE_INSTALL_PREFIX}/${DATADIR}")
|
||||
endif()
|
||||
|
||||
find_package(Qt5LinguistTools)
|
||||
|
@ -255,7 +255,7 @@ install(TARGETS ${BINARY_NAME}-qt
|
|||
if(UNIX AND NOT APPLE)
|
||||
find_program(DESKTOP_FILE_INSTALL desktop-file-install)
|
||||
if(DESKTOP_FILE_INSTALL)
|
||||
install(CODE "execute_process(COMMAND ${DESKTOP_FILE_INSTALL} \"${CMAKE_SOURCE_DIR}/res/mgba-qt.desktop\" --dir \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/applications/\")")
|
||||
install(CODE "execute_process(COMMAND ${DESKTOP_FILE_INSTALL} \"${CMAKE_SOURCE_DIR}/res/mgba-qt.desktop\" --dir \"\$ENV{DESTDIR}\${CMAKE_INSTALL_FULL_DATADIR}/applications/\")")
|
||||
endif()
|
||||
endif()
|
||||
if(UNIX)
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "GBAApp.h"
|
||||
|
||||
#include "AudioProcessor.h"
|
||||
#include "ConfigController.h"
|
||||
#include "Display.h"
|
||||
#include "GameController.h"
|
||||
#include "Window.h"
|
||||
|
@ -14,10 +15,8 @@
|
|||
#include <QFileInfo>
|
||||
#include <QFileOpenEvent>
|
||||
#include <QIcon>
|
||||
#include <QTranslator>
|
||||
|
||||
#include <mgba/core/version.h>
|
||||
#include <mgba/internal/gba/video.h>
|
||||
#include <mgba-util/socket.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
|
@ -31,8 +30,9 @@ static GBAApp* g_app = nullptr;
|
|||
|
||||
mLOG_DEFINE_CATEGORY(QT, "Qt", "platform.qt");
|
||||
|
||||
GBAApp::GBAApp(int& argc, char* argv[])
|
||||
GBAApp::GBAApp(int& argc, char* argv[], ConfigController* config)
|
||||
: QApplication(argc, argv)
|
||||
, m_configController(config)
|
||||
{
|
||||
g_app = this;
|
||||
|
||||
|
@ -41,15 +41,9 @@ GBAApp::GBAApp(int& argc, char* argv[])
|
|||
#endif
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
setWindowIcon(QIcon(":/res/mgba-1024.png"));
|
||||
setWindowIcon(QIcon(":/res/mgba-512.png"));
|
||||
#endif
|
||||
|
||||
QTranslator* translator = new QTranslator(this);
|
||||
if (translator->load(QLocale(), QLatin1String(binaryName), QLatin1String("-"), QLatin1String(":/translations"))) {
|
||||
installTranslator(translator);
|
||||
}
|
||||
|
||||
|
||||
SocketSubsystemInit();
|
||||
qRegisterMetaType<const uint32_t*>("const uint32_t*");
|
||||
qRegisterMetaType<mCoreThread*>("mCoreThread*");
|
||||
|
@ -57,50 +51,15 @@ GBAApp::GBAApp(int& argc, char* argv[])
|
|||
QApplication::setApplicationName(projectName);
|
||||
QApplication::setApplicationVersion(projectVersion);
|
||||
|
||||
if (!m_configController.getQtOption("displayDriver").isNull()) {
|
||||
Display::setDriver(static_cast<Display::Driver>(m_configController.getQtOption("displayDriver").toInt()));
|
||||
}
|
||||
|
||||
mArguments args;
|
||||
mGraphicsOpts graphicsOpts;
|
||||
mSubParser subparser;
|
||||
initParserForGraphics(&subparser, &graphicsOpts);
|
||||
bool loaded = m_configController.parseArguments(&args, argc, argv, &subparser);
|
||||
if (loaded && args.showHelp) {
|
||||
usage(argv[0], subparser.usage);
|
||||
::exit(0);
|
||||
return;
|
||||
if (!m_configController->getQtOption("displayDriver").isNull()) {
|
||||
Display::setDriver(static_cast<Display::Driver>(m_configController->getQtOption("displayDriver").toInt()));
|
||||
}
|
||||
|
||||
reloadGameDB();
|
||||
|
||||
if (!m_configController.getQtOption("audioDriver").isNull()) {
|
||||
AudioProcessor::setDriver(static_cast<AudioProcessor::Driver>(m_configController.getQtOption("audioDriver").toInt()));
|
||||
if (!m_configController->getQtOption("audioDriver").isNull()) {
|
||||
AudioProcessor::setDriver(static_cast<AudioProcessor::Driver>(m_configController->getQtOption("audioDriver").toInt()));
|
||||
}
|
||||
Window* w = new Window(&m_configController);
|
||||
connect(w, &Window::destroyed, [this, w]() {
|
||||
m_windows.removeAll(w);
|
||||
});
|
||||
m_windows.append(w);
|
||||
|
||||
if (loaded) {
|
||||
w->argumentsPassed(&args);
|
||||
} else {
|
||||
w->loadConfig();
|
||||
}
|
||||
freeArguments(&args);
|
||||
|
||||
if (graphicsOpts.multiplier) {
|
||||
w->resizeFrame(QSize(VIDEO_HORIZONTAL_PIXELS * graphicsOpts.multiplier, VIDEO_VERTICAL_PIXELS * graphicsOpts.multiplier));
|
||||
}
|
||||
if (graphicsOpts.fullscreen) {
|
||||
w->enterFullScreen();
|
||||
}
|
||||
|
||||
w->show();
|
||||
|
||||
w->controller()->setMultiplayerController(&m_multiplayer);
|
||||
w->multiplayerChanged();
|
||||
}
|
||||
|
||||
GBAApp::~GBAApp() {
|
||||
|
@ -122,7 +81,7 @@ Window* GBAApp::newWindow() {
|
|||
if (m_windows.count() >= MAX_GBAS) {
|
||||
return nullptr;
|
||||
}
|
||||
Window* w = new Window(&m_configController, m_multiplayer.attached());
|
||||
Window* w = new Window(m_configController, m_multiplayer.attached());
|
||||
int windowId = m_multiplayer.attached();
|
||||
connect(w, &Window::destroyed, [this, w]() {
|
||||
m_windows.removeAll(w);
|
||||
|
@ -165,10 +124,10 @@ void GBAApp::continueAll(const QList<Window*>& paused) {
|
|||
QString GBAApp::getOpenFileName(QWidget* owner, const QString& title, const QString& filter) {
|
||||
QList<Window*> paused;
|
||||
pauseAll(&paused);
|
||||
QString filename = QFileDialog::getOpenFileName(owner, title, m_configController.getOption("lastDirectory"), filter);
|
||||
QString filename = QFileDialog::getOpenFileName(owner, title, m_configController->getOption("lastDirectory"), filter);
|
||||
continueAll(paused);
|
||||
if (!filename.isEmpty()) {
|
||||
m_configController.setOption("lastDirectory", QFileInfo(filename).dir().canonicalPath());
|
||||
m_configController->setOption("lastDirectory", QFileInfo(filename).dir().canonicalPath());
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
@ -176,10 +135,10 @@ QString GBAApp::getOpenFileName(QWidget* owner, const QString& title, const QStr
|
|||
QString GBAApp::getSaveFileName(QWidget* owner, const QString& title, const QString& filter) {
|
||||
QList<Window*> paused;
|
||||
pauseAll(&paused);
|
||||
QString filename = QFileDialog::getSaveFileName(owner, title, m_configController.getOption("lastDirectory"), filter);
|
||||
QString filename = QFileDialog::getSaveFileName(owner, title, m_configController->getOption("lastDirectory"), filter);
|
||||
continueAll(paused);
|
||||
if (!filename.isEmpty()) {
|
||||
m_configController.setOption("lastDirectory", QFileInfo(filename).dir().canonicalPath());
|
||||
m_configController->setOption("lastDirectory", QFileInfo(filename).dir().canonicalPath());
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
@ -187,10 +146,10 @@ QString GBAApp::getSaveFileName(QWidget* owner, const QString& title, const QStr
|
|||
QString GBAApp::getOpenDirectoryName(QWidget* owner, const QString& title) {
|
||||
QList<Window*> paused;
|
||||
pauseAll(&paused);
|
||||
QString filename = QFileDialog::getExistingDirectory(owner, title, m_configController.getOption("lastDirectory"));
|
||||
QString filename = QFileDialog::getExistingDirectory(owner, title, m_configController->getOption("lastDirectory"));
|
||||
continueAll(paused);
|
||||
if (!filename.isEmpty()) {
|
||||
m_configController.setOption("lastDirectory", QFileInfo(filename).dir().canonicalPath());
|
||||
m_configController->setOption("lastDirectory", QFileInfo(filename).dir().canonicalPath());
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <QFileDialog>
|
||||
#include <QThread>
|
||||
|
||||
#include "ConfigController.h"
|
||||
#include "MultiplayerController.h"
|
||||
|
||||
struct NoIntroDB;
|
||||
|
@ -21,6 +20,7 @@ mLOG_DECLARE_CATEGORY(QT);
|
|||
|
||||
namespace QGBA {
|
||||
|
||||
class ConfigController;
|
||||
class GameController;
|
||||
class Window;
|
||||
|
||||
|
@ -43,7 +43,7 @@ class GBAApp : public QApplication {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GBAApp(int& argc, char* argv[]);
|
||||
GBAApp(int& argc, char* argv[], ConfigController*);
|
||||
~GBAApp();
|
||||
static GBAApp* app();
|
||||
|
||||
|
@ -67,7 +67,7 @@ private:
|
|||
void pauseAll(QList<Window*>* paused);
|
||||
void continueAll(const QList<Window*>& paused);
|
||||
|
||||
ConfigController m_configController;
|
||||
ConfigController* m_configController;
|
||||
QList<Window*> m_windows;
|
||||
MultiplayerController m_multiplayer;
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "InputController.h"
|
||||
#include "LogController.h"
|
||||
#include "MultiplayerController.h"
|
||||
#include "Override.h"
|
||||
#include "VFileDevice.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
|
|
@ -151,6 +151,7 @@ void MemorySearch::refresh() {
|
|||
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);
|
||||
QTableWidgetItem* type;
|
||||
if (m_ui.numHex->isChecked()) {
|
||||
switch (result->type) {
|
||||
case mCORE_MEMORY_SEARCH_8:
|
||||
|
@ -182,9 +183,30 @@ void MemorySearch::refresh() {
|
|||
item = new QTableWidgetItem("?"); // TODO
|
||||
}
|
||||
}
|
||||
QString divisor;
|
||||
if (result->guessDivisor > 1) {
|
||||
divisor = tr(" (⅟%0×)").arg(result->guessDivisor);
|
||||
}
|
||||
switch (result->type) {
|
||||
case mCORE_MEMORY_SEARCH_8:
|
||||
type = new QTableWidgetItem(tr("1 byte%0").arg(divisor));
|
||||
break;
|
||||
case mCORE_MEMORY_SEARCH_16:
|
||||
type = new QTableWidgetItem(tr("2 bytes%0").arg(divisor));
|
||||
break;
|
||||
case mCORE_MEMORY_SEARCH_GUESS:
|
||||
case mCORE_MEMORY_SEARCH_32:
|
||||
type = new QTableWidgetItem(tr("4 bytes%0").arg(divisor));
|
||||
break;
|
||||
case mCORE_MEMORY_SEARCH_STRING:
|
||||
item = new QTableWidgetItem("?"); // TODO
|
||||
}
|
||||
m_ui.results->setItem(i, 1, item);
|
||||
m_ui.results->setItem(i, 2, type);
|
||||
}
|
||||
m_ui.results->sortItems(0);
|
||||
m_ui.results->resizeColumnsToContents();
|
||||
m_ui.results->resizeRowsToContents();
|
||||
}
|
||||
|
||||
void MemorySearch::openMemory() {
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
<string>Memory Search</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="1">
|
||||
|
@ -214,10 +214,26 @@
|
|||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>MemorySearch</receiver>
|
||||
<slot>close()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>315</x>
|
||||
<y>357</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>315</x>
|
||||
<y>188</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<buttongroups>
|
||||
<buttongroup name="type"/>
|
||||
<buttongroup name="width"/>
|
||||
<buttongroup name="numType"/>
|
||||
<buttongroup name="type"/>
|
||||
</buttongroups>
|
||||
</ui>
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "ShortcutView.h"
|
||||
|
||||
#include <mgba/core/serialize.h>
|
||||
#include <mgba/core/version.h>
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
|
||||
using namespace QGBA;
|
||||
|
@ -145,6 +146,19 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
|
|||
}
|
||||
});
|
||||
|
||||
m_ui.languages->setItemData(0, QLocale("en"));
|
||||
QDir ts(":/translations/");
|
||||
for (auto name : ts.entryList()) {
|
||||
if (!name.endsWith(".qm")) {
|
||||
continue;
|
||||
}
|
||||
QLocale locale(name.remove(QString("%0-").arg(binaryName)).remove(".qm"));
|
||||
m_ui.languages->addItem(locale.nativeLanguageName(), locale);
|
||||
if (locale == QLocale()) {
|
||||
m_ui.languages->setCurrentIndex(m_ui.languages->count() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
m_keyView = new ShortcutView();
|
||||
m_keyView->setModel(inputController->keyIndex());
|
||||
m_keyView->setInputController(inputController);
|
||||
|
@ -239,6 +253,12 @@ void SettingsView::updateConfig() {
|
|||
emit displayDriverChanged();
|
||||
}
|
||||
|
||||
QLocale language = m_ui.languages->itemData(m_ui.languages->currentIndex()).toLocale();
|
||||
if (language != m_controller->getQtOption("language").toLocale() && !(language.bcp47Name() == QLocale::system().bcp47Name() && m_controller->getQtOption("language").isNull())) {
|
||||
m_controller->setQtOption("language", language.bcp47Name());
|
||||
emit languageChanged();
|
||||
}
|
||||
|
||||
m_controller->write();
|
||||
|
||||
m_input->rebuildIndex(m_shortcutView->root());
|
||||
|
|
|
@ -30,6 +30,7 @@ signals:
|
|||
void audioDriverChanged();
|
||||
void displayDriverChanged();
|
||||
void pathsChanged();
|
||||
void languageChanged();
|
||||
void libraryCleared();
|
||||
|
||||
private slots:
|
||||
|
|
|
@ -398,17 +398,30 @@
|
|||
</widget>
|
||||
<widget class="QWidget" name="interface_2">
|
||||
<layout class="QFormLayout" name="formLayout_4">
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="showLibrary">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_26">
|
||||
<property name="text">
|
||||
<string>Show when no game open</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
<string>Language</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="languages">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>English</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Library:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="libraryStyle">
|
||||
<item>
|
||||
<property name="text">
|
||||
|
@ -422,35 +435,38 @@
|
|||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="showLibrary">
|
||||
<property name="text">
|
||||
<string>Library:</string>
|
||||
<string>Show when no game open</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<item row="4" column="1">
|
||||
<widget class="QPushButton" name="clearCache">
|
||||
<property name="text">
|
||||
<string>Clear cache</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="Line" name="line_8">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<item row="6" column="1">
|
||||
<widget class="QCheckBox" name="allowOpposingDirections">
|
||||
<property name="text">
|
||||
<string>Allow opposing input directions</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<item row="7" column="1">
|
||||
<widget class="QCheckBox" name="suspendScreensaver">
|
||||
<property name="text">
|
||||
<string>Suspend screensaver</string>
|
||||
|
@ -460,13 +476,20 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<item row="8" column="1">
|
||||
<widget class="QCheckBox" name="pauseOnFocusLost">
|
||||
<property name="text">
|
||||
<string>Pause when inactive</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="Line" name="line_10">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="emulation">
|
||||
|
|
|
@ -86,7 +86,11 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent)
|
|||
|
||||
m_screenWidget->setMinimumSize(m_display->minimumSize());
|
||||
m_screenWidget->setSizePolicy(m_display->sizePolicy());
|
||||
int i = 2;
|
||||
#if defined(M_CORE_GBA)
|
||||
float i = 2;
|
||||
#elif defined(M_CORE_GB)
|
||||
float i = 3;
|
||||
#endif
|
||||
QVariant multiplier = m_config->getOption("scaleMultiplier");
|
||||
if (!multiplier.isNull()) {
|
||||
m_savedScale = multiplier.toInt();
|
||||
|
@ -120,8 +124,11 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent)
|
|||
m_controller->loadGame(output, path.second, path.first);
|
||||
}
|
||||
});
|
||||
#elif defined(M_CORE_GBA)
|
||||
m_screenWidget->setSizeHint(QSize(VIDEO_HORIZONTAL_PIXELS * i, VIDEO_VERTICAL_PIXELS * i));
|
||||
#endif
|
||||
#if defined(M_CORE_GBA)
|
||||
resizeFrame(QSize(VIDEO_HORIZONTAL_PIXELS * i, VIDEO_VERTICAL_PIXELS * i));
|
||||
#elif defined(M_CORE_GB)
|
||||
resizeFrame(QSize(GB_VIDEO_HORIZONTAL_PIXELS * i, GB_VIDEO_VERTICAL_PIXELS * i));
|
||||
#endif
|
||||
m_screenWidget->setPixmap(m_logo);
|
||||
m_screenWidget->setLockAspectRatio(m_logo.width(), m_logo.height());
|
||||
|
@ -242,9 +249,6 @@ void Window::argumentsPassed(mArguments* args) {
|
|||
|
||||
void Window::resizeFrame(const QSize& size) {
|
||||
QSize newSize(size);
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
||||
newSize /= m_screenWidget->devicePixelRatioF();
|
||||
#endif
|
||||
m_screenWidget->setSizeHint(newSize);
|
||||
newSize -= m_screenWidget->size();
|
||||
newSize += this->size();
|
||||
|
@ -468,6 +472,7 @@ void Window::openSettingsWindow() {
|
|||
connect(settingsWindow, &SettingsView::biosLoaded, m_controller, &GameController::loadBIOS);
|
||||
connect(settingsWindow, &SettingsView::audioDriverChanged, m_controller, &GameController::reloadAudioDriver);
|
||||
connect(settingsWindow, &SettingsView::displayDriverChanged, this, &Window::mustRestart);
|
||||
connect(settingsWindow, &SettingsView::languageChanged, this, &Window::mustRestart);
|
||||
connect(settingsWindow, &SettingsView::pathsChanged, this, &Window::reloadConfig);
|
||||
connect(settingsWindow, &SettingsView::libraryCleared, m_libraryView, &LibraryController::clear);
|
||||
openView(settingsWindow);
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "LibraryController.h"
|
||||
|
||||
#include "../GBAApp.h"
|
||||
#include "ConfigController.h"
|
||||
#include "GBAApp.h"
|
||||
#include "LibraryGrid.h"
|
||||
#include "LibraryTree.h"
|
||||
|
||||
|
@ -45,8 +46,10 @@ LibraryController::LibraryController(QWidget* parent, const QString& path, Confi
|
|||
mLibraryListingInit(&m_listing, 0);
|
||||
|
||||
if (!path.isNull()) {
|
||||
// This can return NULL if the library is already open
|
||||
m_library = mLibraryLoad(path.toUtf8().constData());
|
||||
} else {
|
||||
}
|
||||
if (!m_library) {
|
||||
m_library = mLibraryCreateEmpty();
|
||||
}
|
||||
|
||||
|
|
|
@ -7,14 +7,16 @@
|
|||
// This must be defined before anything else is included.
|
||||
#define SDL_MAIN_HANDLED
|
||||
|
||||
#include "ConfigController.h"
|
||||
#include "GBAApp.h"
|
||||
#include "Window.h"
|
||||
|
||||
#include <mgba/core/version.h>
|
||||
#include <mgba/internal/gba/video.h>
|
||||
|
||||
#include <QLibraryInfo>
|
||||
#include <QTranslator>
|
||||
|
||||
#include <mgba/core/version.h>
|
||||
|
||||
#ifdef QT_STATIC
|
||||
#include <QtPlugin>
|
||||
#ifdef _WIN32
|
||||
|
@ -25,13 +27,32 @@ Q_IMPORT_PLUGIN(QWindowsAudioPlugin);
|
|||
#endif
|
||||
#endif
|
||||
|
||||
using namespace QGBA;
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
#ifdef BUILD_SDL
|
||||
SDL_SetMainReady();
|
||||
#endif
|
||||
QGBA::GBAApp application(argc, argv);
|
||||
|
||||
QLocale locale = QLocale::system();
|
||||
ConfigController configController;
|
||||
|
||||
QLocale locale;
|
||||
if (!configController.getQtOption("language").isNull()) {
|
||||
locale = QLocale(configController.getQtOption("language").toString());
|
||||
QLocale::setDefault(locale);
|
||||
}
|
||||
|
||||
mArguments args;
|
||||
mGraphicsOpts graphicsOpts;
|
||||
mSubParser subparser;
|
||||
initParserForGraphics(&subparser, &graphicsOpts);
|
||||
bool loaded = configController.parseArguments(&args, argc, argv, &subparser);
|
||||
if (loaded && args.showHelp) {
|
||||
usage(argv[0], subparser.usage);
|
||||
return 0;
|
||||
}
|
||||
|
||||
GBAApp application(argc, argv, &configController);
|
||||
|
||||
QTranslator qtTranslator;
|
||||
qtTranslator.load(locale, "qt", "_", QLibraryInfo::location(QLibraryInfo::TranslationsPath));
|
||||
|
@ -41,5 +62,22 @@ int main(int argc, char* argv[]) {
|
|||
langTranslator.load(locale, binaryName, "-", ":/translations/");
|
||||
application.installTranslator(&langTranslator);
|
||||
|
||||
Window* w = application.newWindow();
|
||||
if (loaded) {
|
||||
w->argumentsPassed(&args);
|
||||
} else {
|
||||
w->loadConfig();
|
||||
}
|
||||
freeArguments(&args);
|
||||
|
||||
if (graphicsOpts.multiplier) {
|
||||
w->resizeFrame(QSize(VIDEO_HORIZONTAL_PIXELS * graphicsOpts.multiplier, VIDEO_VERTICAL_PIXELS * graphicsOpts.multiplier));
|
||||
}
|
||||
if (graphicsOpts.fullscreen) {
|
||||
w->enterFullScreen();
|
||||
}
|
||||
|
||||
w->show();
|
||||
|
||||
return application.exec();
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue