Author SHA1 Message Date
dranke e30dfb5e81 fix build? 2024-06-07 15:29:04 +02:00
dranke 109a1755b9 fix build 2024-06-07 15:24:31 +02:00
dranke d1a3bb9db5 build testing branch 2024-06-07 15:16:31 +02:00
25 changed files with 179 additions and 478 deletions

No files matched your search

+1 -1
View File
@@ -2,7 +2,7 @@ name: Rust
on: on:
push: push:
branches: [ master ] branches: [ master, testing ]
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
Generated
+136 -334
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -27,5 +27,5 @@ tokio = { version = "1.17.0", features = ["sync", "rt"], optional = true }
tokio-stream = { version = "0.1.8", features = ["sync"], optional = true } tokio-stream = { version = "0.1.8", features = ["sync"], optional = true }
webbrowser = { version = "0.6.0", optional = true } webbrowser = { version = "0.6.0", optional = true }
ansi_term = "0.12.1" ansi_term = "0.12.1"
cpal = { version = "0.14.1", optional = true } cpal = { version = "0.13.5", optional = true }
rubato = { version = "0.11.0", optional = true } rubato = { version = "0.11.0", optional = true }
-1
View File
@@ -34,7 +34,6 @@ import "env.rectangle_outline" fn rectangle_outline(f32, f32, f32, f32, i32);
import "env.circle_outline" fn circle_outline(f32, f32, f32, i32); import "env.circle_outline" fn circle_outline(f32, f32, f32, i32);
import "env.exp" fn exp(f32) -> f32; import "env.exp" fn exp(f32) -> f32;
import "env.playNote" fn playNote(i32, i32); import "env.playNote" fn playNote(i32, i32);
import "env.sndGes" fn sndGes(i32) -> f32;
const TIME_MS = 0x40; const TIME_MS = 0x40;
const GAMEPAD = 0x44; const GAMEPAD = 0x44;
-1
View File
@@ -34,7 +34,6 @@
(import "env" "circle_outline" (func $circle_outline (param f32) (param f32) (param f32) (param i32))) (import "env" "circle_outline" (func $circle_outline (param f32) (param f32) (param f32) (param i32)))
(import "env" "exp" (func $exp (param f32) (result f32))) (import "env" "exp" (func $exp (param f32) (result f32)))
(import "env" "playNote" (func $playNote (param i32) (param i32))) (import "env" "playNote" (func $playNote (param i32) (param i32)))
(import "env" "sndGes" (func $sndGes (param i32) (result f32)))
;; to use defines, include this file with a preprocessor ;; to use defines, include this file with a preprocessor
;; like gpp (https://logological.org/gpp). ;; like gpp (https://logological.org/gpp).
+1 -1
View File
@@ -391,7 +391,7 @@ checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]] [[package]]
name = "upkr" name = "upkr"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/exoticorn/upkr.git?rev=d93aec186c9fb91d962c488682a2db125c61306c#d93aec186c9fb91d962c488682a2db125c61306c" source = "git+https://github.com/exoticorn/upkr.git?rev=2e7983fc#2e7983fc650788d98da2eecef2d16f63e849e4a0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cdivsufsort", "cdivsufsort",
Binary file not shown.
Binary file not shown.
+3 -2
View File
@@ -10,7 +10,7 @@ const GesState.Size = GesState.Filter + 8*4;
const GesStateOffset = 32; const GesStateOffset = 32;
const GesBufferOffset = 32 + GesState.Size; const GesBufferOffset = 32 + GesState.Size;
export fn sndGes(t: i32) -> f32 { export fn gesSnd(t: i32) -> f32 {
let baseAddr = 0!0x12c78; let baseAddr = 0!0x12c78;
if !(t & 127) { if !(t & 127) {
let i: i32; let i: i32;
@@ -62,6 +62,7 @@ export fn sndGes(t: i32) -> f32 {
let phase = channelState!GesChannelState.Phase; let phase = channelState!GesChannelState.Phase;
let inline pulseWidth = channelReg?1; let inline pulseWidth = channelReg?1;
let phaseShift = (pulseWidth - 128) * 255;
let invPhaseInc = 1 as f32 / phaseInc as f32; let invPhaseInc = 1 as f32 / phaseInc as f32;
i = 0; i = 0;
@@ -130,7 +131,7 @@ export fn sndGes(t: i32) -> f32 {
let phaseInc = (freq * (65536.0 / 44100.0)) as i32; let phaseInc = (freq * (65536.0 / 44100.0)) as i32;
let phase = channelState!GesChannelState.Phase; let phase = channelState!GesChannelState.Phase;
if modSrc < ch { if modSrc > ch {
phase = phase - (phaseInc << 6); phase = phase - (phaseInc << 6);
} }
+10 -10
View File
@@ -372,7 +372,16 @@ export fn printChar(char: i32) {
global mut controlCodeLength = 0; global mut controlCodeLength = 0;
fn printSingleChar(char: i32) { fn printSingleChar(char: i32) {
if outputChannel >= 2 & (char < 4 | char > 6) { if char >= 4 & char <= 6 {
outputChannel = char - 4;
if !outputChannel {
textCursorX = 0;
textCursorY = 0;
}
return;
}
if outputChannel >= 2 {
logChar(char); logChar(char);
return; return;
} }
@@ -390,15 +399,6 @@ fn printSingleChar(char: i32) {
return; return;
} }
if char >= 4 & char <= 6 {
outputChannel = char - 4;
if !outputChannel {
textCursorX = 0;
textCursorY = 0;
}
return;
}
if char == 7 { if char == 7 {
80?0 = 80?0 ^ 2; 80?0 = 80?0 ^ 2;
return; return;
-13
View File
@@ -29,19 +29,6 @@ Examplers for older versions:
## Versions ## Versions
### v0.2.1
* [Web runtime](v0.2.1)
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.2.1/microw8-0.2.1-linux.tgz)
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.2.1/microw8-0.2.1-macos.tgz)
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.2.1/microw8-0.2.1-windows.zip)
Changes:
* new gpu accelerated renderer with (optional) crt filter
* optimized `hline` function, a big speed-up when drawing large filled circles or rectangles
* print fractional size of packed `uw8` cart
### v0.2.0 ### v0.2.0
* [Web runtime](v0.2.0) * [Web runtime](v0.2.0)
-1
View File
@@ -1 +0,0 @@
<!doctype html><html><head><meta charset="utf8"><title>MicroW8</title><link href="https://fonts.googleapis.com/css2?family=Josefin+Sans:wght@500&display=swap" rel="stylesheet"><style>html,body,canvas{margin:0;padding:0}html{color:#808070;height:100%;background-color:#202024;font-family:Josefin Sans,Verdana,Arial Black,sans-serif}body{height:100%;grid-template-rows:1fr 0fr;display:grid}#uw8{position:absolute}#uw8 a{font-size:130%}#centered{place-self:center}a{color:#303630}a:hover{color:#405040}.screen{width:320px;height:240px;image-rendering:pixelated;background-color:#202428;border:4px solid #303040;margin-bottom:8px;box-shadow:5px 5px 20px #000}#screen{cursor:none}#start{font-size:150%}#timer:before{content:"";width:12px;height:12px;background-color:red;border-radius:6px;margin-right:3px;display:inline-block}#message{margin-bottom:8px}.error{color:#e04030}button{color:#808070;background-color:#303440;border:1px solid #404040;border-radius:4px;padding:4px;font-family:inherit}button:hover{background-color:#202010}button:active{background-color:#504450}@media (min-width:680px) and (min-height:620px){.screen{width:640px;height:480px}}@media (min-width:1000px) and (min-height:800px){.screen{width:960px;height:720px}}@media (width:640px) and (height:480px){.screen{width:640px;height:480px;border:0;margin:0}body{overflow:hidden}}</style></head><body> <div id="uw8"> <a href="https://exoticorn.github.io/microw8">MicroW8</a> 0.2.1 </div> <div id="centered"> <canvas class="screen" id="screen" width="320" height="240"> </canvas> <button class="screen" id="start" style="display:none">Click to start</button> <div id="timer" hidden></div> <div id="message"></div> <button id="cartButton" style="visibility:hidden">Load cart...</button> </div> <div id="footer"> <a href="http://unlicense.org/" ref="license">Unlicense</a> </div> </body><script type="module">function e(e){return e&&e.__esModule?e.default:e}class t extends AudioWorkletNode{constructor(e){super(e,"apu",{outputChannelCount:[2]})}}let a=(...e)=>new Uint8Array(...e),n=(...e)=>new Uint32Array(...e);let s=function(s,A={}){A.setMessage||(A.setMessage=(e,t)=>{t&&console.log("error: "+t)});let r,B,o,i,l,d,F,u,g=s.getContext("2d"),c=g.createImageData(320,240),D=A.devkitMode,C=0,f=null==A.keyboardElement?s:A.keyboardElement;if(f){let e=e=>{let t,a="keydown"==e.type;switch(e.code){case"ArrowUp":t=1;break;case"ArrowDown":t=2;break;case"ArrowLeft":t=4;break;case"ArrowRight":t=8;break;case"KeyZ":t=16;break;case"KeyX":t=32;break;case"KeyA":t=64;break;case"KeyS":t=128;break;case"KeyR":a&&w(B,!0);break;case"F9":a&&s.toBlob((e=>{h(e,".png")})),e.preventDefault();break;case"F10":a&&function(){if(l)return l.stop(),l=null,F.disconnect(u),F=null,void(u=null);let e=s.captureStream();u=o.createMediaStreamDestination(),F=i,i.connect(u),e.addTrack(u.stream.getAudioTracks()[0]),l=new MediaRecorder(e,{mimeType:"video/webm",videoBitsPerSecond:25e6});let t=[];l.ondataavailable=e=>{t.push(e.data)};let a=A.timerElement;a&&(a.hidden=!1,a.innerText="00:00");function n(){if(d){if(a){let e=Math.floor((Date.now()-d)/1e3);a.innerText=Math.floor(e/60).toString().padStart(2,"0")+":"+(e%60).toString().padStart(2,"0")}setTimeout(n,1e3)}}l.onstop=()=>{a&&(a.hidden=!0),h(new Blob(t,{type:"video/webm"}),".webm")},l.start(),d=Date.now(),setTimeout(n,1e3)}(),e.preventDefault()}a?C|=t:C&=~t};f.onkeydown=e,f.onkeyup=e}async function w(l,d){r&&(r(),r=null),o=new AudioContext({sampleRate:44100});let F=!0,u=new AbortController;r=()=>{o.close(),F=!1,u.abort()};let f=l.byteLength;if(A.setMessage(f),0==f)return;await o.audioWorklet.addModule(e("data:application/javascript,let%20e%3D%28...e%29%3D%3Enew%20Uint8Array%28...e%29%3Bclass%20s%20extends%20AudioWorkletProcessor%7Bconstructor%28%29%7Bsuper%28%29%2Cthis.sampleIndex%3D0%2Cthis.currentTime%3D0%2Cthis.isFirstMessage%3D%210%2Cthis.pendingUpdates%3D%5B%5D%2Cthis.port.onmessage%3De%3D%3E%7Bthis.memory%3F%28this.isFirstMessage%26%26%28this.currentTime%2B%3D%28e.data.t-this.currentTime%29%2F8%2Cthis.isFirstMessage%3D%211%29%2Cthis.pendingUpdates.push%28e.data%29%29%3Athis.load%28e.data%5B0%5D%2Ce.data%5B1%5D%29%7D%7Dasync%20load%28e%2Cs%29%7Blet%20t%3Dnew%20WebAssembly.Memory%28%7Binitial%3A4%2Cmaximum%3A4%7D%29%2Ci%3D%7Benv%3A%7Bmemory%3At%7D%7D%3Bfor%28let%20e%20of%5B%22acos%22%2C%22asin%22%2C%22atan%22%2C%22atan2%22%2C%22cos%22%2C%22exp%22%2C%22log%22%2C%22sin%22%2C%22tan%22%2C%22pow%22%5D%29i.env%5Be%5D%3DMath%5Be%5D%3Bfor%28let%20e%3D9%3Be%3C64%3B%2B%2Be%29i.env%5B%22reserved%22%2Be%5D%3D%28%29%3D%3E%7B%7D%3Blet%20r%3D%22%22%3Bi.env.logChar%3De%3D%3E%7B10%3D%3De%3F%28console.log%28r%29%2Cr%3D%22%22%29%3Ar%2B%3DString.fromCharCode%28e%29%7D%3Bfor%28let%20e%3D0%3Be%3C16%3B%2B%2Be%29i.env%5B%22g_reserved%22%2Be%5D%3D0%3Blet%20n%3Dasync%20e%3D%3E%28await%20WebAssembly.instantiate%28e%2Ci%29%29.instance%2Ca%3Dawait%20n%28e%29%3Bfor%28let%20e%20in%20a.exports%29i.env%5Be%5D%3Da.exports%5Be%5D%3Blet%20o%3Dawait%20n%28s%29%3Bthis.memory%3Dt%2Cthis.snd%3Do.exports.snd%7Line truncated
+1 -1
View File
@@ -4,7 +4,7 @@
<section> <section>
<h1 class="text-center heading-text">A WebAssembly based fantasy console</h1> <h1 class="text-center heading-text">A WebAssembly based fantasy console</h1>
</section> </section>
<a href="v0.2.1"> <a href="v0.2.0">
<img class="demonstration-gif" style="width:640px;height:480px;image-rendering:pixelated" src="img/technotunnel.png"></img> <img class="demonstration-gif" style="width:640px;height:480px;image-rendering:pixelated" src="img/technotunnel.png"></img>
</a> </a>
</div> </div>
+3 -2
View File
@@ -81,12 +81,11 @@ fn run(mut args: Arguments) -> Result<()> {
#[cfg(not(feature = "native"))] #[cfg(not(feature = "native"))]
let run_browser = args.contains(["-b", "--browser"]) || true; let run_browser = args.contains(["-b", "--browser"]) || true;
#[allow(unused)]
let disable_audio = args.contains(["-m", "--no-audio"]); let disable_audio = args.contains(["-m", "--no-audio"]);
#[cfg(feature = "native")] #[cfg(feature = "native")]
let window_config = { let window_config = {
let mut config = uw8_window::WindowConfig::default(); let mut config = WindowConfig::default();
if !run_browser { if !run_browser {
config.parse_arguments(&mut args); config.parse_arguments(&mut args);
} }
@@ -99,6 +98,8 @@ fn run(mut args: Arguments) -> Result<()> {
use std::process::exit; use std::process::exit;
use uw8_window::WindowConfig;
let mut runtime: Box<dyn Runtime> = if !run_browser { let mut runtime: Box<dyn Runtime> = if !run_browser {
#[cfg(not(feature = "native"))] #[cfg(not(feature = "native"))]
unimplemented!(); unimplemented!();
+1 -1
View File
@@ -1 +1 @@
<!doctype html><html><head><meta charset="utf8"><title>uw8-run</title><style>html,body,canvas{background-color:#202024;margin:0;padding:0}html{height:100%}body{height:100%;grid-template-rows:1fr;display:grid}#screen{image-rendering:pixelated;border:4px solid #303040;place-self:center}#message{width:calc(100% - 16px);color:#c64;z-index:2;background-color:#0006;padding:8px;font:700 12pt sans-serif;position:absolute}@media (min-width:648px) and (min-height:488px){#screen{width:640px;height:480px}}@media (min-width:968px) and (min-height:728px){#screen{width:960px;height:720px}}</style></head><body> <canvas id="screen" width="320" height="240" tabindex="1"> </canvas> <div id="message"></div> </body><script type="module">function e(e){return e&&e.__esModule?e.default:e}class t extends AudioWorkletNode{constructor(e){super(e,"apu",{outputChannelCount:[2]})}}let A=(...e)=>new Uint8Array(...e),a=(...e)=>new Uint32Array(...e);let s=function(s,B={}){B.setMessage||(B.setMessage=(e,t)=>{t&&console.log("error: "+t)});let n,r,o,i,l,F,d,C,D=s.getContext("2d"),u=D.createImageData(320,240),c=B.devkitMode,Q=0,f=null==B.keyboardElement?s:B.keyboardElement;if(f){let e=e=>{let t,A="keydown"==e.type;switch(e.code){case"ArrowUp":t=1;break;case"ArrowDown":t=2;break;case"ArrowLeft":t=4;break;case"ArrowRight":t=8;break;case"KeyZ":t=16;break;case"KeyX":t=32;break;case"KeyA":t=64;break;case"KeyS":t=128;break;case"KeyR":A&&g(r,!0);break;case"F9":A&&s.toBlob((e=>{h(e,".png")})),e.preventDefault();break;case"F10":A&&function(){if(l)return l.stop(),l=null,d.disconnect(C),d=null,void(C=null);let e=s.captureStream();C=o.createMediaStreamDestination(),d=i,i.connect(C),e.addTrack(C.stream.getAudioTracks()[0]),l=new MediaRecorder(e,{mimeType:"video/webm",videoBitsPerSecond:25e6});let t=[];l.ondataavailable=e=>{t.push(e.data)};let A=B.timerElement;A&&(A.hidden=!1,A.innerText="00:00");function a(){if(F){if(A){let e=Math.floor((Date.now()-F)/1e3);A.innerText=Math.floor(e/60).toString().padStart(2,"0")+":"+(e%60).toString().padStart(2,"0")}setTimeout(a,1e3)}}l.onstop=()=>{A&&(A.hidden=!0),h(new Blob(t,{type:"video/webm"}),".webm")},l.start(),F=Date.now(),setTimeout(a,1e3)}(),e.preventDefault()}A?Q|=t:Q&=~t};f.onkeydown=e,f.onkeyup=e}async function g(l,F){n&&(n(),n=null),o=new AudioContext({sampleRate:44100});let d=!0,C=new AbortController;n=()=>{o.close(),d=!1,C.abort()};let f=l.byteLength;if(B.setMessage(f),0==f)return;await o.audioWorklet.addModule(e("data:application/javascript,let%20e%3D%28...e%29%3D%3Enew%20Uint8Array%28...e%29%3Bclass%20s%20extends%20AudioWorkletProcessor%7Bconstructor%28%29%7Bsuper%28%29%2Cthis.sampleIndex%3D0%2Cthis.currentTime%3D0%2Cthis.isFirstMessage%3D%210%2Cthis.pendingUpdates%3D%5B%5D%2Cthis.port.onmessage%3De%3D%3E%7Bthis.memory%3F%28this.isFirstMessage%26%26%28this.currentTime%2B%3D%28e.data.t-this.currentTime%29%2F8%2Cthis.isFirstMessage%3D%211%29%2Cthis.pendingUpdates.push%28e.data%29%29%3Athis.load%28e.data%5B0%5D%2Ce.data%5B1%5D%29%7D%7Dasync%20load%28e%2Cs%29%7Blet%20t%3Dnew%20WebAssembly.Memory%28%7Binitial%3A4%2Cmaximum%3A4%7D%29%2Ci%3D%7Benv%3A%7Bmemory%3At%7D%7D%3Bfor%28let%20e%20of%5B%22acos%22%2C%22asin%22%2C%22atan%22%2C%22atan2%22%2C%22cos%22%2C%22exp%22%2C%22log%22%2C%22sin%22%2C%22tan%22%2C%22pow%22%5D%29i.env%5Be%5D%3DMath%5Be%5D%3Bfor%28let%20e%3D9%3Be%3C64%3B%2B%2Be%29i.env%5B%22reserved%22%2Be%5D%3D%28%29%3D%3E%7B%7D%3Blet%20r%3D%22%22%3Bi.env.logChar%3De%3D%3E%7B10%3D%3De%3F%28console.log%28r%29%2Cr%3D%22%22%29%3Ar%2B%3DString.fromCharCode%28e%29%7D%3Bfor%28let%20e%3D0%3Be%3C16%3B%2B%2Be%29i.env%5B%22g_reserved%22%2Be%5D%3D0%3Blet%20n%3Dasync%20e%3D%3E%28await%20WebAssembly.instantiate%28e%2Ci%29%29.instance%2Ca%3Dawait%20n%28e%29%3Bfor%28let%20e%20in%20a.exports%29i.env%5Be%5D%3Da.exports%5Be%5D%3Blet%20o%3Dawait%20n%28s%29%3Bthis.memory%3Dt%2Cthis.snd%3Do.exports.snd%7C%7Ca.exports.sndGes%2Cthis.port.postMessage%282%29%7Dprocess%28s%2Ct%2Ci%29%7Bif%28this.isFirstMessage%3D%210%2Cthis.snd%29%7Bfor%28%3Bthis.pendingUpdates.length%3E0%26%26this.pendingUpdates%5B0%5D.t%3C%3Dthis.currentTime%3B%29e%28this.memory.buffer%2C80%2C32%29.set%28e%28this.pendingUpdates.shift%28%29.r%29%29%3Bnew%20Uint32Array%28this.memory.buffer%29%5B16%5D%3Dthis.currentTime%3Blet%20s%3Dt%5B0%5D%2Ci%3Dthis.sampleIndex%2Cr%3Ds%5B0%5D.length%3Bfor%28let%20e%3D0%3Be%3Cr%3B%2B%2Be%29s%5B0%5D%5Be%5D%3Dthis.snd%28i%2B%2B%29%2Cs%5B1%5D%5Be%5D%3Dthis.snd%28i%2B%2B%29%3Bthis.sampleIndex%3D4294967295%26i%2Cthis.currentTime%2B%3Dr%2F44.1%7Dreturn%210%7D%7DregisterProcessor%28%22apu%22%2Cs%29%3B")),i=new t(o);let h,m=0,w=new Promise((e=>h=e)),p=e=>{m|=e,3==m&&h&&(h(!0),h=null)},y=()=>{"suspended"==o.state?(B.startButton&&(B.startButton.style="",s.style="display:none"),(B.startButton||s).onclick=()=>{o.resume()}):(B.startButton&&(B.startButton.style="display:none",s.style=""),p(1))};o.onstatechange=y,y(),r=l;let E=window.location.pathname;if(f<=1024&&!F){let e="";for(let t of A(l))e+=String.fromCharCode(t);E+="#"+btoa(e),E!=window.locLine truncated <!doctype html><html><head><meta charset="utf8"><title>uw8-run</title><style>html,body,canvas{background-color:#202024;margin:0;padding:0}html{height:100%}body{height:100%;grid-template-rows:1fr;display:grid}#screen{image-rendering:pixelated;border:4px solid #303040;place-self:center}#message{width:calc(100% - 16px);color:#c64;z-index:2;background-color:#0006;padding:8px;font:700 12pt sans-serif;position:absolute}@media (min-width:648px) and (min-height:488px){#screen{width:640px;height:480px}}@media (min-width:968px) and (min-height:728px){#screen{width:960px;height:720px}}</style></head><body> <canvas id="screen" width="320" height="240" tabindex="1"> </canvas> <div id="message"></div> </body><script type="module">function e(e){return e&&e.__esModule?e.default:e}class t extends AudioWorkletNode{constructor(e){super(e,"apu",{outputChannelCount:[2]})}}let A=(...e)=>new Uint8Array(...e),a=(...e)=>new Uint32Array(...e);let s=function(s,B={}){B.setMessage||(B.setMessage=(e,t)=>{t&&console.log("error: "+t)});let n,r,o,i,l,F,d,g,C=s.getContext("2d"),u=C.createImageData(320,240),D=B.devkitMode,c=0,f=null==B.keyboardElement?s:B.keyboardElement;if(f){let e=e=>{let t,A="keydown"==e.type;switch(e.code){case"ArrowUp":t=1;break;case"ArrowDown":t=2;break;case"ArrowLeft":t=4;break;case"ArrowRight":t=8;break;case"KeyZ":t=16;break;case"KeyX":t=32;break;case"KeyA":t=64;break;case"KeyS":t=128;break;case"KeyR":A&&w(r,!0);break;case"F9":A&&s.toBlob((e=>{I(e,".png")})),e.preventDefault();break;case"F10":A&&function(){if(l)return l.stop(),l=null,d.disconnect(g),d=null,void(g=null);let e=s.captureStream();g=o.createMediaStreamDestination(),d=i,i.connect(g),e.addTrack(g.stream.getAudioTracks()[0]),l=new MediaRecorder(e,{mimeType:"video/webm",videoBitsPerSecond:25e6});let t=[];l.ondataavailable=e=>{t.push(e.data)};let A=B.timerElement;A&&(A.hidden=!1,A.innerText="00:00");function a(){if(F){if(A){let e=Math.floor((Date.now()-F)/1e3);A.innerText=Math.floor(e/60).toString().padStart(2,"0")+":"+(e%60).toString().padStart(2,"0")}setTimeout(a,1e3)}}l.onstop=()=>{A&&(A.hidden=!0),I(new Blob(t,{type:"video/webm"}),".webm")},l.start(),F=Date.now(),setTimeout(a,1e3)}(),e.preventDefault()}A?c|=t:c&=~t};f.onkeydown=e,f.onkeyup=e}async function w(l,F){n&&(n(),n=null),o=new AudioContext({sampleRate:44100});let d=!0,g=new AbortController;n=()=>{o.close(),d=!1,g.abort()};let f=l.byteLength;if(B.setMessage(f),0==f)return;await o.audioWorklet.addModule(e("data:application/javascript,let%20e%3D%28...e%29%3D%3Enew%20Uint8Array%28...e%29%3Bclass%20s%20extends%20AudioWorkletProcessor%7Bconstructor%28%29%7Bsuper%28%29%2Cthis.sampleIndex%3D0%2Cthis.currentTime%3D0%2Cthis.isFirstMessage%3D%210%2Cthis.pendingUpdates%3D%5B%5D%2Cthis.port.onmessage%3De%3D%3E%7Bthis.memory%3F%28this.isFirstMessage%26%26%28this.currentTime%2B%3D%28e.data.t-this.currentTime%29%2F8%2Cthis.isFirstMessage%3D%211%29%2Cthis.pendingUpdates.push%28e.data%29%29%3Athis.load%28e.data%5B0%5D%2Ce.data%5B1%5D%29%7D%7Dasync%20load%28e%2Cs%29%7Blet%20t%3Dnew%20WebAssembly.Memory%28%7Binitial%3A4%2Cmaximum%3A4%7D%29%2Ci%3D%7Benv%3A%7Bmemory%3At%7D%7D%3Bfor%28let%20e%20of%5B%22acos%22%2C%22asin%22%2C%22atan%22%2C%22atan2%22%2C%22cos%22%2C%22exp%22%2C%22log%22%2C%22sin%22%2C%22tan%22%2C%22pow%22%5D%29i.env%5Be%5D%3DMath%5Be%5D%3Bfor%28let%20e%3D9%3Be%3C64%3B%2B%2Be%29i.env%5B%22reserved%22%2Be%5D%3D%28%29%3D%3E%7B%7D%3Blet%20r%3D%22%22%3Bi.env.logChar%3De%3D%3E%7B10%3D%3De%3F%28console.log%28r%29%2Cr%3D%22%22%29%3Ar%2B%3DString.fromCharCode%28e%29%7D%3Bfor%28let%20e%3D0%3Be%3C16%3B%2B%2Be%29i.env%5B%22g_reserved%22%2Be%5D%3D0%3Blet%20n%3Dasync%20e%3D%3E%28await%20WebAssembly.instantiate%28e%2Ci%29%29.instance%2Ca%3Dawait%20n%28e%29%3Bfor%28let%20e%20in%20a.exports%29i.env%5Be%5D%3Da.exports%5Be%5D%3Blet%20o%3Dawait%20n%28s%29%3Bthis.memory%3Dt%2Cthis.snd%3Do.exports.snd%7C%7Ca.exports.gesSnd%2Cthis.port.postMessage%282%29%7Dprocess%28s%2Ct%2Ci%29%7Bif%28this.isFirstMessage%3D%210%2Cthis.snd%29%7Bfor%28%3Bthis.pendingUpdates.length%3E0%26%26this.pendingUpdates%5B0%5D.t%3C%3Dthis.currentTime%3B%29e%28this.memory.buffer%2C80%2C32%29.set%28e%28this.pendingUpdates.shift%28%29.r%29%29%3Bnew%20Uint32Array%28this.memory.buffer%29%5B16%5D%3Dthis.currentTime%3Blet%20s%3Dt%5B0%5D%2Ci%3Dthis.sampleIndex%2Cr%3Ds%5B0%5D.length%3Bfor%28let%20e%3D0%3Be%3Cr%3B%2B%2Be%29s%5B0%5D%5Be%5D%3Dthis.snd%28i%2B%2B%29%2Cs%5B1%5D%5Be%5D%3Dthis.snd%28i%2B%2B%29%3Bthis.sampleIndex%3D4294967295%26i%2Cthis.currentTime%2B%3Dr%2F44.1%7Dreturn%210%7D%7DregisterProcessor%28%22apu%22%2Cs%29%3B")),i=new t(o);let I,h=0,Q=new Promise((e=>I=e)),m=e=>{h|=e,3==h&&I&&(I(!0),I=null)},M=()=>{"suspended"==o.state?(B.startButton&&(B.startButton.style="",s.style="display:none"),(B.startButton||s).onclick=()=>{o.resume()}):(B.startButton&&(B.startButton.style="display:none",s.style=""),m(1))};o.onstatechange=M,M(),r=l;let E=window.location.pathname;if(f<=1024&&!F){let e="";for(let t of A(l))e+=String.fromCharCode(t);E+="#"+btoa(e),E!=window.locLine truncated
+7 -21
View File
@@ -134,10 +134,6 @@ impl super::Runtime for MicroW8 {
let end_frame = platform_instance.get_typed_func::<(), (), _>(&mut store, "endFrame")?; let end_frame = platform_instance.get_typed_func::<(), (), _>(&mut store, "endFrame")?;
let update = instance.get_typed_func::<(), (), _>(&mut store, "upd").ok(); let update = instance.get_typed_func::<(), (), _>(&mut store, "upd").ok();
if let Some(start) = instance.get_typed_func::<(), (), _>(&mut store, "start").ok() {
start.call(&mut store, ())?;
}
let (sound_tx, stream) = if self.disable_audio { let (sound_tx, stream) = if self.disable_audio {
(None, None) (None, None)
} else { } else {
@@ -318,7 +314,7 @@ fn init_sound(
let snd = instance let snd = instance
.get_typed_func::<(i32,), f32, _>(&mut store, "snd") .get_typed_func::<(i32,), f32, _>(&mut store, "snd")
.or_else(|_| platform_instance.get_typed_func::<(i32,), f32, _>(&mut store, "sndGes"))?; .or_else(|_| platform_instance.get_typed_func::<(i32,), f32, _>(&mut store, "gesSnd"))?;
let host = cpal::default_host(); let host = cpal::default_host();
let device = host let device = host
@@ -346,7 +342,7 @@ fn init_sound(
.ok_or_else(|| anyhow!("Could not find float output config"))?; .ok_or_else(|| anyhow!("Could not find float output config"))?;
let sample_rate = cpal::SampleRate(44100) let sample_rate = cpal::SampleRate(44100)
.max(config.min_sample_rate()) .max(config.min_sample_rate())
.min(config.max_sample_rate()); .max(config.max_sample_rate());
let config = config.with_sample_rate(sample_rate); let config = config.with_sample_rate(sample_rate);
let buffer_size = match *config.buffer_size() { let buffer_size = match *config.buffer_size() {
cpal::SupportedBufferSize::Unknown => cpal::BufferSize::Default, cpal::SupportedBufferSize::Unknown => cpal::BufferSize::Default,
@@ -423,14 +419,6 @@ fn init_sound(
mem[64..68].copy_from_slice(&current_time.to_le_bytes()); mem[64..68].copy_from_slice(&current_time.to_le_bytes());
} }
fn clamp_sample(s: f32) -> f32 {
if s.is_nan() {
0.0
} else {
s.max(-1.0).min(1.0)
}
}
if let Some(ref mut resampler) = resampler { if let Some(ref mut resampler) = resampler {
while !buffer.is_empty() { while !buffer.is_empty() {
let copy_size = resampler.output_buffers[0] let copy_size = resampler.output_buffers[0]
@@ -441,12 +429,10 @@ fn init_sound(
resampler.input_buffers[0].clear(); resampler.input_buffers[0].clear();
resampler.input_buffers[1].clear(); resampler.input_buffers[1].clear();
for _ in 0..resampler.resampler.input_frames_next() { for _ in 0..resampler.resampler.input_frames_next() {
resampler.input_buffers[0].push(clamp_sample( resampler.input_buffers[0]
snd.call(&mut store, (sample_index,)).unwrap_or(0.0), .push(snd.call(&mut store, (sample_index,)).unwrap_or(0.0));
)); resampler.input_buffers[1]
resampler.input_buffers[1].push(clamp_sample( .push(snd.call(&mut store, (sample_index + 1,)).unwrap_or(0.0));
snd.call(&mut store, (sample_index + 1,)).unwrap_or(0.0),
));
sample_index = sample_index.wrapping_add(2); sample_index = sample_index.wrapping_add(2);
} }
@@ -472,7 +458,7 @@ fn init_sound(
} }
} else { } else {
for v in buffer { for v in buffer {
*v = clamp_sample(snd.call(&mut store, (sample_index,)).unwrap_or(0.0)); *v = snd.call(&mut store, (sample_index,)).unwrap_or(0.0);
sample_index = sample_index.wrapping_add(1); sample_index = sample_index.wrapping_add(1);
} }
} }
+8 -16
View File
@@ -11,7 +11,6 @@ use warp::{http::Response, Filter};
pub struct RunWebServer { pub struct RunWebServer {
cart: Arc<Mutex<Vec<u8>>>, cart: Arc<Mutex<Vec<u8>>>,
tx: broadcast::Sender<()>, tx: broadcast::Sender<()>,
socket_addr: SocketAddr,
} }
impl RunWebServer { impl RunWebServer {
@@ -19,13 +18,8 @@ impl RunWebServer {
let cart = Arc::new(Mutex::new(Vec::new())); let cart = Arc::new(Mutex::new(Vec::new()));
let (tx, _) = broadcast::channel(1); let (tx, _) = broadcast::channel(1);
let socket_addr = "127.0.0.1:3030"
.parse::<SocketAddr>()
.expect("Failed to parse socket address");
let server_cart = cart.clone(); let server_cart = cart.clone();
let server_tx = tx.clone(); let server_tx = tx.clone();
let server_addr = socket_addr.clone();
thread::spawn(move || { thread::spawn(move || {
let rt = tokio::runtime::Builder::new_current_thread() let rt = tokio::runtime::Builder::new_current_thread()
.enable_io() .enable_io()
@@ -53,26 +47,24 @@ impl RunWebServer {
warp::sse::reply(warp::sse::keep_alive().stream(event_stream(&server_tx))) warp::sse::reply(warp::sse::keep_alive().stream(event_stream(&server_tx)))
}); });
let server_future = warp::serve(html.or(cart).or(events)).bind(server_addr); let socket_addr = "127.0.0.1:3030"
.parse::<SocketAddr>()
.expect("Failed to parse socket address");
let server_future = warp::serve(html.or(cart).or(events)).bind(socket_addr);
println!("Point browser at http://{}", socket_addr);
let _ignore_result = webbrowser::open(&format!("http://{}", socket_addr));
server_future.await server_future.await
}); });
}); });
RunWebServer { RunWebServer { cart, tx }
cart,
tx,
socket_addr,
}
} }
} }
impl super::Runtime for RunWebServer { impl super::Runtime for RunWebServer {
fn load(&mut self, module_data: &[u8]) -> Result<()> { fn load(&mut self, module_data: &[u8]) -> Result<()> {
if let Ok(mut lock) = self.cart.lock() { if let Ok(mut lock) = self.cart.lock() {
if lock.is_empty() && !module_data.is_empty() {
println!("Point browser at http://{}", self.socket_addr);
let _ignore_result = webbrowser::open(&format!("http://{}", self.socket_addr));
}
lock.clear(); lock.clear();
lock.extend_from_slice(module_data); lock.extend_from_slice(module_data);
} }
-5
View File
@@ -1,5 +0,0 @@
include "../examples/include/microw8-api.cwa"
export fn start() {
printChar('Test');
}
-13
View File
@@ -1,13 +0,0 @@
include "../examples/include/microw8-api.cwa"
export fn upd() {
printString(USER_MEM);
}
data USER_MEM {
i8(12, 31, 5, 6) "Text mode"
i8(5, 31, 4, 5) "Graphics mode"
i8(6) "Console output\nSecond line\n"
i8(4, 31, 4, 12) "Back to text mode"
i8(0)
}
-1
View File
@@ -1 +0,0 @@
* add support for 16bit sound (not just float)
-1
View File
@@ -167,7 +167,6 @@ impl BaseModule {
add_function(&mut functions, &type_map, "exp", &[F32], Some(F32)); add_function(&mut functions, &type_map, "exp", &[F32], Some(F32));
add_function(&mut functions, &type_map, "playNote", &[I32, I32], None); add_function(&mut functions, &type_map, "playNote", &[I32, I32], None);
add_function(&mut functions, &type_map, "sndGes", &[I32], Some(F32));
for i in functions.len()..64 { for i in functions.len()..64 {
add_function( add_function(
-5
View File
@@ -220,11 +220,6 @@ impl<'a> ParsedModule<'a> {
validate_table_section(reader)?; validate_table_section(reader)?;
table_section = Some(Section::new(range, ())); table_section = Some(Section::new(range, ()));
} }
Payload::MemorySection(reader) => {
if reader.get_count() != 0 {
bail!("Found non-empty MemorySection. Memory has to be imported!");
}
}
Payload::ElementSection(mut reader) => { Payload::ElementSection(mut reader) => {
let mut elements = Vec::with_capacity(reader.get_count() as usize); let mut elements = Vec::with_capacity(reader.get_count() as usize);
for _ in 0..reader.get_count() { for _ in 0..reader.get_count() {
+6 -42
View File
@@ -4,64 +4,31 @@ use std::time::Instant;
mod cpu; mod cpu;
mod gpu; mod gpu;
pub struct Window { pub struct Window(Box<dyn WindowImpl>);
inner: Box<dyn WindowImpl>,
fps_counter: Option<FpsCounter>,
}
struct FpsCounter {
start: Instant,
num_frames: u32,
}
impl Window { impl Window {
pub fn new(config: WindowConfig) -> Result<Window> { pub fn new(config: WindowConfig) -> Result<Window> {
let fps_counter = if config.fps_counter {
Some(FpsCounter {
start: Instant::now(),
num_frames: 0,
})
} else {
None
};
if config.enable_gpu { if config.enable_gpu {
match gpu::Window::new(config) { match gpu::Window::new(config) {
Ok(window) => { Ok(window) => return Ok(Window(Box::new(window))),
return Ok(Window {
inner: Box::new(window),
fps_counter,
})
}
Err(err) => eprintln!( Err(err) => eprintln!(
"Failed to create gpu window: {}\nFalling back tp cpu window", "Failed to create gpu window: {}\nFalling back tp cpu window",
err err
), ),
} }
} }
cpu::Window::new().map(|window| Window { cpu::Window::new().map(|window| Window(Box::new(window)))
inner: Box::new(window),
fps_counter,
})
} }
pub fn begin_frame(&mut self) -> Input { pub fn begin_frame(&mut self) -> Input {
self.inner.begin_frame() self.0.begin_frame()
} }
pub fn end_frame(&mut self, framebuffer: &[u8], palette: &[u8], next_frame: Instant) { pub fn end_frame(&mut self, framebuffer: &[u8], palette: &[u8], next_frame: Instant) {
self.inner.end_frame(framebuffer, palette, next_frame); self.0.end_frame(framebuffer, palette, next_frame)
if let Some(ref mut fps_counter) = self.fps_counter {
fps_counter.num_frames += 1;
let elapsed = fps_counter.start.elapsed().as_secs_f32();
if elapsed >= 1.0 {
println!("fps: {:.1}", fps_counter.num_frames as f32 / elapsed);
fps_counter.num_frames = 0;
fps_counter.start = Instant::now();
}
}
} }
pub fn is_open(&self) -> bool { pub fn is_open(&self) -> bool {
self.inner.is_open() self.0.is_open()
} }
} }
@@ -70,7 +37,6 @@ pub struct WindowConfig {
enable_gpu: bool, enable_gpu: bool,
filter: u32, filter: u32,
fullscreen: bool, fullscreen: bool,
fps_counter: bool,
} }
impl Default for WindowConfig { impl Default for WindowConfig {
@@ -79,7 +45,6 @@ impl Default for WindowConfig {
enable_gpu: true, enable_gpu: true,
filter: 5, filter: 5,
fullscreen: false, fullscreen: false,
fps_counter: false,
} }
} }
} }
@@ -101,7 +66,6 @@ impl WindowConfig {
} }
} }
self.fullscreen = args.contains("--fullscreen"); self.fullscreen = args.contains("--fullscreen");
self.fps_counter = args.contains("--fps");
} }
} }
+1 -1
View File
@@ -63,7 +63,7 @@ class APU extends AudioWorkletProcessor {
this.memory = memory; this.memory = memory;
this.snd = instance.exports.snd || platform_instance.exports.sndGes; this.snd = instance.exports.snd || platform_instance.exports.gesSnd;
this.port.postMessage(2); this.port.postMessage(2);
} }
-4
View File
@@ -263,10 +263,6 @@ export default function MicroW8(screen, config = {}) {
window.addEventListener('blur', () => updateVisibility(false), { signal: abortController.signal }); window.addEventListener('blur', () => updateVisibility(false), { signal: abortController.signal });
updateVisibility(document.hasFocus()); updateVisibility(document.hasFocus());
if (instance.exports.start) {
instance.exports.start();
}
function mainloop() { function mainloop() {
if (!keepRunning) { if (!keepRunning) {
return; return;