mirror of
https://github.com/exoticorn/microw8.git
synced 2026-01-20 19:26:43 +01:00
Compare commits
19 Commits
gpu-window
...
v0.2.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 9e19ff1761 | |||
| 5d85aeac09 | |||
| 30eb953d5d | |||
| abdf780533 | |||
| fc05a54e2c | |||
| b33099c828 | |||
| d478d3ad49 | |||
| 502852e59a | |||
| 5efa8b3465 | |||
| daf2a02cd8 | |||
| 8d5374a867 | |||
| 142b6a4c15 | |||
| 877fceb089 | |||
| f0ba0f2b99 | |||
| 760664eb77 | |||
| 465e66ff4b | |||
| e4579d81bc | |||
| 1f5042059c | |||
| 499bb02f2c |
2511
Cargo.lock
generated
2511
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
22
Cargo.toml
22
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "uw8"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@@ -11,19 +11,21 @@ native = ["wasmtime", "uw8-window", "cpal", "rubato" ]
|
||||
browser = ["warp", "tokio", "tokio-stream", "webbrowser"]
|
||||
|
||||
[dependencies]
|
||||
wasmtime = { version = "0.37.0", optional = true }
|
||||
wasmtime = { version = "5.0.0", optional = true }
|
||||
anyhow = "1"
|
||||
env_logger = "0.10"
|
||||
log = "0.4"
|
||||
uw8-window = { path = "uw8-window", optional = true }
|
||||
notify = "4"
|
||||
pico-args = "0.4"
|
||||
notify = "5"
|
||||
pico-args = "0.5"
|
||||
curlywas = { git = "https://github.com/exoticorn/curlywas.git", rev = "0e7ea50" }
|
||||
wat = "1"
|
||||
uw8-tool = { path = "uw8-tool" }
|
||||
same-file = "1"
|
||||
warp = { version = "0.3.2", optional = true }
|
||||
tokio = { version = "1.17.0", features = ["sync", "rt"], optional = true }
|
||||
tokio-stream = { version = "0.1.8", features = ["sync"], optional = true }
|
||||
webbrowser = { version = "0.6.0", optional = true }
|
||||
warp = { version = "0.3.3", optional = true }
|
||||
tokio = { version = "1.24.0", features = ["sync", "rt"], optional = true }
|
||||
tokio-stream = { version = "0.1.11", features = ["sync"], optional = true }
|
||||
webbrowser = { version = "0.8.6", optional = true }
|
||||
ansi_term = "0.12.1"
|
||||
cpal = { version = "0.13.5", optional = true }
|
||||
rubato = { version = "0.11.0", optional = true }
|
||||
cpal = { version = "0.14.2", optional = true }
|
||||
rubato = { version = "0.12.0", optional = true }
|
||||
|
||||
27
README.md
27
README.md
@@ -15,9 +15,9 @@ See [here](https://exoticorn.github.io/microw8/) for more information and docs.
|
||||
|
||||
## Downloads
|
||||
|
||||
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.1.2/microw8-0.1.2-linux.tgz)
|
||||
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.1.2/microw8-0.1.2-macos.tgz)
|
||||
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.1.2/microw8-0.1.2-windows.zip)
|
||||
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.2.2/microw8-0.2.2-linux.tgz)
|
||||
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.2.2/microw8-0.2.2-macos.tgz)
|
||||
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.2.2/microw8-0.2.2-windows.zip)
|
||||
|
||||
The download includes
|
||||
|
||||
@@ -43,6 +43,27 @@ Options:
|
||||
-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.
|
||||
|
||||
when using the native runtime:
|
||||
|
||||
-m, --no-audio : Disable audio, also reduces cpu load a bit
|
||||
--no-gpu : Force old cpu-only window code
|
||||
--filter FILTER : Select an upscale filter at startup
|
||||
--fullscreen : Start in fullscreen mode
|
||||
|
||||
Note that the cpu-only window does not support fullscreen nor upscale filters.
|
||||
|
||||
Unless --no-gpu is given, uw8 will first try to open a gpu accelerated window, falling back to the old cpu-only window if that fails.
|
||||
Therefore you should rarely need to manually pass --no-gpu. If you prefer the old pixel doubling look to the now default crt filter,
|
||||
you can just pass "--filter nearest" or "--filter 1".
|
||||
|
||||
The upscale filter options are:
|
||||
1, nearest : Anti-aliased nearest filter
|
||||
2, fast_crt : Very simple, cheap crt filter, not very good below a window size of 960x720
|
||||
3, ss_crt : Super sampled crt filter, a little more demanding on the GPU but scales well to smaller window sizes
|
||||
4, chromatic_crt : Variant of fast_crt with a slight offset of the three color dots of a pixel, still pretty cheap
|
||||
5, auto_crt (default) : ss_crt below 960x720, chromatic_crt otherwise
|
||||
|
||||
You can switch the upscale filter at any time using the keys 1-5. You can toggle fullscreen with F.
|
||||
|
||||
uw8 pack [<options>] <infile> <outfile>
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ import "env.rectangle_outline" fn rectangle_outline(f32, f32, f32, f32, i32);
|
||||
import "env.circle_outline" fn circle_outline(f32, f32, f32, i32);
|
||||
import "env.exp" fn exp(f32) -> f32;
|
||||
import "env.playNote" fn playNote(i32, i32);
|
||||
import "env.sndGes" fn sndGes(i32) -> f32;
|
||||
|
||||
const TIME_MS = 0x40;
|
||||
const GAMEPAD = 0x44;
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
(import "env" "circle_outline" (func $circle_outline (param f32) (param f32) (param f32) (param i32)))
|
||||
(import "env" "exp" (func $exp (param f32) (result f32)))
|
||||
(import "env" "playNote" (func $playNote (param i32) (param i32)))
|
||||
(import "env" "sndGes" (func $sndGes (param i32) (result f32)))
|
||||
|
||||
;; to use defines, include this file with a preprocessor
|
||||
;; like gpp (https://logological.org/gpp).
|
||||
|
||||
186
platform/Cargo.lock
generated
186
platform/Cargo.lock
generated
@@ -110,9 +110,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.3.0"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836"
|
||||
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
@@ -151,32 +151,39 @@ dependencies = [
|
||||
"anyhow",
|
||||
"ariadne",
|
||||
"chumsky",
|
||||
"pico-args",
|
||||
"pico-args 0.4.2",
|
||||
"wasm-encoder 0.10.0",
|
||||
"wasmparser 0.83.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fallible_collections"
|
||||
version = "0.4.4"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52db5973b6a19247baf19b30f41c23a1bfffc2e9ce0a5db2f60e3cd5dc8895f7"
|
||||
checksum = "3f57ccc32870366ae684be48b32a1a2e196f98a42a9b4361fe77e13fd4a34755"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.22"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
|
||||
checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crc32fast",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.3"
|
||||
@@ -190,9 +197,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.11.2"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
dependencies = [
|
||||
"ahash 0.7.6",
|
||||
]
|
||||
@@ -212,6 +219,26 @@ version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
@@ -225,17 +252,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.112"
|
||||
name = "lexopt"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
|
||||
checksum = "478ee9e62aaeaf5b140bd4138753d1f109765488581444218d3ddda43234f3e8"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.139"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||
|
||||
[[package]]
|
||||
name = "lodepng"
|
||||
version = "3.4.7"
|
||||
version = "3.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24844d5c0b922ddd52fb5bf0964a4c7f8e799a946ec01bb463771eb04fc1a323"
|
||||
checksum = "f0ad39f75bbaa4b10bb6f2316543632a8046a5bcf9c785488d79720b21f044f8"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"fallible_collections",
|
||||
"flate2",
|
||||
"libc",
|
||||
@@ -253,12 +287,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.4.4"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
|
||||
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -288,12 +321,24 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
||||
|
||||
[[package]]
|
||||
name = "pico-args"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468"
|
||||
|
||||
[[package]]
|
||||
name = "pico-args"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
|
||||
|
||||
[[package]]
|
||||
name = "platform"
|
||||
version = "0.1.0"
|
||||
@@ -330,9 +375,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rgb"
|
||||
version = "0.8.31"
|
||||
version = "0.8.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a374af9a0e5fdcdd98c1c7b64f05004f9ea2555b6c75f211daa81268a3c50f1"
|
||||
checksum = "3603b7d71ca82644f79b5a06d1220e9a58ede60bd32255f698cb1af8838b8db3"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
@@ -357,6 +402,26 @@ dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.43"
|
||||
@@ -376,6 +441,36 @@ dependencies = [
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.9.0"
|
||||
@@ -390,13 +485,24 @@ checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "upkr"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/exoticorn/upkr.git?rev=2e7983fc#2e7983fc650788d98da2eecef2d16f63e849e4a0"
|
||||
version = "0.2.1"
|
||||
source = "git+https://github.com/exoticorn/upkr.git?rev=080db40d0088bbee2bdf3c5c75288ac7853d6b7a#080db40d0088bbee2bdf3c5c75288ac7853d6b7a"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cdivsufsort",
|
||||
"pbr",
|
||||
"pico-args",
|
||||
"lexopt",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -405,11 +511,11 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"pbr",
|
||||
"pico-args",
|
||||
"pico-args 0.5.0",
|
||||
"upkr",
|
||||
"walrus",
|
||||
"wasm-encoder 0.8.0",
|
||||
"wasmparser 0.81.0",
|
||||
"wasm-encoder 0.22.0",
|
||||
"wasmparser 0.99.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -452,18 +558,18 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.8.0"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db0c351632e46cc06a58a696a6c11e4cf90cad4b9f8f07a0b59128d616c29bb0"
|
||||
checksum = "aa9d9bf45fc46f71c407837c9b30b1e874197f2dc357588430b21e5017d290ab"
|
||||
dependencies = [
|
||||
"leb128",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.10.0"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa9d9bf45fc46f71c407837c9b30b1e874197f2dc357588430b21e5017d290ab"
|
||||
checksum = "ef126be0e14bdf355ac1a8b41afc89195289e5c7179f80118e3abddb472f0810"
|
||||
dependencies = [
|
||||
"leb128",
|
||||
]
|
||||
@@ -474,18 +580,22 @@ version = "0.77.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b35c86d22e720a07d954ebbed772d01180501afe7d03d464f413bb5f8914a8d6"
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.81.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98930446519f63d00a836efdc22f67766ceae8dbcc1571379f2bcabc6b2b9abc"
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.83.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a"
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.99.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ef3b717afc67f848f412d4f02c127dd3e35a0eecd58c684580414df4fde01d3"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
||||
@@ -9,4 +9,4 @@ edition = "2021"
|
||||
curlywas = { git="https://github.com/exoticorn/curlywas.git", rev="0e7ea50" }
|
||||
uw8-tool = { path="../uw8-tool" }
|
||||
anyhow = "1"
|
||||
lodepng = "3.4"
|
||||
lodepng = "3.7.2"
|
||||
Binary file not shown.
Binary file not shown.
@@ -10,7 +10,7 @@ const GesState.Size = GesState.Filter + 8*4;
|
||||
const GesStateOffset = 32;
|
||||
const GesBufferOffset = 32 + GesState.Size;
|
||||
|
||||
export fn gesSnd(t: i32) -> f32 {
|
||||
export fn sndGes(t: i32) -> f32 {
|
||||
let baseAddr = 0!0x12c78;
|
||||
if !(t & 127) {
|
||||
let i: i32;
|
||||
|
||||
@@ -372,16 +372,7 @@ export fn printChar(char: i32) {
|
||||
global mut controlCodeLength = 0;
|
||||
|
||||
fn printSingleChar(char: i32) {
|
||||
if char >= 4 & char <= 6 {
|
||||
outputChannel = char - 4;
|
||||
if !outputChannel {
|
||||
textCursorX = 0;
|
||||
textCursorY = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if outputChannel >= 2 {
|
||||
if outputChannel >= 2 & (char < 4 | char > 6) {
|
||||
logChar(char);
|
||||
return;
|
||||
}
|
||||
@@ -399,6 +390,15 @@ fn printSingleChar(char: i32) {
|
||||
return;
|
||||
}
|
||||
|
||||
if char >= 4 & char <= 6 {
|
||||
outputChannel = char - 4;
|
||||
if !outputChannel {
|
||||
textCursorX = 0;
|
||||
textCursorY = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if char == 7 {
|
||||
80?0 = 80?0 ^ 2;
|
||||
return;
|
||||
|
||||
@@ -14,6 +14,8 @@ The initial motivation behind MicroW8 was to explore whether there was a way to
|
||||
* Memory: 256KB
|
||||
* Gamepad input (D-Pad + 4 Buttons)
|
||||
|
||||
For detailed [documentation see here](docs).
|
||||
|
||||
## Examples
|
||||
* [Skip Ahead](v0.2.0#AgVfq24KI2Ok2o8qVtPYj27fSuGnfeSKgbOkIOsaEQMov8TDYQ6UjdjwkZrYcM1i9alo4/+Bhm1PRFEa0YHJlJAk/PGoc2K41rejv9ZSqJqIHNjr7cappqhOR2jT+jk+0b0+U6hO+geRCTP2aufWs7L+f/Z27NFY8LKlqPSv+C6Rd6+ohoKi6sYl5Kcrlf1cyTinV7jTTnmbcXWVDBA5rRKxAGMUTDS8rHxqSztRITOaQVP1pSdYgi/BDdOJOxSOIkeaId84S+Ycls5na7EgwSfVIpgqF+tcfkUecb8t2mQrXA7pyKrh/wzHn5N6Oe5aOgmzY2YpTIct) (249 bytes): A port of my [TIC-80 256byte game](http://tic80.com/play?cart=1735) from LoveByte'21, now with sound
|
||||
* [Fireworks](v0.2.0#AgwvgP+M59snqjl4CMKw5sqm1Zw9yJCbSviMjeLUdHus2a3yl/a99+uiBeqZgP/2jqSjrLjRk73COMM6OSLpsxK8ugT1kuk/q4hQUqqPpGozHoa0laulzGGcahzdfdJsYaK1sIdeIYS9M5PnJx/Wk9H+PvWEPy2Zvv7I6IW7Fg==) (127 bytes): Some fireworks to welcome 2022.
|
||||
@@ -29,6 +31,33 @@ Examplers for older versions:
|
||||
|
||||
## Versions
|
||||
|
||||
### v0.2.2
|
||||
|
||||
* [Web runtime](v0.2.2)
|
||||
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.2.2/microw8-0.2.2-linux.tgz)
|
||||
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.2.2/microw8-0.2.2-macos.tgz)
|
||||
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.2.2/microw8-0.2.2-windows.zip)
|
||||
|
||||
Changes:
|
||||
|
||||
* call `start` function after loading cart if the cart exports one
|
||||
* fix `sndGes` having the wrong name and not being included in the auto imports
|
||||
* fix control codes 4-6 (change text output mode) being invoked when used as parameters in other control sequences
|
||||
* only open browser window once a cart was compiled sucessfully when running with `-b`
|
||||
|
||||
### v0.2.1
|
||||
|
||||
* [Web runtime](v0.2.1)
|
||||
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.2.1/microw8-0.2.1-linux.tgz)
|
||||
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.2.1/microw8-0.2.1-macos.tgz)
|
||||
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.2.1/microw8-0.2.1-windows.zip)
|
||||
|
||||
Changes:
|
||||
|
||||
* new gpu accelerated renderer with (optional) crt filter
|
||||
* optimized `hline` function, a big speed-up when drawing large filled circles or rectangles
|
||||
* print fractional size of packed `uw8` cart
|
||||
|
||||
### v0.2.0
|
||||
|
||||
* [Web runtime](v0.2.0)
|
||||
|
||||
@@ -12,6 +12,9 @@ at offset 120 in memory with the 32bpp palette located at 0x13000.
|
||||
|
||||
The memory has to be imported as `env` `memory` and has a maximum size of 256kb (4 pages).
|
||||
|
||||
If the module exports a function called `start`, it will be called once after the module is
|
||||
loaded.
|
||||
|
||||
# Memory map
|
||||
|
||||
```
|
||||
@@ -436,6 +439,30 @@ and execution of the cart is stopped. Defaults to 30 (0.5s)
|
||||
* `-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.
|
||||
|
||||
when using the native runtime:
|
||||
|
||||
* `-m`, `--no-audio`: Disable audio, also reduces cpu load a bit
|
||||
* `--no-gpu`: Force old cpu-only window code
|
||||
* `--filter FILTER`: Select an upscale filter at startup
|
||||
* `--fullscreen`: Start in fullscreen mode
|
||||
|
||||
Note that the cpu-only window does not support fullscreen nor upscale filters.
|
||||
|
||||
Unless --no-gpu is given, uw8 will first try to open a gpu accelerated window, falling back to the old cpu-only window if that fails.
|
||||
Therefore you should rarely need to manually pass --no-gpu. If you prefer the old pixel doubling look to the now default crt filter,
|
||||
you can just pass `--filter nearest` or `--filter 1`.
|
||||
|
||||
The upscale filter options are:
|
||||
```
|
||||
1, nearest : Anti-aliased nearest filter
|
||||
2, fast_crt : Very simple, cheap crt filter, not very good below a window size of 960x720
|
||||
3, ss_crt : Super sampled crt filter, a little more demanding on the GPU but scales well to smaller window sizes
|
||||
4, chromatic_crt : Variant of fast_crt with a slight offset of the three color dots of a pixel, still pretty cheap
|
||||
5, auto_crt (default) : ss_crt below 960x720, chromatic_crt otherwise
|
||||
```
|
||||
|
||||
You can switch the upscale filter at any time using the keys 1-5. You can toggle fullscreen with F.
|
||||
|
||||
## `uw8 pack`
|
||||
|
||||
Usage:
|
||||
|
||||
1
site/static/v0.2.1/index.html
Normal file
1
site/static/v0.2.1/index.html
Normal file
File diff suppressed because one or more lines are too long
1
site/static/v0.2.2/index.html
Normal file
1
site/static/v0.2.2/index.html
Normal file
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@
|
||||
<section>
|
||||
<h1 class="text-center heading-text">A WebAssembly based fantasy console</h1>
|
||||
</section>
|
||||
<a href="v0.2.0">
|
||||
<a href="v0.2.2">
|
||||
<img class="demonstration-gif" style="width:640px;height:480px;image-rendering:pixelated" src="img/technotunnel.png"></img>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use notify::{DebouncedEvent, RecommendedWatcher, Watcher};
|
||||
use std::{collections::BTreeSet, path::PathBuf, sync::mpsc, time::Duration};
|
||||
use notify::{Event, EventKind, RecommendedWatcher, Watcher};
|
||||
use std::{collections::BTreeSet, path::PathBuf, sync::mpsc};
|
||||
|
||||
pub struct FileWatcher {
|
||||
watcher: RecommendedWatcher,
|
||||
watched_files: BTreeSet<PathBuf>,
|
||||
directories: BTreeSet<PathBuf>,
|
||||
rx: mpsc::Receiver<DebouncedEvent>,
|
||||
rx: mpsc::Receiver<Event>,
|
||||
}
|
||||
|
||||
impl FileWatcher {
|
||||
pub fn new() -> Result<FileWatcher> {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let watcher = notify::watcher(tx, Duration::from_millis(100))?;
|
||||
let watcher = notify::recommended_watcher(move |res| match res {
|
||||
Ok(event) => _ = tx.send(event),
|
||||
Err(err) => eprintln!("Error watching for file changes: {err}"),
|
||||
})?;
|
||||
Ok(FileWatcher {
|
||||
watcher,
|
||||
watched_files: BTreeSet::new(),
|
||||
@@ -36,9 +39,10 @@ impl FileWatcher {
|
||||
}
|
||||
|
||||
pub fn poll_changed_file(&self) -> Result<Option<PathBuf>> {
|
||||
let event = self.rx.try_recv();
|
||||
match event {
|
||||
Ok(DebouncedEvent::Create(path) | DebouncedEvent::Write(path)) => {
|
||||
match self.rx.try_recv() {
|
||||
Ok(event) => match event.kind {
|
||||
EventKind::Create(_) | EventKind::Modify(_) => {
|
||||
for path in event.paths {
|
||||
let handle = same_file::Handle::from_path(&path)?;
|
||||
for file in &self.watched_files {
|
||||
if handle == same_file::Handle::from_path(file)? {
|
||||
@@ -46,6 +50,9 @@ impl FileWatcher {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Err(mpsc::TryRecvError::Disconnected) => bail!("File watcher disconnected"),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
22
src/main.rs
22
src/main.rs
@@ -13,6 +13,7 @@ use uw8::RunWebServer;
|
||||
use uw8::Runtime;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
env_logger::Builder::from_env(env_logger::Env::default()).init();
|
||||
let mut args = Arguments::from_env();
|
||||
|
||||
// try to enable ansi support in win10 cmd shell
|
||||
@@ -35,7 +36,7 @@ fn main() -> Result<()> {
|
||||
println!();
|
||||
println!("Usage:");
|
||||
#[cfg(any(feature = "native", feature = "browser"))]
|
||||
println!(" uw8 run [-t/--timeout <frames>] [--no-gpu] [--b/--browser] [-w/--watch] [-p/--pack] [-u/--uncompressed] [-l/--level] [-o/--output <out-file>] <file>");
|
||||
println!(" uw8 run [-t/--timeout <frames>] [--b/--browser] [-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 unpack <in-file> <out-file>");
|
||||
println!(" uw8 compile [-d/--debug] <in-file> <out-file>");
|
||||
@@ -54,8 +55,6 @@ fn run(mut args: Arguments) -> Result<()> {
|
||||
let watch_mode = args.contains(["-w", "--watch"]);
|
||||
#[allow(unused)]
|
||||
let timeout: Option<u32> = args.opt_value_from_str(["-t", "--timeout"])?;
|
||||
#[allow(unused)]
|
||||
let gpu = !args.contains("--no-gpu");
|
||||
|
||||
let mut config = Config::default();
|
||||
if args.contains(["-p", "--pack"]) {
|
||||
@@ -82,7 +81,17 @@ fn run(mut args: Arguments) -> Result<()> {
|
||||
#[cfg(not(feature = "native"))]
|
||||
let run_browser = args.contains(["-b", "--browser"]) || true;
|
||||
|
||||
let disable_audio = args.contains(["-m", "--disable-audio"]);
|
||||
#[allow(unused)]
|
||||
let disable_audio = args.contains(["-m", "--no-audio"]);
|
||||
|
||||
#[cfg(feature = "native")]
|
||||
let window_config = {
|
||||
let mut config = uw8_window::WindowConfig::default();
|
||||
if !run_browser {
|
||||
config.parse_arguments(&mut args);
|
||||
}
|
||||
config
|
||||
};
|
||||
|
||||
let filename = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
|
||||
|
||||
@@ -95,7 +104,7 @@ fn run(mut args: Arguments) -> Result<()> {
|
||||
unimplemented!();
|
||||
#[cfg(feature = "native")]
|
||||
{
|
||||
let mut microw8 = MicroW8::new(timeout, gpu)?;
|
||||
let mut microw8 = MicroW8::new(timeout, window_config)?;
|
||||
if disable_audio {
|
||||
microw8.disable_audio();
|
||||
}
|
||||
@@ -167,7 +176,8 @@ fn load_cart(filename: &Path, config: &Config) -> (Result<Vec<u8>>, Vec<PathBuf>
|
||||
if let Some(ref pack_config) = config.pack {
|
||||
cart = uw8_tool::pack(&cart, pack_config)?;
|
||||
println!(
|
||||
"\npacked size: {:.2} bytes",
|
||||
"\npacked size: {} bytes ({:.2})",
|
||||
cart.len(),
|
||||
uw8_tool::compressed_size(&cart)
|
||||
);
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -5,23 +5,20 @@ use std::{thread, time::Instant};
|
||||
use anyhow::{anyhow, Result};
|
||||
use cpal::traits::*;
|
||||
use rubato::Resampler;
|
||||
use uw8_window::{Window, WindowConfig};
|
||||
use wasmtime::{
|
||||
Engine, GlobalType, Memory, MemoryType, Module, Mutability, Store, TypedFunc, ValType,
|
||||
};
|
||||
|
||||
pub struct MicroW8 {
|
||||
tx: mpsc::SyncSender<Option<UW8Instance>>,
|
||||
rx: mpsc::Receiver<UIEvent>,
|
||||
window: Window,
|
||||
stream: Option<cpal::Stream>,
|
||||
engine: Engine,
|
||||
loader_module: Module,
|
||||
disable_audio: bool,
|
||||
module_data: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
enum UIEvent {
|
||||
Error(Result<()>),
|
||||
Reset,
|
||||
timeout: u32,
|
||||
instance: Option<UW8Instance>,
|
||||
}
|
||||
|
||||
struct UW8Instance {
|
||||
@@ -48,7 +45,7 @@ struct UW8WatchDog {
|
||||
}
|
||||
|
||||
impl MicroW8 {
|
||||
pub fn new(timeout: Option<u32>, gpu: bool) -> Result<MicroW8> {
|
||||
pub fn new(timeout: Option<u32>, window_config: WindowConfig) -> Result<MicroW8> {
|
||||
let mut config = wasmtime::Config::new();
|
||||
config.cranelift_opt_level(wasmtime::OptLevel::Speed);
|
||||
if timeout.is_some() {
|
||||
@@ -59,39 +56,17 @@ impl MicroW8 {
|
||||
let loader_module =
|
||||
wasmtime::Module::new(&engine, include_bytes!("../platform/bin/loader.wasm"))?;
|
||||
|
||||
let (to_ui_tx, to_ui_rx) = mpsc::sync_channel(2);
|
||||
let (from_ui_tx, from_ui_rx) = mpsc::sync_channel(1);
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let mut state = State {
|
||||
instance: None,
|
||||
timeout: timeout.unwrap_or(0),
|
||||
};
|
||||
|
||||
uw8_window::run(gpu, move |framebuffer, gamepad, reset| {
|
||||
while let Ok(instance) = to_ui_rx.try_recv() {
|
||||
state.instance = instance;
|
||||
}
|
||||
|
||||
if reset {
|
||||
from_ui_tx.send(UIEvent::Reset).unwrap();
|
||||
}
|
||||
|
||||
state.run_frame(framebuffer, gamepad).unwrap_or_else(|err| {
|
||||
from_ui_tx.send(UIEvent::Error(Err(err))).unwrap();
|
||||
Instant::now()
|
||||
})
|
||||
});
|
||||
});
|
||||
let window = Window::new(window_config)?;
|
||||
|
||||
Ok(MicroW8 {
|
||||
tx: to_ui_tx,
|
||||
rx: from_ui_rx,
|
||||
window,
|
||||
stream: None,
|
||||
engine,
|
||||
loader_module,
|
||||
disable_audio: false,
|
||||
module_data: None,
|
||||
timeout: timeout.unwrap_or(0),
|
||||
instance: None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -102,12 +77,12 @@ impl MicroW8 {
|
||||
|
||||
impl super::Runtime for MicroW8 {
|
||||
fn is_open(&self) -> bool {
|
||||
true
|
||||
self.window.is_open()
|
||||
}
|
||||
|
||||
fn load(&mut self, module_data: &[u8]) -> Result<()> {
|
||||
self.stream = None;
|
||||
self.tx.send(None)?;
|
||||
self.instance = None;
|
||||
|
||||
let mut store = wasmtime::Store::new(&self.engine, ());
|
||||
store.set_epoch_deadline(60);
|
||||
@@ -118,7 +93,7 @@ impl super::Runtime for MicroW8 {
|
||||
linker.define("env", "memory", memory)?;
|
||||
|
||||
let loader_instance = linker.instantiate(&mut store, &self.loader_module)?;
|
||||
let load_uw8 = loader_instance.get_typed_func::<i32, i32, _>(&mut store, "load_uw8")?;
|
||||
let load_uw8 = loader_instance.get_typed_func::<i32, i32>(&mut store, "load_uw8")?;
|
||||
|
||||
let platform_data = include_bytes!("../platform/bin/platform.uw8");
|
||||
memory.data_mut(&mut store)[..platform_data.len()].copy_from_slice(platform_data);
|
||||
@@ -156,8 +131,12 @@ impl super::Runtime for MicroW8 {
|
||||
}
|
||||
|
||||
let instance = linker.instantiate(&mut store, &module)?;
|
||||
let end_frame = platform_instance.get_typed_func::<(), (), _>(&mut store, "endFrame")?;
|
||||
let update = instance.get_typed_func::<(), (), _>(&mut store, "upd").ok();
|
||||
let end_frame = platform_instance.get_typed_func::<(), ()>(&mut store, "endFrame")?;
|
||||
let update = instance.get_typed_func::<(), ()>(&mut store, "upd").ok();
|
||||
|
||||
if let Some(start) = instance.get_typed_func::<(), ()>(&mut store, "start").ok() {
|
||||
start.call(&mut store, ())?;
|
||||
}
|
||||
|
||||
let (sound_tx, stream) = if self.disable_audio {
|
||||
(None, None)
|
||||
@@ -174,7 +153,7 @@ impl super::Runtime for MicroW8 {
|
||||
}
|
||||
};
|
||||
|
||||
self.tx.send(Some(UW8Instance {
|
||||
self.instance = Some(UW8Instance {
|
||||
store,
|
||||
memory,
|
||||
end_frame,
|
||||
@@ -182,56 +161,36 @@ impl super::Runtime for MicroW8 {
|
||||
start_time: Instant::now(),
|
||||
watchdog,
|
||||
sound_tx,
|
||||
}))?;
|
||||
});
|
||||
self.stream = stream;
|
||||
self.module_data = Some(module_data.into());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_frame(&mut self) -> Result<()> {
|
||||
if let Ok(event) = self.rx.try_recv() {
|
||||
match event {
|
||||
UIEvent::Error(err) => err,
|
||||
UIEvent::Reset => {
|
||||
let input = self.window.begin_frame();
|
||||
|
||||
if input.reset {
|
||||
if let Some(module_data) = self.module_data.take() {
|
||||
self.load(&module_data)
|
||||
} else {
|
||||
Ok(())
|
||||
self.load(&module_data)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct State {
|
||||
instance: Option<UW8Instance>,
|
||||
timeout: u32,
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn run_frame(
|
||||
&mut self,
|
||||
framebuffer: &mut dyn uw8_window::Framebuffer,
|
||||
gamepad: u32,
|
||||
) -> Result<Instant> {
|
||||
let now = Instant::now();
|
||||
let mut result = Ok(now);
|
||||
let mut result = Ok(());
|
||||
if let Some(mut instance) = self.instance.take() {
|
||||
let time = (now - instance.start_time).as_millis() as i32;
|
||||
{
|
||||
let next_frame = {
|
||||
let offset = ((time as u32 as i64 * 6) % 100 - 50) / 6;
|
||||
let max = now + Duration::from_millis(17);
|
||||
let next_center = now + Duration::from_millis((16 - offset) as u64);
|
||||
result = Ok(next_center.min(max));
|
||||
}
|
||||
next_center.min(max)
|
||||
};
|
||||
|
||||
{
|
||||
let mem = instance.memory.data_mut(&mut instance.store);
|
||||
mem[64..68].copy_from_slice(&time.to_le_bytes());
|
||||
mem[68..72].copy_from_slice(&gamepad.to_le_bytes());
|
||||
mem[68..72].copy_from_slice(&input.gamepads);
|
||||
}
|
||||
|
||||
instance.store.set_epoch_deadline(self.timeout as u64);
|
||||
@@ -255,14 +214,16 @@ impl State {
|
||||
|
||||
let framebuffer_mem = &memory[120..(120 + 320 * 240)];
|
||||
let palette_mem = &memory[0x13000..];
|
||||
framebuffer.update(framebuffer_mem, palette_mem);
|
||||
self.window
|
||||
.end_frame(framebuffer_mem, palette_mem, next_frame);
|
||||
|
||||
if result.is_ok() {
|
||||
self.instance = Some(instance);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result?)
|
||||
result?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,8 +317,8 @@ fn init_sound(
|
||||
let instance = linker.instantiate(&mut store, module)?;
|
||||
|
||||
let snd = instance
|
||||
.get_typed_func::<(i32,), f32, _>(&mut store, "snd")
|
||||
.or_else(|_| platform_instance.get_typed_func::<(i32,), f32, _>(&mut store, "gesSnd"))?;
|
||||
.get_typed_func::<(i32,), f32>(&mut store, "snd")
|
||||
.or_else(|_| platform_instance.get_typed_func::<(i32,), f32>(&mut store, "sndGes"))?;
|
||||
|
||||
let host = cpal::default_host();
|
||||
let device = host
|
||||
@@ -385,7 +346,7 @@ fn init_sound(
|
||||
.ok_or_else(|| anyhow!("Could not find float output config"))?;
|
||||
let sample_rate = cpal::SampleRate(44100)
|
||||
.max(config.min_sample_rate())
|
||||
.max(config.max_sample_rate());
|
||||
.min(config.max_sample_rate());
|
||||
let config = config.with_sample_rate(sample_rate);
|
||||
let buffer_size = match *config.buffer_size() {
|
||||
cpal::SupportedBufferSize::Unknown => cpal::BufferSize::Default,
|
||||
|
||||
@@ -11,6 +11,7 @@ use warp::{http::Response, Filter};
|
||||
pub struct RunWebServer {
|
||||
cart: Arc<Mutex<Vec<u8>>>,
|
||||
tx: broadcast::Sender<()>,
|
||||
socket_addr: SocketAddr,
|
||||
}
|
||||
|
||||
impl RunWebServer {
|
||||
@@ -18,8 +19,13 @@ impl RunWebServer {
|
||||
let cart = Arc::new(Mutex::new(Vec::new()));
|
||||
let (tx, _) = broadcast::channel(1);
|
||||
|
||||
let socket_addr = "127.0.0.1:3030"
|
||||
.parse::<SocketAddr>()
|
||||
.expect("Failed to parse socket address");
|
||||
|
||||
let server_cart = cart.clone();
|
||||
let server_tx = tx.clone();
|
||||
let server_addr = socket_addr.clone();
|
||||
thread::spawn(move || {
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_io()
|
||||
@@ -47,24 +53,26 @@ impl RunWebServer {
|
||||
warp::sse::reply(warp::sse::keep_alive().stream(event_stream(&server_tx)))
|
||||
});
|
||||
|
||||
let socket_addr = "127.0.0.1:3030"
|
||||
.parse::<SocketAddr>()
|
||||
.expect("Failed to parse socket address");
|
||||
|
||||
let server_future = warp::serve(html.or(cart).or(events)).bind(socket_addr);
|
||||
println!("Point browser at http://{}", socket_addr);
|
||||
let _ignore_result = webbrowser::open(&format!("http://{}", socket_addr));
|
||||
let server_future = warp::serve(html.or(cart).or(events)).bind(server_addr);
|
||||
server_future.await
|
||||
});
|
||||
});
|
||||
|
||||
RunWebServer { cart, tx }
|
||||
RunWebServer {
|
||||
cart,
|
||||
tx,
|
||||
socket_addr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl super::Runtime for RunWebServer {
|
||||
fn load(&mut self, module_data: &[u8]) -> Result<()> {
|
||||
if let Ok(mut lock) = self.cart.lock() {
|
||||
if lock.is_empty() && !module_data.is_empty() {
|
||||
println!("Point browser at http://{}", self.socket_addr);
|
||||
let _ignore_result = webbrowser::open(&format!("http://{}", self.socket_addr));
|
||||
}
|
||||
lock.clear();
|
||||
lock.extend_from_slice(module_data);
|
||||
}
|
||||
|
||||
13
test.cwa
13
test.cwa
@@ -1,13 +0,0 @@
|
||||
import "env.memory" memory(4);
|
||||
import "env.printString" fn print(i32);
|
||||
|
||||
export fn upd() {
|
||||
}
|
||||
|
||||
start fn start() {
|
||||
print(0);
|
||||
}
|
||||
|
||||
data 0 {
|
||||
"Press " i8(0xe0) " and " i8(0xe1) " to adjust, " i8(0xcc) " to commit." i8(0)
|
||||
}
|
||||
5
test/start_fn.cwa
Normal file
5
test/start_fn.cwa
Normal file
@@ -0,0 +1,5 @@
|
||||
include "../examples/include/microw8-api.cwa"
|
||||
|
||||
export fn start() {
|
||||
printChar('Test');
|
||||
}
|
||||
13
test/text_modes.cwa
Normal file
13
test/text_modes.cwa
Normal file
@@ -0,0 +1,13 @@
|
||||
include "../examples/include/microw8-api.cwa"
|
||||
|
||||
export fn upd() {
|
||||
printString(USER_MEM);
|
||||
}
|
||||
|
||||
data USER_MEM {
|
||||
i8(12, 31, 5, 6) "Text mode"
|
||||
i8(5, 31, 4, 5) "Graphics mode"
|
||||
i8(6) "Console output\nSecond line\n"
|
||||
i8(4, 31, 4, 12) "Back to text mode"
|
||||
i8(0)
|
||||
}
|
||||
136
uw8-tool/Cargo.lock
generated
136
uw8-tool/Cargo.lock
generated
@@ -56,6 +56,21 @@ dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.3"
|
||||
@@ -71,6 +86,26 @@ version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
@@ -83,6 +118,12 @@ version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
|
||||
|
||||
[[package]]
|
||||
name = "lexopt"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "478ee9e62aaeaf5b140bd4138753d1f109765488581444218d3ddda43234f3e8"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.112"
|
||||
@@ -120,10 +161,16 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pico-args"
|
||||
version = "0.4.2"
|
||||
name = "percent-encoding"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468"
|
||||
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
||||
|
||||
[[package]]
|
||||
name = "pico-args"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
@@ -163,6 +210,26 @@ dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.44"
|
||||
@@ -174,6 +241,36 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.8.0"
|
||||
@@ -188,13 +285,24 @@ checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "upkr"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/exoticorn/upkr.git?rev=d93aec186c9fb91d962c488682a2db125c61306c#d93aec186c9fb91d962c488682a2db125c61306c"
|
||||
version = "0.2.1"
|
||||
source = "git+https://github.com/exoticorn/upkr.git?rev=080db40d0088bbee2bdf3c5c75288ac7853d6b7a#080db40d0088bbee2bdf3c5c75288ac7853d6b7a"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cdivsufsort",
|
||||
"pbr",
|
||||
"pico-args",
|
||||
"lexopt",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -207,7 +315,7 @@ dependencies = [
|
||||
"upkr",
|
||||
"walrus",
|
||||
"wasm-encoder",
|
||||
"wasmparser 0.81.0",
|
||||
"wasmparser 0.99.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -244,9 +352,9 @@ checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.8.0"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db0c351632e46cc06a58a696a6c11e4cf90cad4b9f8f07a0b59128d616c29bb0"
|
||||
checksum = "ef126be0e14bdf355ac1a8b41afc89195289e5c7179f80118e3abddb472f0810"
|
||||
dependencies = [
|
||||
"leb128",
|
||||
]
|
||||
@@ -259,9 +367,13 @@ checksum = "b35c86d22e720a07d954ebbed772d01180501afe7d03d464f413bb5f8914a8d6"
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.81.0"
|
||||
version = "0.99.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98930446519f63d00a836efdc22f67766ceae8dbcc1571379f2bcabc6b2b9abc"
|
||||
checksum = "9ef3b717afc67f848f412d4f02c127dd3e35a0eecd58c684580414df4fde01d3"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
|
||||
@@ -6,10 +6,10 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
wasmparser = "0.81"
|
||||
wasm-encoder = "0.8"
|
||||
wasmparser = "0.99"
|
||||
wasm-encoder = "0.22"
|
||||
walrus = "0.19"
|
||||
anyhow = "1"
|
||||
pico-args = "0.4"
|
||||
upkr = { git = "https://github.com/exoticorn/upkr.git", rev = "d93aec186c9fb91d962c488682a2db125c61306c" }
|
||||
pico-args = "0.5"
|
||||
upkr = { git = "https://github.com/exoticorn/upkr.git", rev = "080db40d0088bbee2bdf3c5c75288ac7853d6b7a" }
|
||||
pbr = "1"
|
||||
@@ -3,7 +3,7 @@ use std::{collections::HashMap, fs::File, path::Path};
|
||||
use anyhow::{bail, Result};
|
||||
use std::io::prelude::*;
|
||||
use wasm_encoder::{
|
||||
CodeSection, EntityType, Export, ExportSection, Function, FunctionSection, ImportSection,
|
||||
CodeSection, EntityType, ExportKind, ExportSection, Function, FunctionSection, ImportSection,
|
||||
Instruction, MemoryType, Module, TypeSection, ValType,
|
||||
};
|
||||
use ValType::*;
|
||||
@@ -167,6 +167,7 @@ impl BaseModule {
|
||||
add_function(&mut functions, &type_map, "exp", &[F32], Some(F32));
|
||||
|
||||
add_function(&mut functions, &type_map, "playNote", &[I32, I32], None);
|
||||
add_function(&mut functions, &type_map, "sndGes", &[I32], Some(F32));
|
||||
|
||||
for i in functions.len()..64 {
|
||||
add_function(
|
||||
@@ -217,13 +218,13 @@ impl BaseModule {
|
||||
let mut imports = ImportSection::new();
|
||||
|
||||
for (module, name, type_) in &self.function_imports {
|
||||
imports.import(*module, Some(name.as_str()), EntityType::Function(*type_));
|
||||
imports.import(*module, name.as_str(), EntityType::Function(*type_));
|
||||
}
|
||||
|
||||
for (module, name, import) in &self.global_imports {
|
||||
imports.import(
|
||||
*module,
|
||||
Some(name.as_str()),
|
||||
name.as_str(),
|
||||
EntityType::Global(wasm_encoder::GlobalType {
|
||||
val_type: import.type_,
|
||||
mutable: import.mutable,
|
||||
@@ -233,11 +234,12 @@ impl BaseModule {
|
||||
|
||||
imports.import(
|
||||
"env",
|
||||
Some("memory"),
|
||||
"memory",
|
||||
MemoryType {
|
||||
minimum: self.memory as u64,
|
||||
maximum: None,
|
||||
memory64: false,
|
||||
shared: false,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -258,7 +260,7 @@ impl BaseModule {
|
||||
let mut exports = ExportSection::new();
|
||||
|
||||
for (name, fnc) in &self.exports {
|
||||
exports.export(*name, Export::Function(*fnc));
|
||||
exports.export(*name, ExportKind::Func, *fnc);
|
||||
}
|
||||
|
||||
module.section(&exports);
|
||||
@@ -286,7 +288,7 @@ impl BaseModule {
|
||||
|
||||
pub fn create_binary(path: &Path) -> Result<()> {
|
||||
let base1 = BaseModule::for_format_version(1)?.to_wasm();
|
||||
let data = upkr::pack(&base1, 4, false, None);
|
||||
let data = upkr::pack(&base1, 4, &upkr::Config::default(), None);
|
||||
File::create(path)?.write_all(&data)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use std::{
|
||||
use wasm_encoder as enc;
|
||||
use wasmparser::{
|
||||
BinaryReader, ExportSectionReader, ExternalKind, FunctionBody, FunctionSectionReader,
|
||||
ImportSectionEntryType, ImportSectionReader, TableSectionReader, TypeSectionReader,
|
||||
ImportSectionReader, TableSectionReader, TypeRef, TypeSectionReader,
|
||||
};
|
||||
|
||||
pub struct PackConfig {
|
||||
@@ -63,7 +63,7 @@ pub fn pack(data: &[u8], config: &PackConfig) -> Result<Vec<u8>> {
|
||||
uw8.extend_from_slice(&upkr::pack(
|
||||
&result[8..],
|
||||
level,
|
||||
false,
|
||||
&upkr::Config::default(),
|
||||
Some(&mut |pos| {
|
||||
pb.set(pos as u64);
|
||||
}),
|
||||
@@ -90,7 +90,10 @@ pub fn unpack(data: Vec<u8>) -> Result<Vec<u8>> {
|
||||
let (version, data) = match data[0] {
|
||||
0 => return Ok(data),
|
||||
1 => (1, data[1..].to_vec()),
|
||||
2 => (1, upkr::unpack(&data[1..], false)),
|
||||
2 => (
|
||||
1,
|
||||
upkr::unpack(&data[1..], &upkr::Config::default(), 4 * 1024 * 1024)?,
|
||||
),
|
||||
other => bail!("Uknown format version {}", other),
|
||||
};
|
||||
|
||||
@@ -133,8 +136,8 @@ pub fn unpack(data: Vec<u8>) -> Result<Vec<u8>> {
|
||||
Ok(dest)
|
||||
}
|
||||
|
||||
fn to_val_type(type_: &wasmparser::Type) -> Result<ValType> {
|
||||
use wasmparser::Type::*;
|
||||
fn to_val_type(type_: &wasmparser::ValType) -> Result<ValType> {
|
||||
use wasmparser::ValType::*;
|
||||
Ok(match *type_ {
|
||||
I32 => ValType::I32,
|
||||
I64 => ValType::I64,
|
||||
@@ -144,7 +147,7 @@ fn to_val_type(type_: &wasmparser::Type) -> Result<ValType> {
|
||||
})
|
||||
}
|
||||
|
||||
fn to_val_type_vec(types: &[wasmparser::Type]) -> Result<Vec<ValType>> {
|
||||
fn to_val_type_vec(types: &[wasmparser::ValType]) -> Result<Vec<ValType>> {
|
||||
types.into_iter().map(to_val_type).collect()
|
||||
}
|
||||
|
||||
@@ -202,7 +205,7 @@ impl<'a> ParsedModule<'a> {
|
||||
import_section = Some(Section::new(range, ImportSection::parse(reader)?));
|
||||
}
|
||||
Payload::GlobalSection(reader) => {
|
||||
global_section = Some(Section::new(range, reader.get_count()));
|
||||
global_section = Some(Section::new(range, reader.count()));
|
||||
}
|
||||
Payload::FunctionSection(reader) => {
|
||||
function_section = Some(Section::new(range, read_function_section(reader)?));
|
||||
@@ -220,17 +223,22 @@ impl<'a> ParsedModule<'a> {
|
||||
validate_table_section(reader)?;
|
||||
table_section = Some(Section::new(range, ()));
|
||||
}
|
||||
Payload::ElementSection(mut reader) => {
|
||||
let mut elements = Vec::with_capacity(reader.get_count() as usize);
|
||||
for _ in 0..reader.get_count() {
|
||||
elements.push(Element::parse(reader.read()?)?);
|
||||
Payload::MemorySection(reader) => {
|
||||
if reader.count() != 0 {
|
||||
bail!("Found non-empty MemorySection. Memory has to be imported!");
|
||||
}
|
||||
}
|
||||
Payload::ElementSection(reader) => {
|
||||
let mut elements = Vec::with_capacity(reader.count() as usize);
|
||||
for element in reader {
|
||||
elements.push(Element::parse(element?)?);
|
||||
}
|
||||
element_section = Some(elements);
|
||||
}
|
||||
Payload::CodeSectionStart { .. } => (),
|
||||
Payload::CodeSectionEntry(body) => function_bodies.push(body),
|
||||
Payload::CustomSection { .. } => (),
|
||||
Payload::End => break,
|
||||
Payload::End(..) => break,
|
||||
other => bail!("Unsupported section: {:?}", other),
|
||||
}
|
||||
|
||||
@@ -458,7 +466,7 @@ impl<'a> ParsedModule<'a> {
|
||||
{
|
||||
let mut export_section = enc::ExportSection::new();
|
||||
for (name, fnc) in my_exports {
|
||||
export_section.export(&name, enc::Export::Function(fnc));
|
||||
export_section.export(&name, enc::ExportKind::Func, fnc);
|
||||
}
|
||||
module.section(&export_section);
|
||||
}
|
||||
@@ -481,7 +489,7 @@ impl<'a> ParsedModule<'a> {
|
||||
}
|
||||
element_section.active(
|
||||
None,
|
||||
&wasm_encoder::Instruction::I32Const(element.start_index as i32),
|
||||
&wasm_encoder::ConstExpr::i32_const(element.start_index as i32),
|
||||
ValType::FuncRef,
|
||||
wasm_encoder::Elements::Functions(&functions),
|
||||
);
|
||||
@@ -530,28 +538,27 @@ fn read_type_section(reader: TypeSectionReader) -> Result<Vec<base_module::Funct
|
||||
|
||||
for type_def in reader {
|
||||
match type_def? {
|
||||
wasmparser::TypeDef::Func(fnc) => {
|
||||
if fnc.returns.len() > 1 {
|
||||
wasmparser::Type::Func(fnc) => {
|
||||
if fnc.results().len() > 1 {
|
||||
bail!("Multi-value not supported");
|
||||
}
|
||||
let params = to_val_type_vec(&fnc.params)?;
|
||||
let result = to_val_type_vec(&fnc.returns)?.into_iter().next();
|
||||
let params = to_val_type_vec(fnc.params())?;
|
||||
let result = to_val_type_vec(fnc.results())?.into_iter().next();
|
||||
function_types.push(FunctionType { params, result });
|
||||
}
|
||||
t => bail!("Unsupported type def {:?}", t),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(function_types)
|
||||
}
|
||||
|
||||
fn validate_table_section(mut reader: TableSectionReader) -> Result<()> {
|
||||
if reader.get_count() != 1 {
|
||||
fn validate_table_section(reader: TableSectionReader) -> Result<()> {
|
||||
if reader.count() != 1 {
|
||||
bail!("Only up to one table supported");
|
||||
}
|
||||
|
||||
let type_ = reader.read()?;
|
||||
if type_.element_type != wasmparser::Type::FuncRef {
|
||||
let type_ = reader.into_iter().next().unwrap()?;
|
||||
if type_.element_type != wasmparser::ValType::FuncRef {
|
||||
bail!("Only one funcref table is supported");
|
||||
}
|
||||
|
||||
@@ -585,21 +592,20 @@ impl ImportSection {
|
||||
|
||||
for import in reader {
|
||||
let import = import?;
|
||||
if let Some(field) = import.field {
|
||||
match import.ty {
|
||||
ImportSectionEntryType::Function(type_) => {
|
||||
TypeRef::Func(type_) => {
|
||||
functions.push(FunctionImport {
|
||||
module: import.module.to_string(),
|
||||
field: field.to_string(),
|
||||
field: import.name.to_string(),
|
||||
type_,
|
||||
});
|
||||
}
|
||||
ImportSectionEntryType::Memory(mem) => {
|
||||
if import.module != "env" || field != "memory" {
|
||||
TypeRef::Memory(mem) => {
|
||||
if import.module != "env" || import.name != "memory" {
|
||||
bail!(
|
||||
"Wrong name of memory import {}.{}, should be env.memory",
|
||||
import.module,
|
||||
field
|
||||
import.name
|
||||
);
|
||||
}
|
||||
if mem.memory64 || mem.shared {
|
||||
@@ -607,10 +613,10 @@ impl ImportSection {
|
||||
}
|
||||
memory = mem.maximum.unwrap_or(mem.initial) as u32;
|
||||
}
|
||||
ImportSectionEntryType::Global(glbl) => {
|
||||
TypeRef::Global(glbl) => {
|
||||
globals.push(GlobalImport {
|
||||
module: import.module.to_string(),
|
||||
field: field.to_string(),
|
||||
field: import.name.to_string(),
|
||||
type_: GlobalType {
|
||||
type_: to_val_type(&glbl.content_type)?,
|
||||
mutable: glbl.mutable,
|
||||
@@ -619,12 +625,6 @@ impl ImportSection {
|
||||
}
|
||||
_ => bail!("Unsupported import item {:?}", import.ty),
|
||||
}
|
||||
} else {
|
||||
bail!(
|
||||
"Found import without field, only module '{}'",
|
||||
import.module
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ImportSection {
|
||||
@@ -643,17 +643,17 @@ struct Element {
|
||||
|
||||
impl Element {
|
||||
fn parse(element: wasmparser::Element) -> Result<Element> {
|
||||
if element.ty != wasmparser::Type::FuncRef {
|
||||
bail!("Table element type is not FuncRef");
|
||||
}
|
||||
|
||||
match element.items {
|
||||
wasmparser::ElementItems::Functions(funcs_reader) => {
|
||||
let start_index = if let wasmparser::ElementKind::Active {
|
||||
init_expr,
|
||||
offset_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()? {
|
||||
let mut init_reader = offset_expr.get_operators_reader();
|
||||
if let wasmparser::Operator::I32Const { value: start_index } =
|
||||
init_reader.read()?
|
||||
{
|
||||
start_index as u32
|
||||
} else {
|
||||
bail!("Table element start index is not a integer constant");
|
||||
@@ -662,15 +662,9 @@ impl Element {
|
||||
bail!("Unsupported table element kind");
|
||||
};
|
||||
|
||||
let mut items_reader = element.items.get_items_reader()?;
|
||||
|
||||
let mut functions = Vec::with_capacity(items_reader.get_count() as usize);
|
||||
for _ in 0..items_reader.get_count() {
|
||||
if let wasmparser::ElementItem::Func(index) = items_reader.read()? {
|
||||
functions.push(index);
|
||||
} else {
|
||||
bail!("Table element item is not a function");
|
||||
}
|
||||
let mut functions = Vec::with_capacity(funcs_reader.count() as usize);
|
||||
for index in funcs_reader {
|
||||
functions.push(index?);
|
||||
}
|
||||
|
||||
Ok(Element {
|
||||
@@ -678,6 +672,9 @@ impl Element {
|
||||
functions,
|
||||
})
|
||||
}
|
||||
_ => bail!("Table element type is not FuncRef"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -707,8 +704,8 @@ fn read_export_section(reader: ExportSectionReader) -> Result<Vec<(String, u32)>
|
||||
for export in reader {
|
||||
let export = export?;
|
||||
match export.kind {
|
||||
ExternalKind::Function => {
|
||||
function_exports.push((export.field.to_string(), export.index));
|
||||
ExternalKind::Func => {
|
||||
function_exports.push((export.name.to_string(), export.index));
|
||||
}
|
||||
_ => (), // just ignore all other kinds since MicroW8 doesn't expect any exports other than functions
|
||||
}
|
||||
@@ -729,13 +726,11 @@ fn remap_function(
|
||||
}
|
||||
let mut function = enc::Function::new(locals);
|
||||
|
||||
let block_type = |ty: wasmparser::TypeOrFuncType| -> Result<enc::BlockType> {
|
||||
let block_type = |ty: wasmparser::BlockType| -> Result<enc::BlockType> {
|
||||
Ok(match ty {
|
||||
wasmparser::TypeOrFuncType::Type(wasmparser::Type::EmptyBlockType) => {
|
||||
enc::BlockType::Empty
|
||||
}
|
||||
wasmparser::TypeOrFuncType::Type(ty) => enc::BlockType::Result(to_val_type(&ty)?),
|
||||
wasmparser::TypeOrFuncType::FuncType(ty) => enc::BlockType::FunctionType(
|
||||
wasmparser::BlockType::Empty => enc::BlockType::Empty,
|
||||
wasmparser::BlockType::Type(ty) => enc::BlockType::Result(to_val_type(&ty)?),
|
||||
wasmparser::BlockType::FuncType(ty) => enc::BlockType::FunctionType(
|
||||
*type_map
|
||||
.get(&ty)
|
||||
.ok_or_else(|| anyhow!("Function type index out of range: {}", ty))?,
|
||||
@@ -749,7 +744,7 @@ fn remap_function(
|
||||
.ok_or_else(|| anyhow!("Global index out of range: {}", idx))?)
|
||||
};
|
||||
|
||||
fn mem(m: wasmparser::MemoryImmediate) -> enc::MemArg {
|
||||
fn mem(m: wasmparser::MemArg) -> enc::MemArg {
|
||||
enc::MemArg {
|
||||
offset: m.offset,
|
||||
align: m.align as u32,
|
||||
@@ -764,9 +759,9 @@ fn remap_function(
|
||||
function.instruction(&match op? {
|
||||
De::Unreachable => En::Unreachable,
|
||||
De::Nop => En::Nop,
|
||||
De::Block { ty } => En::Block(block_type(ty)?),
|
||||
De::Loop { ty } => En::Loop(block_type(ty)?),
|
||||
De::If { ty } => En::If(block_type(ty)?),
|
||||
De::Block { blockty } => En::Block(block_type(blockty)?),
|
||||
De::Loop { blockty } => En::Loop(block_type(blockty)?),
|
||||
De::If { blockty } => En::If(block_type(blockty)?),
|
||||
De::Else => En::Else,
|
||||
De::Try { .. } | De::Catch { .. } | De::Throw { .. } | De::Rethrow { .. } => todo!(),
|
||||
De::End => En::End,
|
||||
@@ -779,9 +774,13 @@ fn remap_function(
|
||||
.get(&function_index)
|
||||
.ok_or_else(|| anyhow!("Function index out of range: {}", function_index))?,
|
||||
),
|
||||
De::CallIndirect { index, table_index } => En::CallIndirect {
|
||||
De::CallIndirect {
|
||||
type_index,
|
||||
table_index,
|
||||
table_byte: _, // what is this supposed to be?
|
||||
} => En::CallIndirect {
|
||||
ty: *type_map
|
||||
.get(&index)
|
||||
.get(&type_index)
|
||||
.ok_or_else(|| anyhow!("Unknown function type in call indirect"))?,
|
||||
table: table_index,
|
||||
},
|
||||
@@ -801,16 +800,16 @@ fn remap_function(
|
||||
De::I64Load { memarg } => En::I64Load(mem(memarg)),
|
||||
De::F32Load { memarg } => En::F32Load(mem(memarg)),
|
||||
De::F64Load { memarg } => En::F64Load(mem(memarg)),
|
||||
De::I32Load8S { memarg } => En::I32Load8_S(mem(memarg)),
|
||||
De::I32Load8U { memarg } => En::I32Load8_U(mem(memarg)),
|
||||
De::I32Load16S { memarg } => En::I32Load16_S(mem(memarg)),
|
||||
De::I32Load16U { memarg } => En::I32Load16_U(mem(memarg)),
|
||||
De::I64Load8S { memarg } => En::I64Load8_S(mem(memarg)),
|
||||
De::I64Load8U { memarg } => En::I64Load8_U(mem(memarg)),
|
||||
De::I64Load16S { memarg } => En::I64Load16_S(mem(memarg)),
|
||||
De::I64Load16U { memarg } => En::I64Load16_U(mem(memarg)),
|
||||
De::I64Load32S { memarg } => En::I64Load32_S(mem(memarg)),
|
||||
De::I64Load32U { memarg } => En::I64Load32_U(mem(memarg)),
|
||||
De::I32Load8S { memarg } => En::I32Load8S(mem(memarg)),
|
||||
De::I32Load8U { memarg } => En::I32Load8U(mem(memarg)),
|
||||
De::I32Load16S { memarg } => En::I32Load16S(mem(memarg)),
|
||||
De::I32Load16U { memarg } => En::I32Load16U(mem(memarg)),
|
||||
De::I64Load8S { memarg } => En::I64Load8S(mem(memarg)),
|
||||
De::I64Load8U { memarg } => En::I64Load8U(mem(memarg)),
|
||||
De::I64Load16S { memarg } => En::I64Load16S(mem(memarg)),
|
||||
De::I64Load16U { memarg } => En::I64Load16U(mem(memarg)),
|
||||
De::I64Load32S { memarg } => En::I64Load32S(mem(memarg)),
|
||||
De::I64Load32U { memarg } => En::I64Load32U(mem(memarg)),
|
||||
De::I32Store { memarg } => En::I32Store(mem(memarg)),
|
||||
De::I64Store { memarg } => En::I64Store(mem(memarg)),
|
||||
De::F32Store { memarg } => En::F32Store(mem(memarg)),
|
||||
@@ -829,7 +828,7 @@ fn remap_function(
|
||||
De::RefNull { .. } | De::RefIsNull { .. } | De::RefFunc { .. } => todo!(),
|
||||
De::I32Eqz => En::I32Eqz,
|
||||
De::I32Eq => En::I32Eq,
|
||||
De::I32Ne => En::I32Neq,
|
||||
De::I32Ne => En::I32Ne,
|
||||
De::I32LtS => En::I32LtS,
|
||||
De::I32LtU => En::I32LtU,
|
||||
De::I32GtS => En::I32GtS,
|
||||
@@ -840,7 +839,7 @@ fn remap_function(
|
||||
De::I32GeU => En::I32GeU,
|
||||
De::I64Eqz => En::I64Eqz,
|
||||
De::I64Eq => En::I64Eq,
|
||||
De::I64Ne => En::I64Neq,
|
||||
De::I64Ne => En::I64Ne,
|
||||
De::I64LtS => En::I64LtS,
|
||||
De::I64LtU => En::I64LtU,
|
||||
De::I64GtS => En::I64GtS,
|
||||
@@ -850,13 +849,13 @@ fn remap_function(
|
||||
De::I64GeS => En::I64GeS,
|
||||
De::I64GeU => En::I64GeU,
|
||||
De::F32Eq => En::F32Eq,
|
||||
De::F32Ne => En::F32Neq,
|
||||
De::F32Ne => En::F32Ne,
|
||||
De::F32Lt => En::F32Lt,
|
||||
De::F32Gt => En::F32Gt,
|
||||
De::F32Le => En::F32Le,
|
||||
De::F32Ge => En::F32Ge,
|
||||
De::F64Eq => En::F64Eq,
|
||||
De::F64Ne => En::F64Neq,
|
||||
De::F64Ne => En::F64Ne,
|
||||
De::F64Lt => En::F64Lt,
|
||||
De::F64Gt => En::F64Gt,
|
||||
De::F64Le => En::F64Le,
|
||||
@@ -963,7 +962,7 @@ fn remap_function(
|
||||
De::I64TruncSatF32U => En::I64TruncSatF32U,
|
||||
De::I64TruncSatF64S => En::I64TruncSatF64S,
|
||||
De::I64TruncSatF64U => En::I64TruncSatF64U,
|
||||
De::MemoryCopy { src, dst } => En::MemoryCopy { src, dst },
|
||||
De::MemoryCopy { src_mem, dst_mem } => En::MemoryCopy { src_mem, dst_mem },
|
||||
De::MemoryFill { mem } => En::MemoryFill(mem),
|
||||
other => bail!("Unsupported instruction {:?}", other),
|
||||
});
|
||||
|
||||
847
uw8-window/Cargo.lock
generated
847
uw8-window/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -6,12 +6,13 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
winit = "0.26.1"
|
||||
env_logger = "0.9"
|
||||
winit = "0.27.5"
|
||||
env_logger = "0.10"
|
||||
log = "0.4"
|
||||
wgpu = "0.13.1"
|
||||
pollster = "0.2"
|
||||
bytemuck = { version = "1.4", features = [ "derive" ] }
|
||||
pico-args = "0.5"
|
||||
wgpu = "0.15"
|
||||
pollster = "0.2.5"
|
||||
bytemuck = { version = "1.13", features = [ "derive" ] }
|
||||
anyhow = "1"
|
||||
minifb = { version = "0.23.0", default-features = false, features = ["x11"] }
|
||||
winapi = "0.3.9"
|
||||
winapi = { version = "0.3.9", features = [ "timeapi" ] }
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use std::time::Instant;
|
||||
|
||||
use crate::Framebuffer;
|
||||
use minifb::{Key, Window, WindowOptions};
|
||||
use crate::{Input, WindowImpl};
|
||||
use anyhow::Result;
|
||||
use minifb::{Key, WindowOptions};
|
||||
|
||||
static GAMEPAD_KEYS: &[Key] = &[
|
||||
Key::Up,
|
||||
@@ -14,13 +15,19 @@ static GAMEPAD_KEYS: &[Key] = &[
|
||||
Key::S,
|
||||
];
|
||||
|
||||
pub fn run(mut update: Box<dyn FnMut(&mut dyn Framebuffer, u32, bool) -> Instant + 'static>) -> ! {
|
||||
pub struct Window {
|
||||
window: minifb::Window,
|
||||
buffer: Vec<u32>,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new() -> Result<Window> {
|
||||
#[cfg(target_os = "windows")]
|
||||
unsafe {
|
||||
winapi::um::timeapi::timeBeginPeriod(1);
|
||||
}
|
||||
|
||||
let mut buffer: Vec<u32> = vec![0; 320 * 240];
|
||||
let buffer: Vec<u32> = vec![0; 320 * 240];
|
||||
|
||||
let options = WindowOptions {
|
||||
scale: minifb::Scale::X2,
|
||||
@@ -28,44 +35,33 @@ pub fn run(mut update: Box<dyn FnMut(&mut dyn Framebuffer, u32, bool) -> Instant
|
||||
resize: true,
|
||||
..Default::default()
|
||||
};
|
||||
let mut window = Window::new("MicroW8", 320, 240, options).unwrap();
|
||||
let window = minifb::Window::new("MicroW8", 320, 240, options).unwrap();
|
||||
|
||||
let mut next_frame = Instant::now();
|
||||
while window.is_open() && !window.is_key_down(Key::Escape) {
|
||||
if let Some(sleep) = next_frame.checked_duration_since(Instant::now()) {
|
||||
std::thread::sleep(sleep);
|
||||
Ok(Window { window, buffer })
|
||||
}
|
||||
}
|
||||
|
||||
let mut gamepad = 0;
|
||||
for key in window.get_keys() {
|
||||
impl WindowImpl for Window {
|
||||
fn begin_frame(&mut self) -> Input {
|
||||
let mut gamepads = [0u8; 4];
|
||||
for key in self.window.get_keys() {
|
||||
if let Some(index) = GAMEPAD_KEYS
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, &k)| k == key)
|
||||
.map(|(i, _)| i)
|
||||
{
|
||||
gamepad |= 1 << index;
|
||||
gamepads[0] |= 1 << index;
|
||||
}
|
||||
}
|
||||
|
||||
next_frame = update(
|
||||
&mut CpuFramebuffer {
|
||||
buffer: &mut buffer,
|
||||
},
|
||||
gamepad,
|
||||
window.is_key_pressed(Key::R, minifb::KeyRepeat::No),
|
||||
);
|
||||
window.update_with_buffer(&buffer, 320, 240).unwrap();
|
||||
Input {
|
||||
gamepads,
|
||||
reset: self.window.is_key_pressed(Key::R, minifb::KeyRepeat::No),
|
||||
}
|
||||
}
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
struct CpuFramebuffer<'a> {
|
||||
buffer: &'a mut Vec<u32>,
|
||||
}
|
||||
|
||||
impl<'a> Framebuffer for CpuFramebuffer<'a> {
|
||||
fn update(&mut self, framebuffer: &[u8], palette: &[u8]) {
|
||||
fn end_frame(&mut self, framebuffer: &[u8], palette: &[u8], next_frame: Instant) {
|
||||
for (i, &color_index) in framebuffer.iter().enumerate() {
|
||||
let offset = color_index as usize * 4;
|
||||
self.buffer[i] = 0xff000000
|
||||
@@ -73,5 +69,15 @@ impl<'a> Framebuffer for CpuFramebuffer<'a> {
|
||||
| ((palette[offset + 1] as u32) << 8)
|
||||
| palette[offset + 2] as u32;
|
||||
}
|
||||
self.window
|
||||
.update_with_buffer(&self.buffer, 320, 240)
|
||||
.unwrap();
|
||||
if let Some(sleep) = next_frame.checked_duration_since(Instant::now()) {
|
||||
std::thread::sleep(sleep);
|
||||
}
|
||||
}
|
||||
|
||||
fn is_open(&self) -> bool {
|
||||
self.window.is_open() && !self.window.is_key_down(Key::Escape)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ impl CrtFilter {
|
||||
}
|
||||
|
||||
impl Filter for CrtFilter {
|
||||
fn resize(&self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>) {
|
||||
fn resize(&mut self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>) {
|
||||
let uniforms = Uniforms {
|
||||
texture_scale: texture_scale_from_resolution(new_size),
|
||||
};
|
||||
|
||||
@@ -36,36 +36,36 @@ fn sample_pixel(coords: vec2<i32>, offset: vec4<f32>) -> vec3<f32> {
|
||||
|
||||
@fragment
|
||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
let pixel = floor(in.tex_coords);
|
||||
let o = vec2<f32>(0.5) - (in.tex_coords - pixel);
|
||||
let pixel = vec2<i32>(pixel);
|
||||
let pixelf = floor(in.tex_coords);
|
||||
let o = vec2<f32>(0.5) - (in.tex_coords - pixelf);
|
||||
let pixel = vec2<i32>(pixelf);
|
||||
|
||||
let offset_x = o.xxxx + vec4<f32>(-0.125, 0.375, 0.125, -0.375) * uniforms.texture_scale.z;
|
||||
let offset_y = o.yyyy + vec4<f32>(-0.375, -0.125, 0.375, 0.125) * uniforms.texture_scale.z;
|
||||
|
||||
let offset_x0 = max(abs(offset_x + vec4<f32>(-1.0)) - vec4<f32>(0.5), vec4<f32>(0.0));
|
||||
let offset_x1 = max(abs(offset_x) - vec4<f32>(0.5), vec4<f32>(0.0));
|
||||
let offset_x2 = max(abs(offset_x + vec4<f32>(1.0)) - vec4<f32>(0.5), vec4<f32>(0.0));
|
||||
var offset_x0 = max(abs(offset_x + vec4<f32>(-1.0)) - vec4<f32>(0.5), vec4<f32>(0.0));
|
||||
var offset_x1 = max(abs(offset_x) - vec4<f32>(0.5), vec4<f32>(0.0));
|
||||
var offset_x2 = max(abs(offset_x + vec4<f32>(1.0)) - vec4<f32>(0.5), vec4<f32>(0.0));
|
||||
|
||||
let offset_x0 = offset_x0 * offset_x0;
|
||||
let offset_x1 = offset_x1 * offset_x1;
|
||||
let offset_x2 = offset_x2 * offset_x2;
|
||||
offset_x0 = offset_x0 * offset_x0;
|
||||
offset_x1 = offset_x1 * offset_x1;
|
||||
offset_x2 = offset_x2 * offset_x2;
|
||||
|
||||
let offset_yr = offset_y + vec4<f32>(-1.0);
|
||||
let offset_yr = vec4<f32>(0.02) + offset_yr * offset_yr;
|
||||
var offset_yr = offset_y + vec4<f32>(-1.0);
|
||||
offset_yr = vec4<f32>(0.02) + offset_yr * offset_yr;
|
||||
|
||||
var acc = sample_pixel(pixel + vec2<i32>(-1, -1), offset_x0 + offset_yr);
|
||||
acc = acc + sample_pixel(pixel + vec2<i32>(0, -1), offset_x1 + offset_yr);
|
||||
acc = acc + sample_pixel(pixel + vec2<i32>(1, -1), offset_x2 + offset_yr);
|
||||
|
||||
let offset_yr = vec4<f32>(0.02) + offset_y * offset_y;
|
||||
offset_yr = vec4<f32>(0.02) + offset_y * offset_y;
|
||||
|
||||
acc = acc + sample_pixel(pixel + vec2<i32>(-1, 0), offset_x0 + offset_yr);
|
||||
acc = acc + sample_pixel(pixel, offset_x1 + offset_yr);
|
||||
acc = acc + sample_pixel(pixel + vec2<i32>(1, 0), offset_x2 + offset_yr);
|
||||
|
||||
let offset_yr = offset_y + vec4<f32>(1.0);
|
||||
let offset_yr = vec4<f32>(0.02) + offset_yr * offset_yr;
|
||||
offset_yr = offset_y + vec4<f32>(1.0);
|
||||
offset_yr = vec4<f32>(0.02) + offset_yr * offset_yr;
|
||||
|
||||
acc = acc + sample_pixel(pixel + vec2<i32>(-1, 1), offset_x0 + offset_yr);
|
||||
acc = acc + sample_pixel(pixel + vec2<i32>(0, 1), offset_x1 + offset_yr);
|
||||
|
||||
@@ -15,6 +15,7 @@ impl FastCrtFilter {
|
||||
screen: &wgpu::TextureView,
|
||||
resolution: PhysicalSize<u32>,
|
||||
surface_format: wgpu::TextureFormat,
|
||||
chromatic: bool,
|
||||
) -> FastCrtFilter {
|
||||
let uniforms = Uniforms {
|
||||
texture_scale: texture_scale_from_resolution(resolution),
|
||||
@@ -104,7 +105,11 @@ impl FastCrtFilter {
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: "fs_main",
|
||||
entry_point: if chromatic {
|
||||
"fs_main_chromatic"
|
||||
} else {
|
||||
"fs_main"
|
||||
},
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format: surface_format,
|
||||
blend: None,
|
||||
@@ -126,7 +131,7 @@ impl FastCrtFilter {
|
||||
}
|
||||
|
||||
impl Filter for FastCrtFilter {
|
||||
fn resize(&self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>) {
|
||||
fn resize(&mut self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>) {
|
||||
let uniforms = Uniforms {
|
||||
texture_scale: texture_scale_from_resolution(new_size),
|
||||
};
|
||||
|
||||
@@ -34,10 +34,9 @@ fn col_factor(offset: f32) -> f32 {
|
||||
return 1.0 / (1.0 + offset * offset * 16.0);
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
let base = round(in.tex_coords) - vec2<f32>(0.5);
|
||||
let frac = in.tex_coords - base;
|
||||
fn sample_screen(tex_coords: vec2<f32>) -> vec4<f32> {
|
||||
let base = round(tex_coords) - vec2<f32>(0.5);
|
||||
let frac = tex_coords - base;
|
||||
|
||||
let top_factor = row_factor(frac.y);
|
||||
let bottom_factor = row_factor(frac.y - 1.0);
|
||||
@@ -52,3 +51,16 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
return textureSample(screen_texture, linear_sampler, vec2<f32>(u, v) / vec2<f32>(320.0, 240.0)) * (top_factor + bottom_factor) * (left_factor + right_factor) * 1.1;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
return sample_screen(in.tex_coords);
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_main_chromatic(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
let r = sample_screen(in.tex_coords + vec2<f32>(0.2, 0.2)).r;
|
||||
let g = sample_screen(in.tex_coords + vec2<f32>(0.07, -0.27)).g;
|
||||
let b = sample_screen(in.tex_coords + vec2<f32>(-0.27, 0.07)).b;
|
||||
return vec4<f32>(r, g, b, 1.0);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::Framebuffer;
|
||||
use crate::{Input, WindowConfig, WindowImpl};
|
||||
use anyhow::{anyhow, Result};
|
||||
use std::{num::NonZeroU32, time::Instant};
|
||||
|
||||
@@ -9,12 +9,7 @@ use winit::{
|
||||
window::{Fullscreen, WindowBuilder},
|
||||
};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use winit::platform::macos::EventLoopExtMacOS;
|
||||
#[cfg(target_os = "linux")]
|
||||
use winit::platform::unix::EventLoopExtUnix;
|
||||
#[cfg(target_os = "windows")]
|
||||
use winit::platform::windows::EventLoopExtWindows;
|
||||
use winit::platform::run_return::EventLoopExtRunReturn;
|
||||
|
||||
mod crt;
|
||||
mod fast_crt;
|
||||
@@ -25,29 +20,41 @@ use fast_crt::FastCrtFilter;
|
||||
use square::SquareFilter;
|
||||
|
||||
pub struct Window {
|
||||
event_loop: EventLoop<()>,
|
||||
window: winit::window::Window,
|
||||
instance: wgpu::Instance,
|
||||
_instance: wgpu::Instance,
|
||||
surface: wgpu::Surface,
|
||||
adapter: wgpu::Adapter,
|
||||
_adapter: wgpu::Adapter,
|
||||
device: wgpu::Device,
|
||||
queue: wgpu::Queue,
|
||||
palette_screen_mode: PaletteScreenMode,
|
||||
surface_config: wgpu::SurfaceConfiguration,
|
||||
filter: Box<dyn Filter>,
|
||||
event_loop: EventLoop<()>,
|
||||
window: winit::window::Window,
|
||||
gamepads: [u8; 4],
|
||||
next_frame: Instant,
|
||||
is_fullscreen: bool,
|
||||
is_open: bool,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new() -> Result<Window> {
|
||||
async fn create() -> Result<Window> {
|
||||
let event_loop = EventLoop::new_any_thread();
|
||||
pub fn new(window_config: WindowConfig) -> Result<Window> {
|
||||
async fn create(window_config: WindowConfig) -> Result<Window> {
|
||||
let event_loop = EventLoop::new();
|
||||
let window = WindowBuilder::new()
|
||||
.with_inner_size(PhysicalSize::new(640u32, 480))
|
||||
.with_min_inner_size(PhysicalSize::new(320u32, 240))
|
||||
.with_title("MicroW8")
|
||||
.with_fullscreen(if window_config.fullscreen {
|
||||
Some(Fullscreen::Borderless(None))
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.build(&event_loop)?;
|
||||
|
||||
window.set_cursor_visible(false);
|
||||
|
||||
let instance = wgpu::Instance::new(wgpu::Backends::all());
|
||||
let surface = unsafe { instance.create_surface(&window) };
|
||||
let instance = wgpu::Instance::new(Default::default());
|
||||
let surface = unsafe { instance.create_surface(&window) }?;
|
||||
let adapter = instance
|
||||
.request_adapter(&wgpu::RequestAdapterOptions {
|
||||
power_preference: wgpu::PowerPreference::LowPower,
|
||||
@@ -61,70 +68,65 @@ impl Window {
|
||||
.request_device(&wgpu::DeviceDescriptor::default(), None)
|
||||
.await?;
|
||||
|
||||
Ok(Window {
|
||||
event_loop,
|
||||
window,
|
||||
instance,
|
||||
surface,
|
||||
adapter,
|
||||
device,
|
||||
queue,
|
||||
})
|
||||
}
|
||||
|
||||
pollster::block_on(create())
|
||||
}
|
||||
|
||||
pub fn run(
|
||||
self,
|
||||
mut update: Box<dyn FnMut(&mut dyn Framebuffer, u32, bool) -> Instant + 'static>,
|
||||
) -> ! {
|
||||
let Window {
|
||||
event_loop,
|
||||
window,
|
||||
instance,
|
||||
surface,
|
||||
adapter,
|
||||
device,
|
||||
queue,
|
||||
} = self;
|
||||
|
||||
let palette_screen_mode = PaletteScreenMode::new(&device);
|
||||
|
||||
let mut surface_config = wgpu::SurfaceConfiguration {
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
format: surface.get_supported_formats(&adapter)[0],
|
||||
width: window.inner_size().width,
|
||||
height: window.inner_size().height,
|
||||
let surface_config = wgpu::SurfaceConfiguration {
|
||||
present_mode: wgpu::PresentMode::AutoNoVsync,
|
||||
..surface.get_default_config(&adapter, window.inner_size().width, window.inner_size().height).expect("Surface incompatible with adapter")
|
||||
};
|
||||
|
||||
let mut filter: Box<dyn Filter> = Box::new(CrtFilter::new(
|
||||
let filter: Box<dyn Filter> = create_filter(
|
||||
&device,
|
||||
&palette_screen_mode.screen_view,
|
||||
window.inner_size(),
|
||||
surface_config.format,
|
||||
));
|
||||
window_config.filter,
|
||||
);
|
||||
|
||||
surface.configure(&device, &surface_config);
|
||||
|
||||
Ok(Window {
|
||||
event_loop,
|
||||
window,
|
||||
_instance: instance,
|
||||
surface,
|
||||
_adapter: adapter,
|
||||
device,
|
||||
queue,
|
||||
palette_screen_mode,
|
||||
surface_config,
|
||||
filter,
|
||||
gamepads: [0; 4],
|
||||
next_frame: Instant::now(),
|
||||
is_fullscreen: window_config.fullscreen,
|
||||
is_open: true,
|
||||
})
|
||||
}
|
||||
|
||||
pollster::block_on(create(window_config))
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowImpl for Window {
|
||||
fn begin_frame(&mut self) -> Input {
|
||||
let mut reset = false;
|
||||
let mut gamepad = 0;
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
let _ = (&window, &instance, &surface, &adapter, &device);
|
||||
|
||||
self.event_loop.run_return(|event, _, control_flow| {
|
||||
*control_flow = ControlFlow::WaitUntil(self.next_frame);
|
||||
let mut new_filter = None;
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::Resized(new_size) => {
|
||||
surface_config.width = new_size.width;
|
||||
surface_config.height = new_size.height;
|
||||
surface.configure(&device, &surface_config);
|
||||
filter.resize(&queue, new_size);
|
||||
self.surface_config.width = new_size.width;
|
||||
self.surface_config.height = new_size.height;
|
||||
self.surface.configure(&self.device, &self.surface_config);
|
||||
self.filter.resize(&self.queue, new_size);
|
||||
}
|
||||
WindowEvent::CloseRequested => {
|
||||
self.is_open = false;
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
WindowEvent::KeyboardInput { input, .. } => {
|
||||
fn gamepad_button(input: &winit::event::KeyboardInput) -> u32 {
|
||||
fn gamepad_button(input: &winit::event::KeyboardInput) -> u8 {
|
||||
match input.scancode {
|
||||
44 => 16,
|
||||
45 => 32,
|
||||
@@ -141,78 +143,79 @@ impl Window {
|
||||
}
|
||||
if input.state == winit::event::ElementState::Pressed {
|
||||
match input.virtual_keycode {
|
||||
Some(VirtualKeyCode::Escape) => *control_flow = ControlFlow::Exit,
|
||||
Some(VirtualKeyCode::Escape) => {
|
||||
self.is_open = false;
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
Some(VirtualKeyCode::F) => {
|
||||
window.set_fullscreen(if window.fullscreen().is_some() {
|
||||
let fullscreen = if self.window.fullscreen().is_some() {
|
||||
None
|
||||
} else {
|
||||
Some(Fullscreen::Borderless(None))
|
||||
});
|
||||
};
|
||||
self.is_fullscreen = fullscreen.is_some();
|
||||
self.window.set_fullscreen(fullscreen);
|
||||
}
|
||||
Some(VirtualKeyCode::R) => reset = true,
|
||||
Some(VirtualKeyCode::Key1) => {
|
||||
filter = Box::new(SquareFilter::new(
|
||||
&device,
|
||||
&palette_screen_mode.screen_view,
|
||||
window.inner_size(),
|
||||
surface_config.format,
|
||||
))
|
||||
}
|
||||
Some(VirtualKeyCode::Key2) => {
|
||||
filter = Box::new(FastCrtFilter::new(
|
||||
&device,
|
||||
&palette_screen_mode.screen_view,
|
||||
window.inner_size(),
|
||||
surface_config.format,
|
||||
))
|
||||
}
|
||||
Some(VirtualKeyCode::Key3) => {
|
||||
filter = Box::new(CrtFilter::new(
|
||||
&device,
|
||||
&palette_screen_mode.screen_view,
|
||||
window.inner_size(),
|
||||
surface_config.format,
|
||||
))
|
||||
}
|
||||
Some(VirtualKeyCode::Key1) => new_filter = Some(1),
|
||||
Some(VirtualKeyCode::Key2) => new_filter = Some(2),
|
||||
Some(VirtualKeyCode::Key3) => new_filter = Some(3),
|
||||
Some(VirtualKeyCode::Key4) => new_filter = Some(4),
|
||||
Some(VirtualKeyCode::Key5) => new_filter = Some(5),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
gamepad |= gamepad_button(&input);
|
||||
self.gamepads[0] |= gamepad_button(&input);
|
||||
} else {
|
||||
gamepad &= !gamepad_button(&input);
|
||||
self.gamepads[0] &= !gamepad_button(&input);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::MainEventsCleared => {
|
||||
if let ControlFlow::WaitUntil(t) = *control_flow {
|
||||
if Instant::now() < t {
|
||||
return;
|
||||
Event::RedrawEventsCleared => {
|
||||
if Instant::now() >= self.next_frame
|
||||
// workaround needed on Wayland until the next winit release
|
||||
&& self.window.fullscreen().is_some() == self.is_fullscreen
|
||||
{
|
||||
*control_flow = ControlFlow::Exit
|
||||
}
|
||||
}
|
||||
let next_frame = update(
|
||||
&mut GpuFramebuffer {
|
||||
queue: &queue,
|
||||
framebuffer: &palette_screen_mode,
|
||||
},
|
||||
gamepad,
|
||||
reset,
|
||||
_ => (),
|
||||
}
|
||||
if let Some(new_filter) = new_filter {
|
||||
self.filter = create_filter(
|
||||
&self.device,
|
||||
&self.palette_screen_mode.screen_view,
|
||||
self.window.inner_size(),
|
||||
self.surface_config.format,
|
||||
new_filter,
|
||||
);
|
||||
reset = false;
|
||||
*control_flow = ControlFlow::WaitUntil(next_frame);
|
||||
}
|
||||
});
|
||||
Input {
|
||||
gamepads: self.gamepads,
|
||||
reset,
|
||||
}
|
||||
}
|
||||
|
||||
let output = surface.get_current_texture().unwrap();
|
||||
fn end_frame(&mut self, framebuffer: &[u8], palette: &[u8], next_frame: Instant) {
|
||||
self.next_frame = next_frame;
|
||||
self.palette_screen_mode
|
||||
.write_framebuffer(&self.queue, framebuffer);
|
||||
self.palette_screen_mode.write_palette(&self.queue, palette);
|
||||
|
||||
let output = self.surface.get_current_texture().unwrap();
|
||||
let view = output
|
||||
.texture
|
||||
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let mut encoder = device
|
||||
let mut encoder = self
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
||||
|
||||
palette_screen_mode.resolve_screen(&mut encoder);
|
||||
self.palette_screen_mode.resolve_screen(&mut encoder);
|
||||
|
||||
{
|
||||
let mut render_pass =
|
||||
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: None,
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: &view,
|
||||
@@ -230,35 +233,105 @@ impl Window {
|
||||
depth_stencil_attachment: None,
|
||||
});
|
||||
|
||||
filter.render(&mut render_pass);
|
||||
self.filter.render(&mut render_pass);
|
||||
}
|
||||
|
||||
queue.submit(std::iter::once(encoder.finish()));
|
||||
self.queue.submit(std::iter::once(encoder.finish()));
|
||||
output.present();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
|
||||
fn is_open(&self) -> bool {
|
||||
self.is_open
|
||||
}
|
||||
}
|
||||
|
||||
struct GpuFramebuffer<'a> {
|
||||
framebuffer: &'a PaletteScreenMode,
|
||||
queue: &'a wgpu::Queue,
|
||||
}
|
||||
|
||||
impl<'a> Framebuffer for GpuFramebuffer<'a> {
|
||||
fn update(&mut self, pixels: &[u8], palette: &[u8]) {
|
||||
self.framebuffer.write_framebuffer(self.queue, pixels);
|
||||
self.framebuffer.write_palette(self.queue, palette);
|
||||
fn create_filter(
|
||||
device: &wgpu::Device,
|
||||
screen_texture: &wgpu::TextureView,
|
||||
window_size: PhysicalSize<u32>,
|
||||
surface_format: wgpu::TextureFormat,
|
||||
filter: u32,
|
||||
) -> Box<dyn Filter> {
|
||||
match filter {
|
||||
1 => Box::new(SquareFilter::new(
|
||||
device,
|
||||
screen_texture,
|
||||
window_size,
|
||||
surface_format,
|
||||
)),
|
||||
2 => Box::new(FastCrtFilter::new(
|
||||
device,
|
||||
screen_texture,
|
||||
window_size,
|
||||
surface_format,
|
||||
false,
|
||||
)),
|
||||
3 => Box::new(CrtFilter::new(
|
||||
device,
|
||||
screen_texture,
|
||||
window_size,
|
||||
surface_format,
|
||||
)),
|
||||
4 => Box::new(FastCrtFilter::new(
|
||||
device,
|
||||
screen_texture,
|
||||
window_size,
|
||||
surface_format,
|
||||
true,
|
||||
)),
|
||||
_ => Box::new(AutoCrtFilter::new(
|
||||
device,
|
||||
screen_texture,
|
||||
window_size,
|
||||
surface_format,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
trait Filter {
|
||||
fn resize(&self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>);
|
||||
fn resize(&mut self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>);
|
||||
fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>);
|
||||
}
|
||||
|
||||
struct AutoCrtFilter {
|
||||
small: CrtFilter,
|
||||
large: FastCrtFilter,
|
||||
resolution: PhysicalSize<u32>,
|
||||
}
|
||||
|
||||
impl AutoCrtFilter {
|
||||
fn new(
|
||||
device: &wgpu::Device,
|
||||
screen: &wgpu::TextureView,
|
||||
resolution: PhysicalSize<u32>,
|
||||
surface_format: wgpu::TextureFormat,
|
||||
) -> AutoCrtFilter {
|
||||
let small = CrtFilter::new(device, screen, resolution, surface_format);
|
||||
let large = FastCrtFilter::new(device, screen, resolution, surface_format, true);
|
||||
AutoCrtFilter {
|
||||
small,
|
||||
large,
|
||||
resolution,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Filter for AutoCrtFilter {
|
||||
fn resize(&mut self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>) {
|
||||
self.small.resize(queue, new_size);
|
||||
self.large.resize(queue, new_size);
|
||||
self.resolution = new_size;
|
||||
}
|
||||
|
||||
fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) {
|
||||
if self.resolution.width < 960 || self.resolution.height < 720 {
|
||||
self.small.render(render_pass);
|
||||
} else {
|
||||
self.large.render(render_pass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct PaletteScreenMode {
|
||||
framebuffer: wgpu::Texture,
|
||||
palette: wgpu::Texture,
|
||||
@@ -281,6 +354,7 @@ impl PaletteScreenMode {
|
||||
format: wgpu::TextureFormat::R8Uint,
|
||||
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
||||
label: None,
|
||||
view_formats: &[]
|
||||
});
|
||||
|
||||
let palette_texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
@@ -295,6 +369,7 @@ impl PaletteScreenMode {
|
||||
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
||||
label: None,
|
||||
view_formats: &[]
|
||||
});
|
||||
|
||||
let screen_texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
@@ -309,6 +384,7 @@ impl PaletteScreenMode {
|
||||
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
label: None,
|
||||
view_formats: &[]
|
||||
});
|
||||
|
||||
let framebuffer_texture_view =
|
||||
|
||||
@@ -126,7 +126,7 @@ impl SquareFilter {
|
||||
}
|
||||
|
||||
impl Filter for SquareFilter {
|
||||
fn resize(&self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>) {
|
||||
fn resize(&mut self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>) {
|
||||
let uniforms = Uniforms {
|
||||
texture_scale: texture_scale_from_resolution(new_size),
|
||||
};
|
||||
|
||||
@@ -1,24 +1,117 @@
|
||||
use anyhow::Result;
|
||||
use std::time::Instant;
|
||||
|
||||
mod cpu;
|
||||
mod gpu;
|
||||
|
||||
pub fn run<F: 'static + FnMut(&mut dyn Framebuffer, u32, bool) -> Instant>(
|
||||
gpu: bool,
|
||||
update: F,
|
||||
) -> ! {
|
||||
if gpu {
|
||||
match gpu::Window::new() {
|
||||
Ok(window) => window.run(Box::new(update)),
|
||||
pub struct Window {
|
||||
inner: Box<dyn WindowImpl>,
|
||||
fps_counter: Option<FpsCounter>,
|
||||
}
|
||||
|
||||
struct FpsCounter {
|
||||
start: Instant,
|
||||
num_frames: u32,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new(config: WindowConfig) -> Result<Window> {
|
||||
let fps_counter = if config.fps_counter {
|
||||
Some(FpsCounter {
|
||||
start: Instant::now(),
|
||||
num_frames: 0,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if config.enable_gpu {
|
||||
match gpu::Window::new(config) {
|
||||
Ok(window) => {
|
||||
return Ok(Window {
|
||||
inner: Box::new(window),
|
||||
fps_counter,
|
||||
})
|
||||
}
|
||||
Err(err) => eprintln!(
|
||||
"Failed to create gpu window: {}\nFalling back to cpu window",
|
||||
"Failed to create gpu window: {}\nFalling back tp cpu window",
|
||||
err
|
||||
),
|
||||
}
|
||||
}
|
||||
cpu::run(Box::new(update));
|
||||
cpu::Window::new().map(|window| Window {
|
||||
inner: Box::new(window),
|
||||
fps_counter,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn begin_frame(&mut self) -> Input {
|
||||
self.inner.begin_frame()
|
||||
}
|
||||
pub fn end_frame(&mut self, framebuffer: &[u8], palette: &[u8], next_frame: Instant) {
|
||||
self.inner.end_frame(framebuffer, palette, next_frame);
|
||||
if let Some(ref mut fps_counter) = self.fps_counter {
|
||||
fps_counter.num_frames += 1;
|
||||
let elapsed = fps_counter.start.elapsed().as_secs_f32();
|
||||
if elapsed >= 1.0 {
|
||||
println!("fps: {:.1}", fps_counter.num_frames as f32 / elapsed);
|
||||
fps_counter.num_frames = 0;
|
||||
fps_counter.start = Instant::now();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_open(&self) -> bool {
|
||||
self.inner.is_open()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Framebuffer {
|
||||
fn update(&mut self, pixels: &[u8], palette: &[u8]);
|
||||
#[derive(Debug)]
|
||||
pub struct WindowConfig {
|
||||
enable_gpu: bool,
|
||||
filter: u32,
|
||||
fullscreen: bool,
|
||||
fps_counter: bool,
|
||||
}
|
||||
|
||||
impl Default for WindowConfig {
|
||||
fn default() -> WindowConfig {
|
||||
WindowConfig {
|
||||
enable_gpu: true,
|
||||
filter: 5,
|
||||
fullscreen: false,
|
||||
fps_counter: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowConfig {
|
||||
pub fn parse_arguments(&mut self, args: &mut pico_args::Arguments) {
|
||||
self.enable_gpu = !args.contains("--no-gpu");
|
||||
if let Some(filter) = args.opt_value_from_str::<_, String>("--filter").unwrap() {
|
||||
self.filter = match filter.as_str() {
|
||||
"1" | "nearest" => 1,
|
||||
"2" | "fast_crt" => 2,
|
||||
"3" | "ss_crt" => 3,
|
||||
"4" | "chromatic" => 4,
|
||||
"5" | "auto_crt" => 5,
|
||||
o => {
|
||||
println!("Unknown --filter '{}'", o);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.fullscreen = args.contains("--fullscreen");
|
||||
self.fps_counter = args.contains("--fps");
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Input {
|
||||
pub gamepads: [u8; 4],
|
||||
pub reset: bool,
|
||||
}
|
||||
|
||||
trait WindowImpl {
|
||||
fn begin_frame(&mut self) -> Input;
|
||||
fn end_frame(&mut self, framebuffer: &[u8], palette: &[u8], next_frame: Instant);
|
||||
fn is_open(&self) -> bool;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
use std::time::Instant;
|
||||
use uw8_window::WindowConfig;
|
||||
|
||||
fn main() {
|
||||
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
|
||||
|
||||
let mut args = pico_args::Arguments::from_env();
|
||||
|
||||
let mut framebuffer = vec![0u8; 320 * 240];
|
||||
let start_time = Instant::now();
|
||||
let mut start_time = Instant::now();
|
||||
|
||||
let mut palette = vec![0u32; 256];
|
||||
for i in 0..256 {
|
||||
@@ -18,11 +21,19 @@ fn main() {
|
||||
let mut fps_start = Instant::now();
|
||||
let mut fps_counter = 0;
|
||||
|
||||
uw8_window::run(true, move |gpu_framebuffer, _gamepads, _reset| {
|
||||
for _ in 0..1 {
|
||||
draw_frame(&mut framebuffer, start_time.elapsed().as_secs_f32());
|
||||
let mut window_config = WindowConfig::default();
|
||||
window_config.parse_arguments(&mut args);
|
||||
|
||||
let mut window = uw8_window::Window::new(window_config).unwrap();
|
||||
|
||||
while window.is_open() {
|
||||
let input = window.begin_frame();
|
||||
if input.reset {
|
||||
start_time = Instant::now();
|
||||
}
|
||||
gpu_framebuffer.update(&framebuffer, bytemuck::cast_slice(&palette));
|
||||
draw_frame(&mut framebuffer, start_time.elapsed().as_secs_f32());
|
||||
window.end_frame(&framebuffer, bytemuck::cast_slice(&palette), Instant::now());
|
||||
|
||||
fps_counter += 1;
|
||||
let elapsed = fps_start.elapsed().as_secs_f32();
|
||||
if elapsed >= 1.0 {
|
||||
@@ -30,8 +41,7 @@ fn main() {
|
||||
fps_start = Instant::now();
|
||||
fps_counter = 0;
|
||||
}
|
||||
Instant::now()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_frame(framebuffer: &mut [u8], time: f32) {
|
||||
|
||||
@@ -63,7 +63,7 @@ class APU extends AudioWorkletProcessor {
|
||||
|
||||
this.memory = memory;
|
||||
|
||||
this.snd = instance.exports.snd || platform_instance.exports.gesSnd;
|
||||
this.snd = instance.exports.snd || platform_instance.exports.sndGes;
|
||||
|
||||
this.port.postMessage(2);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<div id="uw8">
|
||||
<a href="https://exoticorn.github.io/microw8">MicroW8</a> 0.2.1
|
||||
<a href="https://exoticorn.github.io/microw8">MicroW8</a> 0.2.2
|
||||
</div>
|
||||
<div id="centered">
|
||||
<canvas class="screen" id="screen" width="320" height="240">
|
||||
|
||||
@@ -263,6 +263,10 @@ export default function MicroW8(screen, config = {}) {
|
||||
window.addEventListener('blur', () => updateVisibility(false), { signal: abortController.signal });
|
||||
updateVisibility(document.hasFocus());
|
||||
|
||||
if (instance.exports.start) {
|
||||
instance.exports.start();
|
||||
}
|
||||
|
||||
function mainloop() {
|
||||
if (!keepRunning) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user