diff --git a/src/core/scripting.c b/src/core/scripting.c index 49e65fc3e1a..1e0655c0a9f 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -9,6 +9,10 @@ #include #ifdef M_CORE_GBA #include +#include +#endif +#ifdef M_CORE_GB +#include #endif #include #include @@ -411,6 +415,75 @@ static void _mScriptCoreWriteRegister(struct mCore* core, const char* regName, i core->writeRegister(core, regName, &in); } +static uint16_t _mScriptCoreReadPalette(struct mCore* core, uint32_t index) { + const uint16_t* palette; + uint32_t count; + + switch (core->platform(core)) { +#ifdef M_CORE_GBA + case mPLATFORM_GBA: + struct GBA* gba = (struct GBA*) core->board; + palette = gba->video.palette; + count = 512; + break; +#endif +#ifdef M_CORE_GB + case mPLATFORM_GB: + struct GB* gb = (struct GB*) core->board; + palette = gb->video.palette; + count = 64; + break; +#endif + default: + return 0x8000; + } + + if (index >= count) { + return 0x8000; + } + + uint16_t color; + LOAD_16LE(color, 2 * index, palette); + + return color; +} + +static void _mScriptCoreWritePalette(struct mCore* core, uint32_t index, uint16_t color) { + uint16_t* palette; + switch (core->platform(core)) { +#ifdef M_CORE_GBA + case mPLATFORM_GBA: + if (index >= 512) { + return; + } + struct GBA* gba = (struct GBA*) core->board; + palette = gba->video.palette; + + STORE_16LE(color, 2 * index, palette); + + struct GBAVideoRenderer* gbaRenderer = gba->video.renderer; + gbaRenderer->writePalette(gbaRenderer, index, palette[index]); + break; +#endif +#ifdef M_CORE_GB + case mPLATFORM_GB: + if (index >= 64) { + return; + } + struct GB* gb = (struct GB*) core->board; + palette = gb->video.palette; + + STORE_16LE(color, 2 * index, palette); + + struct GBVideoRenderer* gbRenderer = gb->video.renderer; + gbRenderer->writePalette(gbRenderer, index, palette[index]); + break; +#endif + default: + break; + } +} + static struct mScriptValue* _mScriptCoreSaveState(struct mCore* core, int32_t flags) { struct VFile* vf = VFileMemChunk(NULL, 0); if (!mCoreSaveStateNamed(core, vf, flags)) { @@ -532,6 +605,10 @@ mSCRIPT_DECLARE_STRUCT_VOID_D_METHOD(mCore, busWrite32, 2, U32, address, U32, va mSCRIPT_DECLARE_STRUCT_METHOD(mCore, WRAPPER, readRegister, _mScriptCoreReadRegister, 1, CHARP, regName); mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mCore, writeRegister, _mScriptCoreWriteRegister, 2, CHARP, regName, S32, value); +// Palette functions +mSCRIPT_DECLARE_STRUCT_METHOD(mCore, U16, readPalette, _mScriptCoreReadPalette, 1, U32, index); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mCore, writePalette, _mScriptCoreWritePalette, 2, U32, index, U16, color); + // Savestate functions mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, WSTR, saveStateBuffer, _mScriptCoreSaveState, 1, S32, flags); mSCRIPT_DECLARE_STRUCT_METHOD_WITH_DEFAULTS(mCore, BOOL, loadStateBuffer, _mScriptCoreLoadState, 2, STR, buffer, S32, flags); @@ -619,6 +696,11 @@ mSCRIPT_DEFINE_STRUCT(mCore) mSCRIPT_DEFINE_DOCSTRING("Write the value of the register with the given name") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, writeRegister) + mSCRIPT_DEFINE_DOCSTRING("Read a 16-bit value encoding the RGB channels in 5 bits each from the given palette index (0-indexed). See util.unpackColor") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, readPalette) + mSCRIPT_DEFINE_DOCSTRING("Write a 16-bit value encoding the RGB channels in 5 bits each to the given palette index (0-indexed). See util.packColor") + mSCRIPT_DEFINE_STRUCT_METHOD(mCore, writePalette) + mSCRIPT_DEFINE_DOCSTRING("Save state and return as a buffer. See C.SAVESTATE for possible values for `flags`") mSCRIPT_DEFINE_STRUCT_METHOD(mCore, saveStateBuffer) mSCRIPT_DEFINE_DOCSTRING("Load state from a buffer. See C.SAVESTATE for possible values for `flags`") diff --git a/src/core/test/scripting.c b/src/core/test/scripting.c index bea6242ecef..178e8e27b4b 100644 --- a/src/core/test/scripting.c +++ b/src/core/test/scripting.c @@ -14,6 +14,7 @@ #include "script/test.h" #ifdef M_CORE_GBA +#include #include #define TEST_PLATFORM mPLATFORM_GBA #define RAM_BASE GBA_BASE_IWRAM @@ -25,6 +26,10 @@ #error "Need a valid platform for testing" #endif +#ifdef M_CORE_GB +#include +#endif + struct mScriptTestLogger { struct mLogger d; char* log; @@ -328,6 +333,74 @@ M_TEST_DEFINE(screenshot) { TEARDOWN_CORE; } +M_TEST_DEFINE(readPalette) { + SETUP_LUA; + CREATE_CORE; + + uint16_t* palette = 0; + + switch(core->platform(core)) { +#ifdef M_CORE_GBA + case mPLATFORM_GBA: + struct GBA* gba = (struct GBA*) core->board; + palette = gba->video.palette; + break; +#endif +#ifdef M_CORE_GB + case mPLATFORM_GB: + struct GB* gb = (struct GB*) core->board; + palette = gb->video.palette; + break; +#endif + default: + break; + } + + if (palette != 0) { + palette[0] = 123; + + TEST_PROGRAM("assert(emu.readPalette)") + TEST_PROGRAM("assert(emu:readPalette(0) == 123)") + } + + mScriptContextDeinit(&context); + TEARDOWN_CORE; +} + +M_TEST_DEFINE(writePalette) { + SETUP_LUA; + CREATE_CORE; + + uint16_t* palette = 0; + + switch(core->platform(core)) { +#if(TEST_PLATFORM == mPLATFORM_GBA) + case mPLATFORM_GBA: + struct GBA* gba = (struct GBA*) core->board; + palette = gba->video.palette; + break; +#endif +#if(TEST_PLATFORM == mPLATFORM_GB) + case mPLATFORM_GB: + struct GB* gb = (struct GB*) core->board; + palette = gb->video.palette; + break; +#endif + default: + break; + } + + if (palette != 0) { + TEST_PROGRAM("assert(emu.writePalette)") + TEST_PROGRAM("emu:writePalette(0, 123)") + + assert_true(palette[0] == 123); + } + + mScriptContextDeinit(&context); + TEARDOWN_CORE; +} + #ifdef ENABLE_DEBUGGERS void _setupBp(struct mCore* core) { switch (core->platform(core)) { @@ -860,6 +933,8 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptCore, cmocka_unit_test(memoryWrite), cmocka_unit_test(logging), cmocka_unit_test(screenshot), + cmocka_unit_test(readPalette), + cmocka_unit_test(writePalette), #ifdef ENABLE_DEBUGGERS #ifdef M_CORE_GBA cmocka_unit_test(basicBreakpointGBA), diff --git a/src/script/stdlib.c b/src/script/stdlib.c index f84b6251531..935d8478d9f 100644 --- a/src/script/stdlib.c +++ b/src/script/stdlib.c @@ -78,6 +78,31 @@ static struct mScriptValue* mScriptExpandBitmask(uint64_t mask) { mSCRIPT_BIND_FUNCTION(mScriptMakeBitmask_Binding, U64, mScriptMakeBitmask, 1, LIST, bits); mSCRIPT_BIND_FUNCTION(mScriptExpandBitmask_Binding, WLIST, mScriptExpandBitmask, 1, U64, mask); +static uint16_t mScriptPackColor(uint8_t red, uint8_t green, uint8_t blue) { + return (uint16_t) (((blue & 31) << 10) | ((green & 31) << 5) | (red & 31)); +} + +static struct mScriptValue* mScriptUnpackColor(uint16_t color) { + struct mScriptValue* tbl = mScriptValueAlloc(mSCRIPT_TYPE_MS_TABLE); + + char* keys[3] = {"red", "green", "blue"}; + + for (size_t i = 0; i < 3; ++i) { + uint16_t part = (color >> 5*i) & 31; + struct mScriptValue* ikey = mScriptValueCreateFromUInt(i + 1); + struct mScriptValue* skey = mScriptStringCreateFromASCII(keys[i]); + struct mScriptValue* val = mScriptValueCreateFromUInt(part); + + mScriptTableInsert(tbl, ikey, val); + mScriptTableInsert(tbl, skey, val); + } + + return tbl; +} + +mSCRIPT_BIND_FUNCTION(mScriptPackColor_Binding, U16, mScriptPackColor, 3, U8, red, U8, green, U8, blue); +mSCRIPT_BIND_FUNCTION(mScriptUnpackColor_Binding, WTABLE, mScriptUnpackColor, 1, U16, color); + mSCRIPT_DEFINE_STRUCT(mScriptCallbackManager) mSCRIPT_DEFINE_CLASS_DOCSTRING( "A global singleton object `callbacks` used for managing callbacks. The following callbacks are defined:\n\n" @@ -272,6 +297,8 @@ void mScriptContextAttachStdlib(struct mScriptContext* context) { mScriptContextExportNamespace(context, "util", (struct mScriptKVPair[]) { mSCRIPT_KV_PAIR(makeBitmask, &mScriptMakeBitmask_Binding), mSCRIPT_KV_PAIR(expandBitmask, &mScriptExpandBitmask_Binding), + mSCRIPT_KV_PAIR(packColor, &mScriptPackColor_Binding), + mSCRIPT_KV_PAIR(unpackColor, &mScriptUnpackColor_Binding), mSCRIPT_KV_PAIR(newRectangle, &mRectangleNew_Binding), mSCRIPT_KV_PAIR(newSize, &mSizeNew_Binding), mSCRIPT_KV_SENTINEL @@ -279,6 +306,8 @@ void mScriptContextAttachStdlib(struct mScriptContext* context) { mScriptContextSetDocstring(context, "util", "Basic utility library"); mScriptContextSetDocstring(context, "util.makeBitmask", "Compile a list of bit indices into a bitmask"); mScriptContextSetDocstring(context, "util.expandBitmask", "Expand a bitmask into a list of bit indices"); + mScriptContextSetDocstring(context, "util.packColor", "Pack three RGB channels (0-31) into a 16-bit value."); + mScriptContextSetDocstring(context, "util.unpackColor", "Split a color in a 16-bit value into a table of red, blue and green channels (indexed by name or integer) from 0 to 31."); mScriptContextSetDocstring(context, "util.newRectangle", "Create a new mRectangle"); mScriptContextSetDocstring(context, "util.newSize", "Create a new mSize"); diff --git a/src/script/test/stdlib.c b/src/script/test/stdlib.c index 9b1056db5fd..514590ff5cb 100644 --- a/src/script/test/stdlib.c +++ b/src/script/test/stdlib.c @@ -66,6 +66,31 @@ M_TEST_DEFINE(bitUnmask) { mScriptContextDeinit(&context); } +M_TEST_DEFINE(packColor) { + SETUP_LUA; + + TEST_PROGRAM("assert(util)"); + TEST_PROGRAM("assert(util.packColor)"); + TEST_PROGRAM("assert(util.packColor(31, 16, 3) == 3615)"); + + mScriptContextDeinit(&context); +} + +M_TEST_DEFINE(unpackColor) { + SETUP_LUA; + + TEST_PROGRAM("assert(util)"); + TEST_PROGRAM("assert(util.unpackColor)"); + TEST_PROGRAM("assert(util.unpackColor(3615).red == 31)"); + TEST_PROGRAM("assert(util.unpackColor(3615)[1] == 31)"); + TEST_PROGRAM("assert(util.unpackColor(3615).green == 16)"); + TEST_PROGRAM("assert(util.unpackColor(3615)[2] == 16)"); + TEST_PROGRAM("assert(util.unpackColor(3615).blue == 3)"); + TEST_PROGRAM("assert(util.unpackColor(3615)[3] == 3)"); + + mScriptContextDeinit(&context); +} + M_TEST_DEFINE(callbacks) { SETUP_LUA; @@ -260,6 +285,8 @@ M_TEST_DEFINE(size) { M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptStdlib, cmocka_unit_test(bitMask), cmocka_unit_test(bitUnmask), + cmocka_unit_test(packColor), + cmocka_unit_test(unpackColor), cmocka_unit_test(callbacks), cmocka_unit_test(oneshot), cmocka_unit_test(callbackWeakref),