30 Commits

Author SHA1 Message Date
c9ae04e652 last preparations for 0.1.0 release 2022-01-13 06:20:00 +01:00
179b3eaed1 Finish docs, add hero screenshot 2022-01-12 22:18:36 +01:00
b7d0004307 write up usage instructions for the uw8 tool 2022-01-12 00:03:54 +01:00
b34685b1c2 finished api docs 2022-01-10 23:52:09 +01:00
3d79239a01 work on api documentation 2022-01-09 23:30:14 +01:00
f062e545f6 implement "itch.io support" 2022-01-04 22:34:21 +01:00
68890e62ad don't exit on runtime error in watch mode 2022-01-03 23:36:58 +01:00
10ba7ed3bb make use of nontrapping-fptoint, add tunnel.cwa as reference 2022-01-02 16:34:11 +01:00
acea5cb6e0 enable nontrapping fptoint feature in c and zig example 2022-01-02 16:06:01 +01:00
d239775411 forgot to add file 2022-01-02 15:42:28 +01:00
20365a0dd0 port tunnel example to C 2022-01-02 15:22:11 +01:00
88ee0e1bef port rust tunnel example to zig 2022-01-02 13:58:56 +01:00
26206a312a add filter-exports command to automatically remove unused exports
this removes the need for a manual step in the rust example
2022-01-01 19:02:58 +01:00
6a75988489 update rust example 2022-01-01 15:51:18 +01:00
397ff19d80 test remaining control codes 2022-01-01 15:37:17 +01:00
b7e8ddf0f1 test new control codes 2022-01-01 14:19:58 +01:00
ed9d9fdeb5 implement more control codes 2021-12-31 23:09:49 +01:00
cd1275a78f implement framework for control codes 2021-12-31 22:12:21 +01:00
32345876b9 v0.1pre5 + some virtual fireworks for today 2021-12-31 11:08:31 +01:00
6ca63b87e5 add rectangle_outline function 2021-12-30 22:03:46 +01:00
bf7604d385 implement circle_outline function 2021-12-30 16:25:53 +01:00
0f22b1efb9 some more loader size optimizations 2021-12-28 21:43:58 +01:00
4521fb73ef size optimized uncompress function in loader 2021-12-28 17:32:11 +01:00
6f22e00487 update upkr version 2021-12-27 23:46:03 +01:00
cf0e40a0e5 add uw8 pack command 2021-12-27 22:02:27 +01:00
462dc3a1c6 update upkr (new tweaked compressed format) 2021-12-27 17:34:00 +01:00
0f795ae061 binaries should be built with --release 2021-12-22 21:49:57 +01:00
2eca3fa8c8 install libxkbcommon-dev for linux build 2021-12-22 21:38:51 +01:00
7db0d66ad4 first attempt at ci setup to build uw8 binaries 2021-12-22 21:31:29 +01:00
28713bd1df implement drop handler to accept carts via drag'n'drop 2021-12-19 14:45:16 +01:00
43 changed files with 1491 additions and 338 deletions

50
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,50 @@
name: Rust
on:
push:
branches: [ master ]
env:
CARGO_TERM_COLOR: always
jobs:
build:
strategy:
matrix:
build: [ linux, windows, macos ]
include:
- build: linux
os: ubuntu-latest
exe: uw8
- build: windows
os: windows-latest
exe: uw8.exe
- build: macos
os: macos-latest
exe: uw8
runs-on: ${{ matrix.os }}
steps:
- name: Install dependencies
run: sudo apt-get install -y libxkbcommon-dev
if: matrix.os == 'ubuntu-latest'
- name: Checkout
uses: actions/checkout@v2
- name: Cache build dirs
uses: actions/cache@v2
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Build
run: cargo build --release --verbose
- name: Upload artifact
uses: actions/upload-artifact@v2
with:
name: uw8-${{ matrix.build }}
path: target/release/${{ matrix.exe }}

2
.gitignore vendored
View File

@@ -1,3 +1,5 @@
/target
.cargo/
.vscode/
/examples/**/*.wasm
/examples/**/*.uw8

237
Cargo.lock generated
View File

@@ -8,7 +8,16 @@ version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e61f2b7f93d2c7d2b08263acaa4a363b3e276806c68af6134c44f523bf1aacd"
dependencies = [
"gimli",
"gimli 0.25.0",
]
[[package]]
name = "addr2line"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
dependencies = [
"gimli 0.26.1",
]
[[package]]
@@ -37,18 +46,18 @@ dependencies = [
[[package]]
name = "ansi_term"
version = "0.11.0"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "anyhow"
version = "1.0.44"
version = "1.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1"
checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3"
[[package]]
name = "ariadne"
@@ -78,11 +87,11 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "backtrace"
version = "0.3.62"
version = "0.3.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "091bcdf2da9950f96aa522681ce805e6857f6ca8df73833d35736ab2dc78e152"
checksum = "321629d8ba6513061f26707241fa9bc89524ff1cd7a915a97ef0c62c666ce1b6"
dependencies = [
"addr2line",
"addr2line 0.17.0",
"cc",
"cfg-if 1.0.0",
"libc",
@@ -152,9 +161,9 @@ checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c"
[[package]]
name = "cc"
version = "1.0.71"
version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
dependencies = [
"jobserver",
]
@@ -201,9 +210,9 @@ dependencies = [
[[package]]
name = "clang-sys"
version = "1.2.2"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10612c0ec0e0a1ff0e97980647cb058a6e7aedb913d01d009c406b8b7d0b26ee"
checksum = "fa66045b9cb23c2e9c1520732030608b02ee07e5cfaa5a521ec15ded7fa24c90"
dependencies = [
"glob",
"libc",
@@ -212,9 +221,9 @@ dependencies = [
[[package]]
name = "clap"
version = "2.33.3"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"ansi_term",
"atty",
@@ -258,9 +267,9 @@ dependencies = [
[[package]]
name = "cpp_demangle"
version = "0.3.3"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea47428dc9d2237f3c6bc134472edfd63ebba0af932e783506dcfd66f10d18a"
checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f"
dependencies = [
"cfg-if 1.0.0",
]
@@ -293,7 +302,7 @@ dependencies = [
"cranelift-codegen-meta",
"cranelift-codegen-shared",
"cranelift-entity",
"gimli",
"gimli 0.25.0",
"log",
"regalloc",
"smallvec",
@@ -366,9 +375,9 @@ dependencies = [
[[package]]
name = "crc32fast"
version = "1.2.1"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836"
dependencies = [
"cfg-if 1.0.0",
]
@@ -423,6 +432,12 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "cty"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
[[package]]
name = "curlywas"
version = "0.1.0"
@@ -480,9 +495,9 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "encoding_rs"
version = "0.8.29"
version = "0.8.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a74ea89a0a1b98f6332de42c95baff457ada66d1cb4030f9ff151b2041a1c746"
checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df"
dependencies = [
"cfg-if 1.0.0",
]
@@ -515,9 +530,9 @@ dependencies = [
[[package]]
name = "errno"
version = "0.2.7"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa68f2fb9cae9d37c9b2b3584aba698a2e97f72d7aef7b9f7aa71d8b54ce46fe"
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
dependencies = [
"errno-dragonfly",
"libc",
@@ -641,6 +656,12 @@ dependencies = [
"stable_deref_trait",
]
[[package]]
name = "gimli"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
[[package]]
name = "glob"
version = "0.3.0"
@@ -653,6 +674,15 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]]
name = "heck"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "hermit-abi"
version = "0.1.19"
@@ -677,6 +707,12 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "id-arena"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
[[package]]
name = "indexmap"
version = "1.7.0"
@@ -719,9 +755,9 @@ dependencies = [
[[package]]
name = "itertools"
version = "0.10.1"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
dependencies = [
"either",
]
@@ -774,15 +810,15 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.105"
version = "0.2.112"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "869d572136620d55835903746bcb5cdc54cb2851fd0aeec53220b4bb65ef3013"
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
[[package]]
name = "libloading"
version = "0.7.1"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0cf036d15402bea3c5d4de17b3fce76b3e4a56ebc1f577be0e7a72f7c607cf0"
checksum = "afe203d669ec979b7128619bae5a63b7b42e9203c1b29146079ee05e2f604b52"
dependencies = [
"cfg-if 1.0.0",
"winapi 0.3.9",
@@ -814,9 +850,9 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "memoffset"
version = "0.6.4"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg",
]
@@ -829,7 +865,7 @@ checksum = "7b6e41119d1667465608d36488fa5dcd228057a26c156e25f17f492f38435124"
dependencies = [
"cc",
"orbclient",
"raw-window-handle",
"raw-window-handle 0.3.4",
"tempfile",
"wayland-client",
"wayland-cursor",
@@ -842,9 +878,9 @@ dependencies = [
[[package]]
name = "minimal-lexical"
version = "0.1.4"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c64630dcdd71f1a64c435f54885086a0de5d6a12d104d69b165fb7d5286d677"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
@@ -901,9 +937,9 @@ dependencies = [
[[package]]
name = "more-asserts"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0debeb9fcf88823ea64d64e4a815ab1643f33127d995978e099942ce38f25238"
checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389"
[[package]]
name = "net2"
@@ -941,9 +977,9 @@ dependencies = [
[[package]]
name = "nom"
version = "7.0.0"
version = "7.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ffd9d26838a953b4af82cbeb9f1592c6798916983959be223a7124e992742c1"
checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109"
dependencies = [
"memchr",
"minimal-lexical",
@@ -979,9 +1015,9 @@ dependencies = [
[[package]]
name = "num_cpus"
version = "1.13.0"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
dependencies = [
"hermit-abi",
"libc",
@@ -1009,9 +1045,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.8.0"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
[[package]]
name = "opaque-debug"
@@ -1026,7 +1062,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c976c5018e7f1db4359616d8b31ef8ae7d9649b11803c0b38fff67fd2999fc8"
dependencies = [
"libc",
"raw-window-handle",
"raw-window-handle 0.3.4",
"redox_syscall",
"sdl2",
"sdl2-sys",
@@ -1036,9 +1072,9 @@ dependencies = [
[[package]]
name = "paste"
version = "1.0.5"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf547ad0c65e31259204bd90935776d1c693cec2f4ff7abb7a1bbbd40dfe58"
checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5"
[[package]]
name = "pbr"
@@ -1066,9 +1102,9 @@ checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468"
[[package]]
name = "pkg-config"
version = "0.3.22"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f"
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
[[package]]
name = "ppv-lite86"
@@ -1084,9 +1120,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "proc-macro2"
version = "1.0.30"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
dependencies = [
"unicode-xid",
]
@@ -1108,9 +1144,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "1.0.10"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
checksum = "22849d5b648cf354b3d9aa83fc555f3496061bfd3b34e01824c40a621a260aa1"
dependencies = [
"proc-macro2",
]
@@ -1157,11 +1193,21 @@ dependencies = [
[[package]]
name = "raw-window-handle"
version = "0.3.3"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a441a7a6c80ad6473bd4b74ec1c9a4c951794285bf941c2126f607c72e48211"
checksum = "e28f55143d0548dad60bb4fbdc835a3d7ac6acc3324506450c5fdd6e42903a76"
dependencies = [
"libc",
"raw-window-handle 0.4.2",
]
[[package]]
name = "raw-window-handle"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fba75eee94a9d5273a68c9e1e105d9cffe1ef700532325788389e5a83e2522b7"
dependencies = [
"cty",
]
[[package]]
@@ -1302,7 +1348,7 @@ dependencies = [
"bitflags",
"lazy_static",
"libc",
"raw-window-handle",
"raw-window-handle 0.3.4",
"sdl2-sys",
]
@@ -1323,18 +1369,18 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.130"
version = "1.0.132"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
checksum = "8b9875c23cf305cd1fd7eb77234cbb705f21ea6a72c637a5c6db5fe4b8e7f008"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.130"
version = "1.0.132"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
checksum = "ecc0db5cb2556c0e558887d9bbdcf6ac4471e83ff66cf696e5419024d1606276"
dependencies = [
"proc-macro2",
"quote",
@@ -1386,9 +1432,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "syn"
version = "1.0.80"
version = "1.0.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194"
checksum = "ecb2e6da8ee5eb9a61068762a32fa9619cc591ceb055b3687f4cd4051ec2e06b"
dependencies = [
"proc-macro2",
"quote",
@@ -1397,9 +1443,9 @@ dependencies = [
[[package]]
name = "tar"
version = "0.4.37"
version = "0.4.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6f5515d3add52e0bbdcad7b83c388bb36ba7b754dda3b5f5bc2d38640cdba5c"
checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6"
dependencies = [
"filetime",
"libc",
@@ -1494,9 +1540,15 @@ dependencies = [
[[package]]
name = "typenum"
version = "1.14.0"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]]
name = "unicode-segmentation"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
[[package]]
name = "unicode-width"
@@ -1524,7 +1576,7 @@ dependencies = [
[[package]]
name = "upkr"
version = "0.1.0"
source = "git+https://github.com/exoticorn/upkr.git?rev=7d280bd#7d280bd533b037d579b6cdf12fb69046eb5c2c91"
source = "git+https://github.com/exoticorn/upkr.git?rev=2e7983fc#2e7983fc650788d98da2eecef2d16f63e849e4a0"
dependencies = [
"anyhow",
"cdivsufsort",
@@ -1554,6 +1606,7 @@ dependencies = [
"pbr",
"pico-args",
"upkr",
"walrus",
"wasm-encoder",
"wasmparser 0.81.0",
]
@@ -1587,6 +1640,32 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "walrus"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eb08e48cde54c05f363d984bb54ce374f49e242def9468d2e1b6c2372d291f8"
dependencies = [
"anyhow",
"id-arena",
"leb128",
"log",
"walrus-macro",
"wasmparser 0.77.0",
]
[[package]]
name = "walrus-macro"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a6e5bd22c71e77d60140b0bd5be56155a37e5bd14e24f5f87298040d0cc40d7"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
@@ -1656,6 +1735,12 @@ dependencies = [
"leb128",
]
[[package]]
name = "wasmparser"
version = "0.77.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b35c86d22e720a07d954ebbed772d01180501afe7d03d464f413bb5f8914a8d6"
[[package]]
name = "wasmparser"
version = "0.80.2"
@@ -1735,7 +1820,7 @@ dependencies = [
"cranelift-frontend",
"cranelift-native",
"cranelift-wasm",
"gimli",
"gimli 0.25.0",
"more-asserts",
"object 0.26.2",
"target-lexicon",
@@ -1753,7 +1838,7 @@ dependencies = [
"anyhow",
"cfg-if 1.0.0",
"cranelift-entity",
"gimli",
"gimli 0.25.0",
"indexmap",
"log",
"more-asserts",
@@ -1782,11 +1867,11 @@ version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24f46dd757225f29a419be415ea6fb8558df9b0194f07e3a6a9c99d0e14dd534"
dependencies = [
"addr2line",
"addr2line 0.16.0",
"anyhow",
"bincode",
"cfg-if 1.0.0",
"gimli",
"gimli 0.25.0",
"libc",
"log",
"more-asserts",
@@ -2024,7 +2109,7 @@ version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7"
dependencies = [
"nom 7.0.0",
"nom 7.1.0",
]
[[package]]
@@ -2063,18 +2148,18 @@ checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71"
[[package]]
name = "zstd"
version = "0.9.0+zstd.1.5.0"
version = "0.9.1+zstd.1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07749a5dc2cb6b36661290245e350f15ec3bbb304e493db54a1d354480522ccd"
checksum = "538b8347df9257b7fbce37677ef7535c00a3c7bf1f81023cc328ed7fe4b41de8"
dependencies = [
"zstd-safe",
]
[[package]]
name = "zstd-safe"
version = "4.1.1+zstd.1.5.0"
version = "4.1.2+zstd.1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c91c90f2c593b003603e5e0493c837088df4469da25aafff8bce42ba48caf079"
checksum = "9fb4cfe2f6e6d35c5d27ecd9d256c4b6f7933c4895654917460ec56c29336cc1"
dependencies = [
"libc",
"zstd-sys",
@@ -2082,9 +2167,9 @@ dependencies = [
[[package]]
name = "zstd-sys"
version = "1.6.1+zstd.1.5.0"
version = "1.6.2+zstd.1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "615120c7a2431d16cf1cf979e7fc31ba7a5b5e5707b29c8a99e5dbf8a8392a33"
checksum = "2daf2f248d9ea44454bfcb2516534e8b8ad2fc91bf818a1885495fc42bc8ac9f"
dependencies = [
"cc",
"libc",

View File

@@ -1 +1,61 @@
See [here](https://exoticorn.github.io/microw8/).
# MicroW8
MicroW8 is a WebAssembly based fantasy console inspired by the likes of [TIC-80](https://tic80.com/), [WASM-4](https://wasm4.org/) and [PICO-8](https://www.lexaloffle.com/pico-8.php).
The initial motivation behind MicroW8 was to explore whether there was a way to make WebAssembly viable for size-coding. (Size coding being the art of creating tiny (often <= 256 bytes) graphical effects and games.) The available examples so far are all in this space, however, I very carefully made sure that all design decisions make sense from the point of view of bigger projects as well.
See [here](https://exoticorn.github.io/microw8/) for more information and docs.
## Specs
* Screen: 320x240, 256 colors, 60Hz
* Modules: Up to 256KB (WASM)
* Memory: 256KB
* Gamepad input (D-Pad + 4 Buttons)
## Download
The download includes
* `microw8.html`: The web runtime, a small, self-contained html file that can be opened in any modern browser to load and run MicroW8 carts.
* `uw8`/`uw8.exe`: The MicroW8 dev tool, including a native runtime.
* `examples`: Example source code in CurlyWas and Wat (WebAssembly text format).
* `carts`: The examples compiled to `.uw8` carts.
## uw8 dev tool
```
uw8 run [<options>] <file>
Runs <file> which can be a binary WebAssembly module, an `.uw8` cart, a wat (WebAssembly text format) source file or a CurlyWas source file.
Options:
-w, --watch : Reloads the given file every time it changes on disk.
-p, --pack : Pack the file into an .uw8 cart before running it and print the resulting size.
-u, --uncompressed : Use the uncompressed uw8 format for packing.
-l LEVEL, --level LEVEL : Compression level (0-9). Higher compression levels are really slow.
-o FILE, --output FILE : Write the loaded and optionally packed cart back to disk.
uw8 pack [<options>] <infile> <outfile>
Packs the WebAssembly module or text file, or CurlyWas source file into a .uw8 cart.
Options:
-u, --uncompressed : Use the uncompressed uw8 format for packing.
-l LEVEL, --level LEVEL : Compression level (0-9). Higher compression levels are really slow.
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>.
```
## Examples
* [Fireworks](https://exoticorn.github.io/microw8/v0.1pre5#AgwvgP+M59snqjl4CMKw5sqm1Zw9yJCbSviMjeLUdHus2a3yl/a99+uiBeqZgP/2jqSjrLjRk73COMM6OSLpsxK8ugT1kuk/q4hQUqqPpGozHoa0laulzGGcahzdfdJsYaK1sIdeIYS9M5PnJx/Wk9H+PvWEPy2Zvv7I6IW7Fg==) (127 bytes): Some fireworks to welcome 2022.
* [Skip Ahead](https://exoticorn.github.io/microw8/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
* [OhNoAnotherTunnel](https://exoticorn.github.io/microw8/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
* [Technotunnel](https://exoticorn.github.io/microw8/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

6
examples/c/build.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/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 && \
uw8 filter-exports cart.wasm cart.wasm && \
wasm-opt -Oz --fast-math --strip-producers -o cart.wasm cart.wasm && \
uw8 pack -l 9 cart.wasm cart.uw8

27
examples/c/cart.c Normal file
View File

@@ -0,0 +1,27 @@
#define IMPORT(MODULE, NAME) __attribute__((import_module(MODULE), import_name(NAME)))
IMPORT("env", "atan2") extern float atan2(float, float);
IMPORT("env", "time") extern float time();
float sqrt(float v) {
return __builtin_sqrt(v);
}
#define FRAMEBUFFER ((unsigned char*)120)
void upd() {
int i = 0;
for( ;; ) {
float t = time() * 63.0f;
float x = (float)(i % 320 - 160);
float y = (float)(i / 320 - 120);
float d = 40000.0f / sqrt(x * x + y * y);
float u = atan2(x, y) * 512.0f / 3.141f;
unsigned char c = (unsigned char)((int)(d + t * 2.0f) ^ (int)(u + t)) >> 4;
FRAMEBUFFER[i] = c;
i += 1;
if(i >= 320*240) break;
}
}

View File

@@ -0,0 +1,23 @@
import "env.memory" memory(4);
import "env.printString" fn printString(i32);
export fn upd() {
printString(0x20000);
}
data 0x20000 {
i8(14, 0xfd, 15, 15, 12) // clear screen to color 0xfd
"Top left"
i8(14, 10, 11, 11, 11, 11) // scroll up 4 lines
i8(31, 28, 29, 14, 0xfd) "Bottom right"
i8(14, 10, 10, 10) // scroll down 2 lines
i8(31, 40, 3, 14, 10, 15, 0xf0) "Other colors"
i8(24, 0xb0) "inverted"
i8(13, 10, 8, 8) "->"
i8(10, 10, 9, 9, 1) "|<-"
i8(5, 31, 7, 28+17, 15, 0xe3) "Graphics text!"
i8(5, 31, 6, 28+16, 15, 0xe5) "Graphics text!"
i8(4, 24, 14, 10, 0x90, 0x80, 0xf1)
i8(31, 37, 29, 0xf1, 0x80, 0x90)
i8(0)
}

View File

@@ -0,0 +1,32 @@
import "env.time" fn time() -> f32;
import "env.circle" fn circle(f32, f32, f32, i32);
import "env.cls" fn cls(i32);
import "env.randomSeed" fn seed(i32);
import "env.randomf" fn randomf() -> f32;
import "env.sin" fn sin(f32) -> f32;
import "env.cos" fn cos(f32) -> f32;
import "env.fmod" fn fmod(f32, f32) -> f32;
export fn upd() {
cls(0);
let i: i32;
loop pixels {
let inline rocket = i #>> 9;
let lazy local_time = fmod(time() + rocket as f32 / 5 as f32, 2 as f32);
let lazy rocket = rocket + nearest(time() - local_time) as i32 * 10;
seed(rocket);
let inline x = randomf() * 645 as f32;
let y = randomf() * 133 as f32;
let lazy angle = { seed(i); randomf() } * 44 as f32;
let inline dx = sin(angle);
let inline dy = cos(angle);
let lazy dist = local_time * (randomf() * 44 as f32);
circle(
x + dx * dist,
y + dy * dist + local_time * local_time * 24 as f32,
1 as f32, (rocket % 11 + 1) * 16 - (local_time * 7 as f32) as i32 - (i % 4)
);
branch_if (i := i + 1) < 5120: pixels;
}
}

View File

@@ -0,0 +1,19 @@
import "env.memory" memory(4);
import "env.atan2" fn atan2(f32, f32) -> f32;
import "env.time" fn time() -> f32;
export fn upd() {
let i: i32;
loop pixels {
let inline t = time() * 63 as f32;
let lazy x = (i % 320 - 160) as f32;
let lazy y = (i / 320 - 120) as f32;
let inline d = 40000 as f32 / sqrt(x * x + y * y);
let inline u = atan2(x, y) * (512.0 / 3.141);
let inline c = ((i32.trunc_sat_f32_s(d + t * 2 as f32) ^ i32.trunc_sat_f32_s(u + t)) & 255) >> 4;
i?120 = c;
branch_if (i := i + 1) < 320*240: pixels;
}
}

View File

@@ -1,2 +1,4 @@
rustc --target=wasm32-unknown-unknown --crate-type cdylib -C opt-level="z" -C "link-args=--import-memory --initial-memory=262144 -zstack-size=65536" -o tunnel.wasm tunnel.rs && \
wasm-opt -Oz -o tunnel.wasm tunnel.wasm
rustc --target=wasm32-unknown-unknown -C target-feature=+nontrapping-fptoint --crate-type cdylib -C opt-level="z" -C "link-args=--import-memory --initial-memory=262144 -zstack-size=90000" -o tunnel.wasm tunnel.rs && \
uw8 filter-exports tunnel.wasm tunnel.wasm && \
wasm-opt -Oz --strip-producers -o tunnel.wasm tunnel.wasm && \
uw8 pack -l 9 tunnel.wasm tunnel.uw8

View File

@@ -5,21 +5,23 @@ A nightly rust compiler is needed for the unstable sqrtf32
intrinsic.
Simply compiling with rustc as shown in build.sh results in a
342 byte tunnel.wasm. Using wasm-opt this can be reduced to
243 bytes.
361 byte tunnel.wasm. Using wasm-opt this can be reduced to
255 bytes.
When you disassemble this wasm file using wasm2wat you can see
these globals and exports:
(global (;0;) i32 (i32.const 65536))
(global (;1;) i32 (i32.const 65536))
(global (;0;) i32 (i32.const 90000))
(global (;1;) i32 (i32.const 90000))
(export "__data_end" (global 0))
(export "__heap_base" (global 1))
They are meant to be used for heap allocations and stack for any
values that are not simple scalars (i32, f32, etc.). Since our
code doesn't actually use any of that, we can just delete them
in a text editor and assemble the code again with wat2wasm.
code doesn't actually use any of that, the globals are only
referenced by the exports and we can remove them using
'uw8 filter-exports' (preferably before running wasm-opt) which
removes all exports except those used by the MicroW8 platform.
This gives us a 199 byte wasm file. Running this through
uw8-tool pack brings us to the final size of 137 bytes.
This gives us a 211 byte wasm file. Running this through
uw8 pack brings us to the final size of 119 bytes.

View File

@@ -9,37 +9,41 @@ mod env {
extern "C" {
pub fn atan2(x: f32, y: f32) -> f32;
}
extern "C" {
pub fn time() -> f32;
}
}
fn atan2(x: f32, y: f32) -> f32 {
unsafe { env::atan2(x, y) }
}
fn time() -> f32 {
unsafe { env::time() }
}
fn sqrt(v: f32) -> f32 {
unsafe { core::intrinsics::sqrtf32(v) }
}
fn ftoi(v: f32) -> i32 {
// The compiler is allowed to do bad things to our code if this
// ever results in a value that doesn't fit in an i32.
// (the joy of undefined behavior)
// But that would trap in wasm anyway, so we don't really
// care.
unsafe { v.to_int_unchecked() }
}
#[no_mangle]
pub fn tic(time: i32) {
for i in 0..320 * 240 {
let t = time as f32 / 10 as f32;
pub fn upd() {
let mut i: i32 = 0;
loop {
let t = time() * 63.;
let x = (i % 320 - 160) as f32;
let y = (i / 320 - 120) as f32;
let d = 40000 as f32 / sqrt(x * x + y * y + 1 as f32);
let u = atan2(x, y) * 512f32 / 3.141;
let c = (ftoi(d + t * 2 as f32) ^ ftoi(u + t)) as u8;
let d = 40000 as f32 / sqrt(x * x + y * y);
let u = atan2(x, y) * 512. / 3.141;
let c = ((d + t * 2.) as i32 ^ (u + t) as i32) as u8 >> 4;
unsafe {
*((120 + i) as *mut u8) = c;
}
i += 1;
if i >= 320*240 {
break;
}
}
}

Binary file not shown.

2
examples/zig/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/zig-cache/
/zig-out/

41
examples/zig/build.zig Normal file
View File

@@ -0,0 +1,41 @@
const std = @import("std");
pub fn build(b: *std.build.Builder) void {
const mode = std.builtin.Mode.ReleaseSmall;
const lib = b.addSharedLibrary("cart", "main.zig", .unversioned);
lib.setBuildMode(mode);
lib.setTarget(.{
.cpu_arch = .wasm32,
.os_tag = .freestanding,
.cpu_features_add = std.Target.wasm.featureSet(&.{ .nontrapping_fptoint })
});
lib.import_memory = true;
lib.initial_memory = 262144;
lib.max_memory = 262144;
lib.global_base = 81920;
lib.stack_size = 8192;
lib.install();
if (lib.install_step) |install_step| {
const run_filter_exports = b.addSystemCommand(&[_][]const u8{
"uw8", "filter-exports", "zig-out/lib/cart.wasm", "zig-out/lib/cart-filtered.wasm"
});
run_filter_exports.step.dependOn(&install_step.step);
const run_wasm_opt = b.addSystemCommand(&[_][]const u8{
"wasm-opt", "-Oz", "-o", "zig-out/cart.wasm", "zig-out/lib/cart-filtered.wasm"
});
run_wasm_opt.step.dependOn(&run_filter_exports.step);
const run_uw8_pack = b.addSystemCommand(&[_][]const u8{
"uw8", "pack", "-l", "9", "zig-out/cart.wasm", "zig-out/cart.uw8"
});
run_uw8_pack.step.dependOn(&run_wasm_opt.step);
const make_opt = b.step("make_opt", "make size optimized cart");
make_opt.dependOn(&run_uw8_pack.step);
b.default_step = make_opt;
}
}

20
examples/zig/main.zig Normal file
View File

@@ -0,0 +1,20 @@
extern fn atan2(x: f32, y: f32) f32;
extern fn time() f32;
pub const FRAMEBUFFER: *[320*240]u8 = @intToPtr(*[320*240]u8, 120);
export fn upd() void {
var i: u32 = 0;
while(true) {
var t = time() * 63.0;
var x = @intToFloat(f32, (@intCast(i32, i % 320) - 160));
var y = @intToFloat(f32, (@intCast(i32, i / 320) - 120));
var d = 40000.0 / @sqrt(x * x + y * y);
var u = atan2(x, y) * 512.0 / 3.141;
var c = @intCast(u8, (@floatToInt(i32, d + t * 2.0) ^ @floatToInt(i32, u + t)) & 255) >> 4;
FRAMEBUFFER[@as(usize, i)] = c;
i += 1;
if(i >= 320*240) { break; }
}
}

30
platform/Cargo.lock generated
View File

@@ -30,9 +30,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.47"
version = "1.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38d9ff5d688f1c13395289f67db01d4826b46dd694e7580accdc3e8430f2d98e"
checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3"
[[package]]
name = "ariadne"
@@ -51,9 +51,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bytemuck"
version = "1.7.2"
version = "1.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b"
checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f"
[[package]]
name = "cc"
@@ -110,9 +110,9 @@ dependencies = [
[[package]]
name = "crc32fast"
version = "1.2.2"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3825b1e8580894917dc4468cb634a1b4e9745fddc854edad72d9c04644c0319f"
checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836"
dependencies = [
"cfg-if",
]
@@ -158,9 +158,9 @@ dependencies = [
[[package]]
name = "fallible_collections"
version = "0.4.3"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaefd4190151d458f16f0793d3452d7f13aeb3701566a4cefc4c37598876cc00"
checksum = "52db5973b6a19247baf19b30f41c23a1bfffc2e9ce0a5db2f60e3cd5dc8895f7"
dependencies = [
"hashbrown",
]
@@ -211,9 +211,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.108"
version = "0.2.112"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
[[package]]
name = "lodepng"
@@ -248,9 +248,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.8.0"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
[[package]]
name = "pbr"
@@ -288,9 +288,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "rgb"
version = "0.8.29"
version = "0.8.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a27fa03bb1e3e2941f52d4a555a395a72bf79b0a85fbbaab79447050c97d978c"
checksum = "9a374af9a0e5fdcdd98c1c7b64f05004f9ea2555b6c75f211daa81268a3c50f1"
dependencies = [
"bytemuck",
]
@@ -326,7 +326,7 @@ dependencies = [
[[package]]
name = "upkr"
version = "0.1.0"
source = "git+https://github.com/exoticorn/upkr.git?rev=7d280bd#7d280bd533b037d579b6cdf12fb69046eb5c2c91"
source = "git+https://github.com/exoticorn/upkr.git?rev=2e7983fc#2e7983fc650788d98da2eecef2d16f63e849e4a0"
dependencies = [
"anyhow",
"cdivsufsort",

Binary file not shown.

Binary file not shown.

View File

@@ -14,9 +14,9 @@ export fn load_uw8(module_size: i32) -> i32 {
} else {
copy(0x1e000, 0, module_size);
}
copy(0, 0x3c800, 8);
copy(0, 0x3c200, 8);
let base_start = 0x3c808;
let base_start = 0x3c208;
let dest = 8;
let src = 0x1e001;
@@ -43,17 +43,16 @@ export fn load_uw8(module_size: i32) -> i32 {
}
fn section_size(ptr: i32) -> i32 {
let p = ptr + 1;
let l = 0;
let shift = 0;
let p = ptr;
let l: i32;
let shift: i32;
loop size {
let lazy b = p?0;
let lazy b = (p := p + 1)?0;
l = l | ((b & 127) << shift);
shift = shift + 7;
p = p + 1;
branch_if b & 128: size;
branch_if b >> 7: size;
}
p - ptr + l
p + 1 - ptr + l
}
fn copy_section(dest: i32, src: i32) -> i32 {
@@ -63,10 +62,10 @@ fn copy_section(dest: i32, src: i32) -> i32 {
}
fn copy(dest: i32, src: i32, len: i32) {
if len > 0 {
loop bytes {
if len > 0 {
(dest + (len := len - 1))?0 = (src + len)?0;
branch_if len: bytes
branch bytes;
}
}
}
@@ -84,20 +83,21 @@ export fn uncompress(src_ptr: i32, dest_ptr: i32) -> i32 {
let offset: i32;
let byte: i32;
let i: i32;
loop init_contexts {
i!0x3c000 = 0x800;
branch_if (i := i + 4) < (256 + 1 + 128) * 4: init_contexts
i?0x3c000 = 0x80;
branch_if (i := i + 1) < 256 + 1 + 128: init_contexts;
}
let prev_was_match: i32;
block finished {
loop unpack_loop {
if upkr_bit(0) {
if upkr_bit(256) {
offset = upkr_length(257) - 1;
branch_if !offset: finished
let lazy is_match = upkr_bit(0);
if is_match {
let inline new_offset = if prev_was_match { 1 } else { upkr_bit(256) };
if new_offset {
branch_if !(offset := upkr_length(257) - 1): finished;
}
let length = upkr_length(257 + 64);
loop copy {
@@ -107,15 +107,14 @@ export fn uncompress(src_ptr: i32, dest_ptr: i32) -> i32 {
}
} else {
// literal
i = 0;
byte = 1;
let byte = 1;
loop literal {
byte = (byte << 1) | upkr_bit(byte);
branch_if (i := i + 1) < 8: literal;
branch_if (byte := (byte << 1) | upkr_bit(byte)) < 256: literal;
}
dest_ptr?0 = byte;
dest_ptr = dest_ptr + 1;
}
prev_was_match = is_match;
branch unpack_loop;
}
}
@@ -127,9 +126,8 @@ fn upkr_length(context_index: i32) -> i32 {
let length: i32;
let bit_pos: i32;
loop bits {
if upkr_bit(context_index) {
length = length | (upkr_bit(context_index + 1) << bit_pos);
context_index = context_index + 2;
if upkr_bit(context_index + bit_pos) {
length = length | (upkr_bit(context_index + bit_pos + 32) << bit_pos);
bit_pos = bit_pos + 1;
branch bits;
}
@@ -138,34 +136,29 @@ fn upkr_length(context_index: i32) -> i32 {
}
fn upkr_bit(context_index: i32) -> i32 {
let prob = (context_index * 4)!0x3c000;
let lazy prob = context_index?0x3c000;
loop refill {
if upkr_state < 1<<16 {
if upkr_state < 1<<12 {
upkr_state = (upkr_state << 8) | upkr_src_ptr?0;
upkr_src_ptr = upkr_src_ptr + 1;
branch refill;
}
}
let lazy state_low = upkr_state & 0xfff;
let bit = state_low < prob;
let lazy state_low = upkr_state & 0xff;
let lazy state_hi = upkr_state >> 8;
let lazy bit = state_low < prob;
if bit {
upkr_state = prob * (upkr_state >> 12) + state_low;
prob = prob + ((0x1000 - prob) >> 4);
} else {
upkr_state = (0x1000 - prob) * (upkr_state >> 12) + state_low - prob;
prob = prob - (prob >> 4);
}
upkr_state = state_low + select(bit, prob * state_hi, (0x100 - prob) * state_hi - prob);
(context_index * 4)!0x3c000 = prob;
context_index?0x3c000 = prob + ((7 + bit * 257 - prob) >> 4);
bit
}
start fn unpack_base() {
base_end = uncompress(0, 0x3c800);
base_end = uncompress(0, 0x3c200);
}
data 0 {

View File

@@ -21,7 +21,7 @@ fn main() -> Result<()> {
println!("Compressing platform module");
let platform = uw8_tool::pack(
&platform,
uw8_tool::PackConfig::default().with_compression_level(4),
&uw8_tool::PackConfig::default().with_compression_level(4),
)?;
File::create("bin/platform.uw8")?.write_all(&platform)?;
println!("Platform module: {} bytes", platform.len());

View File

@@ -115,6 +115,26 @@ export fn rectangle(x: f32, y: f32, w: f32, h: f32, col: i32) {
}
}
export fn rectangle_outline(x: f32, y: f32, w: f32, h: f32, col: i32) {
let xl = nearest(x) as i32;
let xr = nearest(x + w) as i32;
let yt = nearest(y) as i32;
let yb = nearest(y + h) as i32;
hline(xl, xr, yt, col);
if yt < yb {
hline(xl, xr, yb - 1, col);
loop y {
setPixel(xl, yt, col);
if xl < xr {
setPixel(xr - 1, yt, col);
}
branch_if (yt := yt + 1) < yb: y;
}
}
}
export fn circle(cx: f32, cy: f32, radius: f32, col: i32) {
let y = clamp(nearest(cy - radius) as i32, 0, 240);
let maxY = clamp(nearest(cy + radius) as i32, 0, 240);
@@ -136,6 +156,58 @@ export fn circle(cx: f32, cy: f32, radius: f32, col: i32) {
}
}
export fn circle_outline(cx: f32, cy: f32, radius: f32, col: i32) {
let prev_w: f32;
let y = clamp(nearest(cy - radius) as i32, -1, 241);
let maxY = clamp(nearest(cy + radius) as i32, -1, 241);
loop lines {
let lazy dy = y as f32 - cy + 0.5;
let inline q = radius * radius - dy * dy;
let w = sqrt(max(0 as f32, q));
let xlp = nearest(cx - prev_w) as i32;
let xl = nearest(cx - w) as i32;
let xrp = nearest(cx + prev_w) as i32;
let xr = nearest(cx + w) as i32;
if w >= prev_w {
if xl < xlp {
hline(xl, xlp, y, col);
} else {
if xl < xr {
setPixel(xl, y, col);
}
}
if xr > xrp {
hline(xrp, xr, y, col);
} else {
if xl < xr {
setPixel(xr - 1, y, col);
}
}
} else {
if xl > xlp {
hline(xlp, xl, y - 1, col);
} else {
if xlp < xrp {
setPixel(xlp, y - 1, col);
}
}
if xr < xrp {
hline(xr, xrp, y - 1, col);
} else {
if xlp < xrp {
setPixel(xrp - 1, y - 1, col);
}
}
}
y = y + 1;
prev_w = w;
branch_if y <= maxY: lines;
}
}
export fn line(x1: f32, y1: f32, x2: f32, y2: f32, col: i32) {
let swapTmp: f32;
if x1 > x2 {
@@ -236,14 +308,47 @@ export fn printChar(char: i32) {
}
}
global mut controlCodeLength = 0;
fn printSingleChar(char: i32) {
if char == 4 | char == 5 {
graphicsText = char == 5;
controlCodeLength?0x12d20 = char;
controlCodeLength = controlCodeLength + 1;
char = 0x12d20?0;
if char < 32 & controlCodeLength < char?0x12d00 {
return;
}
controlCodeLength = 0;
if char == 1 {
drawChar(0x12d20?1);
return;
}
if char == 10 | (!graphicsText & textCursorX >= 320) {
if char == 4 | char == 5 {
graphicsText = char == 5;
textCursorX = 0;
textCursorY = 0;
return;
}
if char == 8 {
textCursorX = textCursorX - 8;
if !graphicsText & textCursorX < 0 {
textCursorX = 320-8;
printSingleChar(11);
}
return;
}
if char == 9 {
if !graphicsText & textCursorX >= 320 {
printChar(0xd0a);
}
textCursorX = textCursorX + 8;
return;
}
if char == 10 {
textCursorY = textCursorY + 8;
if !graphicsText & textCursorY >= 240 {
textCursorY = 240 - 8;
@@ -257,6 +362,78 @@ fn printSingleChar(char: i32) {
return;
}
if char == 11 {
textCursorY = textCursorY - 8;
if !graphicsText & textCursorY < 0 {
textCursorY = 0;
let i = 320 * (240 - 8);
loop scroll_copy {
i!(116 + 320 * 8) = i!116;
branch_if (i := i - 4): scroll_copy;
}
rectangle(0 as f32, 0 as f32, 320 as f32, 8 as f32, bgColor);
}
return;
}
if char == 12 {
cls(bgColor);
return;
}
if char == 13 {
textCursorX = 0;
return;
}
if char == 14 {
bgColor = 0x12d20?1;
return;
}
if char == 15 {
textColor = 0x12d20?1;
return;
}
if char == 24 {
let tmp = textColor;
textColor = bgColor;
bgColor = tmp;
return;
}
if char == 31 {
textCursorX = 0x12d20?1 * (8 - graphicsText * 6);
textCursorY = 0x12d20?2 * (8 - graphicsText * 7);
return;
}
if char < 31 {
return;
}
drawChar(char);
}
data(0x12d00) {
i8(
1, 2, 1, 1, // 0-3
1, 1, 1, 1, // 4-7
1, 1, 1, 1, // 8-11
1, 1, 2, 2, // 12-15,
1, 1, 1, 1, // 16-19,
1, 1, 1, 1, // 20-23,
1, 1, 1, 1, // 24-27,
1, 1, 1, 3 // 28-31
)
}
fn drawChar(char: i32) {
if !graphicsText & textCursorX >= 320 {
printChar(0xd0a);
}
let y: i32;
loop rows {
let bits = (char * 8 + y)?0x13400;

2
release/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/binaries
/build

54
release/make-release Executable file
View File

@@ -0,0 +1,54 @@
#!/bin/bash
set -e
cd $(dirname $0)
# build web runtime
pushd ../web
rm -rf .parcel-cache
yarn parcel build src/index.html
popd
rm -rf build
mkdir -p build/microw8-linux
mkdir build/microw8-macos
mkdir build/microw8-windows
# unzip binaries build by github actions
pushd binaries
unzip -o uw8-linux.zip
chmod +x uw8
mv uw8 ../build/microw8-linux
unzip -o uw8-macos.zip
chmod +x uw8
mv uw8 ../build/microw8-macos
unzip -o uw8-windows.zip
mv uw8.exe ../build/microw8-windows
popd
for dir in build/*; do
mkdir $dir/examples
for example in ../examples/curlywas/*.cwa; do
cp $example $dir/examples
done
for example in ../examples/wat/*.wat; do
cp $example $dir/examples
done
mkdir $dir/carts
for example in $dir/examples/*; do
build/microw8-linux/uw8 pack -l 9 $example $dir/carts/$(basename ${example%.*}).uw8
done
cp ../web/dist/index.html $dir/microw8.html
cp ../README.md $dir
cp ../UNLICENSE $dir
done
VERSION=$(build/microw8-linux/uw8 version)
cd build
tar czf microw8-$VERSION-linux.tgz microw8-linux
tar czf microw8-$VERSION-macos.tgz microw8-macos
zip -r -9 microw8-$VERSION-windows.zip microw8-windows

0
site/.SRCINFO Normal file
View File

View File

@@ -1,125 +1,33 @@
+++
+++
## About
MicroW8 is a WebAssembly based fantasy console inspired by the likes of [TIC-80](https://tic80.com/), [WASM-4](https://wasm4.org/) and [PICO-8](https://www.lexaloffle.com/pico-8.php).
The initial motivation behind MicroW8 was to explore whether there was a way to make WebAssembly viable for size-coding. (Size coding being the art of creating tiny (often <= 256 bytes) graphical effects and games.) The available examples so far are all in this space, however, I very carefully made sure that all design decisions make sense from the point of view of bigger projects as well.
## Specs
* Screen: 320x240, 256 colors, 60Hz
* Modules: Up to 256KB (WASM)
* Memory: 256KB
* Gamepad input (D-Pad + 4 Buttons)
## Examples
* [Fireworks](v0.1pre5#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
* [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
* [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
* [Font & Palette](v0.1pre4#AgKaeeOuwg5gCKvFIeiitEwMpUI2rymEcu+DDB1vMu9uBoufvUxIr4Y5p4Jj2ukoNO4PE7QS5cN1ZyDMCRfSzYIGZxKlN2J6NKEWK7KVPk9wVUgn1Ip+hsMinWgEO8ETKfPuHoIa4kjI+ULFOMad7vd3rt/lh1Vy9w+R2MXG/7T61d3c7C6KY+eQNS0eW3ys4iU8R6SycuWZuuZ2Sg3Qxp826s+Kt+2qBojpzNOSoyFqyrVyYMTKEkSl0BZOj59Cs1hPm5bq0F1MmVhGAzMhW9V4YeAe): Just a simple viewer for the default font and 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.
* [CircleWorm](v0.1pre2#AQp7AXkCAX8CfUEgEA0DQCABskEEspUiAkECspUgALJBiCeylSIDQQWylJIQAEEBspJBoAGylCACQQOylSADQQSylJIQAEEBspJB+ACylCADQRGylCACQQKylJIQAEECspJBELKUIAFBAmxBP2oQEiABQQFqIgFBP0gNAAsL) (126 bytes uncompressed): Just a test for the circle fill function.
## Versions
* [v0.1pre1](v0.1pre1)
* [v0.1pre2](v0.1pre2)
* [v0.1pre3](v0.1pre3)
* [v0.1pre4](v0.1pre4)
## Spec
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.
After calling `upd` MicroW8 will display the 320x240 8bpp framebuffer located
at offset 120 in memory with the 32bpp palette located at 0x13000.
The memory has to be imported as `"env" "memory"` and has a maximum size of 256kb (4 pages).
Other imports provided by the platform, also all in module `env`:
* `fn acos(f32) -> f32`
* `fn asin(f32) -> f32`
* `fn atan(f32) -> f32`
* `fn atan2(f32, f32) -> f32`
* `fn cos(f32) -> f32`
* `fn exp(f32, f32) -> f32`
* `fn log(f32) -> f32`
* `fn sin(f32) -> f32`
* `fn tan(f32) -> f32`
* `fn pow(f32) -> f32`
* `fn fmod(f32, f32) -> f32`
* `fn random() -> i32`
* `fn randomf() -> f32`
* `fn randomSeed(i32)`
* `fn cls(color: i32)`
* `fn setPixel(x: i32, y: i32, color: i32)`
* `fn getPixel(x: i32, y: i32) -> i32`
* `fn hline(left: i32, right: i32, y: i32, color: i32)`
* `fn rectangle(x1: f32, y1: f32, x2: f32, y2: f32, color: i32)`
* `fn circle(cx: f32, cy: f32, radius: f32, color: i32)`
* `fn line(x1: f32, y1: f32, x2: f32, y2: f32, color: i32)`
* `fn time() -> f32`
* `fn isButtonPressed(btn: i32) -> i32`
* `fn isButtonTriggered(btn: i32) -> i32`
* `fn printChar(char: i32)`
* `fn printString(ptr: i32)`
* `fn printInt(num: i32)`
* `fn setTextColor(color: i32)`
* `fn setBackgroundColor(color: i32)`
* `fn setCursorPosition(x: i32, y: i32)`
### Memory map
```
00000-00040: user memory
00040-00044: time since module start in ms
00044-0004c: gamepad state
0004c-00078: reserved
00078-12c78: frame buffer
12c78-13000: reserved
13000-13400: palette
13400-13c00: font
13c00-14000: reserved
14000-40000: user memory
```
## `.uw8` format
The first byte of the file specifies the format version:
#### Format version `00`:
This file is simply a standard WebAssembly module
#### Format version `01`:
The rest of this file is the same as a WebAssembly
module with the 8 byte header removed. This module
can leave out sections which are then taken from
a base module provided by MicroW8.
You can generate this base module yourself using
`uw8-tool`. As a quick summary, it provides all function
types with up to 5 parameters (i32 or f32) where the
`f32` parameters always preceed the `i32` parameters.
Then it includes all imports that MicroW8 provides,
a function section with a single function of type
`() -> void` and an export section that exports
the first function in the file under the name `upd`.
#### Format version `02`:
Same as version `01` except everything after the first byte is compressed
using a [custom LZ compression scheme](https://github.com/exoticorn/upkr).
## Tooling
The [Web Assembly Binary Toolkit](https://github.com/WebAssembly/wabt) includes
a few useful tools, eg. `wat2wasm` to compile the WebAssemby text format to binary
wasm and `wasm2wat` to disassemble wasm binaries.
If you don't like the look of the `.wat` text format, you might want to take a
look at [CurlyWas](https://github.com/exoticorn/curlywas), a curly-braces infix
syntax for WebAssembly.
Once you have a size-efficient `.wasm` file, you can use [uw8-tool](https://github.com/exoticorn/microw8/tree/master/uw8-tool)
(currently included in the MicroW8 repository) to strip off sections of the
WebAssembly module that are provided by the MicroW8 platform.
Writing code for MicroW8 in C, Rust, AssemblyScript etc. should absolutely
possible but no examples are provided, yet.
## Examples
* [Skip Ahead](v0.1pre4#AiP94km33q5hBFbBcKl2W1ZMY67wHByUz15/qRHIr71Dvq8bTVux/DXZzSTKU5MufRXxnGEn6mOul4dil/5WLT1Y+Y/4TGd1E3U4rQaxlpcKObxc5YLdr4xo6jyBND96a569LoECv6Q4EIqxJi+3QPE21r/++js41XsBLr44X/O2XpP3kHQva3B8o6duD5WkLCf7PhsxjTBeND1ADIaJe05JCrBim18RPY9VYFmltqQ9gVzbxE/ZpojR/GJ4aCbx0hRn73RPhpA7Cd4jk0AVSOeRx+7kuHrLzpaeqeYWK5pYd/4Rv+8=) (230 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
* [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
* [Font & Palette](v0.1pre4#AgKaeeOuwg5gCKvFIeiitEwMpUI2rymEcu+DDB1vMu9uBoufvUxIr4Y5p4Jj2ukoNO4PE7QS5cN1ZyDMCRfSzYIGZxKlN2J6NKEWK7KVPk9wVUgn1Ip+hsMinWgEO8ETKfPuHoIa4kjI+ULFOMad7vd3rt/lh1Vy9w+R2MXG/7T61d3c7C6KY+eQNS0eW3ys4iU8R6SycuWZuuZ2Sg3Qxp826s+Kt+2qBojpzNOSoyFqyrVyYMTKEkSl0BZOj59Cs1hPm5bq0F1MmVhGAzMhW9V4YeAe): Just a simple viewer for the default font and 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.
* [CircleWorm](v0.1pre2#AQp7AXkCAX8CfUEgEA0DQCABskEEspUiAkECspUgALJBiCeylSIDQQWylJIQAEEBspJBoAGylCACQQOylSADQQSylJIQAEEBspJB+ACylCADQRGylCACQQKylJIQAEECspJBELKUIAFBAmxBP2oQEiABQQFqIgFBP0gNAAsL) (126 bytes uncompressed): Just a test for the circle fill function.
* [v0.1pre5](v0.1pre5)

390
site/content/docs.md Normal file
View File

@@ -0,0 +1,390 @@
+++
title = "Docs"
description = "Docs"
+++
# Overview
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.
After calling `upd` MicroW8 will display the 320x240 8bpp framebuffer located
at offset 120 in memory with the 32bpp palette located at 0x13000.
The memory has to be imported as `env` `memory` and has a maximum size of 256kb (4 pages).
# Memory map
```
00000-00040: user memory
00040-00044: time since module start in ms
00044-0004c: gamepad state
0004c-00078: reserved
00078-12c78: frame buffer
12c78-13000: reserved
13000-13400: palette
13400-13c00: font
13c00-14000: reserved
14000-40000: user memory
```
# API
All API functions are found in the `env` module.
## Math
These all do what you'd expect them to. All angles are in radians.
### fn asin(x: f32) -> f32
Returns the arcsine of `x`.
### fn acos(x: f32) -> f32
Returns the arccosine of `x`.
### fn atan(f32) -> f32
Returns the arctangent of `x`.
### fn atan2(y: f32, y: f32) -> f32
Returns the angle between the point `(x, y)` and the positive x-axis.
### fn sin(angle: f32) -> f32
Returns the sine of `angle`.
### fn tan(angle: f32) -> f32
Returns the tangent of `angle`.
### fn cos(angle: f32) -> f32
Returns the cosine of `angle`.
### fn exp(x: f32) -> f32
Returns `e^x`.
### fn log(x: f32) -> f32
Returns the natural logarithmus of `x`. Ie. `e^log(x) == x`.
### fn pow(x: f32, y: f32) -> f32
Returns `x^y`.
### fn fmod(x: f32, y: f32) -> f32
Returns `x` modulo `y`, ie. `x - floor(x / y) * y`. This means the sign of the result of `fmod` is the same as `y`.
## Random
MicroW8 provides a pretty good PRNG, namely xorshift64*. It is initialized to a constant seed at each startup, so if you
want to vary the random sequence you'll need to provide a seed yourself.
### fn random() -> i32
Returns a (pseudo-)random 32bit integer.
### fn randomf() -> f32
Returns a (pseudo-)random float equally distributed in `[0,1)`.
### fn randomSeed(seed: i32)
Seeds the PRNG with the given seed. The seed function is reasonably strong so that you can use
```
randomSeed(index);
random()
```
as a cheap random-access PRNG (aka noise function).
## 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 palette can be changed by writing 32bit rgba colors to addresses 0x13000-0x13400.
The drawing functions are sub-pixel accurate where applicable (line, circle). Pixel centers lie halfway between integer
coordinates. Ie. the top-left pixel covers the area `0,0 - 1,1`, with `0.5,0.5` being the pixel center.
### fn cls(color: i32)
Clears the screen to the given color index. Also sets the text cursor to `0, 0` and disables graphical text mode.
### fn setPixel(x: i32, y: i32, color: i32)
Sets the pixel at `x, y` to the given color index.
### fn getPixel(x: i32, y: i32) -> i32
Returns the color index at `x, y`. Returns `0` if the given coordinates are outside the screen.
### fn hline(left: i32, right: i32, y: i32, color: i32)
Fills the horizontal line `[left, right), y` with the given color index.
### fn rectangle(x: f32, y: f32, w: f32, h: f32, color: i32)
Fills the rectangle `x,y - x+w,y+h` with the given color index.
(Sets all pixels where the pixel center lies inside the rectangle.)
### fn circle(cx: f32, cy: f32, radius: f32, color: i32)
Fills the circle at `cx, cy` and with `radius` with the given color index.
(Sets all pixels where the pixel center lies inside the circle.)
### fn rectangle_outline(x: f32, y: f32, w: f32, h: f32, color: i32)
Draws a one pixel outline on the inside of the given rectangle.
(Draws the outermost pixels that are still inside the rectangle area.)
### fn circle_outline(cx: f32, cy: f32, radius: f32, color: i32)
Draws a one pixel outline on the inside of the given circle.
(Draws the outermost pixels that are still inside the circle area.)
### fn line(x1: f32, y1: f32, x2: f32, y2: f32, color: i32)
Draws a line from `x1,y1` to `x2,y2` in the given color index.
## Input
MicroW8 provides input from a gamepad with one D-Pad and 4 buttons, or a keyboard emulation thereof.
The buttons are numbered
| Button | Keyboard | Index |
| ------ | ----------- | ----- |
| Up | Arrow-Up | 0 |
| Down | Arrow-Down | 1 |
| Left | Arrow-Left | 2 |
| Right | Arrow-Right | 3 |
| A | Z | 4 |
| B | X | 5 |
| X | A | 6 |
| Y | S | 7 |
In addition to using the API functions below, the gamepad state can also be read as a bitfield of
pressed buttons at address 0x44. 0x48 holds the buttons that were pressed last frame.
### fn isButtonPressed(btn: i32) -> i32
Returns whether the buttons with the given index is pressed this frame.
### fn isButtonTriggered(btn: i32) -> i32
Returns whether the given button is newly pressed this frame.
### fn time() -> f32
Returns the time in seconds since the start of the cart.
The integer time in milliseconds can also be read at address 0x40.
## Text output
The default font can be seen [here](../v0.1pre4#AgKaeeOuwg5gCKvFIeiitEwMpUI2rymEcu+DDB1vMu9uBoufvUxIr4Y5p4Jj2ukoNO4PE7QS5cN1ZyDMCRfSzYIGZxKlN2J6NKEWK7KVPk9wVUgn1Ip+hsMinWgEO8ETKfPuHoIa4kjI+ULFOMad7vd3rt/lh1Vy9w+R2MXG/7T61d3c7C6KY+eQNS0eW3ys4iU8R6SycuWZuuZ2Sg3Qxp826s+Kt+2qBojpzNOSoyFqyrVyYMTKEkSl0BZOj59Cs1hPm5bq0F1MmVhGAzMhW9V4YeAe).
The font can be changed by writing 1bpp 8x8 characters to addresses 0x13400-0x13c00.
All text printing is done at the cursor position, which is advanced after printing each character.
The cursor is not visible.
Text printing can operate in two modes - normal and graphics. After startup and after `cls()` normal mode is active.
### Normal mode
In normal mode, text printing is constrained to an 8x8 character grid. Setting the cursor position to `2,3` will start printing at pixel coordinates `16,24`.
When printing characters, the full 8x8 pixels are painted with the text and background colors according to the character graphics in the font.
When moving/printing past the left or right border the cursor will automatically wrap to the previous/next line. When moving/printing past the upper/lower border, the screen will be scrolled down/up 8 pixels, filling the fresh line with the background color.
### Graphics mode
In graphics mode, text can be printed to any pixel position, the cursor position is set in pixel coordinates.
When printing characters only the foreground pixels are set, the background is "transparent".
Moving/printing past any border does not cause any special operation, the cursor just goes off-screen.
### Control chars
Characters 0-31 are control characters and don't print by default. They take the next 0-2 following characters as parameters.
Avoid the reserved control chars, they are currently NOPs but their behavior can change in later MicroW8 versions.
| Code | Parameters | Operation |
| ----- | ---------- | ------------------------------------ |
| 0 | - | Nop |
| 1 | char | Print char (including control chars) |
| 2-3 | - | Reserved |
| 4 | - | Switch to normal mode |
| 5 | - | Switch to graphics mode |
| 6-7 | - | Reserved |
| 8 | - | Move cursor left |
| 9 | - | Move cursor right |
| 10 | - | Move cursor down |
| 11 | - | Move cursor up |
| 12 | - | do `cls(background_color)` |
| 13 | - | Move cursor to the left border |
| 14 | color | Set the background color |
| 15 | color | Set the text color |
| 16-23 | - | Reserved |
| 24 | - | Swap text/background colors |
| 25-30 | - | Reserved |
| 31 | x, y | Set cursor position (*) |
(*) In graphics mode, the x coordinate is doubled when using control char 31 to be able to cover the whole screen with one byte.
### fn printChar(char: i32)
Prints the character in the lower 8 bits of `char`. If the upper 24 bits are non-zero, right-shifts `char` by 8 bits and loops back to the beginning.
### fn printString(ptr: i32)
Prints the zero-terminated string at the given memory address.
### fn printInt(num: i32)
Prints `num` as a signed decimal number.
### fn setTextColor(color: i32)
Sets the text color.
### fn setBackgroundColor(color: i32)
Sets the background color.
### fn setCursorPosition(x: i32, y: i32)
Sets the cursor position. In normal mode `x` and `y` are multiplied by 8 to get the pixel position, in graphics mode they are used as is.
# The `uw8` tool
The `uw8` tool included in the MicroW8 download includes a number of useful tools for developing MicroW8 carts. For small productions written in
wat or CurlyWas you don't need anything apart from `uw8` and a text editor of your choice.
## `uw8 run`
Usage:
`uw8 run [<options>] <file>`
Runs `<file>` which can be a binary WebAssembly module, an `.uw8` cart, a wat (WebAssembly text format) source file or a [CurlyWas](https://github.com/exoticorn/curlywas) source file.
Options:
* `-w`, `--watch`: Reloads the given file every time it changes on disk.
* `-p`, `--pack`: Pack the file into an `.uw8` cart before running it and print the resulting size.
* `-u`, `--uncompressed`: Use the uncompressed `uw8` format for packing.
* `-l LEVEL`, `--level LEVEL`: Compression level (0-9). Higher compression levels are really slow.
* `-o FILE`, `--output FILE`: Write the loaded and optionally packed cart back to disk.
## `uw8 pack`
Usage:
`uw8 pack [<options>] <infile> <outfile>`
Packs the WebAssembly module or text file, or [CurlyWas](https://github.com/exoticorn/curlywas) source file into a `.uw8` cart.
Options:
* `-u`, `--uncompressed`: Use the uncompressed `uw8` format for packing.
* `-l LEVEL`, `--level LEVEL`: Compression level (0-9). Higher compression levels are really slow.
## `uw8 filter-exports`
Usage:
`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`.
When compiling C code (or Rust, zig or others) to WebAssembly, you end up with a few exported global variables that are used for managing the heap and C stack, even if the code doesn't actually use those features. You can use this command to automatically remove them and gain a few bytes. See the C, Rust and zig examples in the MicroW8 repository.
# Other useful tools
The [Web Assembly Binary Toolkit](https://github.com/WebAssembly/wabt) includes
a few useful tools, eg. `wat2wasm` to compile the WebAssemby text format to binary
wasm and `wasm2wat` to disassemble wasm binaries.
[Binaryen](https://github.com/WebAssembly/binaryen) includes `wasm-opt` which enable additional optimizations over what LLVM (the backend that is used by most compilers that target WebAssembly) can do.
# Distribution
The classical distribution option is just to put the `.uw8` cart into a zip file, let people run it themselves, either in the `uw8` tool or in the web runtime.
If you want to go this way, you might consider including `microw8.html` in your download. It's specifically designed to be a small (~10KB at the moment), self-contained HTML file for just this reason. That way, anyone who has downloaded you production can run it, even when offline, provided they have a modern web browser at hand. Also, should future versions of MicroW8 ever introduce any kind of incompatibilities, they'd still have a compatible version right there without hunting arround for an old version.
## Base64 encoded link
For small productions (<= 1024 bytes), when you load them in the web runtime, the URL is automatically updated to include the cart as base64 encoded data. You can just give that URL to others for them to run your prod.
## url parameter
Another option is to put the cart on a webserver and add `#url=url/to/the/cart.uw8` to the end of the web runtime URL. ([Like this](../v0.1pre5#url=../uw8/skipahead.uw8))
If the cart and the web runtime are on different domains, you'll have to make sure that [CORS header](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#the_http_response_headers) are enabled for the cart, otherwise the web runtime won't be able to load it.
Feel free to put the web runtime on your own server if it makes sense to you, its [license](https://unlicense.org/) allows you to do anything you want with it.
## `.html` + `.uw8`
At startup the web runtime will try to load a cart in the same directory as the `.html` file. If the URL of the web runtime ends in `.html` it will try to load a cart with the same name and the extension `.uw8`. If the URL of the web runtime ends in a `/` it will try to load a `cart.uw8` at that location.
So, you could for example serve the web runtime as `https://example.org/mytunnel.html` and the cart as `https://example.org/mytunnel.uw8` and send people to the HTML page to run the cart. Or you could put them up as `https://example.org/mytunnel/index.html` and `https://example.org/mytunnel/cart.uw8` and send people to `https://example.org/mytunnel`.
If a cart is found and loaded in this way, the load button is hidden.
## Itch.io
The above `.html` + `.uw8` option works great on [Itch.io](https://itch.io) as well. Put these two files into a zip archive:
* `index.html`: a copy of the web runtime (`microw8.html` in the MicroW8 download)
* `index.uw8`: Your game cart
Upload the zip file to itch.io and make sure to set the embedded viewport size to exactly (!) 640x480 pixel. At that exact size the web runtime hides everything except for the MicroW8 screen.
If instead you actually *want* to display the border around the screen and the byte size you can try a size of about 720x620.
[See here for an example upload.](https://exoticorn.itch.io/skipahead)
# `.uw8` format
The first byte of the file specifies the format version:
## Format version `00`:
This file is simply a standard WebAssembly module
## Format version `01`:
The rest of this file is the same as a WebAssembly
module with the 8 byte header removed. This module
can leave out sections which are then taken from
a base module provided by MicroW8.
You can generate this base module yourself using
`uw8-tool`. As a quick summary, it provides all function
types with up to 5 parameters (i32 or f32) where the
`f32` parameters always preceed the `i32` parameters.
Then it includes all imports that MicroW8 provides,
a function section with a single function of type
`() -> void` and an export section that exports
the first function in the file under the name `upd`.
## Format version `02`:
Same as version `01` except everything after the first byte is compressed
using a [custom LZ compression scheme](https://github.com/exoticorn/upkr).

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -2,12 +2,16 @@
{% block hero %}
<div>
<section>
<h1 class="text-center heading-text">A WebAssembly based sizecoding platform</h1>
<h1 class="text-center heading-text">A WebAssembly based fantasy console</h1>
</section>
<a href="v0.1pre4">
<div class="demonstration-gif" style="width:640px;height:512px;background-color:black"></div>
<a href="v0.1pre5">
<img class="demonstration-gif" style="width:640px;height:480px;image-rendering:pixelated" src="img/technotunnel.png"></img>
</a>
</div>
<div class="explore-more text"
onclick="document.getElementById('features').scrollIntoView({behavior: 'smooth'})">
Explore More ⇩
</div>
{% endblock hero %}
{% block footer %}

View File

@@ -17,16 +17,26 @@ fn main() -> Result<()> {
let mut args = Arguments::from_env();
match args.subcommand()?.as_ref().map(|s| s.as_str()) {
Some("version") => {
println!("{}", env!("CARGO_PKG_VERSION"));
Ok(())
}
Some("run") => run(args),
Some("pack") => pack(args),
Some("filter-exports") => filter_exports(args),
Some("help") | None => {
println!("uw8 {}", env!("CARGO_PKG_VERSION"));
println!();
println!("Usage:");
println!(" uw8 run [-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 filter-exports <in-wasm> <out-wasm>");
Ok(())
}
Some(other) => {
eprintln!("Unknown command '{}'", other);
process::exit(1);
}
None => {
println!("Usage:");
println!(" uw8 run [-w] [-p] [-c] [-l] [-o <out-file>] <file>");
Ok(())
}
}
}
@@ -34,13 +44,17 @@ fn run(mut args: Arguments) -> Result<()> {
let watch_mode = args.contains(["-w", "--watch"]);
let mut config = Config::default();
config.pack = args.contains(["-p", "--pack"]);
if args.contains(["-c", "--compress"]) {
config.compression = Some(2);
if args.contains(["-p", "--pack"]) {
let mut pack = uw8_tool::PackConfig::default();
if args.contains(["-u", "--uncompressed"]) {
pack = pack.uncompressed();
}
if let Some(level) = args.opt_value_from_str(["-l", "--level"])? {
config.compression = Some(level);
pack = pack.with_compression_level(level);
}
config.pack = Some(pack);
}
if let Some(path) =
@@ -60,7 +74,7 @@ fn run(mut args: Arguments) -> Result<()> {
watcher.watch(&filename, notify::RecursiveMode::NonRecursive)?;
}
if let Err(err) = load_cart(&filename, &mut uw8, &config) {
if let Err(err) = start_cart(&filename, &mut uw8, &config) {
eprintln!("Load error: {}", err);
if !watch_mode {
exit(1);
@@ -70,7 +84,7 @@ fn run(mut args: Arguments) -> Result<()> {
while uw8.is_open() {
match rx.try_recv() {
Ok(DebouncedEvent::Create(_) | DebouncedEvent::Write(_)) => {
if let Err(err) = load_cart(&filename, &mut uw8, &config) {
if let Err(err) = start_cart(&filename, &mut uw8, &config) {
eprintln!("Load error: {}", err);
}
}
@@ -78,7 +92,12 @@ fn run(mut args: Arguments) -> Result<()> {
_ => (),
}
uw8.run_frame()?;
if let Err(err) = uw8.run_frame() {
eprintln!("Runtime error: {}", err);
if !watch_mode {
exit(1);
}
}
}
Ok(())
@@ -86,12 +105,11 @@ fn run(mut args: Arguments) -> Result<()> {
#[derive(Default)]
struct Config {
pack: bool,
compression: Option<u8>,
pack: Option<uw8_tool::PackConfig>,
output_path: Option<PathBuf>,
}
fn load_cart(filename: &Path, uw8: &mut MicroW8, config: &Config) -> Result<()> {
fn load_cart(filename: &Path, pack: &Option<uw8_tool::PackConfig>) -> Result<Vec<u8>> {
let mut cart = vec![];
File::open(filename)?.read_to_end(&mut cart)?;
@@ -104,15 +122,17 @@ fn load_cart(filename: &Path, uw8: &mut MicroW8, config: &Config) -> Result<()>
};
}
if config.pack {
let mut pack_config = uw8_tool::PackConfig::default();
if let Some(level) = config.compression {
pack_config = pack_config.with_compression_level(level);
}
if let Some(pack_config) = pack {
cart = uw8_tool::pack(&cart, pack_config)?;
println!("packed size: {} bytes", cart.len());
}
Ok(cart)
}
fn start_cart(filename: &Path, uw8: &mut MicroW8, config: &Config) -> Result<()> {
let cart = load_cart(filename, &config.pack)?;
if let Some(ref path) = config.output_path {
File::create(path)?.write_all(&cart)?;
}
@@ -124,3 +144,34 @@ fn load_cart(filename: &Path, uw8: &mut MicroW8, config: &Config) -> Result<()>
Ok(())
}
}
fn pack(mut args: Arguments) -> Result<()> {
let mut pack_config = uw8_tool::PackConfig::default();
if args.contains(["-u", "--uncompressed"]) {
pack_config = pack_config.uncompressed();
}
if let Some(level) = args.opt_value_from_str(["-l", "--level"])? {
pack_config = pack_config.with_compression_level(level);
}
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 cart = load_cart(&in_file, &Some(pack_config))?;
File::create(out_file)?.write_all(&cart)?;
Ok(())
}
fn filter_exports(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::filter_exports(&in_file, &out_file)?;
Ok(())
}

110
uw8-tool/Cargo.lock generated
View File

@@ -4,9 +4,9 @@ version = 3
[[package]]
name = "anyhow"
version = "1.0.45"
version = "1.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee10e43ae4a853c0a3591d4e2ada1719e553be18199d9da9d4a83f5927c2f5c7"
checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3"
[[package]]
name = "autocfg"
@@ -56,6 +56,21 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "heck"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "id-arena"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
[[package]]
name = "lazy_static"
version = "1.4.0"
@@ -70,9 +85,18 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.108"
version = "0.2.112"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119"
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
[[package]]
name = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
[[package]]
name = "num-traits"
@@ -101,6 +125,24 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468"
[[package]]
name = "proc-macro2"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "sacabase"
version = "2.0.0"
@@ -110,6 +152,17 @@ dependencies = [
"num-traits",
]
[[package]]
name = "syn"
version = "1.0.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecb2e6da8ee5eb9a61068762a32fa9619cc591ceb055b3687f4cd4051ec2e06b"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "time"
version = "0.1.44"
@@ -121,10 +174,22 @@ dependencies = [
"winapi",
]
[[package]]
name = "unicode-segmentation"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "upkr"
version = "0.1.0"
source = "git+https://github.com/exoticorn/upkr.git?rev=7d280bd#7d280bd533b037d579b6cdf12fb69046eb5c2c91"
source = "git+https://github.com/exoticorn/upkr.git?rev=2e7983fc#2e7983fc650788d98da2eecef2d16f63e849e4a0"
dependencies = [
"anyhow",
"cdivsufsort",
@@ -140,8 +205,35 @@ dependencies = [
"pbr",
"pico-args",
"upkr",
"walrus",
"wasm-encoder",
"wasmparser",
"wasmparser 0.81.0",
]
[[package]]
name = "walrus"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eb08e48cde54c05f363d984bb54ce374f49e242def9468d2e1b6c2372d291f8"
dependencies = [
"anyhow",
"id-arena",
"leb128",
"log",
"walrus-macro",
"wasmparser 0.77.0",
]
[[package]]
name = "walrus-macro"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a6e5bd22c71e77d60140b0bd5be56155a37e5bd14e24f5f87298040d0cc40d7"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
@@ -159,6 +251,12 @@ dependencies = [
"leb128",
]
[[package]]
name = "wasmparser"
version = "0.77.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b35c86d22e720a07d954ebbed772d01180501afe7d03d464f413bb5f8914a8d6"
[[package]]
name = "wasmparser"
version = "0.81.0"

View File

@@ -8,7 +8,8 @@ edition = "2021"
[dependencies]
wasmparser = "0.81"
wasm-encoder = "0.8"
walrus = "0.19"
anyhow = "1"
pico-args = "0.4"
upkr = { git = "https://github.com/exoticorn/upkr.git", rev = "7d280bd" }
upkr = { git = "https://github.com/exoticorn/upkr.git", rev = "2e7983fc" }
pbr = "1"

View File

@@ -89,6 +89,9 @@ impl BaseModule {
add_function(&mut functions, &type_map, "setBackgroundColor", &[I32], None);
add_function(&mut functions, &type_map, "setCursorPosition", &[I32, I32], None);
add_function(&mut functions, &type_map, "rectangle_outline", &[F32, F32, F32, F32, I32], None);
add_function(&mut functions, &type_map, "circle_outline", &[F32, F32, F32, I32], None);
for i in functions.len()..64 {
add_function(
&mut functions,

View File

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

View File

@@ -1,5 +1,7 @@
mod base_module;
mod pack;
mod filter_exports;
pub use base_module::BaseModule;
pub use pack::{pack, pack_file, unpack, unpack_file, PackConfig};
pub use filter_exports::filter_exports;

View File

@@ -15,18 +15,23 @@ fn main() -> Result<()> {
}
"pack" => {
let mut config = uw8_tool::PackConfig::default();
if args.contains(["-c", "--compress"]) {
config = config.with_compression();
if args.contains(["-u", "--uncompressed"]) {
config = config.uncompressed();
}
let source: PathBuf = args.free_from_str()?;
let dest: PathBuf = args.free_from_str()?;
uw8_tool::pack_file(&source, &dest, config)?;
uw8_tool::pack_file(&source, &dest, &config)?;
}
"unpack" => {
let source: PathBuf = args.free_from_str()?;
let dest: PathBuf = args.free_from_str()?;
uw8_tool::unpack_file(&source, &dest)?;
}
"filter-exports" => {
let source: PathBuf = args.free_from_str()?;
let dest: PathBuf = args.free_from_str()?;
uw8_tool::filter_exports(&source, &dest)?;
}
_ => {
eprintln!("Unknown subcommand '{}'", cmd);
print_help();
@@ -44,6 +49,7 @@ fn print_help() {
"Usage:
uw8-tool make-base <version>
uw8-tool pack <wasm file> <uw8 file>
uw8-tool unpack <uw8 file> <wasm file>"
uw8-tool unpack <uw8 file> <wasm file>
uw8-tool filter-exports <wasm file> <wasm file>"
);
}

View File

@@ -18,8 +18,8 @@ pub struct PackConfig {
}
impl PackConfig {
pub fn with_compression(mut self) -> Self {
self.compression = Some(2);
pub fn uncompressed(mut self) -> Self {
self.compression = None;
self
}
@@ -31,11 +31,11 @@ impl PackConfig {
impl Default for PackConfig {
fn default() -> PackConfig {
PackConfig { compression: None }
PackConfig { compression: Some(2) }
}
}
pub fn pack_file(source: &Path, dest: &Path, config: PackConfig) -> Result<()> {
pub fn pack_file(source: &Path, dest: &Path, config: &PackConfig) -> Result<()> {
let mut source_data = vec![];
File::open(source)?.read_to_end(&mut source_data)?;
@@ -45,7 +45,7 @@ pub fn pack_file(source: &Path, dest: &Path, config: PackConfig) -> Result<()> {
Ok(())
}
pub fn pack(data: &[u8], config: PackConfig) -> Result<Vec<u8>> {
pub fn pack(data: &[u8], config: &PackConfig) -> Result<Vec<u8>> {
let base = BaseModule::for_format_version(1)?;
let parsed_module = ParsedModule::parse(data)?;
@@ -66,6 +66,7 @@ pub fn pack(data: &[u8], config: PackConfig) -> Result<Vec<u8>> {
}),
));
pb.finish();
std::io::stdout().flush()?;
Ok(uw8)
} else {
let mut uw8 = vec![1];

View File

@@ -9,10 +9,13 @@
</style>
</head>
<body>
<div id="uw8">
<a href="https://exoticorn.github.io/microw8">MicroW8</a> 0.1.0
</div>
<div id="centered">
<canvas id="screen" width="320" height="240"></canvas>
<div id="message"></div>
<button id="cartButton">Load cart...</button>
<button id="cartButton" style="visibility:hidden">Load cart...</button>
</div>
<div id="footer">
<a href="http://unlicense.org/" ref="license">Unlicense</a>

View File

@@ -221,7 +221,12 @@ async function runModule(data, keepUrl) {
}
async function runModuleFromURL(url, keepUrl) {
runModule(await (await fetch(url)).arrayBuffer(), keepUrl);
let response = await fetch(url);
let type = response.headers.get('Content-Type');
if(type && type.includes('html')) {
throw false;
}
runModule(await response.arrayBuffer(), keepUrl);
}
function runModuleFromHash() {
@@ -238,9 +243,11 @@ function runModuleFromHash() {
}
window.onhashchange = runModuleFromHash;
runModuleFromHash();
document.getElementById('cartButton').onclick = () => {
let setupLoad = () => {
let loadButton = document.getElementById('cartButton');
loadButton.style = '';
loadButton.onclick = () => {
let fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = '.wasm,.uw8,application/wasm';
@@ -250,4 +257,42 @@ document.getElementById('cartButton').onclick = () => {
}
};
fileInput.click();
};
screen.ondragover = (e) => {
e.preventDefault();
};
screen.ondrop = (e) => {
let files = e.dataTransfer && e.dataTransfer.files;
if(files && files.length == 1) {
e.preventDefault();
runModuleFromURL(URL.createObjectURL(e.dataTransfer.files[0]));
}
}
runModuleFromHash();
};
let location = window.location;
if(location.hash.length != 0) {
setupLoad();
} else {
(async () => {
let url = location.href;
if(url.endsWith('.html')) {
url = url.slice(0, url.length - 4) + 'uw8';
} else {
if(!url.endsWith('/')) {
url += '/';
}
url += 'cart.uw8';
}
try {
await runModuleFromURL(url, true);
} catch(e) {
setupLoad();
}
})();
}

View File

@@ -16,15 +16,19 @@ body {
grid-template-rows: 1fr 0fr;
}
#uw8 {
position: absolute;
}
#uw8 a {
font-size: 130%;
}
#centered {
align-self: center;
justify-self: center;
}
.footer {
color: #202420;
}
a {
color: #303630;
}
@@ -69,7 +73,7 @@ button:active {
background-color: #504450;
}
@media (min-width: 680px) and (min-height: 560px) {
@media (min-width: 680px) and (min-height: 620px) {
#screen {
width: 640px;
height: 480px;
@@ -82,3 +86,16 @@ button:active {
height: 720px;
}
}
@media (width:640px) and (height:480px) {
#screen {
width: 640px;
height: 480px;
border: 0;
margin: 0;
}
body {
overflow: hidden;
}
}