mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'feature/python-bindings'
This commit is contained in:
commit
6d898542c7
|
@ -2,3 +2,4 @@
|
||||||
*.user*
|
*.user*
|
||||||
*~
|
*~
|
||||||
*.swp
|
*.swp
|
||||||
|
*.pyc
|
||||||
|
|
|
@ -28,6 +28,7 @@ set(BUILD_PERF OFF CACHE BOOL "Build performance profiling tool")
|
||||||
set(BUILD_TEST OFF CACHE BOOL "Build testing harness")
|
set(BUILD_TEST OFF CACHE BOOL "Build testing harness")
|
||||||
set(BUILD_SUITE OFF CACHE BOOL "Build test suite")
|
set(BUILD_SUITE OFF CACHE BOOL "Build test suite")
|
||||||
set(BUILD_EXAMPLE OFF CACHE BOOL "Build example frontends")
|
set(BUILD_EXAMPLE OFF CACHE BOOL "Build example frontends")
|
||||||
|
set(BUILD_PYTHON OFF CACHE BOOL "Build Python bindings")
|
||||||
set(BUILD_STATIC OFF CACHE BOOL "Build a static library")
|
set(BUILD_STATIC OFF CACHE BOOL "Build a static library")
|
||||||
set(BUILD_SHARED ON CACHE BOOL "Build a shared library")
|
set(BUILD_SHARED ON CACHE BOOL "Build a shared library")
|
||||||
set(SKIP_LIBRARY OFF CACHE BOOL "Skip building the library (useful for only building libretro or OpenEmu cores)")
|
set(SKIP_LIBRARY OFF CACHE BOOL "Skip building the library (useful for only building libretro or OpenEmu cores)")
|
||||||
|
@ -151,6 +152,9 @@ add_custom_target(version-info ALL
|
||||||
|
|
||||||
include(${CMAKE_CURRENT_SOURCE_DIR}/version.cmake)
|
include(${CMAKE_CURRENT_SOURCE_DIR}/version.cmake)
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/core/version.c.in ${CMAKE_CURRENT_BINARY_DIR}/version.c)
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/core/version.c.in ${CMAKE_CURRENT_BINARY_DIR}/version.c)
|
||||||
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/core/flags.h.in ${CMAKE_CURRENT_BINARY_DIR}/flags.h)
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/flags.h DESTINATION include/mgba COMPONENT lib${BINARY_NAME})
|
||||||
|
|
||||||
list(APPEND UTIL_SRC ${CMAKE_CURRENT_BINARY_DIR}/version.c)
|
list(APPEND UTIL_SRC ${CMAKE_CURRENT_BINARY_DIR}/version.c)
|
||||||
source_group("Generated sources" FILES ${CMAKE_CURRENT_BINARY_DIR}/version.c)
|
source_group("Generated sources" FILES ${CMAKE_CURRENT_BINARY_DIR}/version.c)
|
||||||
|
|
||||||
|
@ -755,6 +759,11 @@ if(BUILD_SUITE)
|
||||||
add_test(${BINARY_NAME}-suite ${BINARY_NAME}-suite)
|
add_test(${BINARY_NAME}-suite ${BINARY_NAME}-suite)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(BUILD_PYTHON)
|
||||||
|
enable_testing()
|
||||||
|
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/platform/python ${CMAKE_CURRENT_BINARY_DIR}/python)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(BUILD_EXAMPLE)
|
if(BUILD_EXAMPLE)
|
||||||
add_executable(${BINARY_NAME}-example-server ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/example/client-server/server.c)
|
add_executable(${BINARY_NAME}-example-server ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/example/client-server/server.c)
|
||||||
target_link_libraries(${BINARY_NAME}-example-server ${BINARY_NAME})
|
target_link_libraries(${BINARY_NAME}-example-server ${BINARY_NAME})
|
||||||
|
@ -838,6 +847,7 @@ if(NOT QUIET)
|
||||||
message(STATUS " Profiling: ${BUILD_PERF}")
|
message(STATUS " Profiling: ${BUILD_PERF}")
|
||||||
message(STATUS " Test harness: ${BUILD_TEST}")
|
message(STATUS " Test harness: ${BUILD_TEST}")
|
||||||
message(STATUS " Test suite: ${BUILD_SUITE}")
|
message(STATUS " Test suite: ${BUILD_SUITE}")
|
||||||
|
message(STATUS " Python bindings: ${BUILD_PYTHON}")
|
||||||
message(STATUS " Examples: ${BUILD_EXAMPLE}")
|
message(STATUS " Examples: ${BUILD_EXAMPLE}")
|
||||||
message(STATUS "Cores:")
|
message(STATUS "Cores:")
|
||||||
message(STATUS " Libretro core: ${BUILD_LIBRETRO}")
|
message(STATUS " Libretro core: ${BUILD_LIBRETRO}")
|
||||||
|
|
|
@ -75,7 +75,7 @@ union PSR {
|
||||||
unsigned z : 1;
|
unsigned z : 1;
|
||||||
unsigned c : 1;
|
unsigned c : 1;
|
||||||
unsigned v : 1;
|
unsigned v : 1;
|
||||||
unsigned : 20;
|
unsigned unused : 20;
|
||||||
unsigned i : 1;
|
unsigned i : 1;
|
||||||
unsigned f : 1;
|
unsigned f : 1;
|
||||||
unsigned t : 1;
|
unsigned t : 1;
|
||||||
|
@ -85,7 +85,7 @@ union PSR {
|
||||||
unsigned t : 1;
|
unsigned t : 1;
|
||||||
unsigned f : 1;
|
unsigned f : 1;
|
||||||
unsigned i : 1;
|
unsigned i : 1;
|
||||||
unsigned : 20;
|
unsigned unused : 20;
|
||||||
unsigned v : 1;
|
unsigned v : 1;
|
||||||
unsigned c : 1;
|
unsigned c : 1;
|
||||||
unsigned z : 1;
|
unsigned z : 1;
|
||||||
|
|
|
@ -12,6 +12,8 @@ CXX_GUARD_START
|
||||||
|
|
||||||
#include "debugger/debugger.h"
|
#include "debugger/debugger.h"
|
||||||
|
|
||||||
|
#include "arm/arm.h"
|
||||||
|
|
||||||
struct ARMDebugBreakpoint {
|
struct ARMDebugBreakpoint {
|
||||||
uint32_t address;
|
uint32_t address;
|
||||||
bool isSw;
|
bool isSw;
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
#ifndef FLAGS_H
|
||||||
|
#define FLAGS_H
|
||||||
|
|
||||||
|
#ifndef MINIMAL_CORE
|
||||||
|
#cmakedefine MINIMAL_CORE @MINIMAL_CORE@
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// BUILD flags
|
||||||
|
|
||||||
|
#ifndef BUILD_GL
|
||||||
|
#cmakedefine BUILD_GL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BUILD_GLES2
|
||||||
|
#cmakedefine BUILD_GLES2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// COLOR flags
|
||||||
|
|
||||||
|
#ifndef COLOR_16_BIT
|
||||||
|
#cmakedefine COLOR_16_BIT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef COLOR_5_6_5
|
||||||
|
#cmakedefine COLOR_5_6_5
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// M_CORE flags
|
||||||
|
|
||||||
|
#ifndef M_CORE_GBA
|
||||||
|
#cmakedefine M_CORE_GBA
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef M_CORE_GB
|
||||||
|
#cmakedefine M_CORE_GB
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// USE flags
|
||||||
|
|
||||||
|
#ifndef MINIMAL_CORE
|
||||||
|
|
||||||
|
#ifndef USE_DEBUGGERS
|
||||||
|
#cmakedefine USE_DEBUGGERS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef USE_EDITLINE
|
||||||
|
#cmakedefine USE_EDITLINE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef USE_EPOXY
|
||||||
|
#cmakedefine USE_EPOXY
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef USE_FFMPEG
|
||||||
|
#cmakedefine USE_FFMPEG
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef USE_GDB_STUB
|
||||||
|
#cmakedefine USE_GDB_STUB
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef USE_LIBAV
|
||||||
|
#cmakedefine USE_LIBAV
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef USE_LIBZIP
|
||||||
|
#cmakedefine USE_LIBZIP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef USE_LZMA
|
||||||
|
#cmakedefine USE_LZMA
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef USE_MAGICK
|
||||||
|
#cmakedefine USE_MAGICK
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef USE_MINIZIP
|
||||||
|
#cmakedefine USE_MINIZIP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef USE_PNG
|
||||||
|
#cmakedefine USE_PNG
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef USE_PTHREADS
|
||||||
|
#cmakedefine USE_PTHREADS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef USE_ZLIB
|
||||||
|
#cmakedefine USE_ZLIB
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -44,4 +44,18 @@ const char* mLogCategoryName(int category) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mLog(int category, enum mLogLevel level, const char* format, ...) {
|
||||||
|
struct mLogger* context = mLogGetContext();
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
if (context) {
|
||||||
|
context->log(context, category, level, format, args);
|
||||||
|
} else {
|
||||||
|
printf("%s: ", mLogCategoryName(category));
|
||||||
|
vprintf(format, args);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
mLOG_DEFINE_CATEGORY(STATUS, "Status")
|
mLOG_DEFINE_CATEGORY(STATUS, "Status")
|
||||||
|
|
|
@ -32,19 +32,7 @@ int mLogGenerateCategory(const char*);
|
||||||
const char* mLogCategoryName(int);
|
const char* mLogCategoryName(int);
|
||||||
|
|
||||||
ATTRIBUTE_FORMAT(printf, 3, 4)
|
ATTRIBUTE_FORMAT(printf, 3, 4)
|
||||||
static inline void mLog(int category, enum mLogLevel level, const char* format, ...) {
|
void mLog(int category, enum mLogLevel level, const char* format, ...);
|
||||||
struct mLogger* context = mLogGetContext();
|
|
||||||
va_list args;
|
|
||||||
va_start(args, format);
|
|
||||||
if (context) {
|
|
||||||
context->log(context, category, level, format, args);
|
|
||||||
} else {
|
|
||||||
printf("%s: ", mLogCategoryName(category));
|
|
||||||
vprintf(format, args);
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define mLOG(CATEGORY, LEVEL, ...) mLog(_mLOG_CAT_ ## CATEGORY (), mLOG_ ## LEVEL, __VA_ARGS__)
|
#define mLOG(CATEGORY, LEVEL, ...) mLog(_mLOG_CAT_ ## CATEGORY (), mLOG_ ## LEVEL, __VA_ARGS__)
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
CXX_GUARD_START
|
CXX_GUARD_START
|
||||||
|
|
||||||
#include "arm/arm.h"
|
#include "core/cpu.h"
|
||||||
#include "core/log.h"
|
#include "core/log.h"
|
||||||
#include "util/vector.h"
|
#include "util/vector.h"
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ enum mDebuggerState {
|
||||||
enum mWatchpointType {
|
enum mWatchpointType {
|
||||||
WATCHPOINT_WRITE = 1,
|
WATCHPOINT_WRITE = 1,
|
||||||
WATCHPOINT_READ = 2,
|
WATCHPOINT_READ = 2,
|
||||||
WATCHPOINT_RW = WATCHPOINT_WRITE | WATCHPOINT_READ
|
WATCHPOINT_RW = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
enum mBreakpointType {
|
enum mBreakpointType {
|
||||||
|
|
|
@ -122,7 +122,6 @@ void GBHalt(struct LR35902Core* cpu);
|
||||||
struct VFile;
|
struct VFile;
|
||||||
bool GBLoadROM(struct GB* gb, struct VFile* vf);
|
bool GBLoadROM(struct GB* gb, struct VFile* vf);
|
||||||
bool GBLoadSave(struct GB* gb, struct VFile* vf);
|
bool GBLoadSave(struct GB* gb, struct VFile* vf);
|
||||||
void GBYankROM(struct GB* gb);
|
|
||||||
void GBUnloadROM(struct GB* gb);
|
void GBUnloadROM(struct GB* gb);
|
||||||
void GBSynthesizeROM(struct VFile* vf);
|
void GBSynthesizeROM(struct VFile* vf);
|
||||||
|
|
||||||
|
|
|
@ -19,17 +19,17 @@ enum {
|
||||||
GB_VIDEO_HORIZONTAL_PIXELS = 160,
|
GB_VIDEO_HORIZONTAL_PIXELS = 160,
|
||||||
GB_VIDEO_VERTICAL_PIXELS = 144,
|
GB_VIDEO_VERTICAL_PIXELS = 144,
|
||||||
GB_VIDEO_VBLANK_PIXELS = 10,
|
GB_VIDEO_VBLANK_PIXELS = 10,
|
||||||
GB_VIDEO_VERTICAL_TOTAL_PIXELS = GB_VIDEO_VERTICAL_PIXELS + GB_VIDEO_VBLANK_PIXELS,
|
GB_VIDEO_VERTICAL_TOTAL_PIXELS = 154,
|
||||||
|
|
||||||
// TODO: Figure out exact lengths
|
// TODO: Figure out exact lengths
|
||||||
GB_VIDEO_MODE_2_LENGTH = 76,
|
GB_VIDEO_MODE_2_LENGTH = 76,
|
||||||
GB_VIDEO_MODE_3_LENGTH_BASE = 171,
|
GB_VIDEO_MODE_3_LENGTH_BASE = 171,
|
||||||
GB_VIDEO_MODE_0_LENGTH_BASE = 209,
|
GB_VIDEO_MODE_0_LENGTH_BASE = 209,
|
||||||
|
|
||||||
GB_VIDEO_HORIZONTAL_LENGTH = GB_VIDEO_MODE_0_LENGTH_BASE + GB_VIDEO_MODE_2_LENGTH + GB_VIDEO_MODE_3_LENGTH_BASE,
|
GB_VIDEO_HORIZONTAL_LENGTH = 456,
|
||||||
|
|
||||||
GB_VIDEO_MODE_1_LENGTH = GB_VIDEO_HORIZONTAL_LENGTH * GB_VIDEO_VBLANK_PIXELS,
|
GB_VIDEO_MODE_1_LENGTH = 65664,
|
||||||
GB_VIDEO_TOTAL_LENGTH = GB_VIDEO_HORIZONTAL_LENGTH * GB_VIDEO_VERTICAL_TOTAL_PIXELS,
|
GB_VIDEO_TOTAL_LENGTH = 70224,
|
||||||
|
|
||||||
GB_BASE_MAP = 0x1800,
|
GB_BASE_MAP = 0x1800,
|
||||||
GB_SIZE_MAP = 0x0400
|
GB_SIZE_MAP = 0x0400
|
||||||
|
|
|
@ -118,7 +118,7 @@ struct GBAMemory {
|
||||||
uint32_t* wram;
|
uint32_t* wram;
|
||||||
uint32_t* iwram;
|
uint32_t* iwram;
|
||||||
uint32_t* rom;
|
uint32_t* rom;
|
||||||
uint16_t io[SIZE_IO >> 1];
|
uint16_t io[512];
|
||||||
|
|
||||||
struct GBACartridgeHardware hw;
|
struct GBACartridgeHardware hw;
|
||||||
struct GBASavedata savedata;
|
struct GBASavedata savedata;
|
||||||
|
|
|
@ -245,7 +245,7 @@ bool GBAVBMSetStream(struct GBAVBMContext* vbm, struct VFile* vf) {
|
||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
vf->read(vf, &flags, sizeof(flags));
|
vf->read(vf, &flags, sizeof(flags));
|
||||||
if (flags & 2) {
|
if (flags & 2) {
|
||||||
#if USE_ZLIB
|
#ifdef USE_ZLIB
|
||||||
vbm->d.initFrom = INIT_FROM_SAVEGAME;
|
vbm->d.initFrom = INIT_FROM_SAVEGAME;
|
||||||
#else
|
#else
|
||||||
// zlib is needed to parse the savegame
|
// zlib is needed to parse the savegame
|
||||||
|
|
|
@ -20,13 +20,13 @@ enum {
|
||||||
VIDEO_HBLANK_PIXELS = 68,
|
VIDEO_HBLANK_PIXELS = 68,
|
||||||
VIDEO_HDRAW_LENGTH = 1006,
|
VIDEO_HDRAW_LENGTH = 1006,
|
||||||
VIDEO_HBLANK_LENGTH = 226,
|
VIDEO_HBLANK_LENGTH = 226,
|
||||||
VIDEO_HORIZONTAL_LENGTH = VIDEO_HDRAW_LENGTH + VIDEO_HBLANK_LENGTH,
|
VIDEO_HORIZONTAL_LENGTH = 1232,
|
||||||
|
|
||||||
VIDEO_VERTICAL_PIXELS = 160,
|
VIDEO_VERTICAL_PIXELS = 160,
|
||||||
VIDEO_VBLANK_PIXELS = 68,
|
VIDEO_VBLANK_PIXELS = 68,
|
||||||
VIDEO_VERTICAL_TOTAL_PIXELS = VIDEO_VERTICAL_PIXELS + VIDEO_VBLANK_PIXELS,
|
VIDEO_VERTICAL_TOTAL_PIXELS = 228,
|
||||||
|
|
||||||
VIDEO_TOTAL_LENGTH = VIDEO_HORIZONTAL_LENGTH * VIDEO_VERTICAL_TOTAL_PIXELS,
|
VIDEO_TOTAL_LENGTH = 280896,
|
||||||
|
|
||||||
OBJ_HBLANK_FREE_LENGTH = 954,
|
OBJ_HBLANK_FREE_LENGTH = 954,
|
||||||
OBJ_LENGTH = 1210,
|
OBJ_LENGTH = 1210,
|
||||||
|
@ -178,7 +178,7 @@ struct GBAVideo {
|
||||||
// VCOUNT
|
// VCOUNT
|
||||||
int vcount;
|
int vcount;
|
||||||
|
|
||||||
uint16_t palette[SIZE_PALETTE_RAM >> 1];
|
uint16_t palette[512];
|
||||||
uint16_t* vram;
|
uint16_t* vram;
|
||||||
union GBAOAM oam;
|
union GBAOAM oam;
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,36 @@
|
||||||
#include "lr35902/emitter-lr35902.h"
|
#include "lr35902/emitter-lr35902.h"
|
||||||
#include "lr35902/lr35902.h"
|
#include "lr35902/lr35902.h"
|
||||||
|
|
||||||
|
static inline uint16_t LR35902ReadHL(struct LR35902Core* cpu) {
|
||||||
|
uint16_t hl;
|
||||||
|
LOAD_16LE(hl, 0, &cpu->hl);
|
||||||
|
return hl;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void LR35902WriteHL(struct LR35902Core* cpu, uint16_t hl) {
|
||||||
|
STORE_16LE(hl, 0, &cpu->hl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint16_t LR35902ReadBC(struct LR35902Core* cpu) {
|
||||||
|
uint16_t bc;
|
||||||
|
LOAD_16LE(bc, 0, &cpu->bc);
|
||||||
|
return bc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void LR35902WriteBC(struct LR35902Core* cpu, uint16_t bc) {
|
||||||
|
STORE_16LE(bc, 0, &cpu->bc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint16_t LR35902ReadDE(struct LR35902Core* cpu) {
|
||||||
|
uint16_t de;
|
||||||
|
LOAD_16LE(de, 0, &cpu->de);
|
||||||
|
return de;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void LR35902WriteDE(struct LR35902Core* cpu, uint16_t de) {
|
||||||
|
STORE_16LE(de, 0, &cpu->de);
|
||||||
|
}
|
||||||
|
|
||||||
#define DEFINE_INSTRUCTION_LR35902(NAME, BODY) \
|
#define DEFINE_INSTRUCTION_LR35902(NAME, BODY) \
|
||||||
static void _LR35902Instruction ## NAME (struct LR35902Core* cpu) { \
|
static void _LR35902Instruction ## NAME (struct LR35902Core* cpu) { \
|
||||||
UNUSED(cpu); \
|
UNUSED(cpu); \
|
||||||
|
|
|
@ -23,9 +23,9 @@ union FlagRegister {
|
||||||
unsigned n : 1;
|
unsigned n : 1;
|
||||||
unsigned h : 1;
|
unsigned h : 1;
|
||||||
unsigned c : 1;
|
unsigned c : 1;
|
||||||
unsigned : 4;
|
unsigned unused : 4;
|
||||||
#else
|
#else
|
||||||
unsigned : 4;
|
unsigned unused : 4;
|
||||||
unsigned c : 1;
|
unsigned c : 1;
|
||||||
unsigned h : 1;
|
unsigned h : 1;
|
||||||
unsigned n : 1;
|
unsigned n : 1;
|
||||||
|
@ -127,36 +127,6 @@ struct LR35902Core {
|
||||||
struct mCPUComponent** components;
|
struct mCPUComponent** components;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline uint16_t LR35902ReadHL(struct LR35902Core* cpu) {
|
|
||||||
uint16_t hl;
|
|
||||||
LOAD_16LE(hl, 0, &cpu->hl);
|
|
||||||
return hl;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void LR35902WriteHL(struct LR35902Core* cpu, uint16_t hl) {
|
|
||||||
STORE_16LE(hl, 0, &cpu->hl);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint16_t LR35902ReadBC(struct LR35902Core* cpu) {
|
|
||||||
uint16_t bc;
|
|
||||||
LOAD_16LE(bc, 0, &cpu->bc);
|
|
||||||
return bc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void LR35902WriteBC(struct LR35902Core* cpu, uint16_t bc) {
|
|
||||||
STORE_16LE(bc, 0, &cpu->bc);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint16_t LR35902ReadDE(struct LR35902Core* cpu) {
|
|
||||||
uint16_t de;
|
|
||||||
LOAD_16LE(de, 0, &cpu->de);
|
|
||||||
return de;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void LR35902WriteDE(struct LR35902Core* cpu, uint16_t de) {
|
|
||||||
STORE_16LE(de, 0, &cpu->de);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LR35902Init(struct LR35902Core* cpu);
|
void LR35902Init(struct LR35902Core* cpu);
|
||||||
void LR35902Deinit(struct LR35902Core* cpu);
|
void LR35902Deinit(struct LR35902Core* cpu);
|
||||||
void LR35902SetComponents(struct LR35902Core* cpu, struct mCPUComponent* master, int extra, struct mCPUComponent** extras);
|
void LR35902SetComponents(struct LR35902Core* cpu, struct mCPUComponent* master, int extra, struct mCPUComponent** extras);
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
find_program(PYTHON python)
|
||||||
|
|
||||||
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in ${CMAKE_CURRENT_BINARY_DIR}/setup.py)
|
||||||
|
|
||||||
|
add_custom_command(OUTPUT build/lib/${BINARY_NAME}/__init__.py
|
||||||
|
COMMAND BINDIR=${CMAKE_CURRENT_BINARY_DIR}/.. ${PYTHON} ${CMAKE_CURRENT_BINARY_DIR}/setup.py build --build-base ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
DEPENDS ${BINARY_NAME}
|
||||||
|
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/setup.py
|
||||||
|
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/_builder.py)
|
||||||
|
|
||||||
|
add_custom_target(${BINARY_NAME}-py ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/build/lib/${BINARY_NAME}/__init__.py)
|
|
@ -0,0 +1,49 @@
|
||||||
|
#define COMMON_H
|
||||||
|
#define PNG_H
|
||||||
|
#define _SYS_TIME_H
|
||||||
|
#define _SYS_TIME_H_
|
||||||
|
#define _TIME_H
|
||||||
|
#define _TIME_H_
|
||||||
|
|
||||||
|
#define ATTRIBUTE_FORMAT(X, Y, Z)
|
||||||
|
#define DECL_BITFIELD(newtype, oldtype) typedef oldtype newtype
|
||||||
|
#define DECL_BIT(type, name, bit)
|
||||||
|
#define DECL_BITS(type, name, bit, nbits)
|
||||||
|
|
||||||
|
#define CXX_GUARD_START
|
||||||
|
#define CXX_GUARD_END
|
||||||
|
|
||||||
|
typedef int... time_t;
|
||||||
|
typedef int... off_t;
|
||||||
|
typedef ... va_list;
|
||||||
|
typedef ...* png_structp;
|
||||||
|
typedef ...* png_infop;
|
||||||
|
typedef ...* png_unknown_chunkp;
|
||||||
|
|
||||||
|
void free(void*);
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#undef const
|
||||||
|
|
||||||
|
#include "flags.h"
|
||||||
|
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/tile-cache.h"
|
||||||
|
#include "platform/python/vfs-py.h"
|
||||||
|
#include "platform/python/log.h"
|
||||||
|
|
||||||
|
#ifdef USE_PNG
|
||||||
|
#include "util/png-io.h"
|
||||||
|
#endif
|
||||||
|
#ifdef M_CORE_GBA
|
||||||
|
#include "arm/arm.h"
|
||||||
|
#include "gba/gba.h"
|
||||||
|
#include "gba/input.h"
|
||||||
|
#include "gba/renderers/tile-cache.h"
|
||||||
|
#endif
|
||||||
|
#ifdef M_CORE_GB
|
||||||
|
#include "lr35902/lr35902.h"
|
||||||
|
#include "gb/gb.h"
|
||||||
|
#include "gba/input.h"
|
||||||
|
#include "gb/renderers/tile-cache.h"
|
||||||
|
#endif
|
|
@ -0,0 +1,64 @@
|
||||||
|
import cffi
|
||||||
|
import os, os.path
|
||||||
|
import shlex
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
ffi = cffi.FFI()
|
||||||
|
pydir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
srcdir = os.path.join(pydir, "..", "..")
|
||||||
|
bindir = os.environ.get("BINDIR", os.path.join(os.getcwd(), ".."))
|
||||||
|
|
||||||
|
cpp = shlex.split(os.environ.get("CPP", "cc -E"))
|
||||||
|
cppflags = shlex.split(os.environ.get("CPPFLAGS", ""))
|
||||||
|
if __name__ == "__main__":
|
||||||
|
cppflags.extend(sys.argv[1:])
|
||||||
|
cppflags.extend(["-I" + srcdir, "-I" + bindir])
|
||||||
|
|
||||||
|
ffi.set_source("mgba._pylib", """
|
||||||
|
#include "flags.h"
|
||||||
|
#include "util/common.h"
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/log.h"
|
||||||
|
#include "core/tile-cache.h"
|
||||||
|
#include "arm/arm.h"
|
||||||
|
#include "gba/gba.h"
|
||||||
|
#include "gba/input.h"
|
||||||
|
#include "gba/renderers/tile-cache.h"
|
||||||
|
#include "lr35902/lr35902.h"
|
||||||
|
#include "gb/gb.h"
|
||||||
|
#include "gb/renderers/tile-cache.h"
|
||||||
|
#include "util/png-io.h"
|
||||||
|
#include "util/vfs.h"
|
||||||
|
|
||||||
|
struct VFile* VFileFromPython(void* fileobj);
|
||||||
|
|
||||||
|
struct VFilePy {
|
||||||
|
struct VFile d;
|
||||||
|
void* fileobj;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mLogger* mLoggerPythonCreate(void* pyobj);
|
||||||
|
|
||||||
|
struct mLoggerPy {
|
||||||
|
struct mLogger d;
|
||||||
|
void* pyobj;
|
||||||
|
};
|
||||||
|
""", include_dirs=[srcdir],
|
||||||
|
extra_compile_args=cppflags,
|
||||||
|
libraries=["mgba"],
|
||||||
|
library_dirs=[bindir],
|
||||||
|
sources=[os.path.join(pydir, path) for path in ["vfs-py.c", "log.c"]])
|
||||||
|
|
||||||
|
preprocessed = subprocess.check_output(cpp + ["-fno-inline", "-P"] + cppflags + [os.path.join(pydir, "_builder.h")], universal_newlines=True)
|
||||||
|
|
||||||
|
lines = []
|
||||||
|
for line in preprocessed.splitlines():
|
||||||
|
line = line.strip()
|
||||||
|
if line.startswith('#'):
|
||||||
|
continue
|
||||||
|
lines.append(line)
|
||||||
|
ffi.cdef('\n'.join(lines))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
ffi.compile()
|
|
@ -0,0 +1,27 @@
|
||||||
|
/* Copyright (c) 2013-2016 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#include "core/log.h"
|
||||||
|
|
||||||
|
struct mLoggerPy {
|
||||||
|
struct mLogger d;
|
||||||
|
void* pyobj;
|
||||||
|
};
|
||||||
|
|
||||||
|
void _pyLog(void* logger, int category, enum mLogLevel level, const char* message);
|
||||||
|
|
||||||
|
static void _pyLogShim(struct mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) {
|
||||||
|
struct mLoggerPy* pylogger = (struct mLoggerPy*) logger;
|
||||||
|
char message[256] = {0};
|
||||||
|
vsnprintf(message, sizeof(message) - 1, format, args);
|
||||||
|
_pyLog(pylogger, category, level, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mLogger* mLoggerPythonCreate(void* pyobj) {
|
||||||
|
struct mLoggerPy* logger = malloc(sizeof(*logger));
|
||||||
|
logger->d.log = _pyLogShim;
|
||||||
|
logger->pyobj = pyobj;
|
||||||
|
return &logger->d;
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
/* Copyright (c) 2013-2016 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#include "core/log.h"
|
||||||
|
|
||||||
|
struct mLoggerPy {
|
||||||
|
struct mLogger d;
|
||||||
|
void* pyobj;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mLogger* mLoggerPythonCreate(void* pyobj);
|
||||||
|
|
||||||
|
extern "Python+C" void _pyLog(void* logger, int category, enum mLogLevel level, const char* message);
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Copyright (c) 2013-2016 Jeffrey Pfau
|
||||||
|
#
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
from ._pylib import ffi, lib
|
||||||
|
|
||||||
|
class _ARMRegisters:
|
||||||
|
def __init__(self, cpu):
|
||||||
|
self._cpu = cpu
|
||||||
|
|
||||||
|
def __getitem__(self, r):
|
||||||
|
if r > lib.ARM_PC:
|
||||||
|
raise IndexError("Register out of range")
|
||||||
|
return self._cpu._native.gprs[r]
|
||||||
|
|
||||||
|
def __setitem__(self, r, value):
|
||||||
|
if r >= lib.ARM_PC:
|
||||||
|
raise IndexError("Register out of range")
|
||||||
|
self._cpu._native.gprs[r] = value
|
||||||
|
|
||||||
|
class ARMCore:
|
||||||
|
def __init__(self, native):
|
||||||
|
self._native = ffi.cast("struct ARMCore*", native)
|
||||||
|
self.gprs = _ARMRegisters(self)
|
||||||
|
self.cpsr = self._native.cpsr
|
||||||
|
self.spsr = self._native.spsr
|
||||||
|
|
|
@ -0,0 +1,162 @@
|
||||||
|
# Copyright (c) 2013-2016 Jeffrey Pfau
|
||||||
|
#
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
from ._pylib import ffi, lib
|
||||||
|
from . import tile
|
||||||
|
from cached_property import cached_property
|
||||||
|
|
||||||
|
def find(path):
|
||||||
|
core = lib.mCoreFind(path.encode('UTF-8'))
|
||||||
|
if core == ffi.NULL:
|
||||||
|
return None
|
||||||
|
return Core._init(core)
|
||||||
|
|
||||||
|
def findVF(vf):
|
||||||
|
core = lib.mCoreFindVF(vf.handle)
|
||||||
|
if core == ffi.NULL:
|
||||||
|
return None
|
||||||
|
return Core._init(core)
|
||||||
|
|
||||||
|
def loadPath(path):
|
||||||
|
core = find(path)
|
||||||
|
if not core or not core.loadFile(path):
|
||||||
|
return None
|
||||||
|
return core
|
||||||
|
|
||||||
|
def loadVF(vf):
|
||||||
|
core = findVF(vf)
|
||||||
|
if not core or not core.loadROM(vf):
|
||||||
|
return None
|
||||||
|
return core
|
||||||
|
|
||||||
|
def needsReset(f):
|
||||||
|
def wrapper(self, *args, **kwargs):
|
||||||
|
if not self._wasReset:
|
||||||
|
raise RuntimeError("Core must be reset first")
|
||||||
|
return f(self, *args, **kwargs)
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
class Core(object):
|
||||||
|
def __init__(self, native):
|
||||||
|
self._core = native
|
||||||
|
self._wasReset = False
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def tiles(self):
|
||||||
|
return tile.TileView(self)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _init(cls, native):
|
||||||
|
core = ffi.gc(native, native.deinit)
|
||||||
|
success = bool(core.init(core))
|
||||||
|
if not success:
|
||||||
|
raise RuntimeError("Failed to initialize core")
|
||||||
|
if hasattr(cls, 'PLATFORM_GBA') and core.platform(core) == cls.PLATFORM_GBA:
|
||||||
|
return GBA(core)
|
||||||
|
if hasattr(cls, 'PLATFORM_GB') and core.platform(core) == cls.PLATFORM_GB:
|
||||||
|
return GB(core)
|
||||||
|
return Core(core)
|
||||||
|
|
||||||
|
def _deinit(self):
|
||||||
|
self._core.deinit(self._core)
|
||||||
|
|
||||||
|
def loadFile(self, path):
|
||||||
|
return bool(lib.mCoreLoadFile(self._core, path.encode('UTF-8')))
|
||||||
|
|
||||||
|
def isROM(self, vf):
|
||||||
|
return bool(self._core.isROM(vf.handle))
|
||||||
|
|
||||||
|
def loadROM(self, vf):
|
||||||
|
return bool(self._core.loadROM(self._core, vf.handle))
|
||||||
|
|
||||||
|
def loadSave(self, vf):
|
||||||
|
return bool(self._core.loadSave(self._core, vf.handle))
|
||||||
|
|
||||||
|
def loadTemporarySave(self, vf):
|
||||||
|
return bool(self._core.loadTemporarySave(self._core, vf.handle))
|
||||||
|
|
||||||
|
def loadPatch(self, vf):
|
||||||
|
return bool(self._core.loadPatch(self._core, vf.handle))
|
||||||
|
|
||||||
|
def autoloadSave(self):
|
||||||
|
return bool(lib.mCoreAutoloadSave(self._core))
|
||||||
|
|
||||||
|
def autoloadPatch(self):
|
||||||
|
return bool(lib.mCoreAutoloadPatch(self._core))
|
||||||
|
|
||||||
|
def platform(self):
|
||||||
|
return self._core.platform(self._core)
|
||||||
|
|
||||||
|
def desiredVideoDimensions(self):
|
||||||
|
width = ffi.new("unsigned*")
|
||||||
|
height = ffi.new("unsigned*")
|
||||||
|
self._core.desiredVideoDimensions(self._core, width, height)
|
||||||
|
return width[0], height[0]
|
||||||
|
|
||||||
|
def setVideoBuffer(self, image):
|
||||||
|
self._core.setVideoBuffer(self._core, image.buffer, image.stride)
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self._core.reset(self._core)
|
||||||
|
self._wasReset = True
|
||||||
|
|
||||||
|
@needsReset
|
||||||
|
def runFrame(self):
|
||||||
|
self._core.runFrame(self._core)
|
||||||
|
|
||||||
|
@needsReset
|
||||||
|
def runLoop(self):
|
||||||
|
self._core.runLoop(self._core)
|
||||||
|
|
||||||
|
@needsReset
|
||||||
|
def step(self):
|
||||||
|
self._core.step(self._core)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _keysToInt(*args, **kwargs):
|
||||||
|
keys = 0
|
||||||
|
if 'raw' in kwargs:
|
||||||
|
keys = kwargs['raw']
|
||||||
|
for key in args:
|
||||||
|
keys |= 1 << key
|
||||||
|
return keys
|
||||||
|
|
||||||
|
def setKeys(self, *args, **kwargs):
|
||||||
|
self._core.setKeys(self._core, self._keysToInt(*args, **kwargs))
|
||||||
|
|
||||||
|
def addKeys(self, *args, **kwargs):
|
||||||
|
self._core.addKeys(self._core, self._keysToInt(*args, **kwargs))
|
||||||
|
|
||||||
|
def clearKeys(self, *args, **kwargs):
|
||||||
|
self._core.clearKeys(self._core, self._keysToInt(*args, **kwargs))
|
||||||
|
|
||||||
|
@needsReset
|
||||||
|
def frameCounter(self):
|
||||||
|
return self._core.frameCounter(self._core)
|
||||||
|
|
||||||
|
def frameCycles(self):
|
||||||
|
return self._core.frameCycles(self._core)
|
||||||
|
|
||||||
|
def frequency(self):
|
||||||
|
return self._core.frequency(self._core)
|
||||||
|
|
||||||
|
def getGameTitle(self):
|
||||||
|
title = ffi.new("char[16]")
|
||||||
|
self._core.getGameTitle(self._core, title)
|
||||||
|
return ffi.string(title, 16).decode("ascii")
|
||||||
|
|
||||||
|
def getGameCode(self):
|
||||||
|
code = ffi.new("char[12]")
|
||||||
|
self._core.getGameCode(self._core, code)
|
||||||
|
return ffi.string(code, 12).decode("ascii")
|
||||||
|
|
||||||
|
if hasattr(lib, 'PLATFORM_GBA'):
|
||||||
|
from .gba import GBA
|
||||||
|
Core.PLATFORM_GBA = lib.PLATFORM_GBA
|
||||||
|
|
||||||
|
if hasattr(lib, 'PLATFORM_GB'):
|
||||||
|
from .gb import GB
|
||||||
|
from .lr35902 import LR35902Core
|
||||||
|
Core.PLATFORM_GB = lib.PLATFORM_GB
|
|
@ -0,0 +1,81 @@
|
||||||
|
# Copyright (c) 2013-2016 Jeffrey Pfau
|
||||||
|
#
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
from ._pylib import ffi, lib
|
||||||
|
from .lr35902 import LR35902Core
|
||||||
|
from .core import Core, needsReset
|
||||||
|
from .memory import Memory
|
||||||
|
from .tile import Sprite
|
||||||
|
|
||||||
|
class GB(Core):
|
||||||
|
KEY_A = lib.GBA_KEY_A
|
||||||
|
KEY_B = lib.GBA_KEY_B
|
||||||
|
KEY_SELECT = lib.GBA_KEY_SELECT
|
||||||
|
KEY_START = lib.GBA_KEY_START
|
||||||
|
KEY_DOWN = lib.GBA_KEY_DOWN
|
||||||
|
KEY_UP = lib.GBA_KEY_UP
|
||||||
|
KEY_LEFT = lib.GBA_KEY_LEFT
|
||||||
|
KEY_RIGHT = lib.GBA_KEY_RIGHT
|
||||||
|
|
||||||
|
def __init__(self, native):
|
||||||
|
super(GB, self).__init__(native)
|
||||||
|
self._native = ffi.cast("struct GB*", native.board)
|
||||||
|
self.sprites = GBObjs(self)
|
||||||
|
self.cpu = LR35902Core(self._core.cpu)
|
||||||
|
|
||||||
|
@needsReset
|
||||||
|
def _initTileCache(self, cache):
|
||||||
|
lib.GBVideoTileCacheInit(cache)
|
||||||
|
lib.GBVideoTileCacheAssociate(cache, ffi.addressof(self._native.video))
|
||||||
|
|
||||||
|
def _deinitTileCache(self, cache):
|
||||||
|
self._native.video.renderer.cache = ffi.NULL
|
||||||
|
lib.mTileCacheDeinit(cache)
|
||||||
|
|
||||||
|
class GBMemory(Memory):
|
||||||
|
def __init__(self, core):
|
||||||
|
super(GBMemory, self).__init__(core, 0x10000)
|
||||||
|
|
||||||
|
self.cart = Memory(core, lib.GB_SIZE_CART_BANK0 * 2, lib.GB_BASE_CART_BANK0)
|
||||||
|
self.vram = Memory(core, lib.GB_SIZE_VRAM, lib.GB_BASE_VRAM)
|
||||||
|
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.oam = Memory(core, lib.GB_SIZE_OAM, lib.GB_BASE_OAM)
|
||||||
|
self.io = Memory(core, lib.GB_SIZE_IO, lib.GB_BASE_IO)
|
||||||
|
self.hram = Memory(core, lib.GB_SIZE_HRAM, lib.GB_BASE_HRAM)
|
||||||
|
|
||||||
|
class GBSprite(Sprite):
|
||||||
|
PALETTE_BASE = 8,
|
||||||
|
|
||||||
|
def __init__(self, obj, core):
|
||||||
|
self.x = obj.x
|
||||||
|
self.y = obj.y
|
||||||
|
self.tile = obj.tile
|
||||||
|
self._attr = obj.attr
|
||||||
|
self.width = 8
|
||||||
|
lcdc = core._native.memory.io[0x40]
|
||||||
|
self.height = 16 if lcdc & 4 else 8
|
||||||
|
if core._native.model >= lib.GB_MODEL_CGB:
|
||||||
|
if self._attr & 8:
|
||||||
|
self.tile += 512
|
||||||
|
self.paletteId = self._attr & 7
|
||||||
|
else:
|
||||||
|
self.paletteId = (self._attr >> 4) & 1
|
||||||
|
|
||||||
|
|
||||||
|
class GBObjs:
|
||||||
|
def __init__(self, core):
|
||||||
|
self._core = core
|
||||||
|
self._obj = core._native.video.oam.obj
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return 40
|
||||||
|
|
||||||
|
def __getitem__(self, index):
|
||||||
|
if index >= len(self):
|
||||||
|
raise IndexError()
|
||||||
|
sprite = GBSprite(self._obj[index], self._core)
|
||||||
|
sprite.constitute(self._core.tiles, 0, 0)
|
||||||
|
return sprite
|
|
@ -0,0 +1,96 @@
|
||||||
|
# Copyright (c) 2013-2016 Jeffrey Pfau
|
||||||
|
#
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
from ._pylib import ffi, lib
|
||||||
|
from .arm import ARMCore
|
||||||
|
from .core import Core, needsReset
|
||||||
|
from .tile import Sprite
|
||||||
|
from .memory import Memory
|
||||||
|
|
||||||
|
class GBA(Core):
|
||||||
|
KEY_A = lib.GBA_KEY_A
|
||||||
|
KEY_B = lib.GBA_KEY_B
|
||||||
|
KEY_SELECT = lib.GBA_KEY_SELECT
|
||||||
|
KEY_START = lib.GBA_KEY_START
|
||||||
|
KEY_DOWN = lib.GBA_KEY_DOWN
|
||||||
|
KEY_UP = lib.GBA_KEY_UP
|
||||||
|
KEY_LEFT = lib.GBA_KEY_LEFT
|
||||||
|
KEY_RIGHT = lib.GBA_KEY_RIGHT
|
||||||
|
KEY_L = lib.GBA_KEY_L
|
||||||
|
KEY_R = lib.GBA_KEY_R
|
||||||
|
|
||||||
|
def __init__(self, native):
|
||||||
|
super(GBA, self).__init__(native)
|
||||||
|
self._native = ffi.cast("struct GBA*", native.board)
|
||||||
|
self.sprites = GBAObjs(self)
|
||||||
|
self.cpu = ARMCore(self._core.cpu)
|
||||||
|
|
||||||
|
@needsReset
|
||||||
|
def _initTileCache(self, cache):
|
||||||
|
lib.GBAVideoTileCacheInit(cache)
|
||||||
|
lib.GBAVideoTileCacheAssociate(cache, ffi.addressof(self._native.video))
|
||||||
|
|
||||||
|
def _deinitTileCache(self, cache):
|
||||||
|
self._native.video.renderer.cache = ffi.NULL
|
||||||
|
lib.mTileCacheDeinit(cache)
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
super(GBA, self).reset()
|
||||||
|
self.memory = GBAMemory(self._core, self._native.memory.romSize)
|
||||||
|
|
||||||
|
class GBAMemory(Memory):
|
||||||
|
def __init__(self, core, romSize=lib.SIZE_CART0):
|
||||||
|
super(GBAMemory, self).__init__(core, 0x100000000)
|
||||||
|
|
||||||
|
self.bios = Memory(core, lib.SIZE_BIOS, lib.BASE_BIOS)
|
||||||
|
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.io = Memory(core, lib.SIZE_IO, lib.BASE_IO)
|
||||||
|
self.palette = Memory(core, lib.SIZE_PALETTE_RAM, lib.BASE_PALETTE_RAM)
|
||||||
|
self.vram = Memory(core, lib.SIZE_VRAM, lib.BASE_VRAM)
|
||||||
|
self.oam = Memory(core, lib.SIZE_OAM, lib.BASE_OAM)
|
||||||
|
self.cart0 = Memory(core, romSize, lib.BASE_CART0)
|
||||||
|
self.cart1 = Memory(core, romSize, lib.BASE_CART1)
|
||||||
|
self.cart2 = Memory(core, romSize, lib.BASE_CART2)
|
||||||
|
self.cart = self.cart0
|
||||||
|
self.rom = self.cart0
|
||||||
|
self.sram = Memory(core, lib.SIZE_CART_SRAM, lib.BASE_CART_SRAM)
|
||||||
|
|
||||||
|
class GBASprite(Sprite):
|
||||||
|
TILE_BASE = 0x800, 0x400
|
||||||
|
PALETTE_BASE = 0x10, 1
|
||||||
|
|
||||||
|
def __init__(self, obj):
|
||||||
|
self._a = obj.a
|
||||||
|
self._b = obj.b
|
||||||
|
self._c = obj.c
|
||||||
|
self.x = self._b & 0x1FF
|
||||||
|
self.y = self._a & 0xFF
|
||||||
|
self._shape = self._a >> 14
|
||||||
|
self._size = self._b >> 14
|
||||||
|
self._256Color = bool(self._a & 0x2000)
|
||||||
|
self.width, self.height = lib.GBAVideoObjSizes[self._shape * 4 + self._size]
|
||||||
|
self.tile = self._c & 0x3FF
|
||||||
|
if self._256Color:
|
||||||
|
self.paletteId = 0
|
||||||
|
self.tile >>= 1
|
||||||
|
else:
|
||||||
|
self.paletteId = self._c >> 12
|
||||||
|
|
||||||
|
class GBAObjs:
|
||||||
|
def __init__(self, core):
|
||||||
|
self._core = core
|
||||||
|
self._obj = core._native.video.oam.obj
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return 128
|
||||||
|
|
||||||
|
def __getitem__(self, index):
|
||||||
|
if index >= len(self):
|
||||||
|
raise IndexError()
|
||||||
|
sprite = GBASprite(self._obj[index])
|
||||||
|
map1D = bool(self._core._native.memory.io[0] & 0x40)
|
||||||
|
sprite.constitute(self._core.tiles, 0 if map1D else 0x20, int(sprite._256Color))
|
||||||
|
return sprite
|
|
@ -0,0 +1,69 @@
|
||||||
|
# Copyright (c) 2013-2016 Jeffrey Pfau
|
||||||
|
#
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
from ._pylib import ffi, lib
|
||||||
|
from . import png
|
||||||
|
|
||||||
|
class Image:
|
||||||
|
def __init__(self, width, height, stride=0):
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
self.stride = stride
|
||||||
|
self.constitute()
|
||||||
|
|
||||||
|
def constitute(self):
|
||||||
|
if self.stride <= 0:
|
||||||
|
self.stride = self.width
|
||||||
|
self.buffer = ffi.new("color_t[{}]".format(self.stride * self.height))
|
||||||
|
|
||||||
|
def savePNG(self, f):
|
||||||
|
p = png.PNG(f)
|
||||||
|
success = p.writeHeader(self)
|
||||||
|
success = success and p.writePixels(self)
|
||||||
|
p.writeClose()
|
||||||
|
return success
|
||||||
|
|
||||||
|
def u16ToU32(c):
|
||||||
|
r = c & 0x1F
|
||||||
|
g = (c >> 5) & 0x1F
|
||||||
|
b = (c >> 10) & 0x1F
|
||||||
|
a = (c >> 15) & 1
|
||||||
|
abgr = r << 3
|
||||||
|
abgr |= g << 11
|
||||||
|
abgr |= b << 19
|
||||||
|
abgr |= (a * 0xFF) << 24
|
||||||
|
return abgr
|
||||||
|
|
||||||
|
def u32ToU16(c):
|
||||||
|
r = (c >> 3) & 0x1F
|
||||||
|
g = (c >> 11) & 0x1F
|
||||||
|
b = (c >> 19) & 0x1F
|
||||||
|
a = c >> 31
|
||||||
|
abgr = r
|
||||||
|
abgr |= g << 5
|
||||||
|
abgr |= b << 10
|
||||||
|
abgr |= a << 15
|
||||||
|
return abgr
|
||||||
|
|
||||||
|
if ffi.sizeof("color_t") == 2:
|
||||||
|
def colorToU16(c):
|
||||||
|
return c
|
||||||
|
|
||||||
|
colorToU32 = u16ToU32
|
||||||
|
|
||||||
|
def u16ToColor(c):
|
||||||
|
return c
|
||||||
|
|
||||||
|
u32ToColor = u32ToU16
|
||||||
|
else:
|
||||||
|
def colorToU32(c):
|
||||||
|
return c
|
||||||
|
|
||||||
|
colorToU16 = u32ToU16
|
||||||
|
|
||||||
|
def u32ToColor(c):
|
||||||
|
return c
|
||||||
|
|
||||||
|
u16ToColor = u16ToU32
|
|
@ -0,0 +1,38 @@
|
||||||
|
# Copyright (c) 2013-2016 Jeffrey Pfau
|
||||||
|
#
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
from ._pylib import ffi, lib
|
||||||
|
|
||||||
|
@ffi.def_extern()
|
||||||
|
def _pyLog(logger, category, level, message):
|
||||||
|
l = ffi.cast("struct mLoggerPy*", logger)
|
||||||
|
ffi.from_handle(l.pyobj).log(category, level, ffi.string(message).decode('UTF-8'))
|
||||||
|
|
||||||
|
def installDefault(logger):
|
||||||
|
lib.mLogSetDefaultLogger(logger._native)
|
||||||
|
|
||||||
|
class Logger(object):
|
||||||
|
FATAL = lib.mLOG_FATAL
|
||||||
|
DEBUG = lib.mLOG_DEBUG
|
||||||
|
INFO = lib.mLOG_INFO
|
||||||
|
WARN = lib.mLOG_WARN
|
||||||
|
ERROR = lib.mLOG_ERROR
|
||||||
|
STUB = lib.mLOG_STUB
|
||||||
|
GAME_ERROR = lib.mLOG_GAME_ERROR
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._handle = ffi.new_handle(self)
|
||||||
|
self._native = ffi.gc(lib.mLoggerPythonCreate(self._handle), lib.free)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def categoryName(category):
|
||||||
|
return ffi.string(lib.mLogCategoryName(category)).decode('UTF-8')
|
||||||
|
|
||||||
|
def log(self, category, level, message):
|
||||||
|
print("{}: {}".format(self.categoryName(category), message))
|
||||||
|
|
||||||
|
class NullLogger(Logger):
|
||||||
|
def log(self, category, level, message):
|
||||||
|
pass
|
|
@ -0,0 +1,123 @@
|
||||||
|
# Copyright (c) 2013-2016 Jeffrey Pfau
|
||||||
|
#
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
from ._pylib import ffi, lib
|
||||||
|
|
||||||
|
class LR35902Core:
|
||||||
|
def __init__(self, native):
|
||||||
|
self._native = ffi.cast("struct LR35902Core*", native)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def a(self):
|
||||||
|
return self._native.a
|
||||||
|
|
||||||
|
@property
|
||||||
|
def b(self):
|
||||||
|
return self._native.b
|
||||||
|
|
||||||
|
@property
|
||||||
|
def c(self):
|
||||||
|
return self._native.c
|
||||||
|
|
||||||
|
@property
|
||||||
|
def d(self):
|
||||||
|
return self._native.d
|
||||||
|
|
||||||
|
@property
|
||||||
|
def e(self):
|
||||||
|
return self._native.e
|
||||||
|
|
||||||
|
@property
|
||||||
|
def f(self):
|
||||||
|
return self._native.f
|
||||||
|
|
||||||
|
@property
|
||||||
|
def h(self):
|
||||||
|
return self._native.h
|
||||||
|
|
||||||
|
@property
|
||||||
|
def l(self):
|
||||||
|
return self._native.l
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sp(self):
|
||||||
|
return self._native.sp
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pc(self):
|
||||||
|
return self._native.pc
|
||||||
|
|
||||||
|
@property
|
||||||
|
def af(self):
|
||||||
|
return (self.a << 8) | self.f
|
||||||
|
|
||||||
|
@property
|
||||||
|
def bc(self):
|
||||||
|
return (self.b << 8) | self.c
|
||||||
|
|
||||||
|
@property
|
||||||
|
def de(self):
|
||||||
|
return (self.d << 8) | self.e
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hl(self):
|
||||||
|
return (self.h << 8) | self.l
|
||||||
|
|
||||||
|
@a.setter
|
||||||
|
def a(self, value):
|
||||||
|
self._native.a = value
|
||||||
|
|
||||||
|
@b.setter
|
||||||
|
def b(self, value):
|
||||||
|
self._native.b = value
|
||||||
|
|
||||||
|
@c.setter
|
||||||
|
def c(self, value):
|
||||||
|
self._native.c = value
|
||||||
|
|
||||||
|
@d.setter
|
||||||
|
def d(self, value):
|
||||||
|
self._native.d = value
|
||||||
|
|
||||||
|
@e.setter
|
||||||
|
def e(self, value):
|
||||||
|
self._native.e = value
|
||||||
|
|
||||||
|
@f.setter
|
||||||
|
def f(self, value):
|
||||||
|
self._native.f.packed = value
|
||||||
|
self._native.f.unused = 0
|
||||||
|
|
||||||
|
@h.setter
|
||||||
|
def h(self, value):
|
||||||
|
self._native.h = value
|
||||||
|
|
||||||
|
@l.setter
|
||||||
|
def l(self, value):
|
||||||
|
self._native.l = value
|
||||||
|
|
||||||
|
@sp.setter
|
||||||
|
def sp(self, value):
|
||||||
|
self._native.sp = value
|
||||||
|
|
||||||
|
@af.setter
|
||||||
|
def af(self, value):
|
||||||
|
self.a = value >> 8
|
||||||
|
self.f = value & 0xFF
|
||||||
|
|
||||||
|
@bc.setter
|
||||||
|
def bc(self, value):
|
||||||
|
self.b = value >> 8
|
||||||
|
self.c = value & 0xFF
|
||||||
|
|
||||||
|
@de.setter
|
||||||
|
def de(self, value):
|
||||||
|
self.d = value >> 8
|
||||||
|
self.e = value & 0xFF
|
||||||
|
|
||||||
|
@hl.setter
|
||||||
|
def hl(self, value):
|
||||||
|
self.h = value >> 8
|
||||||
|
self.l = value & 0xFF
|
|
@ -0,0 +1,83 @@
|
||||||
|
# Copyright (c) 2013-2016 Jeffrey Pfau
|
||||||
|
#
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
from ._pylib import ffi, lib
|
||||||
|
|
||||||
|
class MemoryView(object):
|
||||||
|
def __init__(self, core, width, size, base=0, sign="u"):
|
||||||
|
self._core = core
|
||||||
|
self._width = width
|
||||||
|
self._size = size
|
||||||
|
self._base = base
|
||||||
|
self._busRead = getattr(self._core, "busRead" + str(width * 8))
|
||||||
|
self._busWrite = getattr(self._core, "busWrite" + str(width * 8))
|
||||||
|
self._rawRead = getattr(self._core, "rawRead" + str(width * 8))
|
||||||
|
self._rawWrite = 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
|
||||||
|
if sign == "u" or sign == "unsigned":
|
||||||
|
self._type = "uint{}_t".format(width * 8)
|
||||||
|
elif sign == "i" or sign == "s" or sign == "signed":
|
||||||
|
self._type = "int{}_t".format(width * 8)
|
||||||
|
else:
|
||||||
|
raise ValueError("Invalid sign type: '{}'".format(sign))
|
||||||
|
|
||||||
|
def _addrCheck(self, address):
|
||||||
|
if isinstance(address, slice):
|
||||||
|
start = address.start or 0
|
||||||
|
stop = self._size - self._width if address.stop is None else address.stop
|
||||||
|
else:
|
||||||
|
start = address
|
||||||
|
stop = address + self._width
|
||||||
|
if start >= self._size or stop > self._size:
|
||||||
|
raise IndexError()
|
||||||
|
if start < 0 or stop < 0:
|
||||||
|
raise IndexError()
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return self._size
|
||||||
|
|
||||||
|
def __getitem__(self, address):
|
||||||
|
self._addrCheck(address)
|
||||||
|
if isinstance(address, slice):
|
||||||
|
start = address.start or 0
|
||||||
|
stop = self._size - self._width if address.stop is None else address.stop
|
||||||
|
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)]
|
||||||
|
else:
|
||||||
|
return int(ffi.cast(self._type, self._busRead(self._core, self._base + address)))
|
||||||
|
|
||||||
|
def __setitem__(self, address, value):
|
||||||
|
self._addrCheck(address)
|
||||||
|
if isinstance(address, slice):
|
||||||
|
start = address.start or 0
|
||||||
|
stop = self._size - self._width if address.stop is None else address.stop
|
||||||
|
step = address.step or self._width
|
||||||
|
for a in range(start, stop, step):
|
||||||
|
self._busWrite(self._core, self._base + a, value[a] & self._mask)
|
||||||
|
else:
|
||||||
|
self._busWrite(self._core, self._base + address, value & self._mask)
|
||||||
|
|
||||||
|
def rawRead(self, address, segment=-1):
|
||||||
|
self._addrCheck(address)
|
||||||
|
return int(ffi.cast(self._type, self._rawRead(self._core, self._base + address, segment)))
|
||||||
|
|
||||||
|
def rawWrite(self, address, value, segment=-1):
|
||||||
|
self._addrCheck(address)
|
||||||
|
self._rawWrite(self._core, self._base + address, segment, value & self._mask)
|
||||||
|
|
||||||
|
class Memory(object):
|
||||||
|
def __init__(self, core, size, base=0):
|
||||||
|
self.size = size
|
||||||
|
self.base = base
|
||||||
|
|
||||||
|
self.u8 = MemoryView(core, 1, size, base, "u")
|
||||||
|
self.u16 = MemoryView(core, 2, size, base, "u")
|
||||||
|
self.u32 = MemoryView(core, 4, size, base, "u")
|
||||||
|
self.s8 = MemoryView(core, 1, size, base, "s")
|
||||||
|
self.s16 = MemoryView(core, 2, size, base, "s")
|
||||||
|
self.s32 = MemoryView(core, 4, size, base, "s")
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return self._size
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Copyright (c) 2013-2016 Jeffrey Pfau
|
||||||
|
#
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
from ._pylib import ffi, lib
|
||||||
|
from . import vfs
|
||||||
|
|
||||||
|
class PNG:
|
||||||
|
def __init__(self, f):
|
||||||
|
self.vf = vfs.open(f)
|
||||||
|
|
||||||
|
def writeHeader(self, image):
|
||||||
|
self._png = lib.PNGWriteOpen(self.vf.handle)
|
||||||
|
self._info = lib.PNGWriteHeader(self._png, image.width, image.height)
|
||||||
|
return self._info != ffi.NULL
|
||||||
|
|
||||||
|
def writePixels(self, image):
|
||||||
|
return lib.PNGWritePixels(self._png, image.width, image.height, image.stride, image.buffer)
|
||||||
|
|
||||||
|
def writeClose(self):
|
||||||
|
lib.PNGWriteClose(self._png, self._info)
|
||||||
|
del self._png
|
||||||
|
del self._info
|
|
@ -0,0 +1,57 @@
|
||||||
|
# Copyright (c) 2013-2016 Jeffrey Pfau
|
||||||
|
#
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
from ._pylib import ffi, lib
|
||||||
|
from . import image
|
||||||
|
|
||||||
|
class Tile:
|
||||||
|
def __init__(self, data):
|
||||||
|
self.buffer = data
|
||||||
|
|
||||||
|
def toImage(self):
|
||||||
|
i = image.Image(8, 8)
|
||||||
|
self.composite(i, 0, 0)
|
||||||
|
return i
|
||||||
|
|
||||||
|
def composite(self, i, x, y):
|
||||||
|
for iy in range(8):
|
||||||
|
for ix in range(8):
|
||||||
|
i.buffer[ix + x + (iy + y) * i.stride] = image.u16ToColor(self.buffer[ix + iy * 8])
|
||||||
|
|
||||||
|
class TileView:
|
||||||
|
def __init__(self, core):
|
||||||
|
self.core = core
|
||||||
|
self.cache = ffi.gc(ffi.new("struct mTileCache*"), core._deinitTileCache)
|
||||||
|
core._initTileCache(self.cache)
|
||||||
|
lib.mTileCacheSetPalette(self.cache, 0)
|
||||||
|
self.paletteSet = 0
|
||||||
|
|
||||||
|
def getTile(self, tile, palette):
|
||||||
|
return Tile(lib.mTileCacheGetTile(self.cache, tile, palette))
|
||||||
|
|
||||||
|
def setPalette(self, paletteSet):
|
||||||
|
if paletteSet > 1 or paletteSet < 0:
|
||||||
|
raise IndexError("Palette Set ID out of bounds")
|
||||||
|
lib.mTileCacheSetPalette(self.cache, paletteSet)
|
||||||
|
self.paletteSet = paletteSet
|
||||||
|
|
||||||
|
class Sprite(object):
|
||||||
|
TILE_BASE = 0, 0
|
||||||
|
PALETTE_BASE = 0, 0
|
||||||
|
|
||||||
|
def constitute(self, tileView, tilePitch, paletteSet):
|
||||||
|
oldPaletteSet = tileView.paletteSet
|
||||||
|
tileView.setPalette(paletteSet)
|
||||||
|
i = image.Image(self.width, self.height)
|
||||||
|
tileId = self.tile + self.TILE_BASE[paletteSet]
|
||||||
|
for y in range(self.height // 8):
|
||||||
|
for x in range(self.width // 8):
|
||||||
|
tile = tileView.getTile(tileId, self.paletteId + self.PALETTE_BASE[paletteSet])
|
||||||
|
tile.composite(i, x * 8, y * 8)
|
||||||
|
tileId += 1
|
||||||
|
if tilePitch:
|
||||||
|
tileId += tilePitch - self.width // 8
|
||||||
|
self.image = i
|
||||||
|
tileView.setPalette(oldPaletteSet)
|
|
@ -0,0 +1,130 @@
|
||||||
|
# Copyright (c) 2013-2016 Jeffrey Pfau
|
||||||
|
#
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
from ._pylib import ffi, lib
|
||||||
|
import mmap
|
||||||
|
import os
|
||||||
|
|
||||||
|
@ffi.def_extern()
|
||||||
|
def _vfpClose(vf):
|
||||||
|
vfp = ffi.cast("struct VFilePy*", vf)
|
||||||
|
ffi.from_handle(vfp.fileobj).close()
|
||||||
|
|
||||||
|
@ffi.def_extern()
|
||||||
|
def _vfpSeek(vf, offset, whence):
|
||||||
|
vfp = ffi.cast("struct VFilePy*", vf)
|
||||||
|
f = ffi.from_handle(vfp.fileobj)
|
||||||
|
f.seek(offset, whence)
|
||||||
|
return f.tell()
|
||||||
|
|
||||||
|
@ffi.def_extern()
|
||||||
|
def _vfpRead(vf, buffer, size):
|
||||||
|
vfp = ffi.cast("struct VFilePy*", vf)
|
||||||
|
pybuf = ffi.buffer(buffer, size)
|
||||||
|
ffi.from_handle(vfp.fileobj).readinto(pybuf)
|
||||||
|
return size
|
||||||
|
|
||||||
|
@ffi.def_extern()
|
||||||
|
def _vfpWrite(vf, buffer, size):
|
||||||
|
vfp = ffi.cast("struct VFilePy*", vf)
|
||||||
|
pybuf = ffi.buffer(buffer, size)
|
||||||
|
ffi.from_handle(vfp.fileobj).write(pybuf)
|
||||||
|
return size
|
||||||
|
|
||||||
|
@ffi.def_extern()
|
||||||
|
def _vfpMap(vf, size, flags):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@ffi.def_extern()
|
||||||
|
def _vfpUnmap(vf, memory, size):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@ffi.def_extern()
|
||||||
|
def _vfpTruncate(vf, size):
|
||||||
|
vfp = ffi.cast("struct VFilePy*", vf)
|
||||||
|
ffi.from_handle(vfp.fileobj).truncate(size)
|
||||||
|
|
||||||
|
@ffi.def_extern()
|
||||||
|
def _vfpSize(vf):
|
||||||
|
vfp = ffi.cast("struct VFilePy*", vf)
|
||||||
|
f = ffi.from_handle(vfp.fileobj)
|
||||||
|
pos = f.tell()
|
||||||
|
f.seek(0, os.SEEK_END)
|
||||||
|
size = f.tell()
|
||||||
|
f.seek(pos, os.SEEK_SET)
|
||||||
|
return size
|
||||||
|
|
||||||
|
@ffi.def_extern()
|
||||||
|
def _vfpSync(vf, buffer, size):
|
||||||
|
vfp = ffi.cast("struct VFilePy*", vf)
|
||||||
|
f = ffi.from_handle(vfp.fileobj)
|
||||||
|
if buffer and size:
|
||||||
|
pos = f.tell()
|
||||||
|
f.seek(0, os.SEEK_SET)
|
||||||
|
_vfpWrite(vf, buffer, size)
|
||||||
|
f.seek(pos, os.SEEK_SET)
|
||||||
|
f.flush()
|
||||||
|
os.fsync()
|
||||||
|
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"):
|
||||||
|
flags = 0
|
||||||
|
if mode.startswith("r"):
|
||||||
|
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
|
||||||
|
|
||||||
|
return VFile(lib.VFileOpen(path.encode("UTF-8"), flags))
|
||||||
|
|
||||||
|
class VFile:
|
||||||
|
def __init__(self, vf):
|
||||||
|
self.handle = vf
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
return self.handle.close(self.handle)
|
||||||
|
|
||||||
|
def seek(self, offset, whence):
|
||||||
|
return self.handle.seek(self.handle, offset, whence)
|
||||||
|
|
||||||
|
def read(self, buffer, size):
|
||||||
|
return self.handle.read(self.handle, buffer, size)
|
||||||
|
|
||||||
|
def readline(self, buffer, size):
|
||||||
|
return self.handle.readline(self.handle, buffer, size)
|
||||||
|
|
||||||
|
def write(self, buffer, size):
|
||||||
|
return self.handle.write(self.handle, buffer, size)
|
||||||
|
|
||||||
|
def map(self, size, flags):
|
||||||
|
return self.handle.map(self.handle, size, flags)
|
||||||
|
|
||||||
|
def unmap(self, memory, size):
|
||||||
|
self.handle.unmap(self.handle, memory, size)
|
||||||
|
|
||||||
|
def truncate(self, size):
|
||||||
|
self.handle.truncate(self.handle, size)
|
||||||
|
|
||||||
|
def size(self):
|
||||||
|
return self.handle.size(self.handle)
|
||||||
|
|
||||||
|
def sync(self, buffer, size):
|
||||||
|
return self.handle.sync(self.handle, buffer, size)
|
|
@ -0,0 +1,24 @@
|
||||||
|
from setuptools import setup
|
||||||
|
import re
|
||||||
|
|
||||||
|
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"
|
||||||
|
]
|
||||||
|
|
||||||
|
setup(name="${BINARY_NAME}",
|
||||||
|
version=re.sub("/", "-", "${VERSION_STRING}"),
|
||||||
|
author="Jeffrey Pfau",
|
||||||
|
author_email="jeffrey@endrift.com",
|
||||||
|
url="http://github.com/mgba-emu/mgba/",
|
||||||
|
packages=["mgba"],
|
||||||
|
setup_requires=['cffi>=1.6'],
|
||||||
|
install_requires=['cffi>=1.6', 'cached-property'],
|
||||||
|
cffi_modules=["_builder.py:ffi"],
|
||||||
|
license="MPL 2.0",
|
||||||
|
classifiers=classifiers
|
||||||
|
)
|
|
@ -0,0 +1,46 @@
|
||||||
|
/* Copyright (c) 2013-2016 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#include "util/vfs.h"
|
||||||
|
|
||||||
|
struct VFilePy {
|
||||||
|
struct VFile d;
|
||||||
|
void* fileobj;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool _vfpClose(struct VFile* vf);
|
||||||
|
off_t _vfpSeek(struct VFile* vf, off_t offset, int whence);
|
||||||
|
ssize_t _vfpRead(struct VFile* vf, void* buffer, size_t size);
|
||||||
|
ssize_t _vfpWrite(struct VFile* vf, const void* buffer, size_t size);
|
||||||
|
void* _vfpMap(struct VFile* vf, size_t size, int flags);
|
||||||
|
void _vfpUnmap(struct VFile* vf, void* memory, size_t size);
|
||||||
|
void _vfpTruncate(struct VFile* vf, size_t size);
|
||||||
|
ssize_t _vfpSize(struct VFile* vf);
|
||||||
|
bool _vfpSync(struct VFile* vf, const void* buffer, size_t size);
|
||||||
|
|
||||||
|
struct VFile* VFileFromPython(void* fileobj) {
|
||||||
|
if (!fileobj) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VFilePy* vfp = malloc(sizeof(struct VFilePy));
|
||||||
|
if (!vfp) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
vfp->fileobj = fileobj;
|
||||||
|
vfp->d.close = _vfpClose;
|
||||||
|
vfp->d.seek = _vfpSeek;
|
||||||
|
vfp->d.read = _vfpRead;
|
||||||
|
vfp->d.readline = VFileReadline;
|
||||||
|
vfp->d.write = _vfpWrite;
|
||||||
|
vfp->d.map = _vfpMap;
|
||||||
|
vfp->d.unmap = _vfpUnmap;
|
||||||
|
vfp->d.truncate = _vfpTruncate;
|
||||||
|
vfp->d.size = _vfpSize;
|
||||||
|
vfp->d.sync = _vfpSync;
|
||||||
|
|
||||||
|
return &vfp->d;
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/* Copyright (c) 2013-2016 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "util/vfs.h"
|
||||||
|
|
||||||
|
struct VFilePy {
|
||||||
|
struct VFile d;
|
||||||
|
void* fileobj;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VFile* VFileFromPython(void* fileobj);
|
||||||
|
|
||||||
|
extern "Python+C" {
|
||||||
|
|
||||||
|
bool _vfpClose(struct VFile* vf);
|
||||||
|
off_t _vfpSeek(struct VFile* vf, off_t offset, int whence);
|
||||||
|
ssize_t _vfpRead(struct VFile* vf, void* buffer, size_t size);
|
||||||
|
ssize_t _vfpWrite(struct VFile* vf, const void* buffer, size_t size);
|
||||||
|
void* _vfpMap(struct VFile* vf, size_t size, int flags);
|
||||||
|
void _vfpUnmap(struct VFile* vf, void* memory, size_t size);
|
||||||
|
void _vfpTruncate(struct VFile* vf, size_t size);
|
||||||
|
ssize_t _vfpSize(struct VFile* vf);
|
||||||
|
bool _vfpSync(struct VFile* vf, const void* buffer, size_t size);
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
/* Copyright (c) 2013-2015 Jeffrey Pfau
|
/* Copyright (c) 2013-2016 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
|
||||||
|
@ -7,16 +7,20 @@
|
||||||
|
|
||||||
#include "gl-common.h"
|
#include "gl-common.h"
|
||||||
|
|
||||||
|
#include "core/thread.h"
|
||||||
|
|
||||||
|
#ifndef __APPLE__
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
static bool mSDLGLES2Init(struct mSDLRenderer* renderer);
|
static bool mSDLGLES2Init(struct mSDLRenderer* renderer);
|
||||||
static void mSDLGLES2RunloopGBA(struct mSDLRenderer* renderer, void* user);
|
static void mSDLGLES2Runloop(struct mSDLRenderer* renderer, void* user);
|
||||||
static void mSDLGLES2Deinit(struct mSDLRenderer* renderer);
|
static void mSDLGLES2Deinit(struct mSDLRenderer* renderer);
|
||||||
|
|
||||||
void mSDLGLES2Create(struct mSDLRenderer* renderer) {
|
void mSDLGLES2Create(struct mSDLRenderer* renderer) {
|
||||||
renderer->init = mSDLGLES2Init;
|
renderer->init = mSDLGLES2Init;
|
||||||
renderer->deinit = mSDLGLES2Deinit;
|
renderer->deinit = mSDLGLES2Deinit;
|
||||||
renderer->runloop = mSDLGLES2RunloopGBA;
|
renderer->runloop = mSDLGLES2Runloop;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mSDLGLES2Init(struct mSDLRenderer* renderer) {
|
bool mSDLGLES2Init(struct mSDLRenderer* renderer) {
|
||||||
|
@ -93,8 +97,13 @@ bool mSDLGLES2Init(struct mSDLRenderer* renderer) {
|
||||||
mSDLGLCommonInit(renderer);
|
mSDLGLCommonInit(renderer);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
renderer->d.outputBuffer = memalign(16, VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * 4);
|
#ifndef __APPLE__
|
||||||
renderer->d.outputBufferStride = VIDEO_HORIZONTAL_PIXELS;
|
renderer->outputBuffer = memalign(16, renderer->width * renderer->height * BYTES_PER_PIXEL);
|
||||||
|
#else
|
||||||
|
posix_memalign((void**) &renderer->outputBuffer, 16, renderer->width * renderer->height * BYTES_PER_PIXEL);
|
||||||
|
#endif
|
||||||
|
memset(renderer->outputBuffer, 0, renderer->width * renderer->height * BYTES_PER_PIXEL);
|
||||||
|
renderer->core->setVideoBuffer(renderer->core, renderer->outputBuffer, renderer->width);
|
||||||
|
|
||||||
mGLES2ContextCreate(&renderer->gl2);
|
mGLES2ContextCreate(&renderer->gl2);
|
||||||
renderer->gl2.d.user = renderer;
|
renderer->gl2.d.user = renderer;
|
||||||
|
@ -107,17 +116,17 @@ bool mSDLGLES2Init(struct mSDLRenderer* renderer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void mSDLGLES2Runloop(struct mSDLRenderer* renderer, void* user) {
|
void mSDLGLES2Runloop(struct mSDLRenderer* renderer, void* user) {
|
||||||
struct GBAThread* context = user;
|
struct mCoreThread* context = user;
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
struct VideoBackend* v = &renderer->gl2.d;
|
struct VideoBackend* v = &renderer->gl2.d;
|
||||||
|
|
||||||
while (context->state < THREAD_EXITING) {
|
while (context->state < THREAD_EXITING) {
|
||||||
while (SDL_PollEvent(&event)) {
|
while (SDL_PollEvent(&event)) {
|
||||||
mSDLHandleEventGBA(context, &renderer->player, &event);
|
mSDLHandleEvent(context, &renderer->player, &event);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mCoreSyncWaitFrameStart(&context->sync)) {
|
if (mCoreSyncWaitFrameStart(&context->sync)) {
|
||||||
v->postFrame(v, renderer->d.outputBuffer);
|
v->postFrame(v, renderer->outputBuffer);
|
||||||
}
|
}
|
||||||
mCoreSyncWaitFrameEnd(&context->sync);
|
mCoreSyncWaitFrameEnd(&context->sync);
|
||||||
v->drawFrame(v);
|
v->drawFrame(v);
|
||||||
|
@ -142,5 +151,5 @@ void mSDLGLES2Deinit(struct mSDLRenderer* renderer) {
|
||||||
#elif SDL_VERSION_ATLEAST(2, 0, 0)
|
#elif SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
SDL_GL_DeleteContext(renderer->glCtx);
|
SDL_GL_DeleteContext(renderer->glCtx);
|
||||||
#endif
|
#endif
|
||||||
free(renderer->d.outputBuffer);
|
free(renderer->outputBuffer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ void blip_set_rates( blip_t*, double clock_rate, double sample_rate );
|
||||||
|
|
||||||
enum { /** Maximum clock_rate/sample_rate ratio. For a given sample_rate,
|
enum { /** Maximum clock_rate/sample_rate ratio. For a given sample_rate,
|
||||||
clock_rate must not be greater than sample_rate*blip_max_ratio. */
|
clock_rate must not be greater than sample_rate*blip_max_ratio. */
|
||||||
blip_max_ratio = 1 << 20 };
|
blip_max_ratio = 0x100000 };
|
||||||
|
|
||||||
/** Clears entire buffer. Afterwards, blip_samples_avail() == 0. */
|
/** Clears entire buffer. Afterwards, blip_samples_avail() == 0. */
|
||||||
void blip_clear( blip_t* );
|
void blip_clear( blip_t* );
|
||||||
|
|
|
@ -163,6 +163,14 @@ size_t TableSize(const struct Table* table) {
|
||||||
return table->size;
|
return table->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HashTableInit(struct Table* table, size_t initialSize, void (deinitializer(void*))) {
|
||||||
|
TableInit(table, initialSize, deinitializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HashTableDeinit(struct Table* table) {
|
||||||
|
TableDeinit(table);
|
||||||
|
}
|
||||||
|
|
||||||
void* HashTableLookup(const struct Table* table, const char* key) {
|
void* HashTableLookup(const struct Table* table, const char* key) {
|
||||||
uint32_t hash = hash32(key, strlen(key), 0);
|
uint32_t hash = hash32(key, strlen(key), 0);
|
||||||
const struct TableList* list;
|
const struct TableList* list;
|
||||||
|
|
|
@ -31,13 +31,8 @@ void TableClear(struct Table*);
|
||||||
void TableEnumerate(const struct Table*, void (handler(uint32_t key, void* value, void* user)), void* user);
|
void TableEnumerate(const struct Table*, void (handler(uint32_t key, void* value, void* user)), void* user);
|
||||||
size_t TableSize(const struct Table*);
|
size_t TableSize(const struct Table*);
|
||||||
|
|
||||||
static inline void HashTableInit(struct Table* table, size_t initialSize, void (deinitializer(void*))) {
|
void HashTableInit(struct Table* table, size_t initialSize, void (deinitializer(void*)));
|
||||||
TableInit(table, initialSize, deinitializer);
|
void HashTableDeinit(struct Table* table);
|
||||||
}
|
|
||||||
|
|
||||||
static inline void HashTableDeinit(struct Table* table) {
|
|
||||||
TableDeinit(table);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* HashTableLookup(const struct Table*, const char* key);
|
void* HashTableLookup(const struct Table*, const char* key);
|
||||||
void HashTableInsert(struct Table*, const char* key, void* value);
|
void HashTableInsert(struct Table*, const char* key, void* value);
|
||||||
|
|
|
@ -104,7 +104,7 @@ struct VDir* VDirOpenArchive(const char* path) {
|
||||||
dir = VDirOpenZip(path, 0);
|
dir = VDirOpenZip(path, 0);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if USE_LZMA
|
#ifdef USE_LZMA
|
||||||
if (!dir) {
|
if (!dir) {
|
||||||
dir = VDirOpen7z(path, 0);
|
dir = VDirOpen7z(path, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,12 +67,11 @@ struct VDir {
|
||||||
struct VFile* VFileOpen(const char* path, int flags);
|
struct VFile* VFileOpen(const char* path, int flags);
|
||||||
|
|
||||||
struct VFile* VFileOpenFD(const char* path, int flags);
|
struct VFile* VFileOpenFD(const char* path, int flags);
|
||||||
struct VFile* VFileFOpen(const char* path, const char* mode);
|
|
||||||
struct VFile* VFileFromFD(int fd);
|
struct VFile* VFileFromFD(int fd);
|
||||||
|
|
||||||
struct VFile* VFileFromMemory(void* mem, size_t size);
|
struct VFile* VFileFromMemory(void* mem, size_t size);
|
||||||
struct VFile* VFileFromConstMemory(const void* mem, size_t size);
|
struct VFile* VFileFromConstMemory(const void* mem, size_t size);
|
||||||
struct VFile* VFileMemChunk(const void* mem, size_t size);
|
struct VFile* VFileMemChunk(const void* mem, size_t size);
|
||||||
struct VFile* VFileFromFILE(FILE* file);
|
|
||||||
|
|
||||||
struct VDir* VDirOpen(const char* path);
|
struct VDir* VDirOpen(const char* path);
|
||||||
struct VDir* VDirOpenArchive(const char* path);
|
struct VDir* VDirOpenArchive(const char* path);
|
||||||
|
@ -85,7 +84,11 @@ struct VDir* VDirOpenZip(const char* path, int flags);
|
||||||
struct VDir* VDirOpen7z(const char* path, int flags);
|
struct VDir* VDirOpen7z(const char* path, int flags);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(WII) || defined(_3DS)
|
||||||
|
struct VFile* VFileFOpen(const char* path, const char* mode);
|
||||||
|
struct VFile* VFileFromFILE(FILE* file);
|
||||||
struct VDir* VDeviceList(void);
|
struct VDir* VDeviceList(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
void separatePath(const char* path, char* dirname, char* basename, char* extension);
|
void separatePath(const char* path, char* dirname, char* basename, char* extension);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue