nesbox / TIC-80

TIC-80 is a fantasy computer for making, playing and sharing tiny games.
https://tic80.com
MIT License
4.95k stars 482 forks source link

Adding WebAssembly runtime support via BINARY chunks #1259

Closed msx80 closed 2 years ago

msx80 commented 3 years ago

hi there! is there any interest in adding WebAssembly as a supported "language"? I've been experimenting with this wasm interpreter, it can run binary wasm file by interpreting the bytecode. It's pretty easy to attach custom api functions and claims to be pretty fast. Obviously it won't be editable inside tic-80 editor (we could show a base64 encoding or something) but it would open up a lot of possibilities, like making games in any language that targets WebAssembly.

wasm3 looks like a well written software, doesn't have dependencies and is standard c, should compile on most platforms.

i'd like to give it a try eventually :)

rdrpenguin04 commented 3 years ago

One problem with that is sandboxing WASM inside TIC-80 itself when TIC-80 is running on the web, as well as handing APIs to it.

The other thing is that WASM was really made for the web; putting it in a toy computer doesn't quite make sense.

Good thinking though!

msx80 commented 3 years ago

One problem with that is sandboxing WASM inside TIC-80 itself when TIC-80 is running on the web, as well as handing APIs to it.

It shouldn't be a problem, wasm3 is an interpreter and it says it runs just fine on the browser.

The other thing is that WASM was really made for the web; putting it in a toy computer doesn't quite make sense.

Well it surely was born for that, but it's becoming an important generic "architecture independent assembler", and can run outside the browser just fine. The WASI initiative is bringing an OS abstraction layer so that wasm program can be executed on any machine just like other regular programs (we wouldn't need that tho, we would just link the tic80 api).

I agree that, philosophically, it's not exacly at home on TIC-80, but would add so many languages to the machine at the price of one. And it looks like it's pretty clean to integrate. I mean, it's not too far stretched to think that we could run doom on TIC-80 with that thing :D ok maybe not but still. Anyway, looks fun enougth to give it a try

nesbox commented 3 years ago

Hi, We could add a new chunk type with binary data and just show this data like in hex viewers. image

Or maybe, we could disassemble the wasm byte code, allow the user to edit the ASM lang, and compile it back on saving.

In case the user uses GoLang/clang or any other to compile to WASM, we can add import wasm ... command to embed it to a cart.

Just my thoughts on how it could work and I think it's definitely a good idea to add WASM support :)

nesbox commented 3 years ago

Just realized, seems we can add normal CPU limitation with fixed instructions per tick with wasm ...

StinkerB06 commented 3 years ago

Can a cartridge have both Lua and WASM inside by that matter?

On Sun, Nov 29, 2020, 8:04 AM Vadim Grigoruk notifications@github.com wrote:

Just realized, seems we can add normal CPU limitation with fixed instructions per tick with wasm ...

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/nesbox/TIC-80/issues/1259#issuecomment-735416473, or unsubscribe https://github.com/notifications/unsubscribe-auth/AHFK2N4GLKHTU6TCFK5VIFLSSJWJVANCNFSM4UGOHF7Q .

nesbox commented 3 years ago

It can have both Lua and WASM but will be loaded only one depending on the priority.

Anrock commented 3 years ago

Hm, this could be slightly unexpected way implementing #1007

msx80 commented 3 years ago

We could add a new chunk type with binary data and just show this data like in hex viewers.

Uhm it would be a great amount of work, i don't know if it's worth the effort.

Or maybe, we could disassemble the wasm byte code, allow the user to edit the ASM lang, and compile it back on saving.

Yes, wasm has a 1 to 1 binary and text representation called wat, but there are two problems: first, we'd need a new dependency for a converter (most probably this one, altho at first sight it's pretty small and clean). Second, the text representation is a bit horrible, looks nothing like an ASM, it's some stack-machine s-expression lisp-like crap, like this:

(module
 (type $FUNCSIG$viii (func (param i32 i32 i32)))
 (type $FUNCSIG$ii (func (param i32) (result i32)))
 (type $FUNCSIG$iii (func (param i32 i32) (result i32)))
 (import "env" "pix" (func $pix (param i32 i32 i32)))
 (import "env" "printf" (func $printf (param i32 i32) (result i32)))
 (table 0 anyfunc)
 (memory $0 1)
 (data (i32.const 16) "hello there!\00")
 (export "memory" (memory $0))
 (export "main" (func $main))
 (func $main (; 2 ;) (result i32)
  (local $0 i32)
  (local $1 i32)
  (set_local $0
   (i32.const 0)
  )
  (loop $label$0
   (set_local $1
    (i32.const 0)
   )
   (loop $label$1
    (call $pix
     (get_local $1)
     (get_local $0)
     (i32.const 1)
    )
    (br_if $label$1
     (i32.ne
      (tee_local $1
       (i32.add
        (get_local $1)
        (i32.const 1)
       )
      )
      (i32.const 240)
     )
    )
   )
   (br_if $label$0
    (i32.ne
     (tee_local $0
      (i32.add
       (get_local $0)
       (i32.const 1)
      )
     )
     (i32.const 136)
    )
   )
  )
  (drop
   (call $printf
    (i32.const 16)
    (i32.const 0)
   )
  )
  (i32.const 0)
 )
)

Beside being scarcely human editable, it has the problem of being super verbose, i'm afraid it would eat up the allowed 64k of text pretty quickly. This is a 1167 bytes translation of a 167 bytes C program. The binary representation is 221 bytes. On the other hand, any assembler language requires much more code than an high level counterpart like LUA to achieve the same result, so 64k of text would be a limiting factor anyway.

Anyway for a start, in the proof-of-concept, i'm going to probably encode the binary representation to base64 and vice versa or something.

In case the user uses GoLang/clang or any other to compile to WASM, we can add import wasm ... command to embed it to a cart.

Yes something like that would be good, it requires some tought on how stuff is stored in a cart, i'm not totally familiar. Like sprites and sounds.

Hm, this could be slightly unexpected way implementing #1007

In a way yes, althou as showed before it doesn't look like any 8 bit era assembler of any kind

Anrock commented 3 years ago

In a way yes, althou as showed before it doesn't look like any 8 bit era assembler of any kind

Oof, yes. Totally forgot that wasm text is fugly. And it's also stack-based.

OTOH - if we compile everything to wasm and have only wasm interpreter instead of the whole zoo as of now - we can have CPU cycle limiter. Or at least reuse wasm interpreter for custom 8bit assembler - should be relatively easy to translate.

StinkerB06 commented 3 years ago

Beside being scarcely human editable, it has the problem of being super verbose, i'm afraid it would eat up the allowed 64k of text pretty quickly.

Actually, the newest TIC-80 version removed the code size restrictions. When you save a cart that's 65536 characters or greater, it saves in a compressed format in a separate cartridge chunk, hoping that never hits above 65535 bytes.

msx80 commented 3 years ago

We have the very first C program running on TIC-80 (maybe) :D

cprogram

(sorry for the picture of a TV but i was in hurry) The code is as follow, compiled online from WasmExplorer:

void pix(int x, int y, int color);

void TIC()
{
  for (int x = 0; x<240; x++)
  {
    for(int y = 0; y<136; y++)
    {
      pix(x, y, (y*x) % 16 );
    }
  }
}

compiled to a wat of about 1k and a wasm of 136 bytes. It's just a proof of concept, it reads wasm code from a fixed external file (what's in the cart is ignored) and only the pix function is wired, but it shows the system works.

asiekierka commented 3 years ago

I feel that adding languages which work best only when used outside of a fantasy computer would defeat a key "design value" of fantasy computers.

If you want C, you could consider using something like https://github.com/jpoirier/picoc

RobLoach commented 3 years ago

That is an interesting question, @asiekierka. While having WASM gives support for using languages like C, Rust, Go, JavaScript, etc, it would require compilation outside of the TIC-80 application itself. The cart wouldn't really have code associated with it, but a binary chunk instead. Perhaps the code would just be something like script: wasm, as nesbox hinted above.

msx80 commented 3 years ago

I feel that adding languages which work best only when used outside of a fantasy computer would defeat a key "design value" of fantasy computers.

If you want C, you could consider using something like https://github.com/jpoirier/picoc

That's a fair point. But also, FC means different things to different people. Many could find it useful to work with a language they're more familiar with. Anyway, i'm just experimenting a bit, i like this kind of stuff and if nesbox find it unfitting i'm totally ok with it not being included in TIC-80, really. Personally, given that it can be implemented with minimal or no disruption of the codebase, i like the idea. After my first game, that i programmed completely from within TIC-80, i wrote the others in external editors anyway. TIC-80 is pretty usable actually, but still i prefer to have more code on the screen and more readability. TIC-80 instant reload is perfect for a mixed experience (like graphics on tic and code on an editor).

So a little update. I actually ditched the idea of having some binary base64 encoding as the code and went on to include wat2wasm, since as @StinkerB06 said the code limit has been lifted. So now the code you see is the actual textual representation (wat), you can change it, comment on it and everything. It is translated to binary representation on the fly and executed. Language selector is ;; script: wasm

screenshot

I think i like it this way, it integrates just like any other language, but there's just a mainly practical problem: if i program something with any c or rust language, i can easily get the Wat but it's a bit hard to efficiently put it into the cart (beside manually copying/pasting inside tic80). Ideally we should use text carts and the instant reloading, but they contains some header and footer comments with necessary stuff, namely the "script: wasm" comment and the comment encoded assets at the end. This makes it impossible to just compile&overwriting your wat.

One solution would be like nesbox said to use an "import" command, but i don't like it very much as it separates code in different files and breaks the neat "self included cart" model (just think of the "dofile" command was removed as it proved to be problematic IIRC). Also i don't want to bring disruption to the codebase as it's pretty neat as it is. Perhaps some simple external scripting trick that replace the code in a cart could be enought? some "sed" command or something, to be run after compilation.

msx80 commented 3 years ago

Hi there, so i reached a good status with this thing. I've committed everything in my fork at https://github.com/msx80/TIC-80 if anyone wants to give it a try. I've implemented most used APIs, like spr, map, cls, pix, circ, rect etc and they works, the others should be straightforward to implement. Error management works. There's a demo cart included and you can go "new wasm" to get a new wasm cart ready to run. Here you can find a couple of C demo program with simple instructions to build them.

The most problematic thing now is the dependency on Wabt, which has a couple of problems: 1) it's C++ and it needs to be built using -std=c++11, so i added the flag on CMakeLists and 2) the project uses a cmake configuration process to generate a config.h file. Since i'm not sure it can be called from TIC-80 build, i just added a materialized config.h, which is good for standard linux build platform (but fails on other). It should be easy to fix it dinamically but i'm not sure it's the right way. This dependency is needed to convert from textual representation (wat) to binary (wasm). Alas i couldn't find any simpler, perhaps C based "wat2wasm" converters anywhere. The interpreter (wasm3) works without problem and performances seems good, i haven't tested it extensively but it can do a full screen "pix" with no problems.

If there's interest in including this feature on tic80 i can go on and implement the missing apis etc. and perhaps discuss the best way to handle the wabt dependency.

Anrock commented 3 years ago

I wonder if some kind of "compile" command should be added to TIC-80 console which will be noop for lua and whatever else for wasm, moonscript, wren and other things.

joshgoebel commented 2 years ago

@msx80 You still have interest in this? If you'd be willing to revive your branch and rebase it onto the latest code in master I might be able to assist a bit. I'm much more interested in this after playing around with WASM in WASM-4 and seeing how nice it is to build compiled things (and how easy it makes porting small games). In fact because of all the knowledge sharing possible with the WASM-4 project I think this is probably a much smarter target for compiling to than my work on 68000 emulation.

What I think is needed here:

Not sure how far you got with the header part... Once we have basic WASM support in place we should be able to pretty much support the same list of compiled languages that WASM-4 supports: C, C++, D, Go, Nelua, Nim, Odin, Rust, Zig, etc...

Some of these might be a tight fit with only 12kb of usable RAM, but then I'd argue that for the WASM target at least we need to increase the RAM to be comparable with the other scripted environs... perhaps with WASM (and only WASM) you get an extra 64kb of user space RAM... to make up for the megabytes and megabytes you can easily allocate in one of the scripted environs.

joshgoebel commented 2 years ago

I'm looking at initWasm but I can't find where you map the TIC's RAM into the WASM memory space, are you not doing that?

msx80 commented 2 years ago

Hi there, no i'm not mapping it, memory can be accessed with the poke/peek commands.. I let the wasm program allocate its own (unlimited) memory with the "memory" instruction (which is done by the interpreter). Feels like the closest thing to script languages. But the mapping could be done if needed, it could have its uses.

I'd love the idea to resume the work on wasm, i'll reply properly as soon as i have some spare moments! Also i wasn't following the "binary chunk" stuff, i have to dig into it, looks good for wasm!

msx80 commented 2 years ago

Ps i don't know if you saw it but here is a working, compilable proof of concept with basic C api, it should be a good starting point:

https://github.com/msx80/TIC-80-wasm-demo

joshgoebel commented 2 years ago

Hi there, no i'm not mapping it, memory can be accessed with the poke/peek commands..

True, but does nothing for porting where it's expected that RAM is RAM... I can use pointers, etc... plus doing essentially memcpy(a,b,1000) should not require 1000 round trips outside WASM to the TIC-80 C code to call peek and poke 2000 times... all of this should happen inside the WASM VM with directly access to RAM.

Feels like the closest thing to script languages.

I see your thinking, but I think this is entirely the wrong direction... the unlimited nature of the other runtimes is often seen as a problem, not something we want to continue to emulate if we finally have a runtime that's going to make it easy to lock such things down.

I'll have to look at how WASM3 allows memory to be abstracted... what we're doing now with VRAM might be problematic if WASM3 expects just a pointer to a contiguous chunk of RAM vs say having callbacks to access RAM.

vshymanskyy commented 2 years ago

There's no built in api for that, but it should be easy to modify wasm3 to abstract away the memory access. I can help with that ;)

joshgoebel commented 2 years ago

Are you familiar with the source? I'm trying to just find where various opcodes are handled or the main runloop and not seeing it... I wanted to see what abstraction they already have in front of the memory access...

joshgoebel commented 2 years ago

Ok, m3_exec.h has the goods. I think it might just be easiest to create the VM and then offer it's memory back to TIC-80... WASM thinking in 64kb blocks so at the very least we're getting 128kb... which gives you 32kb extra "for free" for memory for use by programs... Evidentally the setup is pretty trivial at least:

env = m3_NewEnvironment();
runtime = m3_NewRuntime(env, 4096, NULL);
runtime->memory.maxPages = 2;
ResizeMemory(runtime, 2);

Then just m3_GetMemory to get the pointer and we have our TIC RAM setup and ready to go and already inside the VM.

joshgoebel commented 2 years ago

@msx80 I started the rebasing myself an probably will finish it tomorrow. I'll add a link when I push a branch.

msx80 commented 2 years ago

Hi there, no i'm not mapping it, memory can be accessed with the poke/peek commands..

True, but does nothing for porting where it's expected that RAM is RAM... I can use pointers, etc... plus doing essentially memcpy(a,b,1000) should not require 1000 round trips outside WASM to the TIC-80 C code to call peek and poke 2000 times... all of this should happen inside the WASM VM with directly access to RAM.

Ok, sounds good to me. If @vshymanskyy (great to see you here :) ) can help with abstract memory access, it would be easy to do.

I see your thinking, but I think this is entirely the wrong direction... the unlimited nature of the other runtimes is often seen as a problem, not something we want to continue to emulate if we finally have a runtime that's going to make it easy to lock such things down.

Uhm i see what you say but i'm not sold. For some people it's a problem, for me it's an essential feature :) First off, the tic80 "ram" is not where you're supposed to put your game stuff, no game in tic80 puts game data there. It's supposed to be some kind of "hardware interfacing ram" and game data is put into some outside heap. Also historically the ram has been expanded and lower free positions has been claimed. I see the challenging appeal of having a small, fixed amount of memory, other FCs do that, but not tic80. I think if we implement WASM it should be treated the same way as other languages. But hey that's my view, there's no right or wrong way to do it, it's more about the phylosophy of the fantasy console, if anything @nesbox should decide :) Personally i prefer not being limited with RAM, i think it suits better for doing "full blown games" as opposed to demos or small, proof-of-concept games (althou it's not at all necessary).

Just as the binary chunk stuff, this is another dividing point between "dynamic scripts based FC" and "down to the metal FC", that needs to be sorted out if tic80 wants to be both :)

joshgoebel commented 2 years ago

All code below is pseudo-code, not actual WASM instructions since I don't know all the instructions off the top of my head.

Are their any thoughts on the LOW-LEVEL Wasm API? WASM functions have a fixed # of parameters and a single output value (I'm sure WASM-3 [our interpretter] supports multi-output values yet)... so that means that a lot of function signatures will look similar to a low-level C AP using -1 "nulls":

// psuedo-code!!
pix(0,0,5) // write pixel, returns 5
pix(0,0,-1) // read pixel

So our pix would just be a function with 3 arguments that always returns the color... someone is free to ignore it. And if you want to read the pixel you pass -1 for the color to be written. Of course in the higher-level language wrapper libraries there may be much nicer ways to deal with this. In C++ for example you'd likely just make the -1 a default argument for the last parameter and then the 2 or 3 argument version of pix works "magically".

This however gets uglier for some fo the functions with a zillion params:

map [x=0 y=0] [w=30 h=17] [sx=0 sy=0] [colorkey=-1] [scale=1] [remap=nil]

Do we want to just do the lowest-level thing possible in WASM and require all these params, all the time? IE to just call map() one would need (low-level) the following WASM instructions:

// psuedocode
i32.push 0
i32.push 0
i32.push 30
i32.push 17
i32.push 0
i32.push 0
i32.push -1
i32.push 1
i32.push -1
call $map

I think this may be OK - and let the higher-level libraries sort out doing it nicer if possible...

It does get a bit ridiculous for some functions though like clip... we either require the programmer to keep track of the details, or we build it into the TIC_80 API layer... so either:

i32.push 0
i32.push 0
i32.push MAX_WIDTH
i32.push MAX_HEIGHT
call $clip

OR:

i32.push -1
i32.push -1
i32.push -1
i32.push -1
call $clip

I actually started out with clip(x,y,w,h) and noclip()... would we be open to possibly expanding the API in a few areas (for all languages) for some of the more ridiculous examples... or would it be ok if WASM was just a "special case" and supported a slightly extended API to make some for the edge cases nicer?

joshgoebel commented 2 years ago

Taking a simpler example:

sync [mask=0] [bank=0] [tocart=false]

I feel like the "simple just works" TIC approach boiled down to WASM might would be -1 for "i dunno" or "just do the right thing"... ie, no one would need to know the defaults to call the API - that would be all handled INSIDE TIC, not WASM. So to call sync(), effectively sync(0,0,false) you would need to know that -1 was "default" and the number of arguments (to push the right number of args onto the stack).

i32.push -1
i32.push -1
i32.push -1
call $func

Does that sound right? This would prevent all the different language libraries from having to reimplement the defaults over and over for every language.

IE, our C interface would then look like the following:

m3ApiRawFunction(wasmtic_sync)
{
    m3ApiGetArg      (int32_t, mask);
    m3ApiGetArg      (int8_t, bank);
    m3ApiGetArg      (int8_t, tocart);

    tic_mem* tic = (tic_mem*)getWasmCore(runtime);

    bool toCart;

    if (mask == -1) { mask = 0; }
    if (bank == -1) { bank = 0; }
    toCart = (tocart == -1 || tocart == 0) ? false : true;

    if (bank >=0 && bank < TIC_BANKS)
        tic_api_sync(tic, mask, bank, toCart);

    m3ApiSuccess();
}
joshgoebel commented 2 years ago

https://github.com/joshgoebel/zig_bunnymark

And the TIC-80 library header I'm using: https://github.com/joshgoebel/zig_bunnymark/blob/main/src/tic80.zig

Running ~ as fast as Lua on my machine (~700 bunnies or so before FPS drops)... I wonder if we're constrained by the graphics pipeline more than the VMs at this point? (I haven't tried to optimize anything, just do the simplest possible port of the Lua code to Zig).

What's working:

What's not:

What I'm not sure we need (at least part of initial pass):

I want to look for any last cleanups and then I'll rebase and push the latest changes I have in my branch. Note: I'm not using any emscripten stuff at all (and not really interested to based on prior experience)... this is using the same build system/stack that WASM4 is using... so if we wanted to have a C/C++ stub I'd suggest using the WASI stuff, same as Wasm4 is...

Though I'm not sure it matters HOW someone builds their wasm files, just so long as they get the imports, exports, memory, and stack pieces right, etc... I think if we're going to encourage people though and say we support a given language we should have a "blessed" library/build system that we know works and we can provide support for. That's really a separate topic though and I'll create a new issue for it.

joshgoebel commented 2 years ago

First off, the tic80 "ram" is not where you're supposed to put your game stuff, no game in tic80 puts game data there. It's supposed to be...

I'd argue only because it makes no sense for interpreted languages - it'd be 10x harder to used the built-in RAM (than to use native variables, etc). So this isn't some kind of global truism of "how it has to be", it's just what makes sense for interpreted languages. For compiled languages this isn't an option. There is just a single pool of memory (the question is how much), and nothing else.

Also historically the ram has been expanded and lower free positions has been claimed.

Not sure how that's relevant? If we want to reserve some more low RAM for future hardware expansion while we're at it we could... that could be done while still keeping an overall low memory profile.

it's more about the phylosophy of the fantasy console,

Yeah, and I think so far our philosophy has been a bit forced [because of the languages we use], rather than purely engineered... it's hard to put a cap on the interpreted languages, even if we wanted to - and there would be compatibility issues perhaps with some existing cartridges, etc... But WASM (our FIRST compiled VM) gives us an opportunity to rethink our philosophy - and I just don't think we should waste that - regardless of what we ultimately decide. :-)

if anything @nesbox should decide :)

Of course, and I'm certain he will. :-)

i prefer not being limited with RAM, i think it suits better for doing "full blown games" as opposed to demos or small, proof-of-concept games (althou it's not at all necessary).

I think the same type of games one builds in Lua should be possible with WASM... (assuming we want those games on TIC-80 in general) so perhaps that means more than 128kb, but I just don't feel that means "unlimited"... I think it's time we finally close the door on that (since we so easily can)... and say that TIC-80 WASM games have a fixed memory ceiling, even if it's a bit roomier than originally imagined. Perhaps 256kb or 512k would be more appropriate?

Plus "unlimited" isn't really an option... there is no reason to assign tons of RAM we don't need at start up and expanding RAM dynamically (I'm pretty certain) requires collaboration between the WASM runtime and the internal code, so that would require expanding the TIC-80 API to do things like allowing shrink/grow RAM... which would be something very different (and only apply to WASM)... so either way WASM is going to be unique, just because it's a very unique environment.

Also just giving WASM it's own larger memory pool is strange in another way (but perhaps unavoidable)... ie, it's kind of weird if poke(HIGH_RAM, 22) works inside WASM but not Lua... because in the Lua runtime the memory was capped at say 96kb while in WASM it was 256kb, etc...

msx80 commented 2 years ago

I'm not actually sure how much memory would be needed for a decent game, writter for example in Rust. But it should be enought to have all the necessary lists and tables and counters etc. If we have to put a cap, i suggest it be big enougth to make sure there's room for advanced games and trickeries of sorts, and to leave some room for future things. I'd go for 512k or more, perhaps 1 mega. Keep in mind that 96k is already gone for io.

Another idea: have a "-- memory: 2M" magic comment to allocate required memory?

Also just giving WASM it's own larger memory pool is strange in another way (but perhaps unavoidable)... ie, it's kind of weird if poke(HIGH_RAM, 22) works inside WASM but not Lua... because in the Lua runtime the memory was capped at say 96kb while in WASM it was 256kb, etc...

If i'm understanding correctly, we could just as well expand the ram for all implementations, for the sake of uniformity. Lua games will have some more "poke/peek accessible memory" which would very probably not care about.

Also, we'll have to decide how much free space to set aside for future developments, becouse wasm/binary games will have to start allocating memory somewhere. If they start at 96k, then we have 12k of memory allocated for future uses, and will not be expandable without breaking binary cartridges.

msx80 commented 2 years ago

I feel like the "simple just works" TIC approach boiled down to WASM might would be -1 for "i dunno" or "just do the right thing"... ie, no one would need to know the defaults to call the API - that would be all handled INSIDE TIC, not WASM. So to call sync(), effectively sync(0,0,false) you would need to know that -1 was "default" and the number of arguments (to push the right number of args onto the stack).

So the problem with how to map the API to simpler languages (like C or wasm) is tougth. Maybe we could do a separated issue for this?

Anyway, i think there are two ways: one where the defaults are implemented in the language and one were they're implemented in TIC. I also think the second one offer more value (you avoid repeating all the defaults values), but it's a bit harder to implements. It would require changes to the tic80 layer (which is not much trouble).

The most delicate thing would be to choose a value to communicate "default" to tic80. You propose -1 which works for most cases but what if -1 is also a valid option for the parameter? I think tic80 accepts negative values for coordinates in api. In "map" you can pass negatives to sx and sy, -1 would be ambiguous as the default is 0.

PS we already have some kind of "c api" in api.h. I think it's sensible to use it (or at least take it into consideration)

joshgoebel commented 2 years ago

If i'm understanding correctly, we could just as well expand the ram for all implementations, for the sake of uniformity. Lua games will have some more "poke/peek accessible memory" which would very probably not care about.

I think this would solve #1777 (provided it was a multiple of 64kb). However it's very wasteful of memory... if a Lua game is using 2mb and we're allocated 2mb of "RAM" that it's NEVER going to use, we'd doubled our memory requirements - this could possible have an impact on some fo the smaller platforms we run on? I'm not sure how small we go...

joshgoebel commented 2 years ago

@msx80 Would you have any time to work on the C/C++ low-level libs? I know you already have a tiny slice done... I'd recommend we follow WASM-4 and use WASI, but it's also possible they are more interchangeable than I realize. I think at this point a bunny mark written in C would be nice. :-) Really I suppose it's what's easier for the most people... since ultimately people should be able to setup whatever build tool chain they like as long as you have the ability to pass the right memory pointers to the linker, ie: It needs to know the bottom 80kb of RAM is reserved for mapped I/O, etc...

The WASM-4 people have already figured this out which is why I think it's nice to not reinvent the wheel. :-)

joshgoebel commented 2 years ago

Closing via #1772. (which was manually merged)