-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Generating WASM for TIC-80 #7
Comments
Hey @ion1! Happy to help you :) Declaring a
|
Thank you! I got it to work. I found two ways to add the data value as a dependency to the function which uses it: const foo = func(/*...*/); foo.deps.push(bar); const foo = func({ /*...*/ }, ([], [], ctx) => { ctx.deps.push(bar); }); Is one of them preferred over the other?
TIC-80 only ever loads a single Wasm module at a time. The named imports are just for TIC-80 to provide the RAM and a number of system functions (such as Here's TIC-80's definition of the print function to be provided, and here's it being linked to the Wasm runtime. PS. This is a small thing, but it would be nice to be able to do I'll just dump my current code in here in case someone reading this later finds it helpful.import {
call,
Const,
data,
Dependency,
drop,
func,
i32,
importFunc,
importMemory,
Module,
} from "wasmati";
import binaryen from "binaryen";
import { writeFileSync } from "fs";
function main() {
const memory = importMemory({ min: 4, max: 4 });
setImportPath(memory, "env", "memory");
const enc = new TextEncoder();
const helloWorldOffset = 0x18000;
const helloWorld = data(
{ memory, offset: Const.i32(helloWorldOffset) },
enc.encode("Hello world!\x00")
);
const endOfRamText = enc.encode("The end of RAM\x00");
const endOfRamOffset = 0x40000 - endOfRamText.length;
const endOfRam = data(
{ memory, offset: Const.i32(endOfRamOffset) },
endOfRamText
);
const cls = importFunc({ in: [i32], out: [] }, () => {});
setImportPath(cls, "env", "cls");
const print = importFunc(
{ in: [i32, i32, i32, i32, i32, i32, i32], out: [i32] },
() => {}
);
setImportPath(print, "env", "print");
const TIC = func({ in: [], out: [], locals: [] }, ([], [], _ctx) => {
call(cls, [13]);
call(print, [helloWorldOffset, 84, 61, 15, 0, 1, 0]);
drop();
call(print, [endOfRamOffset, 79, 68, 14, 0, 1, 0]);
drop();
});
TIC.deps.push(helloWorld);
TIC.deps.push(endOfRam);
const module = Module({
exports: { TIC },
memory,
});
const wasmUnopt = module.toBytes();
const moduleB = binaryen.readBinary(module.toBytes());
moduleB.optimize();
const wasm = moduleB.emitBinary();
console.info(`Unoptimized: ${wasmUnopt.length}`);
console.info(`Optimized: ${wasm.length}`);
writeFileSync("build/cart-unopt.wasm", wasmUnopt);
writeFileSync("build/cart.wasm", wasm);
}
function setImportPath(
input: Dependency.AnyImport,
module: string,
path: string
) {
input.module = module;
input.string = path;
}
main(); |
Out of these I would clearly prefer the first one! The callback is supposed to contain just the function body. I just want to mention though that you don't usually need explicit dependency pushing 😅 Like, when you call one function in another, it becomes a dependency automatically. Only with
Good feedback, there are a couple of places where "stack arguments as input" is not implemented yet and it's always nice to be able to do that |
On second thought, taking that JavaScript function might actually be really useful: I could mock the TIC-80 env functions my Wasm code uses and run a test suite outside TIC-80. Is there a way to provide a custom Perhaps it could look something like: const mockMemory = new WebAssembly.Memory({ initial: 4, maximum: 4 });
const memory = importMemory(
{ module: "env", path: "memory", min: 4, max: 4 },
mockMemory,
); |
That's already what importMemory does :D |
In general wasmati is already optimizing for the flow where you instantiate the wasm and use it directly in the same JS process. That's why all import objects have their JS import attached right away |
Ah, how silly of me. :-D As I didn't use that code path earlier, I failed to consider that it may already exist. I see that I can let It would be nice if the function parameter to |
Thanks for the project!
I'm trying to generate WASM for TIC-80. I have encountered the following issues while trying to generate a hello world using wasmati:
I don't see how to import the memory.
importMemory
doesn't seem to take module/name parameters. My current result from wasmati looks like the following.One needs to do the following to gain access to the 256 KiB of memory TIC-80 provides.
I don't see how to import functions.
importFunc
doesn't seem to take module/name parameters, and it takes a JavaScript function parameter which I can't provide. My current result from wasmati looks like the following:Examples of importing functions provided by TIC-80:
I don't see how to add data with a starting offset. The content parameter to
importMemory
seems to write starting from zero.In TIC-80, The first 96 KiB are reserved for things such as video RAM and I/O. To write data into the beginning of the free RAM, one needs to do the following.
Apologies if I have missed how to do these things correctly.
A hello world in WAT
My closest attempt at implementing it using wasmati
The output from the above
The text was updated successfully, but these errors were encountered: