I have added a new demo (in two parts) to the demos repo. The first part (/sys/modules/famicom) is a "module" that extends Engine BASIC with a couple of commands that allow you to start and control a Famicom emulator from BASIC. It implements commands like FAMILOAD to load a ROM, FAMULATE to emulate one frame, FAMINPUT to pass gamepad input to the emulator, and FAMIREAD() to read the emulated Famicom's memory.
The second part (/famicom) consists of two BASIC programs that use the famicom module. The first program (play.bas) simply forwards gamepad input to the emulator and looks like this:
#REQUIRE "famicom"
INPUT "ROM file? ";rom$
FAMIMOVE PSIZE(0)-256, 0
FAMILOAD rom$
DO
f=FRAME()
FAMULATE
FAMINPUT PAD(0)
VSYNC f+1
LOOP
The second program (autoplay.bas) does a very bad job at playing Super Mario Bros. automatically. Feel free to improve it.
About modules
A "module" is an extension to the Engine BASIC system implemented in C (or whatever you can compile into an ELF object file). It resides in the root filesystem in /sys/modules/<module name>, and can be loaded with LOADMOD "<module name>" or by adding a #REQUIRE "<module name>" statement at the start of your BASIC program.
The system will then either run a program called loader.bas in the module directory, if one exists, or look for pre-compiled object files called <module name>_<cpu architecture>.o, e.g. famicom_arm.o or famicom_x86_64.o and load one of those.
Check https://github.com/uli/basicengine-demos/tree/master/sys/modules/famicom (README.md and Makefile) for examples on how to cross-compile and natively compile C code for Engine BASIC.
Adding new command and functions to Engine BASIC
Extending BASIC works by calling the eb_add_command(), eb_add_numfun() or eb_add_strfun() functions from an __initcall() function that is executed at load time, for instance:
eb_add_command("FAMILOAD", syn_famiload, famiload);
eb_add_numfun("FAMIREAD", syn_famiread, famiread);
syn_famiload and syn_famiread are syntax descriptions. famiload() and famiread() are the handlers that implement the command/function.
The syntax is defined by an array of tokens that end with I_EOL:
const enum token_t syn_famiload[] = { I_STR, I_EOL }; describes a command that takes a string as its argument.
const enum token_t syn_famiread[] = { I_OPEN, I_NUM, I_CLOSE, I_EOL }; describes a function that takes a numeric argument enclosed in parentheses.
The command handlers receive a pointer to a parameter array. Only string and numeric parameters are included in the array; "filler" tokens (such as the parentheses in the syn_famiread example) are not.
Check https://github.com/uli/basicengine-demos/blob/master/sys/modules/famicom/fami.c to see how it all works together in practice.
Custom background layers
The famicom module also uses new functionality that allows you to hook custom background layer painters into the BG/sprite engine to display the emulated screen. Check the painter() function in fami.c and the eb_add_bg_layer() and eb_remove_bg_layer() functions for an example of how to do that.