mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' (early part) into medusa
This commit is contained in:
commit
3496336f46
7
CHANGES
7
CHANGES
|
@ -53,6 +53,12 @@ Bugfixes:
|
||||||
- GBA Hardware: Fix RTC overriding light sensor (fixes mgba.io/i/1069)
|
- GBA Hardware: Fix RTC overriding light sensor (fixes mgba.io/i/1069)
|
||||||
- GBA Savedata: Fix savedata modified time updating when read-only
|
- GBA Savedata: Fix savedata modified time updating when read-only
|
||||||
- GB Video: Fix enabling window when LY > WY (fixes mgba.io/i/409)
|
- GB Video: Fix enabling window when LY > WY (fixes mgba.io/i/409)
|
||||||
|
- GBA Video: Start timing mid-scanline when skipping BIOS
|
||||||
|
- Core: Fix audio sync breaking when interrupted
|
||||||
|
- Qt: Improve FPS timer stability
|
||||||
|
- GBA Serialize: Fix loading channel 3 volume (fixes mgba.io/i/1107)
|
||||||
|
- GBA SIO: Fix unconnected SIOCNT for multi mode (fixes mgba.io/i/1105)
|
||||||
|
- GBA BIOS: Fix BitUnPack final byte
|
||||||
Misc:
|
Misc:
|
||||||
- GBA Timer: Use global cycles for timers
|
- GBA Timer: Use global cycles for timers
|
||||||
- GBA: Extend oddly-sized ROMs to full address space (fixes mgba.io/i/722)
|
- GBA: Extend oddly-sized ROMs to full address space (fixes mgba.io/i/722)
|
||||||
|
@ -77,6 +83,7 @@ Misc:
|
||||||
- Qt: Add load alternate save option
|
- Qt: Add load alternate save option
|
||||||
- GB Audio: Improved audio quality
|
- GB Audio: Improved audio quality
|
||||||
- GB, GBA Audio: Increase max audio volume
|
- GB, GBA Audio: Increase max audio volume
|
||||||
|
- GB: Fix VRAM/palette locking (fixes mgba.io/i/1109)
|
||||||
|
|
||||||
0.6.3: (2017-04-14)
|
0.6.3: (2017-04-14)
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
|
|
@ -909,10 +909,6 @@ endif()
|
||||||
|
|
||||||
if(BUILD_SDL)
|
if(BUILD_SDL)
|
||||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/platform/sdl ${CMAKE_CURRENT_BINARY_DIR}/sdl)
|
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/platform/sdl ${CMAKE_CURRENT_BINARY_DIR}/sdl)
|
||||||
# The SDL platform CMakeLists could decide to disable SDL, so check again before adding the define.
|
|
||||||
if(BUILD_SDL)
|
|
||||||
add_definitions(-DBUILD_SDL)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(BUILD_QT)
|
if(BUILD_QT)
|
||||||
|
@ -967,7 +963,7 @@ if(BUILD_EXAMPLE)
|
||||||
target_link_libraries(${BINARY_NAME}-example-server ${BINARY_NAME})
|
target_link_libraries(${BINARY_NAME}-example-server ${BINARY_NAME})
|
||||||
set_target_properties(${BINARY_NAME}-example-server PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
|
set_target_properties(${BINARY_NAME}-example-server PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
|
||||||
|
|
||||||
if(BUILD_SDL)
|
if(FOUND_SDL)
|
||||||
add_executable(${BINARY_NAME}-example-client ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/example/client-server/client.c)
|
add_executable(${BINARY_NAME}-example-client ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/example/client-server/client.c)
|
||||||
target_link_libraries(${BINARY_NAME}-example-client ${BINARY_NAME} ${SDL_LIBRARY} ${SDLMAIN_LIBRARY} ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY})
|
target_link_libraries(${BINARY_NAME}-example-client ${BINARY_NAME} ${SDL_LIBRARY} ${SDLMAIN_LIBRARY} ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY})
|
||||||
set_target_properties(${BINARY_NAME}-example-client PROPERTIES
|
set_target_properties(${BINARY_NAME}-example-client PROPERTIES
|
||||||
|
@ -1071,12 +1067,12 @@ if(BUILD_QT)
|
||||||
cpack_add_component(${BINARY_NAME}-qt GROUP qt DEPENDS base)
|
cpack_add_component(${BINARY_NAME}-qt GROUP qt DEPENDS base)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(BUILD_SDL)
|
if(FOUND_SDL)
|
||||||
cpack_add_component_group(sdl PARENT_GROUP base)
|
cpack_add_component_group(sdl PARENT_GROUP base)
|
||||||
cpack_add_component(${BINARY_NAME}-sdl GROUP sdl DEPENDS base)
|
cpack_add_component(${BINARY_NAME}-sdl GROUP sdl DEPENDS base)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
cpack_add_component_group($test PARENT_GROUP dev)
|
cpack_add_component_group(test PARENT_GROUP dev)
|
||||||
cpack_add_component(${BINARY_NAME}-perf GROUP test DEPENDS dev)
|
cpack_add_component(${BINARY_NAME}-perf GROUP test DEPENDS dev)
|
||||||
cpack_add_component(${BINARY_NAME}-fuzz GROUP test DEPENDS dev)
|
cpack_add_component(${BINARY_NAME}-fuzz GROUP test DEPENDS dev)
|
||||||
cpack_add_component(tbl-fuzz GROUP test DEPENDS dev)
|
cpack_add_component(tbl-fuzz GROUP test DEPENDS dev)
|
||||||
|
|
|
@ -140,7 +140,7 @@ This will build and install medusa into `/usr/bin` and `/usr/lib`. Dependencies
|
||||||
|
|
||||||
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:
|
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
|
brew install cmake ffmpeg imagemagick libzip qt5 sdl2 libedit pkg-config
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake -DCMAKE_PREFIX_PATH=`brew --prefix qt5` ..
|
cmake -DCMAKE_PREFIX_PATH=`brew --prefix qt5` ..
|
||||||
|
|
|
@ -122,7 +122,7 @@ Damit wird mGBA gebaut und in `/usr/bin` und `/usr/lib` installiert. Installiert
|
||||||
|
|
||||||
Wenn Du macOS verwendest, sind die einzelnen Schritte etwas anders. Angenommen, dass Du eine Homebrew-Paketverwaltung verwendest, werden folgende Schritte zum installieren der Abhängigkeiten und anschließenden bauen von mGBA empfohlen:
|
Wenn Du macOS verwendest, sind die einzelnen Schritte etwas anders. Angenommen, dass Du eine Homebrew-Paketverwaltung verwendest, werden folgende Schritte zum installieren der Abhängigkeiten und anschließenden bauen von mGBA empfohlen:
|
||||||
|
|
||||||
brew install cmake ffmpeg imagemagick libzip qt5 sdl2 libedit
|
brew install cmake ffmpeg imagemagick libzip qt5 sdl2 libedit pkg-config
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake -DCMAKE_PREFIX_PATH='brew --prefix qt5' ..
|
cmake -DCMAKE_PREFIX_PATH='brew --prefix qt5' ..
|
||||||
|
|
|
@ -136,12 +136,13 @@ typedef intptr_t ssize_t;
|
||||||
uint32_t lo; \
|
uint32_t lo; \
|
||||||
}; \
|
}; \
|
||||||
uint64_t b64; \
|
uint64_t b64; \
|
||||||
} *bswap = (void*) &DEST; \
|
} bswap; \
|
||||||
const void* _ptr = (ARR); \
|
const void* _ptr = (ARR); \
|
||||||
__asm__( \
|
__asm__( \
|
||||||
"lwbrx %0, %2, %3 \n" \
|
"lwbrx %0, %2, %3 \n" \
|
||||||
"lwbrx %1, %2, %4 \n" \
|
"lwbrx %1, %2, %4 \n" \
|
||||||
: "=&r"(bswap->lo), "=&r"(bswap->hi) : "b"(_ptr), "r"(_addr), "r"(_addr + 4)) ; \
|
: "=&r"(bswap.lo), "=&r"(bswap.hi) : "b"(_ptr), "r"(_addr), "r"(_addr + 4)) ; \
|
||||||
|
DEST = bswap.b64; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define STORE_64LE(SRC, ADDR, ARR) { \
|
#define STORE_64LE(SRC, ADDR, ARR) { \
|
||||||
|
@ -152,12 +153,12 @@ typedef intptr_t ssize_t;
|
||||||
uint32_t lo; \
|
uint32_t lo; \
|
||||||
}; \
|
}; \
|
||||||
uint64_t b64; \
|
uint64_t b64; \
|
||||||
} *bswap = (void*) &SRC; \
|
} bswap = { .b64 = SRC }; \
|
||||||
const void* _ptr = (ARR); \
|
const void* _ptr = (ARR); \
|
||||||
__asm__( \
|
__asm__( \
|
||||||
"stwbrx %0, %2, %3 \n" \
|
"stwbrx %0, %2, %3 \n" \
|
||||||
"stwbrx %1, %2, %4 \n" \
|
"stwbrx %1, %2, %4 \n" \
|
||||||
: : "r"(bswap->hi), "r"(bswap->lo), "b"(_ptr), "r"(_addr), "r"(_addr + 4) : "memory"); \
|
: : "r"(bswap.hi), "r"(bswap.lo), "b"(_ptr), "r"(_addr), "r"(_addr + 4) : "memory"); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(__llvm__) || (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
|
#elif defined(__llvm__) || (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
|
||||||
|
|
|
@ -33,7 +33,8 @@ bool mCoreSyncWaitFrameStart(struct mCoreSync* sync);
|
||||||
void mCoreSyncWaitFrameEnd(struct mCoreSync* sync);
|
void mCoreSyncWaitFrameEnd(struct mCoreSync* sync);
|
||||||
void mCoreSyncSetVideoSync(struct mCoreSync* sync, bool wait);
|
void mCoreSyncSetVideoSync(struct mCoreSync* sync, bool wait);
|
||||||
|
|
||||||
void mCoreSyncProduceAudio(struct mCoreSync* sync, bool wait);
|
struct blip_t;
|
||||||
|
bool mCoreSyncProduceAudio(struct mCoreSync* sync, const struct blip_t*, size_t samples);
|
||||||
void mCoreSyncLockAudio(struct mCoreSync* sync);
|
void mCoreSyncLockAudio(struct mCoreSync* sync);
|
||||||
void mCoreSyncUnlockAudio(struct mCoreSync* sync);
|
void mCoreSyncUnlockAudio(struct mCoreSync* sync);
|
||||||
void mCoreSyncConsumeAudio(struct mCoreSync* sync);
|
void mCoreSyncConsumeAudio(struct mCoreSync* sync);
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#cmakedefine BUILD_GLES2
|
#cmakedefine BUILD_GLES2
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// COLOR flags
|
// Miscellaneous flags
|
||||||
|
|
||||||
#ifndef COLOR_16_BIT
|
#ifndef COLOR_16_BIT
|
||||||
#cmakedefine COLOR_16_BIT
|
#cmakedefine COLOR_16_BIT
|
||||||
|
@ -25,6 +25,14 @@
|
||||||
#cmakedefine COLOR_5_6_5
|
#cmakedefine COLOR_5_6_5
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef DISABLE_THREADING
|
||||||
|
#cmakedefine DISABLE_THREADING
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FIXED_ROM_BUFFER
|
||||||
|
#cmakedefine FIXED_ROM_BUFFER
|
||||||
|
#endif
|
||||||
|
|
||||||
// M_CORE flags
|
// M_CORE flags
|
||||||
|
|
||||||
#ifndef M_CORE_GBA
|
#ifndef M_CORE_GBA
|
||||||
|
@ -39,9 +47,13 @@
|
||||||
#cmakedefine M_CORE_DS
|
#cmakedefine M_CORE_DS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// USE flags
|
// ENABLE flags
|
||||||
|
|
||||||
#ifndef MINIMAL_CORE
|
#ifndef ENABLE_SCRIPTING
|
||||||
|
#cmakedefine ENABLE_SCRIPTING
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// USE flags
|
||||||
|
|
||||||
#ifndef USE_DEBUGGERS
|
#ifndef USE_DEBUGGERS
|
||||||
#cmakedefine USE_DEBUGGERS
|
#cmakedefine USE_DEBUGGERS
|
||||||
|
@ -51,6 +63,10 @@
|
||||||
#cmakedefine USE_EDITLINE
|
#cmakedefine USE_EDITLINE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef USE_ELF
|
||||||
|
#cmakedefine USE_ELF
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef USE_EPOXY
|
#ifndef USE_EPOXY
|
||||||
#cmakedefine USE_EPOXY
|
#cmakedefine USE_EPOXY
|
||||||
#endif
|
#endif
|
||||||
|
@ -100,5 +116,3 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -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/. */
|
||||||
#include <mgba/core/sync.h>
|
#include <mgba/core/sync.h>
|
||||||
|
|
||||||
|
#include <mgba/core/blip_buf.h>
|
||||||
|
|
||||||
static void _changeVideoSync(struct mCoreSync* sync, bool frameOn) {
|
static void _changeVideoSync(struct mCoreSync* sync, bool frameOn) {
|
||||||
// Make sure the video thread can process events while the GBA thread is paused
|
// Make sure the video thread can process events while the GBA thread is paused
|
||||||
MutexLock(&sync->videoFrameMutex);
|
MutexLock(&sync->videoFrameMutex);
|
||||||
|
@ -76,16 +78,20 @@ void mCoreSyncSetVideoSync(struct mCoreSync* sync, bool wait) {
|
||||||
_changeVideoSync(sync, wait);
|
_changeVideoSync(sync, wait);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mCoreSyncProduceAudio(struct mCoreSync* sync, bool wait) {
|
bool mCoreSyncProduceAudio(struct mCoreSync* sync, const struct blip_t* buf, size_t samples) {
|
||||||
if (!sync) {
|
if (!sync) {
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sync->audioWait && wait) {
|
size_t produced = blip_samples_avail(buf);
|
||||||
// TODO loop properly in event of spurious wakeups
|
size_t producedNew = produced;
|
||||||
|
while (sync->audioWait && producedNew >= samples) {
|
||||||
ConditionWait(&sync->audioRequiredCond, &sync->audioBufferMutex);
|
ConditionWait(&sync->audioRequiredCond, &sync->audioBufferMutex);
|
||||||
|
produced = producedNew;
|
||||||
|
producedNew = blip_samples_avail(buf);
|
||||||
}
|
}
|
||||||
MutexUnlock(&sync->audioBufferMutex);
|
MutexUnlock(&sync->audioBufferMutex);
|
||||||
|
return producedNew != produced;
|
||||||
}
|
}
|
||||||
|
|
||||||
void mCoreSyncLockAudio(struct mCoreSync* sync) {
|
void mCoreSyncLockAudio(struct mCoreSync* sync) {
|
||||||
|
|
|
@ -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/core/thread.h>
|
#include <mgba/core/thread.h>
|
||||||
|
|
||||||
|
#include <mgba/core/blip_buf.h>
|
||||||
#include <mgba/core/core.h>
|
#include <mgba/core/core.h>
|
||||||
#include <mgba/core/serialize.h>
|
#include <mgba/core/serialize.h>
|
||||||
#include <mgba-util/patch.h>
|
#include <mgba-util/patch.h>
|
||||||
|
@ -219,6 +220,11 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) {
|
||||||
deferred = impl->state;
|
deferred = impl->state;
|
||||||
while (impl->state >= THREAD_WAITING && impl->state <= THREAD_MAX_WAITING) {
|
while (impl->state >= THREAD_WAITING && impl->state <= THREAD_MAX_WAITING) {
|
||||||
ConditionWait(&impl->stateCond, &impl->stateMutex);
|
ConditionWait(&impl->stateCond, &impl->stateMutex);
|
||||||
|
|
||||||
|
if (impl->sync.audioWait) {
|
||||||
|
mCoreSyncLockAudio(&impl->sync);
|
||||||
|
mCoreSyncProduceAudio(&impl->sync, core->getAudioChannel(core, 0), core->getAudioBufferSize(core));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MutexUnlock(&impl->stateMutex);
|
MutexUnlock(&impl->stateMutex);
|
||||||
|
|
|
@ -352,7 +352,7 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
||||||
audio->p->stream->postAudioFrame(audio->p->stream, sampleLeft, sampleRight);
|
audio->p->stream->postAudioFrame(audio->p->stream, sampleLeft, sampleRight);
|
||||||
}
|
}
|
||||||
bool wait = produced >= audio->samples;
|
bool wait = produced >= audio->samples;
|
||||||
mCoreSyncProduceAudio(audio->p->sync, wait);
|
mCoreSyncProduceAudio(audio->p->sync, audio->left, wait);
|
||||||
|
|
||||||
if (wait && audio->p->stream && audio->p->stream->postAudioBuffer) {
|
if (wait && audio->p->stream && audio->p->stream->postAudioBuffer) {
|
||||||
audio->p->stream->postAudioBuffer(audio->p->stream, audio->left, audio->right);
|
audio->p->stream->postAudioBuffer(audio->p->stream, audio->left, audio->right);
|
||||||
|
|
|
@ -667,7 +667,10 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
||||||
audio->p->stream->postAudioFrame(audio->p->stream, sampleLeft, sampleRight);
|
audio->p->stream->postAudioFrame(audio->p->stream, sampleLeft, sampleRight);
|
||||||
}
|
}
|
||||||
bool wait = produced >= audio->samples;
|
bool wait = produced >= audio->samples;
|
||||||
mCoreSyncProduceAudio(audio->p->sync, wait);
|
if (!mCoreSyncProduceAudio(audio->p->sync, audio->left, audio->samples)) {
|
||||||
|
// Interrupted
|
||||||
|
audio->p->earlyExit = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (wait && audio->p->stream && audio->p->stream->postAudioBuffer) {
|
if (wait && audio->p->stream && audio->p->stream->postAudioBuffer) {
|
||||||
audio->p->stream->postAudioBuffer(audio->p->stream, audio->left, audio->right);
|
audio->p->stream->postAudioBuffer(audio->p->stream, audio->left, audio->right);
|
||||||
|
|
|
@ -644,17 +644,17 @@ void GBProcessEvents(struct LR35902Core* cpu) {
|
||||||
} while (gb->cpuBlocked);
|
} while (gb->cpuBlocked);
|
||||||
cpu->nextEvent = nextEvent;
|
cpu->nextEvent = nextEvent;
|
||||||
|
|
||||||
if (gb->earlyExit) {
|
|
||||||
gb->earlyExit = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (cpu->halted) {
|
if (cpu->halted) {
|
||||||
cpu->cycles = cpu->nextEvent;
|
cpu->cycles = cpu->nextEvent;
|
||||||
if (!gb->memory.ie || !gb->memory.ime) {
|
if (!gb->memory.ie || !gb->memory.ime) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (gb->earlyExit) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
} while (cpu->cycles >= cpu->nextEvent);
|
} while (cpu->cycles >= cpu->nextEvent);
|
||||||
|
gb->earlyExit = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBSetInterrupts(struct LR35902Core* cpu, bool enable) {
|
void GBSetInterrupts(struct LR35902Core* cpu, bool enable) {
|
||||||
|
|
33
src/gb/io.c
33
src/gb/io.c
|
@ -104,6 +104,8 @@ static const uint8_t _registerMask[] = {
|
||||||
[REG_IE] = 0xE0,
|
[REG_IE] = 0xE0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static uint8_t _readKeys(struct GB* gb);
|
||||||
|
|
||||||
static void _writeSGBBits(struct GB* gb, int bits) {
|
static void _writeSGBBits(struct GB* gb, int bits) {
|
||||||
if (!bits) {
|
if (!bits) {
|
||||||
gb->sgbBit = -1;
|
gb->sgbBit = -1;
|
||||||
|
@ -394,10 +396,12 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case REG_JOYP:
|
case REG_JOYP:
|
||||||
|
gb->memory.io[REG_JOYP] = value | 0x0F;
|
||||||
|
_readKeys(gb);
|
||||||
if (gb->model == GB_MODEL_SGB) {
|
if (gb->model == GB_MODEL_SGB) {
|
||||||
_writeSGBBits(gb, (value >> 4) & 3);
|
_writeSGBBits(gb, (value >> 4) & 3);
|
||||||
}
|
}
|
||||||
break;
|
return;
|
||||||
case REG_TIMA:
|
case REG_TIMA:
|
||||||
if (value && mTimingUntil(&gb->timing, &gb->timer.irq) > 1) {
|
if (value && mTimingUntil(&gb->timing, &gb->timer.irq) > 1) {
|
||||||
mTimingDeschedule(&gb->timing, &gb->timer.irq);
|
mTimingDeschedule(&gb->timing, &gb->timer.irq);
|
||||||
|
@ -485,8 +489,10 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) {
|
||||||
gb->memory.io[REG_BCPD] = gb->video.palette[gb->video.bcpIndex >> 1] >> (8 * (gb->video.bcpIndex & 1));
|
gb->memory.io[REG_BCPD] = gb->video.palette[gb->video.bcpIndex >> 1] >> (8 * (gb->video.bcpIndex & 1));
|
||||||
break;
|
break;
|
||||||
case REG_BCPD:
|
case REG_BCPD:
|
||||||
GBVideoProcessDots(&gb->video, 0);
|
if (gb->video.mode != 3) {
|
||||||
GBVideoWritePalette(&gb->video, address, value);
|
GBVideoProcessDots(&gb->video, 0);
|
||||||
|
GBVideoWritePalette(&gb->video, address, value);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
case REG_OCPS:
|
case REG_OCPS:
|
||||||
gb->video.ocpIndex = value & 0x3F;
|
gb->video.ocpIndex = value & 0x3F;
|
||||||
|
@ -494,8 +500,10 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) {
|
||||||
gb->memory.io[REG_OCPD] = gb->video.palette[8 * 4 + (gb->video.ocpIndex >> 1)] >> (8 * (gb->video.ocpIndex & 1));
|
gb->memory.io[REG_OCPD] = gb->video.palette[8 * 4 + (gb->video.ocpIndex >> 1)] >> (8 * (gb->video.ocpIndex & 1));
|
||||||
break;
|
break;
|
||||||
case REG_OCPD:
|
case REG_OCPD:
|
||||||
GBVideoProcessDots(&gb->video, 0);
|
if (gb->video.mode != 3) {
|
||||||
GBVideoWritePalette(&gb->video, address, value);
|
GBVideoProcessDots(&gb->video, 0);
|
||||||
|
GBVideoWritePalette(&gb->video, address, value);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
case REG_SVBK:
|
case REG_SVBK:
|
||||||
GBMemorySwitchWramBank(&gb->memory, value);
|
GBMemorySwitchWramBank(&gb->memory, value);
|
||||||
|
@ -522,7 +530,8 @@ static uint8_t _readKeys(struct GB* gb) {
|
||||||
if (gb->sgbCurrentController != 0) {
|
if (gb->sgbCurrentController != 0) {
|
||||||
keys = 0;
|
keys = 0;
|
||||||
}
|
}
|
||||||
switch (gb->memory.io[REG_JOYP] & 0x30) {
|
uint8_t joyp = gb->memory.io[REG_JOYP];
|
||||||
|
switch (joyp & 0x30) {
|
||||||
case 0x30:
|
case 0x30:
|
||||||
keys = gb->sgbCurrentController;
|
keys = gb->sgbCurrentController;
|
||||||
break;
|
break;
|
||||||
|
@ -535,7 +544,12 @@ static uint8_t _readKeys(struct GB* gb) {
|
||||||
keys |= keys >> 4;
|
keys |= keys >> 4;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return (0xC0 | (gb->memory.io[REG_JOYP] | 0xF)) ^ (keys & 0xF);
|
gb->memory.io[REG_JOYP] = (0xCF | joyp) ^ (keys & 0xF);
|
||||||
|
if (joyp & ~gb->memory.io[REG_JOYP] & 0xF) {
|
||||||
|
gb->memory.io[REG_IF] |= (1 << GB_IRQ_KEYPAD);
|
||||||
|
GBUpdateIRQs(gb);
|
||||||
|
}
|
||||||
|
return gb->memory.io[REG_JOYP];
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t GBIORead(struct GB* gb, unsigned address) {
|
uint8_t GBIORead(struct GB* gb, unsigned address) {
|
||||||
|
@ -639,10 +653,7 @@ uint8_t GBIORead(struct GB* gb, unsigned address) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBTestKeypadIRQ(struct GB* gb) {
|
void GBTestKeypadIRQ(struct GB* gb) {
|
||||||
if (_readKeys(gb)) {
|
_readKeys(gb);
|
||||||
gb->memory.io[REG_IF] |= (1 << GB_IRQ_KEYPAD);
|
|
||||||
GBUpdateIRQs(gb);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GBSerializedState;
|
struct GBSerializedState;
|
||||||
|
|
|
@ -239,7 +239,10 @@ uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address) {
|
||||||
return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
|
return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
|
||||||
case GB_REGION_VRAM:
|
case GB_REGION_VRAM:
|
||||||
case GB_REGION_VRAM + 1:
|
case GB_REGION_VRAM + 1:
|
||||||
return gb->video.vramBank[address & (GB_SIZE_VRAM_BANK0 - 1)];
|
if (gb->video.mode != 3) {
|
||||||
|
return gb->video.vramBank[address & (GB_SIZE_VRAM_BANK0 - 1)];
|
||||||
|
}
|
||||||
|
return 0xFF;
|
||||||
case GB_REGION_EXTERNAL_RAM:
|
case GB_REGION_EXTERNAL_RAM:
|
||||||
case GB_REGION_EXTERNAL_RAM + 1:
|
case GB_REGION_EXTERNAL_RAM + 1:
|
||||||
if (memory->rtcAccess) {
|
if (memory->rtcAccess) {
|
||||||
|
@ -309,8 +312,10 @@ void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) {
|
||||||
return;
|
return;
|
||||||
case GB_REGION_VRAM:
|
case GB_REGION_VRAM:
|
||||||
case GB_REGION_VRAM + 1:
|
case GB_REGION_VRAM + 1:
|
||||||
gb->video.renderer->writeVRAM(gb->video.renderer, (address & (GB_SIZE_VRAM_BANK0 - 1)) | (GB_SIZE_VRAM_BANK0 * gb->video.vramCurrentBank));
|
if (gb->video.mode != 3) {
|
||||||
gb->video.vramBank[address & (GB_SIZE_VRAM_BANK0 - 1)] = value;
|
gb->video.renderer->writeVRAM(gb->video.renderer, (address & (GB_SIZE_VRAM_BANK0 - 1)) | (GB_SIZE_VRAM_BANK0 * gb->video.vramCurrentBank));
|
||||||
|
gb->video.vramBank[address & (GB_SIZE_VRAM_BANK0 - 1)] = value;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
case GB_REGION_EXTERNAL_RAM:
|
case GB_REGION_EXTERNAL_RAM:
|
||||||
case GB_REGION_EXTERNAL_RAM + 1:
|
case GB_REGION_EXTERNAL_RAM + 1:
|
||||||
|
@ -490,7 +495,7 @@ uint8_t GBMemoryWriteHDMA5(struct GB* gb, uint8_t value) {
|
||||||
gb->memory.hdmaDest |= 0x8000;
|
gb->memory.hdmaDest |= 0x8000;
|
||||||
bool wasHdma = gb->memory.isHdma;
|
bool wasHdma = gb->memory.isHdma;
|
||||||
gb->memory.isHdma = value & 0x80;
|
gb->memory.isHdma = value & 0x80;
|
||||||
if ((!wasHdma && !gb->memory.isHdma) || gb->video.mode == 0) {
|
if ((!wasHdma && !gb->memory.isHdma) || (GBRegisterLCDCIsEnable(gb->memory.io[REG_LCDC]) && gb->video.mode == 0)) {
|
||||||
if (gb->memory.isHdma) {
|
if (gb->memory.isHdma) {
|
||||||
gb->memory.hdmaRemaining = 0x10;
|
gb->memory.hdmaRemaining = 0x10;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -12,7 +12,12 @@
|
||||||
#include <mgba-util/crc32.h>
|
#include <mgba-util/crc32.h>
|
||||||
|
|
||||||
static const struct GBCartridgeOverride _overrides[] = {
|
static const struct GBCartridgeOverride _overrides[] = {
|
||||||
// None yet
|
// Pokemon Spaceworld 1997 demo
|
||||||
|
{ 0x232a067d, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (debug)
|
||||||
|
{ 0x630ed957, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (non-debug)
|
||||||
|
{ 0x5aff0038, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (debug)
|
||||||
|
{ 0xa61856bd, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (non-debug)
|
||||||
|
|
||||||
{ 0, 0, 0, { 0 } }
|
{ 0, 0, 0, { 0 } }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -147,7 +147,7 @@ void GBVideoReset(struct GBVideo* video) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBVideoDeinit(struct GBVideo* video) {
|
void GBVideoDeinit(struct GBVideo* video) {
|
||||||
GBVideoAssociateRenderer(video, &dummyRenderer);
|
video->renderer->deinit(video->renderer);
|
||||||
mappedMemoryFree(video->vram, GB_SIZE_VRAM);
|
mappedMemoryFree(video->vram, GB_SIZE_VRAM);
|
||||||
if (video->renderer->sgbCharRam) {
|
if (video->renderer->sgbCharRam) {
|
||||||
mappedMemoryFree(video->renderer->sgbCharRam, SGB_SIZE_CHAR_RAM);
|
mappedMemoryFree(video->renderer->sgbCharRam, SGB_SIZE_CHAR_RAM);
|
||||||
|
@ -437,6 +437,7 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
|
||||||
}
|
}
|
||||||
if (GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) && !GBRegisterLCDCIsEnable(value)) {
|
if (GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) && !GBRegisterLCDCIsEnable(value)) {
|
||||||
// TODO: Fix serialization; this gets internal and visible modes out of sync
|
// TODO: Fix serialization; this gets internal and visible modes out of sync
|
||||||
|
video->mode = 0;
|
||||||
video->stat = GBRegisterSTATSetMode(video->stat, 0);
|
video->stat = GBRegisterSTATSetMode(video->stat, 0);
|
||||||
video->p->memory.io[REG_STAT] = video->stat;
|
video->p->memory.io[REG_STAT] = video->stat;
|
||||||
video->ly = 0;
|
video->ly = 0;
|
||||||
|
|
|
@ -307,7 +307,10 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
||||||
audio->p->stream->postAudioFrame(audio->p->stream, sampleLeft, sampleRight);
|
audio->p->stream->postAudioFrame(audio->p->stream, sampleLeft, sampleRight);
|
||||||
}
|
}
|
||||||
bool wait = produced >= audio->samples;
|
bool wait = produced >= audio->samples;
|
||||||
mCoreSyncProduceAudio(audio->p->sync, wait);
|
if (!mCoreSyncProduceAudio(audio->p->sync, audio->psg.left, audio->samples)) {
|
||||||
|
// Interrupted
|
||||||
|
audio->p->earlyExit = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (wait && audio->p->stream && audio->p->stream->postAudioBuffer) {
|
if (wait && audio->p->stream && audio->p->stream->postAudioBuffer) {
|
||||||
audio->p->stream->postAudioBuffer(audio->p->stream, audio->psg.left, audio->psg.right);
|
audio->p->stream->postAudioBuffer(audio->p->stream, audio->psg.left, audio->psg.right);
|
||||||
|
|
|
@ -809,7 +809,7 @@ static void _unBitPack(struct GBA* gba) {
|
||||||
uint32_t out = 0;
|
uint32_t out = 0;
|
||||||
int bitsRemaining = 0;
|
int bitsRemaining = 0;
|
||||||
int bitsEaten = 0;
|
int bitsEaten = 0;
|
||||||
while (sourceLen > 0) {
|
while (sourceLen > 0 || bitsRemaining) {
|
||||||
if (!bitsRemaining) {
|
if (!bitsRemaining) {
|
||||||
in = cpu->memory.load8(cpu, source, 0);
|
in = cpu->memory.load8(cpu, source, 0);
|
||||||
bitsRemaining = 8;
|
bitsRemaining = 8;
|
||||||
|
|
|
@ -264,11 +264,6 @@ static void GBAProcessEvents(struct ARMCore* cpu) {
|
||||||
} while (gba->cpuBlocked);
|
} while (gba->cpuBlocked);
|
||||||
|
|
||||||
cpu->nextEvent = nextEvent;
|
cpu->nextEvent = nextEvent;
|
||||||
|
|
||||||
if (gba->earlyExit) {
|
|
||||||
gba->earlyExit = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (cpu->halted) {
|
if (cpu->halted) {
|
||||||
cpu->cycles = nextEvent;
|
cpu->cycles = nextEvent;
|
||||||
if (!gba->memory.io[REG_IME >> 1] || !gba->memory.io[REG_IE >> 1]) {
|
if (!gba->memory.io[REG_IME >> 1] || !gba->memory.io[REG_IE >> 1]) {
|
||||||
|
@ -280,7 +275,11 @@ static void GBAProcessEvents(struct ARMCore* cpu) {
|
||||||
mLOG(GBA, FATAL, "Negative cycles will pass: %i", nextEvent);
|
mLOG(GBA, FATAL, "Negative cycles will pass: %i", nextEvent);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (gba->earlyExit) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
gba->earlyExit = false;
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
if (gba->cpuBlocked) {
|
if (gba->cpuBlocked) {
|
||||||
mLOG(GBA, FATAL, "CPU is blocked!");
|
mLOG(GBA, FATAL, "CPU is blocked!");
|
||||||
|
|
|
@ -293,7 +293,7 @@ static const int _isWSpecialRegister[REG_MAX >> 1] = {
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
// Audio
|
// Audio
|
||||||
1, 1, 1, 0, 1, 0, 1, 0,
|
1, 1, 1, 0, 1, 0, 1, 0,
|
||||||
1, 1, 1, 0, 1, 0, 1, 0,
|
1, 0, 1, 0, 1, 0, 1, 0,
|
||||||
1, 0, 1, 0, 0, 0, 0, 0,
|
1, 0, 1, 0, 0, 0, 0, 0,
|
||||||
1, 1, 1, 1, 1, 1, 1, 1,
|
1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
1, 1, 1, 1, 0, 0, 0, 0,
|
1, 1, 1, 1, 0, 0, 0, 0,
|
||||||
|
|
|
@ -163,6 +163,9 @@ void GBASIOWriteSIOCNT(struct GBASIO* sio, uint16_t value) {
|
||||||
value &= ~0x0080;
|
value &= ~0x0080;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case SIO_MULTI:
|
||||||
|
value |= 0xC;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// TODO
|
// TODO
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* Copyright (c) 2013-2016 Jeffrey Pfau
|
/* Copyright (c) 2013-2018 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,6 +12,8 @@
|
||||||
#define TIMER_RELOAD_DELAY 0
|
#define TIMER_RELOAD_DELAY 0
|
||||||
#define TIMER_STARTUP_DELAY 2
|
#define TIMER_STARTUP_DELAY 2
|
||||||
|
|
||||||
|
#define REG_TMCNT_LO(X) (REG_TM0CNT_LO + ((X) << 2))
|
||||||
|
|
||||||
static void GBATimerIrq(struct GBA* gba, int timerId) {
|
static void GBATimerIrq(struct GBA* gba, int timerId) {
|
||||||
struct GBATimer* timer = &gba->timers[timerId];
|
struct GBATimer* timer = &gba->timers[timerId];
|
||||||
if (GBATimerFlagsIsIrqPending(timer->flags)) {
|
if (GBATimerFlagsIsIrqPending(timer->flags)) {
|
||||||
|
@ -45,12 +47,11 @@ static void GBATimerIrq3(struct mTiming* timing, void* context, uint32_t cyclesL
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBATimerUpdate(struct mTiming* timing, struct GBATimer* timer, uint16_t* io, uint32_t cyclesLate) {
|
void GBATimerUpdate(struct mTiming* timing, struct GBATimer* timer, uint16_t* io, uint32_t cyclesLate) {
|
||||||
*io = timer->reload;
|
if (GBATimerFlagsIsCountUp(timer->flags)) {
|
||||||
int32_t currentTime = mTimingCurrentTime(timing) - cyclesLate;
|
*io = timer->reload;
|
||||||
int32_t tickMask = (1 << GBATimerFlagsGetPrescaleBits(timer->flags)) - 1;
|
} else {
|
||||||
currentTime &= ~tickMask;
|
GBATimerUpdateRegisterInternal(timer, timing, io, TIMER_RELOAD_DELAY + cyclesLate);
|
||||||
timer->lastEvent = currentTime;
|
}
|
||||||
GBATimerUpdateRegisterInternal(timer, timing, io, TIMER_RELOAD_DELAY + cyclesLate);
|
|
||||||
|
|
||||||
if (GBATimerFlagsIsDoIrq(timer->flags)) {
|
if (GBATimerFlagsIsDoIrq(timer->flags)) {
|
||||||
timer->flags = GBATimerFlagsFillIrqPending(timer->flags);
|
timer->flags = GBATimerFlagsFillIrqPending(timer->flags);
|
||||||
|
@ -159,20 +160,30 @@ void GBATimerUpdateRegisterInternal(struct GBATimer* timer, struct mTiming* timi
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Align timer
|
||||||
int prescaleBits = GBATimerFlagsGetPrescaleBits(timer->flags);
|
int prescaleBits = GBATimerFlagsGetPrescaleBits(timer->flags);
|
||||||
int32_t currentTime = mTimingCurrentTime(timing) - skew;
|
int32_t currentTime = mTimingCurrentTime(timing) - skew;
|
||||||
int32_t tickMask = (1 << prescaleBits) - 1;
|
int32_t tickMask = (1 << prescaleBits) - 1;
|
||||||
currentTime &= ~tickMask;
|
currentTime &= ~tickMask;
|
||||||
|
|
||||||
|
// Update register
|
||||||
int32_t tickIncrement = currentTime - timer->lastEvent;
|
int32_t tickIncrement = currentTime - timer->lastEvent;
|
||||||
timer->lastEvent = currentTime;
|
timer->lastEvent = currentTime;
|
||||||
tickIncrement >>= prescaleBits;
|
tickIncrement >>= prescaleBits;
|
||||||
tickIncrement += *io;
|
tickIncrement += *io;
|
||||||
*io = tickIncrement;
|
*io = tickIncrement;
|
||||||
if (!mTimingIsScheduled(timing, &timer->event)) {
|
while (tickIncrement >= 0x10000) {
|
||||||
tickIncrement = (0x10000 - tickIncrement) << prescaleBits;
|
tickIncrement -= 0x10000 - timer->reload;
|
||||||
currentTime -= mTimingCurrentTime(timing) - skew;
|
|
||||||
mTimingSchedule(timing, &timer->event, tickIncrement + currentTime);
|
|
||||||
}
|
}
|
||||||
|
*io = tickIncrement;
|
||||||
|
|
||||||
|
// Schedule next update
|
||||||
|
tickIncrement = (0x10000 - tickIncrement) << prescaleBits;
|
||||||
|
currentTime += tickIncrement;
|
||||||
|
currentTime &= ~tickMask;
|
||||||
|
currentTime -= mTimingCurrentTime(timing);
|
||||||
|
mTimingDeschedule(timing, &timer->event);
|
||||||
|
mTimingSchedule(timing, &timer->event, currentTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBATimerWriteTMCNT_LO(struct GBATimer* timer, uint16_t reload) {
|
void GBATimerWriteTMCNT_LO(struct GBATimer* timer, uint16_t reload) {
|
||||||
|
|
|
@ -80,16 +80,18 @@ void GBAVideoInit(struct GBAVideo* video) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAVideoReset(struct GBAVideo* video) {
|
void GBAVideoReset(struct GBAVideo* video) {
|
||||||
|
int32_t nextEvent = VIDEO_HDRAW_LENGTH;
|
||||||
if (video->p->memory.fullBios) {
|
if (video->p->memory.fullBios) {
|
||||||
video->vcount = 0;
|
video->vcount = 0;
|
||||||
} else {
|
} else {
|
||||||
// TODO: Verify exact scanline hardware
|
// TODO: Verify exact scanline on hardware
|
||||||
video->vcount = 0x7E;
|
video->vcount = 0x7E;
|
||||||
|
nextEvent = 170;
|
||||||
}
|
}
|
||||||
video->p->memory.io[REG_VCOUNT >> 1] = video->vcount;
|
video->p->memory.io[REG_VCOUNT >> 1] = video->vcount;
|
||||||
|
|
||||||
video->event.callback = _startHblank;
|
video->event.callback = _startHblank;
|
||||||
mTimingSchedule(&video->p->timing, &video->event, VIDEO_HDRAW_LENGTH);
|
mTimingSchedule(&video->p->timing, &video->event, nextEvent);
|
||||||
|
|
||||||
video->frameCounter = 0;
|
video->frameCounter = 0;
|
||||||
video->frameskipCounter = 0;
|
video->frameskipCounter = 0;
|
||||||
|
@ -113,7 +115,7 @@ void GBAVideoReset(struct GBAVideo* video) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBAVideoDeinit(struct GBAVideo* video) {
|
void GBAVideoDeinit(struct GBAVideo* video) {
|
||||||
GBAVideoAssociateRenderer(video, &dummyRenderer);
|
video->renderer->deinit(video->renderer);
|
||||||
mappedMemoryFree(video->vram, SIZE_VRAM);
|
mappedMemoryFree(video->vram, SIZE_VRAM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -99,6 +99,7 @@ struct mDebuggerPlatform* LR35902DebuggerPlatformCreate(void) {
|
||||||
void LR35902DebuggerInit(void* cpu, struct mDebuggerPlatform* platform) {
|
void LR35902DebuggerInit(void* cpu, struct mDebuggerPlatform* platform) {
|
||||||
struct LR35902Debugger* debugger = (struct LR35902Debugger*) platform;
|
struct LR35902Debugger* debugger = (struct LR35902Debugger*) platform;
|
||||||
debugger->cpu = cpu;
|
debugger->cpu = cpu;
|
||||||
|
debugger->originalMemory = debugger->cpu->memory;
|
||||||
LR35902DebugBreakpointListInit(&debugger->breakpoints, 0);
|
LR35902DebugBreakpointListInit(&debugger->breakpoints, 0);
|
||||||
LR35902DebugWatchpointListInit(&debugger->watchpoints, 0);
|
LR35902DebugWatchpointListInit(&debugger->watchpoints, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
[MESSAGES CONTROL]
|
||||||
|
disable=line-too-long,missing-docstring,too-few-public-methods,too-many-instance-attributes,too-many-public-methods,wrong-import-order
|
|
@ -11,15 +11,6 @@ endforeach()
|
||||||
|
|
||||||
file(GLOB PYTHON_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/*.h)
|
file(GLOB PYTHON_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/*.h)
|
||||||
|
|
||||||
if(NOT GIT_TAG)
|
|
||||||
if(GIT_BRANCH STREQUAL "master" OR NOT GIT_BRANCH)
|
|
||||||
set(PYLIB_VERSION -b .dev${GIT_REV}+g${GIT_COMMIT_SHORT})
|
|
||||||
else()
|
|
||||||
set(PYLIB_VERSION -b .dev${GIT_REV}+${GIT_BRANCH}.g${GIT_COMMIT_SHORT})
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in ${CMAKE_CURRENT_BINARY_DIR}/setup.py)
|
|
||||||
|
|
||||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lib.c
|
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lib.c
|
||||||
COMMAND BINDIR=${CMAKE_CURRENT_BINARY_DIR}/.. CPPFLAGS="${INCLUDE_FLAGS}" ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/_builder.py
|
COMMAND BINDIR=${CMAKE_CURRENT_BINARY_DIR}/.. CPPFLAGS="${INCLUDE_FLAGS}" ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/_builder.py
|
||||||
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/lib.c
|
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/lib.c
|
||||||
|
@ -35,15 +26,29 @@ set_target_properties(${BINARY_NAME}-pylib PROPERTIES INCLUDE_DIRECTORIES "${CMA
|
||||||
set_target_properties(${BINARY_NAME}-pylib PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
|
set_target_properties(${BINARY_NAME}-pylib PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
|
||||||
|
|
||||||
add_custom_target(${BINARY_NAME}-py ALL
|
add_custom_target(${BINARY_NAME}-py ALL
|
||||||
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/setup.py egg_info -e ${CMAKE_CURRENT_BINARY_DIR} ${PYLIB_VERSION}
|
COMMAND BINDIR=${CMAKE_CURRENT_BINARY_DIR}/.. LIBDIR=${CMAKE_CURRENT_BINARY_DIR}/.. CPPFLAGS="${INCLUDE_FLAGS}" ${PYTHON_EXECUTABLE} setup.py build -b ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
COMMAND BINDIR=${CMAKE_CURRENT_BINARY_DIR}/.. CPPFLAGS="${INCLUDE_FLAGS}" ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/setup.py build -b ${CMAKE_CURRENT_BINARY_DIR}
|
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
DEPENDS ${BINARY_NAME}
|
DEPENDS ${BINARY_NAME}
|
||||||
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/setup.py
|
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/setup.py
|
||||||
DEPENDS ${PYTHON_HEADERS}
|
DEPENDS ${PYTHON_HEADERS}
|
||||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/_builder.py
|
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/_builder.py
|
||||||
DEPENDS ${BINARY_NAME}-pylib)
|
DEPENDS ${BINARY_NAME}-pylib)
|
||||||
|
|
||||||
|
add_custom_target(${BINARY_NAME}-py-install
|
||||||
|
COMMAND BINDIR=${CMAKE_CURRENT_BINARY_DIR}/.. LIBDIR=${CMAKE_CURRENT_BINARY_DIR}/.. CPPFLAGS="${INCLUDE_FLAGS}" ${PYTHON_EXECUTABLE} setup.py install -b ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
DEPENDS ${BINARY_NAME}-py)
|
||||||
|
|
||||||
|
add_custom_target(${BINARY_NAME}-py-develop
|
||||||
|
COMMAND BINDIR=${CMAKE_CURRENT_BINARY_DIR}/.. LIBDIR=${CMAKE_CURRENT_BINARY_DIR}/.. CPPFLAGS="${INCLUDE_FLAGS}" ${PYTHON_EXECUTABLE} setup.py develop -b ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
DEPENDS ${BINARY_NAME}-py)
|
||||||
|
|
||||||
|
add_custom_target(${BINARY_NAME}-py-bdist
|
||||||
|
COMMAND BINDIR=${CMAKE_CURRENT_BINARY_DIR}/.. LIBDIR=${CMAKE_CURRENT_BINARY_DIR}/.. CPPFLAGS="${INCLUDE_FLAGS}" ${PYTHON_EXECUTABLE} setup.py bdist_wheel -b ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
DEPENDS ${BINARY_NAME}-py)
|
||||||
|
|
||||||
file(GLOB BASE_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/test_*.py)
|
file(GLOB BASE_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/test_*.py)
|
||||||
file(GLOB SUBTESTS ${CMAKE_CURRENT_SOURCE_DIR}/tests/*/test_*.py)
|
file(GLOB SUBTESTS ${CMAKE_CURRENT_SOURCE_DIR}/tests/*/test_*.py)
|
||||||
foreach(TEST IN LISTS BASE_TESTS SUBTESTS)
|
foreach(TEST IN LISTS BASE_TESTS SUBTESTS)
|
||||||
|
@ -56,6 +61,8 @@ foreach(TEST IN LISTS BASE_TESTS SUBTESTS)
|
||||||
endif()
|
endif()
|
||||||
string(REGEX REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/(tests/.*/)?test_" "" TEST_NAME "${TEST}")
|
string(REGEX REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/(tests/.*/)?test_" "" TEST_NAME "${TEST}")
|
||||||
string(REPLACE ".py" "" TEST_NAME "${TEST_NAME}")
|
string(REPLACE ".py" "" TEST_NAME "${TEST_NAME}")
|
||||||
add_test(python-${TEST_NAME} ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/setup.py build -b ${CMAKE_CURRENT_BINARY_DIR} pytest --extras --addopts ${TEST})
|
add_test(NAME python-${TEST_NAME}
|
||||||
set_tests_properties(python-${TEST_NAME} PROPERTIES ENVIRONMENT "${PATH}=${CMAKE_CURRENT_BINARY_DIR}/..")
|
COMMAND ${PYTHON_EXECUTABLE} setup.py build -b ${CMAKE_CURRENT_BINARY_DIR} pytest --extras --addopts ${TEST}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
set_tests_properties(python-${TEST_NAME} PROPERTIES ENVIRONMENT "${PATH}=${CMAKE_CURRENT_BINARY_DIR}/..;BINDIR=${CMAKE_CURRENT_BINARY_DIR}/..;LIBDIR=${CMAKE_CURRENT_BINARY_DIR}/..;CPPFLAGS=${INCLUDE_FLAGS}")
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
|
@ -80,10 +80,6 @@ ffi.embedding_api('\n'.join(lines))
|
||||||
|
|
||||||
ffi.embedding_init_code("""
|
ffi.embedding_init_code("""
|
||||||
import os, os.path
|
import os, os.path
|
||||||
venv = os.getenv('VIRTUAL_ENV')
|
|
||||||
if venv:
|
|
||||||
activate = os.path.join(venv, 'bin', 'activate_this.py')
|
|
||||||
exec(compile(open(activate, "rb").read(), activate, 'exec'), dict(__file__=activate))
|
|
||||||
from mgba._pylib import ffi, lib
|
from mgba._pylib import ffi, lib
|
||||||
symbols = {}
|
symbols = {}
|
||||||
globalSyms = {
|
globalSyms = {
|
||||||
|
@ -111,7 +107,7 @@ ffi.embedding_init_code("""
|
||||||
from mgba.vfs import VFile
|
from mgba.vfs import VFile
|
||||||
vf = VFile(vf)
|
vf = VFile(vf)
|
||||||
name = ffi.string(name)
|
name = ffi.string(name)
|
||||||
source = vf.readAll().decode('utf-8')
|
source = vf.read_all().decode('utf-8')
|
||||||
try:
|
try:
|
||||||
code = compile(source, name, 'exec')
|
code = compile(source, name, 'exec')
|
||||||
pendingCode.append(code)
|
pendingCode.append(code)
|
||||||
|
|
|
@ -2,15 +2,16 @@ from PIL.ImageChops import difference
|
||||||
from PIL.ImageOps import autocontrast
|
from PIL.ImageOps import autocontrast
|
||||||
from PIL.Image import open as PIOpen
|
from PIL.Image import open as PIOpen
|
||||||
|
|
||||||
|
|
||||||
class VideoFrame(object):
|
class VideoFrame(object):
|
||||||
def __init__(self, pilImage):
|
def __init__(self, pil_image):
|
||||||
self.image = pilImage.convert('RGB')
|
self.image = pil_image.convert('RGB')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def diff(a, b):
|
def diff(a, b):
|
||||||
diff = difference(a.image, b.image)
|
diff = difference(a.image, b.image)
|
||||||
diffNormalized = autocontrast(diff)
|
diff_normalized = autocontrast(diff)
|
||||||
return (VideoFrame(diff), VideoFrame(diffNormalized))
|
return (VideoFrame(diff), VideoFrame(diff_normalized))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load(path):
|
def load(path):
|
||||||
|
|
|
@ -4,44 +4,38 @@ from . import VideoFrame
|
||||||
|
|
||||||
Output = namedtuple('Output', ['video'])
|
Output = namedtuple('Output', ['video'])
|
||||||
|
|
||||||
|
|
||||||
class Tracer(object):
|
class Tracer(object):
|
||||||
def __init__(self, core):
|
def __init__(self, core):
|
||||||
self.core = core
|
self.core = core
|
||||||
self.fb = Image(*core.desiredVideoDimensions())
|
self.framebuffer = Image(*core.desired_video_dimensions())
|
||||||
self.core.setVideoBuffer(self.fb)
|
self.core.set_video_buffer(self.framebuffer)
|
||||||
self._videoFifo = []
|
self._video_fifo = []
|
||||||
|
|
||||||
def yieldFrames(self, skip=0, limit=None):
|
def yield_frames(self, skip=0, limit=None):
|
||||||
self.core.reset()
|
self.core.reset()
|
||||||
skip = (skip or 0) + 1
|
skip = (skip or 0) + 1
|
||||||
while skip > 0:
|
while skip > 0:
|
||||||
frame = self.core.frameCounter
|
frame = self.core.frame_counter
|
||||||
self.core.runFrame()
|
self.core.run_frame()
|
||||||
skip -= 1
|
skip -= 1
|
||||||
while frame <= self.core.frameCounter and limit != 0:
|
while frame <= self.core.frame_counter and limit != 0:
|
||||||
self._videoFifo.append(VideoFrame(self.fb.toPIL()))
|
self._video_fifo.append(VideoFrame(self.framebuffer.to_pil()))
|
||||||
yield frame
|
yield frame
|
||||||
frame = self.core.frameCounter
|
frame = self.core.frame_counter
|
||||||
self.core.runFrame()
|
self.core.run_frame()
|
||||||
if limit is not None:
|
if limit is not None:
|
||||||
assert limit >= 0
|
assert limit >= 0
|
||||||
limit -= 1
|
limit -= 1
|
||||||
|
|
||||||
def video(self, generator=None, **kwargs):
|
def video(self, generator=None, **kwargs):
|
||||||
if not generator:
|
if not generator:
|
||||||
generator = self.yieldFrames(**kwargs)
|
generator = self.yield_frames(**kwargs)
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
if self._videoFifo:
|
if self._video_fifo:
|
||||||
result = self._videoFifo[0]
|
yield self._video_fifo.pop(0)
|
||||||
self._videoFifo = self._videoFifo[1:]
|
|
||||||
yield result
|
|
||||||
else:
|
else:
|
||||||
next(generator)
|
next(generator)
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
return
|
return
|
||||||
|
|
||||||
def output(self, **kwargs):
|
|
||||||
generator = self.yieldFrames(**kwargs)
|
|
||||||
|
|
||||||
return mCoreOutput(video=self.video(generator=generator, **kwargs))
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import os, os.path
|
import os
|
||||||
import mgba.core, mgba.image
|
import os.path
|
||||||
|
import mgba.core
|
||||||
|
import mgba.image
|
||||||
import cinema.movie
|
import cinema.movie
|
||||||
import itertools
|
import itertools
|
||||||
import glob
|
import glob
|
||||||
|
@ -7,20 +9,21 @@ import re
|
||||||
import yaml
|
import yaml
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from cinema import VideoFrame
|
from cinema import VideoFrame
|
||||||
from cinema.util import dictMerge
|
from cinema.util import dict_merge
|
||||||
|
|
||||||
|
|
||||||
class CinemaTest(object):
|
class CinemaTest(object):
|
||||||
TEST = 'test.(mvl|gb|gba|nds)'
|
TEST = 'test.(mvl|gb|gba|nds)'
|
||||||
|
|
||||||
def __init__(self, path, root, settings={}):
|
def __init__(self, path, root, settings={}):
|
||||||
self.fullPath = path or []
|
self.full_path = path or []
|
||||||
self.path = os.path.abspath(os.path.join(root, *self.fullPath))
|
self.path = os.path.abspath(os.path.join(root, *self.full_path))
|
||||||
self.root = root
|
self.root = root
|
||||||
self.name = '.'.join(path)
|
self.name = '.'.join(path)
|
||||||
self.settings = settings
|
self.settings = settings
|
||||||
try:
|
try:
|
||||||
with open(os.path.join(self.path, 'manifest.yml'), 'r') as f:
|
with open(os.path.join(self.path, 'manifest.yml'), 'r') as f:
|
||||||
dictMerge(self.settings, yaml.safe_load(f))
|
dict_merge(self.settings, yaml.safe_load(f))
|
||||||
except IOError:
|
except IOError:
|
||||||
pass
|
pass
|
||||||
self.tests = {}
|
self.tests = {}
|
||||||
|
@ -28,41 +31,42 @@ class CinemaTest(object):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<%s %s>' % (self.__class__.__name__, self.name)
|
return '<%s %s>' % (self.__class__.__name__, self.name)
|
||||||
|
|
||||||
def setUp(self):
|
def setup(self):
|
||||||
results = [f for f in glob.glob(os.path.join(self.path, 'test.*')) if re.search(self.TEST, f)]
|
results = [f for f in glob.glob(os.path.join(self.path, 'test.*')) if re.search(self.TEST, f)]
|
||||||
self.core = mgba.core.loadPath(results[0])
|
self.core = mgba.core.load_path(results[0])
|
||||||
if 'config' in self.settings:
|
if 'config' in self.settings:
|
||||||
self.config = mgba.core.Config(defaults=self.settings['config'])
|
self.config = mgba.core.Config(defaults=self.settings['config'])
|
||||||
self.core.loadConfig(self.config)
|
self.core.load_config(self.config)
|
||||||
self.core.reset()
|
self.core.reset()
|
||||||
|
|
||||||
def addTest(self, name, cls=None, settings={}):
|
def add_test(self, name, cls=None, settings={}):
|
||||||
cls = cls or self.__class__
|
cls = cls or self.__class__
|
||||||
newSettings = deepcopy(self.settings)
|
new_settings = deepcopy(self.settings)
|
||||||
dictMerge(newSettings, settings)
|
dict_merge(new_settings, settings)
|
||||||
self.tests[name] = cls(self.fullPath + [name], self.root, newSettings)
|
self.tests[name] = cls(self.full_path + [name], self.root, new_settings)
|
||||||
return self.tests[name]
|
return self.tests[name]
|
||||||
|
|
||||||
def outputSettings(self):
|
def output_settings(self):
|
||||||
outputSettings = {}
|
output_settings = {}
|
||||||
if 'frames' in self.settings:
|
if 'frames' in self.settings:
|
||||||
outputSettings['limit'] = self.settings['frames']
|
output_settings['limit'] = self.settings['frames']
|
||||||
if 'skip' in self.settings:
|
if 'skip' in self.settings:
|
||||||
outputSettings['skip'] = self.settings['skip']
|
output_settings['skip'] = self.settings['skip']
|
||||||
return outputSettings
|
return output_settings
|
||||||
|
|
||||||
def __lt__(self, other):
|
def __lt__(self, other):
|
||||||
return self.path < other.path
|
return self.path < other.path
|
||||||
|
|
||||||
|
|
||||||
class VideoTest(CinemaTest):
|
class VideoTest(CinemaTest):
|
||||||
BASELINE = 'baseline_%04u.png'
|
BASELINE = 'baseline_%04u.png'
|
||||||
|
|
||||||
def setUp(self):
|
def setup(self):
|
||||||
super(VideoTest, self).setUp();
|
super(VideoTest, self).setup()
|
||||||
self.tracer = cinema.movie.Tracer(self.core)
|
self.tracer = cinema.movie.Tracer(self.core)
|
||||||
|
|
||||||
def generateFrames(self):
|
def generate_frames(self):
|
||||||
for i, frame in zip(itertools.count(), self.tracer.video(**self.outputSettings())):
|
for i, frame in zip(itertools.count(), self.tracer.video(**self.output_settings())):
|
||||||
try:
|
try:
|
||||||
baseline = VideoFrame.load(os.path.join(self.path, self.BASELINE % i))
|
baseline = VideoFrame.load(os.path.join(self.path, self.BASELINE % i))
|
||||||
yield baseline, frame, VideoFrame.diff(baseline, frame)
|
yield baseline, frame, VideoFrame.diff(baseline, frame)
|
||||||
|
@ -70,14 +74,15 @@ class VideoTest(CinemaTest):
|
||||||
yield None, frame, (None, None)
|
yield None, frame, (None, None)
|
||||||
|
|
||||||
def test(self):
|
def test(self):
|
||||||
self.baseline, self.frames, self.diffs = zip(*self.generateFrames())
|
self.baseline, self.frames, self.diffs = zip(*self.generate_frames())
|
||||||
assert not any(any(diffs[0].image.convert("L").point(bool).getdata()) for diffs in self.diffs)
|
assert not any(any(diffs[0].image.convert("L").point(bool).getdata()) for diffs in self.diffs)
|
||||||
|
|
||||||
def generateBaseline(self):
|
def generate_baseline(self):
|
||||||
for i, frame in zip(itertools.count(), self.tracer.video(**self.outputSettings())):
|
for i, frame in zip(itertools.count(), self.tracer.video(**self.output_settings())):
|
||||||
frame.save(os.path.join(self.path, self.BASELINE % i))
|
frame.save(os.path.join(self.path, self.BASELINE % i))
|
||||||
|
|
||||||
def gatherTests(root=os.getcwd()):
|
|
||||||
|
def gather_tests(root=os.getcwd()):
|
||||||
tests = CinemaTest([], root)
|
tests = CinemaTest([], root)
|
||||||
for path, _, files in os.walk(root):
|
for path, _, files in os.walk(root):
|
||||||
test = [f for f in files if re.match(CinemaTest.TEST, f)]
|
test = [f for f in files if re.match(CinemaTest.TEST, f)]
|
||||||
|
@ -85,12 +90,12 @@ def gatherTests(root=os.getcwd()):
|
||||||
continue
|
continue
|
||||||
prefix = os.path.commonprefix([path, root])
|
prefix = os.path.commonprefix([path, root])
|
||||||
suffix = path[len(prefix)+1:]
|
suffix = path[len(prefix)+1:]
|
||||||
testPath = suffix.split(os.sep)
|
test_path = suffix.split(os.sep)
|
||||||
testRoot = tests
|
test_root = tests
|
||||||
for component in testPath[:-1]:
|
for component in test_path[:-1]:
|
||||||
newTest = testRoot.tests.get(component)
|
new_test = test_root.tests.get(component)
|
||||||
if not newTest:
|
if not new_test:
|
||||||
newTest = testRoot.addTest(component)
|
new_test = test_root.add_test(component)
|
||||||
testRoot = newTest
|
test_root = new_test
|
||||||
testRoot.addTest(testPath[-1], VideoTest)
|
test_root.add_test(test_path[-1], VideoTest)
|
||||||
return tests
|
return tests
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
def dictMerge(a, b):
|
def dict_merge(a, b):
|
||||||
for key, value in b.items():
|
for key, value in b.items():
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
if key in a:
|
if key in a:
|
||||||
dictMerge(a[key], value)
|
dict_merge(a[key], value)
|
||||||
else:
|
else:
|
||||||
a[key] = dict(value)
|
a[key] = dict(value)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -3,31 +3,34 @@
|
||||||
# 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/.
|
||||||
from ._pylib import ffi, lib
|
from ._pylib import ffi, lib # pylint: disable=no-name-in-module
|
||||||
|
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
def createCallback(structName, cbName, funcName=None):
|
|
||||||
funcName = funcName or "_py{}{}".format(structName, cbName[0].upper() + cbName[1:])
|
|
||||||
fullStruct = "struct {}*".format(structName)
|
|
||||||
def cb(handle, *args):
|
|
||||||
h = ffi.cast(fullStruct, handle)
|
|
||||||
return getattr(ffi.from_handle(h.pyobj), cbName)(*args)
|
|
||||||
|
|
||||||
return ffi.def_extern(name=funcName)(cb)
|
def create_callback(struct_name, cb_name, func_name=None):
|
||||||
|
func_name = func_name or "_py{}{}".format(struct_name, cb_name[0].upper() + cb_name[1:])
|
||||||
|
full_struct = "struct {}*".format(struct_name)
|
||||||
|
|
||||||
version = ffi.string(lib.projectVersion).decode('utf-8')
|
def callback(handle, *args):
|
||||||
|
handle = ffi.cast(full_struct, handle)
|
||||||
|
return getattr(ffi.from_handle(handle.pyobj), cb_name)(*args)
|
||||||
|
|
||||||
|
return ffi.def_extern(name=func_name)(callback)
|
||||||
|
|
||||||
|
|
||||||
|
__version__ = ffi.string(lib.projectVersion).decode('utf-8')
|
||||||
|
|
||||||
GitInfo = namedtuple("GitInfo", "commit commitShort branch revision")
|
GitInfo = namedtuple("GitInfo", "commit commitShort branch revision")
|
||||||
|
|
||||||
git = {}
|
GIT = {}
|
||||||
if lib.gitCommit and lib.gitCommit != "(unknown)":
|
if lib.gitCommit and lib.gitCommit != "(unknown)":
|
||||||
git['commit'] = ffi.string(lib.gitCommit).decode('utf-8')
|
GIT['commit'] = ffi.string(lib.gitCommit).decode('utf-8')
|
||||||
if lib.gitCommitShort and lib.gitCommitShort != "(unknown)":
|
if lib.gitCommitShort and lib.gitCommitShort != "(unknown)":
|
||||||
git['commitShort'] = ffi.string(lib.gitCommitShort).decode('utf-8')
|
GIT['commitShort'] = ffi.string(lib.gitCommitShort).decode('utf-8')
|
||||||
if lib.gitBranch and lib.gitBranch != "(unknown)":
|
if lib.gitBranch and lib.gitBranch != "(unknown)":
|
||||||
git['branch'] = ffi.string(lib.gitBranch).decode('utf-8')
|
GIT['branch'] = ffi.string(lib.gitBranch).decode('utf-8')
|
||||||
if lib.gitRevision > 0:
|
if lib.gitRevision > 0:
|
||||||
git['revision'] = lib.gitRevision
|
GIT['revision'] = lib.gitRevision
|
||||||
|
|
||||||
git = GitInfo(**git)
|
GIT = GitInfo(**GIT)
|
||||||
|
|
|
@ -3,21 +3,23 @@
|
||||||
# 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/.
|
||||||
from ._pylib import ffi, lib
|
from ._pylib import ffi, lib # pylint: disable=no-name-in-module
|
||||||
|
|
||||||
|
|
||||||
class _ARMRegisters:
|
class _ARMRegisters:
|
||||||
def __init__(self, cpu):
|
def __init__(self, cpu):
|
||||||
self._cpu = cpu
|
self._cpu = cpu
|
||||||
|
|
||||||
def __getitem__(self, r):
|
def __getitem__(self, reg):
|
||||||
if r > lib.ARM_PC:
|
if reg > lib.ARM_PC:
|
||||||
raise IndexError("Register out of range")
|
raise IndexError("Register out of range")
|
||||||
return self._cpu._native.gprs[r]
|
return self._cpu._native.gprs[reg]
|
||||||
|
|
||||||
def __setitem__(self, r, value):
|
def __setitem__(self, reg, value):
|
||||||
if r >= lib.ARM_PC:
|
if reg >= lib.ARM_PC:
|
||||||
raise IndexError("Register out of range")
|
raise IndexError("Register out of range")
|
||||||
self._cpu._native.gprs[r] = value
|
self._cpu._native.gprs[reg] = value
|
||||||
|
|
||||||
|
|
||||||
class ARMCore:
|
class ARMCore:
|
||||||
def __init__(self, native):
|
def __init__(self, native):
|
||||||
|
@ -25,4 +27,3 @@ class ARMCore:
|
||||||
self.gprs = _ARMRegisters(self)
|
self.gprs = _ARMRegisters(self)
|
||||||
self.cpsr = self._native.cpsr
|
self.cpsr = self._native.cpsr
|
||||||
self.spsr = self._native.spsr
|
self.spsr = self._native.spsr
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,11 @@
|
||||||
# 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/.
|
||||||
from ._pylib import ffi, lib
|
from ._pylib import ffi, lib # pylint: disable=no-name-in-module
|
||||||
from . import tile, createCallback
|
from . import tile
|
||||||
from cached_property import cached_property
|
from cached_property import cached_property
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
|
||||||
def find(path):
|
def find(path):
|
||||||
core = lib.mCoreFind(path.encode('UTF-8'))
|
core = lib.mCoreFind(path.encode('UTF-8'))
|
||||||
|
@ -13,82 +15,95 @@ def find(path):
|
||||||
return None
|
return None
|
||||||
return Core._init(core)
|
return Core._init(core)
|
||||||
|
|
||||||
def findVF(vf):
|
|
||||||
core = lib.mCoreFindVF(vf.handle)
|
def find_vf(vfile):
|
||||||
|
core = lib.mCoreFindVF(vfile.handle)
|
||||||
if core == ffi.NULL:
|
if core == ffi.NULL:
|
||||||
return None
|
return None
|
||||||
return Core._init(core)
|
return Core._init(core)
|
||||||
|
|
||||||
def loadPath(path):
|
|
||||||
|
def load_path(path):
|
||||||
core = find(path)
|
core = find(path)
|
||||||
if not core or not core.loadFile(path):
|
if not core or not core.load_file(path):
|
||||||
return None
|
return None
|
||||||
return core
|
return core
|
||||||
|
|
||||||
def loadVF(vf):
|
|
||||||
core = findVF(vf)
|
def load_vf(vfile):
|
||||||
if not core or not core.loadROM(vf):
|
core = find_vf(vfile)
|
||||||
|
if not core or not core.load_rom(vfile):
|
||||||
return None
|
return None
|
||||||
return core
|
return core
|
||||||
|
|
||||||
def needsReset(f):
|
|
||||||
|
def needs_reset(func):
|
||||||
|
@wraps(func)
|
||||||
def wrapper(self, *args, **kwargs):
|
def wrapper(self, *args, **kwargs):
|
||||||
if not self._wasReset:
|
if not self._was_reset:
|
||||||
raise RuntimeError("Core must be reset first")
|
raise RuntimeError("Core must be reset first")
|
||||||
return f(self, *args, **kwargs)
|
return func(self, *args, **kwargs)
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
def protected(f):
|
|
||||||
|
def protected(func):
|
||||||
|
@wraps(func)
|
||||||
def wrapper(self, *args, **kwargs):
|
def wrapper(self, *args, **kwargs):
|
||||||
if self._protected:
|
if self._protected:
|
||||||
raise RuntimeError("Core is protected")
|
raise RuntimeError("Core is protected")
|
||||||
return f(self, *args, **kwargs)
|
return func(self, *args, **kwargs)
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
@ffi.def_extern()
|
|
||||||
def _mCorePythonCallbacksVideoFrameStarted(user):
|
|
||||||
context = ffi.from_handle(user)
|
|
||||||
context._videoFrameStarted()
|
|
||||||
|
|
||||||
@ffi.def_extern()
|
@ffi.def_extern()
|
||||||
def _mCorePythonCallbacksVideoFrameEnded(user):
|
def _mCorePythonCallbacksVideoFrameStarted(user): # pylint: disable=invalid-name
|
||||||
context = ffi.from_handle(user)
|
context = ffi.from_handle(user)
|
||||||
context._videoFrameEnded()
|
context._video_frame_started()
|
||||||
|
|
||||||
|
|
||||||
@ffi.def_extern()
|
@ffi.def_extern()
|
||||||
def _mCorePythonCallbacksCoreCrashed(user):
|
def _mCorePythonCallbacksVideoFrameEnded(user): # pylint: disable=invalid-name
|
||||||
context = ffi.from_handle(user)
|
context = ffi.from_handle(user)
|
||||||
context._coreCrashed()
|
context._video_frame_ended()
|
||||||
|
|
||||||
|
|
||||||
@ffi.def_extern()
|
@ffi.def_extern()
|
||||||
def _mCorePythonCallbacksSleep(user):
|
def _mCorePythonCallbacksCoreCrashed(user): # pylint: disable=invalid-name
|
||||||
|
context = ffi.from_handle(user)
|
||||||
|
context._core_crashed()
|
||||||
|
|
||||||
|
|
||||||
|
@ffi.def_extern()
|
||||||
|
def _mCorePythonCallbacksSleep(user): # pylint: disable=invalid-name
|
||||||
context = ffi.from_handle(user)
|
context = ffi.from_handle(user)
|
||||||
context._sleep()
|
context._sleep()
|
||||||
|
|
||||||
|
|
||||||
class CoreCallbacks(object):
|
class CoreCallbacks(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._handle = ffi.new_handle(self)
|
self._handle = ffi.new_handle(self)
|
||||||
self.videoFrameStarted = []
|
self.video_frame_started = []
|
||||||
self.videoFrameEnded = []
|
self.video_frame_ended = []
|
||||||
self.coreCrashed = []
|
self.core_crashed = []
|
||||||
self.sleep = []
|
self.sleep = []
|
||||||
self.context = lib.mCorePythonCallbackCreate(self._handle)
|
self.context = lib.mCorePythonCallbackCreate(self._handle)
|
||||||
|
|
||||||
def _videoFrameStarted(self):
|
def _video_frame_started(self):
|
||||||
for cb in self.videoFrameStarted:
|
for callback in self.video_frame_started:
|
||||||
cb()
|
callback()
|
||||||
|
|
||||||
def _videoFrameEnded(self):
|
def _video_frame_ended(self):
|
||||||
for cb in self.videoFrameEnded:
|
for callback in self.video_frame_ended:
|
||||||
cb()
|
callback()
|
||||||
|
|
||||||
def _coreCrashed(self):
|
def _core_crashed(self):
|
||||||
for cb in self.coreCrashed:
|
for callback in self.core_crashed:
|
||||||
cb()
|
callback()
|
||||||
|
|
||||||
def _sleep(self):
|
def _sleep(self):
|
||||||
for cb in self.sleep:
|
for callback in self.sleep:
|
||||||
cb()
|
callback()
|
||||||
|
|
||||||
|
|
||||||
class Core(object):
|
class Core(object):
|
||||||
if hasattr(lib, 'PLATFORM_GBA'):
|
if hasattr(lib, 'PLATFORM_GBA'):
|
||||||
|
@ -102,36 +117,36 @@ class Core(object):
|
||||||
|
|
||||||
def __init__(self, native):
|
def __init__(self, native):
|
||||||
self._core = native
|
self._core = native
|
||||||
self._wasReset = False
|
self._was_reset = False
|
||||||
self._protected = False
|
self._protected = False
|
||||||
self._callbacks = CoreCallbacks()
|
self._callbacks = CoreCallbacks()
|
||||||
self._core.addCoreCallbacks(self._core, self._callbacks.context)
|
self._core.addCoreCallbacks(self._core, self._callbacks.context)
|
||||||
self.config = Config(ffi.addressof(native.config))
|
self.config = Config(ffi.addressof(native.config))
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
self._wasReset = False
|
self._was_reset = False
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def graphicsCache(self):
|
def graphics_cache(self):
|
||||||
if not self._wasReset:
|
if not self._was_reset:
|
||||||
raise RuntimeError("Core must be reset first")
|
raise RuntimeError("Core must be reset first")
|
||||||
return tile.CacheSet(self)
|
return tile.CacheSet(self)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def tiles(self):
|
def tiles(self):
|
||||||
t = []
|
tiles = []
|
||||||
ts = ffi.addressof(self.graphicsCache.cache.tiles)
|
native_tiles = ffi.addressof(self.graphics_cache.cache.tiles)
|
||||||
for i in range(lib.mTileCacheSetSize(ts)):
|
for i in range(lib.mTileCacheSetSize(native_tiles)):
|
||||||
t.append(tile.TileView(lib.mTileCacheSetGetPointer(ts, i)))
|
tiles.append(tile.TileView(lib.mTileCacheSetGetPointer(native_tiles, i)))
|
||||||
return t
|
return tiles
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def maps(self):
|
def maps(self):
|
||||||
m = []
|
maps = []
|
||||||
ms = ffi.addressof(self.graphicsCache.cache.maps)
|
native_maps = ffi.addressof(self.graphics_cache.cache.maps)
|
||||||
for i in range(lib.mMapCacheSetSize(ms)):
|
for i in range(lib.mMapCacheSetSize(native_maps)):
|
||||||
m.append(tile.MapView(lib.mMapCacheSetGetPointer(ms, i)))
|
maps.append(tile.MapView(lib.mMapCacheSetGetPointer(native_maps, i)))
|
||||||
return m
|
return maps
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _init(cls, native):
|
def _init(cls, native):
|
||||||
|
@ -156,73 +171,73 @@ class Core(object):
|
||||||
return Core(core)
|
return Core(core)
|
||||||
|
|
||||||
def _load(self):
|
def _load(self):
|
||||||
self._wasReset = True
|
self._was_reset = True
|
||||||
|
|
||||||
def loadFile(self, path):
|
def load_file(self, path):
|
||||||
return bool(lib.mCoreLoadFile(self._core, path.encode('UTF-8')))
|
return bool(lib.mCoreLoadFile(self._core, path.encode('UTF-8')))
|
||||||
|
|
||||||
def isROM(self, vf):
|
def is_rom(self, vfile):
|
||||||
return bool(self._core.isROM(vf.handle))
|
return bool(self._core.isROM(vfile.handle))
|
||||||
|
|
||||||
def loadROM(self, vf):
|
def load_rom(self, vfile):
|
||||||
return bool(self._core.loadROM(self._core, vf.handle))
|
return bool(self._core.loadROM(self._core, vfile.handle))
|
||||||
|
|
||||||
def loadBIOS(self, vf, id=0):
|
def load_bios(self, vfile, id=0):
|
||||||
return bool(self._core.loadBIOS(self._core, vf.handle, id))
|
return bool(self._core.loadBIOS(self._core, vfile.handle, id))
|
||||||
|
|
||||||
def loadSave(self, vf):
|
def load_save(self, vfile):
|
||||||
return bool(self._core.loadSave(self._core, vf.handle))
|
return bool(self._core.loadSave(self._core, vfile.handle))
|
||||||
|
|
||||||
def loadTemporarySave(self, vf):
|
def load_temporary_save(self, vfile):
|
||||||
return bool(self._core.loadTemporarySave(self._core, vf.handle))
|
return bool(self._core.loadTemporarySave(self._core, vfile.handle))
|
||||||
|
|
||||||
def loadPatch(self, vf):
|
def load_patch(self, vfile):
|
||||||
return bool(self._core.loadPatch(self._core, vf.handle))
|
return bool(self._core.loadPatch(self._core, vfile.handle))
|
||||||
|
|
||||||
def loadConfig(self, config):
|
def load_config(self, config):
|
||||||
lib.mCoreLoadForeignConfig(self._core, config._native)
|
lib.mCoreLoadForeignConfig(self._core, config._native)
|
||||||
|
|
||||||
def autoloadSave(self):
|
def autoload_save(self):
|
||||||
return bool(lib.mCoreAutoloadSave(self._core))
|
return bool(lib.mCoreAutoloadSave(self._core))
|
||||||
|
|
||||||
def autoloadPatch(self):
|
def autoload_patch(self):
|
||||||
return bool(lib.mCoreAutoloadPatch(self._core))
|
return bool(lib.mCoreAutoloadPatch(self._core))
|
||||||
|
|
||||||
def autoloadCheats(self):
|
def autoload_cheats(self):
|
||||||
return bool(lib.mCoreAutoloadCheats(self._core))
|
return bool(lib.mCoreAutoloadCheats(self._core))
|
||||||
|
|
||||||
def platform(self):
|
def platform(self):
|
||||||
return self._core.platform(self._core)
|
return self._core.platform(self._core)
|
||||||
|
|
||||||
def desiredVideoDimensions(self):
|
def desired_video_dimensions(self):
|
||||||
width = ffi.new("unsigned*")
|
width = ffi.new("unsigned*")
|
||||||
height = ffi.new("unsigned*")
|
height = ffi.new("unsigned*")
|
||||||
self._core.desiredVideoDimensions(self._core, width, height)
|
self._core.desiredVideoDimensions(self._core, width, height)
|
||||||
return width[0], height[0]
|
return width[0], height[0]
|
||||||
|
|
||||||
def setVideoBuffer(self, image):
|
def set_video_buffer(self, image):
|
||||||
self._core.setVideoBuffer(self._core, image.buffer, image.stride)
|
self._core.setVideoBuffer(self._core, image.buffer, image.stride)
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
self._core.reset(self._core)
|
self._core.reset(self._core)
|
||||||
self._load()
|
self._load()
|
||||||
|
|
||||||
@needsReset
|
@needs_reset
|
||||||
@protected
|
@protected
|
||||||
def runFrame(self):
|
def run_frame(self):
|
||||||
self._core.runFrame(self._core)
|
self._core.runFrame(self._core)
|
||||||
|
|
||||||
@needsReset
|
@needs_reset
|
||||||
@protected
|
@protected
|
||||||
def runLoop(self):
|
def run_loop(self):
|
||||||
self._core.runLoop(self._core)
|
self._core.runLoop(self._core)
|
||||||
|
|
||||||
@needsReset
|
@needs_reset
|
||||||
def step(self):
|
def step(self):
|
||||||
self._core.step(self._core)
|
self._core.step(self._core)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _keysToInt(*args, **kwargs):
|
def _keys_to_int(*args, **kwargs):
|
||||||
keys = 0
|
keys = 0
|
||||||
if 'raw' in kwargs:
|
if 'raw' in kwargs:
|
||||||
keys = kwargs['raw']
|
keys = kwargs['raw']
|
||||||
|
@ -230,22 +245,25 @@ class Core(object):
|
||||||
keys |= 1 << key
|
keys |= 1 << key
|
||||||
return keys
|
return keys
|
||||||
|
|
||||||
def setKeys(self, *args, **kwargs):
|
@protected
|
||||||
self._core.setKeys(self._core, self._keysToInt(*args, **kwargs))
|
def set_keys(self, *args, **kwargs):
|
||||||
|
self._core.setKeys(self._core, self._keys_to_int(*args, **kwargs))
|
||||||
|
|
||||||
def addKeys(self, *args, **kwargs):
|
@protected
|
||||||
self._core.addKeys(self._core, self._keysToInt(*args, **kwargs))
|
def add_keys(self, *args, **kwargs):
|
||||||
|
self._core.addKeys(self._core, self._keys_to_int(*args, **kwargs))
|
||||||
|
|
||||||
def clearKeys(self, *args, **kwargs):
|
@protected
|
||||||
self._core.clearKeys(self._core, self._keysToInt(*args, **kwargs))
|
def clear_keys(self, *args, **kwargs):
|
||||||
|
self._core.clearKeys(self._core, self._keys_to_int(*args, **kwargs))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@needsReset
|
@needs_reset
|
||||||
def frameCounter(self):
|
def frame_counter(self):
|
||||||
return self._core.frameCounter(self._core)
|
return self._core.frameCounter(self._core)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def frameCycles(self):
|
def frame_cycles(self):
|
||||||
return self._core.frameCycles(self._core)
|
return self._core.frameCycles(self._core)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -253,24 +271,25 @@ class Core(object):
|
||||||
return self._core.frequency(self._core)
|
return self._core.frequency(self._core)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def gameTitle(self):
|
def game_title(self):
|
||||||
title = ffi.new("char[16]")
|
title = ffi.new("char[16]")
|
||||||
self._core.getGameTitle(self._core, title)
|
self._core.getGameTitle(self._core, title)
|
||||||
return ffi.string(title, 16).decode("ascii")
|
return ffi.string(title, 16).decode("ascii")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def gameCode(self):
|
def game_code(self):
|
||||||
code = ffi.new("char[12]")
|
code = ffi.new("char[12]")
|
||||||
self._core.getGameCode(self._core, code)
|
self._core.getGameCode(self._core, code)
|
||||||
return ffi.string(code, 12).decode("ascii")
|
return ffi.string(code, 12).decode("ascii")
|
||||||
|
|
||||||
def addFrameCallback(self, cb):
|
def add_frame_callback(self, callback):
|
||||||
self._callbacks.videoFrameEnded.append(cb)
|
self._callbacks.video_frame_ended.append(callback)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def crc32(self):
|
def crc32(self):
|
||||||
return self._native.romCrc32
|
return self._native.romCrc32
|
||||||
|
|
||||||
|
|
||||||
class ICoreOwner(object):
|
class ICoreOwner(object):
|
||||||
def claim(self):
|
def claim(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
@ -287,6 +306,7 @@ class ICoreOwner(object):
|
||||||
self.core._protected = False
|
self.core._protected = False
|
||||||
self.release()
|
self.release()
|
||||||
|
|
||||||
|
|
||||||
class IRunner(object):
|
class IRunner(object):
|
||||||
def pause(self):
|
def pause(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
@ -294,15 +314,18 @@ class IRunner(object):
|
||||||
def unpause(self):
|
def unpause(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def useCore(self):
|
def use_core(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def isRunning(self):
|
@property
|
||||||
|
def running(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def isPaused(self):
|
@property
|
||||||
|
def paused(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class Config(object):
|
class Config(object):
|
||||||
def __init__(self, native=None, port=None, defaults={}):
|
def __init__(self, native=None, port=None, defaults={}):
|
||||||
if not native:
|
if not native:
|
||||||
|
|
|
@ -3,26 +3,27 @@
|
||||||
# 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/.
|
||||||
from ._pylib import ffi, lib
|
from ._pylib import ffi, lib # pylint: disable=no-name-in-module
|
||||||
from .core import IRunner, ICoreOwner, Core
|
from .core import IRunner, ICoreOwner, Core
|
||||||
import io
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
class DebuggerCoreOwner(ICoreOwner):
|
class DebuggerCoreOwner(ICoreOwner):
|
||||||
def __init__(self, debugger):
|
def __init__(self, debugger):
|
||||||
self.debugger = debugger
|
self.debugger = debugger
|
||||||
self.wasPaused = False
|
self.was_paused = False
|
||||||
|
|
||||||
def claim(self):
|
def claim(self):
|
||||||
if self.debugger.isRunning():
|
if self.debugger.isRunning():
|
||||||
self.wasPaused = True
|
self.was_paused = True
|
||||||
self.debugger.pause()
|
self.debugger.pause()
|
||||||
return self.debugger._core
|
return self.debugger._core
|
||||||
|
|
||||||
def release(self):
|
def release(self):
|
||||||
if self.wasPaused:
|
if self.was_paused:
|
||||||
self.debugger.unpause()
|
self.debugger.unpause()
|
||||||
|
|
||||||
|
|
||||||
class NativeDebugger(IRunner):
|
class NativeDebugger(IRunner):
|
||||||
WATCHPOINT_WRITE = lib.WATCHPOINT_WRITE
|
WATCHPOINT_WRITE = lib.WATCHPOINT_WRITE
|
||||||
WATCHPOINT_READ = lib.WATCHPOINT_READ
|
WATCHPOINT_READ = lib.WATCHPOINT_READ
|
||||||
|
@ -49,37 +50,40 @@ class NativeDebugger(IRunner):
|
||||||
def unpause(self):
|
def unpause(self):
|
||||||
self._native.state = lib.DEBUGGER_RUNNING
|
self._native.state = lib.DEBUGGER_RUNNING
|
||||||
|
|
||||||
def isRunning(self):
|
@property
|
||||||
|
def running(self):
|
||||||
return self._native.state == lib.DEBUGGER_RUNNING
|
return self._native.state == lib.DEBUGGER_RUNNING
|
||||||
|
|
||||||
def isPaused(self):
|
@property
|
||||||
|
def paused(self):
|
||||||
return self._native.state in (lib.DEBUGGER_PAUSED, lib.DEBUGGER_CUSTOM)
|
return self._native.state in (lib.DEBUGGER_PAUSED, lib.DEBUGGER_CUSTOM)
|
||||||
|
|
||||||
def useCore(self):
|
def use_core(self):
|
||||||
return DebuggerCoreOwner(self)
|
return DebuggerCoreOwner(self)
|
||||||
|
|
||||||
def setBreakpoint(self, address):
|
def set_breakpoint(self, address):
|
||||||
if not self._native.platform.setBreakpoint:
|
if not self._native.platform.setBreakpoint:
|
||||||
raise RuntimeError("Platform does not support breakpoints")
|
raise RuntimeError("Platform does not support breakpoints")
|
||||||
self._native.platform.setBreakpoint(self._native.platform, address)
|
self._native.platform.setBreakpoint(self._native.platform, address)
|
||||||
|
|
||||||
def clearBreakpoint(self, address):
|
def clear_breakpoint(self, address):
|
||||||
if not self._native.platform.setBreakpoint:
|
if not self._native.platform.setBreakpoint:
|
||||||
raise RuntimeError("Platform does not support breakpoints")
|
raise RuntimeError("Platform does not support breakpoints")
|
||||||
self._native.platform.clearBreakpoint(self._native.platform, address)
|
self._native.platform.clearBreakpoint(self._native.platform, address)
|
||||||
|
|
||||||
def setWatchpoint(self, address):
|
def set_watchpoint(self, address):
|
||||||
if not self._native.platform.setWatchpoint:
|
if not self._native.platform.setWatchpoint:
|
||||||
raise RuntimeError("Platform does not support watchpoints")
|
raise RuntimeError("Platform does not support watchpoints")
|
||||||
self._native.platform.setWatchpoint(self._native.platform, address)
|
self._native.platform.setWatchpoint(self._native.platform, address)
|
||||||
|
|
||||||
def clearWatchpoint(self, address):
|
def clear_watchpoint(self, address):
|
||||||
if not self._native.platform.clearWatchpoint:
|
if not self._native.platform.clearWatchpoint:
|
||||||
raise RuntimeError("Platform does not support watchpoints")
|
raise RuntimeError("Platform does not support watchpoints")
|
||||||
self._native.platform.clearWatchpoint(self._native.platform, address)
|
self._native.platform.clearWatchpoint(self._native.platform, address)
|
||||||
|
|
||||||
def addCallback(self, cb):
|
def add_callback(self, callback):
|
||||||
self._cbs.append(cb)
|
self._cbs.append(callback)
|
||||||
|
|
||||||
|
|
||||||
class CLIBackend(object):
|
class CLIBackend(object):
|
||||||
def __init__(self, backend):
|
def __init__(self, backend):
|
||||||
|
@ -88,6 +92,7 @@ class CLIBackend(object):
|
||||||
def write(self, string):
|
def write(self, string):
|
||||||
self.backend.printf(string)
|
self.backend.printf(string)
|
||||||
|
|
||||||
|
|
||||||
class CLIDebugger(NativeDebugger):
|
class CLIDebugger(NativeDebugger):
|
||||||
def __init__(self, native):
|
def __init__(self, native):
|
||||||
super(CLIDebugger, self).__init__(native)
|
super(CLIDebugger, self).__init__(native)
|
||||||
|
@ -97,5 +102,5 @@ class CLIDebugger(NativeDebugger):
|
||||||
message = message.format(*args, **kwargs)
|
message = message.format(*args, **kwargs)
|
||||||
self._cli.backend.printf(ffi.new("char []", b"%s"), ffi.new("char []", message.encode('utf-8')))
|
self._cli.backend.printf(ffi.new("char []", b"%s"), ffi.new("char []", message.encode('utf-8')))
|
||||||
|
|
||||||
def installPrint(self):
|
def install_print(self):
|
||||||
sys.stdout = CLIBackend(self)
|
sys.stdout = CLIBackend(self)
|
||||||
|
|
|
@ -8,6 +8,7 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def search(core):
|
def search(core):
|
||||||
crc32 = None
|
crc32 = None
|
||||||
if hasattr(core, 'PLATFORM_GBA') and core.platform() == core.PLATFORM_GBA:
|
if hasattr(core, 'PLATFORM_GBA') and core.platform() == core.PLATFORM_GBA:
|
||||||
|
|
|
@ -3,12 +3,13 @@
|
||||||
# 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/.
|
||||||
from ._pylib import ffi, lib
|
from ._pylib import ffi, lib # pylint: disable=no-name-in-module
|
||||||
from .lr35902 import LR35902Core
|
from .lr35902 import LR35902Core
|
||||||
from .core import Core, needsReset
|
from .core import Core, needs_reset
|
||||||
from .memory import Memory
|
from .memory import Memory
|
||||||
from .tile import Sprite
|
from .tile import Sprite
|
||||||
from . import createCallback
|
from . import create_callback
|
||||||
|
|
||||||
|
|
||||||
class GB(Core):
|
class GB(Core):
|
||||||
KEY_A = lib.GBA_KEY_A
|
KEY_A = lib.GBA_KEY_A
|
||||||
|
@ -25,31 +26,34 @@ class GB(Core):
|
||||||
self._native = ffi.cast("struct GB*", native.board)
|
self._native = ffi.cast("struct GB*", native.board)
|
||||||
self.sprites = GBObjs(self)
|
self.sprites = GBObjs(self)
|
||||||
self.cpu = LR35902Core(self._core.cpu)
|
self.cpu = LR35902Core(self._core.cpu)
|
||||||
|
self.memory = None
|
||||||
|
|
||||||
@needsReset
|
@needs_reset
|
||||||
def _initCache(self, cache):
|
def _init_cache(self, cache):
|
||||||
lib.GBVideoCacheInit(cache)
|
lib.GBVideoCacheInit(cache)
|
||||||
lib.GBVideoCacheAssociate(cache, ffi.addressof(self._native.video))
|
lib.GBVideoCacheAssociate(cache, ffi.addressof(self._native.video))
|
||||||
|
|
||||||
def _deinitCache(self, cache):
|
def _deinit_cache(self, cache):
|
||||||
lib.mCacheSetDeinit(cache)
|
lib.mCacheSetDeinit(cache)
|
||||||
if self._wasReset:
|
if self._was_reset:
|
||||||
self._native.video.renderer.cache = ffi.NULL
|
self._native.video.renderer.cache = ffi.NULL
|
||||||
|
|
||||||
def _load(self):
|
def _load(self):
|
||||||
super(GB, self)._load()
|
super(GB, self)._load()
|
||||||
self.memory = GBMemory(self._core)
|
self.memory = GBMemory(self._core)
|
||||||
|
|
||||||
def attachSIO(self, link):
|
def attach_sio(self, link):
|
||||||
lib.GBSIOSetDriver(ffi.addressof(self._native.sio), link._native)
|
lib.GBSIOSetDriver(ffi.addressof(self._native.sio), link._native)
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
lib.GBSIOSetDriver(ffi.addressof(self._native.sio), ffi.NULL)
|
lib.GBSIOSetDriver(ffi.addressof(self._native.sio), ffi.NULL)
|
||||||
|
|
||||||
createCallback("GBSIOPythonDriver", "init")
|
|
||||||
createCallback("GBSIOPythonDriver", "deinit")
|
create_callback("GBSIOPythonDriver", "init")
|
||||||
createCallback("GBSIOPythonDriver", "writeSB")
|
create_callback("GBSIOPythonDriver", "deinit")
|
||||||
createCallback("GBSIOPythonDriver", "writeSC")
|
create_callback("GBSIOPythonDriver", "writeSB")
|
||||||
|
create_callback("GBSIOPythonDriver", "writeSC")
|
||||||
|
|
||||||
|
|
||||||
class GBSIODriver(object):
|
class GBSIODriver(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -62,53 +66,55 @@ class GBSIODriver(object):
|
||||||
def deinit(self):
|
def deinit(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def writeSB(self, value):
|
def write_sb(self, value):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def writeSC(self, value):
|
def write_sc(self, value):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
class GBSIOSimpleDriver(GBSIODriver):
|
class GBSIOSimpleDriver(GBSIODriver):
|
||||||
def __init__(self, period=0x100):
|
def __init__(self, period=0x100):
|
||||||
super(GBSIOSimpleDriver, self).__init__()
|
super(GBSIOSimpleDriver, self).__init__()
|
||||||
self.rx = 0x00
|
self.rx = 0x00 # pylint: disable=invalid-name
|
||||||
self._period = period
|
self._period = period
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
self._native.p.period = self._period
|
self._native.p.period = self._period
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def writeSB(self, value):
|
def write_sb(self, value):
|
||||||
self.rx = value
|
self.rx = value # pylint: disable=invalid-name
|
||||||
|
|
||||||
def writeSC(self, value):
|
def write_sc(self, value):
|
||||||
self._native.p.period = self._period
|
self._native.p.period = self._period
|
||||||
if value & 0x80:
|
if value & 0x80:
|
||||||
lib.mTimingDeschedule(ffi.addressof(self._native.p.p.timing), ffi.addressof(self._native.p.event))
|
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)
|
lib.mTimingSchedule(ffi.addressof(self._native.p.p.timing), ffi.addressof(self._native.p.event), self._native.p.period)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def isReady(self):
|
def is_ready(self):
|
||||||
return not self._native.p.remainingBits
|
return not self._native.p.remainingBits
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tx(self):
|
def tx(self): # pylint: disable=invalid-name
|
||||||
self._native.p.pendingSB
|
return self._native.p.pendingSB
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def period(self):
|
def period(self):
|
||||||
return self._native.p.period
|
return self._native.p.period
|
||||||
|
|
||||||
@tx.setter
|
@tx.setter
|
||||||
def tx(self, newTx):
|
def tx(self, newTx): # pylint: disable=invalid-name
|
||||||
self._native.p.pendingSB = newTx
|
self._native.p.pendingSB = newTx
|
||||||
self._native.p.remainingBits = 8
|
self._native.p.remainingBits = 8
|
||||||
|
|
||||||
@period.setter
|
@period.setter
|
||||||
def period(self, newPeriod):
|
def period(self, new_period):
|
||||||
self._period = newPeriod
|
self._period = new_period
|
||||||
if self._native.p:
|
if self._native.p:
|
||||||
self._native.p.period = newPeriod
|
self._native.p.period = new_period
|
||||||
|
|
||||||
|
|
||||||
class GBMemory(Memory):
|
class GBMemory(Memory):
|
||||||
def __init__(self, core):
|
def __init__(self, core):
|
||||||
|
@ -119,15 +125,16 @@ class GBMemory(Memory):
|
||||||
self.sram = Memory(core, lib.GB_SIZE_EXTERNAL_RAM, lib.GB_REGION_EXTERNAL_RAM)
|
self.sram = Memory(core, lib.GB_SIZE_EXTERNAL_RAM, lib.GB_REGION_EXTERNAL_RAM)
|
||||||
self.iwram = Memory(core, lib.GB_SIZE_WORKING_RAM_BANK0, lib.GB_BASE_WORKING_RAM_BANK0)
|
self.iwram = Memory(core, lib.GB_SIZE_WORKING_RAM_BANK0, lib.GB_BASE_WORKING_RAM_BANK0)
|
||||||
self.oam = Memory(core, lib.GB_SIZE_OAM, lib.GB_BASE_OAM)
|
self.oam = Memory(core, lib.GB_SIZE_OAM, lib.GB_BASE_OAM)
|
||||||
self.io = Memory(core, lib.GB_SIZE_IO, lib.GB_BASE_IO)
|
self.io = Memory(core, lib.GB_SIZE_IO, lib.GB_BASE_IO) # pylint: disable=invalid-name
|
||||||
self.hram = Memory(core, lib.GB_SIZE_HRAM, lib.GB_BASE_HRAM)
|
self.hram = Memory(core, lib.GB_SIZE_HRAM, lib.GB_BASE_HRAM)
|
||||||
|
|
||||||
|
|
||||||
class GBSprite(Sprite):
|
class GBSprite(Sprite):
|
||||||
PALETTE_BASE = 8,
|
PALETTE_BASE = (8,)
|
||||||
|
|
||||||
def __init__(self, obj, core):
|
def __init__(self, obj, core):
|
||||||
self.x = obj.x
|
self.x = obj.x # pylint: disable=invalid-name
|
||||||
self.y = obj.y
|
self.y = obj.y # pylint: disable=invalid-name
|
||||||
self.tile = obj.tile
|
self.tile = obj.tile
|
||||||
self._attr = obj.attr
|
self._attr = obj.attr
|
||||||
self.width = 8
|
self.width = 8
|
||||||
|
@ -136,10 +143,10 @@ class GBSprite(Sprite):
|
||||||
if core._native.model >= lib.GB_MODEL_CGB:
|
if core._native.model >= lib.GB_MODEL_CGB:
|
||||||
if self._attr & 8:
|
if self._attr & 8:
|
||||||
self.tile += 512
|
self.tile += 512
|
||||||
self.paletteId = self._attr & 7
|
self.palette_id = self._attr & 7
|
||||||
else:
|
else:
|
||||||
self.paletteId = (self._attr >> 4) & 1
|
self.palette_id = (self._attr >> 4) & 1
|
||||||
self.paletteId += 8
|
self.palette_id += 8
|
||||||
|
|
||||||
|
|
||||||
class GBObjs:
|
class GBObjs:
|
||||||
|
|
|
@ -3,12 +3,13 @@
|
||||||
# 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/.
|
||||||
from ._pylib import ffi, lib
|
from ._pylib import ffi, lib # pylint: disable=no-name-in-module
|
||||||
from .arm import ARMCore
|
from .arm import ARMCore
|
||||||
from .core import Core, needsReset
|
from .core import Core, needs_reset
|
||||||
from .tile import Sprite
|
from .tile import Sprite
|
||||||
from .memory import Memory
|
from .memory import Memory
|
||||||
from . import createCallback
|
from . import create_callback
|
||||||
|
|
||||||
|
|
||||||
class GBA(Core):
|
class GBA(Core):
|
||||||
KEY_A = lib.GBA_KEY_A
|
KEY_A = lib.GBA_KEY_A
|
||||||
|
@ -34,23 +35,24 @@ class GBA(Core):
|
||||||
self._native = ffi.cast("struct GBA*", native.board)
|
self._native = ffi.cast("struct GBA*", native.board)
|
||||||
self.sprites = GBAObjs(self)
|
self.sprites = GBAObjs(self)
|
||||||
self.cpu = ARMCore(self._core.cpu)
|
self.cpu = ARMCore(self._core.cpu)
|
||||||
|
self.memory = None
|
||||||
self._sio = set()
|
self._sio = set()
|
||||||
|
|
||||||
@needsReset
|
@needs_reset
|
||||||
def _initCache(self, cache):
|
def _init_cache(self, cache):
|
||||||
lib.GBAVideoCacheInit(cache)
|
lib.GBAVideoCacheInit(cache)
|
||||||
lib.GBAVideoCacheAssociate(cache, ffi.addressof(self._native.video))
|
lib.GBAVideoCacheAssociate(cache, ffi.addressof(self._native.video))
|
||||||
|
|
||||||
def _deinitCache(self, cache):
|
def _deinit_cache(self, cache):
|
||||||
lib.mCacheSetDeinit(cache)
|
lib.mCacheSetDeinit(cache)
|
||||||
if self._wasReset:
|
if self._was_reset:
|
||||||
self._native.video.renderer.cache = ffi.NULL
|
self._native.video.renderer.cache = ffi.NULL
|
||||||
|
|
||||||
def _load(self):
|
def _load(self):
|
||||||
super(GBA, self)._load()
|
super(GBA, self)._load()
|
||||||
self.memory = GBAMemory(self._core, self._native.memory.romSize)
|
self.memory = GBAMemory(self._core, self._native.memory.romSize)
|
||||||
|
|
||||||
def attachSIO(self, link, mode=lib.SIO_MULTI):
|
def attach_sio(self, link, mode=lib.SIO_MULTI):
|
||||||
self._sio.add(mode)
|
self._sio.add(mode)
|
||||||
lib.GBASIOSetDriver(ffi.addressof(self._native.sio), link._native, mode)
|
lib.GBASIOSetDriver(ffi.addressof(self._native.sio), link._native, mode)
|
||||||
|
|
||||||
|
@ -58,14 +60,18 @@ class GBA(Core):
|
||||||
for mode in self._sio:
|
for mode in self._sio:
|
||||||
lib.GBASIOSetDriver(ffi.addressof(self._native.sio), ffi.NULL, mode)
|
lib.GBASIOSetDriver(ffi.addressof(self._native.sio), ffi.NULL, mode)
|
||||||
|
|
||||||
createCallback("GBASIOPythonDriver", "init")
|
|
||||||
createCallback("GBASIOPythonDriver", "deinit")
|
create_callback("GBASIOPythonDriver", "init")
|
||||||
createCallback("GBASIOPythonDriver", "load")
|
create_callback("GBASIOPythonDriver", "deinit")
|
||||||
createCallback("GBASIOPythonDriver", "unload")
|
create_callback("GBASIOPythonDriver", "load")
|
||||||
createCallback("GBASIOPythonDriver", "writeRegister")
|
create_callback("GBASIOPythonDriver", "unload")
|
||||||
|
create_callback("GBASIOPythonDriver", "writeRegister")
|
||||||
|
|
||||||
|
|
||||||
class GBASIODriver(object):
|
class GBASIODriver(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
super(GBASIODriver, self).__init__()
|
||||||
|
|
||||||
self._handle = ffi.new_handle(self)
|
self._handle = ffi.new_handle(self)
|
||||||
self._native = ffi.gc(lib.GBASIOPythonDriverCreate(self._handle), lib.free)
|
self._native = ffi.gc(lib.GBASIOPythonDriverCreate(self._handle), lib.free)
|
||||||
|
|
||||||
|
@ -81,9 +87,10 @@ class GBASIODriver(object):
|
||||||
def unload(self):
|
def unload(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def writeRegister(self, address, value):
|
def write_register(self, address, value):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
class GBASIOJOYDriver(GBASIODriver):
|
class GBASIOJOYDriver(GBASIODriver):
|
||||||
RESET = lib.JOY_RESET
|
RESET = lib.JOY_RESET
|
||||||
POLL = lib.JOY_POLL
|
POLL = lib.JOY_POLL
|
||||||
|
@ -91,10 +98,11 @@ class GBASIOJOYDriver(GBASIODriver):
|
||||||
RECV = lib.JOY_RECV
|
RECV = lib.JOY_RECV
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._handle = ffi.new_handle(self)
|
super(GBASIOJOYDriver, self).__init__()
|
||||||
|
|
||||||
self._native = ffi.gc(lib.GBASIOJOYPythonDriverCreate(self._handle), lib.free)
|
self._native = ffi.gc(lib.GBASIOJOYPythonDriverCreate(self._handle), lib.free)
|
||||||
|
|
||||||
def sendCommand(self, cmd, data):
|
def send_command(self, cmd, data):
|
||||||
buffer = ffi.new('uint8_t[5]')
|
buffer = ffi.new('uint8_t[5]')
|
||||||
try:
|
try:
|
||||||
buffer[0] = data[0]
|
buffer[0] = data[0]
|
||||||
|
@ -110,6 +118,7 @@ class GBASIOJOYDriver(GBASIODriver):
|
||||||
return bytes(buffer[0:outlen])
|
return bytes(buffer[0:outlen])
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class GBAMemory(Memory):
|
class GBAMemory(Memory):
|
||||||
def __init__(self, core, romSize=lib.SIZE_CART0):
|
def __init__(self, core, romSize=lib.SIZE_CART0):
|
||||||
super(GBAMemory, self).__init__(core, 0x100000000)
|
super(GBAMemory, self).__init__(core, 0x100000000)
|
||||||
|
@ -117,7 +126,7 @@ class GBAMemory(Memory):
|
||||||
self.bios = Memory(core, lib.SIZE_BIOS, lib.BASE_BIOS)
|
self.bios = Memory(core, lib.SIZE_BIOS, lib.BASE_BIOS)
|
||||||
self.wram = Memory(core, lib.SIZE_WORKING_RAM, lib.BASE_WORKING_RAM)
|
self.wram = Memory(core, lib.SIZE_WORKING_RAM, lib.BASE_WORKING_RAM)
|
||||||
self.iwram = Memory(core, lib.SIZE_WORKING_IRAM, lib.BASE_WORKING_IRAM)
|
self.iwram = Memory(core, lib.SIZE_WORKING_IRAM, lib.BASE_WORKING_IRAM)
|
||||||
self.io = Memory(core, lib.SIZE_IO, lib.BASE_IO)
|
self.io = Memory(core, lib.SIZE_IO, lib.BASE_IO) # pylint: disable=invalid-name
|
||||||
self.palette = Memory(core, lib.SIZE_PALETTE_RAM, lib.BASE_PALETTE_RAM)
|
self.palette = Memory(core, lib.SIZE_PALETTE_RAM, lib.BASE_PALETTE_RAM)
|
||||||
self.vram = Memory(core, lib.SIZE_VRAM, lib.BASE_VRAM)
|
self.vram = Memory(core, lib.SIZE_VRAM, lib.BASE_VRAM)
|
||||||
self.oam = Memory(core, lib.SIZE_OAM, lib.BASE_OAM)
|
self.oam = Memory(core, lib.SIZE_OAM, lib.BASE_OAM)
|
||||||
|
@ -128,6 +137,7 @@ class GBAMemory(Memory):
|
||||||
self.rom = self.cart0
|
self.rom = self.cart0
|
||||||
self.sram = Memory(core, lib.SIZE_CART_SRAM, lib.BASE_CART_SRAM)
|
self.sram = Memory(core, lib.SIZE_CART_SRAM, lib.BASE_CART_SRAM)
|
||||||
|
|
||||||
|
|
||||||
class GBASprite(Sprite):
|
class GBASprite(Sprite):
|
||||||
TILE_BASE = 0x800, 0x400
|
TILE_BASE = 0x800, 0x400
|
||||||
PALETTE_BASE = 0x10, 1
|
PALETTE_BASE = 0x10, 1
|
||||||
|
@ -136,18 +146,19 @@ class GBASprite(Sprite):
|
||||||
self._a = obj.a
|
self._a = obj.a
|
||||||
self._b = obj.b
|
self._b = obj.b
|
||||||
self._c = obj.c
|
self._c = obj.c
|
||||||
self.x = self._b & 0x1FF
|
self.x = self._b & 0x1FF # pylint: disable=invalid-name
|
||||||
self.y = self._a & 0xFF
|
self.y = self._a & 0xFF # pylint: disable=invalid-name
|
||||||
self._shape = self._a >> 14
|
self._shape = self._a >> 14
|
||||||
self._size = self._b >> 14
|
self._size = self._b >> 14
|
||||||
self._256Color = bool(self._a & 0x2000)
|
self._256_color = bool(self._a & 0x2000)
|
||||||
self.width, self.height = lib.GBAVideoObjSizes[self._shape * 4 + self._size]
|
self.width, self.height = lib.GBAVideoObjSizes[self._shape * 4 + self._size]
|
||||||
self.tile = self._c & 0x3FF
|
self.tile = self._c & 0x3FF
|
||||||
if self._256Color:
|
if self._256_color:
|
||||||
self.paletteId = 0
|
self.palette_id = 0
|
||||||
self.tile >>= 1
|
self.tile >>= 1
|
||||||
else:
|
else:
|
||||||
self.paletteId = self._c >> 12
|
self.palette_id = self._c >> 12
|
||||||
|
|
||||||
|
|
||||||
class GBAObjs:
|
class GBAObjs:
|
||||||
def __init__(self, core):
|
def __init__(self, core):
|
||||||
|
@ -161,7 +172,7 @@ class GBAObjs:
|
||||||
if index >= len(self):
|
if index >= len(self):
|
||||||
raise IndexError()
|
raise IndexError()
|
||||||
sprite = GBASprite(self._obj[index])
|
sprite = GBASprite(self._obj[index])
|
||||||
tiles = self._core.tiles[3 if sprite._256Color else 2]
|
tiles = self._core.tiles[3 if sprite._256_color else 2]
|
||||||
map1D = bool(self._core._native.memory.io[0] & 0x40)
|
map_1d = bool(self._core._native.memory.io[0] & 0x40)
|
||||||
sprite.constitute(tiles, 0 if map1D else 0x20)
|
sprite.constitute(tiles, 0 if map_1d else 0x20)
|
||||||
return sprite
|
return sprite
|
||||||
|
|
|
@ -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/.
|
||||||
from ._pylib import ffi, lib
|
from ._pylib import ffi # pylint: disable=no-name-in-module
|
||||||
from . import png
|
from . import png
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -11,6 +11,7 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Image:
|
class Image:
|
||||||
def __init__(self, width, height, stride=0, alpha=False):
|
def __init__(self, width, height, stride=0, alpha=False):
|
||||||
self.width = width
|
self.width = width
|
||||||
|
@ -24,58 +25,63 @@ class Image:
|
||||||
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 save_png(self, fileobj):
|
||||||
p = png.PNG(f, mode=png.MODE_RGBA if self.alpha else png.MODE_RGB)
|
png_file = png.PNG(fileobj, mode=png.MODE_RGBA if self.alpha else png.MODE_RGB)
|
||||||
success = p.writeHeader(self)
|
success = png_file.write_header(self)
|
||||||
success = success and p.writePixels(self)
|
success = success and png_file.write_pixels(self)
|
||||||
p.writeClose()
|
png_file.write_close()
|
||||||
return success
|
return success
|
||||||
|
|
||||||
if 'PImage' in globals():
|
if 'PImage' in globals():
|
||||||
def toPIL(self):
|
def to_pil(self):
|
||||||
type = "RGBA" if self.alpha else "RGBX"
|
colorspace = "RGBA" if self.alpha else "RGBX"
|
||||||
return PImage.frombytes(type, (self.width, self.height), ffi.buffer(self.buffer), "raw",
|
return PImage.frombytes(colorspace, (self.width, self.height), ffi.buffer(self.buffer), "raw",
|
||||||
type, self.stride * 4)
|
colorspace, self.stride * 4)
|
||||||
|
|
||||||
def u16ToU32(c):
|
|
||||||
r = c & 0x1F
|
def u16_to_u32(color):
|
||||||
g = (c >> 5) & 0x1F
|
# pylint: disable=invalid-name
|
||||||
b = (c >> 10) & 0x1F
|
r = color & 0x1F
|
||||||
a = (c >> 15) & 1
|
g = (color >> 5) & 0x1F
|
||||||
|
b = (color >> 10) & 0x1F
|
||||||
|
a = (color >> 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):
|
|
||||||
r = (c >> 3) & 0x1F
|
def u32_to_u16(color):
|
||||||
g = (c >> 11) & 0x1F
|
# pylint: disable=invalid-name
|
||||||
b = (c >> 19) & 0x1F
|
r = (color >> 3) & 0x1F
|
||||||
a = c >> 31
|
g = (color >> 11) & 0x1F
|
||||||
|
b = (color >> 19) & 0x1F
|
||||||
|
a = color >> 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 color_to_u16(color):
|
||||||
return c
|
return color
|
||||||
|
|
||||||
colorToU32 = u16ToU32
|
color_to_u32 = u16_to_u32 # pylint: disable=invalid-name
|
||||||
|
|
||||||
def u16ToColor(c):
|
def u16_to_color(color):
|
||||||
return c
|
return color
|
||||||
|
|
||||||
u32ToColor = u32ToU16
|
u32_to_color = u32_to_u16 # pylint: disable=invalid-name
|
||||||
else:
|
else:
|
||||||
def colorToU32(c):
|
def color_to_u32(color):
|
||||||
return c
|
return color
|
||||||
|
|
||||||
colorToU16 = u32ToU16
|
color_to_u16 = u32_to_u16 # pylint: disable=invalid-name
|
||||||
|
|
||||||
def u32ToColor(c):
|
def u32_to_color(color):
|
||||||
return c
|
return color
|
||||||
|
|
||||||
u16ToColor = u16ToU32
|
u16_to_color = u16_to_u32 # pylint: disable=invalid-name
|
||||||
|
|
|
@ -3,17 +3,15 @@
|
||||||
# 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/.
|
||||||
from ._pylib import ffi, lib
|
from ._pylib import ffi, lib # pylint: disable=no-name-in-module
|
||||||
from . import createCallback
|
from . import create_callback
|
||||||
|
|
||||||
createCallback("mLoggerPy", "log", "_pyLog")
|
create_callback("mLoggerPy", "log", "_pyLog")
|
||||||
|
|
||||||
defaultLogger = None
|
|
||||||
|
|
||||||
def installDefault(logger):
|
def install_default(logger):
|
||||||
global defaultLogger
|
Logger.install_default(logger)
|
||||||
defaultLogger = logger
|
|
||||||
lib.mLogSetDefaultLogger(logger._native)
|
|
||||||
|
|
||||||
class Logger(object):
|
class Logger(object):
|
||||||
FATAL = lib.mLOG_FATAL
|
FATAL = lib.mLOG_FATAL
|
||||||
|
@ -24,16 +22,24 @@ class Logger(object):
|
||||||
STUB = lib.mLOG_STUB
|
STUB = lib.mLOG_STUB
|
||||||
GAME_ERROR = lib.mLOG_GAME_ERROR
|
GAME_ERROR = lib.mLOG_GAME_ERROR
|
||||||
|
|
||||||
|
_DEFAULT_LOGGER = None
|
||||||
|
|
||||||
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 category_name(category):
|
||||||
return ffi.string(lib.mLogCategoryName(category)).decode('UTF-8')
|
return ffi.string(lib.mLogCategoryName(category)).decode('UTF-8')
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def install_default(cls, logger):
|
||||||
|
cls._DEFAULT_LOGGER = logger
|
||||||
|
lib.mLogSetDefaultLogger(logger._native)
|
||||||
|
|
||||||
def log(self, category, level, message):
|
def log(self, category, level, message):
|
||||||
print("{}: {}".format(self.categoryName(category), message))
|
print("{}: {}".format(self.category_name(category), message))
|
||||||
|
|
||||||
|
|
||||||
class NullLogger(Logger):
|
class NullLogger(Logger):
|
||||||
def log(self, category, level, message):
|
def log(self, category, level, message):
|
||||||
|
|
|
@ -3,9 +3,11 @@
|
||||||
# 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/.
|
||||||
from ._pylib import ffi, lib
|
from ._pylib import ffi # pylint: disable=no-name-in-module
|
||||||
|
|
||||||
|
|
||||||
class LR35902Core:
|
class LR35902Core:
|
||||||
|
# pylint: disable=invalid-name
|
||||||
def __init__(self, native):
|
def __init__(self, native):
|
||||||
self._native = ffi.cast("struct LR35902Core*", native)
|
self._native = ffi.cast("struct LR35902Core*", native)
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
# 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/.
|
||||||
from ._pylib import ffi, lib
|
from ._pylib import ffi, lib # pylint: disable=no-name-in-module
|
||||||
|
|
||||||
|
|
||||||
class MemoryView(object):
|
class MemoryView(object):
|
||||||
def __init__(self, core, width, size, base=0, sign="u"):
|
def __init__(self, core, width, size, base=0, sign="u"):
|
||||||
|
@ -11,11 +12,11 @@ class MemoryView(object):
|
||||||
self._width = width
|
self._width = width
|
||||||
self._size = size
|
self._size = size
|
||||||
self._base = base
|
self._base = base
|
||||||
self._busRead = getattr(self._core, "busRead" + str(width * 8))
|
self._bus_read = getattr(self._core, "busRead" + str(width * 8))
|
||||||
self._busWrite = getattr(self._core, "busWrite" + str(width * 8))
|
self._bus_write = getattr(self._core, "busWrite" + str(width * 8))
|
||||||
self._rawRead = getattr(self._core, "rawRead" + str(width * 8))
|
self._raw_read = getattr(self._core, "rawRead" + str(width * 8))
|
||||||
self._rawWrite = getattr(self._core, "rawWrite" + str(width * 8))
|
self._raw_write = getattr(self._core, "rawWrite" + str(width * 8))
|
||||||
self._mask = (1 << (width * 8)) - 1 # Used to force values to fit within range so that negative values work
|
self._mask = (1 << (width * 8)) - 1 # Used to force values to fit within range so that negative values work
|
||||||
if sign == "u" or sign == "unsigned":
|
if sign == "u" or sign == "unsigned":
|
||||||
self._type = "uint{}_t".format(width * 8)
|
self._type = "uint{}_t".format(width * 8)
|
||||||
elif sign == "i" or sign == "s" or sign == "signed":
|
elif sign == "i" or sign == "s" or sign == "signed":
|
||||||
|
@ -23,7 +24,7 @@ class MemoryView(object):
|
||||||
else:
|
else:
|
||||||
raise ValueError("Invalid sign type: '{}'".format(sign))
|
raise ValueError("Invalid sign type: '{}'".format(sign))
|
||||||
|
|
||||||
def _addrCheck(self, address):
|
def _addr_check(self, address):
|
||||||
if isinstance(address, slice):
|
if isinstance(address, slice):
|
||||||
start = address.start or 0
|
start = address.start or 0
|
||||||
stop = self._size - self._width if address.stop is None else address.stop
|
stop = self._size - self._width if address.stop is None else address.stop
|
||||||
|
@ -39,33 +40,32 @@ class MemoryView(object):
|
||||||
return self._size
|
return self._size
|
||||||
|
|
||||||
def __getitem__(self, address):
|
def __getitem__(self, address):
|
||||||
self._addrCheck(address)
|
self._addr_check(address)
|
||||||
if isinstance(address, slice):
|
if isinstance(address, slice):
|
||||||
start = address.start or 0
|
start = address.start or 0
|
||||||
stop = self._size - self._width if address.stop is None else address.stop
|
stop = self._size - self._width if address.stop is None else address.stop
|
||||||
step = address.step or self._width
|
step = address.step or self._width
|
||||||
return [int(ffi.cast(self._type, self._busRead(self._core, self._base + a))) for a in range(start, stop, step)]
|
return [int(ffi.cast(self._type, self._bus_read(self._core, self._base + a))) for a in range(start, stop, step)]
|
||||||
else:
|
return int(ffi.cast(self._type, self._bus_read(self._core, self._base + address)))
|
||||||
return int(ffi.cast(self._type, self._busRead(self._core, self._base + address)))
|
|
||||||
|
|
||||||
def __setitem__(self, address, value):
|
def __setitem__(self, address, value):
|
||||||
self._addrCheck(address)
|
self._addr_check(address)
|
||||||
if isinstance(address, slice):
|
if isinstance(address, slice):
|
||||||
start = address.start or 0
|
start = address.start or 0
|
||||||
stop = self._size - self._width if address.stop is None else address.stop
|
stop = self._size - self._width if address.stop is None else address.stop
|
||||||
step = address.step or self._width
|
step = address.step or self._width
|
||||||
for a in range(start, stop, step):
|
for addr in range(start, stop, step):
|
||||||
self._busWrite(self._core, self._base + a, value[a] & self._mask)
|
self._bus_write(self._core, self._base + addr, value[addr] & self._mask)
|
||||||
else:
|
else:
|
||||||
self._busWrite(self._core, self._base + address, value & self._mask)
|
self._bus_write(self._core, self._base + address, value & self._mask)
|
||||||
|
|
||||||
def rawRead(self, address, segment=-1):
|
def raw_read(self, address, segment=-1):
|
||||||
self._addrCheck(address)
|
self._addr_check(address)
|
||||||
return int(ffi.cast(self._type, self._rawRead(self._core, self._base + address, segment)))
|
return int(ffi.cast(self._type, self._raw_read(self._core, self._base + address, segment)))
|
||||||
|
|
||||||
def rawWrite(self, address, value, segment=-1):
|
def raw_write(self, address, value, segment=-1):
|
||||||
self._addrCheck(address)
|
self._addr_check(address)
|
||||||
self._rawWrite(self._core, self._base + address, segment, value & self._mask)
|
self._raw_write(self._core, self._base + address, segment, value & self._mask)
|
||||||
|
|
||||||
|
|
||||||
class MemorySearchResult(object):
|
class MemorySearchResult(object):
|
||||||
|
@ -75,12 +75,13 @@ class MemorySearchResult(object):
|
||||||
self.guessDivisor = result.guessDivisor
|
self.guessDivisor = result.guessDivisor
|
||||||
self.type = result.type
|
self.type = result.type
|
||||||
|
|
||||||
if result.type == Memory.SEARCH_8:
|
if result.type == Memory.SEARCH_INT:
|
||||||
self._memory = memory.u8
|
if result.width == 1:
|
||||||
elif result.type == Memory.SEARCH_16:
|
self._memory = memory.u8
|
||||||
self._memory = memory.u16
|
elif result.width == 2:
|
||||||
elif result.type == Memory.SEARCH_32:
|
self._memory = memory.u16
|
||||||
self._memory = memory.u32
|
elif result.width == 4:
|
||||||
|
self._memory = memory.u32
|
||||||
elif result.type == Memory.SEARCH_STRING:
|
elif result.type == Memory.SEARCH_STRING:
|
||||||
self._memory = memory.u8
|
self._memory = memory.u8
|
||||||
else:
|
else:
|
||||||
|
@ -123,7 +124,7 @@ class Memory(object):
|
||||||
self.s32 = MemoryView(core, 4, size, base, "s")
|
self.s32 = MemoryView(core, 4, size, base, "s")
|
||||||
|
|
||||||
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=[]):
|
def search(self, value, type=SEARCH_GUESS, flags=RW, limit=10000, old_results=[]):
|
||||||
results = ffi.new("struct mCoreMemorySearchResults*")
|
results = ffi.new("struct mCoreMemorySearchResults*")
|
||||||
|
@ -138,11 +139,11 @@ class Memory(object):
|
||||||
params.valueStr = ffi.new("char[]", str(value).encode("ascii"))
|
params.valueStr = ffi.new("char[]", str(value).encode("ascii"))
|
||||||
|
|
||||||
for result in old_results:
|
for result in old_results:
|
||||||
r = lib.mCoreMemorySearchResultsAppend(results)
|
native_result = lib.mCoreMemorySearchResultsAppend(results)
|
||||||
r.address = result.address
|
native_result.address = result.address
|
||||||
r.segment = result.segment
|
native_result.segment = result.segment
|
||||||
r.guessDivisor = result.guessDivisor
|
native_result.guessDivisor = result.guessDivisor
|
||||||
r.type = result.type
|
native_result.type = result.type
|
||||||
if old_results:
|
if old_results:
|
||||||
lib.mCoreMemorySearchRepeat(self._core, params, results)
|
lib.mCoreMemorySearchRepeat(self._core, params, results)
|
||||||
else:
|
else:
|
||||||
|
@ -154,5 +155,4 @@ class Memory(object):
|
||||||
def __getitem__(self, address):
|
def __getitem__(self, address):
|
||||||
if isinstance(address, slice):
|
if isinstance(address, slice):
|
||||||
return bytearray(self.u8[address])
|
return bytearray(self.u8[address])
|
||||||
else:
|
return self.u8[address]
|
||||||
return self.u8[address]
|
|
||||||
|
|
|
@ -3,37 +3,41 @@
|
||||||
# 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/.
|
||||||
from ._pylib import ffi, lib
|
from ._pylib import ffi, lib # pylint: disable=no-name-in-module
|
||||||
from . import vfs
|
from . import vfs
|
||||||
|
|
||||||
MODE_RGB = 0
|
MODE_RGB = 0
|
||||||
MODE_RGBA = 1
|
MODE_RGBA = 1
|
||||||
MODE_INDEX = 2
|
MODE_INDEX = 2
|
||||||
|
|
||||||
|
|
||||||
class PNG:
|
class PNG:
|
||||||
def __init__(self, f, mode=MODE_RGB):
|
def __init__(self, f, mode=MODE_RGB):
|
||||||
self.vf = vfs.open(f)
|
self._vfile = vfs.open(f)
|
||||||
self.mode = mode
|
self._png = None
|
||||||
|
self._info = None
|
||||||
|
self.mode = mode
|
||||||
|
|
||||||
def writeHeader(self, image):
|
def write_header(self, image):
|
||||||
self._png = lib.PNGWriteOpen(self.vf.handle)
|
self._png = lib.PNGWriteOpen(self._vfile.handle)
|
||||||
if self.mode == MODE_RGB:
|
if self.mode == MODE_RGB:
|
||||||
self._info = lib.PNGWriteHeader(self._png, image.width, image.height)
|
self._info = lib.PNGWriteHeader(self._png, image.width, image.height)
|
||||||
if self.mode == MODE_RGBA:
|
if self.mode == MODE_RGBA:
|
||||||
self._info = lib.PNGWriteHeaderA(self._png, image.width, image.height)
|
self._info = lib.PNGWriteHeaderA(self._png, image.width, image.height)
|
||||||
if self.mode == MODE_INDEX:
|
if self.mode == MODE_INDEX:
|
||||||
self._info = lib.PNGWriteHeader8(self._png, image.width, image.height)
|
self._info = lib.PNGWriteHeader8(self._png, image.width, image.height)
|
||||||
return self._info != ffi.NULL
|
return self._info != ffi.NULL
|
||||||
|
|
||||||
def writePixels(self, image):
|
def write_pixels(self, image):
|
||||||
if self.mode == MODE_RGB:
|
if self.mode == MODE_RGB:
|
||||||
return lib.PNGWritePixels(self._png, image.width, image.height, image.stride, image.buffer)
|
return lib.PNGWritePixels(self._png, image.width, image.height, image.stride, image.buffer)
|
||||||
if self.mode == MODE_RGBA:
|
if self.mode == MODE_RGBA:
|
||||||
return lib.PNGWritePixelsA(self._png, image.width, image.height, image.stride, image.buffer)
|
return lib.PNGWritePixelsA(self._png, image.width, image.height, image.stride, image.buffer)
|
||||||
if self.mode == MODE_INDEX:
|
if self.mode == MODE_INDEX:
|
||||||
return lib.PNGWritePixels8(self._png, image.width, image.height, image.stride, image.buffer)
|
return lib.PNGWritePixels8(self._png, image.width, image.height, image.stride, image.buffer)
|
||||||
|
return False
|
||||||
|
|
||||||
def writeClose(self):
|
def write_close(self):
|
||||||
lib.PNGWriteClose(self._png, self._info)
|
lib.PNGWriteClose(self._png, self._info)
|
||||||
del self._png
|
self._png = None
|
||||||
del self._info
|
self._info = None
|
||||||
|
|
|
@ -3,9 +3,10 @@
|
||||||
# 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/.
|
||||||
from ._pylib import ffi, lib
|
from ._pylib import ffi, lib # pylint: disable=no-name-in-module
|
||||||
from .core import IRunner, ICoreOwner, Core
|
from .core import IRunner, ICoreOwner, Core
|
||||||
|
|
||||||
|
|
||||||
class ThreadCoreOwner(ICoreOwner):
|
class ThreadCoreOwner(ICoreOwner):
|
||||||
def __init__(self, thread):
|
def __init__(self, thread):
|
||||||
self.thread = thread
|
self.thread = thread
|
||||||
|
@ -19,12 +20,13 @@ class ThreadCoreOwner(ICoreOwner):
|
||||||
def release(self):
|
def release(self):
|
||||||
lib.mCoreThreadContinue(self.thread._native)
|
lib.mCoreThreadContinue(self.thread._native)
|
||||||
|
|
||||||
|
|
||||||
class Thread(IRunner):
|
class Thread(IRunner):
|
||||||
def __init__(self, native=None):
|
def __init__(self, native=None):
|
||||||
if native:
|
if native:
|
||||||
self._native = native
|
self._native = native
|
||||||
self._core = Core(native.core)
|
self._core = Core(native.core)
|
||||||
self._core._wasReset = lib.mCoreThreadHasStarted(self._native)
|
self._core._was_reset = lib.mCoreThreadHasStarted(self._native)
|
||||||
else:
|
else:
|
||||||
self._native = ffi.new("struct mCoreThread*")
|
self._native = ffi.new("struct mCoreThread*")
|
||||||
|
|
||||||
|
@ -34,7 +36,7 @@ class Thread(IRunner):
|
||||||
self._core = core
|
self._core = core
|
||||||
self._native.core = core._core
|
self._native.core = core._core
|
||||||
lib.mCoreThreadStart(self._native)
|
lib.mCoreThreadStart(self._native)
|
||||||
self._core._wasReset = lib.mCoreThreadHasStarted(self._native)
|
self._core._was_reset = lib.mCoreThreadHasStarted(self._native)
|
||||||
|
|
||||||
def end(self):
|
def end(self):
|
||||||
if not lib.mCoreThreadHasStarted(self._native):
|
if not lib.mCoreThreadHasStarted(self._native):
|
||||||
|
@ -48,11 +50,13 @@ class Thread(IRunner):
|
||||||
def unpause(self):
|
def unpause(self):
|
||||||
lib.mCoreThreadUnpause(self._native)
|
lib.mCoreThreadUnpause(self._native)
|
||||||
|
|
||||||
def isRunning(self):
|
@property
|
||||||
|
def running(self):
|
||||||
return bool(lib.mCoreThreadIsActive(self._native))
|
return bool(lib.mCoreThreadIsActive(self._native))
|
||||||
|
|
||||||
def isPaused(self):
|
@property
|
||||||
|
def paused(self):
|
||||||
return bool(lib.mCoreThreadIsPaused(self._native))
|
return bool(lib.mCoreThreadIsPaused(self._native))
|
||||||
|
|
||||||
def useCore(self):
|
def use_core(self):
|
||||||
return ThreadCoreOwner(self)
|
return ThreadCoreOwner(self)
|
||||||
|
|
|
@ -3,14 +3,15 @@
|
||||||
# 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/.
|
||||||
from ._pylib import ffi, lib
|
from ._pylib import ffi, lib # pylint: disable=no-name-in-module
|
||||||
from . import image
|
from . import image
|
||||||
|
|
||||||
|
|
||||||
class Tile:
|
class Tile:
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
self.buffer = data
|
self.buffer = data
|
||||||
|
|
||||||
def toImage(self):
|
def to_image(self):
|
||||||
i = image.Image(8, 8)
|
i = image.Image(8, 8)
|
||||||
self.composite(i, 0, 0)
|
self.composite(i, 0, 0)
|
||||||
return i
|
return i
|
||||||
|
@ -19,19 +20,22 @@ class Tile:
|
||||||
for iy in range(8):
|
for iy in range(8):
|
||||||
ffi.memmove(ffi.addressof(i.buffer, x + (iy + y) * i.stride), ffi.addressof(self.buffer, iy * 8), 8 * ffi.sizeof("color_t"))
|
ffi.memmove(ffi.addressof(i.buffer, x + (iy + y) * i.stride), ffi.addressof(self.buffer, iy * 8), 8 * ffi.sizeof("color_t"))
|
||||||
|
|
||||||
|
|
||||||
class CacheSet:
|
class CacheSet:
|
||||||
def __init__(self, core):
|
def __init__(self, core):
|
||||||
self.core = core
|
self.core = core
|
||||||
self.cache = ffi.gc(ffi.new("struct mCacheSet*"), core._deinitCache)
|
self.cache = ffi.gc(ffi.new("struct mCacheSet*"), core._deinit_cache)
|
||||||
core._initCache(self.cache)
|
core._init_cache(self.cache)
|
||||||
|
|
||||||
|
|
||||||
class TileView:
|
class TileView:
|
||||||
def __init__(self, cache):
|
def __init__(self, cache):
|
||||||
self.cache = cache
|
self.cache = cache
|
||||||
|
|
||||||
def getTile(self, tile, palette):
|
def get_tile(self, tile, palette):
|
||||||
return Tile(lib.mTileCacheGetTile(self.cache, tile, palette))
|
return Tile(lib.mTileCacheGetTile(self.cache, tile, palette))
|
||||||
|
|
||||||
|
|
||||||
class MapView:
|
class MapView:
|
||||||
def __init__(self, cache):
|
def __init__(self, cache):
|
||||||
self.cache = cache
|
self.cache = cache
|
||||||
|
@ -54,6 +58,7 @@ class MapView:
|
||||||
ffi.memmove(ffi.addressof(i.buffer, i.stride * y), row, self.width * 8 * ffi.sizeof("color_t"))
|
ffi.memmove(ffi.addressof(i.buffer, i.stride * y), row, self.width * 8 * ffi.sizeof("color_t"))
|
||||||
return i
|
return i
|
||||||
|
|
||||||
|
|
||||||
class Sprite(object):
|
class Sprite(object):
|
||||||
def constitute(self, tileView, tilePitch):
|
def constitute(self, tileView, tilePitch):
|
||||||
i = image.Image(self.width, self.height, alpha=True)
|
i = image.Image(self.width, self.height, alpha=True)
|
||||||
|
|
|
@ -3,139 +3,152 @@
|
||||||
# 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/.
|
||||||
from ._pylib import ffi, lib
|
# pylint: disable=invalid-name,unused-argument
|
||||||
import mmap
|
from ._pylib import ffi, lib # pylint: disable=no-name-in-module
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
@ffi.def_extern()
|
@ffi.def_extern()
|
||||||
def _vfpClose(vf):
|
def _vfpClose(vf):
|
||||||
vfp = ffi.cast("struct VFilePy*", vf)
|
vfp = ffi.cast("struct VFilePy*", vf)
|
||||||
ffi.from_handle(vfp.fileobj).close()
|
ffi.from_handle(vfp.fileobj).close()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@ffi.def_extern()
|
@ffi.def_extern()
|
||||||
def _vfpSeek(vf, offset, whence):
|
def _vfpSeek(vf, offset, whence):
|
||||||
vfp = ffi.cast("struct VFilePy*", vf)
|
vfp = ffi.cast("struct VFilePy*", vf)
|
||||||
f = ffi.from_handle(vfp.fileobj)
|
f = ffi.from_handle(vfp.fileobj)
|
||||||
f.seek(offset, whence)
|
f.seek(offset, whence)
|
||||||
return f.tell()
|
return f.tell()
|
||||||
|
|
||||||
|
|
||||||
@ffi.def_extern()
|
@ffi.def_extern()
|
||||||
def _vfpRead(vf, buffer, size):
|
def _vfpRead(vf, buffer, size):
|
||||||
vfp = ffi.cast("struct VFilePy*", vf)
|
vfp = ffi.cast("struct VFilePy*", vf)
|
||||||
pybuf = ffi.buffer(buffer, size)
|
pybuf = ffi.buffer(buffer, size)
|
||||||
ffi.from_handle(vfp.fileobj).readinto(pybuf)
|
ffi.from_handle(vfp.fileobj).readinto(pybuf)
|
||||||
return size
|
return size
|
||||||
|
|
||||||
|
|
||||||
@ffi.def_extern()
|
@ffi.def_extern()
|
||||||
def _vfpWrite(vf, buffer, size):
|
def _vfpWrite(vf, buffer, size):
|
||||||
vfp = ffi.cast("struct VFilePy*", vf)
|
vfp = ffi.cast("struct VFilePy*", vf)
|
||||||
pybuf = ffi.buffer(buffer, size)
|
pybuf = ffi.buffer(buffer, size)
|
||||||
ffi.from_handle(vfp.fileobj).write(pybuf)
|
ffi.from_handle(vfp.fileobj).write(pybuf)
|
||||||
return size
|
return size
|
||||||
|
|
||||||
|
|
||||||
@ffi.def_extern()
|
@ffi.def_extern()
|
||||||
def _vfpMap(vf, size, flags):
|
def _vfpMap(vf, size, flags):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@ffi.def_extern()
|
@ffi.def_extern()
|
||||||
def _vfpUnmap(vf, memory, size):
|
def _vfpUnmap(vf, memory, size):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@ffi.def_extern()
|
@ffi.def_extern()
|
||||||
def _vfpTruncate(vf, size):
|
def _vfpTruncate(vf, size):
|
||||||
vfp = ffi.cast("struct VFilePy*", vf)
|
vfp = ffi.cast("struct VFilePy*", vf)
|
||||||
ffi.from_handle(vfp.fileobj).truncate(size)
|
ffi.from_handle(vfp.fileobj).truncate(size)
|
||||||
|
|
||||||
|
|
||||||
@ffi.def_extern()
|
@ffi.def_extern()
|
||||||
def _vfpSize(vf):
|
def _vfpSize(vf):
|
||||||
vfp = ffi.cast("struct VFilePy*", vf)
|
vfp = ffi.cast("struct VFilePy*", vf)
|
||||||
f = ffi.from_handle(vfp.fileobj)
|
f = ffi.from_handle(vfp.fileobj)
|
||||||
pos = f.tell()
|
pos = f.tell()
|
||||||
f.seek(0, os.SEEK_END)
|
f.seek(0, os.SEEK_END)
|
||||||
size = f.tell()
|
size = f.tell()
|
||||||
f.seek(pos, os.SEEK_SET)
|
f.seek(pos, os.SEEK_SET)
|
||||||
return size
|
return size
|
||||||
|
|
||||||
|
|
||||||
@ffi.def_extern()
|
@ffi.def_extern()
|
||||||
def _vfpSync(vf, buffer, size):
|
def _vfpSync(vf, buffer, size):
|
||||||
vfp = ffi.cast("struct VFilePy*", vf)
|
vfp = ffi.cast("struct VFilePy*", vf)
|
||||||
f = ffi.from_handle(vfp.fileobj)
|
f = ffi.from_handle(vfp.fileobj)
|
||||||
if buffer and size:
|
if buffer and size:
|
||||||
pos = f.tell()
|
pos = f.tell()
|
||||||
f.seek(0, os.SEEK_SET)
|
f.seek(0, os.SEEK_SET)
|
||||||
_vfpWrite(vf, buffer, size)
|
_vfpWrite(vf, buffer, size)
|
||||||
f.seek(pos, os.SEEK_SET)
|
f.seek(pos, os.SEEK_SET)
|
||||||
f.flush()
|
f.flush()
|
||||||
os.fsync()
|
os.fsync()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def open(f):
|
|
||||||
handle = ffi.new_handle(f)
|
|
||||||
vf = VFile(lib.VFileFromPython(handle))
|
|
||||||
# Prevent garbage collection
|
|
||||||
vf._fileobj = f
|
|
||||||
vf._handle = handle
|
|
||||||
return vf
|
|
||||||
|
|
||||||
def openPath(path, mode="r"):
|
def open(f): # pylint: disable=redefined-builtin
|
||||||
flags = 0
|
handle = ffi.new_handle(f)
|
||||||
if mode.startswith("r"):
|
vf = VFile(lib.VFileFromPython(handle), _no_gc=(f, handle))
|
||||||
flags |= os.O_RDONLY
|
return vf
|
||||||
elif mode.startswith("w"):
|
|
||||||
flags |= os.O_WRONLY | os.O_CREAT | os.O_TRUNC
|
|
||||||
elif mode.startswith("a"):
|
|
||||||
flags |= os.O_WRONLY | os.O_CREAT | os.O_APPEND
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if "+" in mode[1:]:
|
|
||||||
flags |= os.O_RDWR
|
|
||||||
if "x" in mode[1:]:
|
|
||||||
flags |= os.O_EXCL
|
|
||||||
|
|
||||||
vf = lib.VFileOpen(path.encode("UTF-8"), flags);
|
def open_path(path, mode="r"):
|
||||||
if vf == ffi.NULL:
|
flags = 0
|
||||||
return None
|
if mode.startswith("r"):
|
||||||
return VFile(vf)
|
flags |= os.O_RDONLY
|
||||||
|
elif mode.startswith("w"):
|
||||||
|
flags |= os.O_WRONLY | os.O_CREAT | os.O_TRUNC
|
||||||
|
elif mode.startswith("a"):
|
||||||
|
flags |= os.O_WRONLY | os.O_CREAT | os.O_APPEND
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if "+" in mode[1:]:
|
||||||
|
flags |= os.O_RDWR
|
||||||
|
if "x" in mode[1:]:
|
||||||
|
flags |= os.O_EXCL
|
||||||
|
|
||||||
|
vf = lib.VFileOpen(path.encode("UTF-8"), flags)
|
||||||
|
if vf == ffi.NULL:
|
||||||
|
return None
|
||||||
|
return VFile(vf)
|
||||||
|
|
||||||
|
|
||||||
class VFile:
|
class VFile:
|
||||||
def __init__(self, vf):
|
def __init__(self, vf, _no_gc=None):
|
||||||
self.handle = vf
|
self.handle = vf
|
||||||
|
self._no_gc = _no_gc
|
||||||
|
|
||||||
def close(self):
|
def __del__(self):
|
||||||
return bool(self.handle.close(self.handle))
|
self.close()
|
||||||
|
|
||||||
def seek(self, offset, whence):
|
def close(self):
|
||||||
return self.handle.seek(self.handle, offset, whence)
|
return bool(self.handle.close(self.handle))
|
||||||
|
|
||||||
def read(self, buffer, size):
|
def seek(self, offset, whence):
|
||||||
return self.handle.read(self.handle, buffer, size)
|
return self.handle.seek(self.handle, offset, whence)
|
||||||
|
|
||||||
def readAll(self, size=0):
|
def read(self, buffer, size):
|
||||||
if not size:
|
return self.handle.read(self.handle, buffer, size)
|
||||||
size = self.size()
|
|
||||||
buffer = ffi.new("char[%i]" % size)
|
|
||||||
size = self.handle.read(self.handle, buffer, size)
|
|
||||||
return ffi.unpack(buffer, size)
|
|
||||||
|
|
||||||
def readline(self, buffer, size):
|
def read_all(self, size=0):
|
||||||
return self.handle.readline(self.handle, buffer, size)
|
if not size:
|
||||||
|
size = self.size()
|
||||||
|
buffer = ffi.new("char[%i]" % size)
|
||||||
|
size = self.handle.read(self.handle, buffer, size)
|
||||||
|
return ffi.unpack(buffer, size)
|
||||||
|
|
||||||
def write(self, buffer, size):
|
def readline(self, buffer, size):
|
||||||
return self.handle.write(self.handle, buffer, size)
|
return self.handle.readline(self.handle, buffer, size)
|
||||||
|
|
||||||
def map(self, size, flags):
|
def write(self, buffer, size):
|
||||||
return self.handle.map(self.handle, size, flags)
|
return self.handle.write(self.handle, buffer, size)
|
||||||
|
|
||||||
def unmap(self, memory, size):
|
def map(self, size, flags):
|
||||||
self.handle.unmap(self.handle, memory, size)
|
return self.handle.map(self.handle, size, flags)
|
||||||
|
|
||||||
def truncate(self, size):
|
def unmap(self, memory, size):
|
||||||
self.handle.truncate(self.handle, size)
|
self.handle.unmap(self.handle, memory, size)
|
||||||
|
|
||||||
def size(self):
|
def truncate(self, size):
|
||||||
return self.handle.size(self.handle)
|
self.handle.truncate(self.handle, size)
|
||||||
|
|
||||||
def sync(self, buffer, size):
|
def size(self):
|
||||||
return self.handle.sync(self.handle, buffer, size)
|
return self.handle.size(self.handle)
|
||||||
|
|
||||||
|
def sync(self, buffer, size):
|
||||||
|
return self.handle.sync(self.handle, buffer, size)
|
||||||
|
|
|
@ -1,2 +1,6 @@
|
||||||
[aliases]
|
[aliases]
|
||||||
test=pytest
|
test=pytest
|
||||||
|
|
||||||
|
[pycodestyle]
|
||||||
|
exclude = .eggs
|
||||||
|
ignore = E501,E741,E743
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
from setuptools import setup
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
|
def get_version_component(piece):
|
||||||
|
return subprocess.check_output(['cmake', '-DPRINT_STRING={}'.format(piece), '-P', '../../../version.cmake']).decode('utf-8').strip()
|
||||||
|
|
||||||
|
|
||||||
|
version = '{}.{}.{}'.format(*(get_version_component(p) for p in ('LIB_VERSION_MAJOR', 'LIB_VERSION_MINOR', 'LIB_VERSION_PATCH')))
|
||||||
|
if not get_version_component('GIT_TAG'):
|
||||||
|
version += '.{}+g{}'.format(*(get_version_component(p) for p in ('GIT_REV', 'GIT_COMMIT_SHORT')))
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name="mgba",
|
||||||
|
version=version,
|
||||||
|
author="Jeffrey Pfau",
|
||||||
|
author_email="jeffrey@endrift.com",
|
||||||
|
url="http://github.com/mgba-emu/mgba/",
|
||||||
|
packages=["mgba"],
|
||||||
|
setup_requires=['cffi>=1.6', 'pytest-runner'],
|
||||||
|
install_requires=['cffi>=1.6', 'cached-property'],
|
||||||
|
extras_require={'pil': ['Pillow>=2.3'], 'cinema': ['pyyaml', 'pytest']},
|
||||||
|
tests_require=['pytest'],
|
||||||
|
cffi_modules=["_builder.py:ffi"],
|
||||||
|
license="MPL 2.0",
|
||||||
|
classifiers=[
|
||||||
|
"Programming Language :: C",
|
||||||
|
"Programming Language :: Python :: 2",
|
||||||
|
"Programming Language :: Python :: 3",
|
||||||
|
"License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)",
|
||||||
|
"Topic :: Games/Entertainment",
|
||||||
|
"Topic :: System :: Emulators"
|
||||||
|
]
|
||||||
|
)
|
|
@ -4,7 +4,7 @@ import mgba.log
|
||||||
import os.path
|
import os.path
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
mgba.log.installDefault(mgba.log.NullLogger())
|
mgba.log.install_default(mgba.log.NullLogger())
|
||||||
|
|
||||||
def flatten(d):
|
def flatten(d):
|
||||||
l = []
|
l = []
|
||||||
|
@ -18,7 +18,7 @@ def flatten(d):
|
||||||
|
|
||||||
def pytest_generate_tests(metafunc):
|
def pytest_generate_tests(metafunc):
|
||||||
if 'vtest' in metafunc.fixturenames:
|
if 'vtest' in metafunc.fixturenames:
|
||||||
tests = cinema.test.gatherTests(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'cinema'))
|
tests = cinema.test.gather_tests(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'cinema'))
|
||||||
testList = flatten(tests)
|
testList = flatten(tests)
|
||||||
params = []
|
params = []
|
||||||
for test in testList:
|
for test in testList:
|
||||||
|
@ -34,9 +34,9 @@ def vtest(request):
|
||||||
return request.param
|
return request.param
|
||||||
|
|
||||||
def test_video(vtest, pytestconfig):
|
def test_video(vtest, pytestconfig):
|
||||||
vtest.setUp()
|
vtest.setup()
|
||||||
if pytestconfig.getoption('--rebaseline'):
|
if pytestconfig.getoption('--rebaseline'):
|
||||||
vtest.generateBaseline()
|
vtest.generate_baseline()
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
vtest.test()
|
vtest.test()
|
||||||
|
|
|
@ -4,26 +4,30 @@ import os
|
||||||
import mgba.vfs as vfs
|
import mgba.vfs as vfs
|
||||||
from mgba._pylib import ffi
|
from mgba._pylib import ffi
|
||||||
|
|
||||||
|
|
||||||
def test_vfs_open():
|
def test_vfs_open():
|
||||||
with open(__file__) as f:
|
with open(__file__) as f:
|
||||||
vf = vfs.open(f)
|
vf = vfs.open(f)
|
||||||
assert vf
|
assert vf
|
||||||
assert vf.close()
|
assert vf.close()
|
||||||
|
|
||||||
def test_vfs_openPath():
|
|
||||||
vf = vfs.openPath(__file__)
|
def test_vfs_open_path():
|
||||||
|
vf = vfs.open_path(__file__)
|
||||||
assert vf
|
assert vf
|
||||||
assert vf.close()
|
assert vf.close()
|
||||||
|
|
||||||
|
|
||||||
def test_vfs_read():
|
def test_vfs_read():
|
||||||
vf = vfs.openPath(__file__)
|
vf = vfs.open_path(__file__)
|
||||||
buffer = ffi.new('char[13]')
|
buffer = ffi.new('char[13]')
|
||||||
assert vf.read(buffer, 13) == 13
|
assert vf.read(buffer, 13) == 13
|
||||||
assert ffi.string(buffer) == b'import pytest'
|
assert ffi.string(buffer) == b'import pytest'
|
||||||
vf.close()
|
vf.close()
|
||||||
|
|
||||||
|
|
||||||
def test_vfs_readline():
|
def test_vfs_readline():
|
||||||
vf = vfs.openPath(__file__)
|
vf = vfs.open_path(__file__)
|
||||||
buffer = ffi.new('char[16]')
|
buffer = ffi.new('char[16]')
|
||||||
linelen = vf.readline(buffer, 16)
|
linelen = vf.readline(buffer, 16)
|
||||||
assert linelen in (14, 15)
|
assert linelen in (14, 15)
|
||||||
|
@ -33,16 +37,18 @@ def test_vfs_readline():
|
||||||
assert ffi.string(buffer) == b'import pytest\r\n'
|
assert ffi.string(buffer) == b'import pytest\r\n'
|
||||||
vf.close()
|
vf.close()
|
||||||
|
|
||||||
def test_vfs_readAllSize():
|
|
||||||
vf = vfs.openPath(__file__)
|
def test_vfs_read_all_size():
|
||||||
buffer = vf.readAll()
|
vf = vfs.open_path(__file__)
|
||||||
|
buffer = vf.read_all()
|
||||||
assert buffer
|
assert buffer
|
||||||
assert len(buffer)
|
assert len(buffer)
|
||||||
assert len(buffer) == vf.size()
|
assert len(buffer) == vf.size()
|
||||||
vf.close()
|
vf.close()
|
||||||
|
|
||||||
|
|
||||||
def test_vfs_seek():
|
def test_vfs_seek():
|
||||||
vf = vfs.openPath(__file__)
|
vf = vfs.open_path(__file__)
|
||||||
assert vf.seek(0, os.SEEK_SET) == 0
|
assert vf.seek(0, os.SEEK_SET) == 0
|
||||||
assert vf.seek(1, os.SEEK_SET) == 1
|
assert vf.seek(1, os.SEEK_SET) == 1
|
||||||
assert vf.seek(1, os.SEEK_CUR) == 2
|
assert vf.seek(1, os.SEEK_CUR) == 2
|
||||||
|
@ -52,6 +58,7 @@ def test_vfs_seek():
|
||||||
assert vf.seek(-1, os.SEEK_END) == vf.size() -1
|
assert vf.seek(-1, os.SEEK_END) == vf.size() -1
|
||||||
vf.close()
|
vf.close()
|
||||||
|
|
||||||
def test_vfs_openPath_invalid():
|
|
||||||
vf = vfs.openPath('.invalid')
|
def test_vfs_open_path_invalid():
|
||||||
|
vf = vfs.open_path('.invalid')
|
||||||
assert not vf
|
assert not vf
|
||||||
|
|
|
@ -6,9 +6,7 @@ set(PLATFORM_SRC)
|
||||||
set(QT_STATIC OFF)
|
set(QT_STATIC OFF)
|
||||||
|
|
||||||
if(BUILD_SDL)
|
if(BUILD_SDL)
|
||||||
if(NOT SDL_FOUND AND NOT SDL2_FOUND)
|
add_definitions(-DBUILD_SDL)
|
||||||
find_package(SDL 1.2 REQUIRED)
|
|
||||||
endif()
|
|
||||||
if(SDL2_FOUND)
|
if(SDL2_FOUND)
|
||||||
link_directories(${SDL2_LIBDIR})
|
link_directories(${SDL2_LIBDIR})
|
||||||
endif()
|
endif()
|
||||||
|
@ -33,9 +31,9 @@ if(NOT BUILD_GL AND NOT BUILD_GLES2)
|
||||||
message(WARNING "OpenGL is recommended to build the Qt port")
|
message(WARNING "OpenGL is recommended to build the Qt port")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
set(FOUND_QT ${Qt5Widgets_FOUND} PARENT_SCOPE)
|
||||||
if(NOT Qt5Widgets_FOUND)
|
if(NOT Qt5Widgets_FOUND)
|
||||||
message(WARNING "Cannot find Qt modules")
|
message(WARNING "Cannot find Qt modules")
|
||||||
set(BUILD_QT OFF PARENT_SCOPE)
|
|
||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
|
@ -204,13 +204,13 @@ CoreController::~CoreController() {
|
||||||
stop();
|
stop();
|
||||||
disconnect();
|
disconnect();
|
||||||
|
|
||||||
|
mCoreThreadJoin(&m_threadContext);
|
||||||
|
|
||||||
if (m_cacheSet) {
|
if (m_cacheSet) {
|
||||||
mCacheSetDeinit(m_cacheSet.get());
|
mCacheSetDeinit(m_cacheSet.get());
|
||||||
m_cacheSet.reset();
|
m_cacheSet.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
mCoreThreadJoin(&m_threadContext);
|
|
||||||
|
|
||||||
mCoreConfigDeinit(&m_threadContext.core->config);
|
mCoreConfigDeinit(&m_threadContext.core->config);
|
||||||
m_threadContext.core->deinit(m_threadContext.core);
|
m_threadContext.core->deinit(m_threadContext.core);
|
||||||
}
|
}
|
||||||
|
@ -360,6 +360,7 @@ void CoreController::start() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreController::stop() {
|
void CoreController::stop() {
|
||||||
|
setSync(false);
|
||||||
#ifdef USE_DEBUGGERS
|
#ifdef USE_DEBUGGERS
|
||||||
setDebugger(nullptr);
|
setDebugger(nullptr);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "GamepadButtonEvent.h"
|
#include "GamepadButtonEvent.h"
|
||||||
#include "VFileDevice.h"
|
#include "VFileDevice.h"
|
||||||
|
|
||||||
|
#include <QAction>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
|
|
@ -160,10 +160,12 @@ Window::Window(CoreManager* manager, ConfigController* config, int playerId, QWi
|
||||||
|
|
||||||
connect(this, &Window::shutdown, m_logView, &QWidget::hide);
|
connect(this, &Window::shutdown, m_logView, &QWidget::hide);
|
||||||
connect(&m_fpsTimer, &QTimer::timeout, this, &Window::showFPS);
|
connect(&m_fpsTimer, &QTimer::timeout, this, &Window::showFPS);
|
||||||
|
connect(&m_frameTimer, &QTimer::timeout, this, &Window::delimitFrames);
|
||||||
connect(&m_focusCheck, &QTimer::timeout, this, &Window::focusCheck);
|
connect(&m_focusCheck, &QTimer::timeout, this, &Window::focusCheck);
|
||||||
|
|
||||||
m_log.setLevels(mLOG_WARN | mLOG_ERROR | mLOG_FATAL);
|
m_log.setLevels(mLOG_WARN | mLOG_ERROR | mLOG_FATAL);
|
||||||
m_fpsTimer.setInterval(FPS_TIMER_INTERVAL);
|
m_fpsTimer.setInterval(FPS_TIMER_INTERVAL);
|
||||||
|
m_frameTimer.setInterval(FRAME_LIST_INTERVAL);
|
||||||
m_focusCheck.setInterval(200);
|
m_focusCheck.setInterval(200);
|
||||||
|
|
||||||
setupMenu(menuBar());
|
setupMenu(menuBar());
|
||||||
|
@ -314,6 +316,7 @@ QString Window::getFilters() const {
|
||||||
QStringList gbFormats{
|
QStringList gbFormats{
|
||||||
"*.gb",
|
"*.gb",
|
||||||
"*.gbc",
|
"*.gbc",
|
||||||
|
"*.sgb",
|
||||||
#if defined(USE_LIBZIP) || defined(USE_ZLIB)
|
#if defined(USE_LIBZIP) || defined(USE_ZLIB)
|
||||||
"*.zip",
|
"*.zip",
|
||||||
#endif
|
#endif
|
||||||
|
@ -726,8 +729,6 @@ void Window::gameStarted() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
multiplayerChanged();
|
|
||||||
updateTitle();
|
|
||||||
QSize size = m_controller->screenDimensions();
|
QSize size = m_controller->screenDimensions();
|
||||||
m_screenWidget->setDimensions(size.width(), size.height());
|
m_screenWidget->setDimensions(size.width(), size.height());
|
||||||
m_config->updateOption("lockIntegerScaling");
|
m_config->updateOption("lockIntegerScaling");
|
||||||
|
@ -744,10 +745,16 @@ void Window::gameStarted() {
|
||||||
menuBar()->hide();
|
menuBar()->hide();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
m_display->startDrawing(m_controller);
|
||||||
|
|
||||||
|
reloadAudioDriver();
|
||||||
|
multiplayerChanged();
|
||||||
|
updateTitle();
|
||||||
|
|
||||||
m_hitUnimplementedBiosCall = false;
|
m_hitUnimplementedBiosCall = false;
|
||||||
if (m_config->getOption("showFps", "1").toInt()) {
|
if (m_config->getOption("showFps", "1").toInt()) {
|
||||||
m_fpsTimer.start();
|
m_fpsTimer.start();
|
||||||
|
m_frameTimer.start();
|
||||||
}
|
}
|
||||||
m_focusCheck.start();
|
m_focusCheck.start();
|
||||||
if (m_display->underMouse()) {
|
if (m_display->underMouse()) {
|
||||||
|
@ -785,12 +792,10 @@ void Window::gameStarted() {
|
||||||
m_audioChannels->addAction(action);
|
m_audioChannels->addAction(action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_display->startDrawing(m_controller);
|
|
||||||
|
|
||||||
reloadAudioDriver();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::gameStopped() {
|
void Window::gameStopped() {
|
||||||
|
m_controller.reset();
|
||||||
for (QPair<QAction*, int> action : m_platformActions) {
|
for (QPair<QAction*, int> action : m_platformActions) {
|
||||||
action.first->setDisabled(false);
|
action.first->setDisabled(false);
|
||||||
}
|
}
|
||||||
|
@ -816,6 +821,7 @@ void Window::gameStopped() {
|
||||||
m_audioChannels->clear();
|
m_audioChannels->clear();
|
||||||
|
|
||||||
m_fpsTimer.stop();
|
m_fpsTimer.stop();
|
||||||
|
m_frameTimer.stop();
|
||||||
m_focusCheck.stop();
|
m_focusCheck.stop();
|
||||||
|
|
||||||
emit paused(false);
|
emit paused(false);
|
||||||
|
@ -944,19 +950,27 @@ void Window::mustRestart() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::recordFrame() {
|
void Window::recordFrame() {
|
||||||
m_frameList.append(QDateTime::currentDateTime());
|
if (m_frameList.isEmpty()) {
|
||||||
while (m_frameList.count() > FRAME_LIST_SIZE) {
|
m_frameList.append(1);
|
||||||
m_frameList.removeFirst();
|
} else {
|
||||||
|
++m_frameList.back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Window::delimitFrames() {
|
||||||
|
if (m_frameList.size() >= FRAME_LIST_SIZE) {
|
||||||
|
m_frameCounter -= m_frameList.takeAt(0);
|
||||||
|
}
|
||||||
|
m_frameCounter += m_frameList.back();
|
||||||
|
m_frameList.append(0);
|
||||||
|
}
|
||||||
|
|
||||||
void Window::showFPS() {
|
void Window::showFPS() {
|
||||||
if (m_frameList.isEmpty()) {
|
if (m_frameList.isEmpty()) {
|
||||||
updateTitle();
|
updateTitle();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
qint64 interval = m_frameList.first().msecsTo(m_frameList.last());
|
float fps = m_frameCounter * 10000.f / (FRAME_LIST_INTERVAL * (m_frameList.size() - 1));
|
||||||
float fps = (m_frameList.count() - 1) * 10000.f / interval;
|
|
||||||
fps = round(fps) / 10.f;
|
fps = round(fps) / 10.f;
|
||||||
updateTitle(fps);
|
updateTitle(fps);
|
||||||
}
|
}
|
||||||
|
@ -1679,9 +1693,11 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
showFps->connect([this](const QVariant& value) {
|
showFps->connect([this](const QVariant& value) {
|
||||||
if (!value.toInt()) {
|
if (!value.toInt()) {
|
||||||
m_fpsTimer.stop();
|
m_fpsTimer.stop();
|
||||||
|
m_frameTimer.stop();
|
||||||
updateTitle();
|
updateTitle();
|
||||||
} else if (m_controller) {
|
} else if (m_controller) {
|
||||||
m_fpsTimer.start();
|
m_fpsTimer.start();
|
||||||
|
m_frameTimer.start();
|
||||||
}
|
}
|
||||||
}, this);
|
}, this);
|
||||||
|
|
||||||
|
@ -1792,17 +1808,19 @@ void Window::setController(CoreController* controller, const QString& fname) {
|
||||||
if (!controller) {
|
if (!controller) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_controller) {
|
||||||
|
m_controller->stop();
|
||||||
|
QTimer::singleShot(0, this, [this, controller, fname]() {
|
||||||
|
setController(controller, fname);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!fname.isEmpty()) {
|
if (!fname.isEmpty()) {
|
||||||
setWindowFilePath(fname);
|
setWindowFilePath(fname);
|
||||||
appendMRU(fname);
|
appendMRU(fname);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_controller) {
|
|
||||||
m_controller->disconnect(this);
|
|
||||||
m_controller->stop();
|
|
||||||
m_controller.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_controller = std::shared_ptr<CoreController>(controller);
|
m_controller = std::shared_ptr<CoreController>(controller);
|
||||||
m_inputController.recalibrateAxes();
|
m_inputController.recalibrateAxes();
|
||||||
m_controller->setInputController(&m_inputController);
|
m_controller->setInputController(&m_inputController);
|
||||||
|
|
|
@ -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/. */
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QAction>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
|
@ -130,6 +131,7 @@ private slots:
|
||||||
void mustRestart();
|
void mustRestart();
|
||||||
|
|
||||||
void recordFrame();
|
void recordFrame();
|
||||||
|
void delimitFrames();
|
||||||
void showFPS();
|
void showFPS();
|
||||||
void focusCheck();
|
void focusCheck();
|
||||||
|
|
||||||
|
@ -137,7 +139,8 @@ private slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const int FPS_TIMER_INTERVAL = 2000;
|
static const int FPS_TIMER_INTERVAL = 2000;
|
||||||
static const int FRAME_LIST_SIZE = 120;
|
static const int FRAME_LIST_INTERVAL = 100;
|
||||||
|
static const int FRAME_LIST_SIZE = 40;
|
||||||
|
|
||||||
void setupMenu(QMenuBar*);
|
void setupMenu(QMenuBar*);
|
||||||
void openStateWindow(LoadSave);
|
void openStateWindow(LoadSave);
|
||||||
|
@ -183,8 +186,10 @@ private:
|
||||||
QPixmap m_logo{":/res/medusa-bg.jpg"};
|
QPixmap m_logo{":/res/medusa-bg.jpg"};
|
||||||
ConfigController* m_config;
|
ConfigController* m_config;
|
||||||
InputController m_inputController;
|
InputController m_inputController;
|
||||||
QList<QDateTime> m_frameList;
|
QList<int> m_frameList;
|
||||||
|
int m_frameCounter = 0;
|
||||||
QTimer m_fpsTimer;
|
QTimer m_fpsTimer;
|
||||||
|
QTimer m_frameTimer;
|
||||||
QList<QString> m_mruFiles;
|
QList<QString> m_mruFiles;
|
||||||
QMenu* m_mruMenu = nullptr;
|
QMenu* m_mruMenu = nullptr;
|
||||||
QMenu* m_videoLayers;
|
QMenu* m_videoLayers;
|
||||||
|
|
|
@ -37,8 +37,10 @@ Q_IMPORT_PLUGIN(AVFServicePlugin);
|
||||||
using namespace QGBA;
|
using namespace QGBA;
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
#if defined(BUILD_SDL) && SDL_VERSION_ATLEAST(2, 0, 0)
|
#ifdef BUILD_SDL
|
||||||
|
#if SDL_VERSION_ATLEAST(2, 0, 0) // CPP does not shortcut function lookup
|
||||||
SDL_SetMainReady();
|
SDL_SetMainReady();
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ConfigController configController;
|
ConfigController configController;
|
||||||
|
|
|
@ -13,13 +13,15 @@ endif()
|
||||||
|
|
||||||
if(SDL_VERSION EQUAL "1.2" OR NOT SDL2_FOUND)
|
if(SDL_VERSION EQUAL "1.2" OR NOT SDL2_FOUND)
|
||||||
find_package(SDL 1.2)
|
find_package(SDL 1.2)
|
||||||
set(SDL_VERSION "1.2" PARENT_SCOPE)
|
if(SDL_FOUND)
|
||||||
set(SDL_VERSION_DEBIAN "1.2debian")
|
set(SDL_VERSION "1.2" PARENT_SCOPE)
|
||||||
set(USE_PIXMAN ON)
|
set(SDL_VERSION_DEBIAN "1.2debian")
|
||||||
|
set(USE_PIXMAN ON)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (NOT SDL2_FOUND AND NOT SDL_FOUND)
|
if (NOT SDL2_FOUND AND NOT SDL_FOUND)
|
||||||
set(BUILD_SDL OFF PARENT_SCOPE)
|
set(SDL_FOUND OFF PARENT_SCOPE)
|
||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ if(NOT GIT_BRANCH)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(DEFINED PRINT_STRING)
|
if(DEFINED PRINT_STRING)
|
||||||
message("${${PRINT_STRING}}")
|
execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${${PRINT_STRING}}")
|
||||||
elseif(NOT VERSION_STRING_CACHE OR NOT VERSION_STRING STREQUAL VERSION_STRING_CACHE)
|
elseif(NOT VERSION_STRING_CACHE OR NOT VERSION_STRING STREQUAL VERSION_STRING_CACHE)
|
||||||
set(VERSION_STRING_CACHE ${VERSION_STRING} CACHE STRING "" FORCE)
|
set(VERSION_STRING_CACHE ${VERSION_STRING} CACHE STRING "" FORCE)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue