mirror of
https://github.com/exoticorn/microw8.git
synced 2026-01-20 19:26:43 +01:00
Compare commits
3 Commits
f21497dd2e
...
v0.1.1
| Author | SHA1 | Date | |
|---|---|---|---|
| c25a52b61b | |||
| 619ea903ba | |||
| 9b900a49e4 |
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -1517,7 +1517,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uw8"
|
name = "uw8"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"curlywas",
|
"curlywas",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "uw8"
|
name = "uw8"
|
||||||
version = "0.1.0"
|
version = "0.1.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
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -53,6 +53,21 @@ Options:
|
|||||||
-l LEVEL, --level LEVEL : Compression level (0-9). Higher compression levels are really slow.
|
-l LEVEL, --level LEVEL : Compression level (0-9). Higher compression levels are really slow.
|
||||||
|
|
||||||
|
|
||||||
|
uw8 unpack <infile> <outfile>
|
||||||
|
|
||||||
|
Unpacks a MicroW8 module into a standard WebAssembly module.
|
||||||
|
|
||||||
|
|
||||||
|
uw8 compile [<options>] <infile> <outfile>
|
||||||
|
|
||||||
|
Compiles a CurlyWas source file to a standard WebAssembly module. Most useful together with
|
||||||
|
the --debug option to get a module that works well in the Chrome debugger.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
-d, --debug : Generate a name section to help debugging
|
||||||
|
|
||||||
|
|
||||||
uw8 filter-exports <infile> <outfile>
|
uw8 filter-exports <infile> <outfile>
|
||||||
|
|
||||||
Reads a binary WebAssembly module, removes all exports not used by the MicroW8 platform + everything that is unreachable without those exports and writes the resulting module to <outfile>.
|
Reads a binary WebAssembly module, removes all exports not used by the MicroW8 platform + everything that is unreachable without those exports and writes the resulting module to <outfile>.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
clang -O2 -Wno-incompatible-library-redeclaration --no-standard-libraries -ffast-math -Xclang -target-feature -Xclang +nontrapping-fptoint -Wl,--no-entry -Wl,--export-all -Wl,--import-memory -Wl,--initial-memory=262144 -Wl,-zstack-size=90000 -o cart.wasm cart.c --target=wasm32 && \
|
clang -O2 -Wno-incompatible-library-redeclaration --no-standard-libraries -ffast-math -Xclang -target-feature -Xclang +nontrapping-fptoint -Wl,--no-entry,--export-all,--import-memory,--initial-memory=262144,--global-base=81920,-zstack-size=4096 -o cart.wasm cart.c --target=wasm32 && \
|
||||||
uw8 filter-exports cart.wasm cart.wasm && \
|
uw8 filter-exports cart.wasm cart.wasm && \
|
||||||
wasm-opt -Oz --fast-math --strip-producers -o cart.wasm cart.wasm && \
|
wasm-opt -Oz --fast-math --strip-producers -o cart.wasm cart.wasm && \
|
||||||
uw8 pack -l 9 cart.wasm cart.uw8
|
uw8 pack -l 9 cart.wasm cart.uw8
|
||||||
@@ -15,11 +15,14 @@ The initial motivation behind MicroW8 was to explore whether there was a way to
|
|||||||
* Gamepad input (D-Pad + 4 Buttons)
|
* Gamepad input (D-Pad + 4 Buttons)
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
* [Fireworks](v0.1pre5#AgwvgP+M59snqjl4CMKw5sqm1Zw9yJCbSviMjeLUdHus2a3yl/a99+uiBeqZgP/2jqSjrLjRk73COMM6OSLpsxK8ugT1kuk/q4hQUqqPpGozHoa0laulzGGcahzdfdJsYaK1sIdeIYS9M5PnJx/Wk9H+PvWEPy2Zvv7I6IW7Fg==) (127 bytes): Some fireworks to welcome 2022.
|
* [Fireworks](v0.1.1#AgwvgP+M59snqjl4CMKw5sqm1Zw9yJCbSviMjeLUdHus2a3yl/a99+uiBeqZgP/2jqSjrLjRk73COMM6OSLpsxK8ugT1kuk/q4hQUqqPpGozHoa0laulzGGcahzdfdJsYaK1sIdeIYS9M5PnJx/Wk9H+PvWEPy2Zvv7I6IW7Fg==) (127 bytes): Some fireworks to welcome 2022.
|
||||||
* [Skip Ahead](v0.1pre5#AgyfpZ80wkW28kiUZ9VIK4v+RPnVxqjK1dz2BcDoNyQPsS2g4OgEzkTe6jyoAfFOmqKrS8SM2aRljBal9mjNn8i4fP9eBK+RehQKxxGtJa9FqftvqEnh3ez1YaYxqj7jgTdzJ/WAYVmKMovBT1myrX3FamqKSOgMsNedLhVTLAhQup3sNcYEjGNo8b0HZ5+AgMgCwYRGCe//XQOMAaAAzqDILgmpEZ/43RKHcQpHEQwbURfNQJpadJe2sz3q5FlQnTGXQ9oSMokidhlC+aR/IpNHieuBGLhFZ2GfnwVQ0geBbQpTPA==) (229 bytes): A port of my [TIC-80 256byte game](http://tic80.com/play?cart=1735) from LoveByte'21
|
* [Skip Ahead](v0.1.1#AgyfpZ80wkW28kiUZ9VIK4v+RPnVxqjK1dz2BcDoNyQPsS2g4OgEzkTe6jyoAfFOmqKrS8SM2aRljBal9mjNn8i4fP9eBK+RehQKxxGtJa9FqftvqEnh3ez1YaYxqj7jgTdzJ/WAYVmKMovBT1myrX3FamqKSOgMsNedLhVTLAhQup3sNcYEjGNo8b0HZ5+AgMgCwYRGCe//XQOMAaAAzqDILgmpEZ/43RKHcQpHEQwbURfNQJpadJe2sz3q5FlQnTGXQ9oSMokidhlC+aR/IpNHieuBGLhFZ2GfnwVQ0geBbQpTPA==) (229 bytes): A port of my [TIC-80 256byte game](http://tic80.com/play?cart=1735) from LoveByte'21
|
||||||
* [OhNoAnotherTunnel](v0.1pre4#Ag95rdCB5Ww5NofyQaKF4P1mrNRso4azgiem4hK99Gh8OMzSpFq3NsNDo7O7pqln10D11l9uXr/ritw7OEzKwbEfCdvaRnS2Z0Kz0iDEZt/gIqOdvFmxsL1MjPQ4XInPbUJpQUonhQq29oP2omFabnQxn0bzoK7mZjcwc5GetHG+hGajkJcRr8oOnjfCol8RD+ha33GYtPnut+GLe4ktzf5UxZwGs6oT9qqC61lRDakN) (177 bytes): A port of my [entry](http://tic80.com/play?cart=1871) in the Outline'21 bytebattle final
|
* [OhNoAnotherTunnel](v0.1.1#AgPP1oEFvPzY/rBZwTumtYn37zeMFgpir1Bkn91jsNcp26VzoUpkAOOJTtnzVBfW+/dGnnIdbq/irBUJztY5wuua80DORTYZndgdwZHcSk15ajc4nyO0g1A6kGWyW56oZk0iPYJA9WtUmoj0Plvy1CGwIZrMe57X7QZcdqc3u6zjTA41Tpiqi9vnO3xbhi8o594Vx0XPXwVzpYq1ZCTYenfAGaXKkDmAFJqiVIsiCg==) (175 bytes): A port of my [entry](http://tic80.com/play?cart=1871) in the Outline'21 bytebattle final
|
||||||
* [Technotunnel](v0.1pre4#AqL8HeK1M9dn2nWNIF5vaq/Vh64pMt5nJIFoFKpBMPUsGtDtpqjo1JbT9LzPhAxCqJ7Yh4TA6oTGd4xhLowf+cWZMY73+7AZmfXJJsBi4cej/hH+4wlAgxFIrnOYnr/18IpnZbsHf0eGm1BhahX74+cVR0TRmNQmYC7GhCNS3mv/3MJn74lCj7t28aBJPjEZhP9fGXdG2u5Egh/Tjdg=) (158 bytes): A port of my [entry](https://tic80.com/play?cart=1873) in the Outline'21 bytebattle quater final
|
* [Technotunnel](v0.1.1#AhPXpq894LaUhp5+HQf39f39/Jc8g5zUrBSc0uyKh36ivskczhY84h55zL8gWpkdvKuRQI+KIt80isKzh8jkM8nILcx0RUvyk8yjE8TgNsgkcORVI0RY5k3qE4ySjaycxa2DVZH61UWZuLsCouuwT7I80TbmmetQSbMywJ/avrrCZIAH0UzQfvOiCJNG48NI0FFY1vjB7a7dcp8Uqg==) (157 bytes): A port of my [entry](https://tic80.com/play?cart=1873) in the Outline'21 bytebattle quater final
|
||||||
* [Font & Palette](v0.1pre4#AgKaeeOuwg5gCKvFIeiitEwMpUI2rymEcu+DDB1vMu9uBoufvUxIr4Y5p4Jj2ukoNO4PE7QS5cN1ZyDMCRfSzYIGZxKlN2J6NKEWK7KVPk9wVUgn1Ip+hsMinWgEO8ETKfPuHoIa4kjI+ULFOMad7vd3rt/lh1Vy9w+R2MXG/7T61d3c7C6KY+eQNS0eW3ys4iU8R6SycuWZuuZ2Sg3Qxp826s+Kt+2qBojpzNOSoyFqyrVyYMTKEkSl0BZOj59Cs1hPm5bq0F1MmVhGAzMhW9V4YeAe): Just a simple viewer for the default font and palette.
|
* [Font & Palette](v0.1.1#At/p39+IBnj6ry1TRe7jzVy2A4tXgBvmoW2itzoyF2aM28pGy5QDiKxqrk8l9sbWZLtnAb+jgOfU+9QhpuyCAkhN6gPOU481IUL/df96vNe3h288Dqwhd3sfFpothIVFsMwRK72kW2hiR7zWsaXyy5pNmjR6BJk4piWx9ApT1ZwoUajhk6/zij6itq/FD1U3jj/J3MOwqZ2ef8Bv6ZPQlJIYVf62icGa69wS6SI1qBpIFiF14F8PcztRVbKIxLpT4ArCS6nz6FPnyUkqATGSBNPJ): Just a simple viewer for the default font and palette.
|
||||||
|
|
||||||
|
Examplers for older versions:
|
||||||
|
|
||||||
* [Technotunnel B/W](v0.1pre2#AQrDAQHAAQIBfwp9A0AgAUEAsiABQcACb7JDmhkgQ5MiBCAEIASUIAFBwAJtQfgAa7IiBSAFlJKRIgaVIgcgByAAskHQD7KVIgIQAEPNzEw/lCIDlCAHIAeUIAOUIAOUQQGykiADIAOUk5GSIgiUIAOTQQqylCACkiIJqCAFIAaVIAiUQQqylCACkiIKqHMgCEEyspQgBpUiCyACkkEUspSocUEFcbJBArIgC5OUQRaylJeoOgB4IAFBAWoiAUGA2ARIDQALCw==) (199 bytes uncompressed): A port of my [entry](https://tic80.com/play?cart=1873) in the Outline'21 bytebattle quater final (older MicroW8 version with monochrome palette)
|
* [Technotunnel B/W](v0.1pre2#AQrDAQHAAQIBfwp9A0AgAUEAsiABQcACb7JDmhkgQ5MiBCAEIASUIAFBwAJtQfgAa7IiBSAFlJKRIgaVIgcgByAAskHQD7KVIgIQAEPNzEw/lCIDlCAHIAeUIAOUIAOUQQGykiADIAOUk5GSIgiUIAOTQQqylCACkiIJqCAFIAaVIAiUQQqylCACkiIKqHMgCEEyspQgBpUiCyACkkEUspSocUEFcbJBArIgC5OUQRaylJeoOgB4IAFBAWoiAUGA2ARIDQALCw==) (199 bytes uncompressed): A port of my [entry](https://tic80.com/play?cart=1873) in the Outline'21 bytebattle quater final (older MicroW8 version with monochrome palette)
|
||||||
* [XorScroll](v0.1pre2#AQovAS0BAX8DQCABIAFBwAJvIABBCm1qIAFBwAJtczoAeCABQQFqIgFBgNgESA0ACws=) (50 bytes uncompressed): A simple scrolling XOR pattern. Fun fact: This is the pre-loaded effect when entering a bytebattle.
|
* [XorScroll](v0.1pre2#AQovAS0BAX8DQCABIAFBwAJvIABBCm1qIAFBwAJtczoAeCABQQFqIgFBgNgESA0ACws=) (50 bytes uncompressed): A simple scrolling XOR pattern. Fun fact: This is the pre-loaded effect when entering a bytebattle.
|
||||||
* [CircleWorm](v0.1pre2#AQp7AXkCAX8CfUEgEA0DQCABskEEspUiAkECspUgALJBiCeylSIDQQWylJIQAEEBspJBoAGylCACQQOylSADQQSylJIQAEEBspJB+ACylCADQRGylCACQQKylJIQAEECspJBELKUIAFBAmxBP2oQEiABQQFqIgFBP0gNAAsL) (126 bytes uncompressed): Just a test for the circle fill function.
|
* [CircleWorm](v0.1pre2#AQp7AXkCAX8CfUEgEA0DQCABskEEspUiAkECspUgALJBiCeylSIDQQWylJIQAEEBspJBoAGylCACQQOylSADQQSylJIQAEEBspJB+ACylCADQRGylCACQQKylJIQAEECspJBELKUIAFBAmxBP2oQEiABQQFqIgFBP0gNAAsL) (126 bytes uncompressed): Just a test for the circle fill function.
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ as a cheap random-access PRNG (aka noise function).
|
|||||||
|
|
||||||
## Graphics
|
## Graphics
|
||||||
|
|
||||||
The default palette can be seen [here](../v0.1pre4#AgKaeeOuwg5gCKvFIeiitEwMpUI2rymEcu+DDB1vMu9uBoufvUxIr4Y5p4Jj2ukoNO4PE7QS5cN1ZyDMCRfSzYIGZxKlN2J6NKEWK7KVPk9wVUgn1Ip+hsMinWgEO8ETKfPuHoIa4kjI+ULFOMad7vd3rt/lh1Vy9w+R2MXG/7T61d3c7C6KY+eQNS0eW3ys4iU8R6SycuWZuuZ2Sg3Qxp826s+Kt+2qBojpzNOSoyFqyrVyYMTKEkSl0BZOj59Cs1hPm5bq0F1MmVhGAzMhW9V4YeAe). (Press Z on the keyboard to switch to palette.)
|
The default palette can be seen [here](../v0.1.0#At/p39+IBnj6ry1TRe7jzVy2A4tXgBvmoW2itzoyF2aM28pGy5QDiKxqrk8l9sbWZLtnAb+jgOfU+9QhpuyCAkhN6gPOU481IUL/df96vNe3h288Dqwhd3sfFpothIVFsMwRK72kW2hiR7zWsaXyy5pNmjR6BJk4piWx9ApT1ZwoUajhk6/zij6itq/FD1U3jj/J3MOwqZ2ef8Bv6ZPQlJIYVf62icGa69wS6SI1qBpIFiF14F8PcztRVbKIxLpT4ArCS6nz6FPnyUkqATGSBNPJ). (Press Z on the keyboard to switch to palette.)
|
||||||
|
|
||||||
The palette can be changed by writing 32bit rgba colors to addresses 0x13000-0x13400.
|
The palette can be changed by writing 32bit rgba colors to addresses 0x13000-0x13400.
|
||||||
|
|
||||||
@@ -192,7 +192,7 @@ The integer time in milliseconds can also be read at address 0x40.
|
|||||||
|
|
||||||
## Text output
|
## Text output
|
||||||
|
|
||||||
The default font can be seen [here](../v0.1pre4#AgKaeeOuwg5gCKvFIeiitEwMpUI2rymEcu+DDB1vMu9uBoufvUxIr4Y5p4Jj2ukoNO4PE7QS5cN1ZyDMCRfSzYIGZxKlN2J6NKEWK7KVPk9wVUgn1Ip+hsMinWgEO8ETKfPuHoIa4kjI+ULFOMad7vd3rt/lh1Vy9w+R2MXG/7T61d3c7C6KY+eQNS0eW3ys4iU8R6SycuWZuuZ2Sg3Qxp826s+Kt+2qBojpzNOSoyFqyrVyYMTKEkSl0BZOj59Cs1hPm5bq0F1MmVhGAzMhW9V4YeAe).
|
The default font can be seen [here](../v0.1.0#At/p39+IBnj6ry1TRe7jzVy2A4tXgBvmoW2itzoyF2aM28pGy5QDiKxqrk8l9sbWZLtnAb+jgOfU+9QhpuyCAkhN6gPOU481IUL/df96vNe3h288Dqwhd3sfFpothIVFsMwRK72kW2hiR7zWsaXyy5pNmjR6BJk4piWx9ApT1ZwoUajhk6/zij6itq/FD1U3jj/J3MOwqZ2ef8Bv6ZPQlJIYVf62icGa69wS6SI1qBpIFiF14F8PcztRVbKIxLpT4ArCS6nz6FPnyUkqATGSBNPJ).
|
||||||
|
|
||||||
The font can be changed by writing 1bpp 8x8 characters to addresses 0x13400-0x13c00.
|
The font can be changed by writing 1bpp 8x8 characters to addresses 0x13400-0x13c00.
|
||||||
|
|
||||||
@@ -305,6 +305,27 @@ Options:
|
|||||||
* `-u`, `--uncompressed`: Use the uncompressed `uw8` format for packing.
|
* `-u`, `--uncompressed`: Use the uncompressed `uw8` format for packing.
|
||||||
* `-l LEVEL`, `--level LEVEL`: Compression level (0-9). Higher compression levels are really slow.
|
* `-l LEVEL`, `--level LEVEL`: Compression level (0-9). Higher compression levels are really slow.
|
||||||
|
|
||||||
|
## `uw8 unpack`
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
`uw8 unpack <infile> <outfile>`
|
||||||
|
|
||||||
|
Unpacks a MicroW8 module into a standard WebAssembly module.
|
||||||
|
|
||||||
|
## `uw8 compile`
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
`uw8 compile [<options>] <infile> <outfile>`
|
||||||
|
|
||||||
|
Compiles a [CurlyWas](https://github.com/exoticorn/curlywas) source file to a standard WebAssembly module. Most useful together with
|
||||||
|
the `--debug` option to get a module that works well in the Chrome debugger.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
* `-d`, `--debug`: Generate a name section to help debugging
|
||||||
|
|
||||||
## `uw8 filter-exports`
|
## `uw8 filter-exports`
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
@@ -390,3 +411,33 @@ the first function in the file under the name `upd`.
|
|||||||
|
|
||||||
Same as version `01` except everything after the first byte is compressed
|
Same as version `01` except everything after the first byte is compressed
|
||||||
using a [custom LZ compression scheme](https://github.com/exoticorn/upkr).
|
using a [custom LZ compression scheme](https://github.com/exoticorn/upkr).
|
||||||
|
|
||||||
|
# The web runtime
|
||||||
|
|
||||||
|
Load carts into the web runtime either by using the "Load cart..." button, or by dragging the file
|
||||||
|
onto the screen area.
|
||||||
|
|
||||||
|
## Input
|
||||||
|
|
||||||
|
For input, you can either use a standard gamepad or keyboard. On a keyboard use the arrow keys and the keys Z, X, A and S to emulate the A, B, X and Y buttons.
|
||||||
|
|
||||||
|
## Video recording
|
||||||
|
|
||||||
|
Press F10 to start recording, press again to stop. Then a download dialog will open for the video file.
|
||||||
|
The file might miss some metadata needed to load in some video editing tools, in that case you can run
|
||||||
|
it through ffmpeg like this `ffmpeg -i IN_NAME.webm -c copy -o OUT_NAME.webm to fix it up.
|
||||||
|
|
||||||
|
To convert it to 1280x720, for example for a lovebyte upload, you can use:
|
||||||
|
|
||||||
|
```
|
||||||
|
ffmpeg -i IN.webm -vf "scale=960:720:flags=neighbor,pad=1280:720:160:0" -r 60 OUT.mp4
|
||||||
|
```
|
||||||
|
|
||||||
|
## Screenshot
|
||||||
|
|
||||||
|
Pressing F9 opens a download dialog with a screenshot.
|
||||||
|
|
||||||
|
## Devkit mode
|
||||||
|
|
||||||
|
Append `#devkit` to the web runtime url in order to switch to devkit mode. In devkit mode, standard web assembly modules
|
||||||
|
are loaded bypassing the loader, removing all size restrictions. At the same time, the memory limit is increased to 1GB.
|
||||||
1
site/static/v0.1.1/index.html
Normal file
1
site/static/v0.1.1/index.html
Normal file
File diff suppressed because one or more lines are too long
@@ -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.1.0">
|
<a href="v0.1.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>
|
||||||
|
|||||||
26
src/main.rs
26
src/main.rs
@@ -20,6 +20,8 @@ fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
Some("run") => run(args),
|
Some("run") => run(args),
|
||||||
Some("pack") => pack(args),
|
Some("pack") => pack(args),
|
||||||
|
Some("unpack") => unpack(args),
|
||||||
|
Some("compile") => compile(args),
|
||||||
Some("filter-exports") => filter_exports(args),
|
Some("filter-exports") => filter_exports(args),
|
||||||
Some("help") | None => {
|
Some("help") | None => {
|
||||||
println!("uw8 {}", env!("CARGO_PKG_VERSION"));
|
println!("uw8 {}", env!("CARGO_PKG_VERSION"));
|
||||||
@@ -27,6 +29,8 @@ fn main() -> Result<()> {
|
|||||||
println!("Usage:");
|
println!("Usage:");
|
||||||
println!(" uw8 run [-t/--timeout <frames>] [-w/--watch] [-p/--pack] [-u/--uncompressed] [-l/--level] [-o/--output <out-file>] <file>");
|
println!(" uw8 run [-t/--timeout <frames>] [-w/--watch] [-p/--pack] [-u/--uncompressed] [-l/--level] [-o/--output <out-file>] <file>");
|
||||||
println!(" uw8 pack [-u/--uncompressed] [-l/--level] <in-file> <out-file>");
|
println!(" uw8 pack [-u/--uncompressed] [-l/--level] <in-file> <out-file>");
|
||||||
|
println!(" uw8 unpack <in-file> <out-file>");
|
||||||
|
println!(" uw8 compile [-d/--debug] <in-file> <out-file>");
|
||||||
println!(" uw8 filter-exports <in-wasm> <out-wasm>");
|
println!(" uw8 filter-exports <in-wasm> <out-wasm>");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -166,6 +170,28 @@ fn pack(mut args: Arguments) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn unpack(mut args: Arguments) -> Result<()> {
|
||||||
|
let in_file = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
|
||||||
|
let out_file = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
|
||||||
|
|
||||||
|
uw8_tool::unpack_file(&in_file, &out_file).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile(mut args: Arguments) -> Result<()> {
|
||||||
|
let mut options = curlywas::Options::default();
|
||||||
|
if args.contains(["-d", "--debug"]) {
|
||||||
|
options = options.with_debug();
|
||||||
|
}
|
||||||
|
|
||||||
|
let in_file = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
|
||||||
|
let out_file = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
|
||||||
|
|
||||||
|
let module = curlywas::compile_file(in_file, options)?;
|
||||||
|
File::create(out_file)?.write_all(&module)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn filter_exports(mut args: Arguments) -> Result<()> {
|
fn filter_exports(mut args: Arguments) -> Result<()> {
|
||||||
let in_file = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
|
let in_file = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
|
||||||
let out_file = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
|
let out_file = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
|
||||||
|
|||||||
@@ -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,
|
||||||
ImportSectionEntryType, ImportSectionReader, TypeSectionReader,
|
ImportSectionEntryType, ImportSectionReader, TableSectionReader, TypeSectionReader,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct PackConfig {
|
pub struct PackConfig {
|
||||||
@@ -31,7 +31,9 @@ impl PackConfig {
|
|||||||
|
|
||||||
impl Default for PackConfig {
|
impl Default for PackConfig {
|
||||||
fn default() -> PackConfig {
|
fn default() -> PackConfig {
|
||||||
PackConfig { compression: Some(2) }
|
PackConfig {
|
||||||
|
compression: Some(2),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,6 +158,8 @@ struct ParsedModule<'a> {
|
|||||||
start_section: Option<u32>,
|
start_section: Option<u32>,
|
||||||
function_bodies: Vec<wasmparser::FunctionBody<'a>>,
|
function_bodies: Vec<wasmparser::FunctionBody<'a>>,
|
||||||
data_section: Option<Section<()>>,
|
data_section: Option<Section<()>>,
|
||||||
|
table_section: Option<Section<()>>,
|
||||||
|
element_section: Option<Vec<Element>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ParsedModule<'a> {
|
impl<'a> ParsedModule<'a> {
|
||||||
@@ -170,6 +174,8 @@ impl<'a> ParsedModule<'a> {
|
|||||||
let mut start_section = None;
|
let mut start_section = None;
|
||||||
let mut function_bodies = Vec::new();
|
let mut function_bodies = Vec::new();
|
||||||
let mut data_section = None;
|
let mut data_section = None;
|
||||||
|
let mut table_section = None;
|
||||||
|
let mut element_section = None;
|
||||||
|
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
|
|
||||||
@@ -209,6 +215,17 @@ impl<'a> ParsedModule<'a> {
|
|||||||
Payload::DataSection(_) => {
|
Payload::DataSection(_) => {
|
||||||
data_section = Some(Section::new(range, ()));
|
data_section = Some(Section::new(range, ()));
|
||||||
}
|
}
|
||||||
|
Payload::TableSection(reader) => {
|
||||||
|
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()?)?);
|
||||||
|
}
|
||||||
|
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 { .. } => (),
|
||||||
@@ -229,6 +246,8 @@ impl<'a> ParsedModule<'a> {
|
|||||||
start_section,
|
start_section,
|
||||||
function_bodies,
|
function_bodies,
|
||||||
data_section,
|
data_section,
|
||||||
|
table_section,
|
||||||
|
element_section,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,6 +424,10 @@ impl<'a> ParsedModule<'a> {
|
|||||||
module.section(&function_section);
|
module.section(&function_section);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(tables) = self.table_section {
|
||||||
|
copy_section(&mut module, &self.data[tables.range.clone()])?;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(ref globals) = self.globals {
|
if let Some(ref globals) = self.globals {
|
||||||
copy_section(&mut module, &self.data[globals.range.clone()])?;
|
copy_section(&mut module, &self.data[globals.range.clone()])?;
|
||||||
for i in 0..globals.data {
|
for i in 0..globals.data {
|
||||||
@@ -446,6 +469,25 @@ impl<'a> ParsedModule<'a> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(elements) = self.element_section {
|
||||||
|
let mut element_section = wasm_encoder::ElementSection::new();
|
||||||
|
for element in elements {
|
||||||
|
let mut functions = Vec::with_capacity(element.functions.len());
|
||||||
|
for index in element.functions {
|
||||||
|
functions.push(*function_map.get(&index).ok_or_else(|| {
|
||||||
|
anyhow!("Function index {} not found in function map", index)
|
||||||
|
})?);
|
||||||
|
}
|
||||||
|
element_section.active(
|
||||||
|
None,
|
||||||
|
&wasm_encoder::Instruction::I32Const(element.start_index as i32),
|
||||||
|
ValType::FuncRef,
|
||||||
|
wasm_encoder::Elements::Functions(&functions),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
module.section(&element_section);
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut code_section = enc::CodeSection::new();
|
let mut code_section = enc::CodeSection::new();
|
||||||
|
|
||||||
@@ -502,6 +544,19 @@ fn read_type_section(reader: TypeSectionReader) -> Result<Vec<base_module::Funct
|
|||||||
Ok(function_types)
|
Ok(function_types)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validate_table_section(mut reader: TableSectionReader) -> Result<()> {
|
||||||
|
if reader.get_count() != 1 {
|
||||||
|
bail!("Only up to one table supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
let type_ = reader.read()?;
|
||||||
|
if type_.element_type != wasmparser::Type::FuncRef {
|
||||||
|
bail!("Only one funcref table is supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Section<T> {
|
struct Section<T> {
|
||||||
range: std::ops::Range<usize>,
|
range: std::ops::Range<usize>,
|
||||||
@@ -579,6 +634,51 @@ impl ImportSection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Element {
|
||||||
|
start_index: u32,
|
||||||
|
functions: Vec<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Element {
|
||||||
|
fn parse(element: wasmparser::Element) -> Result<Element> {
|
||||||
|
if element.ty != wasmparser::Type::FuncRef {
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct FunctionImport {
|
struct FunctionImport {
|
||||||
module: String,
|
module: String,
|
||||||
@@ -678,8 +778,13 @@ fn remap_function(
|
|||||||
.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 {
|
||||||
| De::ReturnCall { .. }
|
ty: *type_map
|
||||||
|
.get(&index)
|
||||||
|
.ok_or_else(|| anyhow!("Unknown function type in call indirect"))?,
|
||||||
|
table: table_index,
|
||||||
|
},
|
||||||
|
De::ReturnCall { .. }
|
||||||
| De::ReturnCallIndirect { .. }
|
| De::ReturnCallIndirect { .. }
|
||||||
| De::Delegate { .. }
|
| De::Delegate { .. }
|
||||||
| De::CatchAll => todo!(),
|
| De::CatchAll => todo!(),
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="uw8">
|
<div id="uw8">
|
||||||
<a href="https://exoticorn.github.io/microw8">MicroW8</a> 0.1.0
|
<a href="https://exoticorn.github.io/microw8">MicroW8</a> 0.1.1
|
||||||
</div>
|
</div>
|
||||||
<div id="centered">
|
<div id="centered">
|
||||||
<canvas id="screen" width="320" height="240">
|
<canvas id="screen" width="320" height="240">
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ let screen = document.getElementById('screen');
|
|||||||
let canvasCtx = screen.getContext('2d');
|
let canvasCtx = screen.getContext('2d');
|
||||||
let imageData = canvasCtx.createImageData(320, 240);
|
let imageData = canvasCtx.createImageData(320, 240);
|
||||||
|
|
||||||
|
let devkitMode;
|
||||||
|
|
||||||
let cancelFunction;
|
let cancelFunction;
|
||||||
|
|
||||||
let currentData;
|
let currentData;
|
||||||
@@ -51,9 +53,17 @@ let keyHandler = (e) => {
|
|||||||
break;
|
break;
|
||||||
case 'KeyR':
|
case 'KeyR':
|
||||||
if (isKeyDown) {
|
if (isKeyDown) {
|
||||||
runModule(currentData);
|
runModule(currentData, true);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'F9':
|
||||||
|
if(isKeyDown) {
|
||||||
|
screen.toBlob(blob => {
|
||||||
|
downloadBlob(blob, '.png');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
break;
|
||||||
case 'F10':
|
case 'F10':
|
||||||
if(isKeyDown) {
|
if(isKeyDown) {
|
||||||
recordVideo();
|
recordVideo();
|
||||||
@@ -102,7 +112,11 @@ async function runModule(data, keepUrl) {
|
|||||||
screen.width = screen.width;
|
screen.width = screen.width;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let memory = new WebAssembly.Memory({ initial: 4, maximum: 4 });
|
let memSize = { initial: 4 };
|
||||||
|
if(!devkitMode) {
|
||||||
|
memSize.maximum = 4;
|
||||||
|
}
|
||||||
|
let memory = new WebAssembly.Memory({ initial: 4, maximum: devkitMode ? 16 : 4 });
|
||||||
let memU8 = U8(memory.buffer);
|
let memU8 = U8(memory.buffer);
|
||||||
|
|
||||||
let importObject = {
|
let importObject = {
|
||||||
@@ -114,7 +128,7 @@ async function runModule(data, keepUrl) {
|
|||||||
let loader;
|
let loader;
|
||||||
|
|
||||||
let loadModuleData = (data) => {
|
let loadModuleData = (data) => {
|
||||||
if (U8(data)[0] != 0) {
|
if (loader && (!devkitMode || U8(data)[0] != 0)) {
|
||||||
memU8.set(U8(data));
|
memU8.set(U8(data));
|
||||||
let length = loader.exports.load_uw8(data.byteLength);
|
let length = loader.exports.load_uw8(data.byteLength);
|
||||||
data = new ArrayBuffer(length);
|
data = new ArrayBuffer(length);
|
||||||
@@ -226,6 +240,14 @@ async function runModule(data, keepUrl) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function downloadBlob(blob, ext) {
|
||||||
|
let a = document.createElement('a');
|
||||||
|
a.href = URL.createObjectURL(blob);
|
||||||
|
a.download = 'microw8_' + new Date().toISOString() + ext;
|
||||||
|
a.click();
|
||||||
|
URL.revokeObjectURL(a.href);
|
||||||
|
}
|
||||||
|
|
||||||
let videoRecorder;
|
let videoRecorder;
|
||||||
let videoStartTime;
|
let videoStartTime;
|
||||||
function recordVideo() {
|
function recordVideo() {
|
||||||
@@ -237,7 +259,7 @@ function recordVideo() {
|
|||||||
|
|
||||||
videoRecorder = new MediaRecorder(screen.captureStream(), {
|
videoRecorder = new MediaRecorder(screen.captureStream(), {
|
||||||
mimeType: 'video/webm',
|
mimeType: 'video/webm',
|
||||||
videoBitsPerSecond: 5000000
|
videoBitsPerSecond: 25000000
|
||||||
});
|
});
|
||||||
|
|
||||||
let chunks = [];
|
let chunks = [];
|
||||||
@@ -251,11 +273,7 @@ function recordVideo() {
|
|||||||
|
|
||||||
videoRecorder.onstop = () => {
|
videoRecorder.onstop = () => {
|
||||||
timer.hidden = true;
|
timer.hidden = true;
|
||||||
let a = document.createElement('a');
|
downloadBlob(new Blob(chunks, {type: 'video/webm'}), '.webm');
|
||||||
a.href = URL.createObjectURL(new Blob(chunks, {type: 'video/webm'}));
|
|
||||||
a.download = 'microw8_' + new Date().toISOString() + 'webm';
|
|
||||||
a.click();
|
|
||||||
URL.revokeObjectURL(a.href);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
videoRecorder.start();
|
videoRecorder.start();
|
||||||
@@ -281,11 +299,16 @@ async function runModuleFromURL(url, keepUrl) {
|
|||||||
if(type && type.includes('html')) {
|
if(type && type.includes('html')) {
|
||||||
throw false;
|
throw false;
|
||||||
}
|
}
|
||||||
runModule(await response.arrayBuffer(), keepUrl);
|
runModule(await response.arrayBuffer(), keepUrl || devkitMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
function runModuleFromHash() {
|
function runModuleFromHash() {
|
||||||
let hash = window.location.hash.slice(1);
|
let hash = window.location.hash.slice(1);
|
||||||
|
if(hash == 'devkit') {
|
||||||
|
devkitMode = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
devkitMode = false;
|
||||||
if (hash.length > 0) {
|
if (hash.length > 0) {
|
||||||
if (hash.startsWith("url=")) {
|
if (hash.startsWith("url=")) {
|
||||||
runModuleFromURL(hash.slice(4), true);
|
runModuleFromURL(hash.slice(4), true);
|
||||||
|
|||||||
Reference in New Issue
Block a user