54 Commits

Author SHA1 Message Date
luchak
2a2bb547d7 Merge 1d89ef8613 into 8913fdaf6b 2025-01-27 21:18:05 -08:00
8913fdaf6b update web runtime to 0.4.0 on homepage 2025-01-22 20:25:59 +01:00
195376cd28 forgot one additional change in 0.4.0 2025-01-22 19:53:22 +01:00
bf14d4bd3f prepare release 0.4.0 2025-01-22 19:49:28 +01:00
0c68328700 add 6 and 7 parameter function types to base module 2025-01-21 22:23:08 +01:00
fb3fd5e8bb add frame counter at memory location 72 2024-06-16 14:10:18 +02:00
b85f96b0bb add support to output sound on devices that only offer surround configs 2024-06-16 13:21:23 +02:00
b69055b58d update to git version of wasmtime
fixes performance regressions since microw8 0.2.1
2024-06-16 13:07:29 +02:00
0878aa3f02 add support for mono-only audio devices 2024-05-26 12:54:59 +02:00
5cd1ecb98a prepare for 0.3.0 release 2024-04-27 23:22:51 +02:00
842cf9f139 update some more dependencies 2024-04-10 00:04:12 +02:00
d5ed809dc7 minimal text scale docs + reset on cls() 2024-04-10 00:03:54 +02:00
285d9aee2c add example for scaled text 2024-04-09 00:09:27 +02:00
4e18d38538 add untested implementation of text scale 2024-04-07 23:59:14 +02:00
d4bad3ba8b add sprite flipping to example 2024-04-07 23:20:13 +02:00
6ddda24156 Merge branch 'sprites' 2024-04-07 22:58:26 +02:00
7003db3a05 update dependencies 2024-04-03 10:26:41 +02:00
8258bc1854 fix typo 2023-09-13 07:36:24 +02:00
a651107104 add start and snd functions to filter exports exclude list 2023-09-03 10:19:30 +02:00
22c35e37f4 remove debug trace 2023-09-02 15:37:27 +02:00
805c939097 add support for sound devices that only accept 16bit audio 2023-08-23 23:03:23 +02:00
440e150896 update dependencies 2023-08-23 00:52:51 +02:00
77b2e27346 clamp snd samples into valid -1.0..1.0 range 2023-08-23 00:20:35 +02:00
09e4fcbf14 add --scale option 2023-08-11 23:52:19 +02:00
c8a9f533ef copy optimized blitSprite impl to grabSprite, add simple docs 2023-04-03 23:44:12 +02:00
4a1d607bcb optimize blitSprite 2023-04-03 00:09:02 +02:00
bbb75838cd first unoptimized version of blitSprite & grabSprite 2023-04-02 23:21:31 +02:00
dbeb242fb2 add support for br_table instruction when packing cart 2023-03-20 23:08:42 +01:00
luchak
1d89ef8613 Support v128.const 2023-02-22 21:21:34 -08:00
luchak
c7be8be2c4 Remove dependency on curlywas SIMD fork 2023-02-13 18:54:02 -08:00
luchak
d9f9500b76 Add SIMD opcode support to uw8-tool 2023-02-13 18:48:19 -08:00
4fe4bce0ad token env var has changed 2023-01-31 00:14:25 +01:00
fe86153562 use fixed version of zola-deploy-action 2023-01-31 00:01:07 +01:00
c9dadaca2e try add safe.directory 2023-01-30 09:48:57 +01:00
5dc3e281ce and again 2023-01-30 09:45:28 +01:00
ce3afb821f another attempt changing the owner to fix permission issue 2023-01-30 09:43:54 +01:00
2652a351ad remove chown again 2023-01-30 09:38:57 +01:00
9109722409 fix main.yml 2023-01-30 09:37:12 +01:00
f861c262a1 update ci actions, hopefully fix permission error 2023-01-30 09:15:09 +01:00
bbfb5eba49 add back event debouncing in file watcher 2023-01-30 00:09:25 +01:00
9e19ff1761 prepare for 0.2.2 release 2023-01-28 12:47:16 +01:00
5d85aeac09 call an exported start function if it exists 2023-01-28 12:31:16 +01:00
30eb953d5d remove test code that was accidentally committed 2023-01-28 11:37:05 +01:00
abdf780533 only open browser once cart has been compiled succesfully 2023-01-28 11:33:49 +01:00
fc05a54e2c fix control codes 4-6 as parameters to other control codes 2023-01-28 10:58:28 +01:00
b33099c828 update more dependencies 2023-01-28 02:11:25 +01:00
d478d3ad49 enable timeapi in winapi crate 2023-01-27 00:13:32 +01:00
502852e59a update uw8-window dependencies 2023-01-26 23:40:54 +01:00
5efa8b3465 first batch of dependency updates 2023-01-26 22:45:34 +01:00
daf2a02cd8 fix sndGes name & add missing auto-import 2023-01-23 23:21:45 +01:00
8d5374a867 add support to ignore empty memory section when packing 2022-11-03 23:27:53 +01:00
142b6a4c15 fix typo in sample rate selection 2022-10-30 00:07:58 +02:00
877fceb089 add --fps parameter to output fps 2022-07-25 23:44:06 +02:00
f0ba0f2b99 update site with 0.2.1 release 2022-07-25 08:46:23 +02:00
51 changed files with 6282 additions and 3663 deletions

View File

@@ -2,7 +2,7 @@ name: Rust
on:
push:
branches: [ master, testing ]
branches: [ master ]
env:
CARGO_TERM_COLOR: always
@@ -30,9 +30,9 @@ jobs:
run: sudo apt-get install -y libxkbcommon-dev libasound2-dev
if: matrix.os == 'ubuntu-latest'
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Cache build dirs
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: |
~/.cargo/bin/
@@ -44,7 +44,7 @@ jobs:
- name: Build
run: cargo build --release --verbose
- name: Upload artifact
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: uw8-${{ matrix.build }}
path: target/release/${{ matrix.exe }}

View File

@@ -8,12 +8,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: build_and_deploy
uses: shalzz/zola-deploy-action@v0.14.1
uses: shalzz/zola-deploy-action@70a101a14bbdeed13e7a42a9ed06b35c9e9e826e
env:
# Target branch
PAGES_BRANCH: gh-pages
BUILD_DIR: site
# Provide personal access token
TOKEN: $GITHUB_ACTOR:${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

3856
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "uw8"
version = "0.2.1"
version = "0.4.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -11,21 +11,21 @@ native = ["wasmtime", "uw8-window", "cpal", "rubato" ]
browser = ["warp", "tokio", "tokio-stream", "webbrowser"]
[dependencies]
wasmtime = { version = "0.37.0", optional = true }
wasmtime = { git = "https://github.com/bytecodealliance/wasmtime.git", rev = "0f48f939b9870036562ca02ff21253547a9f1a5c", optional = true }
anyhow = "1"
env_logger = "0.9"
env_logger = "0.11.3"
log = "0.4"
uw8-window = { path = "uw8-window", optional = true }
notify = "4"
pico-args = "0.4"
notify-debouncer-mini = { version = "0.4.1", default-features = false }
pico-args = "0.5"
curlywas = { git = "https://github.com/exoticorn/curlywas.git", rev = "0e7ea50" }
wat = "1"
uw8-tool = { path = "uw8-tool" }
same-file = "1"
warp = { version = "0.3.2", optional = true }
tokio = { version = "1.17.0", features = ["sync", "rt"], optional = true }
tokio-stream = { version = "0.1.8", features = ["sync"], optional = true }
webbrowser = { version = "0.6.0", optional = true }
warp = { version = "0.3.6", optional = true }
tokio = { version = "1.37.0", features = ["sync", "rt"], optional = true }
tokio-stream = { version = "0.1.15", features = ["sync"], optional = true }
webbrowser = { version = "0.8.13", optional = true }
ansi_term = "0.12.1"
cpal = { version = "0.13.5", optional = true }
rubato = { version = "0.11.0", optional = true }
cpal = { version = "0.15.3", optional = true }
rubato = { version = "0.15.0", optional = true }

View File

@@ -15,9 +15,9 @@ See [here](https://exoticorn.github.io/microw8/) for more information and docs.
## Downloads
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.1.2/microw8-0.1.2-linux.tgz)
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.1.2/microw8-0.1.2-macos.tgz)
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.1.2/microw8-0.1.2-windows.zip)
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.3.0/microw8-0.4.0-linux.tgz)
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.3.0/microw8-0.4.0-macos.tgz)
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.3.0/microw8-0.4.0-windows.zip)
The download includes

View File

@@ -0,0 +1,14 @@
include "../include/microw8-api.cwa"
export fn upd() {
printString(USER_MEM);
}
data USER_MEM {
// clear screen, switch to graphics text mode, text scale 4
i8(12, 5, 30, 4)
// text color, position, print two lines
i8(15, 86, 31, 8, 80) "Hello," i8(8, 8, 8, 8, 8, 10) "MicroW8!"
// print same two lines with different color and slight offset
i8(15, 47, 31, 10, 82) "Hello," i8(8, 8, 8, 8, 8, 10) "MicroW8!" i8(0)
}

View File

@@ -0,0 +1,21 @@
include "../include/microw8-api.cwa"
const SPRITE = 0x20000;
export fn upd() {
cls(0);
let t = time() / 2_f;
let i: i32;
loop spriteLoop {
let inline x = sin(t * -1.3 + i as f32 * (3.141 / 30_f)) * 180_f + 160_f;
let inline y = sin(t * 1.7 + i as f32 * (3.141 / 40_f)) * 140_f + 120_f;
blitSprite(SPRITE, 16, x as i32, y as i32, (i & 3) * 0x200 + 0x100);
branch_if (i +:= 1) < 100: spriteLoop;
}
}
start fn start() {
printChar('OO');
circle(8_f, 8_f, 6_f, 75);
grabSprite(SPRITE, 16, 0, 0, 0);
}

View File

@@ -3,12 +3,14 @@ include "../include/microw8-api.cwa"
export fn upd() {
let i: i32;
loop pixels {
let inline t = time() * 63 as f32;
let lazy x = (i % 320 - 160) as f32;
let lazy y = (i / 320 - 120) as f32;
let inline d = 40000 as f32 / sqrt(x * x + y * y);
let inline u = atan2(x, y) * (512.0 / 3.141);
let inline c = ((i32.trunc_sat_f32_s(d + t * 2 as f32) ^ i32.trunc_sat_f32_s(u + t)) & 255) >> 4;
let inline t = 16!56;
let inline x = (i % 320 - 160) as f32;
let inline y = (i #/ 320 - 120) as f32;
let inline d = 0xa000 as f32 / sqrt(x * x + y * y);
let inline a = atan2(x, y) * 163_f; // (512 / pi)
let inline u = i32.trunc_sat_f32_s(a) + t;
let inline v = i32.trunc_sat_f32_s(d) + t * 2;
let inline c = ((v ^ u) #/ 16) % 16;
i?FRAMEBUFFER = c;
branch_if (i := i + 1) < 320*240: pixels;

View File

@@ -30,10 +30,13 @@ import "env.printInt" fn printInt(i32);
import "env.setTextColor" fn setTextColor(i32);
import "env.setBackgroundColor" fn setBackgroundColor(i32);
import "env.setCursorPosition" fn setCursorPosition(i32, i32);
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.rectangleOutline" fn rectangleOutline(f32, f32, f32, f32, i32);
import "env.circleOutline" fn circleOutline(f32, f32, f32, i32);
import "env.exp" fn exp(f32) -> f32;
import "env.playNote" fn playNote(i32, i32);
import "env.sndGes" fn sndGes(i32) -> f32;
import "env.blitSprite" fn blitSprite(i32, i32, i32, i32, i32);
import "env.grabSprite" fn grabSprite(i32, i32, i32, i32, i32);
const TIME_MS = 0x40;
const GAMEPAD = 0x44;

View File

@@ -30,10 +30,13 @@
(import "env" "setTextColor" (func $setTextColor (param i32)))
(import "env" "setBackgroundColor" (func $setBackgroundColor (param i32)))
(import "env" "setCursorPosition" (func $setCursorPosition (param i32) (param i32)))
(import "env" "rectangle_outline" (func $rectangle_outline (param f32) (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" "rectangleOutline" (func $rectangleOutline (param f32) (param f32) (param f32) (param f32) (param i32)))
(import "env" "circleOutline" (func $circleOutline (param f32) (param f32) (param f32) (param i32)))
(import "env" "exp" (func $exp (param f32) (result f32)))
(import "env" "playNote" (func $playNote (param i32) (param i32)))
(import "env" "sndGes" (func $sndGes (param i32) (result f32)))
(import "env" "blitSprite" (func $blitSprite (param i32) (param i32) (param i32) (param i32) (param i32)))
(import "env" "grabSprite" (func $grabSprite (param i32) (param i32) (param i32) (param i32) (param i32)))
;; to use defines, include this file with a preprocessor
;; like gpp (https://logological.org/gpp).

374
platform/Cargo.lock generated
View File

@@ -19,47 +19,54 @@ dependencies = [
[[package]]
name = "ahash"
version = "0.7.6"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"getrandom",
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "anyhow"
version = "1.0.52"
version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3"
checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
[[package]]
name = "ariadne"
version = "0.1.3"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7080ae01b2f0c312065d4914cd0f0de045eb8832e9415b355106a6cff3073cb4"
checksum = "f1cb2a2046bea8ce5e875551f5772024882de0b540c7f93dfc5d6cf1ca8b030c"
dependencies = [
"yansi",
]
[[package]]
name = "autocfg"
version = "1.0.1"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
[[package]]
name = "bitflags"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "bytemuck"
version = "1.7.3"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f"
checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15"
[[package]]
name = "cc"
version = "1.0.72"
version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41"
[[package]]
name = "cdivsufsort"
@@ -88,54 +95,47 @@ dependencies = [
[[package]]
name = "const-random"
version = "0.1.13"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f590d95d011aa80b063ffe3253422ed5aa462af4e9867d43ce8337562bac77c4"
checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
dependencies = [
"const-random-macro",
"proc-macro-hack",
]
[[package]]
name = "const-random-macro"
version = "0.1.13"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "615f6e27d000a2bffbc7f2f6a8669179378fa27ee4d0a509e985dfc0a7defb40"
checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
dependencies = [
"getrandom",
"lazy_static",
"proc-macro-hack",
"once_cell",
"tiny-keccak",
]
[[package]]
name = "crc32fast"
version = "1.3.0"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836"
checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.1"
version = "0.5.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.5"
version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
dependencies = [
"cfg-if",
"lazy_static",
]
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
[[package]]
name = "crunchy"
@@ -151,37 +151,47 @@ dependencies = [
"anyhow",
"ariadne",
"chumsky",
"pico-args",
"pico-args 0.4.2",
"wasm-encoder 0.10.0",
"wasmparser 0.83.0",
]
[[package]]
name = "fallible_collections"
version = "0.4.4"
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52db5973b6a19247baf19b30f41c23a1bfffc2e9ce0a5db2f60e3cd5dc8895f7"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fallible-iterator"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
[[package]]
name = "fallible_collections"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a88c69768c0a15262df21899142bc6df9b9b823546d4b4b9a7bc2d6c448ec6fd"
dependencies = [
"hashbrown",
"hashbrown 0.13.2",
]
[[package]]
name = "flate2"
version = "1.0.22"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
dependencies = [
"cfg-if",
"crc32fast",
"libc",
"miniz_oxide",
]
[[package]]
name = "getrandom"
version = "0.2.3"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
dependencies = [
"cfg-if",
"libc",
@@ -189,14 +199,37 @@ dependencies = [
]
[[package]]
name = "hashbrown"
version = "0.11.2"
name = "gimli"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
dependencies = [
"ahash 0.7.6",
"fallible-iterator",
"indexmap 1.9.3",
"stable_deref_trait",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hashbrown"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
dependencies = [
"ahash 0.8.11",
]
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "heck"
version = "0.3.3"
@@ -213,10 +246,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
[[package]]
name = "lazy_static"
version = "1.4.0"
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown 0.12.3",
]
[[package]]
name = "indexmap"
version = "2.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [
"equivalent",
"hashbrown 0.14.3",
]
[[package]]
name = "leb128"
@@ -225,17 +272,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.112"
name = "lexopt"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
checksum = "478ee9e62aaeaf5b140bd4138753d1f109765488581444218d3ddda43234f3e8"
[[package]]
name = "libc"
version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "lodepng"
version = "3.4.7"
version = "3.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24844d5c0b922ddd52fb5bf0964a4c7f8e799a946ec01bb463771eb04fc1a323"
checksum = "a42d298694b14401847de29abd44adf278b42e989e516deac7b72018400002d8"
dependencies = [
"crc32fast",
"fallible_collections",
"flate2",
"libc",
@@ -244,47 +298,42 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.14"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "miniz_oxide"
version = "0.4.4"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
dependencies = [
"adler",
"autocfg",
]
[[package]]
name = "num-traits"
version = "0.2.14"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.9.0"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "pbr"
version = "1.0.4"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff5751d87f7c00ae6403eb1fcbba229b9c76c9a30de8c1cf87182177b168cea2"
checksum = "ed5827dfa0d69b6c92493d6c38e633bbaa5937c153d0d7c28bf12313f8c6d514"
dependencies = [
"crossbeam-channel",
"libc",
"time",
"winapi",
]
@@ -294,6 +343,12 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468"
[[package]]
name = "pico-args"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
[[package]]
name = "platform"
version = "0.1.0"
@@ -304,35 +359,29 @@ dependencies = [
"uw8-tool",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro2"
version = "1.0.36"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
dependencies = [
"unicode-xid",
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.15"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rgb"
version = "0.8.31"
version = "0.8.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a374af9a0e5fdcdd98c1c7b64f05004f9ea2555b6c75f211daa81268a3c50f1"
checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8"
dependencies = [
"bytemuck",
]
@@ -347,24 +396,57 @@ dependencies = [
]
[[package]]
name = "syn"
version = "1.0.86"
name = "semver"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
"unicode-ident",
]
[[package]]
name = "time"
version = "0.1.43"
name = "syn"
version = "2.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687"
dependencies = [
"libc",
"winapi",
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
]
[[package]]
@@ -377,26 +459,26 @@ dependencies = [
]
[[package]]
name = "unicode-segmentation"
version = "1.9.0"
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-xid"
version = "0.2.2"
name = "unicode-segmentation"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
[[package]]
name = "upkr"
version = "0.1.0"
source = "git+https://github.com/exoticorn/upkr.git?rev=2e7983fc#2e7983fc650788d98da2eecef2d16f63e849e4a0"
version = "0.2.1"
source = "git+https://github.com/exoticorn/upkr.git?rev=080db40d0088bbee2bdf3c5c75288ac7853d6b7a#080db40d0088bbee2bdf3c5c75288ac7853d6b7a"
dependencies = [
"anyhow",
"cdivsufsort",
"pbr",
"pico-args",
"lexopt",
"thiserror",
]
[[package]]
@@ -405,31 +487,33 @@ version = "0.1.0"
dependencies = [
"anyhow",
"pbr",
"pico-args",
"pico-args 0.5.0",
"upkr",
"walrus",
"wasm-encoder 0.8.0",
"wasmparser 0.81.0",
"wasm-encoder 0.201.0",
"wasmparser 0.201.0",
]
[[package]]
name = "version_check"
version = "0.9.3"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "walrus"
version = "0.19.0"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eb08e48cde54c05f363d984bb54ce374f49e242def9468d2e1b6c2372d291f8"
checksum = "2c03529cd0c4400a2449f640d2f27cd1b48c3065226d15e26d98e4429ab0adb7"
dependencies = [
"anyhow",
"gimli",
"id-arena",
"leb128",
"log",
"walrus-macro",
"wasmparser 0.77.0",
"wasm-encoder 0.29.0",
"wasmparser 0.80.2",
]
[[package]]
@@ -441,23 +525,14 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "wasm-encoder"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db0c351632e46cc06a58a696a6c11e4cf90cad4b9f8f07a0b59128d616c29bb0"
dependencies = [
"leb128",
]
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-encoder"
@@ -469,16 +544,28 @@ dependencies = [
]
[[package]]
name = "wasmparser"
version = "0.77.0"
name = "wasm-encoder"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b35c86d22e720a07d954ebbed772d01180501afe7d03d464f413bb5f8914a8d6"
checksum = "18c41dbd92eaebf3612a39be316540b8377c871cb9bde6b064af962984912881"
dependencies = [
"leb128",
]
[[package]]
name = "wasm-encoder"
version = "0.201.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9c7d2731df60006819b013f64ccc2019691deccf6e11a1804bc850cd6748f1a"
dependencies = [
"leb128",
]
[[package]]
name = "wasmparser"
version = "0.81.0"
version = "0.80.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98930446519f63d00a836efdc22f67766ceae8dbcc1571379f2bcabc6b2b9abc"
checksum = "449167e2832691a1bff24cde28d2804e90e09586a448c8e76984792c44334a6b"
[[package]]
name = "wasmparser"
@@ -486,6 +573,17 @@ version = "0.83.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a"
[[package]]
name = "wasmparser"
version = "0.201.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84e5df6dba6c0d7fafc63a450f1738451ed7a0b52295d83e868218fa286bf708"
dependencies = [
"bitflags",
"indexmap 2.2.6",
"semver",
]
[[package]]
name = "winapi"
version = "0.3.9"
@@ -510,6 +608,26 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "yansi"
version = "0.5.0"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71"
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
[[package]]
name = "zerocopy"
version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
]

View File

@@ -9,4 +9,4 @@ edition = "2021"
curlywas = { git="https://github.com/exoticorn/curlywas.git", rev="0e7ea50" }
uw8-tool = { path="../uw8-tool" }
anyhow = "1"
lodepng = "3.4"
lodepng = "3.7.2"

Binary file not shown.

Binary file not shown.

View File

@@ -10,7 +10,7 @@ const GesState.Size = GesState.Filter + 8*4;
const GesStateOffset = 32;
const GesBufferOffset = 32 + GesState.Size;
export fn gesSnd(t: i32) -> f32 {
export fn sndGes(t: i32) -> f32 {
let baseAddr = 0!0x12c78;
if !(t & 127) {
let i: i32;
@@ -62,7 +62,6 @@ export fn gesSnd(t: i32) -> f32 {
let phase = channelState!GesChannelState.Phase;
let inline pulseWidth = channelReg?1;
let phaseShift = (pulseWidth - 128) * 255;
let invPhaseInc = 1 as f32 / phaseInc as f32;
i = 0;
@@ -131,7 +130,7 @@ export fn gesSnd(t: i32) -> f32 {
let phaseInc = (freq * (65536.0 / 44100.0)) as i32;
let phase = channelState!GesChannelState.Phase;
if modSrc > ch {
if modSrc < ch {
phase = phase - (phaseInc << 6);
}

View File

@@ -62,6 +62,7 @@ export fn cls(col: i32) {
textCursorX = 0;
textCursorY = 0;
outputChannel = 0;
textScale = 1;
memory.fill(120, col, 320*240);
}
@@ -171,7 +172,7 @@ export fn rectangle(x: f32, y: f32, w: f32, h: f32, col: i32) {
}
}
export fn rectangle_outline(x: f32, y: f32, w: f32, h: f32, col: i32) {
export fn rectangleOutline(x: f32, y: f32, w: f32, h: f32, col: i32) {
let xl = nearest(x) as i32;
let xr = nearest(x + w) as i32;
let yt = nearest(y) as i32;
@@ -212,7 +213,7 @@ export fn circle(cx: f32, cy: f32, radius: f32, col: i32) {
}
}
export fn circle_outline(cx: f32, cy: f32, radius: f32, col: i32) {
export fn circleOutline(cx: f32, cy: f32, radius: f32, col: i32) {
let prev_w: f32;
let y = clamp(nearest(cy - radius) as i32, -1, 241);
let maxY = clamp(nearest(cy + radius) as i32, -1, 241);
@@ -352,6 +353,92 @@ export fn line(x1: f32, y1: f32, x2: f32, y2: f32, col: i32) {
setPixel(i32.trunc_sat_f32_s(x1 + f * dx), i32.trunc_sat_f32_s(y1 + f * dy), col);
}
export fn blitSprite(sprite: i32, size: i32, x: i32, y: i32, control: i32) {
let lazy width = size & 65535;
let lazy height = select(size >> 16, size >> 16, width);
let lazy x0 = select(x < 0, -x, 0);
let lazy x1 = select(x + width > 320, 320 - x, width);
let lazy y0 = select(y < 0, -y, 0);
let lazy y1 = select(y + height > 240, 240 - y, height);
let lazy numRows = y1 - y0;
let lazy numCols = x1 - x0;
if numRows <= 0 | numCols <= 0 {
return;
}
let trans = (control & 511) - 256;
let lazy flip_x = 1 - ((control >> 8) & 2);
let lazy flip_y = 1 - ((control >> 9) & 2);
if flip_x < 0 {
sprite += width - 1;
}
if flip_y < 0 {
sprite += (height - 1) * width;
}
let spriteRow = sprite + x0 * flip_x + y0 * flip_y * width;
let screenRow = x + x0 + (y + y0) * 320;
loop yloop {
let lx = 0;
loop xloop {
let lazy col = (spriteRow + lx * flip_x)?0;
if col != trans {
(screenRow + lx)?120 = col;
}
branch_if (lx +:= 1) < numCols: xloop;
}
spriteRow += width * flip_y;
screenRow += 320;
branch_if numRows -:= 1: yloop;
}
}
export fn grabSprite(sprite: i32, size: i32, x: i32, y: i32, control: i32) {
let lazy width = size & 65535;
let lazy height = select(size >> 16, size >> 16, width);
let lazy x0 = select(x < 0, -x, 0);
let lazy x1 = select(x + width > 320, 320 - x, width);
let lazy y0 = select(y < 0, -y, 0);
let lazy y1 = select(y + height > 240, 240 - y, height);
let lazy numRows = y1 - y0;
let lazy numCols = x1 - x0;
if numRows <= 0 | numCols <= 0 {
return;
}
let trans = (control & 511) - 256;
let lazy flip_x = 1 - ((control >> 8) & 2);
let lazy flip_y = 1 - ((control >> 9) & 2);
if flip_x < 0 {
sprite += width - 1;
}
if flip_y < 0 {
sprite += (height - 1) * width;
}
let spriteRow = sprite + x0 * flip_x + y0 * flip_y * width;
let screenRow = x + x0 + (y + y0) * 320;
loop yloop {
let lx = 0;
loop xloop {
let lazy col = (screenRow + lx)?120;
if col != trans {
(spriteRow + lx * flip_x)?0 = col;
}
branch_if (lx +:= 1) < numCols: xloop;
}
spriteRow += width * flip_y;
screenRow += 320;
branch_if numRows -:= 1: yloop;
}
}
//////////
// TEXT //
//////////
@@ -361,6 +448,7 @@ global mut textCursorY = 0;
global mut textColor = 15;
global mut bgColor = 0;
global mut outputChannel = 0;
global mut textScale = 1;
export fn printChar(char: i32) {
loop chars {
@@ -372,16 +460,9 @@ export fn printChar(char: i32) {
global mut controlCodeLength = 0;
fn printSingleChar(char: i32) {
if char >= 4 & char <= 6 {
outputChannel = char - 4;
if !outputChannel {
textCursorX = 0;
textCursorY = 0;
}
return;
}
let charSize = 8 * textScale;
if outputChannel >= 2 {
if outputChannel >= 2 & (char < 4 | char > 6) {
logChar(char);
return;
}
@@ -399,15 +480,24 @@ fn printSingleChar(char: i32) {
return;
}
if char >= 4 & char <= 6 {
outputChannel = char - 4;
if !outputChannel {
textCursorX = 0;
textCursorY = 0;
}
return;
}
if char == 7 {
80?0 = 80?0 ^ 2;
return;
}
if char == 8 {
textCursorX = textCursorX - 8;
textCursorX = textCursorX - charSize;
if !outputChannel & textCursorX < 0 {
textCursorX = 320-8;
textCursorX = 320-charSize;
printSingleChar(11);
}
return;
@@ -417,34 +507,34 @@ fn printSingleChar(char: i32) {
if !outputChannel & textCursorX >= 320 {
printChar(0xd0a);
}
textCursorX = textCursorX + 8;
textCursorX = textCursorX + charSize;
return;
}
if char == 10 {
textCursorY = textCursorY + 8;
textCursorY = textCursorY + charSize;
if !outputChannel & textCursorY >= 240 {
textCursorY = 240 - 8;
textCursorY = 240 - charSize;
let i: i32;
loop scroll_copy {
i!120 = i!(120 + 320 * 8);
branch_if (i := i + 4) < 320 * (240 - 8): scroll_copy;
i!120 = (i + 320 * charSize)!120;
branch_if (i := i + 4) < 320 * (240 - charSize): scroll_copy;
}
rectangle(0 as f32, (240 - 8) as f32, 320 as f32, 8 as f32, bgColor);
rectangle(0 as f32, (240 - charSize) as f32, 320 as f32, charSize as f32, bgColor);
}
return;
}
if char == 11 {
textCursorY = textCursorY - 8;
textCursorY = textCursorY - charSize;
if !outputChannel & textCursorY < 0 {
textCursorY = 0;
let i = 320 * (240 - 8);
let i = 320 * (240 - charSize);
loop scroll_copy {
i!(116 + 320 * 8) = i!116;
(i + 320 * charSize)!116 = i!116;
branch_if (i := i - 4): scroll_copy;
}
rectangle(0 as f32, 0 as f32, 320 as f32, 8 as f32, bgColor);
rectangle(0 as f32, 0 as f32, 320 as f32, charSize as f32, bgColor);
}
return;
}
@@ -476,6 +566,12 @@ fn printSingleChar(char: i32) {
return;
}
if char == 30 {
let scale = 0x12d20?1;
textScale = select(scale > 0 & scale <= 16, scale, 1);
return;
}
if char == 31 {
textCursorX = 0x12d20?1 * (8 - outputChannel * 6);
textCursorY = 0x12d20?2 * (8 - outputChannel * 7);
@@ -498,7 +594,7 @@ data(0x12d00) {
1, 1, 1, 1, // 16-19,
1, 1, 1, 1, // 20-23,
1, 1, 1, 1, // 24-27,
1, 1, 1, 3 // 28-31
1, 1, 2, 3 // 28-31
)
}
@@ -507,26 +603,28 @@ fn drawChar(char: i32) {
printChar(0xd0a);
}
let charSize = 8 * textScale;
let y: i32;
loop rows {
let bits = (char * 8 + y)?0x13400;
let bits = (char * 8 + y / textScale)?0x13400;
let x = 0;
if outputChannel {
loop pixels {
if (bits := bits << 1) & 256 {
if (bits << (x / textScale)) & 128 {
setPixel(textCursorX + x, textCursorY + y, textColor);
}
branch_if (x := x + 1) < 8: pixels;
branch_if (x := x + 1) < charSize: pixels;
}
} else {
loop pixels {
setPixel(textCursorX + x, textCursorY + y, select((bits := bits << 1) & 256, textColor, bgColor));
branch_if (x := x + 1) < 8: pixels;
setPixel(textCursorX + x, textCursorY + y, select((bits << (x / textScale)) & 128, textColor, bgColor));
branch_if (x := x + 1) < charSize: pixels;
}
}
branch_if (y := y + 1) < 8: rows;
branch_if (y := y + 1) < charSize: rows;
}
textCursorX = textCursorX + 8;
textCursorX = textCursorX + charSize;
}
export fn printString(ptr: i32) {

View File

@@ -14,6 +14,8 @@ The initial motivation behind MicroW8 was to explore whether there was a way to
* Memory: 256KB
* Gamepad input (D-Pad + 4 Buttons)
For detailed [documentation see here](docs).
## Examples
* [Skip Ahead](v0.2.0#AgVfq24KI2Ok2o8qVtPYj27fSuGnfeSKgbOkIOsaEQMov8TDYQ6UjdjwkZrYcM1i9alo4/+Bhm1PRFEa0YHJlJAk/PGoc2K41rejv9ZSqJqIHNjr7cappqhOR2jT+jk+0b0+U6hO+geRCTP2aufWs7L+f/Z27NFY8LKlqPSv+C6Rd6+ohoKi6sYl5Kcrlf1cyTinV7jTTnmbcXWVDBA5rRKxAGMUTDS8rHxqSztRITOaQVP1pSdYgi/BDdOJOxSOIkeaId84S+Ycls5na7EgwSfVIpgqF+tcfkUecb8t2mQrXA7pyKrh/wzHn5N6Oe5aOgmzY2YpTIct) (249 bytes): A port of my [TIC-80 256byte game](http://tic80.com/play?cart=1735) from LoveByte'21, now with sound
* [Fireworks](v0.2.0#AgwvgP+M59snqjl4CMKw5sqm1Zw9yJCbSviMjeLUdHus2a3yl/a99+uiBeqZgP/2jqSjrLjRk73COMM6OSLpsxK8ugT1kuk/q4hQUqqPpGozHoa0laulzGGcahzdfdJsYaK1sIdeIYS9M5PnJx/Wk9H+PvWEPy2Zvv7I6IW7Fg==) (127 bytes): Some fireworks to welcome 2022.
@@ -29,23 +31,19 @@ Examplers for older versions:
## Versions
### v0.2.0
### v0.4.0
* [Web runtime](v0.2.0)
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.2.0/microw8-0.2.0-linux.tgz)
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.2.0/microw8-0.2.0-macos.tgz)
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.2.0/microw8-0.2.0-windows.zip)
* [Web runtime](../v0.4.0)
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.4.0/microw8-0.4.0-linux.tgz)
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.4.0/microw8-0.4.0-macos.tgz)
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.4.0/microw8-0.4.0-windows.zip)
Changes:
* [add sound support!](docs#sound)
* add support to redirect text output to the console for debugging using control code 6
* update curlywas:
* add support for `else if`
* add support for escape sequences in strings
* add support for char literals
* add support for binop-assignment, eg. `+=`, `^=`, `<<=` etc. (also support for the tee operator: `+:=`)
* "integer constant cast to float" literal syntax in CurlyWas (ex. `1_f` is equivalent to `1 as f32`)
* add support for sound on mono- and surround-only devices
* update wasmtime dependency to fix performance regression in 0.3.0
* add frame counter since module start at location 72
* add 6 and 7 parameter function types to base module
### Older versions

View File

@@ -5,20 +5,23 @@ description = "Docs"
# Overview
MicroW8 loads WebAssembly modules with a maximum size of 256kb. You module needs to export
MicroW8 loads WebAssembly modules with a maximum size of 256kb. Your module needs to export
a function `fn upd()` which will be called once per frame.
After calling `upd` MicroW8 will display the 320x240 8bpp framebuffer located
at offset 120 in memory with the 32bpp palette located at 0x13000.
The memory has to be imported as `env` `memory` and has a maximum size of 256kb (4 pages).
If the module exports a function called `start`, it will be called once after the module is
loaded.
# Memory map
```
00000-00040: user memory
00040-00044: time since module start in ms
00044-0004c: gamepad state
0004c-00050: reserved
0004c-00050: number of frames since module start
00050-00070: sound data (synced to sound thread)
00070-00078: reserved
00078-12c78: frame buffer
@@ -143,13 +146,13 @@ Fills the circle at `cx, cy` and with `radius` with the given color index.
(Sets all pixels where the pixel center lies inside the circle.)
### fn rectangle_outline(x: f32, y: f32, w: f32, h: f32, color: i32)
### fn rectangleOutline(x: f32, y: f32, w: f32, h: f32, color: i32)
Draws a one pixel outline on the inside of the given rectangle.
(Draws the outermost pixels that are still inside the rectangle area.)
### fn circle_outline(cx: f32, cy: f32, radius: f32, color: i32)
### fn circleOutline(cx: f32, cy: f32, radius: f32, color: i32)
Draws a one pixel outline on the inside of the given circle.
@@ -159,6 +162,21 @@ Draws a one pixel outline on the inside of the given circle.
Draws a line from `x1,y1` to `x2,y2` in the given color index.
### fn blitSprite(spriteData: i32, size: i32, x: i32, y: i32, control: i32)
Copies the pixel data at `spriteData` onto the screen at `x`, `y`. The size of the sprite is passed as `width | (height << 16)`.
If the height is given as 0, the sprite is is treated as square (width x width).
The control parameter controls masking and flipping of the sprite:
* bits 0-7: color mask index
* bit 8: switch on masked blit (pixel with color mask index are treated as transparent)
* bit 9: flip sprite x
* bit 10: flip sprite y
### fn grabSprite(spriteData: i32, size: i32, x: i32, y: i32, control: i32)
Copies the pixel data on the screen at `x`, `y` to `spriteData`. Parameters are exactly the same as `blitSprite`.
## Input
MicroW8 provides input from a gamepad with one D-Pad and 4 buttons, or a keyboard emulation thereof.
@@ -220,6 +238,13 @@ When printing characters only the foreground pixels are set, the background is "
Moving/printing past any border does not cause any special operation, the cursor just goes off-screen.
### Text scale
An integer text scale factor in the range 1x-16x can be set with control char 30. An attempt to
set a scale outside that range will reset the scale to 1x.
After startup and `cls` the scale is initialized to 1x.
### Control chars
Characters 0-31 are control characters and don't print by default. They take the next 0-2 following characters as parameters.
@@ -244,7 +269,8 @@ Avoid the reserved control chars, they are currently NOPs but their behavior can
| 15 | color | Set the text color |
| 16-23 | - | Reserved |
| 24 | - | Swap text/background colors |
| 25-30 | - | Reserved |
| 25-29 | - | Reserved |
| 30 | scale | Set text scale (1-16) |
| 31 | x, y | Set cursor position (*) |
(*) In graphics mode, the x coordinate is doubled when using control char 31 to be able to cover the whole screen with one byte.
@@ -253,7 +279,7 @@ Avoid the reserved control chars, they are currently NOPs but their behavior can
Control code 6 switches all text output (except codes 4 and 5 to switch output back to the screen) to the console. Where exactly this ends
up (if at all) is an implementation detail of the runtimes. The native dev-runtime writes the debug output to `stdout`, the web runtime to
the debug console using `console.log`. Both implementation buffer the output until they encounter a newline character (10) in the output stream.
the debug console using `console.log`. Both implementations buffer the output until they encounter a newline character (10) in the output stream.
There may be future runtimes that ignore the debug output completely.
@@ -568,7 +594,7 @@ a base module provided by MicroW8.
You can generate this base module yourself using
`uw8-tool`. As a quick summary, it provides all function
types with up to 5 parameters (i32 or f32) where the
types with up to 7 parameters (i32 or f32) where the
`f32` parameters always preceed the `i32` parameters.
Then it includes all imports that MicroW8 provides,
a function section with a single function of type

View File

@@ -2,9 +2,64 @@
description = "Versions"
+++
### v0.4.0
* [Web runtime](../v0.4.0)
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.4.0/microw8-0.4.0-linux.tgz)
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.4.0/microw8-0.4.0-macos.tgz)
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.4.0/microw8-0.4.0-windows.zip)
Changes:
* add support for sound on mono- and surround-only devices
* update wasmtime dependency to fix performance regression in 0.3.0
* add frame counter since module start at location 72
* add 6 and 7 parameter function types to base module
### v0.3.0
* [Web runtime](../v0.3.0)
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.3.0/microw8-0.3.0-linux.tgz)
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.3.0/microw8-0.3.0-macos.tgz)
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.3.0/microw8-0.3.0-windows.zip)
Changes:
* add blitSprite and grabSprite API calls
* add support for integer scaling up to 16x for printing text
* fix incompatibility with sound devices only offering 16bit audio formats
* add support for br_table instruction in packed carts
### v0.2.2
* [Web runtime](../v0.2.2)
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.2.2/microw8-0.2.2-linux.tgz)
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.2.2/microw8-0.2.2-macos.tgz)
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.2.2/microw8-0.2.2-windows.zip)
Changes:
* call `start` function after loading cart if the cart exports one
* fix `sndGes` having the wrong name and not being included in the auto imports
* fix control codes 4-6 (change text output mode) being invoked when used as parameters in other control sequences
* only open browser window once a cart was compiled sucessfully when running with `-b`
### 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
* [Web runtime](v0.2.0)
* [Web runtime](../v0.2.0)
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.2.0/microw8-0.2.0-linux.tgz)
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.2.0/microw8-0.2.0-macos.tgz)
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.2.0/microw8-0.2.0-windows.zip)
@@ -22,7 +77,7 @@ Changes:
### v0.2.0-rc3
* [Web runtime](v0.2.0-rc3)
* [Web runtime](../v0.2.0-rc3)
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.2.0-rc3/microw8-0.2.0-rc3-linux.tgz)
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.2.0-rc3/microw8-0.2.0-rc3-macos.tgz)
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.2.0-rc3/microw8-0.2.0-rc3-windows.zip)
@@ -40,7 +95,7 @@ Changes:
### v0.2.0-rc2
* [Web runtime](v0.2.0-rc2)
* [Web runtime](../v0.2.0-rc2)
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.2.0-rc2/microw8-0.2.0-rc2-linux.tgz)
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.2.0-rc2/microw8-0.2.0-rc2-macos.tgz)
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.2.0-rc2/microw8-0.2.0-rc2-windows.zip)
@@ -51,7 +106,7 @@ Changes:
### v0.2.0-rc1
* [Web runtime](v0.2.0-rc1)
* [Web runtime](../v0.2.0-rc1)
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.2.0-rc1/microw8-0.2.0-rc1-linux.tgz)
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.2.0-rc1/microw8-0.2.0-rc1-macos.tgz)
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.2.0-rc1/microw8-0.2.0-rc1-windows.zip)
@@ -67,7 +122,7 @@ Known issues:
### v0.1.2
* [Web runtime](v0.1.2)
* [Web runtime](../v0.1.2)
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.1.2/microw8-0.1.2-linux.tgz)
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.1.2/microw8-0.1.2-macos.tgz)
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.1.2/microw8-0.1.2-windows.zip)
@@ -75,13 +130,13 @@ Known issues:
Changes:
* add option to `uw8 run` to run the cart in the browser using the web runtime
* CurlyWas: implement `include` support
*../ CurlyWas: implement `include` support
* CurlyWas: implement support for constants
* fix crash when trying to draw zero sized line
### v0.1.1
* [Web runtime](v0.1.1)
* [Web runtime](../v0.1.1)
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.1.1/microw8-0.1.1-linux.tgz)
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.1.1/microw8-0.1.1-macos.tgz)
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.1.1/microw8-0.1.1-windows.zip)
@@ -90,16 +145,16 @@ Changes:
* implement more robust file watcher
* add basic video recording on F10 in web runtime
* add screenshot on F9
*../ add screenshot on F9
* add watchdog to interrupt hanging update in native runtime
* add devkit mode to web runtime
* add unpack and compile commands to uw8
*../ add unpack and compile commands to uw8
* add support for table/element section in pack command
* disable wayland support (caused missing window decorations in gnome)
### v0.1.0
* [Web runtime](v0.1.0)
* [Web runtime](../v0.1.0)
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.1.0/microw8-0.1.0-linux.tgz)
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.1.0/microw8-0.1.0-macos.tgz)
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.1.0/microw8-0.1.0-windows.zip)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -4,7 +4,7 @@
<section>
<h1 class="text-center heading-text">A WebAssembly based fantasy console</h1>
</section>
<a href="v0.2.0">
<a href="v0.4.0">
<img class="demonstration-gif" style="width:640px;height:480px;image-rendering:pixelated" src="img/technotunnel.png"></img>
</a>
</div>

View File

@@ -1,9 +1,13 @@
use anyhow::{anyhow, bail, Result};
use notify::{DebouncedEvent, RecommendedWatcher, Watcher};
use notify_debouncer_mini::{
new_debouncer,
notify::{self, RecommendedWatcher},
DebouncedEvent, DebouncedEventKind, Debouncer,
};
use std::{collections::BTreeSet, path::PathBuf, sync::mpsc, time::Duration};
pub struct FileWatcher {
watcher: RecommendedWatcher,
debouncer: Debouncer<RecommendedWatcher>,
watched_files: BTreeSet<PathBuf>,
directories: BTreeSet<PathBuf>,
rx: mpsc::Receiver<DebouncedEvent>,
@@ -12,9 +16,18 @@ pub struct FileWatcher {
impl FileWatcher {
pub fn new() -> Result<FileWatcher> {
let (tx, rx) = mpsc::channel();
let watcher = notify::watcher(tx, Duration::from_millis(100))?;
let debouncer = new_debouncer(Duration::from_millis(100), move |res| match res {
Ok(events) => {
for event in events {
let _ = tx.send(event);
}
}
Err(err) => {
eprintln!("Error watching for file changes: {err}");
}
})?;
Ok(FileWatcher {
watcher,
debouncer,
watched_files: BTreeSet::new(),
directories: BTreeSet::new(),
rx,
@@ -26,7 +39,8 @@ impl FileWatcher {
let parent = path.parent().ok_or_else(|| anyhow!("File has no parent"))?;
if !self.directories.contains(parent) {
self.watcher
self.debouncer
.watcher()
.watch(parent, notify::RecursiveMode::NonRecursive)?;
self.directories.insert(parent.to_path_buf());
}
@@ -36,16 +50,18 @@ impl FileWatcher {
}
pub fn poll_changed_file(&self) -> Result<Option<PathBuf>> {
let event = self.rx.try_recv();
match event {
Ok(DebouncedEvent::Create(path) | DebouncedEvent::Write(path)) => {
let handle = same_file::Handle::from_path(&path)?;
for file in &self.watched_files {
if handle == same_file::Handle::from_path(file)? {
return Ok(Some(path));
match self.rx.try_recv() {
Ok(event) => match event.kind {
DebouncedEventKind::Any => {
let handle = same_file::Handle::from_path(&event.path)?;
for file in &self.watched_files {
if handle == same_file::Handle::from_path(file)? {
return Ok(Some(event.path));
}
}
}
}
_ => (),
},
Err(mpsc::TryRecvError::Disconnected) => bail!("File watcher disconnected"),
_ => (),
}

View File

@@ -81,11 +81,12 @@ fn run(mut args: Arguments) -> Result<()> {
#[cfg(not(feature = "native"))]
let run_browser = args.contains(["-b", "--browser"]) || true;
#[allow(unused)]
let disable_audio = args.contains(["-m", "--no-audio"]);
#[cfg(feature = "native")]
let window_config = {
let mut config = WindowConfig::default();
let mut config = uw8_window::WindowConfig::default();
if !run_browser {
config.parse_arguments(&mut args);
}
@@ -98,8 +99,6 @@ fn run(mut args: Arguments) -> Result<()> {
use std::process::exit;
use uw8_window::WindowConfig;
let mut runtime: Box<dyn Runtime> = if !run_browser {
#[cfg(not(feature = "native"))]
unimplemented!();

File diff suppressed because one or more lines are too long

View File

@@ -2,12 +2,12 @@ use std::sync::{mpsc, Arc, Mutex};
use std::time::Duration;
use std::{thread, time::Instant};
use anyhow::{anyhow, Result};
use anyhow::{anyhow, bail, Result};
use cpal::traits::*;
use rubato::Resampler;
use uw8_window::{Window, WindowConfig};
use wasmtime::{
Engine, GlobalType, Memory, MemoryType, Module, Mutability, Store, TypedFunc, ValType,
Engine, Func, GlobalType, Memory, MemoryType, Module, Mutability, Store, TypedFunc, ValType,
};
pub struct MicroW8 {
@@ -27,6 +27,7 @@ struct UW8Instance {
end_frame: TypedFunc<(), ()>,
update: Option<TypedFunc<(), ()>>,
start_time: Instant,
frame_counter: u32,
watchdog: Arc<Mutex<UW8WatchDog>>,
sound_tx: Option<mpsc::SyncSender<RegisterUpdate>>,
}
@@ -90,10 +91,10 @@ impl super::Runtime for MicroW8 {
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)?;
linker.define(&store, "env", "memory", memory)?;
let loader_instance = linker.instantiate(&mut store, &self.loader_module)?;
let load_uw8 = loader_instance.get_typed_func::<i32, i32, _>(&mut store, "load_uw8")?;
let load_uw8 = loader_instance.get_typed_func::<i32, i32>(&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);
@@ -131,8 +132,12 @@ impl super::Runtime for MicroW8 {
}
let instance = linker.instantiate(&mut store, &module)?;
let end_frame = platform_instance.get_typed_func::<(), (), _>(&mut store, "endFrame")?;
let update = instance.get_typed_func::<(), (), _>(&mut store, "upd").ok();
let end_frame = platform_instance.get_typed_func::<(), ()>(&mut store, "endFrame")?;
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 {
(None, None)
@@ -155,6 +160,7 @@ impl super::Runtime for MicroW8 {
end_frame,
update,
start_time: Instant::now(),
frame_counter: 0,
watchdog,
sound_tx,
});
@@ -187,8 +193,11 @@ impl super::Runtime for MicroW8 {
let mem = instance.memory.data_mut(&mut instance.store);
mem[64..68].copy_from_slice(&time.to_le_bytes());
mem[68..72].copy_from_slice(&input.gamepads);
mem[72..76].copy_from_slice(&instance.frame_counter.to_le_bytes());
}
instance.frame_counter = instance.frame_counter.wrapping_add(1);
instance.store.set_epoch_deadline(self.timeout as u64);
if let Some(ref update) = instance.update {
if let Err(err) = update.call(&mut instance.store, ()) {
@@ -251,15 +260,12 @@ fn add_native_functions(
}
})?;
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 global = wasmtime::Global::new(
&mut *store,
GlobalType::new(ValType::I32, Mutability::Const),
0.into(),
)?;
linker.define(&store, "env", &format!("g_reserved{}", i), global)?;
}
Ok(())
@@ -272,14 +278,18 @@ fn instantiate_platform(
) -> Result<wasmtime::Instance> {
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 exports: Vec<(String, Func)> = platform_instance
.exports(&mut *store)
.map(|e| {
(
e.name().to_owned(),
e.into_func()
.expect("platform surely only exports functions"),
)
})
.collect();
for (name, func) in exports {
linker.define(&store, "env", &name, func)?;
}
Ok(platform_instance)
@@ -306,15 +316,15 @@ fn init_sound(
let memory = wasmtime::Memory::new(&mut store, MemoryType::new(4, Some(4)))?;
let mut linker = wasmtime::Linker::new(engine);
linker.define("env", "memory", memory)?;
linker.define(&store, "env", "memory", memory)?;
add_native_functions(&mut linker, &mut store)?;
let platform_instance = instantiate_platform(&mut linker, &mut store, platform_module)?;
let instance = linker.instantiate(&mut store, module)?;
let snd = instance
.get_typed_func::<(i32,), f32, _>(&mut store, "snd")
.or_else(|_| platform_instance.get_typed_func::<(i32,), f32, _>(&mut store, "gesSnd"))?;
.get_typed_func::<(i32,), f32>(&mut store, "snd")
.or_else(|_| platform_instance.get_typed_func::<(i32,), f32>(&mut store, "sndGes"))?;
let host = cpal::default_host();
let device = host
@@ -323,26 +333,40 @@ fn init_sound(
let mut configs: Vec<_> = device
.supported_output_configs()?
.filter(|config| {
config.channels() == 2 && config.sample_format() == cpal::SampleFormat::F32
config.sample_format() == cpal::SampleFormat::F32
|| config.sample_format() == cpal::SampleFormat::I16
})
.collect();
if configs.is_empty() {
eprintln!(
"No suitable audio output config found on device \"{}\", available configs:",
device.name()?
);
for config in device.supported_output_configs()? {
eprintln!(" {}ch {}", config.channels(), config.sample_format());
}
bail!("Failed to configure audio out");
}
configs.sort_by_key(|config| {
let rate = 44100
.max(config.min_sample_rate().0)
.min(config.max_sample_rate().0);
if rate >= 44100 {
let rate_prio = if rate >= 44100 {
rate - 44100
} else {
(44100 - rate) * 1000
}
};
let format_prio = (config.sample_format() == cpal::SampleFormat::I16) as u32;
let channels_prio = (config.channels() != 2) as u32 * 16777216;
rate_prio + format_prio + channels_prio
});
let config = configs
.into_iter()
.next()
.ok_or_else(|| anyhow!("Could not find float output config"))?;
let config = configs.into_iter().next().unwrap();
let sample_rate = cpal::SampleRate(44100)
.max(config.min_sample_rate())
.max(config.max_sample_rate());
.min(config.max_sample_rate());
let config = config.with_sample_rate(sample_rate);
let buffer_size = match *config.buffer_size() {
cpal::SupportedBufferSize::Unknown => cpal::BufferSize::Default,
@@ -350,6 +374,8 @@ fn init_sound(
cpal::BufferSize::Fixed(256.max(min).min(max))
}
};
let sample_format = config.sample_format();
let num_channels = config.channels();
let config = cpal::StreamConfig {
buffer_size,
..config.config()
@@ -369,8 +395,8 @@ fn init_sound(
None
} else {
let rs = rubato::FftFixedIn::new(44100, sample_rate, 128, 1, 2)?;
let input_buffers = rs.input_buffer_allocate();
let output_buffers = rs.output_buffer_allocate();
let input_buffers = rs.input_buffer_allocate(true);
let output_buffers = rs.output_buffer_allocate(true);
Some(Resampler {
resampler: rs,
input_buffers,
@@ -382,96 +408,220 @@ fn init_sound(
let mut sample_index = 0;
let mut pending_updates: Vec<RegisterUpdate> = Vec::with_capacity(30);
let mut current_time = 0;
let stream = device.build_output_stream(
&config,
move |mut outer_buffer: &mut [f32], _| {
let mut first_update = true;
while let Ok(update) = rx.try_recv() {
if first_update {
current_time += update.time.wrapping_sub(current_time) / 8;
first_update = false;
}
pending_updates.push(update);
let mut callback = move |mut outer_buffer: &mut [f32]| {
let mut first_update = true;
while let Ok(update) = rx.try_recv() {
if first_update {
current_time += update.time.wrapping_sub(current_time) / 8;
first_update = false;
}
pending_updates.push(update);
}
while !outer_buffer.is_empty() {
store.set_epoch_deadline(30);
while pending_updates
.first()
.into_iter()
.any(|u| u.time.wrapping_sub(current_time) <= 0)
{
let update = pending_updates.remove(0);
memory.write(&mut store, 80, &update.data).unwrap();
}
while !outer_buffer.is_empty() {
store.set_epoch_deadline(30);
while pending_updates
.first()
.into_iter()
.any(|u| u.time.wrapping_sub(current_time) <= 0)
{
let update = pending_updates.remove(0);
memory.write(&mut store, 80, &update.data).unwrap();
}
let duration = if let Some(update) = pending_updates.first() {
((update.time.wrapping_sub(current_time) as usize) * sample_rate + 999) / 1000
} else {
outer_buffer.len()
};
let step_size = (duration.max(64) * 2).min(outer_buffer.len());
let duration = if let Some(update) = pending_updates.first() {
((update.time.wrapping_sub(current_time) as usize) * sample_rate + 999) / 1000
let mut buffer = &mut outer_buffer[..step_size];
{
let mem = memory.data_mut(&mut store);
mem[64..68].copy_from_slice(&current_time.to_le_bytes());
}
fn clamp_sample(s: f32) -> f32 {
if s.is_nan() {
0.0
} else {
outer_buffer.len()
};
let step_size = (duration.max(64) * 2).min(outer_buffer.len());
let mut buffer = &mut outer_buffer[..step_size];
{
let mem = memory.data_mut(&mut store);
mem[64..68].copy_from_slice(&current_time.to_le_bytes());
s.max(-1.0).min(1.0)
}
}
if let Some(ref mut resampler) = resampler {
while !buffer.is_empty() {
let copy_size = resampler.output_buffers[0]
.len()
.saturating_sub(resampler.output_index)
.min(buffer.len() / 2);
if copy_size == 0 {
resampler.input_buffers[0].clear();
resampler.input_buffers[1].clear();
for _ in 0..resampler.resampler.input_frames_next() {
resampler.input_buffers[0]
.push(snd.call(&mut store, (sample_index,)).unwrap_or(0.0));
resampler.input_buffers[1]
.push(snd.call(&mut store, (sample_index + 1,)).unwrap_or(0.0));
sample_index = sample_index.wrapping_add(2);
}
resampler
.resampler
.process_into_buffer(
&resampler.input_buffers,
&mut resampler.output_buffers,
None,
)
.unwrap();
resampler.output_index = 0;
} else {
for i in 0..copy_size {
buffer[i * 2] =
resampler.output_buffers[0][resampler.output_index + i];
buffer[i * 2 + 1] =
resampler.output_buffers[1][resampler.output_index + i];
}
resampler.output_index += copy_size;
buffer = &mut buffer[copy_size * 2..];
if let Some(ref mut resampler) = resampler {
while !buffer.is_empty() {
let copy_size = resampler.output_buffers[0]
.len()
.saturating_sub(resampler.output_index)
.min(buffer.len() / 2);
if copy_size == 0 {
resampler.input_buffers[0].clear();
resampler.input_buffers[1].clear();
for _ in 0..resampler.resampler.input_frames_next() {
resampler.input_buffers[0].push(clamp_sample(
snd.call(&mut store, (sample_index,)).unwrap_or(0.0),
));
resampler.input_buffers[1].push(clamp_sample(
snd.call(&mut store, (sample_index + 1,)).unwrap_or(0.0),
));
sample_index = sample_index.wrapping_add(2);
}
}
} else {
for v in buffer {
*v = snd.call(&mut store, (sample_index,)).unwrap_or(0.0);
sample_index = sample_index.wrapping_add(1);
resampler
.resampler
.process_into_buffer(
&resampler.input_buffers,
&mut resampler.output_buffers,
None,
)
.unwrap();
resampler.output_index = 0;
} else {
for i in 0..copy_size {
buffer[i * 2] = resampler.output_buffers[0][resampler.output_index + i];
buffer[i * 2 + 1] =
resampler.output_buffers[1][resampler.output_index + i];
}
resampler.output_index += copy_size;
buffer = &mut buffer[copy_size * 2..];
}
}
outer_buffer = &mut outer_buffer[step_size..];
current_time =
current_time.wrapping_add((step_size * 500 / sample_rate).max(1) as i32);
} else {
for v in buffer {
*v = clamp_sample(snd.call(&mut store, (sample_index,)).unwrap_or(0.0));
sample_index = sample_index.wrapping_add(1);
}
}
},
move |err| {
dbg!(err);
},
)?;
outer_buffer = &mut outer_buffer[step_size..];
current_time = current_time.wrapping_add((step_size * 500 / sample_rate).max(1) as i32);
}
};
fn f32_to_i16<F>(mut buffer: &mut [i16], callback: &mut F)
where
F: FnMut(&mut [f32]),
{
let mut float_buffer = [0f32; 256];
while !buffer.is_empty() {
let step_size = buffer.len().min(float_buffer.len());
let step_buffer = &mut float_buffer[..step_size];
callback(step_buffer);
for (dest, src) in buffer.iter_mut().take(step_size).zip(step_buffer.iter()) {
*dest = (src.max(-1.0).min(1.0) * 32767.0) as i16;
}
buffer = &mut buffer[step_size..];
}
}
fn stereo_to_mono<F>(mut buffer: &mut [f32], callback: &mut F)
where
F: FnMut(&mut [f32]),
{
let mut in_buffer = [0f32; 256];
while !buffer.is_empty() {
let step_size = buffer.len().min(in_buffer.len() / 2);
let step_buffer = &mut in_buffer[..step_size * 2];
callback(step_buffer);
for (index, dest) in buffer.iter_mut().take(step_size).enumerate() {
*dest = (step_buffer[index * 2] + step_buffer[index * 2 + 1]) * 0.5;
}
buffer = &mut buffer[step_size..];
}
}
fn stereo_to_surround<F>(mut buffer: &mut [f32], num_channels: usize, callback: &mut F)
where
F: FnMut(&mut [f32]),
{
let mut in_buffer = [0f32; 256];
buffer.fill(0.);
while !buffer.is_empty() {
let step_size = (buffer.len() / num_channels).min(in_buffer.len() / 2);
let step_buffer = &mut in_buffer[..step_size * 2];
callback(step_buffer);
for index in 0..step_size {
buffer[index * num_channels + 0] = step_buffer[index * 2 + 0];
buffer[index * num_channels + 1] = step_buffer[index * 2 + 1];
}
buffer = &mut buffer[step_size * num_channels..];
}
}
let stream = if sample_format == cpal::SampleFormat::F32 {
if num_channels == 2 {
device.build_output_stream(
&config,
move |buffer: &mut [f32], _| callback(buffer),
move |err| {
dbg!(err);
},
None,
)?
} else if num_channels == 1 {
device.build_output_stream(
&config,
move |buffer: &mut [f32], _| stereo_to_mono(buffer, &mut callback),
move |err| {
dbg!(err);
},
None,
)?
} else {
device.build_output_stream(
&config,
move |buffer: &mut [f32], _| {
stereo_to_surround(buffer, num_channels as usize, &mut callback)
},
move |err| {
dbg!(err);
},
None,
)?
}
} else {
if num_channels == 2 {
device.build_output_stream(
&config,
move |buffer: &mut [i16], _| f32_to_i16(buffer, &mut callback),
move |err| {
dbg!(err);
},
None,
)?
} else if num_channels == 1 {
device.build_output_stream(
&config,
move |buffer: &mut [i16], _| {
f32_to_i16(buffer, &mut |b| stereo_to_mono(b, &mut callback))
},
move |err| {
dbg!(err);
},
None,
)?
} else {
device.build_output_stream(
&config,
move |buffer: &mut [i16], _| {
f32_to_i16(buffer, &mut |b| {
stereo_to_surround(b, num_channels as usize, &mut callback)
})
},
move |err| {
dbg!(err);
},
None,
)?
}
};
Ok(Uw8Sound { stream, tx })
}

View File

@@ -11,6 +11,7 @@ use warp::{http::Response, Filter};
pub struct RunWebServer {
cart: Arc<Mutex<Vec<u8>>>,
tx: broadcast::Sender<()>,
socket_addr: SocketAddr,
}
impl RunWebServer {
@@ -18,8 +19,13 @@ impl RunWebServer {
let cart = Arc::new(Mutex::new(Vec::new()));
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_tx = tx.clone();
let server_addr = socket_addr.clone();
thread::spawn(move || {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_io()
@@ -47,24 +53,26 @@ impl RunWebServer {
warp::sse::reply(warp::sse::keep_alive().stream(event_stream(&server_tx)))
});
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));
let server_future = warp::serve(html.or(cart).or(events)).bind(server_addr);
server_future.await
});
});
RunWebServer { cart, tx }
RunWebServer {
cart,
tx,
socket_addr,
}
}
}
impl super::Runtime for RunWebServer {
fn load(&mut self, module_data: &[u8]) -> Result<()> {
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.extend_from_slice(module_data);
}

View File

@@ -1,13 +0,0 @@
import "env.memory" memory(4);
import "env.printString" fn print(i32);
export fn upd() {
}
start fn start() {
print(0);
}
data 0 {
"Press " i8(0xe0) " and " i8(0xe1) " to adjust, " i8(0xcc) " to commit." i8(0)
}

BIN
test.wasm

Binary file not shown.

View File

@@ -2,17 +2,18 @@ import "env.memory" memory(4);
import "env.pow" fn pow(f32, f32) -> f32;
import "env.sin" fn sin(f32) -> f32;
import "env.cls" fn cls(i32);
import "env.exp" fn exp(f32) -> f32;
import "env.rectangle" fn rectangle(f32, f32, f32, f32, i32);
include "../platform/src/ges.cwa"
export fn snd(t: i32) -> f32 {
gesSnd(t)
sndGes(t)
}
export fn upd() {
80?0 = 32!32 / 200 & 2 | 0x41;
80?3 = (32!32 / 400)%7*12/7 + 40;
80?3 = (32!32 / 400)%8*12/7 + 40;
let pulse = (32!32 * 256 / 2000) & 511;
if pulse >= 256 {
pulse = 511 - pulse;

5
test/start_fn.cwa Normal file
View File

@@ -0,0 +1,5 @@
include "../examples/include/microw8-api.cwa"
export fn start() {
printChar('Test');
}

13
test/text_modes.cwa Normal file
View File

@@ -0,0 +1,13 @@
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
todo.txt Normal file
View File

@@ -0,0 +1 @@
* add support for 16bit sound (not just float)

246
uw8-tool/Cargo.lock generated
View File

@@ -4,21 +4,27 @@ version = 3
[[package]]
name = "anyhow"
version = "1.0.52"
version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3"
checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
[[package]]
name = "autocfg"
version = "1.0.1"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
[[package]]
name = "bitflags"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "cc"
version = "1.0.72"
version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41"
[[package]]
name = "cdivsufsort"
@@ -30,32 +36,56 @@ dependencies = [
"sacabase",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "crossbeam-channel"
version = "0.5.1"
version = "0.5.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.5"
version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fallible-iterator"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
[[package]]
name = "gimli"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
dependencies = [
"cfg-if",
"lazy_static",
"fallible-iterator",
"indexmap 1.9.3",
"stable_deref_trait",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "heck"
version = "0.3.3"
@@ -72,10 +102,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
[[package]]
name = "lazy_static"
version = "1.4.0"
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown 0.12.3",
]
[[package]]
name = "indexmap"
version = "2.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [
"equivalent",
"hashbrown 0.14.3",
]
[[package]]
name = "leb128"
@@ -84,61 +128,63 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.112"
name = "lexopt"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
checksum = "478ee9e62aaeaf5b140bd4138753d1f109765488581444218d3ddda43234f3e8"
[[package]]
name = "libc"
version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "log"
version = "0.4.14"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "num-traits"
version = "0.2.14"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
dependencies = [
"autocfg",
]
[[package]]
name = "pbr"
version = "1.0.4"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff5751d87f7c00ae6403eb1fcbba229b9c76c9a30de8c1cf87182177b168cea2"
checksum = "ed5827dfa0d69b6c92493d6c38e633bbaa5937c153d0d7c28bf12313f8c6d514"
dependencies = [
"crossbeam-channel",
"libc",
"time",
"winapi",
]
[[package]]
name = "pico-args"
version = "0.4.2"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468"
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
[[package]]
name = "proc-macro2"
version = "1.0.36"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
dependencies = [
"unicode-xid",
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.14"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
@@ -153,48 +199,80 @@ dependencies = [
]
[[package]]
name = "syn"
version = "1.0.84"
name = "semver"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecb2e6da8ee5eb9a61068762a32fa9619cc591ceb055b3687f4cd4051ec2e06b"
checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
"unicode-ident",
]
[[package]]
name = "time"
version = "0.1.44"
name = "syn"
version = "2.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687"
dependencies = [
"libc",
"wasi",
"winapi",
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.58",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-segmentation"
version = "1.8.0"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
[[package]]
name = "upkr"
version = "0.1.0"
source = "git+https://github.com/exoticorn/upkr.git?rev=d93aec186c9fb91d962c488682a2db125c61306c#d93aec186c9fb91d962c488682a2db125c61306c"
version = "0.2.1"
source = "git+https://github.com/exoticorn/upkr.git?rev=080db40d0088bbee2bdf3c5c75288ac7853d6b7a#080db40d0088bbee2bdf3c5c75288ac7853d6b7a"
dependencies = [
"anyhow",
"cdivsufsort",
"pbr",
"pico-args",
"lexopt",
"thiserror",
]
[[package]]
@@ -206,22 +284,24 @@ dependencies = [
"pico-args",
"upkr",
"walrus",
"wasm-encoder",
"wasmparser 0.81.0",
"wasm-encoder 0.201.0",
"wasmparser 0.201.0",
]
[[package]]
name = "walrus"
version = "0.19.0"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eb08e48cde54c05f363d984bb54ce374f49e242def9468d2e1b6c2372d291f8"
checksum = "2c03529cd0c4400a2449f640d2f27cd1b48c3065226d15e26d98e4429ab0adb7"
dependencies = [
"anyhow",
"gimli",
"id-arena",
"leb128",
"log",
"walrus-macro",
"wasmparser 0.77.0",
"wasm-encoder 0.29.0",
"wasmparser 0.80.2",
]
[[package]]
@@ -233,35 +313,43 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
name = "wasm-encoder"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
checksum = "18c41dbd92eaebf3612a39be316540b8377c871cb9bde6b064af962984912881"
dependencies = [
"leb128",
]
[[package]]
name = "wasm-encoder"
version = "0.8.0"
version = "0.201.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db0c351632e46cc06a58a696a6c11e4cf90cad4b9f8f07a0b59128d616c29bb0"
checksum = "b9c7d2731df60006819b013f64ccc2019691deccf6e11a1804bc850cd6748f1a"
dependencies = [
"leb128",
]
[[package]]
name = "wasmparser"
version = "0.77.0"
version = "0.80.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b35c86d22e720a07d954ebbed772d01180501afe7d03d464f413bb5f8914a8d6"
checksum = "449167e2832691a1bff24cde28d2804e90e09586a448c8e76984792c44334a6b"
[[package]]
name = "wasmparser"
version = "0.81.0"
version = "0.201.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98930446519f63d00a836efdc22f67766ceae8dbcc1571379f2bcabc6b2b9abc"
checksum = "84e5df6dba6c0d7fafc63a450f1738451ed7a0b52295d83e868218fa286bf708"
dependencies = [
"bitflags",
"indexmap 2.2.6",
"semver",
]
[[package]]
name = "winapi"

View File

@@ -6,10 +6,10 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
wasmparser = "0.81"
wasm-encoder = "0.8"
walrus = "0.19"
wasmparser = "0.201"
wasm-encoder = "0.201"
walrus = { version = "0.20.3", default-features = false }
anyhow = "1"
pico-args = "0.4"
upkr = { git = "https://github.com/exoticorn/upkr.git", rev = "d93aec186c9fb91d962c488682a2db125c61306c" }
pico-args = "0.5"
upkr = { git = "https://github.com/exoticorn/upkr.git", rev = "080db40d0088bbee2bdf3c5c75288ac7853d6b7a" }
pbr = "1"

View File

@@ -3,7 +3,7 @@ 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,
CodeSection, EntityType, ExportKind, ExportSection, Function, FunctionSection, ImportSection,
Instruction, MemoryType, Module, TypeSection, ValType,
};
use ValType::*;
@@ -37,7 +37,7 @@ impl BaseModule {
let mut types = vec![];
let mut type_map = HashMap::new();
for num_params in 0..6 {
for num_params in 0..8 {
for num_f32 in 0..=num_params {
for &result in &[None, Some(ValType::I32), Some(ValType::F32)] {
let mut params = vec![];
@@ -152,14 +152,14 @@ impl BaseModule {
add_function(
&mut functions,
&type_map,
"rectangle_outline",
"rectangleOutline",
&[F32, F32, F32, F32, I32],
None,
);
add_function(
&mut functions,
&type_map,
"circle_outline",
"circleOutline",
&[F32, F32, F32, I32],
None,
);
@@ -167,6 +167,22 @@ impl BaseModule {
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, "sndGes", &[I32], Some(F32));
add_function(
&mut functions,
&type_map,
"blitSprite",
&[I32, I32, I32, I32, I32],
None,
);
add_function(
&mut functions,
&type_map,
"grabSprite",
&[I32, I32, I32, I32, I32],
None,
);
for i in functions.len()..64 {
add_function(
@@ -217,13 +233,13 @@ impl BaseModule {
let mut imports = ImportSection::new();
for (module, name, type_) in &self.function_imports {
imports.import(*module, Some(name.as_str()), EntityType::Function(*type_));
imports.import(*module, name.as_str(), EntityType::Function(*type_));
}
for (module, name, import) in &self.global_imports {
imports.import(
*module,
Some(name.as_str()),
name.as_str(),
EntityType::Global(wasm_encoder::GlobalType {
val_type: import.type_,
mutable: import.mutable,
@@ -233,11 +249,12 @@ impl BaseModule {
imports.import(
"env",
Some("memory"),
"memory",
MemoryType {
minimum: self.memory as u64,
maximum: None,
memory64: false,
shared: false,
},
);
@@ -258,7 +275,7 @@ impl BaseModule {
let mut exports = ExportSection::new();
for (name, fnc) in &self.exports {
exports.export(*name, Export::Function(*fnc));
exports.export(*name, ExportKind::Func, *fnc);
}
module.section(&exports);
@@ -286,7 +303,7 @@ impl BaseModule {
pub fn create_binary(path: &Path) -> Result<()> {
let base1 = BaseModule::for_format_version(1)?.to_wasm();
let data = upkr::pack(&base1, 4, false, None);
let data = upkr::pack(&base1, 4, &upkr::Config::default(), None);
File::create(path)?.write_all(&data)?;
Ok(())
}

View File

@@ -1,13 +1,17 @@
use std::path::Path;
use anyhow::Result;
use std::path::Path;
pub fn filter_exports(in_path: &Path, out_path: &Path) -> Result<()> {
let mut module = walrus::Module::from_file(in_path)?;
let exports_to_delete: Vec<_> = module.exports.iter().filter_map(|export| match export.name.as_str() {
"upd" => None,
_ => Some(export.id())
}).collect();
let exports_to_delete: Vec<_> = module
.exports
.iter()
.filter_map(|export| match export.name.as_str() {
"start" | "upd" | "snd" => None,
_ => Some(export.id()),
})
.collect();
for id in exports_to_delete {
module.exports.delete(id);

View File

@@ -10,7 +10,7 @@ use std::{
use wasm_encoder as enc;
use wasmparser::{
BinaryReader, ExportSectionReader, ExternalKind, FunctionBody, FunctionSectionReader,
ImportSectionEntryType, ImportSectionReader, TableSectionReader, TypeSectionReader,
ImportSectionReader, TableSectionReader, TypeRef, TypeSectionReader,
};
pub struct PackConfig {
@@ -63,7 +63,7 @@ pub fn pack(data: &[u8], config: &PackConfig) -> Result<Vec<u8>> {
uw8.extend_from_slice(&upkr::pack(
&result[8..],
level,
false,
&upkr::Config::default(),
Some(&mut |pos| {
pb.set(pos as u64);
}),
@@ -90,7 +90,10 @@ pub fn unpack(data: Vec<u8>) -> Result<Vec<u8>> {
let (version, data) = match data[0] {
0 => return Ok(data),
1 => (1, data[1..].to_vec()),
2 => (1, upkr::unpack(&data[1..], false)),
2 => (
1,
upkr::unpack(&data[1..], &upkr::Config::default(), 4 * 1024 * 1024)?,
),
other => bail!("Uknown format version {}", other),
};
@@ -133,18 +136,19 @@ pub fn unpack(data: Vec<u8>) -> Result<Vec<u8>> {
Ok(dest)
}
fn to_val_type(type_: &wasmparser::Type) -> Result<ValType> {
use wasmparser::Type::*;
fn to_val_type(type_: &wasmparser::ValType) -> Result<ValType> {
use wasmparser::ValType::*;
Ok(match *type_ {
I32 => ValType::I32,
I64 => ValType::I64,
F32 => ValType::F32,
F64 => ValType::F64,
V128 => ValType::V128,
_ => bail!("Type {:?} isn't a value type", type_),
})
}
fn to_val_type_vec(types: &[wasmparser::Type]) -> Result<Vec<ValType>> {
fn to_val_type_vec(types: &[wasmparser::ValType]) -> Result<Vec<ValType>> {
types.into_iter().map(to_val_type).collect()
}
@@ -202,7 +206,7 @@ impl<'a> ParsedModule<'a> {
import_section = Some(Section::new(range, ImportSection::parse(reader)?));
}
Payload::GlobalSection(reader) => {
global_section = Some(Section::new(range, reader.get_count()));
global_section = Some(Section::new(range, reader.count()));
}
Payload::FunctionSection(reader) => {
function_section = Some(Section::new(range, read_function_section(reader)?));
@@ -220,17 +224,22 @@ impl<'a> ParsedModule<'a> {
validate_table_section(reader)?;
table_section = Some(Section::new(range, ()));
}
Payload::ElementSection(mut reader) => {
let mut elements = Vec::with_capacity(reader.get_count() as usize);
for _ in 0..reader.get_count() {
elements.push(Element::parse(reader.read()?)?);
Payload::MemorySection(reader) => {
if reader.count() != 0 {
bail!("Found non-empty MemorySection. Memory has to be imported!");
}
}
Payload::ElementSection(reader) => {
let mut elements = Vec::with_capacity(reader.count() as usize);
for element in reader {
elements.push(Element::parse(element?)?);
}
element_section = Some(elements);
}
Payload::CodeSectionStart { .. } => (),
Payload::CodeSectionEntry(body) => function_bodies.push(body),
Payload::CustomSection { .. } => (),
Payload::End => break,
Payload::End(..) => break,
other => bail!("Unsupported section: {:?}", other),
}
@@ -458,7 +467,7 @@ impl<'a> ParsedModule<'a> {
{
let mut export_section = enc::ExportSection::new();
for (name, fnc) in my_exports {
export_section.export(&name, enc::Export::Function(fnc));
export_section.export(&name, enc::ExportKind::Func, fnc);
}
module.section(&export_section);
}
@@ -481,8 +490,7 @@ impl<'a> ParsedModule<'a> {
}
element_section.active(
None,
&wasm_encoder::Instruction::I32Const(element.start_index as i32),
ValType::FuncRef,
&wasm_encoder::ConstExpr::i32_const(element.start_index as i32),
wasm_encoder::Elements::Functions(&functions),
);
}
@@ -528,30 +536,32 @@ fn copy_section(module: &mut wasm_encoder::Module, data: &[u8]) -> Result<()> {
fn read_type_section(reader: TypeSectionReader) -> Result<Vec<base_module::FunctionType>> {
let mut function_types = vec![];
for type_def in reader {
match type_def? {
wasmparser::TypeDef::Func(fnc) => {
if fnc.returns.len() > 1 {
bail!("Multi-value not supported");
for rec_group in reader {
for sub_type in rec_group?.into_types() {
match sub_type.composite_type {
wasmparser::CompositeType::Func(fnc) => {
if fnc.results().len() > 1 {
bail!("Multi-value not supported");
}
let params = to_val_type_vec(fnc.params())?;
let result = to_val_type_vec(fnc.results())?.into_iter().next();
function_types.push(FunctionType { params, result });
}
let params = to_val_type_vec(&fnc.params)?;
let result = to_val_type_vec(&fnc.returns)?.into_iter().next();
function_types.push(FunctionType { params, result });
_ => bail!("Only function types are supported"),
}
t => bail!("Unsupported type def {:?}", t),
}
}
Ok(function_types)
}
fn validate_table_section(mut reader: TableSectionReader) -> Result<()> {
if reader.get_count() != 1 {
fn validate_table_section(reader: TableSectionReader) -> Result<()> {
if reader.count() != 1 {
bail!("Only up to one table supported");
}
let type_ = reader.read()?;
if type_.element_type != wasmparser::Type::FuncRef {
let table = reader.into_iter().next().unwrap()?;
if !table.ty.element_type.is_func_ref() {
bail!("Only one funcref table is supported");
}
@@ -585,45 +595,38 @@ impl ImportSection {
for import in reader {
let import = import?;
if let Some(field) = import.field {
match import.ty {
ImportSectionEntryType::Function(type_) => {
functions.push(FunctionImport {
module: import.module.to_string(),
field: field.to_string(),
type_,
});
}
ImportSectionEntryType::Memory(mem) => {
if import.module != "env" || field != "memory" {
bail!(
"Wrong name of memory import {}.{}, should be env.memory",
import.module,
field
);
}
if mem.memory64 || mem.shared {
bail!("Wrong memory import options: {:?}", import.ty);
}
memory = mem.maximum.unwrap_or(mem.initial) as u32;
}
ImportSectionEntryType::Global(glbl) => {
globals.push(GlobalImport {
module: import.module.to_string(),
field: field.to_string(),
type_: GlobalType {
type_: to_val_type(&glbl.content_type)?,
mutable: glbl.mutable,
},
});
}
_ => bail!("Unsupported import item {:?}", import.ty),
match import.ty {
TypeRef::Func(type_) => {
functions.push(FunctionImport {
module: import.module.to_string(),
field: import.name.to_string(),
type_,
});
}
} else {
bail!(
"Found import without field, only module '{}'",
import.module
);
TypeRef::Memory(mem) => {
if import.module != "env" || import.name != "memory" {
bail!(
"Wrong name of memory import {}.{}, should be env.memory",
import.module,
import.name
);
}
if mem.memory64 || mem.shared {
bail!("Wrong memory import options: {:?}", import.ty);
}
memory = mem.maximum.unwrap_or(mem.initial) as u32;
}
TypeRef::Global(glbl) => {
globals.push(GlobalImport {
module: import.module.to_string(),
field: import.name.to_string(),
type_: GlobalType {
type_: to_val_type(&glbl.content_type)?,
mutable: glbl.mutable,
},
});
}
_ => bail!("Unsupported import item {:?}", import.ty),
}
}
@@ -643,40 +646,40 @@ struct Element {
impl Element {
fn parse(element: wasmparser::Element) -> Result<Element> {
if element.ty != wasmparser::Type::FuncRef {
bail!("Table element type is not FuncRef");
}
match element.items {
wasmparser::ElementItems::Functions(funcs_reader) => {
let start_index = if let wasmparser::ElementKind::Active {
offset_expr,
table_index,
} = element.kind
{
if table_index.map(|i| i > 0).unwrap_or(false) {
bail!("Only function table with index 0 is supported");
}
let mut init_reader = offset_expr.get_operators_reader();
if let wasmparser::Operator::I32Const { value: start_index } =
init_reader.read()?
{
start_index as u32
} else {
bail!("Table element start index is not a integer constant");
}
} else {
bail!("Unsupported table element kind");
};
let start_index = if let wasmparser::ElementKind::Active {
init_expr,
table_index: 0,
} = element.kind
{
let mut init_reader = init_expr.get_operators_reader();
if let wasmparser::Operator::I32Const { value: start_index } = init_reader.read()? {
start_index as u32
} else {
bail!("Table element start index is not a integer constant");
}
} else {
bail!("Unsupported table element kind");
};
let mut items_reader = element.items.get_items_reader()?;
let mut functions = Vec::with_capacity(items_reader.get_count() as usize);
for _ in 0..items_reader.get_count() {
if let wasmparser::ElementItem::Func(index) = items_reader.read()? {
functions.push(index);
} else {
bail!("Table element item is not a function");
let mut functions = Vec::with_capacity(funcs_reader.count() as usize);
for index in funcs_reader {
functions.push(index?);
}
Ok(Element {
start_index,
functions,
})
}
_ => bail!("Table element type is not FuncRef"),
}
Ok(Element {
start_index,
functions,
})
}
}
@@ -707,8 +710,8 @@ fn read_export_section(reader: ExportSectionReader) -> Result<Vec<(String, u32)>
for export in reader {
let export = export?;
match export.kind {
ExternalKind::Function => {
function_exports.push((export.field.to_string(), export.index));
ExternalKind::Func => {
function_exports.push((export.name.to_string(), export.index));
}
_ => (), // just ignore all other kinds since MicroW8 doesn't expect any exports other than functions
}
@@ -729,13 +732,11 @@ fn remap_function(
}
let mut function = enc::Function::new(locals);
let block_type = |ty: wasmparser::TypeOrFuncType| -> Result<enc::BlockType> {
let block_type = |ty: wasmparser::BlockType| -> Result<enc::BlockType> {
Ok(match ty {
wasmparser::TypeOrFuncType::Type(wasmparser::Type::EmptyBlockType) => {
enc::BlockType::Empty
}
wasmparser::TypeOrFuncType::Type(ty) => enc::BlockType::Result(to_val_type(&ty)?),
wasmparser::TypeOrFuncType::FuncType(ty) => enc::BlockType::FunctionType(
wasmparser::BlockType::Empty => enc::BlockType::Empty,
wasmparser::BlockType::Type(ty) => enc::BlockType::Result(to_val_type(&ty)?),
wasmparser::BlockType::FuncType(ty) => enc::BlockType::FunctionType(
*type_map
.get(&ty)
.ok_or_else(|| anyhow!("Function type index out of range: {}", ty))?,
@@ -749,7 +750,7 @@ fn remap_function(
.ok_or_else(|| anyhow!("Global index out of range: {}", idx))?)
};
fn mem(m: wasmparser::MemoryImmediate) -> enc::MemArg {
fn mem(m: wasmparser::MemArg) -> enc::MemArg {
enc::MemArg {
offset: m.offset,
align: m.align as u32,
@@ -764,24 +765,31 @@ fn remap_function(
function.instruction(&match op? {
De::Unreachable => En::Unreachable,
De::Nop => En::Nop,
De::Block { ty } => En::Block(block_type(ty)?),
De::Loop { ty } => En::Loop(block_type(ty)?),
De::If { ty } => En::If(block_type(ty)?),
De::Block { blockty } => En::Block(block_type(blockty)?),
De::Loop { blockty } => En::Loop(block_type(blockty)?),
De::If { blockty } => En::If(block_type(blockty)?),
De::Else => En::Else,
De::Try { .. } | De::Catch { .. } | De::Throw { .. } | De::Rethrow { .. } => todo!(),
De::End => En::End,
De::Br { relative_depth } => En::Br(relative_depth),
De::BrIf { relative_depth } => En::BrIf(relative_depth),
De::BrTable { .. } => todo!(),
De::BrTable { targets } => En::BrTable(
targets.targets().collect::<Result<Vec<u32>, _>>()?.into(),
targets.default(),
),
De::Return => En::Return,
De::Call { function_index } => En::Call(
*function_map
.get(&function_index)
.ok_or_else(|| anyhow!("Function index out of range: {}", function_index))?,
),
De::CallIndirect { index, table_index } => En::CallIndirect {
De::CallIndirect {
type_index,
table_index,
table_byte: _, // what is this supposed to be?
} => En::CallIndirect {
ty: *type_map
.get(&index)
.get(&type_index)
.ok_or_else(|| anyhow!("Unknown function type in call indirect"))?,
table: table_index,
},
@@ -801,16 +809,16 @@ fn remap_function(
De::I64Load { memarg } => En::I64Load(mem(memarg)),
De::F32Load { memarg } => En::F32Load(mem(memarg)),
De::F64Load { memarg } => En::F64Load(mem(memarg)),
De::I32Load8S { memarg } => En::I32Load8_S(mem(memarg)),
De::I32Load8U { memarg } => En::I32Load8_U(mem(memarg)),
De::I32Load16S { memarg } => En::I32Load16_S(mem(memarg)),
De::I32Load16U { memarg } => En::I32Load16_U(mem(memarg)),
De::I64Load8S { memarg } => En::I64Load8_S(mem(memarg)),
De::I64Load8U { memarg } => En::I64Load8_U(mem(memarg)),
De::I64Load16S { memarg } => En::I64Load16_S(mem(memarg)),
De::I64Load16U { memarg } => En::I64Load16_U(mem(memarg)),
De::I64Load32S { memarg } => En::I64Load32_S(mem(memarg)),
De::I64Load32U { memarg } => En::I64Load32_U(mem(memarg)),
De::I32Load8S { memarg } => En::I32Load8S(mem(memarg)),
De::I32Load8U { memarg } => En::I32Load8U(mem(memarg)),
De::I32Load16S { memarg } => En::I32Load16S(mem(memarg)),
De::I32Load16U { memarg } => En::I32Load16U(mem(memarg)),
De::I64Load8S { memarg } => En::I64Load8S(mem(memarg)),
De::I64Load8U { memarg } => En::I64Load8U(mem(memarg)),
De::I64Load16S { memarg } => En::I64Load16S(mem(memarg)),
De::I64Load16U { memarg } => En::I64Load16U(mem(memarg)),
De::I64Load32S { memarg } => En::I64Load32S(mem(memarg)),
De::I64Load32U { memarg } => En::I64Load32U(mem(memarg)),
De::I32Store { memarg } => En::I32Store(mem(memarg)),
De::I64Store { memarg } => En::I64Store(mem(memarg)),
De::F32Store { memarg } => En::F32Store(mem(memarg)),
@@ -829,7 +837,7 @@ fn remap_function(
De::RefNull { .. } | De::RefIsNull { .. } | De::RefFunc { .. } => todo!(),
De::I32Eqz => En::I32Eqz,
De::I32Eq => En::I32Eq,
De::I32Ne => En::I32Neq,
De::I32Ne => En::I32Ne,
De::I32LtS => En::I32LtS,
De::I32LtU => En::I32LtU,
De::I32GtS => En::I32GtS,
@@ -840,7 +848,7 @@ fn remap_function(
De::I32GeU => En::I32GeU,
De::I64Eqz => En::I64Eqz,
De::I64Eq => En::I64Eq,
De::I64Ne => En::I64Neq,
De::I64Ne => En::I64Ne,
De::I64LtS => En::I64LtS,
De::I64LtU => En::I64LtU,
De::I64GtS => En::I64GtS,
@@ -850,13 +858,13 @@ fn remap_function(
De::I64GeS => En::I64GeS,
De::I64GeU => En::I64GeU,
De::F32Eq => En::F32Eq,
De::F32Ne => En::F32Neq,
De::F32Ne => En::F32Ne,
De::F32Lt => En::F32Lt,
De::F32Gt => En::F32Gt,
De::F32Le => En::F32Le,
De::F32Ge => En::F32Ge,
De::F64Eq => En::F64Eq,
De::F64Ne => En::F64Neq,
De::F64Ne => En::F64Ne,
De::F64Lt => En::F64Lt,
De::F64Gt => En::F64Gt,
De::F64Le => En::F64Le,
@@ -963,8 +971,246 @@ fn remap_function(
De::I64TruncSatF32U => En::I64TruncSatF32U,
De::I64TruncSatF64S => En::I64TruncSatF64S,
De::I64TruncSatF64U => En::I64TruncSatF64U,
De::MemoryCopy { src, dst } => En::MemoryCopy { src, dst },
De::MemoryCopy { src_mem, dst_mem } => En::MemoryCopy { src_mem, dst_mem },
De::MemoryFill { mem } => En::MemoryFill(mem),
De::V128Const { value } => En::V128Const(value.i128()),
De::V128Load { memarg } => En::V128Load(mem(memarg)),
De::V128Store { memarg } => En::V128Store(mem(memarg)),
De::V128Load8x8S { memarg } => En::V128Load8x8S(mem(memarg)),
De::V128Load8x8U { memarg } => En::V128Load8x8U(mem(memarg)),
De::V128Load16x4S { memarg } => En::V128Load16x4S(mem(memarg)),
De::V128Load16x4U { memarg } => En::V128Load16x4U(mem(memarg)),
De::V128Load32x2S { memarg } => En::V128Load32x2S(mem(memarg)),
De::V128Load32x2U { memarg } => En::V128Load32x2U(mem(memarg)),
De::V128Load8Splat { memarg } => En::V128Load8Splat(mem(memarg)),
De::V128Load16Splat { memarg } => En::V128Load16Splat(mem(memarg)),
De::V128Load32Splat { memarg } => En::V128Load32Splat(mem(memarg)),
De::V128Load64Splat { memarg } => En::V128Load64Splat(mem(memarg)),
De::V128Load32Zero { memarg } => En::V128Load32Zero(mem(memarg)),
De::V128Load64Zero { memarg } => En::V128Load64Zero(mem(memarg)),
De::V128Load8Lane { memarg, lane } => En::V128Load8Lane { memarg: mem(memarg), lane },
De::V128Load16Lane { memarg, lane } => En::V128Load16Lane { memarg: mem(memarg), lane },
De::V128Load32Lane { memarg, lane } => En::V128Load32Lane { memarg: mem(memarg), lane },
De::V128Load64Lane { memarg, lane } => En::V128Load64Lane { memarg: mem(memarg), lane },
De::V128Store8Lane { memarg, lane } => En::V128Store8Lane { memarg: mem(memarg), lane },
De::V128Store16Lane { memarg, lane } => En::V128Store16Lane { memarg: mem(memarg), lane },
De::V128Store32Lane { memarg, lane } => En::V128Store32Lane { memarg: mem(memarg), lane },
De::V128Store64Lane { memarg, lane } => En::V128Store64Lane { memarg: mem(memarg), lane },
De::I8x16ExtractLaneS { lane } => En::I8x16ExtractLaneS(lane),
De::I8x16ExtractLaneU { lane } => En::I8x16ExtractLaneU(lane),
De::I8x16ReplaceLane { lane } => En::I8x16ReplaceLane(lane),
De::I16x8ExtractLaneS { lane } => En::I16x8ExtractLaneS(lane),
De::I16x8ExtractLaneU { lane } => En::I16x8ExtractLaneU(lane),
De::I16x8ReplaceLane { lane } => En::I16x8ReplaceLane(lane),
De::I32x4ExtractLane { lane } => En::I32x4ExtractLane(lane),
De::I32x4ReplaceLane { lane } => En::I32x4ReplaceLane(lane),
De::I64x2ExtractLane { lane } => En::I64x2ExtractLane(lane),
De::I64x2ReplaceLane { lane } => En::I64x2ReplaceLane(lane),
De::F32x4ExtractLane { lane } => En::F32x4ExtractLane(lane),
De::F32x4ReplaceLane { lane } => En::F32x4ReplaceLane(lane),
De::F64x2ExtractLane { lane } => En::F64x2ExtractLane(lane),
De::F64x2ReplaceLane { lane } => En::F64x2ReplaceLane(lane),
De::I8x16Splat => En::I8x16Splat,
De::I16x8Splat => En::I16x8Splat,
De::I32x4Splat => En::I32x4Splat,
De::I64x2Splat => En::I64x2Splat,
De::F32x4Splat => En::F32x4Splat,
De::F64x2Splat => En::F64x2Splat,
De::I8x16Swizzle => En::I8x16Swizzle,
De::I8x16Add => En::I8x16Add,
De::I16x8Add => En::I16x8Add,
De::I32x4Add => En::I32x4Add,
De::I64x2Add => En::I64x2Add,
De::F32x4Add => En::F32x4Add,
De::F64x2Add => En::F64x2Add,
De::I8x16Sub => En::I8x16Sub,
De::I16x8Sub => En::I16x8Sub,
De::I32x4Sub => En::I32x4Sub,
De::I64x2Sub => En::I64x2Sub,
De::F32x4Sub => En::F32x4Sub,
De::F64x2Sub => En::F64x2Sub,
De::I16x8Mul => En::I16x8Mul,
De::I32x4Mul => En::I32x4Mul,
De::I64x2Mul => En::I64x2Mul,
De::F32x4Mul => En::F32x4Mul,
De::F64x2Mul => En::F64x2Mul,
De::I32x4DotI16x8S => En::I32x4DotI16x8S,
De::I8x16Neg => En::I8x16Neg,
De::I16x8Neg => En::I16x8Neg,
De::I32x4Neg => En::I32x4Neg,
De::I64x2Neg => En::I64x2Neg,
De::F32x4Neg => En::F32x4Neg,
De::F64x2Neg => En::F64x2Neg,
De::I16x8ExtMulLowI8x16S => En::I16x8ExtMulLowI8x16S,
De::I16x8ExtMulHighI8x16S => En::I16x8ExtMulHighI8x16S,
De::I16x8ExtMulLowI8x16U => En::I16x8ExtMulLowI8x16U,
De::I16x8ExtMulHighI8x16U => En::I16x8ExtMulHighI8x16U,
De::I32x4ExtMulLowI16x8S => En::I32x4ExtMulLowI16x8S,
De::I32x4ExtMulHighI16x8S => En::I32x4ExtMulHighI16x8S,
De::I32x4ExtMulLowI16x8U => En::I32x4ExtMulLowI16x8U,
De::I32x4ExtMulHighI16x8U => En::I32x4ExtMulHighI16x8U,
De::I64x2ExtMulLowI32x4S => En::I64x2ExtMulLowI32x4S,
De::I64x2ExtMulHighI32x4S => En::I64x2ExtMulHighI32x4S,
De::I64x2ExtMulLowI32x4U => En::I64x2ExtMulLowI32x4U,
De::I64x2ExtMulHighI32x4U => En::I64x2ExtMulHighI32x4U,
De::I16x8ExtAddPairwiseI8x16S => En::I16x8ExtAddPairwiseI8x16S,
De::I16x8ExtAddPairwiseI8x16U => En::I16x8ExtAddPairwiseI8x16U,
De::I32x4ExtAddPairwiseI16x8S => En::I32x4ExtAddPairwiseI16x8S,
De::I32x4ExtAddPairwiseI16x8U => En::I32x4ExtAddPairwiseI16x8U,
De::I8x16AddSatS => En::I8x16AddSatS,
De::I8x16AddSatU => En::I8x16AddSatU,
De::I16x8AddSatS => En::I16x8AddSatS,
De::I16x8AddSatU => En::I16x8AddSatU,
De::I8x16SubSatS => En::I8x16SubSatS,
De::I8x16SubSatU => En::I8x16SubSatU,
De::I16x8SubSatS => En::I16x8SubSatS,
De::I16x8SubSatU => En::I16x8SubSatU,
De::I16x8Q15MulrSatS => En::I16x8Q15MulrSatS,
De::I8x16MinS => En::I8x16MinS,
De::I8x16MinU => En::I8x16MinU,
De::I16x8MinS => En::I16x8MinS,
De::I16x8MinU => En::I16x8MinU,
De::I32x4MinS => En::I32x4MinS,
De::I32x4MinU => En::I32x4MinU,
De::F32x4Min => En::F32x4Min,
De::F64x2Min => En::F64x2Min,
De::F32x4PMin => En::F32x4PMin,
De::F64x2PMin => En::F64x2PMin,
De::I8x16MaxS => En::I8x16MaxS,
De::I8x16MaxU => En::I8x16MaxU,
De::I16x8MaxS => En::I16x8MaxS,
De::I16x8MaxU => En::I16x8MaxU,
De::I32x4MaxS => En::I32x4MaxS,
De::I32x4MaxU => En::I32x4MaxU,
De::F32x4Max => En::F32x4Max,
De::F64x2Max => En::F64x2Max,
De::F32x4PMax => En::F32x4PMax,
De::F64x2PMax => En::F64x2PMax,
De::I8x16AvgrU => En::I8x16AvgrU,
De::I16x8AvgrU => En::I16x8AvgrU,
De::I8x16Abs => En::I8x16Abs,
De::I16x8Abs => En::I16x8Abs,
De::I32x4Abs => En::I32x4Abs,
De::I64x2Abs => En::I64x2Abs,
De::F32x4Abs => En::F32x4Abs,
De::F64x2Abs => En::F64x2Abs,
De::I8x16Shl => En::I8x16Shl,
De::I16x8Shl => En::I16x8Shl,
De::I32x4Shl => En::I32x4Shl,
De::I64x2Shl => En::I64x2Shl,
De::I8x16ShrS => En::I8x16ShrS,
De::I8x16ShrU => En::I8x16ShrU,
De::I16x8ShrS => En::I16x8ShrS,
De::I16x8ShrU => En::I16x8ShrU,
De::I32x4ShrS => En::I32x4ShrS,
De::I32x4ShrU => En::I32x4ShrU,
De::I64x2ShrS => En::I64x2ShrS,
De::I64x2ShrU => En::I64x2ShrU,
De::V128And => En::V128And,
De::V128Or => En::V128Or,
De::V128Xor => En::V128Xor,
De::V128Not => En::V128Not,
De::V128AndNot => En::V128AndNot,
De::V128Bitselect => En::V128Bitselect,
De::I8x16Popcnt => En::I8x16Popcnt,
De::V128AnyTrue => En::V128AnyTrue,
De::I8x16AllTrue => En::I8x16AllTrue,
De::I16x8AllTrue => En::I16x8AllTrue,
De::I32x4AllTrue => En::I32x4AllTrue,
De::I64x2AllTrue => En::I64x2AllTrue,
De::I8x16Bitmask => En::I8x16Bitmask,
De::I16x8Bitmask => En::I16x8Bitmask,
De::I32x4Bitmask => En::I32x4Bitmask,
De::I64x2Bitmask => En::I64x2Bitmask,
De::I8x16Eq => En::I8x16Eq,
De::I16x8Eq => En::I16x8Eq,
De::I32x4Eq => En::I32x4Eq,
De::I64x2Eq => En::I64x2Eq,
De::F32x4Eq => En::F32x4Eq,
De::F64x2Eq => En::F64x2Eq,
De::I8x16Ne => En::I8x16Ne,
De::I16x8Ne => En::I16x8Ne,
De::I32x4Ne => En::I32x4Ne,
De::I64x2Ne => En::I64x2Ne,
De::F32x4Ne => En::F32x4Ne,
De::F64x2Ne => En::F64x2Ne,
De::I8x16LtS => En::I8x16LtS,
De::I8x16LtU => En::I8x16LtU,
De::I16x8LtS => En::I16x8LtS,
De::I16x8LtU => En::I16x8LtU,
De::I32x4LtS => En::I32x4LtS,
De::I32x4LtU => En::I32x4LtU,
De::F32x4Lt => En::F32x4Lt,
De::F64x2Lt => En::F64x2Lt,
De::I8x16LeS => En::I8x16LeS,
De::I8x16LeU => En::I8x16LeU,
De::I16x8LeS => En::I16x8LeS,
De::I16x8LeU => En::I16x8LeU,
De::I32x4LeS => En::I32x4LeS,
De::I32x4LeU => En::I32x4LeU,
De::F32x4Le => En::F32x4Le,
De::F64x2Le => En::F64x2Le,
De::I8x16GtS => En::I8x16GtS,
De::I8x16GtU => En::I8x16GtU,
De::I16x8GtS => En::I16x8GtS,
De::I16x8GtU => En::I16x8GtU,
De::I32x4GtS => En::I32x4GtS,
De::I32x4GtU => En::I32x4GtU,
De::F32x4Gt => En::F32x4Gt,
De::F64x2Gt => En::F64x2Gt,
De::I8x16GeS => En::I8x16GeS,
De::I8x16GeU => En::I8x16GeU,
De::I16x8GeS => En::I16x8GeS,
De::I16x8GeU => En::I16x8GeU,
De::I32x4GeS => En::I32x4GeS,
De::I32x4GeU => En::I32x4GeU,
De::F32x4Ge => En::F32x4Ge,
De::F64x2Ge => En::F64x2Ge,
De::F32x4Div => En::F32x4Div,
De::F64x2Div => En::F64x2Div,
De::F32x4Sqrt => En::F32x4Sqrt,
De::F64x2Sqrt => En::F64x2Sqrt,
De::F32x4Ceil => En::F32x4Ceil,
De::F64x2Ceil => En::F64x2Ceil,
De::F32x4Floor => En::F32x4Floor,
De::F64x2Floor => En::F64x2Floor,
De::F32x4Trunc => En::F32x4Trunc,
De::F64x2Trunc => En::F64x2Trunc,
De::F32x4Nearest => En::F32x4Nearest,
De::F64x2Nearest => En::F64x2Nearest,
De::F32x4ConvertI32x4S => En::F32x4ConvertI32x4S,
De::F32x4ConvertI32x4U => En::F32x4ConvertI32x4U,
De::F64x2ConvertLowI32x4S => En::F64x2ConvertLowI32x4S,
De::F64x2ConvertLowI32x4U => En::F64x2ConvertLowI32x4U,
De::I32x4TruncSatF32x4S => En::I32x4TruncSatF32x4S,
De::I32x4TruncSatF32x4U => En::I32x4TruncSatF32x4U,
De::I32x4TruncSatF64x2SZero => En::I32x4TruncSatF64x2SZero,
De::I32x4TruncSatF64x2UZero => En::I32x4TruncSatF64x2UZero,
De::F32x4DemoteF64x2Zero => En::F32x4DemoteF64x2Zero,
De::F64x2PromoteLowF32x4 => En::F64x2PromoteLowF32x4,
De::I8x16NarrowI16x8S => En::I8x16NarrowI16x8S,
De::I8x16NarrowI16x8U => En::I8x16NarrowI16x8U,
De::I16x8NarrowI32x4S => En::I16x8NarrowI32x4S,
De::I16x8NarrowI32x4U => En::I16x8NarrowI32x4U,
De::I16x8ExtendLowI8x16S => En::I16x8ExtendLowI8x16S,
De::I16x8ExtendHighI8x16S => En::I16x8ExtendHighI8x16S,
De::I16x8ExtendLowI8x16U => En::I16x8ExtendLowI8x16U,
De::I16x8ExtendHighI8x16U => En::I16x8ExtendHighI8x16U,
De::I32x4ExtendLowI16x8S => En::I32x4ExtendLowI16x8S,
De::I32x4ExtendHighI16x8S => En::I32x4ExtendHighI16x8S,
De::I32x4ExtendLowI16x8U => En::I32x4ExtendLowI16x8U,
De::I32x4ExtendHighI16x8U => En::I32x4ExtendHighI16x8U,
De::I64x2ExtendLowI32x4S => En::I64x2ExtendLowI32x4S,
De::I64x2ExtendHighI32x4S => En::I64x2ExtendHighI32x4S,
De::I64x2ExtendLowI32x4U => En::I64x2ExtendLowI32x4U,
De::I64x2ExtendHighI32x4U => En::I64x2ExtendHighI32x4U,
De::I8x16Shuffle { lanes } => En::I8x16Shuffle(lanes),
other => bail!("Unsupported instruction {:?}", other),
});
}

1923
uw8-window/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,13 +6,13 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
winit = "0.26.1"
env_logger = "0.9"
winit = "0.29.15"
env_logger = "0.11.3"
log = "0.4"
pico-args = "0.4"
wgpu = "0.13.1"
pollster = "0.2"
bytemuck = { version = "1.4", features = [ "derive" ] }
pico-args = "0.5"
wgpu = "0.19.3"
pollster = "0.3.0"
bytemuck = { version = "1.15", features = [ "derive" ] }
anyhow = "1"
minifb = { version = "0.23.0", default-features = false, features = ["x11"] }
winapi = "0.3.9"
minifb = { version = "0.25.0", default-features = false, features = ["x11"] }
winapi = { version = "0.3.9", features = [ "timeapi" ] }

View File

@@ -36,36 +36,36 @@ fn sample_pixel(coords: vec2<i32>, offset: vec4<f32>) -> vec3<f32> {
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
let pixel = floor(in.tex_coords);
let o = vec2<f32>(0.5) - (in.tex_coords - pixel);
let pixel = vec2<i32>(pixel);
let pixelf = floor(in.tex_coords);
let o = vec2<f32>(0.5) - (in.tex_coords - pixelf);
let pixel = vec2<i32>(pixelf);
let offset_x = o.xxxx + vec4<f32>(-0.125, 0.375, 0.125, -0.375) * uniforms.texture_scale.z;
let offset_y = o.yyyy + vec4<f32>(-0.375, -0.125, 0.375, 0.125) * uniforms.texture_scale.z;
let offset_x0 = max(abs(offset_x + vec4<f32>(-1.0)) - vec4<f32>(0.5), vec4<f32>(0.0));
let offset_x1 = max(abs(offset_x) - vec4<f32>(0.5), vec4<f32>(0.0));
let offset_x2 = max(abs(offset_x + vec4<f32>(1.0)) - vec4<f32>(0.5), vec4<f32>(0.0));
var offset_x0 = max(abs(offset_x + vec4<f32>(-1.0)) - vec4<f32>(0.5), vec4<f32>(0.0));
var offset_x1 = max(abs(offset_x) - vec4<f32>(0.5), vec4<f32>(0.0));
var offset_x2 = max(abs(offset_x + vec4<f32>(1.0)) - vec4<f32>(0.5), vec4<f32>(0.0));
let offset_x0 = offset_x0 * offset_x0;
let offset_x1 = offset_x1 * offset_x1;
let offset_x2 = offset_x2 * offset_x2;
offset_x0 = offset_x0 * offset_x0;
offset_x1 = offset_x1 * offset_x1;
offset_x2 = offset_x2 * offset_x2;
let offset_yr = offset_y + vec4<f32>(-1.0);
let offset_yr = vec4<f32>(0.02) + offset_yr * offset_yr;
var offset_yr = offset_y + vec4<f32>(-1.0);
offset_yr = vec4<f32>(0.02) + offset_yr * offset_yr;
var acc = sample_pixel(pixel + vec2<i32>(-1, -1), offset_x0 + offset_yr);
acc = acc + sample_pixel(pixel + vec2<i32>(0, -1), offset_x1 + offset_yr);
acc = acc + sample_pixel(pixel + vec2<i32>(1, -1), offset_x2 + offset_yr);
let offset_yr = vec4<f32>(0.02) + offset_y * offset_y;
offset_yr = vec4<f32>(0.02) + offset_y * offset_y;
acc = acc + sample_pixel(pixel + vec2<i32>(-1, 0), offset_x0 + offset_yr);
acc = acc + sample_pixel(pixel, offset_x1 + offset_yr);
acc = acc + sample_pixel(pixel + vec2<i32>(1, 0), offset_x2 + offset_yr);
let offset_yr = offset_y + vec4<f32>(1.0);
let offset_yr = vec4<f32>(0.02) + offset_yr * offset_yr;
offset_yr = offset_y + vec4<f32>(1.0);
offset_yr = vec4<f32>(0.02) + offset_yr * offset_yr;
acc = acc + sample_pixel(pixel + vec2<i32>(-1, 1), offset_x0 + offset_yr);
acc = acc + sample_pixel(pixel + vec2<i32>(0, 1), offset_x1 + offset_yr);

View File

@@ -30,8 +30,8 @@ fn row_factor(offset: f32) -> f32 {
}
fn col_factor(offset: f32) -> f32 {
let offset = max(0.0, abs(offset) - 0.4);
return 1.0 / (1.0 + offset * offset * 16.0);
let o = max(0.0, abs(offset) - 0.4);
return 1.0 / (1.0 + o * o * 16.0);
}
fn sample_screen(tex_coords: vec2<f32>) -> vec4<f32> {

View File

@@ -1,16 +1,16 @@
use crate::{Input, WindowConfig, WindowImpl};
use anyhow::{anyhow, Result};
use std::{num::NonZeroU32, time::Instant};
use std::{sync::Arc, time::Instant};
use winit::{
dpi::PhysicalSize,
event::{Event, VirtualKeyCode, WindowEvent},
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoop},
keyboard::{Key, KeyCode, NamedKey, PhysicalKey},
platform::pump_events::{EventLoopExtPumpEvents, PumpStatus},
window::{Fullscreen, WindowBuilder},
};
use winit::platform::run_return::EventLoopExtRunReturn;
mod crt;
mod fast_crt;
mod square;
@@ -21,7 +21,7 @@ use square::SquareFilter;
pub struct Window {
_instance: wgpu::Instance,
surface: wgpu::Surface,
surface: wgpu::Surface<'static>,
_adapter: wgpu::Adapter,
device: wgpu::Device,
queue: wgpu::Queue,
@@ -29,7 +29,7 @@ pub struct Window {
surface_config: wgpu::SurfaceConfiguration,
filter: Box<dyn Filter>,
event_loop: EventLoop<()>,
window: winit::window::Window,
window: Arc<winit::window::Window>,
gamepads: [u8; 4],
next_frame: Instant,
is_fullscreen: bool,
@@ -39,9 +39,12 @@ pub struct Window {
impl Window {
pub fn new(window_config: WindowConfig) -> Result<Window> {
async fn create(window_config: WindowConfig) -> Result<Window> {
let event_loop = EventLoop::new();
let event_loop = EventLoop::new()?;
let window = WindowBuilder::new()
.with_inner_size(PhysicalSize::new(640u32, 480))
.with_inner_size(PhysicalSize::new(
(320. * window_config.scale).round() as u32,
(240. * window_config.scale).round() as u32,
))
.with_min_inner_size(PhysicalSize::new(320u32, 240))
.with_title("MicroW8")
.with_fullscreen(if window_config.fullscreen {
@@ -53,8 +56,10 @@ impl Window {
window.set_cursor_visible(false);
let instance = wgpu::Instance::new(wgpu::Backends::all());
let surface = unsafe { instance.create_surface(&window) };
let window = Arc::new(window);
let instance = wgpu::Instance::new(Default::default());
let surface = instance.create_surface(window.clone())?;
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::LowPower,
@@ -71,11 +76,14 @@ impl Window {
let palette_screen_mode = PaletteScreenMode::new(&device);
let surface_config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: surface.get_supported_formats(&adapter)[0],
width: window.inner_size().width,
height: window.inner_size().height,
present_mode: wgpu::PresentMode::AutoNoVsync,
..surface
.get_default_config(
&adapter,
window.inner_size().width,
window.inner_size().height,
)
.expect("Surface incompatible with adapter")
};
let filter: Box<dyn Filter> = create_filter(
@@ -113,88 +121,93 @@ impl Window {
impl WindowImpl for Window {
fn begin_frame(&mut self) -> Input {
let mut reset = false;
self.event_loop.run_return(|event, _, control_flow| {
*control_flow = ControlFlow::WaitUntil(self.next_frame);
let mut new_filter = None;
match event {
Event::WindowEvent { event, .. } => match event {
WindowEvent::Resized(new_size) => {
self.surface_config.width = new_size.width;
self.surface_config.height = new_size.height;
self.surface.configure(&self.device, &self.surface_config);
self.filter.resize(&self.queue, new_size);
}
WindowEvent::CloseRequested => {
self.is_open = false;
*control_flow = ControlFlow::Exit;
}
WindowEvent::KeyboardInput { input, .. } => {
fn gamepad_button(input: &winit::event::KeyboardInput) -> u8 {
match input.scancode {
44 => 16,
45 => 32,
30 => 64,
31 => 128,
_ => match input.virtual_keycode {
Some(VirtualKeyCode::Up) => 1,
Some(VirtualKeyCode::Down) => 2,
Some(VirtualKeyCode::Left) => 4,
Some(VirtualKeyCode::Right) => 8,
_ => 0,
},
}
self.event_loop
.set_control_flow(ControlFlow::WaitUntil(self.next_frame));
while self.is_open {
let timeout = self.next_frame.saturating_duration_since(Instant::now());
let status = self.event_loop.pump_events(Some(timeout), |event, elwt| {
let mut new_filter = None;
match event {
Event::WindowEvent { event, .. } => match event {
WindowEvent::Resized(new_size) => {
self.surface_config.width = new_size.width;
self.surface_config.height = new_size.height;
self.surface.configure(&self.device, &self.surface_config);
self.filter.resize(&self.queue, new_size);
}
if input.state == winit::event::ElementState::Pressed {
match input.virtual_keycode {
Some(VirtualKeyCode::Escape) => {
self.is_open = false;
*control_flow = ControlFlow::Exit;
WindowEvent::CloseRequested => {
elwt.exit();
}
WindowEvent::KeyboardInput { event, .. } => {
fn gamepad_button(input: &winit::event::KeyEvent) -> u8 {
match input.physical_key {
PhysicalKey::Code(KeyCode::KeyZ) => 16,
PhysicalKey::Code(KeyCode::KeyX) => 32,
PhysicalKey::Code(KeyCode::KeyA) => 64,
PhysicalKey::Code(KeyCode::KeyS) => 128,
_ => match input.logical_key {
Key::Named(NamedKey::ArrowUp) => 1,
Key::Named(NamedKey::ArrowDown) => 2,
Key::Named(NamedKey::ArrowLeft) => 4,
Key::Named(NamedKey::ArrowRight) => 8,
_ => 0,
},
}
Some(VirtualKeyCode::F) => {
let fullscreen = if self.window.fullscreen().is_some() {
None
} else {
Some(Fullscreen::Borderless(None))
};
self.is_fullscreen = fullscreen.is_some();
self.window.set_fullscreen(fullscreen);
}
Some(VirtualKeyCode::R) => reset = true,
Some(VirtualKeyCode::Key1) => new_filter = Some(1),
Some(VirtualKeyCode::Key2) => new_filter = Some(2),
Some(VirtualKeyCode::Key3) => new_filter = Some(3),
Some(VirtualKeyCode::Key4) => new_filter = Some(4),
Some(VirtualKeyCode::Key5) => new_filter = Some(5),
_ => (),
}
if event.state == winit::event::ElementState::Pressed {
match event.logical_key {
Key::Named(NamedKey::Escape) => {
elwt.exit();
}
Key::Character(ref c) => match c.as_str() {
"f" => {
let fullscreen = if self.window.fullscreen().is_some() {
None
} else {
Some(Fullscreen::Borderless(None))
};
self.is_fullscreen = fullscreen.is_some();
self.window.set_fullscreen(fullscreen);
}
"r" => reset = true,
"1" => new_filter = Some(1),
"2" => new_filter = Some(2),
"3" => new_filter = Some(3),
"4" => new_filter = Some(4),
"5" => new_filter = Some(5),
_ => (),
},
_ => (),
}
self.gamepads[0] |= gamepad_button(&input);
} else {
self.gamepads[0] &= !gamepad_button(&input);
self.gamepads[0] |= gamepad_button(&event);
} else {
self.gamepads[0] &= !gamepad_button(&event);
}
}
}
_ => (),
},
_ => (),
},
Event::RedrawEventsCleared => {
if Instant::now() >= self.next_frame
// workaround needed on Wayland until the next winit release
&& self.window.fullscreen().is_some() == self.is_fullscreen
{
*control_flow = ControlFlow::Exit
}
}
if let Some(new_filter) = new_filter {
self.filter = create_filter(
&self.device,
&self.palette_screen_mode.screen_view,
self.window.inner_size(),
self.surface_config.format,
new_filter,
);
}
});
match status {
PumpStatus::Exit(_) => self.is_open = false,
_ => (),
}
if let Some(new_filter) = new_filter {
self.filter = create_filter(
&self.device,
&self.palette_screen_mode.screen_view,
self.window.inner_size(),
self.surface_config.format,
new_filter,
);
if Instant::now() >= self.next_frame {
break;
}
});
}
Input {
gamepads: self.gamepads,
reset,
@@ -230,10 +243,12 @@ impl WindowImpl for Window {
b: 0.0,
a: 1.0,
}),
store: true,
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
self.filter.render(&mut render_pass);
@@ -357,6 +372,7 @@ impl PaletteScreenMode {
format: wgpu::TextureFormat::R8Uint,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
label: None,
view_formats: &[],
});
let palette_texture = device.create_texture(&wgpu::TextureDescriptor {
@@ -371,6 +387,7 @@ impl PaletteScreenMode {
format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
label: None,
view_formats: &[],
});
let screen_texture = device.create_texture(&wgpu::TextureDescriptor {
@@ -385,6 +402,7 @@ impl PaletteScreenMode {
format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT,
label: None,
view_formats: &[],
});
let framebuffer_texture_view =
@@ -491,7 +509,7 @@ impl PaletteScreenMode {
&bytemuck::cast_slice(pixels),
wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: NonZeroU32::new(320),
bytes_per_row: Some(320),
rows_per_image: None,
},
wgpu::Extent3d {
@@ -513,7 +531,7 @@ impl PaletteScreenMode {
&bytemuck::cast_slice(palette),
wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: NonZeroU32::new(256 * 4),
bytes_per_row: Some(256 * 4),
rows_per_image: None,
},
wgpu::Extent3d {
@@ -532,10 +550,12 @@ impl PaletteScreenMode {
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: true,
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
render_pass.set_pipeline(&self.pipeline);

View File

@@ -4,31 +4,65 @@ use std::time::Instant;
mod cpu;
mod gpu;
pub struct Window(Box<dyn WindowImpl>);
pub struct Window {
inner: Box<dyn WindowImpl>,
fps_counter: Option<FpsCounter>,
}
struct FpsCounter {
start: Instant,
num_frames: u32,
}
impl Window {
pub fn new(config: WindowConfig) -> Result<Window> {
pub fn new(mut config: WindowConfig) -> Result<Window> {
let fps_counter = if config.fps_counter {
Some(FpsCounter {
start: Instant::now(),
num_frames: 0,
})
} else {
None
};
config.scale = config.scale.max(1.).min(20.);
if config.enable_gpu {
match gpu::Window::new(config) {
Ok(window) => return Ok(Window(Box::new(window))),
Ok(window) => {
return Ok(Window {
inner: Box::new(window),
fps_counter,
})
}
Err(err) => eprintln!(
"Failed to create gpu window: {}\nFalling back tp cpu window",
err
),
}
}
cpu::Window::new().map(|window| Window(Box::new(window)))
cpu::Window::new().map(|window| Window {
inner: Box::new(window),
fps_counter,
})
}
pub fn begin_frame(&mut self) -> Input {
self.0.begin_frame()
self.inner.begin_frame()
}
pub fn end_frame(&mut self, framebuffer: &[u8], palette: &[u8], next_frame: Instant) {
self.0.end_frame(framebuffer, palette, next_frame)
self.inner.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 {
self.0.is_open()
self.inner.is_open()
}
}
@@ -37,6 +71,8 @@ pub struct WindowConfig {
enable_gpu: bool,
filter: u32,
fullscreen: bool,
fps_counter: bool,
scale: f32,
}
impl Default for WindowConfig {
@@ -45,6 +81,8 @@ impl Default for WindowConfig {
enable_gpu: true,
filter: 5,
fullscreen: false,
fps_counter: false,
scale: 2.,
}
}
}
@@ -66,6 +104,11 @@ impl WindowConfig {
}
}
self.fullscreen = args.contains("--fullscreen");
self.fps_counter = args.contains("--fps");
self.scale = args
.opt_value_from_str("--scale")
.unwrap()
.unwrap_or(self.scale);
}
}

View File

@@ -4,5 +4,6 @@
"@parcel/optimizer-data-url": "^2.0.0",
"@parcel/transformer-inline-string": "^2.0.0",
"parcel": "^2.0.0"
}
},
"dependencies": {}
}

View File

@@ -63,7 +63,7 @@ class APU extends AudioWorkletProcessor {
this.memory = memory;
this.snd = instance.exports.snd || platform_instance.exports.gesSnd;
this.snd = instance.exports.snd || platform_instance.exports.sndGes;
this.port.postMessage(2);
}

View File

@@ -10,7 +10,7 @@
</head>
<body>
<div id="uw8">
<a href="https://exoticorn.github.io/microw8">MicroW8</a> 0.2.1
<a href="https://exoticorn.github.io/microw8">MicroW8</a> 0.4.0
</div>
<div id="centered">
<canvas class="screen" id="screen" width="320" height="240">

View File

@@ -240,6 +240,7 @@ export default function MicroW8(screen, config = {}) {
await audioReadyPromise;
let startTime = Date.now();
let frameCounter = 0;
const timePerFrame = 1000 / 60;
@@ -263,6 +264,10 @@ export default function MicroW8(screen, config = {}) {
window.addEventListener('blur', () => updateVisibility(false), { signal: abortController.signal });
updateVisibility(document.hasFocus());
if (instance.exports.start) {
instance.exports.start();
}
function mainloop() {
if (!keepRunning) {
return;
@@ -302,6 +307,7 @@ export default function MicroW8(screen, config = {}) {
let time = Date.now() - startTime;
u32Mem[16] = time;
u32Mem[17] = pad | gamepad;
u32Mem[18] = frameCounter++;
if(instance.exports.upd) {
instance.exports.upd();
}

File diff suppressed because it is too large Load Diff