diff --git a/sys/lib/test/README.md b/sys/lib/test/README.md new file mode 100644 index 000000000..c9cfed377 --- /dev/null +++ b/sys/lib/test/README.md @@ -0,0 +1,8 @@ +Development helpers and tests for libnethack.a and nethack.js. + +Copy these files to the NetHack root directory. Commands include: +* run.sh wasm - rebuild makefiles and build nethack.js +* run.sh runwasm - simple testing of nethack.js +* run.sh lib - rebuild makefiles and build libnethack.a +* run.sh runlib - simple testing of libnethack.a +* run.sh bin - build the MacOS binary \ No newline at end of file diff --git a/sys/lib/test/libtest.c b/sys/lib/test/libtest.c new file mode 100644 index 000000000..9b554b986 --- /dev/null +++ b/sys/lib/test/libtest.c @@ -0,0 +1,106 @@ +#include + +/* external functions */ +int nhmain(int argc, char *argv[]); +typedef void(*stub_callback_t)(const char *name, void *ret_ptr, const char *fmt, ...); +void stub_graphics_set_callback(stub_callback_t cb); + +/* forward declarations */ +void window_cb(const char *name, void *ret_ptr, const char *fmt, ...); + + +int main(int argc, char *argv[]) { + stub_graphics_set_callback(window_cb); + nhmain(argc, argv); +} + +void window_cb(const char *name, void *ret_ptr, const char *fmt, ...) { + /* TODO -- see windowCallback below for hints */ + + /* DO SOMETHING HERE */ + *ret_ptr = yourFunctionToRenderGraphics(name, va_list args); +} + + + + +#if 0 +function variadicCallback(name, retPtr, fmt, args) { + // console.log ("variadicCallback called..."); + // console.log("typeof name", typeof name); + // console.log("typeof fmt", typeof fmt); + // console.log("typeof args", typeof args); + name = Module.UTF8ToString(name); + fmt = Module.UTF8ToString(fmt); + // console.log ("name:", name); + // console.log ("fmt:", fmt); + let argTypes = fmt.split(""); + let retType = argTypes.shift(); + // console.log ("arg count:", argTypes.length); + // console.log ("arg types:", argTypes); + // console.log ("ret type:", retType); + + let jsArgs = []; + for (let i = 0; i < argTypes.length; i++) { + let ptr = args + (4*i); + let val = typeLookup(argTypes[i], ptr); + jsArgs.push(val); + } + console.log(`graphics callback: ${name} [${jsArgs}]`); + setReturn(retPtr, retType); +} + +function setReturn(ptr, type, value = 0) { + switch (type) { + case "p": + throw new Error("not implemented"); + case "s": + value=value?value:"(no value)"; + var strPtr = Module.getValue(ptr, "i32"); + Module.stringToUTF8(value, strPtr, 1024); + break; + case "i": + Module.setValue(ptr, value, "i32"); + break; + case "c": + Module.setValue(ptr, value, "i8"); // 'Z' + break; + case "f": + // XXX: I'm not sure why 'double' works and 'float' doesn't + Module.setValue(ptr, value, "double"); + break; + case "d": + Module.setValue(ptr, value, "double"); + break; + case "v": + break; + default: + throw new Error("unknown type"); + } +} + +function typeLookup(type, ptr) { + switch(type) { + case "s": // string + return Module.UTF8ToString(Module.getValue(ptr, "*")); + case "p": // pointer + return Module.getValue(Module.getValue(ptr, "*"), "*"); + case "c": // char + return String.fromCharCode(Module.getValue(Module.getValue(ptr, "*"), "i8")); + case "0": /* 2^0 = 1 byte */ + return Module.getValue(Module.getValue(ptr, "*"), "i8"); + case "1": /* 2^1 = 2 bytes */ + return Module.getValue(Module.getValue(ptr, "*"), "i16"); + case "2": /* 2^2 = 4 bytes */ + case "i": // integer + case "n": // number + return Module.getValue(Module.getValue(ptr, "*"), "i32"); + case "f": // float + return Module.getValue(Module.getValue(ptr, "*"), "float"); + case "d": // double + return Module.getValue(Module.getValue(ptr, "*"), "double"); + default: + throw new TypeError ("unknown type:" + type); + } +} +#endif /* 0 */ \ No newline at end of file diff --git a/sys/lib/test/run.sh b/sys/lib/test/run.sh new file mode 100755 index 000000000..5e2b36dfe --- /dev/null +++ b/sys/lib/test/run.sh @@ -0,0 +1,40 @@ +#!/bin/bash -x + +if [ x$1 == "xlib" ]; then + echo Doing lib... + make spotless + cd sys/lib + ./setup.sh hints/macOS.2020 + cd ../.. + make +fi + +if [ x$1 == "xrunlib" ]; then + LIBS="-Lsrc -lnethack -Llib/lua -llua -lm" + BADLIBS="-lncurses" + gcc -o nhlibtest libtest.c $LIBS $BADLIBS + ./nhlibtest +fi + +if [ x$1 == "xwasm" ]; then + echo Doing wasm... + make spotless + cd sys/lib + ./setup.sh hints/wasm + cd ../.. + make +fi + +if [ x$1 == "xrunwasm" ]; then + node test.js +fi + +if [ x$1 == "xbin" ]; then + echo Doing bin... + make spotless + cd sys/unix + ./setup.sh hints/macOS.2020 + cd ../.. + make +fi + diff --git a/sys/lib/test/test.js b/sys/lib/test/test.js new file mode 100644 index 000000000..146b56d8a --- /dev/null +++ b/sys/lib/test/test.js @@ -0,0 +1,186 @@ +const util = require("util"); + +// Emscripten Module config +let Module = { + // noInitialRun: true, + onRuntimeInitialized: function () { + setGraphicsCallback(); + }, +}; + +var factory = require("./src/nethack.js"); + +// load and run the module +factory(Module); + +function setGraphicsCallback() { + console.log("setting callback function"); + let cb = Module.addFunction(windowCallback, "viiii"); + + console.log("doing call"); + Module.ccall( + "stub_graphics_set_callback", // C function name + null, // return type + ["number"], // arg types + [cb], // arg values + {async: true} // options + ); + + /* TODO: removeFunction */ +} + +function windowCallback(name, retPtr, fmt, args) { + // console.log ("variadicCallback called..."); + // console.log("typeof name", typeof name); + // console.log("typeof fmt", typeof fmt); + // console.log("typeof args", typeof args); + name = Module.UTF8ToString(name); + fmt = Module.UTF8ToString(fmt); + // console.log ("name:", name); + // console.log ("fmt:", fmt); + let argTypes = fmt.split(""); + let retType = argTypes.shift(); + // console.log ("arg count:", argTypes.length); + // console.log ("arg types:", argTypes); + // console.log ("ret type:", retType); + + let jsArgs = []; + for (let i = 0; i < argTypes.length; i++) { + let ptr = args + (4*i); + let val = typeLookup(argTypes[i], ptr); + jsArgs.push(val); + } + console.log(`graphics callback: ${name} [${jsArgs}]`); + let retVal = doGraphics(name, ... jsArgs); + setReturn(name, retPtr, retType, retVal); +} + +function typeLookup(type, ptr) { + switch(type) { + case "s": // string + return Module.UTF8ToString(Module.getValue(ptr, "*")); + case "p": // pointer + ptr = Module.getValue(ptr, "*"); + if(!ptr) return 0; // null pointer + return Module.getValue(ptr, "*"); + case "c": // char + return String.fromCharCode(Module.getValue(Module.getValue(ptr, "*"), "i8")); + case "0": /* 2^0 = 1 byte */ + return Module.getValue(Module.getValue(ptr, "*"), "i8"); + case "1": /* 2^1 = 2 bytes */ + return Module.getValue(Module.getValue(ptr, "*"), "i16"); + case "2": /* 2^2 = 4 bytes */ + case "i": // integer + case "n": // number + return Module.getValue(Module.getValue(ptr, "*"), "i32"); + case "f": // float + return Module.getValue(Module.getValue(ptr, "*"), "float"); + case "d": // double + return Module.getValue(Module.getValue(ptr, "*"), "double"); + default: + throw new TypeError ("unknown type:" + type); + } +} + +function setReturn(name, ptr, type, value = 0) { + + switch (type) { + case "p": + throw new Error("not implemented"); + case "s": + if(typeof value !== "string") + throw new TypeError(`expected ${name} return type to be string`); + value=value?value:"(no value)"; + var strPtr = Module.getValue(ptr, "i32"); + Module.stringToUTF8(value, strPtr, 1024); + break; + case "i": + if(typeof value !== "number" || !Number.isInteger(value)) + throw new TypeError(`expected ${name} return type to be integer`); + Module.setValue(ptr, value, "i32"); + break; + case "c": + if(typeof value !== "number" || value < 0 || value > 128) + throw new TypeError(`expected ${name} return type to be integer representing an ASCII character`); + Module.setValue(ptr, value, "i8"); + break; + case "f": + if(typeof value !== "number" || isFloat(value)) + throw new TypeError(`expected ${name} return type to be float`); + // XXX: I'm not sure why 'double' works and 'float' doesn't + Module.setValue(ptr, value, "double"); + break; + case "d": + if(typeof value !== "number" || isFloat(value)) + throw new TypeError(`expected ${name} return type to be float`); + Module.setValue(ptr, value, "double"); + break; + case "v": + break; + default: + throw new Error("unknown type"); + } + + function isFloat(n){ + return n === +n && n !== (n|0) && !Number.isInteger(n); + } +} + +let winCount = 0; +function doGraphics(name, ... args) { + switch(name) { + case "shim_create_nhwindow": + winCount++; + console.log("creating window", args, "returning", winCount); + return winCount; + case "shim_yn_function": + case "shim_message_menu": + return 121; // 'y' + case "shim_nhgetch": + case "shim_nh_poskey": + return 46; + default: + return 0; + } +} + +// var Module = null; +// factory(Module).then((ret) => { +// if(ret !== Module) { +// console.log("ret and Module are not the same"); +// } else { +// console.log("ret and Module are the same"); +// } +// // console.log("Module", util.inspect(Module, {depth: null, showHidden: true})); +// // TODO: +// // stub_graphics_set_callback(); + +// // options: +// // https://emscripten.org/docs/api_reference/module.html +// // logReadFiles +// // printWithColors +// // noInitialRun +// // onRuntimeInitialized +// console.log("doing run"); +// // Module.run(); + +// // // main loop +// // console.log("creating function"); +// // let fn = Module.addFunction(wasmCallback, "vii"); + +// // console.log("doing call"); +// // Module.ccall( +// // "mainloop", // C function name +// // null, // return type +// // ["number"], // arg types +// // [fn], // arg values +// // {async: true} // options +// // ); +// // // TODO: removeFunction(fn) + +// // var i = 0; +// // setInterval(function() { +// // console.log("interval", i++); +// // },1000); +// }); +