From e5c61e0093a9219cdf95b01eb74aa0f48250d01f Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 11 Oct 2016 13:13:44 -0700 Subject: [PATCH] Python: Preliminary, incomplete bindings --- CMakeLists.txt | 7 +++++ src/arm/arm.h | 4 +-- src/core/log.c | 14 +++++++++ src/core/log.h | 4 +++ src/debugger/debugger.h | 2 +- src/platform/python/CMakeLists.txt | 20 +++++++++++++ src/platform/python/__init__.py | 0 src/platform/python/_builder.h | 6 ++++ src/platform/python/_builder.py | 20 +++++++++++++ src/platform/python/mCore.py | 47 ++++++++++++++++++++++++++++++ src/util/table.c | 8 +++++ src/util/table.h | 5 ++++ 12 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 src/platform/python/CMakeLists.txt create mode 100644 src/platform/python/__init__.py create mode 100644 src/platform/python/_builder.h create mode 100644 src/platform/python/_builder.py create mode 100644 src/platform/python/mCore.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b048f9cc..f74c7dce0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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_SUITE OFF CACHE BOOL "Build test suite") 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_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)") @@ -750,6 +751,11 @@ if(BUILD_SUITE) add_test(${BINARY_NAME}-suite ${BINARY_NAME}-suite) endif() +if(BUILD_PYTHON) + enable_testing() + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/platform/python ${CMAKE_CURRENT_BINARY_DIR}/python) +endif() + if(BUILD_EXAMPLE) 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}) @@ -833,6 +839,7 @@ if(NOT QUIET) message(STATUS " Profiling: ${BUILD_PERF}") message(STATUS " Test harness: ${BUILD_TEST}") message(STATUS " Test suite: ${BUILD_SUITE}") + message(STATUS " Python bindings: ${BUILD_PYTHON}") message(STATUS " Examples: ${BUILD_EXAMPLE}") message(STATUS "Cores:") message(STATUS " Libretro core: ${BUILD_LIBRETRO}") diff --git a/src/arm/arm.h b/src/arm/arm.h index 947cedf15..274906e71 100644 --- a/src/arm/arm.h +++ b/src/arm/arm.h @@ -73,7 +73,7 @@ union PSR { unsigned z : 1; unsigned c : 1; unsigned v : 1; - unsigned : 20; + unsigned unused : 20; unsigned i : 1; unsigned f : 1; unsigned t : 1; @@ -83,7 +83,7 @@ union PSR { unsigned t : 1; unsigned f : 1; unsigned i : 1; - unsigned : 20; + unsigned unused : 20; unsigned v : 1; unsigned c : 1; unsigned z : 1; diff --git a/src/core/log.c b/src/core/log.c index 880379fd9..125907e9a 100644 --- a/src/core/log.c +++ b/src/core/log.c @@ -44,4 +44,18 @@ const char* mLogCategoryName(int category) { 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") diff --git a/src/core/log.h b/src/core/log.h index 03fb31ee9..43be3d374 100644 --- a/src/core/log.h +++ b/src/core/log.h @@ -30,6 +30,7 @@ int mLogGenerateCategory(const char*); const char* mLogCategoryName(int); ATTRIBUTE_FORMAT(printf, 3, 4) +#ifndef __NO_INLINE__ static inline void mLog(int category, enum mLogLevel level, const char* format, ...) { struct mLogger* context = mLogGetContext(); va_list args; @@ -43,6 +44,9 @@ static inline void mLog(int category, enum mLogLevel level, const char* format, } va_end(args); } +#else +void mLog(int category, enum mLogLevel level, const char* format, ...); +#endif #define mLOG(CATEGORY, LEVEL, ...) mLog(_mLOG_CAT_ ## CATEGORY (), mLOG_ ## LEVEL, __VA_ARGS__) diff --git a/src/debugger/debugger.h b/src/debugger/debugger.h index a4ce367f6..fed4c4978 100644 --- a/src/debugger/debugger.h +++ b/src/debugger/debugger.h @@ -35,7 +35,7 @@ enum mDebuggerState { enum mWatchpointType { WATCHPOINT_WRITE = 1, WATCHPOINT_READ = 2, - WATCHPOINT_RW = WATCHPOINT_WRITE | WATCHPOINT_READ + WATCHPOINT_RW = 3 }; enum mBreakpointType { diff --git a/src/platform/python/CMakeLists.txt b/src/platform/python/CMakeLists.txt new file mode 100644 index 000000000..cd30b551f --- /dev/null +++ b/src/platform/python/CMakeLists.txt @@ -0,0 +1,20 @@ +set(PY_INCLUDE_DIRS -I${CMAKE_SOURCE_DIR}/src) +get_property(INCLUDE_DIRECTORIES DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES) +get_property(COMPILE_DEFINITIONS DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY COMPILE_DEFINITIONS) +foreach(INCLUDE_DIR IN LISTS INCLUDE_DIRECTORIES) + list(APPEND PY_INCLUDE_DIRS -I${INCLUDE_DIR}) +endforeach() +foreach(COMPILE_DEF IN LISTS COMPILE_DEFINITIONS) + list(APPEND PY_COMPILE_DEFS -D${COMPILE_DEF}) +endforeach() + +add_custom_command(OUTPUT _builder.h + COMMAND ${CMAKE_C_COMPILER} ${PY_COMPILE_DEFS} ${PY_INCLUDE_DIRS} -fno-inline -E -P -c ${CMAKE_CURRENT_SOURCE_DIR}/_builder.h -o _builder.h + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/_builder.h) +add_custom_target(_builder.h ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/_builder.h) + +add_custom_command(OUTPUT ${BINARY_NAME}/_pylib.so + COMMAND python ${CMAKE_CURRENT_SOURCE_DIR}/_builder.py ${PY_COMPILE_DEFS} ${PY_INCLUDE_DIRS} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/_builder.py + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/_builder.h) +add_custom_target(_pylib.so ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${BINARY_NAME}/_pylib.so) \ No newline at end of file diff --git a/src/platform/python/__init__.py b/src/platform/python/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/platform/python/_builder.h b/src/platform/python/_builder.h new file mode 100644 index 000000000..4c34f6f37 --- /dev/null +++ b/src/platform/python/_builder.h @@ -0,0 +1,6 @@ +#define COMMON_H +#define ATTRIBUTE_FORMAT(X, Y, Z) +typedef long time_t; +typedef ... va_list; +#include +#include "core/core.h" \ No newline at end of file diff --git a/src/platform/python/_builder.py b/src/platform/python/_builder.py new file mode 100644 index 000000000..bcd0bb544 --- /dev/null +++ b/src/platform/python/_builder.py @@ -0,0 +1,20 @@ +import cffi +import os.path +import subprocess +import sys + +ffi = cffi.FFI() +src = os.path.join(os.path.dirname(__file__), "..", "..") + +ffi.set_source("mgba._pylib", """ +#include "util/common.h" +#include "core/core.h" +""", include_dirs=[src], + extra_compile_args=sys.argv[1:], + libraries=["mgba"], + library_dirs=[os.path.join(os.getcwd(), "..")]) + +with open(os.path.join(os.getcwd(), "_builder.h")) as core: + ffi.cdef(core.read()) + +ffi.compile() \ No newline at end of file diff --git a/src/platform/python/mCore.py b/src/platform/python/mCore.py new file mode 100644 index 000000000..f9700d7df --- /dev/null +++ b/src/platform/python/mCore.py @@ -0,0 +1,47 @@ +from _pylib import ffi, lib + +def find(path): + core = lib.mCoreFind(path.encode('UTF-8')) + if core == ffi.NULL: + return None + return mCore(core) + +class mCore: + def __init__(self, native): + self._core = ffi.gc(native, self._deinit) + + def init(self): + return bool(self._core.init(self._core)) + + def _deinit(self): + self._core.deinit(self._core) + + def loadFile(self, path): + return bool(lib.mCoreLoadFile(self._core, path.encode('UTF-8'))) + + 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 reset(self): + self._core.reset(self._core) + + def runFrame(self): + self._core.runFrame(self._core) + + def runLoop(self): + self._core.runLoop(self._core) + + def step(self): + self._core.step(self._core) diff --git a/src/util/table.c b/src/util/table.c index ebc756c29..3f4deb013 100644 --- a/src/util/table.c +++ b/src/util/table.c @@ -163,6 +163,14 @@ size_t TableSize(const struct Table* table) { 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) { uint32_t hash = hash32(key, strlen(key), 0); const struct TableList* list; diff --git a/src/util/table.h b/src/util/table.h index 5dee643fd..a443defbb 100644 --- a/src/util/table.h +++ b/src/util/table.h @@ -29,6 +29,7 @@ void TableClear(struct Table*); void TableEnumerate(const struct Table*, void (handler(uint32_t key, void* value, void* user)), void* user); size_t TableSize(const struct Table*); +#ifndef __NO_INLINE__ static inline void HashTableInit(struct Table* table, size_t initialSize, void (deinitializer(void*))) { TableInit(table, initialSize, deinitializer); } @@ -36,6 +37,10 @@ static inline void HashTableInit(struct Table* table, size_t initialSize, void ( static inline void HashTableDeinit(struct Table* table) { TableDeinit(table); } +#else +void HashTableInit(struct Table* table, size_t initialSize, void (deinitializer(void*))); +void HashTableDeinit(struct Table* table); +#endif void* HashTableLookup(const struct Table*, const char* key); void HashTableInsert(struct Table*, const char* key, void* value);