mirror of
https://github.com/exoticorn/microw8.git
synced 2026-01-20 11:16:42 +01:00
move all imports to module "env", some tweaks to rust example
This commit is contained in:
@@ -5,8 +5,8 @@ A nightly rust compiler is needed for the unstable sqrtf32
|
||||
intrinsic.
|
||||
|
||||
Simply compiling with rustc as shown in build.sh results in a
|
||||
339 byte tunnel.wasm. Using wasm-opt this can be reduced to
|
||||
244 bytes.
|
||||
342 byte tunnel.wasm. Using wasm-opt this can be reduced to
|
||||
243 bytes.
|
||||
|
||||
When you disassemble this wasm file using wasm2wat you can see
|
||||
these globals and exports:
|
||||
@@ -21,5 +21,5 @@ values that are not simple scalars (i32, f32, etc.). Since our
|
||||
code doesn't actually use any of that, we can just delete them
|
||||
in a text editor and assemble the code again with wat2wasm.
|
||||
|
||||
This gives us a 200 byte wasm file. Running this through
|
||||
This gives us a 199 byte wasm file. Running this through
|
||||
uw8-tool pack brings us to the final size of 137 bytes.
|
||||
@@ -1,25 +1,43 @@
|
||||
#![no_std]
|
||||
#![feature(core_intrinsics)]
|
||||
|
||||
#[link(wasm_import_module = "math")]
|
||||
extern "C" {
|
||||
fn atan2(x: f32, y: f32) -> f32;
|
||||
mod env {
|
||||
// "env" is the default module for imports, but it is still needed here
|
||||
// since there is a compiler builtin of the same name which is used
|
||||
// if we don't make it clear that this is a module import.
|
||||
#[link(wasm_import_module = "env")]
|
||||
extern "C" {
|
||||
pub fn atan2(x: f32, y: f32) -> f32;
|
||||
}
|
||||
}
|
||||
|
||||
fn atan2(x: f32, y: f32) -> f32 {
|
||||
unsafe { env::atan2(x, y) }
|
||||
}
|
||||
|
||||
fn sqrt(v: f32) -> f32 {
|
||||
unsafe { core::intrinsics::sqrtf32(v) }
|
||||
}
|
||||
|
||||
fn ftoi(v: f32) -> i32 {
|
||||
// The compiler is allowed to do bad things to our code if this
|
||||
// ever results in a value that doesn't fit in an i32.
|
||||
// (the joy of undefined behavior)
|
||||
// But that would trap in wasm anyway, so we don't really
|
||||
// care.
|
||||
unsafe { v.to_int_unchecked() }
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn tic(time: i32) {
|
||||
unsafe {
|
||||
for i in 0..320 * 256 {
|
||||
let t = time as f32 / 10 as f32;
|
||||
let x = (i % 320 - 160) as f32;
|
||||
let y = (i / 320 - 128) as f32;
|
||||
let d = 20000 as f32 / sqrt(x * x + y * y + 1 as f32);
|
||||
let u = atan2(x, y) * 512f32 / 3.141;
|
||||
let c = ((d + t).to_int_unchecked::<i32>() ^ (u + t).to_int_unchecked::<i32>()) as u8;
|
||||
for i in 0..320 * 256 {
|
||||
let t = time as f32 / 10 as f32;
|
||||
let x = (i % 320 - 160) as f32;
|
||||
let y = (i / 320 - 128) as f32;
|
||||
let d = 20000 as f32 / sqrt(x * x + y * y + 1 as f32);
|
||||
let u = atan2(x, y) * 512f32 / 3.141;
|
||||
let c = (ftoi(d + t) ^ ftoi(u + t)) as u8;
|
||||
unsafe {
|
||||
*((120 + i) as *mut u8) = c;
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
1
site/.gitignore
vendored
Normal file
1
site/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/public/
|
||||
@@ -14,19 +14,18 @@ at offset 120 in memory.
|
||||
|
||||
The memory has to be imported as `"env" "memory"` and has a maximum size of 256kb (4 pages).
|
||||
|
||||
Other imports provided by the platform:
|
||||
Other imports provided by the platform, also all in module `env`:
|
||||
|
||||
* in module `math`:
|
||||
* * `fn acos(f32) -> f32`
|
||||
* * `fn asin(f32) -> f32`
|
||||
* * `fn atan(f32) -> f32`
|
||||
* * `fn atan2(f32, f32) -> f32`
|
||||
* * `fn cos(f32) -> f32`
|
||||
* * `fn exp(f32, f32) -> f32`
|
||||
* * `fn log(f32) -> f32`
|
||||
* * `fn sin(f32) -> f32`
|
||||
* * `fn tan(f32) -> f32`
|
||||
* * `fn pow(f32) -> f32`
|
||||
* `fn acos(f32) -> f32`
|
||||
* `fn asin(f32) -> f32`
|
||||
* `fn atan(f32) -> f32`
|
||||
* `fn atan2(f32, f32) -> f32`
|
||||
* `fn cos(f32) -> f32`
|
||||
* `fn exp(f32, f32) -> f32`
|
||||
* `fn log(f32) -> f32`
|
||||
* `fn sin(f32) -> f32`
|
||||
* `fn tan(f32) -> f32`
|
||||
* `fn pow(f32) -> f32`
|
||||
|
||||
## `.uw8` format
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,25 +1,25 @@
|
||||
use std::{collections::HashMap, fs::File, path::Path};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use std::io::prelude::*;
|
||||
use wasm_encoder::{
|
||||
CodeSection, EntityType, Export, ExportSection, Function, FunctionSection,
|
||||
ImportSection, Instruction, MemoryType, Module, TypeSection, ValType,
|
||||
CodeSection, EntityType, Export, ExportSection, Function, FunctionSection, ImportSection,
|
||||
Instruction, MemoryType, Module, TypeSection, ValType,
|
||||
};
|
||||
use ValType::*;
|
||||
use anyhow::{Result, bail};
|
||||
use std::io::prelude::*;
|
||||
|
||||
pub struct BaseModule {
|
||||
pub types: Vec<FunctionType>,
|
||||
pub function_imports: Vec<(&'static str, String, u32)>,
|
||||
pub functions: Vec<u32>,
|
||||
pub exports: Vec<(&'static str, u32)>,
|
||||
pub memory: u32
|
||||
pub memory: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct FunctionType {
|
||||
pub params: Vec<ValType>,
|
||||
pub result: Option<ValType>
|
||||
pub result: Option<ValType>,
|
||||
}
|
||||
|
||||
impl BaseModule {
|
||||
@@ -48,18 +48,24 @@ impl BaseModule {
|
||||
}
|
||||
|
||||
let mut functions = vec![];
|
||||
add_function(&mut functions, &type_map, "math","sin", &[F32], Some(F32));
|
||||
add_function(&mut functions, &type_map, "math", "cos", &[F32], Some(F32));
|
||||
add_function(&mut functions, &type_map, "math", "tan", &[F32], Some(F32));
|
||||
add_function(&mut functions, &type_map, "math", "asin", &[F32], Some(F32));
|
||||
add_function(&mut functions, &type_map, "math", "acos", &[F32], Some(F32));
|
||||
add_function(&mut functions, &type_map, "math", "atan", &[F32], Some(F32));
|
||||
add_function(&mut functions, &type_map, "math", "atan2", &[F32, F32], Some(F32));
|
||||
add_function(&mut functions, &type_map, "math", "pow", &[F32, F32], Some(F32));
|
||||
add_function(&mut functions, &type_map, "math", "log", &[F32], Some(F32));
|
||||
add_function(&mut functions, &type_map, "sin", &[F32], Some(F32));
|
||||
add_function(&mut functions, &type_map, "cos", &[F32], Some(F32));
|
||||
add_function(&mut functions, &type_map, "tan", &[F32], Some(F32));
|
||||
add_function(&mut functions, &type_map, "asin", &[F32], Some(F32));
|
||||
add_function(&mut functions, &type_map, "acos", &[F32], Some(F32));
|
||||
add_function(&mut functions, &type_map, "atan", &[F32], Some(F32));
|
||||
add_function(&mut functions, &type_map, "atan2", &[F32, F32], Some(F32));
|
||||
add_function(&mut functions, &type_map, "pow", &[F32, F32], Some(F32));
|
||||
add_function(&mut functions, &type_map, "log", &[F32], Some(F32));
|
||||
|
||||
for i in functions.len()..64 {
|
||||
add_function(&mut functions, &type_map, "uw8", &format!("reserved{}", i), &[], None);
|
||||
add_function(
|
||||
&mut functions,
|
||||
&type_map,
|
||||
&format!("reserved{}", i),
|
||||
&[],
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
let first_function = functions.len() as u32;
|
||||
@@ -69,7 +75,7 @@ impl BaseModule {
|
||||
function_imports: functions,
|
||||
functions: vec![lookup_type(&type_map, &[I32], None)],
|
||||
exports: vec![("tic", first_function)],
|
||||
memory: 4
|
||||
memory: 4,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -92,11 +98,15 @@ impl BaseModule {
|
||||
imports.import(*module, Some(name.as_str()), EntityType::Function(*type_));
|
||||
}
|
||||
|
||||
imports.import("env", Some("memory"), MemoryType {
|
||||
minimum: m.memory as u64,
|
||||
maximum: None,
|
||||
memory64: false
|
||||
});
|
||||
imports.import(
|
||||
"env",
|
||||
Some("memory"),
|
||||
MemoryType {
|
||||
minimum: m.memory as u64,
|
||||
maximum: None,
|
||||
memory64: false,
|
||||
},
|
||||
);
|
||||
|
||||
module.section(&imports);
|
||||
}
|
||||
@@ -143,14 +153,28 @@ impl BaseModule {
|
||||
}
|
||||
}
|
||||
|
||||
fn add_function(functions: &mut Vec<(&'static str, String, u32)>, type_map: &HashMap<FunctionType, u32>, module: &'static str, name: &str, params: &[ValType], result: Option<ValType>) {
|
||||
functions.push((module, name.to_string(), lookup_type(type_map, params, result)));
|
||||
fn add_function(
|
||||
functions: &mut Vec<(&'static str, String, u32)>,
|
||||
type_map: &HashMap<FunctionType, u32>,
|
||||
name: &str,
|
||||
params: &[ValType],
|
||||
result: Option<ValType>,
|
||||
) {
|
||||
functions.push((
|
||||
"env".into(),
|
||||
name.to_string(),
|
||||
lookup_type(type_map, params, result),
|
||||
));
|
||||
}
|
||||
|
||||
fn lookup_type(type_map: &HashMap<FunctionType, u32>, params: &[ValType], result: Option<ValType>) -> u32 {
|
||||
fn lookup_type(
|
||||
type_map: &HashMap<FunctionType, u32>,
|
||||
params: &[ValType],
|
||||
result: Option<ValType>,
|
||||
) -> u32 {
|
||||
let key = FunctionType {
|
||||
params: params.to_vec(),
|
||||
result
|
||||
result,
|
||||
};
|
||||
*type_map.get(&key).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ async function loadWasm(url, imports) {
|
||||
|
||||
function setMessage(size, error) {
|
||||
let html = size ? `${size} bytes` : 'Insert cart';
|
||||
if(error) {
|
||||
if (error) {
|
||||
html += ` - <span class="error">${error.replaceAll('<', '<')}</span>`
|
||||
}
|
||||
document.getElementById('message').innerHTML = html;
|
||||
@@ -27,7 +27,7 @@ let canvasCtx = screen.getContext('2d');
|
||||
let cancelFunction;
|
||||
|
||||
async function runModule(data) {
|
||||
if(cancelFunction) {
|
||||
if (cancelFunction) {
|
||||
cancelFunction();
|
||||
cancelFunction = null;
|
||||
}
|
||||
@@ -35,21 +35,21 @@ async function runModule(data) {
|
||||
let cartridgeSize = data.byteLength;
|
||||
|
||||
setMessage(cartridgeSize);
|
||||
if(cartridgeSize == 0) {
|
||||
if (cartridgeSize == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let dataU8Array = new Uint8Array(data);
|
||||
|
||||
let newURL = window.location.pathname;
|
||||
if(cartridgeSize <= 1024) {
|
||||
if (cartridgeSize <= 1024) {
|
||||
let dataString = '';
|
||||
for(let byte of dataU8Array) {
|
||||
for (let byte of dataU8Array) {
|
||||
dataString += String.fromCharCode(byte);
|
||||
}
|
||||
newURL += '#' + btoa(dataString);
|
||||
}
|
||||
if(newURL != window.location.pathname + window.location.hash) {
|
||||
if (newURL != window.location.pathname + window.location.hash) {
|
||||
history.pushState(null, null, newURL);
|
||||
}
|
||||
|
||||
@@ -64,55 +64,53 @@ async function runModule(data) {
|
||||
};
|
||||
let loadMem = loaderImport.env.memory.buffer;
|
||||
let loader = await loadWasm(loaderUrl, loaderImport);
|
||||
|
||||
|
||||
let baseModule = await (await fetch(baseUrl)).arrayBuffer();
|
||||
|
||||
if(dataU8Array[0] != 0) {
|
||||
|
||||
if (dataU8Array[0] != 0) {
|
||||
new Uint8Array(loadMem).set(dataU8Array);
|
||||
new Uint8Array(loadMem).set(new Uint8Array(baseModule), data.byteLength);
|
||||
|
||||
|
||||
let destOffset = data.byteLength + baseModule.byteLength;
|
||||
let endOffset = loader.exports.load_uw8(0, data.byteLength, data.byteLength, destOffset);
|
||||
|
||||
|
||||
data = new ArrayBuffer(endOffset - destOffset);
|
||||
new Uint8Array(data).set(new Uint8Array(loadMem).slice(destOffset, endOffset));
|
||||
}
|
||||
|
||||
|
||||
let importObject = {
|
||||
env: {
|
||||
memory: new WebAssembly.Memory({ initial: 4, maximum: 4 }),
|
||||
},
|
||||
math: {},
|
||||
uw8: {}
|
||||
};
|
||||
|
||||
for(let n of ['acos','asin','atan','atan2','cos','exp','log','sin','tan','pow']) {
|
||||
importObject.math[n] = Math[n];
|
||||
for (let n of ['acos', 'asin', 'atan', 'atan2', 'cos', 'exp', 'log', 'sin', 'tan', 'pow']) {
|
||||
importObject.env[n] = Math[n];
|
||||
}
|
||||
|
||||
for(let i = 9; i < 64; ++i) {
|
||||
importObject.uw8['reserved' + i] = () => {};
|
||||
for (let i = 9; i < 64; ++i) {
|
||||
importObject.env['reserved' + i] = () => { };
|
||||
}
|
||||
|
||||
|
||||
let instance = new WebAssembly.Instance(await WebAssembly.compile(data), importObject);
|
||||
|
||||
|
||||
let buffer = imageData.data;
|
||||
|
||||
|
||||
let startTime = Date.now();
|
||||
|
||||
|
||||
let keepRunning = true;
|
||||
cancelFunction = () => keepRunning = false;
|
||||
|
||||
function mainloop() {
|
||||
if(!keepRunning) {
|
||||
if (!keepRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
instance.exports.tic(Date.now() - startTime);
|
||||
|
||||
let framebuffer = new Uint8Array(importObject.env.memory.buffer.slice(120, 120 + 320*256));
|
||||
for(let i = 0; i < 320*256; ++i) {
|
||||
|
||||
let framebuffer = new Uint8Array(importObject.env.memory.buffer.slice(120, 120 + 320 * 256));
|
||||
for (let i = 0; i < 320 * 256; ++i) {
|
||||
buffer[i * 4] = framebuffer[i];
|
||||
buffer[i * 4 + 1] = framebuffer[i];
|
||||
buffer[i * 4 + 2] = framebuffer[i];
|
||||
@@ -121,15 +119,15 @@ async function runModule(data) {
|
||||
framebufferCanvasCtx.putImageData(imageData, 0, 0);
|
||||
canvasCtx.imageSmoothingEnabled = false;
|
||||
canvasCtx.drawImage(framebufferCanvas, 0, 0, 640, 512);
|
||||
|
||||
|
||||
window.requestAnimationFrame(mainloop);
|
||||
} catch(err) {
|
||||
} catch (err) {
|
||||
setMessage(cartridgeSize, err.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mainloop();
|
||||
} catch(err) {
|
||||
} catch (err) {
|
||||
setMessage(cartridgeSize, err.toString());
|
||||
}
|
||||
}
|
||||
@@ -140,7 +138,7 @@ async function runModuleFromURL(url) {
|
||||
|
||||
function runModuleFromHash() {
|
||||
let base64Data = window.location.hash.slice(1);
|
||||
if(base64Data.length > 0) {
|
||||
if (base64Data.length > 0) {
|
||||
runModuleFromURL('data:;base64,' + base64Data);
|
||||
} else {
|
||||
runModule(new ArrayBuffer(0));
|
||||
@@ -155,7 +153,7 @@ document.getElementById('cartButton').onclick = () => {
|
||||
fileInput.type = 'file';
|
||||
fileInput.accept = '.wasm,.uw8,application/wasm';
|
||||
fileInput.onchange = () => {
|
||||
if(fileInput.files.length > 0) {
|
||||
if (fileInput.files.length > 0) {
|
||||
runModuleFromURL(URL.createObjectURL(fileInput.files[0]));
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user