use std::io::prelude::*; use std::path::Path; use std::{fs::File, time::Instant}; use anyhow::Result; use minifb::{Key, Window, WindowOptions}; use wasmtime::{ Engine, GlobalType, Memory, MemoryType, Module, Mutability, Store, TypedFunc, ValType, }; pub struct MicroW8 { engine: Engine, loader_module: Module, window: Window, window_buffer: Vec, instance: Option, } struct UW8Instance { store: Store<()>, memory: Memory, upd: TypedFunc<(), ()>, start_time: Instant, } impl MicroW8 { pub fn new() -> Result { let engine = wasmtime::Engine::default(); let loader_module = wasmtime::Module::new(&engine, include_bytes!("../platform/bin/loader.wasm"))?; let mut window = Window::new("MicroW8", 320, 240, WindowOptions::default())?; window.limit_update_rate(Some(std::time::Duration::from_micros(16666))); Ok(MicroW8 { engine, loader_module, window, window_buffer: vec![0u32; 320 * 240], instance: None, }) } pub fn is_open(&self) -> bool { self.window.is_open() && !self.window.is_key_down(Key::Escape) } fn reset(&mut self) { self.instance = None; for v in &mut self.window_buffer { *v = 0; } } pub fn load_from_file>(&mut self, path: P) -> Result<()> { self.reset(); let mut module = vec![]; File::open(path)?.read_to_end(&mut module)?; self.load_from_memory(&module) } pub fn load_from_memory(&mut self, module: &[u8]) -> Result<()> { self.reset(); let mut store = wasmtime::Store::new(&self.engine, ()); let memory = wasmtime::Memory::new(&mut store, MemoryType::new(4, Some(4)))?; let mut linker = wasmtime::Linker::new(&self.engine); linker.define("env", "memory", memory.clone())?; let loader_instance = linker.instantiate(&mut store, &self.loader_module)?; let load_uw8 = loader_instance.get_typed_func::(&mut store, "load_uw8")?; let platform_data = include_bytes!("../platform/bin/platform.uw8"); memory.data_mut(&mut store)[..platform_data.len()].copy_from_slice(platform_data); let platform_length = load_uw8.call(&mut store, platform_data.len() as i32)? as u32 as usize; let platform_module = wasmtime::Module::new(&self.engine, &memory.data(&store)[..platform_length])?; memory.data_mut(&mut store)[..module.len()].copy_from_slice(module); let module_length = load_uw8.call(&mut store, module.len() as i32)? as u32 as usize; let module = wasmtime::Module::new(&self.engine, &memory.data(&store)[..module_length])?; linker.func_wrap("env", "acos", |v: f32| v.acos())?; linker.func_wrap("env", "asin", |v: f32| v.asin())?; linker.func_wrap("env", "atan", |v: f32| v.atan())?; linker.func_wrap("env", "atan2", |x: f32, y: f32| x.atan2(y))?; linker.func_wrap("env", "cos", |v: f32| v.cos())?; linker.func_wrap("env", "exp", |v: f32| v.exp())?; linker.func_wrap("env", "log", |v: f32| v.ln())?; linker.func_wrap("env", "sin", |v: f32| v.sin())?; linker.func_wrap("env", "tan", |v: f32| v.tan())?; linker.func_wrap("env", "pow", |a: f32, b: f32| a.powf(b))?; for i in 9..64 { linker.func_wrap("env", &format!("reserved{}", i), || {})?; } for i in 0..16 { linker.define( "env", &format!("g_reserved{}", i), wasmtime::Global::new( &mut store, GlobalType::new(ValType::I32, Mutability::Const), 0.into(), )?, )?; } let platform_instance = linker.instantiate(&mut store, &platform_module)?; for export in platform_instance.exports(&mut store) { linker.define( "env", export.name(), export .into_func() .expect("platform surely only exports functions"), )?; } let instance = linker.instantiate(&mut store, &module)?; let upd = instance.get_typed_func::<(), (), _>(&mut store, "upd")?; self.instance = Some(UW8Instance { store, memory, upd, start_time: Instant::now(), }); Ok(()) } pub fn run_frame(&mut self) -> Result<()> { if let Some(mut instance) = self.instance.take() { instance.memory.data_mut(&mut instance.store)[64..68] .copy_from_slice(&(instance.start_time.elapsed().as_millis() as i32).to_le_bytes()); instance.upd.call(&mut instance.store, ())?; let framebuffer = &instance.memory.data(&instance.store)[120..]; let palette = &framebuffer[320 * 240..]; for i in 0..320 * 240 { let offset = framebuffer[i] as usize * 4; self.window_buffer[i] = 0xff000000 | ((palette[offset + 0] as u32) << 16) | ((palette[offset + 1] as u32) << 8) | palette[offset + 2] as u32; } self.instance = Some(instance); } self.window .update_with_buffer(&self.window_buffer, 320, 240)?; Ok(()) } }