💾 Archived View for republic.circumlunar.space › users › korruptor › blog › 2022-12-20-EmbeddingWren… captured on 2024-06-16 at 12:21:39. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2023-01-29)

-=-=-=-=-=-=-

🕹 Embedding Wren, pt2

It's Xmas time, which, amongst other things, means I have no guilt about working on the side project for a few days...

So I'm back on the EH500!

The last time I picked this up was in the Summer, so I decided to finish embedding Wren and get the rest of the "console" exposed to script.

Changes to Initialise and Tick

When I last discussed this, I posted an example where I loaded a class of static methods and ticked those. I've changed tack since then in two fundamental ways.

First, a class of static methods is limited in what it can operate on (static members), so now I instantiate the class and adjust my call handles appropriately:

{
   wrenEnsureSlots(s_pWrenVM, 1);
   wrenSetSlotHandle(s_pWrenVM, 0, s_pWH_LevelMode_ClassHandle);

   //
   // Call new()
   //
   wrenCall(s_pWrenVM, s_pWH_New);   
	     
   wrenReleaseHandle(s_pWrenVM, s_pWH_LevelMode_ClassHandle);
   s_pWH_LevelMode_ClassHandle = NULL;

   if (wrenGetSlotCount(s_pWrenVM) == 0)
   {
      LOG_ERROR("-- Interpreter error, calling LevelMode->new");
      Framework_Guru(SCRIPT_ERROR);
   }
   s_pWH_LevelMode_ClassHandle = wrenGetSlotHandle(s_pWrenVM, 0);
}

The instantiated class is free to create other objects, hold a list of things to tick, etc. which was what I needed all along.

Secondly, in the Summer, the platform API -- the foreign methods that map to console "registers" and functions in C-land -- was on disk, in its own module, that others could import. I didn't like this. So I've stolen a trick from the Wren implementation in Tic80; it's trivial to store the API in a string, in C code, and load it into VM as the first thing that's parsed, before I do any of the above.

static char const* s_sPlatformAPI = "\n\
class EH {\n\
   construct new() {}\n\
   foreign static GetFrameCount()\n\
   foreign static GetGameDeltaSeconds()\n\
...
   static TO_RADIANS { 0.0174532925 }\n\
   static TO_DEGREES { 57.295779514 }\n\
   static PI { 3.1415926535 }\n\
   static SCREEN_WIDTH { 384 }\n\
   static SCREEN_HEIGHT { 216 }\n\
   static SCREEN_HALF_WIDTH { 192 }\n\
   static SCREEN_HALF_HEIGHT { 108 }\n\
\
}\n"

This has the effect of adding a class full of static methods into module "main", making them accessible anywhere via an import:

import "main" for EH

From the scripting perspective, this makes no difference, but it removes the need for a source file on disk that could go missing or get out of sync with the EH500.

Kicking the Tyres

https://tdi.online/images/eh500_xmas.mp4

Bar palette manipulation, I have everything in the EH500 exposed to Wren. It's been quick to implement -- little more than a day -- and easy enough to test. The slot system is clean, simple and fits my needs perfectly.

It's taken me a while to grok the error messages from the interpreter, but that's no different than any other language... I just need to use it in anger.

Next steps: