4 Commits

28 changed files with 2618 additions and 4003 deletions

View File

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

View File

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

3720
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -15,9 +15,9 @@ See [here](https://exoticorn.github.io/microw8/) for more information and docs.
## Downloads ## Downloads
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.2.2/microw8-0.2.2-linux.tgz) * [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.2.2/microw8-0.2.2-macos.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.2.2/microw8-0.2.2-windows.zip) * [Windows](https://github.com/exoticorn/microw8/releases/download/v0.1.2/microw8-0.1.2-windows.zip)
The download includes The download includes

180
platform/Cargo.lock generated
View File

@@ -110,9 +110,9 @@ dependencies = [
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.3.2" version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
] ]
@@ -151,39 +151,32 @@ dependencies = [
"anyhow", "anyhow",
"ariadne", "ariadne",
"chumsky", "chumsky",
"pico-args 0.4.2", "pico-args",
"wasm-encoder 0.10.0", "wasm-encoder 0.10.0",
"wasmparser 0.83.0", "wasmparser 0.83.0",
] ]
[[package]] [[package]]
name = "fallible_collections" name = "fallible_collections"
version = "0.4.6" version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f57ccc32870366ae684be48b32a1a2e196f98a42a9b4361fe77e13fd4a34755" checksum = "52db5973b6a19247baf19b30f41c23a1bfffc2e9ce0a5db2f60e3cd5dc8895f7"
dependencies = [ dependencies = [
"hashbrown", "hashbrown",
] ]
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.0.25" version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
dependencies = [ dependencies = [
"cfg-if",
"crc32fast", "crc32fast",
"libc",
"miniz_oxide", "miniz_oxide",
] ]
[[package]]
name = "form_urlencoded"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
dependencies = [
"percent-encoding",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.3" version = "0.2.3"
@@ -197,9 +190,9 @@ dependencies = [
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.12.3" version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
dependencies = [ dependencies = [
"ahash 0.7.6", "ahash 0.7.6",
] ]
@@ -219,26 +212,6 @@ version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
[[package]]
name = "idna"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
dependencies = [
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "indexmap"
version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@@ -251,25 +224,18 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "lexopt"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478ee9e62aaeaf5b140bd4138753d1f109765488581444218d3ddda43234f3e8"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.139" version = "0.2.112"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
[[package]] [[package]]
name = "lodepng" name = "lodepng"
version = "3.7.2" version = "3.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0ad39f75bbaa4b10bb6f2316543632a8046a5bcf9c785488d79720b21f044f8" checksum = "24844d5c0b922ddd52fb5bf0964a4c7f8e799a946ec01bb463771eb04fc1a323"
dependencies = [ dependencies = [
"crc32fast",
"fallible_collections", "fallible_collections",
"flate2", "flate2",
"libc", "libc",
@@ -287,11 +253,12 @@ dependencies = [
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.6.2" version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
dependencies = [ dependencies = [
"adler", "adler",
"autocfg",
] ]
[[package]] [[package]]
@@ -321,24 +288,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "percent-encoding"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
[[package]] [[package]]
name = "pico-args" name = "pico-args"
version = "0.4.2" version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468"
[[package]]
name = "pico-args"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
[[package]] [[package]]
name = "platform" name = "platform"
version = "0.1.0" version = "0.1.0"
@@ -375,9 +330,9 @@ dependencies = [
[[package]] [[package]]
name = "rgb" name = "rgb"
version = "0.8.34" version = "0.8.31"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3603b7d71ca82644f79b5a06d1220e9a58ede60bd32255f698cb1af8838b8db3" checksum = "9a374af9a0e5fdcdd98c1c7b64f05004f9ea2555b6c75f211daa81268a3c50f1"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
] ]
@@ -402,26 +357,6 @@ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]]
name = "thiserror"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "time" name = "time"
version = "0.1.43" version = "0.1.43"
@@ -441,36 +376,6 @@ dependencies = [
"crunchy", "crunchy",
] ]
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "unicode-bidi"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
[[package]]
name = "unicode-normalization"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"tinyvec",
]
[[package]] [[package]]
name = "unicode-segmentation" name = "unicode-segmentation"
version = "1.9.0" version = "1.9.0"
@@ -485,24 +390,13 @@ checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]] [[package]]
name = "upkr" name = "upkr"
version = "0.2.1" version = "0.1.0"
source = "git+https://github.com/exoticorn/upkr.git?rev=080db40d0088bbee2bdf3c5c75288ac7853d6b7a#080db40d0088bbee2bdf3c5c75288ac7853d6b7a" source = "git+https://github.com/exoticorn/upkr.git?rev=d93aec186c9fb91d962c488682a2db125c61306c#d93aec186c9fb91d962c488682a2db125c61306c"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cdivsufsort", "cdivsufsort",
"lexopt", "pbr",
"thiserror", "pico-args",
]
[[package]]
name = "url"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
] ]
[[package]] [[package]]
@@ -511,11 +405,11 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"pbr", "pbr",
"pico-args 0.5.0", "pico-args",
"upkr", "upkr",
"walrus", "walrus",
"wasm-encoder 0.22.0", "wasm-encoder 0.8.0",
"wasmparser 0.99.0", "wasmparser 0.81.0",
] ]
[[package]] [[package]]
@@ -558,18 +452,18 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]] [[package]]
name = "wasm-encoder" name = "wasm-encoder"
version = "0.10.0" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa9d9bf45fc46f71c407837c9b30b1e874197f2dc357588430b21e5017d290ab" checksum = "db0c351632e46cc06a58a696a6c11e4cf90cad4b9f8f07a0b59128d616c29bb0"
dependencies = [ dependencies = [
"leb128", "leb128",
] ]
[[package]] [[package]]
name = "wasm-encoder" name = "wasm-encoder"
version = "0.22.0" version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef126be0e14bdf355ac1a8b41afc89195289e5c7179f80118e3abddb472f0810" checksum = "aa9d9bf45fc46f71c407837c9b30b1e874197f2dc357588430b21e5017d290ab"
dependencies = [ dependencies = [
"leb128", "leb128",
] ]
@@ -582,19 +476,15 @@ checksum = "b35c86d22e720a07d954ebbed772d01180501afe7d03d464f413bb5f8914a8d6"
[[package]] [[package]]
name = "wasmparser" name = "wasmparser"
version = "0.83.0" version = "0.81.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a" checksum = "98930446519f63d00a836efdc22f67766ceae8dbcc1571379f2bcabc6b2b9abc"
[[package]] [[package]]
name = "wasmparser" name = "wasmparser"
version = "0.99.0" version = "0.83.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ef3b717afc67f848f412d4f02c127dd3e35a0eecd58c684580414df4fde01d3" checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a"
dependencies = [
"indexmap",
"url",
]
[[package]] [[package]]
name = "winapi" name = "winapi"

View File

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

View File

@@ -14,8 +14,6 @@ The initial motivation behind MicroW8 was to explore whether there was a way to
* Memory: 256KB * Memory: 256KB
* Gamepad input (D-Pad + 4 Buttons) * Gamepad input (D-Pad + 4 Buttons)
For detailed [documentation see here](docs).
## Examples ## 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 * [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. * [Fireworks](v0.2.0#AgwvgP+M59snqjl4CMKw5sqm1Zw9yJCbSviMjeLUdHus2a3yl/a99+uiBeqZgP/2jqSjrLjRk73COMM6OSLpsxK8ugT1kuk/q4hQUqqPpGozHoa0laulzGGcahzdfdJsYaK1sIdeIYS9M5PnJx/Wk9H+PvWEPy2Zvv7I6IW7Fg==) (127 bytes): Some fireworks to welcome 2022.
@@ -31,20 +29,6 @@ Examplers for older versions:
## Versions ## Versions
### 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 ### v0.2.1
* [Web runtime](v0.2.1) * [Web runtime](v0.2.1)

View File

@@ -5,16 +5,13 @@ description = "Docs"
# Overview # Overview
MicroW8 loads WebAssembly modules with a maximum size of 256kb. Your module needs to export MicroW8 loads WebAssembly modules with a maximum size of 256kb. You module needs to export
a function `fn upd()` which will be called once per frame. a function `fn upd()` which will be called once per frame.
After calling `upd` MicroW8 will display the 320x240 8bpp framebuffer located After calling `upd` MicroW8 will display the 320x240 8bpp framebuffer located
at offset 120 in memory with the 32bpp palette located at 0x13000. 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). 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 # Memory map
``` ```

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

@@ -7,7 +7,7 @@ use cpal::traits::*;
use rubato::Resampler; use rubato::Resampler;
use uw8_window::{Window, WindowConfig}; use uw8_window::{Window, WindowConfig};
use wasmtime::{ use wasmtime::{
Engine, Func, GlobalType, Memory, MemoryType, Module, Mutability, Store, TypedFunc, ValType, Engine, GlobalType, Memory, MemoryType, Module, Mutability, Store, TypedFunc, ValType,
}; };
pub struct MicroW8 { pub struct MicroW8 {
@@ -90,10 +90,10 @@ impl super::Runtime for MicroW8 {
let memory = wasmtime::Memory::new(&mut store, MemoryType::new(4, Some(4)))?; let memory = wasmtime::Memory::new(&mut store, MemoryType::new(4, Some(4)))?;
let mut linker = wasmtime::Linker::new(&self.engine); let mut linker = wasmtime::Linker::new(&self.engine);
linker.define(&store, "env", "memory", memory)?; linker.define("env", "memory", memory)?;
let loader_instance = linker.instantiate(&mut store, &self.loader_module)?; 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"); let platform_data = include_bytes!("../platform/bin/platform.uw8");
memory.data_mut(&mut store)[..platform_data.len()].copy_from_slice(platform_data); memory.data_mut(&mut store)[..platform_data.len()].copy_from_slice(platform_data);
@@ -131,10 +131,10 @@ impl super::Runtime for MicroW8 {
} }
let instance = linker.instantiate(&mut store, &module)?; let instance = linker.instantiate(&mut store, &module)?;
let end_frame = platform_instance.get_typed_func::<(), ()>(&mut store, "endFrame")?; let end_frame = platform_instance.get_typed_func::<(), (), _>(&mut store, "endFrame")?;
let update = instance.get_typed_func::<(), ()>(&mut store, "upd").ok(); let update = instance.get_typed_func::<(), (), _>(&mut store, "upd").ok();
if let Some(start) = instance.get_typed_func::<(), ()>(&mut store, "start").ok() { if let Some(start) = instance.get_typed_func::<(), (), _>(&mut store, "start").ok() {
start.call(&mut store, ())?; start.call(&mut store, ())?;
} }
@@ -255,12 +255,15 @@ fn add_native_functions(
} }
})?; })?;
for i in 0..16 { for i in 0..16 {
let global = wasmtime::Global::new( linker.define(
&mut *store, "env",
GlobalType::new(ValType::I32, Mutability::Const), &format!("g_reserved{}", i),
0.into(), wasmtime::Global::new(
&mut *store,
GlobalType::new(ValType::I32, Mutability::Const),
0.into(),
)?,
)?; )?;
linker.define(&store, "env", &format!("g_reserved{}", i), global)?;
} }
Ok(()) Ok(())
@@ -273,18 +276,14 @@ fn instantiate_platform(
) -> Result<wasmtime::Instance> { ) -> Result<wasmtime::Instance> {
let platform_instance = linker.instantiate(&mut *store, &platform_module)?; let platform_instance = linker.instantiate(&mut *store, &platform_module)?;
let exports: Vec<(String, Func)> = platform_instance for export in platform_instance.exports(&mut *store) {
.exports(&mut *store) linker.define(
.map(|e| { "env",
( export.name(),
e.name().to_owned(), export
e.into_func() .into_func()
.expect("platform surely only exports functions"), .expect("platform surely only exports functions"),
) )?;
})
.collect();
for (name, func) in exports {
linker.define(&store, "env", &name, func)?;
} }
Ok(platform_instance) Ok(platform_instance)
@@ -311,15 +310,15 @@ fn init_sound(
let memory = wasmtime::Memory::new(&mut store, MemoryType::new(4, Some(4)))?; let memory = wasmtime::Memory::new(&mut store, MemoryType::new(4, Some(4)))?;
let mut linker = wasmtime::Linker::new(engine); let mut linker = wasmtime::Linker::new(engine);
linker.define(&store, "env", "memory", memory)?; linker.define("env", "memory", memory)?;
add_native_functions(&mut linker, &mut store)?; add_native_functions(&mut linker, &mut store)?;
let platform_instance = instantiate_platform(&mut linker, &mut store, platform_module)?; let platform_instance = instantiate_platform(&mut linker, &mut store, platform_module)?;
let instance = linker.instantiate(&mut store, module)?; let instance = linker.instantiate(&mut store, module)?;
let snd = instance let snd = instance
.get_typed_func::<(i32,), f32>(&mut store, "snd") .get_typed_func::<(i32,), f32, _>(&mut store, "snd")
.or_else(|_| platform_instance.get_typed_func::<(i32,), f32>(&mut store, "sndGes"))?; .or_else(|_| platform_instance.get_typed_func::<(i32,), f32, _>(&mut store, "sndGes"))?;
let host = cpal::default_host(); let host = cpal::default_host();
let device = host let device = host
@@ -328,26 +327,23 @@ fn init_sound(
let mut configs: Vec<_> = device let mut configs: Vec<_> = device
.supported_output_configs()? .supported_output_configs()?
.filter(|config| { .filter(|config| {
config.channels() == 2 config.channels() == 2 && config.sample_format() == cpal::SampleFormat::F32
&& (config.sample_format() == cpal::SampleFormat::F32
|| config.sample_format() == cpal::SampleFormat::I16)
}) })
.collect(); .collect();
configs.sort_by_key(|config| { configs.sort_by_key(|config| {
let rate = 44100 let rate = 44100
.max(config.min_sample_rate().0) .max(config.min_sample_rate().0)
.min(config.max_sample_rate().0); .min(config.max_sample_rate().0);
let prio = if rate >= 44100 { if rate >= 44100 {
rate - 44100 rate - 44100
} else { } else {
(44100 - rate) * 1000 (44100 - rate) * 1000
}; }
prio + (config.sample_format() == cpal::SampleFormat::I16) as u32
}); });
let config = configs let config = configs
.into_iter() .into_iter()
.next() .next()
.ok_or_else(|| anyhow!("Could not find float or 16bit signed output config"))?; .ok_or_else(|| anyhow!("Could not find float output config"))?;
let sample_rate = cpal::SampleRate(44100) let sample_rate = cpal::SampleRate(44100)
.max(config.min_sample_rate()) .max(config.min_sample_rate())
.min(config.max_sample_rate()); .min(config.max_sample_rate());
@@ -358,7 +354,6 @@ fn init_sound(
cpal::BufferSize::Fixed(256.max(min).min(max)) cpal::BufferSize::Fixed(256.max(min).min(max))
} }
}; };
let sample_format = config.sample_format();
let config = cpal::StreamConfig { let config = cpal::StreamConfig {
buffer_size, buffer_size,
..config.config() ..config.config()
@@ -378,8 +373,8 @@ fn init_sound(
None None
} else { } else {
let rs = rubato::FftFixedIn::new(44100, sample_rate, 128, 1, 2)?; let rs = rubato::FftFixedIn::new(44100, sample_rate, 128, 1, 2)?;
let input_buffers = rs.input_buffer_allocate(true); let input_buffers = rs.input_buffer_allocate();
let output_buffers = rs.output_buffer_allocate(true); let output_buffers = rs.output_buffer_allocate();
Some(Resampler { Some(Resampler {
resampler: rs, resampler: rs,
input_buffers, input_buffers,
@@ -391,130 +386,106 @@ fn init_sound(
let mut sample_index = 0; let mut sample_index = 0;
let mut pending_updates: Vec<RegisterUpdate> = Vec::with_capacity(30); let mut pending_updates: Vec<RegisterUpdate> = Vec::with_capacity(30);
let mut current_time = 0; let mut current_time = 0;
let stream = device.build_output_stream(
let mut callback = move |mut outer_buffer: &mut [f32], _: &_| { &config,
let mut first_update = true; move |mut outer_buffer: &mut [f32], _| {
while let Ok(update) = rx.try_recv() { let mut first_update = true;
if first_update { while let Ok(update) = rx.try_recv() {
current_time += update.time.wrapping_sub(current_time) / 8; if first_update {
first_update = false; current_time += update.time.wrapping_sub(current_time) / 8;
} first_update = false;
pending_updates.push(update); }
} 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();
} }
let duration = if let Some(update) = pending_updates.first() { while !outer_buffer.is_empty() {
((update.time.wrapping_sub(current_time) as usize) * sample_rate + 999) / 1000 store.set_epoch_deadline(30);
} else { while pending_updates
outer_buffer.len() .first()
}; .into_iter()
let step_size = (duration.max(64) * 2).min(outer_buffer.len()); .any(|u| u.time.wrapping_sub(current_time) <= 0)
{
let update = pending_updates.remove(0);
memory.write(&mut store, 80, &update.data).unwrap();
}
let mut buffer = &mut outer_buffer[..step_size]; let duration = if let Some(update) = pending_updates.first() {
((update.time.wrapping_sub(current_time) as usize) * sample_rate + 999) / 1000
{
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 { } else {
s.max(-1.0).min(1.0) 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());
} }
}
if let Some(ref mut resampler) = resampler { fn clamp_sample(s: f32) -> f32 {
while !buffer.is_empty() { if s.is_nan() {
let copy_size = resampler.output_buffers[0] 0.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);
}
resampler
.resampler
.process_into_buffer(
&resampler.input_buffers,
&mut resampler.output_buffers,
None,
)
.unwrap();
resampler.output_index = 0;
} else { } else {
for i in 0..copy_size { s.max(-1.0).min(1.0)
buffer[i * 2] = resampler.output_buffers[0][resampler.output_index + i]; }
buffer[i * 2 + 1] = }
resampler.output_buffers[1][resampler.output_index + i];
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);
}
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..];
} }
resampler.output_index += copy_size; }
buffer = &mut buffer[copy_size * 2..]; } 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);
} }
} }
} else {
for v in buffer { outer_buffer = &mut outer_buffer[step_size..];
*v = clamp_sample(snd.call(&mut store, (sample_index,)).unwrap_or(0.0)); current_time =
sample_index = sample_index.wrapping_add(1); current_time.wrapping_add((step_size * 500 / sample_rate).max(1) as i32);
}
} }
},
outer_buffer = &mut outer_buffer[step_size..]; move |err| {
current_time = current_time.wrapping_add((step_size * 500 / sample_rate).max(1) as i32); dbg!(err);
} },
}; )?;
let stream = if sample_format == cpal::SampleFormat::F32 {
device.build_output_stream(
&config,
callback,
move |err| {
dbg!(err);
},
None,
)?
} else {
device.build_output_stream(
&config,
move |mut buffer: &mut [i16], info| {
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, info);
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..];
}
},
move |err| {
dbg!(err);
},
None,
)?
};
Ok(Uw8Sound { stream, tx }) Ok(Uw8Sound { stream, tx })
} }

13
test.cwa Normal file
View File

@@ -0,0 +1,13 @@
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 Normal file

Binary file not shown.

View File

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

134
uw8-tool/Cargo.lock generated
View File

@@ -56,21 +56,6 @@ dependencies = [
"lazy_static", "lazy_static",
] ]
[[package]]
name = "form_urlencoded"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
dependencies = [
"percent-encoding",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.3.3" version = "0.3.3"
@@ -86,26 +71,6 @@ version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
[[package]]
name = "idna"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
dependencies = [
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "indexmap"
version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@@ -118,12 +83,6 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "lexopt"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478ee9e62aaeaf5b140bd4138753d1f109765488581444218d3ddda43234f3e8"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.112" version = "0.2.112"
@@ -160,17 +119,11 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "percent-encoding"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
[[package]] [[package]]
name = "pico-args" name = "pico-args"
version = "0.5.0" version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
@@ -210,26 +163,6 @@ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]]
name = "thiserror"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "time" name = "time"
version = "0.1.44" version = "0.1.44"
@@ -241,36 +174,6 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "unicode-bidi"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
[[package]]
name = "unicode-normalization"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"tinyvec",
]
[[package]] [[package]]
name = "unicode-segmentation" name = "unicode-segmentation"
version = "1.8.0" version = "1.8.0"
@@ -285,24 +188,13 @@ checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]] [[package]]
name = "upkr" name = "upkr"
version = "0.2.1" version = "0.1.0"
source = "git+https://github.com/exoticorn/upkr.git?rev=080db40d0088bbee2bdf3c5c75288ac7853d6b7a#080db40d0088bbee2bdf3c5c75288ac7853d6b7a" source = "git+https://github.com/exoticorn/upkr.git?rev=d93aec186c9fb91d962c488682a2db125c61306c#d93aec186c9fb91d962c488682a2db125c61306c"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cdivsufsort", "cdivsufsort",
"lexopt", "pbr",
"thiserror", "pico-args",
]
[[package]]
name = "url"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
] ]
[[package]] [[package]]
@@ -315,7 +207,7 @@ dependencies = [
"upkr", "upkr",
"walrus", "walrus",
"wasm-encoder", "wasm-encoder",
"wasmparser 0.99.0", "wasmparser 0.81.0",
] ]
[[package]] [[package]]
@@ -352,9 +244,9 @@ checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]] [[package]]
name = "wasm-encoder" name = "wasm-encoder"
version = "0.22.0" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef126be0e14bdf355ac1a8b41afc89195289e5c7179f80118e3abddb472f0810" checksum = "db0c351632e46cc06a58a696a6c11e4cf90cad4b9f8f07a0b59128d616c29bb0"
dependencies = [ dependencies = [
"leb128", "leb128",
] ]
@@ -367,13 +259,9 @@ checksum = "b35c86d22e720a07d954ebbed772d01180501afe7d03d464f413bb5f8914a8d6"
[[package]] [[package]]
name = "wasmparser" name = "wasmparser"
version = "0.99.0" version = "0.81.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ef3b717afc67f848f412d4f02c127dd3e35a0eecd58c684580414df4fde01d3" checksum = "98930446519f63d00a836efdc22f67766ceae8dbcc1571379f2bcabc6b2b9abc"
dependencies = [
"indexmap",
"url",
]
[[package]] [[package]]
name = "winapi" 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 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
wasmparser = "0.99" wasmparser = "0.81"
wasm-encoder = "0.22" wasm-encoder = "0.8"
walrus = "0.19" walrus = "0.19"
anyhow = "1" anyhow = "1"
pico-args = "0.5" pico-args = "0.4"
upkr = { git = "https://github.com/exoticorn/upkr.git", rev = "080db40d0088bbee2bdf3c5c75288ac7853d6b7a" } upkr = { git = "https://github.com/exoticorn/upkr.git", rev = "d93aec186c9fb91d962c488682a2db125c61306c" }
pbr = "1" pbr = "1"

View File

@@ -3,7 +3,7 @@ use std::{collections::HashMap, fs::File, path::Path};
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use std::io::prelude::*; use std::io::prelude::*;
use wasm_encoder::{ use wasm_encoder::{
CodeSection, EntityType, ExportKind, ExportSection, Function, FunctionSection, ImportSection, CodeSection, EntityType, Export, ExportSection, Function, FunctionSection, ImportSection,
Instruction, MemoryType, Module, TypeSection, ValType, Instruction, MemoryType, Module, TypeSection, ValType,
}; };
use ValType::*; use ValType::*;
@@ -218,13 +218,13 @@ impl BaseModule {
let mut imports = ImportSection::new(); let mut imports = ImportSection::new();
for (module, name, type_) in &self.function_imports { for (module, name, type_) in &self.function_imports {
imports.import(*module, name.as_str(), EntityType::Function(*type_)); imports.import(*module, Some(name.as_str()), EntityType::Function(*type_));
} }
for (module, name, import) in &self.global_imports { for (module, name, import) in &self.global_imports {
imports.import( imports.import(
*module, *module,
name.as_str(), Some(name.as_str()),
EntityType::Global(wasm_encoder::GlobalType { EntityType::Global(wasm_encoder::GlobalType {
val_type: import.type_, val_type: import.type_,
mutable: import.mutable, mutable: import.mutable,
@@ -234,12 +234,11 @@ impl BaseModule {
imports.import( imports.import(
"env", "env",
"memory", Some("memory"),
MemoryType { MemoryType {
minimum: self.memory as u64, minimum: self.memory as u64,
maximum: None, maximum: None,
memory64: false, memory64: false,
shared: false,
}, },
); );
@@ -260,7 +259,7 @@ impl BaseModule {
let mut exports = ExportSection::new(); let mut exports = ExportSection::new();
for (name, fnc) in &self.exports { for (name, fnc) in &self.exports {
exports.export(*name, ExportKind::Func, *fnc); exports.export(*name, Export::Function(*fnc));
} }
module.section(&exports); module.section(&exports);
@@ -288,7 +287,7 @@ impl BaseModule {
pub fn create_binary(path: &Path) -> Result<()> { pub fn create_binary(path: &Path) -> Result<()> {
let base1 = BaseModule::for_format_version(1)?.to_wasm(); let base1 = BaseModule::for_format_version(1)?.to_wasm();
let data = upkr::pack(&base1, 4, &upkr::Config::default(), None); let data = upkr::pack(&base1, 4, false, None);
File::create(path)?.write_all(&data)?; File::create(path)?.write_all(&data)?;
Ok(()) Ok(())
} }

View File

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

View File

@@ -10,7 +10,7 @@ use std::{
use wasm_encoder as enc; use wasm_encoder as enc;
use wasmparser::{ use wasmparser::{
BinaryReader, ExportSectionReader, ExternalKind, FunctionBody, FunctionSectionReader, BinaryReader, ExportSectionReader, ExternalKind, FunctionBody, FunctionSectionReader,
ImportSectionReader, TableSectionReader, TypeRef, TypeSectionReader, ImportSectionEntryType, ImportSectionReader, TableSectionReader, TypeSectionReader,
}; };
pub struct PackConfig { pub struct PackConfig {
@@ -63,7 +63,7 @@ pub fn pack(data: &[u8], config: &PackConfig) -> Result<Vec<u8>> {
uw8.extend_from_slice(&upkr::pack( uw8.extend_from_slice(&upkr::pack(
&result[8..], &result[8..],
level, level,
&upkr::Config::default(), false,
Some(&mut |pos| { Some(&mut |pos| {
pb.set(pos as u64); pb.set(pos as u64);
}), }),
@@ -90,10 +90,7 @@ pub fn unpack(data: Vec<u8>) -> Result<Vec<u8>> {
let (version, data) = match data[0] { let (version, data) = match data[0] {
0 => return Ok(data), 0 => return Ok(data),
1 => (1, data[1..].to_vec()), 1 => (1, data[1..].to_vec()),
2 => ( 2 => (1, upkr::unpack(&data[1..], false)),
1,
upkr::unpack(&data[1..], &upkr::Config::default(), 4 * 1024 * 1024)?,
),
other => bail!("Uknown format version {}", other), other => bail!("Uknown format version {}", other),
}; };
@@ -136,8 +133,8 @@ pub fn unpack(data: Vec<u8>) -> Result<Vec<u8>> {
Ok(dest) Ok(dest)
} }
fn to_val_type(type_: &wasmparser::ValType) -> Result<ValType> { fn to_val_type(type_: &wasmparser::Type) -> Result<ValType> {
use wasmparser::ValType::*; use wasmparser::Type::*;
Ok(match *type_ { Ok(match *type_ {
I32 => ValType::I32, I32 => ValType::I32,
I64 => ValType::I64, I64 => ValType::I64,
@@ -147,7 +144,7 @@ fn to_val_type(type_: &wasmparser::ValType) -> Result<ValType> {
}) })
} }
fn to_val_type_vec(types: &[wasmparser::ValType]) -> Result<Vec<ValType>> { fn to_val_type_vec(types: &[wasmparser::Type]) -> Result<Vec<ValType>> {
types.into_iter().map(to_val_type).collect() types.into_iter().map(to_val_type).collect()
} }
@@ -205,7 +202,7 @@ impl<'a> ParsedModule<'a> {
import_section = Some(Section::new(range, ImportSection::parse(reader)?)); import_section = Some(Section::new(range, ImportSection::parse(reader)?));
} }
Payload::GlobalSection(reader) => { Payload::GlobalSection(reader) => {
global_section = Some(Section::new(range, reader.count())); global_section = Some(Section::new(range, reader.get_count()));
} }
Payload::FunctionSection(reader) => { Payload::FunctionSection(reader) => {
function_section = Some(Section::new(range, read_function_section(reader)?)); function_section = Some(Section::new(range, read_function_section(reader)?));
@@ -224,21 +221,21 @@ impl<'a> ParsedModule<'a> {
table_section = Some(Section::new(range, ())); table_section = Some(Section::new(range, ()));
} }
Payload::MemorySection(reader) => { Payload::MemorySection(reader) => {
if reader.count() != 0 { if reader.get_count() != 0 {
bail!("Found non-empty MemorySection. Memory has to be imported!"); bail!("Found non-empty MemorySection. Memory has to be imported!");
} }
} }
Payload::ElementSection(reader) => { Payload::ElementSection(mut reader) => {
let mut elements = Vec::with_capacity(reader.count() as usize); let mut elements = Vec::with_capacity(reader.get_count() as usize);
for element in reader { for _ in 0..reader.get_count() {
elements.push(Element::parse(element?)?); elements.push(Element::parse(reader.read()?)?);
} }
element_section = Some(elements); element_section = Some(elements);
} }
Payload::CodeSectionStart { .. } => (), Payload::CodeSectionStart { .. } => (),
Payload::CodeSectionEntry(body) => function_bodies.push(body), Payload::CodeSectionEntry(body) => function_bodies.push(body),
Payload::CustomSection { .. } => (), Payload::CustomSection { .. } => (),
Payload::End(..) => break, Payload::End => break,
other => bail!("Unsupported section: {:?}", other), other => bail!("Unsupported section: {:?}", other),
} }
@@ -466,7 +463,7 @@ impl<'a> ParsedModule<'a> {
{ {
let mut export_section = enc::ExportSection::new(); let mut export_section = enc::ExportSection::new();
for (name, fnc) in my_exports { for (name, fnc) in my_exports {
export_section.export(&name, enc::ExportKind::Func, fnc); export_section.export(&name, enc::Export::Function(fnc));
} }
module.section(&export_section); module.section(&export_section);
} }
@@ -489,7 +486,7 @@ impl<'a> ParsedModule<'a> {
} }
element_section.active( element_section.active(
None, None,
&wasm_encoder::ConstExpr::i32_const(element.start_index as i32), &wasm_encoder::Instruction::I32Const(element.start_index as i32),
ValType::FuncRef, ValType::FuncRef,
wasm_encoder::Elements::Functions(&functions), wasm_encoder::Elements::Functions(&functions),
); );
@@ -538,27 +535,28 @@ fn read_type_section(reader: TypeSectionReader) -> Result<Vec<base_module::Funct
for type_def in reader { for type_def in reader {
match type_def? { match type_def? {
wasmparser::Type::Func(fnc) => { wasmparser::TypeDef::Func(fnc) => {
if fnc.results().len() > 1 { if fnc.returns.len() > 1 {
bail!("Multi-value not supported"); bail!("Multi-value not supported");
} }
let params = to_val_type_vec(fnc.params())?; let params = to_val_type_vec(&fnc.params)?;
let result = to_val_type_vec(fnc.results())?.into_iter().next(); let result = to_val_type_vec(&fnc.returns)?.into_iter().next();
function_types.push(FunctionType { params, result }); function_types.push(FunctionType { params, result });
} }
t => bail!("Unsupported type def {:?}", t),
} }
} }
Ok(function_types) Ok(function_types)
} }
fn validate_table_section(reader: TableSectionReader) -> Result<()> { fn validate_table_section(mut reader: TableSectionReader) -> Result<()> {
if reader.count() != 1 { if reader.get_count() != 1 {
bail!("Only up to one table supported"); bail!("Only up to one table supported");
} }
let type_ = reader.into_iter().next().unwrap()?; let type_ = reader.read()?;
if type_.element_type != wasmparser::ValType::FuncRef { if type_.element_type != wasmparser::Type::FuncRef {
bail!("Only one funcref table is supported"); bail!("Only one funcref table is supported");
} }
@@ -592,38 +590,45 @@ impl ImportSection {
for import in reader { for import in reader {
let import = import?; let import = import?;
match import.ty { if let Some(field) = import.field {
TypeRef::Func(type_) => { match import.ty {
functions.push(FunctionImport { ImportSectionEntryType::Function(type_) => {
module: import.module.to_string(), functions.push(FunctionImport {
field: import.name.to_string(), module: import.module.to_string(),
type_, field: field.to_string(),
}); type_,
} });
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 { ImportSectionEntryType::Memory(mem) => {
bail!("Wrong memory import options: {:?}", import.ty); 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;
} }
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),
} }
TypeRef::Global(glbl) => { } else {
globals.push(GlobalImport { bail!(
module: import.module.to_string(), "Found import without field, only module '{}'",
field: import.name.to_string(), import.module
type_: GlobalType { );
type_: to_val_type(&glbl.content_type)?,
mutable: glbl.mutable,
},
});
}
_ => bail!("Unsupported import item {:?}", import.ty),
} }
} }
@@ -643,37 +648,40 @@ struct Element {
impl Element { impl Element {
fn parse(element: wasmparser::Element) -> Result<Element> { fn parse(element: wasmparser::Element) -> Result<Element> {
match element.items { if element.ty != wasmparser::Type::FuncRef {
wasmparser::ElementItems::Functions(funcs_reader) => { bail!("Table element type is not FuncRef");
let start_index = if let wasmparser::ElementKind::Active {
offset_expr,
table_index: 0,
} = element.kind
{
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 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"),
} }
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");
}
}
Ok(Element {
start_index,
functions,
})
} }
} }
@@ -704,8 +712,8 @@ fn read_export_section(reader: ExportSectionReader) -> Result<Vec<(String, u32)>
for export in reader { for export in reader {
let export = export?; let export = export?;
match export.kind { match export.kind {
ExternalKind::Func => { ExternalKind::Function => {
function_exports.push((export.name.to_string(), export.index)); function_exports.push((export.field.to_string(), export.index));
} }
_ => (), // just ignore all other kinds since MicroW8 doesn't expect any exports other than functions _ => (), // just ignore all other kinds since MicroW8 doesn't expect any exports other than functions
} }
@@ -726,11 +734,13 @@ fn remap_function(
} }
let mut function = enc::Function::new(locals); let mut function = enc::Function::new(locals);
let block_type = |ty: wasmparser::BlockType| -> Result<enc::BlockType> { let block_type = |ty: wasmparser::TypeOrFuncType| -> Result<enc::BlockType> {
Ok(match ty { Ok(match ty {
wasmparser::BlockType::Empty => enc::BlockType::Empty, wasmparser::TypeOrFuncType::Type(wasmparser::Type::EmptyBlockType) => {
wasmparser::BlockType::Type(ty) => enc::BlockType::Result(to_val_type(&ty)?), enc::BlockType::Empty
wasmparser::BlockType::FuncType(ty) => enc::BlockType::FunctionType( }
wasmparser::TypeOrFuncType::Type(ty) => enc::BlockType::Result(to_val_type(&ty)?),
wasmparser::TypeOrFuncType::FuncType(ty) => enc::BlockType::FunctionType(
*type_map *type_map
.get(&ty) .get(&ty)
.ok_or_else(|| anyhow!("Function type index out of range: {}", ty))?, .ok_or_else(|| anyhow!("Function type index out of range: {}", ty))?,
@@ -744,7 +754,7 @@ fn remap_function(
.ok_or_else(|| anyhow!("Global index out of range: {}", idx))?) .ok_or_else(|| anyhow!("Global index out of range: {}", idx))?)
}; };
fn mem(m: wasmparser::MemArg) -> enc::MemArg { fn mem(m: wasmparser::MemoryImmediate) -> enc::MemArg {
enc::MemArg { enc::MemArg {
offset: m.offset, offset: m.offset,
align: m.align as u32, align: m.align as u32,
@@ -759,31 +769,24 @@ fn remap_function(
function.instruction(&match op? { function.instruction(&match op? {
De::Unreachable => En::Unreachable, De::Unreachable => En::Unreachable,
De::Nop => En::Nop, De::Nop => En::Nop,
De::Block { blockty } => En::Block(block_type(blockty)?), De::Block { ty } => En::Block(block_type(ty)?),
De::Loop { blockty } => En::Loop(block_type(blockty)?), De::Loop { ty } => En::Loop(block_type(ty)?),
De::If { blockty } => En::If(block_type(blockty)?), De::If { ty } => En::If(block_type(ty)?),
De::Else => En::Else, De::Else => En::Else,
De::Try { .. } | De::Catch { .. } | De::Throw { .. } | De::Rethrow { .. } => todo!(), De::Try { .. } | De::Catch { .. } | De::Throw { .. } | De::Rethrow { .. } => todo!(),
De::End => En::End, De::End => En::End,
De::Br { relative_depth } => En::Br(relative_depth), De::Br { relative_depth } => En::Br(relative_depth),
De::BrIf { relative_depth } => En::BrIf(relative_depth), De::BrIf { relative_depth } => En::BrIf(relative_depth),
De::BrTable { targets } => En::BrTable( De::BrTable { .. } => todo!(),
targets.targets().collect::<Result<Vec<u32>, _>>()?.into(),
targets.default(),
),
De::Return => En::Return, De::Return => En::Return,
De::Call { function_index } => En::Call( De::Call { function_index } => En::Call(
*function_map *function_map
.get(&function_index) .get(&function_index)
.ok_or_else(|| anyhow!("Function index out of range: {}", function_index))?, .ok_or_else(|| anyhow!("Function index out of range: {}", function_index))?,
), ),
De::CallIndirect { De::CallIndirect { index, table_index } => En::CallIndirect {
type_index,
table_index,
table_byte: _, // what is this supposed to be?
} => En::CallIndirect {
ty: *type_map ty: *type_map
.get(&type_index) .get(&index)
.ok_or_else(|| anyhow!("Unknown function type in call indirect"))?, .ok_or_else(|| anyhow!("Unknown function type in call indirect"))?,
table: table_index, table: table_index,
}, },
@@ -803,16 +806,16 @@ fn remap_function(
De::I64Load { memarg } => En::I64Load(mem(memarg)), De::I64Load { memarg } => En::I64Load(mem(memarg)),
De::F32Load { memarg } => En::F32Load(mem(memarg)), De::F32Load { memarg } => En::F32Load(mem(memarg)),
De::F64Load { memarg } => En::F64Load(mem(memarg)), De::F64Load { memarg } => En::F64Load(mem(memarg)),
De::I32Load8S { memarg } => En::I32Load8S(mem(memarg)), De::I32Load8S { memarg } => En::I32Load8_S(mem(memarg)),
De::I32Load8U { memarg } => En::I32Load8U(mem(memarg)), De::I32Load8U { memarg } => En::I32Load8_U(mem(memarg)),
De::I32Load16S { memarg } => En::I32Load16S(mem(memarg)), De::I32Load16S { memarg } => En::I32Load16_S(mem(memarg)),
De::I32Load16U { memarg } => En::I32Load16U(mem(memarg)), De::I32Load16U { memarg } => En::I32Load16_U(mem(memarg)),
De::I64Load8S { memarg } => En::I64Load8S(mem(memarg)), De::I64Load8S { memarg } => En::I64Load8_S(mem(memarg)),
De::I64Load8U { memarg } => En::I64Load8U(mem(memarg)), De::I64Load8U { memarg } => En::I64Load8_U(mem(memarg)),
De::I64Load16S { memarg } => En::I64Load16S(mem(memarg)), De::I64Load16S { memarg } => En::I64Load16_S(mem(memarg)),
De::I64Load16U { memarg } => En::I64Load16U(mem(memarg)), De::I64Load16U { memarg } => En::I64Load16_U(mem(memarg)),
De::I64Load32S { memarg } => En::I64Load32S(mem(memarg)), De::I64Load32S { memarg } => En::I64Load32_S(mem(memarg)),
De::I64Load32U { memarg } => En::I64Load32U(mem(memarg)), De::I64Load32U { memarg } => En::I64Load32_U(mem(memarg)),
De::I32Store { memarg } => En::I32Store(mem(memarg)), De::I32Store { memarg } => En::I32Store(mem(memarg)),
De::I64Store { memarg } => En::I64Store(mem(memarg)), De::I64Store { memarg } => En::I64Store(mem(memarg)),
De::F32Store { memarg } => En::F32Store(mem(memarg)), De::F32Store { memarg } => En::F32Store(mem(memarg)),
@@ -831,7 +834,7 @@ fn remap_function(
De::RefNull { .. } | De::RefIsNull { .. } | De::RefFunc { .. } => todo!(), De::RefNull { .. } | De::RefIsNull { .. } | De::RefFunc { .. } => todo!(),
De::I32Eqz => En::I32Eqz, De::I32Eqz => En::I32Eqz,
De::I32Eq => En::I32Eq, De::I32Eq => En::I32Eq,
De::I32Ne => En::I32Ne, De::I32Ne => En::I32Neq,
De::I32LtS => En::I32LtS, De::I32LtS => En::I32LtS,
De::I32LtU => En::I32LtU, De::I32LtU => En::I32LtU,
De::I32GtS => En::I32GtS, De::I32GtS => En::I32GtS,
@@ -842,7 +845,7 @@ fn remap_function(
De::I32GeU => En::I32GeU, De::I32GeU => En::I32GeU,
De::I64Eqz => En::I64Eqz, De::I64Eqz => En::I64Eqz,
De::I64Eq => En::I64Eq, De::I64Eq => En::I64Eq,
De::I64Ne => En::I64Ne, De::I64Ne => En::I64Neq,
De::I64LtS => En::I64LtS, De::I64LtS => En::I64LtS,
De::I64LtU => En::I64LtU, De::I64LtU => En::I64LtU,
De::I64GtS => En::I64GtS, De::I64GtS => En::I64GtS,
@@ -852,13 +855,13 @@ fn remap_function(
De::I64GeS => En::I64GeS, De::I64GeS => En::I64GeS,
De::I64GeU => En::I64GeU, De::I64GeU => En::I64GeU,
De::F32Eq => En::F32Eq, De::F32Eq => En::F32Eq,
De::F32Ne => En::F32Ne, De::F32Ne => En::F32Neq,
De::F32Lt => En::F32Lt, De::F32Lt => En::F32Lt,
De::F32Gt => En::F32Gt, De::F32Gt => En::F32Gt,
De::F32Le => En::F32Le, De::F32Le => En::F32Le,
De::F32Ge => En::F32Ge, De::F32Ge => En::F32Ge,
De::F64Eq => En::F64Eq, De::F64Eq => En::F64Eq,
De::F64Ne => En::F64Ne, De::F64Ne => En::F64Neq,
De::F64Lt => En::F64Lt, De::F64Lt => En::F64Lt,
De::F64Gt => En::F64Gt, De::F64Gt => En::F64Gt,
De::F64Le => En::F64Le, De::F64Le => En::F64Le,
@@ -965,7 +968,7 @@ fn remap_function(
De::I64TruncSatF32U => En::I64TruncSatF32U, De::I64TruncSatF32U => En::I64TruncSatF32U,
De::I64TruncSatF64S => En::I64TruncSatF64S, De::I64TruncSatF64S => En::I64TruncSatF64S,
De::I64TruncSatF64U => En::I64TruncSatF64U, De::I64TruncSatF64U => En::I64TruncSatF64U,
De::MemoryCopy { src_mem, dst_mem } => En::MemoryCopy { src_mem, dst_mem }, De::MemoryCopy { src, dst } => En::MemoryCopy { src, dst },
De::MemoryFill { mem } => En::MemoryFill(mem), De::MemoryFill { mem } => En::MemoryFill(mem),
other => bail!("Unsupported instruction {:?}", other), other => bail!("Unsupported instruction {:?}", other),
}); });

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

View File

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

View File

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

View File

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

View File

@@ -15,7 +15,7 @@ struct FpsCounter {
} }
impl Window { impl Window {
pub fn new(mut config: WindowConfig) -> Result<Window> { pub fn new(config: WindowConfig) -> Result<Window> {
let fps_counter = if config.fps_counter { let fps_counter = if config.fps_counter {
Some(FpsCounter { Some(FpsCounter {
start: Instant::now(), start: Instant::now(),
@@ -24,7 +24,6 @@ impl Window {
} else { } else {
None None
}; };
config.scale = config.scale.max(1.).min(20.);
if config.enable_gpu { if config.enable_gpu {
match gpu::Window::new(config) { match gpu::Window::new(config) {
Ok(window) => { Ok(window) => {
@@ -72,7 +71,6 @@ pub struct WindowConfig {
filter: u32, filter: u32,
fullscreen: bool, fullscreen: bool,
fps_counter: bool, fps_counter: bool,
scale: f32,
} }
impl Default for WindowConfig { impl Default for WindowConfig {
@@ -82,7 +80,6 @@ impl Default for WindowConfig {
filter: 5, filter: 5,
fullscreen: false, fullscreen: false,
fps_counter: false, fps_counter: false,
scale: 2.,
} }
} }
} }
@@ -105,10 +102,6 @@ impl WindowConfig {
} }
self.fullscreen = args.contains("--fullscreen"); self.fullscreen = args.contains("--fullscreen");
self.fps_counter = args.contains("--fps"); self.fps_counter = args.contains("--fps");
self.scale = args
.opt_value_from_str("--scale")
.unwrap()
.unwrap_or(self.scale);
} }
} }

View File

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