mirror of
https://github.com/exoticorn/microw8.git
synced 2026-06-28 05:49:37 +02:00
Compare commits
169 Commits
v0.1.1
..
b9b2c2c927
| Author | SHA1 | Date | |
|---|---|---|---|
| b9b2c2c927 | |||
| 9208c0e652 | |||
| ede0484e9b | |||
| e14db77f26 | |||
| 3ca10fc85a | |||
| 3c4fd20d70 | |||
| 14e31d02fc | |||
| 5e54716007 | |||
| 44566ce1a3 | |||
| 8d860a7a86 | |||
| 9fc3353599 | |||
| fd10378e4d | |||
| 0659f8a57e | |||
| f4fa9e9c62 | |||
| 4c1ce8075e | |||
| 94764d631e | |||
| 8913fdaf6b | |||
| 195376cd28 | |||
| bf14d4bd3f | |||
| 0c68328700 | |||
| fb3fd5e8bb | |||
| b85f96b0bb | |||
| b69055b58d | |||
| 0878aa3f02 | |||
| 5cd1ecb98a | |||
| 842cf9f139 | |||
| d5ed809dc7 | |||
| 285d9aee2c | |||
| 4e18d38538 | |||
| d4bad3ba8b | |||
| 6ddda24156 | |||
| 7003db3a05 | |||
| 8258bc1854 | |||
| a651107104 | |||
| 22c35e37f4 | |||
| 805c939097 | |||
| 440e150896 | |||
| 77b2e27346 | |||
| 09e4fcbf14 | |||
| c8a9f533ef | |||
| 4a1d607bcb | |||
| bbb75838cd | |||
| dbeb242fb2 | |||
| 4fe4bce0ad | |||
| fe86153562 | |||
| c9dadaca2e | |||
| 5dc3e281ce | |||
| ce3afb821f | |||
| 2652a351ad | |||
| 9109722409 | |||
| f861c262a1 | |||
| bbfb5eba49 | |||
| 9e19ff1761 | |||
| 5d85aeac09 | |||
| 30eb953d5d | |||
| abdf780533 | |||
| fc05a54e2c | |||
| b33099c828 | |||
| d478d3ad49 | |||
| 502852e59a | |||
| 5efa8b3465 | |||
| daf2a02cd8 | |||
| 8d5374a867 | |||
| 142b6a4c15 | |||
| 877fceb089 | |||
| f0ba0f2b99 | |||
| 760664eb77 | |||
| 465e66ff4b | |||
| e4579d81bc | |||
| 1f5042059c | |||
| 499bb02f2c | |||
| 57a92ba79a | |||
| 7ec1e68a00 | |||
| 539d19e0d7 | |||
| e9a5f702b4 | |||
| ba0b037ec2 | |||
| 0130d1c906 | |||
| 379ece5cbf | |||
| c9c5cb76bd | |||
| a6d6615231 | |||
| fbc86fa78d | |||
| eb724e8785 | |||
| f559c5b7d4 | |||
| 9dabf75732 | |||
| b0adf7748d | |||
| 7aa70ef39d | |||
| 2ce91ef49c | |||
| 7caad08b7c | |||
| 1f6de62e5d | |||
| caeaa82787 | |||
| e0450c9039 | |||
| 95d0d92a6f | |||
| 7a6dd0ab6d | |||
| e7a00dd9c6 | |||
| a02243d98c | |||
| 599873890a | |||
| 8e9bb002bc | |||
| b2b990333e | |||
| d1556f7be8 | |||
| 9f548cd6f0 | |||
| 7cea4eebd3 | |||
| 3f67e92c5c | |||
| a2714f25e4 | |||
| 7e203d93e6 | |||
| e44c87d1f6 | |||
| 614b7cf358 | |||
| c42a484adb | |||
| 3a5f2bf865 | |||
| 2dee1b30a4 | |||
| 42f7887ab2 | |||
| 4c82f4ad02 | |||
| 4dd8c3b029 | |||
| 2bf8938183 | |||
| 491bf88ade | |||
| e05701300c | |||
| df0c169d54 | |||
| 61941bceeb | |||
| 8fa64519e4 | |||
| 7a52ce4e4c | |||
| 6f20d303c8 | |||
| a5edeb21d8 | |||
| 2839fe5be4 | |||
| 893158e136 | |||
| 7c5f43f152 | |||
| f32b0762b0 | |||
| 9ebb6b6d34 | |||
| 8a10b99eeb | |||
| 6c064a1dd8 | |||
| 37f12f5a2c | |||
| 8ad2885a55 | |||
| 1917057b81 | |||
| 82c1ddb867 | |||
| 8713aa8930 | |||
| 0f82e6e711 | |||
| 0ade24ebf6 | |||
| 29186c806f | |||
| b626d2609a | |||
| 39ead8220f | |||
| ce18a8a162 | |||
| a15e796489 | |||
| f178076b86 | |||
| 81adcf0198 | |||
| 780caf965a | |||
| 2033f9a172 | |||
| 0d514c7dd3 | |||
| a8eb3bda27 | |||
| 8b765a5742 | |||
| 7197c11586 | |||
| 99a423619e | |||
| 9063e872d3 | |||
| 85240599e8 | |||
| 35ec5fdb59 | |||
| a6a82ff5a1 | |||
| 973814a629 | |||
| 00d21b4745 | |||
| d11b46576a | |||
| add49a1f8b | |||
| 8815a8e02e | |||
| eb7c33d412 | |||
| 47ad3b4f30 | |||
| 0f668fb6e9 | |||
| f876f59e80 | |||
| 44b8656f29 | |||
| 90467f7c5b | |||
| c56196bd2e | |||
| 266493ca1c | |||
| 4c75ba2e44 | |||
| f1493ebded | |||
| 5d41733142 |
@@ -27,12 +27,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: sudo apt-get install -y libxkbcommon-dev
|
run: sudo apt-get install -y libxkbcommon-dev libasound2-dev
|
||||||
if: matrix.os == 'ubuntu-latest'
|
if: matrix.os == 'ubuntu-latest'
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
- name: Cache build dirs
|
- name: Cache build dirs
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.cargo/bin/
|
~/.cargo/bin/
|
||||||
@@ -44,7 +44,7 @@ jobs:
|
|||||||
- name: Build
|
- name: Build
|
||||||
run: cargo build --release --verbose
|
run: cargo build --release --verbose
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: uw8-${{ matrix.build }}
|
name: uw8-${{ matrix.build }}
|
||||||
path: target/release/${{ matrix.exe }}
|
path: target/release/${{ matrix.exe }}
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
- name: build_and_deploy
|
- name: build_and_deploy
|
||||||
uses: shalzz/zola-deploy-action@v0.14.1
|
uses: shalzz/zola-deploy-action@70a101a14bbdeed13e7a42a9ed06b35c9e9e826e
|
||||||
env:
|
env:
|
||||||
# Target branch
|
# Target branch
|
||||||
PAGES_BRANCH: gh-pages
|
PAGES_BRANCH: gh-pages
|
||||||
BUILD_DIR: site
|
BUILD_DIR: site
|
||||||
# Provide personal access token
|
# Provide personal access token
|
||||||
TOKEN: $GITHUB_ACTOR:${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
Generated
+3502
-768
File diff suppressed because it is too large
Load Diff
+21
-7
@@ -1,17 +1,31 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "uw8"
|
name = "uw8"
|
||||||
version = "0.1.1"
|
version = "0.4.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["native", "browser"]
|
||||||
|
native = ["wasmtime", "uw8-window", "cpal", "rubato" ]
|
||||||
|
browser = ["warp", "tokio", "tokio-stream", "webbrowser"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wasmtime = "0.30"
|
wasmtime = { git = "https://github.com/bytecodealliance/wasmtime.git", rev = "0f48f939b9870036562ca02ff21253547a9f1a5c", optional = true }
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
minifb = { version = "0.20", default-features = false, features = ["x11"] }
|
env_logger = "0.11.3"
|
||||||
notify = "4"
|
log = "0.4"
|
||||||
pico-args = "0.4"
|
uw8-window = { path = "uw8-window", optional = true }
|
||||||
curlywas = { git = "https://github.com/exoticorn/curlywas.git", rev = "196719b" }
|
notify-debouncer-mini = { version = "0.4.1", default-features = false }
|
||||||
|
pico-args = "0.5"
|
||||||
|
curlywas = { git = "https://github.com/exoticorn/curlywas.git", rev = "0e7ea50" }
|
||||||
wat = "1"
|
wat = "1"
|
||||||
uw8-tool = { path = "uw8-tool" }
|
uw8-tool = { path = "uw8-tool" }
|
||||||
same-file = "1"
|
same-file = "1"
|
||||||
|
warp = { version = "0.3.6", optional = true }
|
||||||
|
tokio = { version = "1.37.0", features = ["sync", "rt"], optional = true }
|
||||||
|
tokio-stream = { version = "0.1.15", features = ["sync"], optional = true }
|
||||||
|
webbrowser = { version = "0.8.13", optional = true }
|
||||||
|
ansi_term = "0.12.1"
|
||||||
|
cpal = { version = "0.15.3", optional = true }
|
||||||
|
rubato = { version = "0.12.0", optional = true }
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ See [here](https://exoticorn.github.io/microw8/) for more information and docs.
|
|||||||
|
|
||||||
## Downloads
|
## Downloads
|
||||||
|
|
||||||
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.1.0/microw8-0.1.0-linux.tgz)
|
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.4.1/microw8-0.4.1-linux.tgz)
|
||||||
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.1.0/microw8-0.1.0-macos.tgz)
|
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.4.1/microw8-0.4.1-macos.tgz)
|
||||||
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.1.0/microw8-0.1.0-windows.zip)
|
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.4.1/microw8-0.4.1-windows.zip)
|
||||||
|
|
||||||
The download includes
|
The download includes
|
||||||
|
|
||||||
@@ -35,6 +35,7 @@ Runs <file> which can be a binary WebAssembly module, an `.uw8` cart, a wat (Web
|
|||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
|
||||||
|
-b, --browser : Run in browser instead of using native runtime
|
||||||
-t, --timeout FRAMES : Sets the timeout in frames (1/60s)
|
-t, --timeout FRAMES : Sets the timeout in frames (1/60s)
|
||||||
-w, --watch : Reloads the given file every time it changes on disk.
|
-w, --watch : Reloads the given file every time it changes on disk.
|
||||||
-p, --pack : Pack the file into an .uw8 cart before running it and print the resulting size.
|
-p, --pack : Pack the file into an .uw8 cart before running it and print the resulting size.
|
||||||
@@ -42,6 +43,27 @@ Options:
|
|||||||
-l LEVEL, --level LEVEL : Compression level (0-9). Higher compression levels are really slow.
|
-l LEVEL, --level LEVEL : Compression level (0-9). Higher compression levels are really slow.
|
||||||
-o FILE, --output FILE : Write the loaded and optionally packed cart back to disk.
|
-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>
|
uw8 pack [<options>] <infile> <outfile>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import "env.memory" memory(4);
|
include "../include/microw8-api.cwa"
|
||||||
import "env.printString" fn printString(i32);
|
|
||||||
|
|
||||||
export fn upd() {
|
export fn upd() {
|
||||||
printString(0x20000);
|
printString(0x20000);
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
// port of cracklebass by pestis (originally on TIC-80)
|
||||||
|
|
||||||
|
include "../include/microw8-api.cwa"
|
||||||
|
|
||||||
|
const MUSIC_DATA = 0x20000;
|
||||||
|
|
||||||
|
export fn upd() {
|
||||||
|
let inline t = 32!32 * 6 / 100;
|
||||||
|
let inline p = t / 1024;
|
||||||
|
|
||||||
|
let channel:i32;
|
||||||
|
|
||||||
|
loop channels {
|
||||||
|
let inline e = t * channel?MUSIC_DATA / 8;
|
||||||
|
let lazy pattern = (8 * channel + p)?(MUSIC_DATA + 56);
|
||||||
|
let lazy n = !!pattern * (8 * pattern + e / 16 % 8)?MUSIC_DATA;
|
||||||
|
let inline prev_ctrl = (channel * 6)?80;
|
||||||
|
(channel * 6)?80 = if n {
|
||||||
|
let inline base_note = 12 + 12 * channel?(MUSIC_DATA + 4) + n;
|
||||||
|
let inline pitch_drop = e % 16 * channel?(MUSIC_DATA + 94);
|
||||||
|
let inline key_pattern = p?(MUSIC_DATA + 8*4 + 56);
|
||||||
|
let inline key = select(key_pattern, (8 * key_pattern + t / 128 % 8)?MUSIC_DATA, 1);
|
||||||
|
(channel * 6)?83 = base_note - pitch_drop / 4 + key;
|
||||||
|
prev_ctrl & 0xfc | (e / 8 & 2) | 1
|
||||||
|
} else {
|
||||||
|
prev_ctrl & 0xfe
|
||||||
|
};
|
||||||
|
|
||||||
|
branch_if (channel := channel + 1) < 4: channels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data 80 {
|
||||||
|
i8(
|
||||||
|
0x44, 0, 0, 0, 0x50, 0x40,
|
||||||
|
0x4, 0x50, 0, 0, 0x80, 0x80,
|
||||||
|
0x40, 0x80, 0, 0, 0x40, 0x40,
|
||||||
|
0, 0, 0, 0, 0x50, 0x50
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
data MUSIC_DATA {
|
||||||
|
i8(
|
||||||
|
16, 2, 8, 8, 1, 2, 2, 3, 1, 0,
|
||||||
|
1,13,16, 0, 1, 8, 1, 0, 1,13,
|
||||||
|
16, 1, 1, 8, 1, 0, 8,13,13, 0,
|
||||||
|
16,13, 1, 0, 1, 0, 1, 0, 1, 1,
|
||||||
|
1, 0, 0, 0, 1, 0,13, 1, 1, 1,
|
||||||
|
6, 8, 1, 1, 6, 8, 1, 1, 2, 1,
|
||||||
|
2, 1, 2, 0, 0, 0, 0, 3, 3, 3,
|
||||||
|
5, 0, 0, 2, 1, 2, 1, 2, 1, 2,
|
||||||
|
0, 4, 4, 0, 4, 4, 4, 4, 0, 0,
|
||||||
|
0, 0, 6, 6, 0, 0, 0, 8
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
/*
|
||||||
|
This program renders a rotating 3D wireframe cube on a 2D screen using the MicroW8 platform.
|
||||||
|
|
||||||
|
code : zbyti
|
||||||
|
date : 2025.04.15
|
||||||
|
platform : MicroW8 0.4.1
|
||||||
|
|
||||||
|
https://exoticorn.github.io/microw8/
|
||||||
|
https://exoticorn.github.io/microw8/docs/
|
||||||
|
|
||||||
|
https://github.com/exoticorn/microw8
|
||||||
|
https://github.com/exoticorn/curlywas
|
||||||
|
|
||||||
|
https://developer.mozilla.org/en-US/docs/WebAssembly
|
||||||
|
|
||||||
|
https://en.wikipedia.org/wiki/Rotation_matrix
|
||||||
|
*/
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// MicroW8 API
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//include "../include/microw8-api.cwa"
|
||||||
|
|
||||||
|
// Import memory allocation: 4 pages (64KB) of memory (256KB total)
|
||||||
|
import "env.memory" memory(4);
|
||||||
|
|
||||||
|
// Import MicroW8 API functions for graphics and math operations
|
||||||
|
import "env.cls" fn cls(i32); // Clears the screen
|
||||||
|
import "env.time" fn time() -> f32; // Returns the current time as a float for animation
|
||||||
|
import "env.sin" fn sin(f32) -> f32; // Computes the sine of an angle (in radians)
|
||||||
|
import "env.cos" fn cos(f32) -> f32; // Computes the cosine of an angle (in radians)
|
||||||
|
import "env.line" fn line(f32, f32, f32, f32, i32); // Draws a line between two 2D points with a color
|
||||||
|
|
||||||
|
// Define the starting address for user memory
|
||||||
|
const USER_MEM = 0x14000;
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// CONSTANTS
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Screen and rendering constants
|
||||||
|
const CENTER_X = 320.0 / 2.0; // X-coordinate of the screen center (320px width)
|
||||||
|
const CENTER_Y = 240.0 / 2.0; // Y-coordinate of the screen center (240px height)
|
||||||
|
const ROTATE_SPEED = 0.5; // Speed at which the cube rotates
|
||||||
|
const PI = 3.14159265; // Mathematical constant Pi for angle conversions
|
||||||
|
const RADIAN = PI / 180.0; // Conversion factor from degrees to radians
|
||||||
|
const SCALE = 65.0; // Scaling factor to adjust the size of the cube on screen
|
||||||
|
const PERSPECTIVE = SCALE * 0.5; // Perspective factor
|
||||||
|
const LINE_COLOR = 0xBF; // Color value for drawing the cube's edges (hexadecimal)
|
||||||
|
|
||||||
|
// Memory layout for vertex data
|
||||||
|
const V_BASE = USER_MEM; // Base vertices stored as 8-bit integers (i8)
|
||||||
|
const V_ROT = V_BASE + (3 * 8); // Rotated vertices stored as 32-bit floats (f32), offset after V_BASE
|
||||||
|
|
||||||
|
// Offsets for accessing X, Y, Z coordinates in memory (in bytes)
|
||||||
|
const X = 0; // Offset for X coordinate
|
||||||
|
const Y = 4; // Offset for Y coordinate
|
||||||
|
const Z = 8; // Offset for Z coordinate
|
||||||
|
|
||||||
|
// Memory offsets for each rotated vertex (8 vertices, 3 floats each, 12 bytes per vertex)
|
||||||
|
const VA = V_ROT + (0 * 3 * 4); // Vertex A (front-top-left)
|
||||||
|
const VB = V_ROT + (1 * 3 * 4); // Vertex B (front-top-right)
|
||||||
|
const VC = V_ROT + (2 * 3 * 4); // Vertex C (front-bottom-right)
|
||||||
|
const VD = V_ROT + (3 * 3 * 4); // Vertex D (front-bottom-left)
|
||||||
|
const VE = V_ROT + (4 * 3 * 4); // Vertex E (back-top-left)
|
||||||
|
const VF = V_ROT + (5 * 3 * 4); // Vertex F (back-top-right)
|
||||||
|
const VG = V_ROT + (6 * 3 * 4); // Vertex G (back-bottom-right)
|
||||||
|
const VH = V_ROT + (7 * 3 * 4); // Vertex H (back-bottom-left)
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Function to rotate the cube around X, Y, and Z axes based on time
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
fn rotate() {
|
||||||
|
// Calculate the rotation angle using the current time for continuous animation
|
||||||
|
let angle = time() * ROTATE_SPEED;
|
||||||
|
|
||||||
|
// Precompute sine and cosine values once for efficiency
|
||||||
|
let sn = sin(angle);
|
||||||
|
let cs = cos(angle);
|
||||||
|
|
||||||
|
let calc = 0;
|
||||||
|
loop calc { // Iterate over all 8 vertices
|
||||||
|
// Calculate memory offset for current vertex (12 bytes per vertex: 4 bytes each for X, Y, Z)
|
||||||
|
let v = calc * 12;
|
||||||
|
let inline vX = v + X;
|
||||||
|
let inline vY = v + Y;
|
||||||
|
let inline vZ = v + Z;
|
||||||
|
|
||||||
|
// Load original vertex coordinates from V_BASE (stored as i8, converted to f32)
|
||||||
|
let x = i32.load8_s(V_BASE+(calc * 3 + 0)) as f32; // X coordinate
|
||||||
|
let y = i32.load8_s(V_BASE+(calc * 3 + 1)) as f32; // Y coordinate
|
||||||
|
let z = i32.load8_s(V_BASE+(calc * 3 + 2)) as f32; // Z coordinate
|
||||||
|
|
||||||
|
// Rotate around Z-axis: updates X and Y, Z stays the same
|
||||||
|
(vX)$V_ROT = x * cs - y * sn;
|
||||||
|
(vY)$V_ROT = x * sn + y * cs;
|
||||||
|
(vZ)$V_ROT = z;
|
||||||
|
|
||||||
|
// Rotate around Y-axis: updates X and Z, Y stays the same
|
||||||
|
x = (vX)$V_ROT;
|
||||||
|
z = (vZ)$V_ROT;
|
||||||
|
(vX)$V_ROT = x * cs + z * sn;
|
||||||
|
(vZ)$V_ROT = z * cs - x * sn;
|
||||||
|
|
||||||
|
// Rotate around X-axis: updates Y and Z, X stays the same
|
||||||
|
y = (vY)$V_ROT;
|
||||||
|
z = (vZ)$V_ROT;
|
||||||
|
(vY)$V_ROT = y * cs - z * sn;
|
||||||
|
(vZ)$V_ROT = y * sn + z * cs;
|
||||||
|
|
||||||
|
// Move to the next vertex until all 8 are processed
|
||||||
|
branch_if (calc +:= 1) < 8: calc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Function to project 3D vertices to 2D screen space and draw the cube's edges
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
fn drawLines() {
|
||||||
|
let scale = 0;
|
||||||
|
loop scale { // Scale and center each vertex for 2D projection
|
||||||
|
// Calculate memory offset for current vertex (12 bytes per vertex: 4 bytes each for X, Y, Z)
|
||||||
|
let v = scale * 12;
|
||||||
|
let inline vX = v + X;
|
||||||
|
let inline vY = v + Y;
|
||||||
|
|
||||||
|
// Load Z coordinate of current vertex (from rotated vertex data)
|
||||||
|
let inline z = (v + Z)$V_ROT;
|
||||||
|
|
||||||
|
// Calculate perspective factor:
|
||||||
|
// - When z=0 (midpoint), factor=1.0 (no scaling)
|
||||||
|
// - Positive z (farther away) → factor<1.0 (objects appear smaller)
|
||||||
|
// - Negative z (closer) → factor>1.0 (objects appear larger)
|
||||||
|
let lazy factor = PERSPECTIVE / (PERSPECTIVE + z) * SCALE;
|
||||||
|
|
||||||
|
// Apply perspective projection, scaling and shift to center for X and Y coordinats:
|
||||||
|
// 1. Multiply by perspective factor
|
||||||
|
// 2. Apply global scaling (SCALE constant)
|
||||||
|
// 3. Center on screen (CENTER_X, CENTER_Y)
|
||||||
|
(vX)$V_ROT = (vX)$V_ROT * factor + CENTER_X; // X
|
||||||
|
(vY)$V_ROT = (vY)$V_ROT * factor + CENTER_Y; // Y
|
||||||
|
|
||||||
|
// Continue until all 8 vertices are scaled
|
||||||
|
branch_if (scale +:= 1) < 8: scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the front face of the cube (vertices A-B-C-D)
|
||||||
|
line(VA$X, VA$Y, VB$X, VB$Y, LINE_COLOR);
|
||||||
|
line(VB$X, VB$Y, VC$X, VC$Y, LINE_COLOR);
|
||||||
|
line(VC$X, VC$Y, VD$X, VD$Y, LINE_COLOR);
|
||||||
|
line(VD$X, VD$Y, VA$X, VA$Y, LINE_COLOR);
|
||||||
|
|
||||||
|
// Draw the back face of the cube (vertices E-F-G-H)
|
||||||
|
line(VE$X, VE$Y, VF$X, VF$Y, LINE_COLOR);
|
||||||
|
line(VF$X, VF$Y, VG$X, VG$Y, LINE_COLOR);
|
||||||
|
line(VG$X, VG$Y, VH$X, VH$Y, LINE_COLOR);
|
||||||
|
line(VH$X, VH$Y, VE$X, VE$Y, LINE_COLOR);
|
||||||
|
|
||||||
|
// Draw edges connecting front and back faces
|
||||||
|
line(VA$X, VA$Y, VE$X, VE$Y, LINE_COLOR);
|
||||||
|
line(VB$X, VB$Y, VF$X, VF$Y, LINE_COLOR);
|
||||||
|
line(VC$X, VC$Y, VG$X, VG$Y, LINE_COLOR);
|
||||||
|
line(VD$X, VD$Y, VH$X, VH$Y, LINE_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Entry point for INIT type function, starts first
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/*
|
||||||
|
export fn start() {
|
||||||
|
let init = 0;
|
||||||
|
loop init {
|
||||||
|
// Calculate memory offset for current vertex (12 bytes per vertex: 4 bytes each for X, Y, Z)
|
||||||
|
let v = init * 12;
|
||||||
|
|
||||||
|
(v + X)$V_ROT = i32.load8_s(V_BASE+(init * 3) + 0) as f32;
|
||||||
|
(v + Y)$V_ROT = i32.load8_s(V_BASE+(init * 3) + 1) as f32;
|
||||||
|
(v + Z)$V_ROT = i32.load8_s(V_BASE+(init * 3) + 2) as f32;
|
||||||
|
branch_if (init +:= 1) < 8: init;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Main update function called every frame to refresh the screen
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export fn upd() {
|
||||||
|
cls(0); // Clear the screen with color 0 (black)
|
||||||
|
rotate(); // Perform cube rotation calculations
|
||||||
|
drawLines(); // Draw the rotated cube on the screen
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// DATA
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/*
|
||||||
|
Initial vertex data for the cube (8 vertices, each with X, Y, Z as 8-bit signed integers)
|
||||||
|
Each vertex represents a corner of a unit cube centered at the origin
|
||||||
|
|
||||||
|
F - front, B - back, L - left, R - right, U - up, D - down
|
||||||
|
*/
|
||||||
|
data V_BASE { // 3 * 8 -> 3 bytes per vertex * 8 vertices = 24 bytes
|
||||||
|
i8(
|
||||||
|
// X Y Z
|
||||||
|
-1, -1, 1, // FLU Vertex A
|
||||||
|
1, -1, 1, // FRU Vertex B
|
||||||
|
1, 1, 1, // FRD Vertex C
|
||||||
|
-1, 1, 1, // FLD Vertex D
|
||||||
|
-1, -1, -1, // BLU Vertex E
|
||||||
|
1, -1, -1, // BRU Vertex F
|
||||||
|
1, 1, -1, // BRD Vertex G
|
||||||
|
-1, 1, -1 // BLD Vertex H
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/*
|
||||||
|
Storage for rotated vertex data (8 vertices, each with X, Y, Z as 32-bit signed floats)
|
||||||
|
Initialized to zero and updated during rotation
|
||||||
|
*/
|
||||||
|
data V_ROT { // 4 * 3 * 8 -> 12 bytes per vertex * 8 vertices = 96 bytes
|
||||||
|
f32(
|
||||||
|
// X Y Z
|
||||||
|
0.0, 0.0, 0.0, // VA -> Vertex A
|
||||||
|
0.0, 0.0, 0.0, // VB -> Vertex B
|
||||||
|
0.0, 0.0, 0.0, // VC -> Vertex C
|
||||||
|
0.0, 0.0, 0.0, // VD -> Vertex D
|
||||||
|
0.0, 0.0, 0.0, // VE -> Vertex E
|
||||||
|
0.0, 0.0, 0.0, // VF -> Vertex F
|
||||||
|
0.0, 0.0, 0.0, // VG -> Vertex G
|
||||||
|
0.0, 0.0, 0.0 // VH -> Vertex H
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// SNIPPETS
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/*
|
||||||
|
let tmp: f32;
|
||||||
|
tmp = -123.0;
|
||||||
|
f32.store(tmp, V_ROT);
|
||||||
|
tmp = f32.load(V_ROT);
|
||||||
|
printInt(tmp as i32);
|
||||||
|
*/
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
@@ -1,11 +1,4 @@
|
|||||||
import "env.time" fn time() -> f32;
|
include "../include/microw8-api.cwa"
|
||||||
import "env.circle" fn circle(f32, f32, f32, i32);
|
|
||||||
import "env.cls" fn cls(i32);
|
|
||||||
import "env.randomSeed" fn seed(i32);
|
|
||||||
import "env.randomf" fn randomf() -> f32;
|
|
||||||
import "env.sin" fn sin(f32) -> f32;
|
|
||||||
import "env.cos" fn cos(f32) -> f32;
|
|
||||||
import "env.fmod" fn fmod(f32, f32) -> f32;
|
|
||||||
|
|
||||||
export fn upd() {
|
export fn upd() {
|
||||||
cls(0);
|
cls(0);
|
||||||
@@ -15,10 +8,10 @@ export fn upd() {
|
|||||||
let inline rocket = i #>> 9;
|
let inline rocket = i #>> 9;
|
||||||
let lazy local_time = fmod(time() + rocket as f32 / 5 as f32, 2 as f32);
|
let lazy local_time = fmod(time() + rocket as f32 / 5 as f32, 2 as f32);
|
||||||
let lazy rocket = rocket + nearest(time() - local_time) as i32 * 10;
|
let lazy rocket = rocket + nearest(time() - local_time) as i32 * 10;
|
||||||
seed(rocket);
|
randomSeed(rocket);
|
||||||
let inline x = randomf() * 645 as f32;
|
let inline x = randomf() * 645 as f32;
|
||||||
let y = randomf() * 133 as f32;
|
let y = randomf() * 133 as f32;
|
||||||
let lazy angle = { seed(i); randomf() } * 44 as f32;
|
let lazy angle = { randomSeed(i); randomf() } * 44 as f32;
|
||||||
let inline dx = sin(angle);
|
let inline dx = sin(angle);
|
||||||
let inline dy = cos(angle);
|
let inline dy = cos(angle);
|
||||||
let lazy dist = local_time * (randomf() * 44 as f32);
|
let lazy dist = local_time * (randomf() * 44 as f32);
|
||||||
|
|||||||
@@ -1,38 +1,30 @@
|
|||||||
import "env.memory" memory(4);
|
include "../include/microw8-api.cwa"
|
||||||
|
|
||||||
import "env.cls" fn cls(i32);
|
|
||||||
import "env.printString" fn printString(i32);
|
|
||||||
import "env.printChar" fn printChar(i32);
|
|
||||||
import "env.setCursorPosition" fn setCursor(i32, i32);
|
|
||||||
import "env.setTextColor" fn setTextColor(i32);
|
|
||||||
import "env.line" fn line(f32, f32, f32, f32, i32);
|
|
||||||
import "env.isButtonTriggered" fn triggered(i32) -> i32;
|
|
||||||
|
|
||||||
global mut mode: i32 = 0;
|
global mut mode: i32 = 0;
|
||||||
|
|
||||||
export fn upd() {
|
export fn upd() {
|
||||||
cls(0);
|
cls(0);
|
||||||
|
|
||||||
if triggered(4) {
|
if isButtonTriggered(BUTTON_A) {
|
||||||
mode = !mode;
|
mode = !mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
setTextColor(15);
|
setTextColor(15);
|
||||||
printString(mode * 0x20000);
|
printString(mode * USER_MEM);
|
||||||
|
|
||||||
let y: i32;
|
let y: i32;
|
||||||
loop y {
|
loop y {
|
||||||
line(0 as f32, (y * 9 + 39) as f32, (14+16*9) as f32, (y * 9 + 39) as f32, 1);
|
line(0 as f32, (y * 9 + 39) as f32, (14+16*9) as f32, (y * 9 + 39) as f32, 1);
|
||||||
line((y * 9 + 15) as f32, 24 as f32, (y * 9 + 15) as f32, (38+16*9) as f32, 1);
|
line((y * 9 + 15) as f32, 24 as f32, (y * 9 + 15) as f32, (38+16*9) as f32, 1);
|
||||||
setTextColor(15);
|
setTextColor(15);
|
||||||
setCursor(y * 9 + 16, 24);
|
setCursorPosition(y * 9 + 16, 24);
|
||||||
let lazy hexChar = select(y < 10, y + 48, y + 87);
|
let lazy hexChar = select(y < 10, y + 48, y + 87);
|
||||||
printChar(hexChar);
|
printChar(hexChar);
|
||||||
setCursor(0, y * 9 + 24+16);
|
setCursorPosition(0, y * 9 + 24+16);
|
||||||
printChar(hexChar);
|
printChar(hexChar);
|
||||||
let x = 0;
|
let x = 0;
|
||||||
loop x {
|
loop x {
|
||||||
setCursor(x * 9 + 16, y * 9 + 24+16);
|
setCursorPosition(x * 9 + 16, y * 9 + 24+16);
|
||||||
setTextColor(select(mode, x + y * 16, -9));
|
setTextColor(select(mode, x + y * 16, -9));
|
||||||
if y >= 2 | mode {
|
if y >= 2 | mode {
|
||||||
printChar(select(mode, 0xa4, x + y * 16));
|
printChar(select(mode, 0xa4, x + y * 16));
|
||||||
@@ -47,6 +39,6 @@ data 0 {
|
|||||||
"Default font: (press " i8(0xcc) " for palette)" i8(5, 0)
|
"Default font: (press " i8(0xcc) " for palette)" i8(5, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
data 0x20000 {
|
data USER_MEM {
|
||||||
"Default palette: (press " i8(0xcc) " for font)" i8(5, 0)
|
"Default palette: (press " i8(0xcc) " for font)" i8(5, 0)
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,4 @@
|
|||||||
import "env.memory" memory(4);
|
include "../include/microw8-api.cwa"
|
||||||
|
|
||||||
import "env.cls" fn cls(i32);
|
|
||||||
import "env.time" fn time() -> f32;
|
|
||||||
import "env.line" fn line(f32, f32, f32, f32, i32);
|
|
||||||
import "env.sin" fn sin(f32) -> f32;
|
|
||||||
import "env.cos" fn cos(f32) -> f32;
|
|
||||||
|
|
||||||
export fn upd() {
|
export fn upd() {
|
||||||
cls(0);
|
cls(0);
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
Plasma effect (sizecoding)
|
||||||
|
|
||||||
|
Combines sine waves to create a 2D pixel pattern with intensity-based colors.
|
||||||
|
|
||||||
|
code : zbyti & Grok 3
|
||||||
|
date : 2025.04.17
|
||||||
|
platform : MicroW8 0.4.1
|
||||||
|
*/
|
||||||
|
|
||||||
|
include "../include/microw8-api.cwa"
|
||||||
|
|
||||||
|
// Constants for color, math, and screen dimensions
|
||||||
|
const BASE_COLOR = 0xF0; // Base color value for pixel coloring
|
||||||
|
const PI = 3.14159265; // Mathematical constant π
|
||||||
|
const RAD = PI / 180.0; // Conversion factor from degrees to radians
|
||||||
|
const SCR_X = 320; // Screen width in pixels
|
||||||
|
const SCR_Y = 240; // Screen height in pixels
|
||||||
|
const SCR_SIZE = SCR_X * SCR_Y; // Screen size in bytes
|
||||||
|
|
||||||
|
// Global variables to track animation phases
|
||||||
|
global mut phaseX = 0; // Phase offset for X-axis wave animation
|
||||||
|
global mut phaseY = 0; // Phase offset for Y-axis wave animation
|
||||||
|
|
||||||
|
// Update function called each frame to render the plasma effect
|
||||||
|
export fn upd() {
|
||||||
|
let i = 0;
|
||||||
|
loop i {
|
||||||
|
// Calculate pixel coordinates from linear index
|
||||||
|
let lazy x = i % SCR_X; // X-coordinate (column)
|
||||||
|
let lazy y = i / SCR_X; // Y-coordinate (row)
|
||||||
|
|
||||||
|
// Compute three sine waves with different frequencies and phases
|
||||||
|
let inline val1 = sin(RAD * 2.25 * (x + phaseX) as f32); // Wave along X-axis
|
||||||
|
let inline val2 = sin(RAD * 3.25 * (y + phaseY) as f32); // Wave along Y-axis
|
||||||
|
let inline val3 = sin(RAD * 1.25 * (x + y + phaseX) as f32); // Diagonal wave
|
||||||
|
|
||||||
|
// Combine waves, scale to color range, and convert to integer
|
||||||
|
let inline c = BASE_COLOR + ((val1 + val2 + val3) * 4.75) as i32;
|
||||||
|
|
||||||
|
// Set pixel color based on computed intensity
|
||||||
|
setPixel(x, y, c);
|
||||||
|
|
||||||
|
// Continue loop until all pixels are processed
|
||||||
|
branch_if (i +:= 1) < (SCR_SIZE): i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update phase offsets for animation (different speeds for dynamic effect)
|
||||||
|
phaseX += 1; // Increment X-phase for horizontal wave movement
|
||||||
|
phaseY += 2; // Increment Y-phase for vertical wave movement
|
||||||
|
}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
Plasma effect (chars)
|
||||||
|
|
||||||
|
Combines sine waves to create a 2D pattern, maps to custom characters,
|
||||||
|
and renders to the 40x30 character grid with intensity-based colors.
|
||||||
|
|
||||||
|
code : zbyti (conversion & optimizations)
|
||||||
|
original : https://github.com/tebe6502/Mad-Pascal/blob/origin/samples/a8/demoeffects/plasma_2.pas
|
||||||
|
date : 2025.04.16
|
||||||
|
platform : MicroW8 0.4.1
|
||||||
|
*/
|
||||||
|
|
||||||
|
include "../include/microw8-api.cwa"
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Constants defining memory layout, screen dimensions, and effect parameters
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const MEM_END = 0x40000;
|
||||||
|
|
||||||
|
const CUSTOM_FONT = FONT + 0x100;
|
||||||
|
const CHARS = 0x20;
|
||||||
|
const BASE_COLOR = 0xF0;
|
||||||
|
|
||||||
|
const PI = 3.14159265;
|
||||||
|
const RAD = PI / 180.0;
|
||||||
|
|
||||||
|
const SCR_X = 320;
|
||||||
|
const SCR_Y = 240;
|
||||||
|
const SCR_W = SCR_X / 8; // 40
|
||||||
|
const SCR_H = SCR_Y / 8; // 30
|
||||||
|
const SCR_SIZE = SCR_X * SCR_Y;
|
||||||
|
|
||||||
|
const SIN_TABLE_SIZE = 128; // Number of entries in the sine lookup table
|
||||||
|
const SIN_TABLE_MASK = SIN_TABLE_SIZE - 1;
|
||||||
|
|
||||||
|
const SIN_TABLE = MEM_END - SIN_TABLE_SIZE; // Memory address for sine table
|
||||||
|
const ROW_BUFFER = SIN_TABLE - SCR_W; // Memory address for precomputed row buffer
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Global variables to track animation state
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
global mut phaseA = 1; // Phase offset for the first sine wave, controls animation
|
||||||
|
global mut phaseB = 5; // Phase offset for the second sine wave, controls animation
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/*
|
||||||
|
Logs a value to the console (STDOUT) with a prefix for debugging purposes
|
||||||
|
|
||||||
|
Args:
|
||||||
|
prefix : 4-character identifier (i32) to label the output
|
||||||
|
log : Integer value to log (i32)
|
||||||
|
|
||||||
|
Prints to console and returns to screen output
|
||||||
|
*/
|
||||||
|
fn console(prefix: i32, log: i32) {
|
||||||
|
printChar('\6'); // Switch output to console
|
||||||
|
printChar(prefix); // Print the prefix
|
||||||
|
printInt(log); // Print the integer value
|
||||||
|
printChar('\n\4'); // Print newline and switch back to screen output
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------
|
||||||
|
|
||||||
|
/*
|
||||||
|
Fills a sine table with precomputed values for fast lookups
|
||||||
|
|
||||||
|
Args:
|
||||||
|
adr : Memory address where the sine table is stored
|
||||||
|
size : Number of entries in the table
|
||||||
|
|
||||||
|
Computes: sin(i * 180/size * radians) * 255 for i = 0 to size-1, scaled to 0-255
|
||||||
|
*/
|
||||||
|
fn fillSin(adr: i32, size: i32) {
|
||||||
|
let i = 0;
|
||||||
|
let inline f = 180.00 / size as f32 * RAD;
|
||||||
|
loop i {
|
||||||
|
(adr+i)?0 = (sin(f * i as f32) * 255.0) as i32;
|
||||||
|
branch_if (i +:= 1) < size: i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/*
|
||||||
|
Initialization function called when the program starts
|
||||||
|
*/
|
||||||
|
export fn start() {
|
||||||
|
// Populate the sine table with values for fast wave calculations
|
||||||
|
fillSin(SIN_TABLE, SIN_TABLE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------
|
||||||
|
|
||||||
|
/*
|
||||||
|
Update function called each frame to render the plasma effect
|
||||||
|
*/
|
||||||
|
export fn upd() {
|
||||||
|
let pA = phaseA; // Local copy of phaseA to avoid modifying global during frame
|
||||||
|
let pB = phaseB; // Local copy of phaseB for the same reason
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
loop i {
|
||||||
|
// Wrap phase values to stay within sine table bounds using bitwise AND
|
||||||
|
pA &= SIN_TABLE_MASK;
|
||||||
|
pB &= SIN_TABLE_MASK;
|
||||||
|
|
||||||
|
// Combine two sine waves and store in row buffer
|
||||||
|
i?ROW_BUFFER = pA?SIN_TABLE + pB?SIN_TABLE;
|
||||||
|
|
||||||
|
pA += 3; // Shift phase for first sine wave (controls wave speed)
|
||||||
|
pB += 7; // Shift phase for second sine wave (different speed for variety)
|
||||||
|
|
||||||
|
branch_if (i +:= 1) < SCR_W: i;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
loop i {
|
||||||
|
let j = 0;
|
||||||
|
loop j {
|
||||||
|
// Combine wave values from row buffer for current position
|
||||||
|
// Use bitwise AND to clamp to 0-255, then scale to 0-15 for character index
|
||||||
|
let c = ((j?ROW_BUFFER + i?ROW_BUFFER) & 255) >> 4;
|
||||||
|
|
||||||
|
// Set text color based on intensity (base color + scaled value)
|
||||||
|
setTextColor(BASE_COLOR + c);
|
||||||
|
// Draw custom character corresponding to intensity
|
||||||
|
printChar(CHARS + c);
|
||||||
|
|
||||||
|
branch_if (j +:= 1) < SCR_W: j;
|
||||||
|
}
|
||||||
|
branch_if (i +:= 1) < SCR_H: i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update global phase offsets for the next frame to animate the pattern
|
||||||
|
phaseA += 0; // Increment phaseA to shift the pattern (+/- speed move)
|
||||||
|
phaseB += 1; // Increment phaseB to shift the pattern (+/- right/left move)
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/*
|
||||||
|
Custom font data defining 16 characters (8x8 pixels each)
|
||||||
|
Each character represents a different intensity level for the plasma effect
|
||||||
|
Pixels range from empty (0x00) to nearly full (0xFE or 0x7F) to simulate gradients
|
||||||
|
*/
|
||||||
|
data CUSTOM_FONT {
|
||||||
|
i8(
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
|
||||||
|
0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00,
|
||||||
|
0x00, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x00,
|
||||||
|
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0x00,
|
||||||
|
0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
|
||||||
|
0x00, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x00,
|
||||||
|
0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x38, 0x38, 0x38, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
include "../include/microw8-api.cwa"
|
||||||
|
|
||||||
|
export fn upd() {
|
||||||
|
printString(USER_MEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
data USER_MEM {
|
||||||
|
// clear screen, switch to graphics text mode, text scale 4
|
||||||
|
i8(12, 5, 30, 4)
|
||||||
|
// text color, position, print two lines
|
||||||
|
i8(15, 86, 31, 8, 80) "Hello," i8(8, 8, 8, 8, 8, 10) "MicroW8!"
|
||||||
|
// print same two lines with different color and slight offset
|
||||||
|
i8(15, 47, 31, 10, 82) "Hello," i8(8, 8, 8, 8, 8, 10) "MicroW8!" i8(0)
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
include "../include/microw8-api.cwa"
|
||||||
|
|
||||||
|
global mut frame = 0;
|
||||||
|
|
||||||
|
export fn upd() {
|
||||||
|
if frame % 16 == 0 {
|
||||||
|
let ch: i32;
|
||||||
|
loop channels {
|
||||||
|
playNote(ch, (ch * 32 + (frame / 16) % 32)?0x20000);
|
||||||
|
branch_if ch := (ch + 1) % 4: channels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frame = frame + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
data 0x20000 {
|
||||||
|
i8(
|
||||||
|
0x4e, 0x0, 0x0, 0x4c, 0x49, 0x0, 0x45, 0x47,
|
||||||
|
0x49, 0x47, 0x45, 0x44, 0x42, 0x0, 0x3d, 0x41,
|
||||||
|
0x44, 0x0, 0x0, 0x47, 0x49, 0x47, 0x45, 0x41,
|
||||||
|
0x44, 0x0, 0x0, 0x0, 0x42, 0x0, 0x0, 0x0,
|
||||||
|
|
||||||
|
0x25, 0, 0x49, 0x25, 0x25, 0, 0x49, 0x38,
|
||||||
|
0x25, 0, 0x49, 0x25, 0x25, 0, 0x49, 0x38,
|
||||||
|
0x25, 0, 0x49, 0x25, 0x25, 0, 0x49, 0x38,
|
||||||
|
0x25, 0, 0x49, 0x25, 0x25, 0, 0x49, 0x38,
|
||||||
|
|
||||||
|
0x2a, 0x0, 0x0, 0x0, 0x2d, 0x0, 0x0, 0x0,
|
||||||
|
0x2c, 0x0, 0x28, 0x0, 0x2a, 0x0, 0x0, 0x0,
|
||||||
|
0x25, 0x0, 0x0, 0x0, 0x29, 0x0, 0x0, 0x0,
|
||||||
|
0x2c, 0x0, 0x2d, 0x0, 0x2a, 0x0, 0x25, 0x0,
|
||||||
|
|
||||||
|
0x0, 0x0, 0x31, 0x0, 0x34, 0x0, 0x0, 0x36,
|
||||||
|
0x38, 0x39, 0x38, 0x34, 0x36, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x3d, 0x3b, 0x39, 0x38, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x39, 0x38, 0x39, 0x38, 0x0, 0x36, 0x0
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,12 +1,4 @@
|
|||||||
import "env.memory" memory(4);
|
include "../include/microw8-api.cwa"
|
||||||
|
|
||||||
import "env.rectangle" fn rect(f32, f32, f32, f32, i32);
|
|
||||||
import "env.circle" fn circle(f32, f32, f32, i32);
|
|
||||||
import "env.isButtonPressed" fn btn(i32) -> i32;
|
|
||||||
import "env.random" fn random() -> i32;
|
|
||||||
import "env.randomSeed" fn randomSeed(i32);
|
|
||||||
import "env.cls" fn cls(i32);
|
|
||||||
import "env.printInt" fn printInt(i32);
|
|
||||||
|
|
||||||
global mut pz: i32 = 4;
|
global mut pz: i32 = 4;
|
||||||
global mut px: f32 = 2.0;
|
global mut px: f32 = 2.0;
|
||||||
@@ -16,10 +8,10 @@ global mut f: f32 = 2.0;
|
|||||||
|
|
||||||
export fn upd() {
|
export fn upd() {
|
||||||
let y: i32;
|
let y: i32;
|
||||||
let inline zero = 0.0;
|
let inline zero = 0_f;
|
||||||
|
|
||||||
let lazy control_speed = 0.03125;
|
let lazy control_speed = 0.03125;
|
||||||
s = s + 0.1875 - (f + control_speed) * btn(4 <| cls(4)) as f32;
|
s = s + 0.1875 - (f + control_speed) * isButtonPressed(4 <| cls(4)) as f32;
|
||||||
f = f * 0.5625;
|
f = f * 0.5625;
|
||||||
|
|
||||||
printInt(pz);
|
printInt(pz);
|
||||||
@@ -33,11 +25,13 @@ export fn upd() {
|
|||||||
|
|
||||||
let inline c = (z & 1) * -2;
|
let inline c = (z & 1) * -2;
|
||||||
let inline yf = y as f32;
|
let inline yf = y as f32;
|
||||||
rect(rx, yf, rw, yf / 6 as f32, c + 1);
|
rectangle(rx, yf, rw, yf / 6 as f32, c + 1);
|
||||||
rect(rx, yf, rw, 1 as f32, c - 4);
|
rectangle(rx, yf, rw, 1 as f32, c - 4);
|
||||||
|
|
||||||
if y == 180 & py > zero {
|
if y == 180 & py > zero {
|
||||||
if x > w | x < zero {
|
if x > w | x < zero {
|
||||||
|
0?80 = 0xc3;
|
||||||
|
3?80 = 32;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
py = zero;
|
py = zero;
|
||||||
@@ -51,7 +45,10 @@ export fn upd() {
|
|||||||
circle(160 as f32, 160 as f32 + py, 22 as f32, -28);
|
circle(160 as f32, 160 as f32 + py, 22 as f32, -28);
|
||||||
circle((160 - 6) as f32, (160 - 6) as f32 + py, 6 as f32, -26);
|
circle((160 - 6) as f32, (160 - 6) as f32 + py, 6 as f32, -26);
|
||||||
|
|
||||||
px = px + (btn(3) - btn(2)) as f32 * control_speed;
|
0?86 = py < zero;
|
||||||
|
3?86 = 32 - py as i32;
|
||||||
|
|
||||||
|
px = px + (isButtonPressed(3) - isButtonPressed(2)) as f32 * control_speed;
|
||||||
py = py + s;
|
py = py + s;
|
||||||
pz = pz + 1;
|
pz = pz + 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
include "../include/microw8-api.cwa"
|
||||||
|
|
||||||
|
const SPRITE = 0x20000;
|
||||||
|
|
||||||
|
export fn upd() {
|
||||||
|
cls(0);
|
||||||
|
let t = time() / 2_f;
|
||||||
|
let i: i32;
|
||||||
|
loop spriteLoop {
|
||||||
|
let inline x = sin(t * -1.3 + i as f32 * (3.141 / 30_f)) * 180_f + 160_f;
|
||||||
|
let inline y = sin(t * 1.7 + i as f32 * (3.141 / 40_f)) * 140_f + 120_f;
|
||||||
|
blitSprite(SPRITE, 16, x as i32, y as i32, (i & 3) * 0x200 + 0x100);
|
||||||
|
branch_if (i +:= 1) < 100: spriteLoop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start fn start() {
|
||||||
|
printChar('OO');
|
||||||
|
circle(8_f, 8_f, 6_f, 75);
|
||||||
|
grabSprite(SPRITE, 16, 0, 0, 0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// Steady On Tim, It's Only A Budget Game
|
||||||
|
// by Gasman / Hooy-Program
|
||||||
|
// ported to MicroW8 by exoticorn/icebird
|
||||||
|
|
||||||
|
include "../include/microw8-api.cwa"
|
||||||
|
|
||||||
|
fn melody(t: i32, T: i32) -> i32 {
|
||||||
|
let inline riff_pos = abs(((T&31) - 16) as f32) as i32;
|
||||||
|
let lazy shift = ((1-((T>>5)&3))%2-1) as f32 / 6 as f32;
|
||||||
|
|
||||||
|
let inline note_count = 5 - (T >= 512);
|
||||||
|
let inline octave = (riff_pos/5) as f32;
|
||||||
|
let inline riff_note = 5514 >> (riff_pos % note_count * 4) & 15;
|
||||||
|
let inline melody_freq = pow(2 as f32, shift + octave - (riff_note as f32 / 12 as f32));
|
||||||
|
let inline melody = (t as f32 * melody_freq) as i32 & 128;
|
||||||
|
|
||||||
|
let inline arp_note = ((0x85>>((t>>12)%3*4)) & 15) - 1;
|
||||||
|
let inline arp_freq = pow(2 as f32, shift + (arp_note as f32 / 12 as f32));
|
||||||
|
let inline arp_vol = (T >= 256) * (12-T%12);
|
||||||
|
let inline arpeggio = ((t as f32 * arp_freq) as i32 & 128) * arp_vol / 12;
|
||||||
|
|
||||||
|
melody + arpeggio
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn snd(t: i32) -> f32 {
|
||||||
|
let lazy T = t/10000;
|
||||||
|
|
||||||
|
let inline mel_arp = melody(t, T)/3 + melody(t, T-3)/5;
|
||||||
|
|
||||||
|
let inline bass_vol = (T >= 128) & (197 >> (T % 8));
|
||||||
|
let inline bass_freq = pow(2 as f32, (((T & 4) * ((T & 7) - 1)) as f32 / 24 as f32 - 5 as f32));
|
||||||
|
let inline bass = ((t as f32 * bass_freq) as i32 & 63) * bass_vol;
|
||||||
|
|
||||||
|
let inline snare_ish = (random() & 31) * (8 - (T + 4) % 8) / 8;
|
||||||
|
|
||||||
|
let inline sample = mel_arp + bass + snare_ish;
|
||||||
|
sample as f32 / 255 as f32
|
||||||
|
}
|
||||||
@@ -1,25 +1,26 @@
|
|||||||
import "env.memory" memory(4);
|
include "../include/microw8-api.cwa"
|
||||||
import "env.sin" fn sin(f32) -> f32;
|
|
||||||
import "env.time" fn time() -> f32;
|
|
||||||
|
|
||||||
export fn upd() {
|
export fn upd() {
|
||||||
let i: i32;
|
let x: i32;
|
||||||
|
let y: i32;
|
||||||
loop screen {
|
loop screen {
|
||||||
let inline t = time() / 2 as f32;
|
let inline t = time() / 2 as f32;
|
||||||
let lazy o = sin(t) * 0.8;
|
let lazy o = sin(t) * 0.75;
|
||||||
let lazy q = (i % 320) as f32 - 160.1;
|
let inline q = x as f32 - 160.5;
|
||||||
let lazy w = (i / 320 - 120) as f32;
|
let inline w = (y - 120) as f32;
|
||||||
let lazy r = sqrt(q*q + w*w);
|
let lazy r = sqrt(q*q + w*w);
|
||||||
let lazy z = q / r;
|
let lazy z = q / r;
|
||||||
let lazy s = z * o + sqrt(z * z * o * o + 1 as f32 - o * o);
|
let lazy s = z * o + sqrt(z * z * o * o + 1 as f32 - o * o);
|
||||||
let lazy q2 = (z * s - o) * 10 as f32 + t;
|
let inline q2 = (z * s - o) * 10 as f32 + t;
|
||||||
let lazy w2 = w / r * s * 10 as f32 + t;
|
let inline w2 = w / r * s * 10 as f32 + t;
|
||||||
let lazy s2 = s * 100 as f32 / r;
|
let inline s2 = s * 100 as f32 / r;
|
||||||
i?120 = max(
|
let inline color = max(
|
||||||
0 as f32,
|
0 as f32,
|
||||||
((q2 as i32 ^ w2 as i32 & ((s2 + time()) * 10 as f32) as i32) & 5) as f32 *
|
((q2 as i32 ^ w2 as i32 & ((s2 + time()) * 10 as f32) as i32) & 5) as f32 *
|
||||||
(4 as f32 - s2) as f32
|
(4 as f32 - s2) as f32
|
||||||
) as i32 - 32;
|
) as i32 - 32;
|
||||||
branch_if (i := i + 1) < 320*240: screen
|
setPixel(x, y, color);
|
||||||
|
branch_if x := (x + 1) % 320: screen;
|
||||||
|
branch_if y := (y + 1) % 320: screen;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
// Steady On Tim, It's Only A Budget Game
|
||||||
|
// original bytebeat by Gasman / Hooy-Program
|
||||||
|
// ported to MicroW8/GES by exoticorn/icebird
|
||||||
|
|
||||||
|
import "env.memory" memory(4);
|
||||||
|
|
||||||
|
fn melody(ch: i32, t: i32, T: i32) {
|
||||||
|
let lazy riff_pos = abs(((T&31) - 16) as f32) as i32;
|
||||||
|
let lazy shift = ((1-((T>>5)&3))%2-1) * 2;
|
||||||
|
|
||||||
|
let inline note_count = 5 - (T >= 512);
|
||||||
|
let inline octave = (riff_pos/5) * 12;
|
||||||
|
let inline riff_note = 5514 >> (riff_pos % note_count * 4) & 15;
|
||||||
|
let inline melody_note = shift + octave - riff_note;
|
||||||
|
|
||||||
|
ch?1 = 230 - riff_pos * 14;
|
||||||
|
ch?3 = melody_note + 64;
|
||||||
|
|
||||||
|
let inline arp_note = shift + ((0x85>>((t/2)%3*4)) & 15) - 1;
|
||||||
|
80?3 = arp_note + 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn upd() {
|
||||||
|
let lazy t = 32!32 / (1000/60);
|
||||||
|
let lazy T = t / 7;
|
||||||
|
melody(98, t, T - 3);
|
||||||
|
melody(92, t, T);
|
||||||
|
|
||||||
|
80?0 = ((T >= 256) & (T/12+(T-3)/12)) * 2 | 0x48; // arp trigger
|
||||||
|
|
||||||
|
if T >= 128 {
|
||||||
|
let inline bass_step = T % 8;
|
||||||
|
86?3 = if bass_step / 2 == 2 {
|
||||||
|
86?0 = 0xd6;
|
||||||
|
81
|
||||||
|
} else {
|
||||||
|
86?0 = ((197 >> bass_step) & 1) | 0x48;
|
||||||
|
((T & 4) * ((T & 7) - 1)) / 2 + 28
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data 80 {
|
||||||
|
i8(
|
||||||
|
0, 0x90, 0, 0, 0, 0x90,
|
||||||
|
0, 0x4c, 0, 0, 0, 0x4c,
|
||||||
|
0x19, 0, 0, 0, 0, 0x4c,
|
||||||
|
0x19, 0, 0, 0, 0, 0x4c,
|
||||||
|
0xfa, 0x84,
|
||||||
|
0xc1, 0xc1, 0, 107, 0, 0x4c
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
include "../../platform/src/ges.cwa"
|
||||||
|
|
||||||
|
import "env.pow" fn pow(f32, f32) -> f32;
|
||||||
|
import "env.exp" fn exp(f32) -> f32;
|
||||||
|
import "env.sin" fn sin(f32) -> f32;
|
||||||
|
|
||||||
|
export fn snd(t: i32) -> f32 {
|
||||||
|
gesSnd(t)
|
||||||
|
}
|
||||||
|
*/
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
import "env.memory" memory(2);
|
include "../include/microw8-api.cwa"
|
||||||
|
|
||||||
import "env.fmod" fn fmod(f32, f32) -> f32;
|
|
||||||
import "env.time" fn time() -> f32;
|
|
||||||
|
|
||||||
export fn upd() {
|
export fn upd() {
|
||||||
let i: i32;
|
let i: i32;
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
import "env.memory" memory(4);
|
include "../include/microw8-api.cwa"
|
||||||
|
|
||||||
import "env.atan2" fn atan2(f32, f32) -> f32;
|
|
||||||
import "env.time" fn time() -> f32;
|
|
||||||
|
|
||||||
export fn upd() {
|
export fn upd() {
|
||||||
let i: i32;
|
let i: i32;
|
||||||
loop pixels {
|
loop pixels {
|
||||||
let inline t = time() * 63 as f32;
|
let inline t = 16!56;
|
||||||
let lazy x = (i % 320 - 160) as f32;
|
let inline x = (i % 320 - 160) as f32;
|
||||||
let lazy y = (i / 320 - 120) as f32;
|
let inline y = (i #/ 320 - 120) as f32;
|
||||||
let inline d = 40000 as f32 / sqrt(x * x + y * y);
|
let inline d = 0xa000 as f32 / sqrt(x * x + y * y);
|
||||||
let inline u = atan2(x, y) * (512.0 / 3.141);
|
let inline a = atan2(x, y) * 163_f; // (512 / pi)
|
||||||
let inline c = ((i32.trunc_sat_f32_s(d + t * 2 as f32) ^ i32.trunc_sat_f32_s(u + t)) & 255) >> 4;
|
let inline u = i32.trunc_sat_f32_s(a) + t;
|
||||||
i?120 = c;
|
let inline v = i32.trunc_sat_f32_s(d) + t * 2;
|
||||||
|
let inline c = ((v ^ u) #/ 16) % 16;
|
||||||
|
i?FRAMEBUFFER = c;
|
||||||
|
|
||||||
branch_if (i := i + 1) < 320*240: pixels;
|
branch_if (i := i + 1) < 320*240: pixels;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
// MicroW8 APIs, to be `include`d in CurlyWas sources
|
||||||
|
import "env.memory" memory(4);
|
||||||
|
|
||||||
|
import "env.sin" fn sin(f32) -> f32;
|
||||||
|
import "env.cos" fn cos(f32) -> f32;
|
||||||
|
import "env.tan" fn tan(f32) -> f32;
|
||||||
|
import "env.asin" fn asin(f32) -> f32;
|
||||||
|
import "env.acos" fn acos(f32) -> f32;
|
||||||
|
import "env.atan" fn atan(f32) -> f32;
|
||||||
|
import "env.atan2" fn atan2(f32, f32) -> f32;
|
||||||
|
import "env.pow" fn pow(f32, f32) -> f32;
|
||||||
|
import "env.log" fn log(f32) -> f32;
|
||||||
|
import "env.fmod" fn fmod(f32, f32) -> f32;
|
||||||
|
import "env.random" fn random() -> i32;
|
||||||
|
import "env.randomf" fn randomf() -> f32;
|
||||||
|
import "env.randomSeed" fn randomSeed(i32);
|
||||||
|
import "env.cls" fn cls(i32);
|
||||||
|
import "env.setPixel" fn setPixel(i32, i32, i32);
|
||||||
|
import "env.getPixel" fn getPixel(i32, i32) -> i32;
|
||||||
|
import "env.hline" fn hline(i32, i32, i32, i32);
|
||||||
|
import "env.rectangle" fn rectangle(f32, f32, f32, f32, i32);
|
||||||
|
import "env.circle" fn circle(f32, f32, f32, i32);
|
||||||
|
import "env.line" fn line(f32, f32, f32, f32, i32);
|
||||||
|
import "env.time" fn time() -> f32;
|
||||||
|
import "env.isButtonPressed" fn isButtonPressed(i32) -> i32;
|
||||||
|
import "env.isButtonTriggered" fn isButtonTriggered(i32) -> i32;
|
||||||
|
import "env.printChar" fn printChar(i32);
|
||||||
|
import "env.printString" fn printString(i32);
|
||||||
|
import "env.printInt" fn printInt(i32);
|
||||||
|
import "env.setTextColor" fn setTextColor(i32);
|
||||||
|
import "env.setBackgroundColor" fn setBackgroundColor(i32);
|
||||||
|
import "env.setCursorPosition" fn setCursorPosition(i32, i32);
|
||||||
|
import "env.rectangleOutline" fn rectangleOutline(f32, f32, f32, f32, i32);
|
||||||
|
import "env.circleOutline" fn circleOutline(f32, f32, f32, i32);
|
||||||
|
import "env.exp" fn exp(f32) -> f32;
|
||||||
|
import "env.playNote" fn playNote(i32, i32);
|
||||||
|
import "env.sndGes" fn sndGes(i32) -> f32;
|
||||||
|
import "env.blitSprite" fn blitSprite(i32, i32, i32, i32, i32);
|
||||||
|
import "env.grabSprite" fn grabSprite(i32, i32, i32, i32, i32);
|
||||||
|
|
||||||
|
const TIME_MS = 0x40;
|
||||||
|
const GAMEPAD = 0x44;
|
||||||
|
const FRAMEBUFFER = 0x78;
|
||||||
|
const PALETTE = 0x13000;
|
||||||
|
const FONT = 0x13400;
|
||||||
|
const USER_MEM = 0x14000;
|
||||||
|
const BUTTON_UP = 0x0;
|
||||||
|
const BUTTON_DOWN = 0x1;
|
||||||
|
const BUTTON_LEFT = 0x2;
|
||||||
|
const BUTTON_RIGHT = 0x3;
|
||||||
|
const BUTTON_A = 0x4;
|
||||||
|
const BUTTON_B = 0x5;
|
||||||
|
const BUTTON_X = 0x6;
|
||||||
|
const BUTTON_Y = 0x7;
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
;; MicroW8 APIs, in WAT (Wasm Text) format
|
||||||
|
(import "env" "memory" (memory 4))
|
||||||
|
|
||||||
|
(import "env" "sin" (func $sin (param f32) (result f32)))
|
||||||
|
(import "env" "cos" (func $cos (param f32) (result f32)))
|
||||||
|
(import "env" "tan" (func $tan (param f32) (result f32)))
|
||||||
|
(import "env" "asin" (func $asin (param f32) (result f32)))
|
||||||
|
(import "env" "acos" (func $acos (param f32) (result f32)))
|
||||||
|
(import "env" "atan" (func $atan (param f32) (result f32)))
|
||||||
|
(import "env" "atan2" (func $atan2 (param f32) (param f32) (result f32)))
|
||||||
|
(import "env" "pow" (func $pow (param f32) (param f32) (result f32)))
|
||||||
|
(import "env" "log" (func $log (param f32) (result f32)))
|
||||||
|
(import "env" "fmod" (func $fmod (param f32) (param f32) (result f32)))
|
||||||
|
(import "env" "random" (func $random (result i32)))
|
||||||
|
(import "env" "randomf" (func $randomf (result f32)))
|
||||||
|
(import "env" "randomSeed" (func $randomSeed (param i32)))
|
||||||
|
(import "env" "cls" (func $cls (param i32)))
|
||||||
|
(import "env" "setPixel" (func $setPixel (param i32) (param i32) (param i32)))
|
||||||
|
(import "env" "getPixel" (func $getPixel (param i32) (param i32) (result i32)))
|
||||||
|
(import "env" "hline" (func $hline (param i32) (param i32) (param i32) (param i32)))
|
||||||
|
(import "env" "rectangle" (func $rectangle (param f32) (param f32) (param f32) (param f32) (param i32)))
|
||||||
|
(import "env" "circle" (func $circle (param f32) (param f32) (param f32) (param i32)))
|
||||||
|
(import "env" "line" (func $line (param f32) (param f32) (param f32) (param f32) (param i32)))
|
||||||
|
(import "env" "time" (func $time (result f32)))
|
||||||
|
(import "env" "isButtonPressed" (func $isButtonPressed (param i32) (result i32)))
|
||||||
|
(import "env" "isButtonTriggered" (func $isButtonTriggered (param i32) (result i32)))
|
||||||
|
(import "env" "printChar" (func $printChar (param i32)))
|
||||||
|
(import "env" "printString" (func $printString (param i32)))
|
||||||
|
(import "env" "printInt" (func $printInt (param i32)))
|
||||||
|
(import "env" "setTextColor" (func $setTextColor (param i32)))
|
||||||
|
(import "env" "setBackgroundColor" (func $setBackgroundColor (param i32)))
|
||||||
|
(import "env" "setCursorPosition" (func $setCursorPosition (param i32) (param i32)))
|
||||||
|
(import "env" "rectangleOutline" (func $rectangleOutline (param f32) (param f32) (param f32) (param f32) (param i32)))
|
||||||
|
(import "env" "circleOutline" (func $circleOutline (param f32) (param f32) (param f32) (param i32)))
|
||||||
|
(import "env" "exp" (func $exp (param f32) (result f32)))
|
||||||
|
(import "env" "playNote" (func $playNote (param i32) (param i32)))
|
||||||
|
(import "env" "sndGes" (func $sndGes (param i32) (result f32)))
|
||||||
|
(import "env" "blitSprite" (func $blitSprite (param i32) (param i32) (param i32) (param i32) (param i32)))
|
||||||
|
(import "env" "grabSprite" (func $grabSprite (param i32) (param i32) (param i32) (param i32) (param i32)))
|
||||||
|
|
||||||
|
;; to use defines, include this file with a preprocessor
|
||||||
|
;; like gpp (https://logological.org/gpp).
|
||||||
|
#define TIME_MS 0x40;
|
||||||
|
#define GAMEPAD 0x44;
|
||||||
|
#define FRAMEBUFFER 0x78;
|
||||||
|
#define PALETTE 0x13000;
|
||||||
|
#define FONT 0x13400;
|
||||||
|
#define USER_MEM 0x14000;
|
||||||
|
#define BUTTON_UP 0x0;
|
||||||
|
#define BUTTON_DOWN 0x1;
|
||||||
|
#define BUTTON_LEFT 0x2;
|
||||||
|
#define BUTTON_RIGHT 0x3;
|
||||||
|
#define BUTTON_A 0x4;
|
||||||
|
#define BUTTON_B 0x5;
|
||||||
|
#define BUTTON_X 0x6;
|
||||||
|
#define BUTTON_Y 0x7;
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
(module
|
||||||
|
(import "env" "atan2" (func $atan2 (param f32 f32) (result f32)))
|
||||||
|
(import "env" "time" (func $time (result f32)))
|
||||||
|
(import "env" "memory" (memory 4))
|
||||||
|
(func (export "upd")
|
||||||
|
(local $y i32)
|
||||||
|
(local $i i32)
|
||||||
|
(local $x i32)
|
||||||
|
|
||||||
|
(loop $pixels
|
||||||
|
i32.const 1
|
||||||
|
local.get $i
|
||||||
|
|
||||||
|
local.get $i
|
||||||
|
|
||||||
|
i32.const 36928
|
||||||
|
f32.convert_i32_s
|
||||||
|
local.get $i
|
||||||
|
i32.const 320
|
||||||
|
i32.rem_s
|
||||||
|
i32.const 160
|
||||||
|
i32.sub
|
||||||
|
local.tee $x
|
||||||
|
local.get $x
|
||||||
|
i32.mul
|
||||||
|
local.get $i
|
||||||
|
i32.const 320
|
||||||
|
i32.div_s
|
||||||
|
i32.const 120
|
||||||
|
i32.sub
|
||||||
|
local.tee $y
|
||||||
|
local.get $y
|
||||||
|
i32.mul
|
||||||
|
i32.add
|
||||||
|
f32.convert_i32_s
|
||||||
|
f32.sqrt
|
||||||
|
f32.div
|
||||||
|
call $time
|
||||||
|
i32.const 163
|
||||||
|
f32.convert_i32_s
|
||||||
|
f32.mul
|
||||||
|
f32.add
|
||||||
|
i32.trunc_sat_f32_s
|
||||||
|
|
||||||
|
local.get $x
|
||||||
|
f32.convert_i32_s
|
||||||
|
local.get $y
|
||||||
|
f32.convert_i32_s
|
||||||
|
call $atan2
|
||||||
|
i32.const 163
|
||||||
|
f32.convert_i32_s
|
||||||
|
f32.mul
|
||||||
|
call $time
|
||||||
|
i32.const 64
|
||||||
|
f32.convert_i32_s
|
||||||
|
f32.mul
|
||||||
|
f32.add
|
||||||
|
i32.trunc_f32_s
|
||||||
|
|
||||||
|
i32.xor
|
||||||
|
i32.const 4
|
||||||
|
i32.shr_s
|
||||||
|
i32.const 15
|
||||||
|
i32.and
|
||||||
|
i32.store8 offset=120
|
||||||
|
|
||||||
|
i32.add
|
||||||
|
local.tee $i
|
||||||
|
i32.const 76800
|
||||||
|
i32.rem_s
|
||||||
|
br_if $pixels
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
Generated
+328
-97
@@ -19,47 +19,54 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ahash"
|
name = "ahash"
|
||||||
version = "0.7.6"
|
version = "0.8.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"cfg-if",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"version_check",
|
"version_check",
|
||||||
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.52"
|
version = "1.0.81"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3"
|
checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ariadne"
|
name = "ariadne"
|
||||||
version = "0.1.3"
|
version = "0.1.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7080ae01b2f0c312065d4914cd0f0de045eb8832e9415b355106a6cff3073cb4"
|
checksum = "f1cb2a2046bea8ce5e875551f5772024882de0b540c7f93dfc5d6cf1ca8b030c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"yansi",
|
"yansi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.0.1"
|
version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck"
|
name = "bytemuck"
|
||||||
version = "1.7.3"
|
version = "1.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f"
|
checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.72"
|
version = "1.0.92"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
|
checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cdivsufsort"
|
name = "cdivsufsort"
|
||||||
@@ -79,63 +86,56 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chumsky"
|
name = "chumsky"
|
||||||
version = "0.5.0"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c2d3efff85e8572b1c3fa0127706af58c4fff8458f8d9436d54b1e97573c7a3f"
|
checksum = "8d02796e4586c6c41aeb68eae9bfb4558a522c35f1430c14b40136c3706e09e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash 0.3.8",
|
"ahash 0.3.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const-random"
|
name = "const-random"
|
||||||
version = "0.1.13"
|
version = "0.1.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f590d95d011aa80b063ffe3253422ed5aa462af4e9867d43ce8337562bac77c4"
|
checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"const-random-macro",
|
"const-random-macro",
|
||||||
"proc-macro-hack",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const-random-macro"
|
name = "const-random-macro"
|
||||||
version = "0.1.13"
|
version = "0.1.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "615f6e27d000a2bffbc7f2f6a8669179378fa27ee4d0a509e985dfc0a7defb40"
|
checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"lazy_static",
|
"once_cell",
|
||||||
"proc-macro-hack",
|
|
||||||
"tiny-keccak",
|
"tiny-keccak",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc32fast"
|
name = "crc32fast"
|
||||||
version = "1.3.0"
|
version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836"
|
checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-channel"
|
name = "crossbeam-channel"
|
||||||
version = "0.5.1"
|
version = "0.5.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
|
checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.5"
|
version = "0.8.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
|
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"lazy_static",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crunchy"
|
name = "crunchy"
|
||||||
@@ -146,42 +146,52 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "curlywas"
|
name = "curlywas"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/exoticorn/curlywas.git?rev=196719b#196719b35ef377cb7e001554b27ac5de013dcf2b"
|
source = "git+https://github.com/exoticorn/curlywas.git?rev=0e7ea50#0e7ea508cd0e76836283ae68a44c9097df83c8ac"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"ariadne",
|
"ariadne",
|
||||||
"chumsky",
|
"chumsky",
|
||||||
"pico-args",
|
"pico-args 0.4.2",
|
||||||
"wasm-encoder",
|
"wasm-encoder 0.10.0",
|
||||||
"wasmparser",
|
"wasmparser 0.83.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fallible_collections"
|
name = "equivalent"
|
||||||
version = "0.4.4"
|
version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "52db5973b6a19247baf19b30f41c23a1bfffc2e9ce0a5db2f60e3cd5dc8895f7"
|
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fallible-iterator"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fallible_collections"
|
||||||
|
version = "0.4.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a88c69768c0a15262df21899142bc6df9b9b823546d4b4b9a7bc2d6c448ec6fd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashbrown",
|
"hashbrown 0.13.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.22"
|
version = "1.0.28"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
|
checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
"libc",
|
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.3"
|
version = "0.2.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
|
checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -189,19 +199,71 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "gimli"
|
||||||
version = "0.11.2"
|
version = "0.26.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash 0.7.6",
|
"fallible-iterator",
|
||||||
|
"indexmap 1.9.3",
|
||||||
|
"stable_deref_trait",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "hashbrown"
|
||||||
version = "1.4.0"
|
version = "0.12.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
|
||||||
|
dependencies = [
|
||||||
|
"ahash 0.8.11",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "id-arena"
|
||||||
|
version = "2.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "1.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"hashbrown 0.12.3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown 0.14.3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "leb128"
|
name = "leb128"
|
||||||
@@ -210,17 +272,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
|
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "lexopt"
|
||||||
version = "0.2.112"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
|
checksum = "478ee9e62aaeaf5b140bd4138753d1f109765488581444218d3ddda43234f3e8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.153"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lodepng"
|
name = "lodepng"
|
||||||
version = "3.4.7"
|
version = "3.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "24844d5c0b922ddd52fb5bf0964a4c7f8e799a946ec01bb463771eb04fc1a323"
|
checksum = "a42d298694b14401847de29abd44adf278b42e989e516deac7b72018400002d8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"crc32fast",
|
||||||
"fallible_collections",
|
"fallible_collections",
|
||||||
"flate2",
|
"flate2",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -228,39 +297,43 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "log"
|
||||||
version = "0.4.4"
|
version = "0.4.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
|
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"adler",
|
"adler",
|
||||||
"autocfg",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.14"
|
version = "0.2.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.9.0"
|
version = "1.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pbr"
|
name = "pbr"
|
||||||
version = "1.0.4"
|
version = "1.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ff5751d87f7c00ae6403eb1fcbba229b9c76c9a30de8c1cf87182177b168cea2"
|
checksum = "ed5827dfa0d69b6c92493d6c38e633bbaa5937c153d0d7c28bf12313f8c6d514"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"libc",
|
"libc",
|
||||||
"time",
|
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -270,6 +343,12 @@ version = "0.4.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468"
|
checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pico-args"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "platform"
|
name = "platform"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -281,16 +360,28 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-hack"
|
name = "proc-macro2"
|
||||||
version = "0.5.19"
|
version = "1.0.79"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
|
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rgb"
|
name = "rgb"
|
||||||
version = "0.8.31"
|
version = "0.8.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9a374af9a0e5fdcdd98c1c7b64f05004f9ea2555b6c75f211daa81268a3c50f1"
|
checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
]
|
]
|
||||||
@@ -305,13 +396,57 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "semver"
|
||||||
version = "0.1.43"
|
version = "1.0.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
|
checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stable_deref_trait"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.109"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"proc-macro2",
|
||||||
"winapi",
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.58"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.58"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.58"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.58",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -323,15 +458,27 @@ dependencies = [
|
|||||||
"crunchy",
|
"crunchy",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "upkr"
|
name = "upkr"
|
||||||
version = "0.1.0"
|
version = "0.2.1"
|
||||||
source = "git+https://github.com/exoticorn/upkr.git?rev=2e7983fc#2e7983fc650788d98da2eecef2d16f63e849e4a0"
|
source = "git+https://github.com/exoticorn/upkr.git?rev=080db40d0088bbee2bdf3c5c75288ac7853d6b7a#080db40d0088bbee2bdf3c5c75288ac7853d6b7a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cdivsufsort",
|
"cdivsufsort",
|
||||||
"pbr",
|
"lexopt",
|
||||||
"pico-args",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -340,38 +487,102 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"pbr",
|
"pbr",
|
||||||
"pico-args",
|
"pico-args 0.5.0",
|
||||||
"upkr",
|
"upkr",
|
||||||
"wasm-encoder",
|
"walrus",
|
||||||
"wasmparser",
|
"wasm-encoder 0.201.0",
|
||||||
|
"wasmparser 0.201.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.3"
|
version = "0.9.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "walrus"
|
||||||
|
version = "0.20.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2c03529cd0c4400a2449f640d2f27cd1b48c3065226d15e26d98e4429ab0adb7"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"gimli",
|
||||||
|
"id-arena",
|
||||||
|
"leb128",
|
||||||
|
"log",
|
||||||
|
"walrus-macro",
|
||||||
|
"wasm-encoder 0.29.0",
|
||||||
|
"wasmparser 0.80.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "walrus-macro"
|
||||||
|
version = "0.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0a6e5bd22c71e77d60140b0bd5be56155a37e5bd14e24f5f87298040d0cc40d7"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.10.2+wasi-snapshot-preview1"
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-encoder"
|
name = "wasm-encoder"
|
||||||
version = "0.8.0"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "db0c351632e46cc06a58a696a6c11e4cf90cad4b9f8f07a0b59128d616c29bb0"
|
checksum = "aa9d9bf45fc46f71c407837c9b30b1e874197f2dc357588430b21e5017d290ab"
|
||||||
|
dependencies = [
|
||||||
|
"leb128",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-encoder"
|
||||||
|
version = "0.29.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "18c41dbd92eaebf3612a39be316540b8377c871cb9bde6b064af962984912881"
|
||||||
|
dependencies = [
|
||||||
|
"leb128",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-encoder"
|
||||||
|
version = "0.201.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9c7d2731df60006819b013f64ccc2019691deccf6e11a1804bc850cd6748f1a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"leb128",
|
"leb128",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasmparser"
|
name = "wasmparser"
|
||||||
version = "0.81.0"
|
version = "0.80.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "98930446519f63d00a836efdc22f67766ceae8dbcc1571379f2bcabc6b2b9abc"
|
checksum = "449167e2832691a1bff24cde28d2804e90e09586a448c8e76984792c44334a6b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasmparser"
|
||||||
|
version = "0.83.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasmparser"
|
||||||
|
version = "0.201.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "84e5df6dba6c0d7fafc63a450f1738451ed7a0b52295d83e868218fa286bf708"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"indexmap 2.2.6",
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
@@ -397,6 +608,26 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yansi"
|
name = "yansi"
|
||||||
version = "0.5.0"
|
version = "0.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71"
|
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.7.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.7.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.58",
|
||||||
|
]
|
||||||
|
|||||||
+2
-2
@@ -6,7 +6,7 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
curlywas = { git="https://github.com/exoticorn/curlywas.git", rev="196719b" }
|
curlywas = { git="https://github.com/exoticorn/curlywas.git", rev="0e7ea50" }
|
||||||
uw8-tool = { path="../uw8-tool" }
|
uw8-tool = { path="../uw8-tool" }
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
lodepng = "3.4"
|
lodepng = "3.7.2"
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
Binary file not shown.
@@ -0,0 +1,221 @@
|
|||||||
|
const GesChannelState.Trigger = 0;
|
||||||
|
const GesChannelState.EnvState = 1;
|
||||||
|
const GesChannelState.EnvVol = 2;
|
||||||
|
const GesChannelState.Phase = 4;
|
||||||
|
const GesChannelState.Size = 8;
|
||||||
|
|
||||||
|
const GesState.Filter = GesChannelState.Size * 4;
|
||||||
|
const GesState.Size = GesState.Filter + 8*4;
|
||||||
|
|
||||||
|
const GesStateOffset = 32;
|
||||||
|
const GesBufferOffset = 32 + GesState.Size;
|
||||||
|
|
||||||
|
export fn sndGes(t: i32) -> f32 {
|
||||||
|
let baseAddr = 0!0x12c78;
|
||||||
|
if !(t & 127) {
|
||||||
|
let i: i32;
|
||||||
|
loop clearLoop {
|
||||||
|
(baseAddr + i)!GesBufferOffset = 0;
|
||||||
|
branch_if (i := i + 4) < 128*4: clearLoop;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ch: i32;
|
||||||
|
loop channelLoop {
|
||||||
|
let lazy channelState = baseAddr + GesStateOffset + ch * GesChannelState.Size;
|
||||||
|
let lazy channelReg = baseAddr + ch * 6;
|
||||||
|
let envState = channelState?GesChannelState.EnvState;
|
||||||
|
let envVol = i32.load16_u(channelState, GesChannelState.EnvVol);
|
||||||
|
|
||||||
|
let lazy oldTrigger = channelState?GesChannelState.Trigger;
|
||||||
|
let lazy ctrl = channelReg?0;
|
||||||
|
if (oldTrigger ^ ctrl) & (ctrl | 2) & 3 {
|
||||||
|
envState = 1;
|
||||||
|
envVol = 0;
|
||||||
|
}
|
||||||
|
channelState?GesChannelState.Trigger = ctrl;
|
||||||
|
|
||||||
|
if envState {
|
||||||
|
let lazy attack = channelReg?4 & 15;
|
||||||
|
envVol = envVol + 12 * pow(1.675, (15 - attack) as f32) as i32;
|
||||||
|
if envVol >= 65535 {
|
||||||
|
envVol = 65535;
|
||||||
|
envState = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let inline decay = (channelReg - (ctrl & 1))?5 >> 4;
|
||||||
|
let inline dec = 8 * pow(1.5625, (15 - decay) as f32) as i32;
|
||||||
|
envVol = envVol - ((dec * (envVol + 8192)) >> 16);
|
||||||
|
let inline sustain = (channelReg?5 & 15) << 12;
|
||||||
|
let lazy targetVol = (ctrl & 1) * sustain;
|
||||||
|
if envVol < targetVol {
|
||||||
|
envVol = targetVol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
channelState?GesChannelState.EnvState = envState;
|
||||||
|
|
||||||
|
i32.store16(envVol, channelState, GesChannelState.EnvVol);
|
||||||
|
|
||||||
|
let inline note = i32.load16_u(channelReg, 2);
|
||||||
|
let lazy freq = 440 as f32 * pow(2.0, (note - 69*256) as f32 / (12*256) as f32);
|
||||||
|
let phaseInc = (freq * (65536.0 / 44100.0)) as i32;
|
||||||
|
|
||||||
|
let phase = channelState!GesChannelState.Phase;
|
||||||
|
|
||||||
|
let inline pulseWidth = channelReg?1;
|
||||||
|
let invPhaseInc = 1 as f32 / phaseInc as f32;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
let wave = ctrl >> 6;
|
||||||
|
if wave < 2 {
|
||||||
|
if wave {
|
||||||
|
let pulsePhase1 = pulseWidth << 23;
|
||||||
|
let pulsePhase2 = (511 - pulseWidth) << 23;
|
||||||
|
loop sawLoop {
|
||||||
|
let p = (phase ^ 32768) << 16;
|
||||||
|
let saw = (p >> 16) - polyBlep(phase, invPhaseInc, -32767);
|
||||||
|
let saw2 = select(p #>= pulsePhase1 & p #< pulsePhase2, -saw, saw);
|
||||||
|
let saw2 = saw2 -
|
||||||
|
polyBlep((p - pulsePhase1) >> 16, invPhaseInc, -saw) -
|
||||||
|
polyBlep((p - pulsePhase2) >> 16, invPhaseInc, saw);
|
||||||
|
(baseAddr + i)!(GesBufferOffset + 128*4) = saw2;
|
||||||
|
phase = phase + phaseInc;
|
||||||
|
branch_if (i := i + 4) < 64*4: sawLoop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
let pulsePhase = 32768 + pulseWidth * 128;
|
||||||
|
loop rectLoop {
|
||||||
|
(baseAddr + i)!(GesBufferOffset + 128*4) = select((phase & 65535) < pulsePhase, -32768, 32767) -
|
||||||
|
polyBlep(phase, invPhaseInc, -32767) -
|
||||||
|
polyBlep(phase - pulsePhase, invPhaseInc, 32767);
|
||||||
|
phase = phase + phaseInc;
|
||||||
|
branch_if (i := i + 4) < 64*4: rectLoop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if wave == 2 {
|
||||||
|
let scale = pulseWidth + 256;
|
||||||
|
loop triLoop {
|
||||||
|
let s = phase << 16;
|
||||||
|
s = (s ^ (s >> 31));
|
||||||
|
s = (s >> 8) * scale;
|
||||||
|
s = (s ^ (s >> 31));
|
||||||
|
(baseAddr + i)!(GesBufferOffset + 128*4) = (s >> 15) - 32768;
|
||||||
|
phase = phase + phaseInc;
|
||||||
|
branch_if (i := i + 4) < 64*4: triLoop;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loop noiseLoop {
|
||||||
|
let s = phase >> 12;
|
||||||
|
let inline pulse = ((phase >> 8) & 255) >= pulseWidth;
|
||||||
|
s = s * 0x6746ba73;
|
||||||
|
s = s ^ (s >> 15) * pulse;
|
||||||
|
(baseAddr + i)!(GesBufferOffset + 128*4) = (s * 0x835776c7) >> 16;
|
||||||
|
phase = phase + phaseInc;
|
||||||
|
branch_if (i := i + 4) < 64*4: noiseLoop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
channelState!GesChannelState.Phase = phase;
|
||||||
|
|
||||||
|
if ctrl & 32 {
|
||||||
|
let lazy modSrc = (ch - 1) & 3;
|
||||||
|
let inline channelState = baseAddr + GesStateOffset + modSrc * GesChannelState.Size;
|
||||||
|
let inline channelReg = baseAddr + modSrc * 6;
|
||||||
|
|
||||||
|
let inline note = i32.load16_u(channelReg, 2);
|
||||||
|
let inline freq = 440 as f32 * pow(2.0, (note - 69*256) as f32 / (12*256) as f32);
|
||||||
|
let phaseInc = (freq * (65536.0 / 44100.0)) as i32;
|
||||||
|
|
||||||
|
let phase = channelState!GesChannelState.Phase;
|
||||||
|
if modSrc < ch {
|
||||||
|
phase = phase - (phaseInc << 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
loop ringLoop {
|
||||||
|
let s = phase << 16;
|
||||||
|
s = (s ^ (s >> 31));
|
||||||
|
(baseAddr + i)!(GesBufferOffset + 128*4) = ((baseAddr + i)!(GesBufferOffset + 128*4) * ((s >> 15) - 32768)) >> 15;
|
||||||
|
phase = phase + phaseInc;
|
||||||
|
branch_if (i := i + 4) < 64*4: ringLoop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let channelVol = ((baseAddr + (ch >> 1))?24 >> ((ch & 1) * 4)) & 15;
|
||||||
|
envVol = envVol * channelVol / 15;
|
||||||
|
|
||||||
|
let leftVol = (select(ctrl & 16, 0x3d5b, 0x6a79) >> (ch * 4)) & 15;
|
||||||
|
let rightVol = 16 - leftVol;
|
||||||
|
|
||||||
|
let lazy filter = (ctrl >> 2) & 3;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
if filter < 2 {
|
||||||
|
if filter {
|
||||||
|
let f = (4096 as f32 - min(4096 as f32, 4096 as f32 * exp(freq * (-8.0 * 3.141 / 44100.0)))) as i32;
|
||||||
|
let low = (baseAddr + ch * 8)!(GesStateOffset + GesState.Filter);
|
||||||
|
loop filterLoop {
|
||||||
|
let in = ((baseAddr + i)!(GesBufferOffset + 128*4) * envVol) >> 18;
|
||||||
|
low = low + (((in - low) * f) >> 12);
|
||||||
|
(baseAddr + i * 2)!GesBufferOffset = (baseAddr + i * 2)!GesBufferOffset + ((low * leftVol) >> 4);
|
||||||
|
(baseAddr + i * 2)!(GesBufferOffset + 4) = (baseAddr + i * 2)!(GesBufferOffset + 4) + ((low * rightVol) >> 4);
|
||||||
|
branch_if (i := i + 4) < 64*4: filterLoop;
|
||||||
|
(baseAddr + ch * 8)!(GesStateOffset + GesState.Filter) = low;
|
||||||
|
(baseAddr + ch * 8)!(GesStateOffset + GesState.Filter + 4) = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loop mixLoop {
|
||||||
|
let sample = ((baseAddr + i)!(GesBufferOffset + 128*4) * envVol) >> 18;
|
||||||
|
(baseAddr + i * 2)!GesBufferOffset = (baseAddr + i * 2)!GesBufferOffset + ((sample * leftVol) >> 4);
|
||||||
|
(baseAddr + i * 2)!(GesBufferOffset + 4) = (baseAddr + i * 2)!(GesBufferOffset + 4) + ((sample * rightVol) >> 4);
|
||||||
|
branch_if (i := i + 4) < 64*4: mixLoop;
|
||||||
|
(baseAddr + ch * 8)!(GesStateOffset + GesState.Filter) = sample;
|
||||||
|
(baseAddr + ch * 8)!(GesStateOffset + GesState.Filter + 4) = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
filter = filter - 2;
|
||||||
|
let ctrl = (baseAddr + filter)?26;
|
||||||
|
let note = i32.load16_u(baseAddr + filter * 2, 28);
|
||||||
|
let inline freq = 440 as f32 * pow(2.0, (note - 69*256) as f32 / (12*256) as f32);
|
||||||
|
let F = (8192 as f32 * sin(min(0.25, freq / 44100 as f32) * 3.1415)) as i32;
|
||||||
|
let Q = 8192 - (ctrl >> 4) * (7000/15);
|
||||||
|
let Qlimit = (8192*4096/F - F/2) * 3 / 4;
|
||||||
|
if Q > Qlimit {
|
||||||
|
Q = Qlimit;
|
||||||
|
}
|
||||||
|
let low_out = ctrl & 1;
|
||||||
|
let high_out = (ctrl >> 1) & 1;
|
||||||
|
let band_out = (ctrl >> 2) & 1;
|
||||||
|
let low = (baseAddr + ch * 8)!(GesStateOffset + GesState.Filter);
|
||||||
|
let band = (baseAddr + ch * 8)!(GesStateOffset + GesState.Filter + 4);
|
||||||
|
loop filterLoop {
|
||||||
|
let in = ((baseAddr + i)!(GesBufferOffset + 128*4) * envVol) >> 18;
|
||||||
|
|
||||||
|
let high = in - low - ((band * Q) >> 12);
|
||||||
|
band = band + ((F * high) >> 12);
|
||||||
|
low = low + ((F * band) >> 12);
|
||||||
|
|
||||||
|
let sample = low * low_out + high * high_out + band * band_out;
|
||||||
|
(baseAddr + i * 2)!GesBufferOffset = (baseAddr + i * 2)!GesBufferOffset + ((sample * leftVol) >> 4);
|
||||||
|
(baseAddr + i * 2)!(GesBufferOffset + 4) = (baseAddr + i * 2)!(GesBufferOffset + 4) + ((sample * rightVol) >> 4);
|
||||||
|
branch_if (i := i + 4) < 64*4: filterLoop;
|
||||||
|
(baseAddr + ch * 8)!(GesStateOffset + GesState.Filter) = low;
|
||||||
|
(baseAddr + ch * 8)!(GesStateOffset + GesState.Filter + 4) = band;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
branch_if (ch := ch + 1) < 4: channelLoop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
((baseAddr + (t & 127) * 4)!GesBufferOffset) as f32 / 32768 as f32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn polyBlep(transientPhase: i32, invPhaseInc: f32, magnitude: i32) -> i32 {
|
||||||
|
let lazy t = ((transientPhase << 16) >> 16) as f32 * invPhaseInc;
|
||||||
|
let lazy x = max(0 as f32, 1 as f32 - abs(t));
|
||||||
|
(f32.copysign(x * x, t) * magnitude as f32) as i32
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import "env.memory" memory(1);
|
||||||
|
import "env.sin" fn sin(f32) -> f32;
|
||||||
|
import "env.pow" fn pow(f32, f32) -> f32;
|
||||||
|
import "env.exp" fn exp(f32) -> f32;
|
||||||
|
|
||||||
|
include "ges.cwa"
|
||||||
@@ -11,13 +11,13 @@ fn main() -> Result<()> {
|
|||||||
convert_font()?;
|
convert_font()?;
|
||||||
|
|
||||||
println!("Compiling loader module");
|
println!("Compiling loader module");
|
||||||
let loader = curlywas::compile_file("src/loader.cwa", curlywas::Options::default())?;
|
let loader = curlywas::compile_file("src/loader.cwa", curlywas::Options::default()).0?;
|
||||||
File::create("bin/loader.wasm")?.write_all(&loader)?;
|
File::create("bin/loader.wasm")?.write_all(&loader)?;
|
||||||
|
|
||||||
println!("Loader (including base module): {} bytes", loader.len());
|
println!("Loader (including base module): {} bytes", loader.len());
|
||||||
|
|
||||||
println!("Compiling platform module");
|
println!("Compiling platform module");
|
||||||
let platform = curlywas::compile_file("src/platform.cwa", curlywas::Options::default())?;
|
let platform = curlywas::compile_file("src/platform.cwa", curlywas::Options::default()).0?;
|
||||||
println!("Compressing platform module");
|
println!("Compressing platform module");
|
||||||
let platform = uw8_tool::pack(
|
let platform = uw8_tool::pack(
|
||||||
&platform,
|
&platform,
|
||||||
|
|||||||
+259
-53
@@ -1,6 +1,10 @@
|
|||||||
import "env.memory" memory(4);
|
import "env.memory" memory(4);
|
||||||
|
|
||||||
|
import "env.sin" fn sin(f32) -> f32;
|
||||||
import "env.cos" fn cos(f32) -> f32;
|
import "env.cos" fn cos(f32) -> f32;
|
||||||
|
import "env.pow" fn pow(f32, f32) -> f32;
|
||||||
|
import "env.exp" fn exp(f32) -> f32;
|
||||||
|
import "env.logChar" fn logChar(i32);
|
||||||
|
|
||||||
export fn time() -> f32 {
|
export fn time() -> f32 {
|
||||||
(0!64) as f32 / 1000 as f32
|
(0!64) as f32 / 1000 as f32
|
||||||
@@ -29,11 +33,9 @@ export fn random() -> i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export fn random64() -> i64 {
|
export fn random64() -> i64 {
|
||||||
let state: i64;
|
let lazy state = randomState ^ (randomState #>> 12i64);
|
||||||
randomState = (state := (
|
let lazy state = state ^ (state << 25i64);
|
||||||
state := randomState ^ (randomState #>> 12i64)
|
randomState = state ^ (state #>> 27i64);
|
||||||
) ^ (state << 25i64)
|
|
||||||
) ^ (state #>> 27i64);
|
|
||||||
randomState * 0x2545f4914f6cdd1di64
|
randomState * 0x2545f4914f6cdd1di64
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,12 +61,9 @@ export fn cls(col: i32) {
|
|||||||
let i: i32;
|
let i: i32;
|
||||||
textCursorX = 0;
|
textCursorX = 0;
|
||||||
textCursorY = 0;
|
textCursorY = 0;
|
||||||
graphicsText = 0;
|
outputChannel = 0;
|
||||||
col = (col & 255) * 0x1010101;
|
textScale = 1;
|
||||||
loop pixels {
|
memory.fill(120, col, 320*240);
|
||||||
i!120 = col;
|
|
||||||
branch_if (i := i + 4) < 320*240: pixels;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn setPixel(x: i32, y: i32, col: i32) {
|
export fn setPixel(x: i32, y: i32, col: i32) {
|
||||||
@@ -88,12 +87,70 @@ fn clamp(v: i32, min: i32, max: i32) -> i32 {
|
|||||||
export fn hline(x1: i32, x2: i32, y: i32, col: i32) {
|
export fn hline(x1: i32, x2: i32, y: i32, col: i32) {
|
||||||
x1 = clamp(x1, 0, 320);
|
x1 = clamp(x1, 0, 320);
|
||||||
x2 = clamp(x2, 0, 320);
|
x2 = clamp(x2, 0, 320);
|
||||||
if x1 < x2 & y #< 240 {
|
if y #>= 240 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let word_start = (x1 + 3) & -4;
|
||||||
|
let word_end = x2 & -4;
|
||||||
|
if word_end > word_start {
|
||||||
|
col = (col & 255) * 0x1010101;
|
||||||
|
let ptr = y * 320 + x1;
|
||||||
|
let end = ptr + word_start - x1;
|
||||||
|
if ptr + 2 <= end {
|
||||||
|
ptr?120 = col;
|
||||||
|
ptr?121 = col;
|
||||||
|
ptr += 2;
|
||||||
|
}
|
||||||
|
if ptr < end {
|
||||||
|
ptr?120 = col;
|
||||||
|
ptr += 1;
|
||||||
|
}
|
||||||
|
end += word_end - word_start;
|
||||||
|
loop words {
|
||||||
|
if ptr + 16 <= end {
|
||||||
|
ptr!120 = col;
|
||||||
|
ptr!124 = col;
|
||||||
|
ptr!128 = col;
|
||||||
|
ptr!132 = col;
|
||||||
|
ptr += 16;
|
||||||
|
branch words;
|
||||||
|
}
|
||||||
|
if ptr + 8 <= end {
|
||||||
|
ptr!120 = col;
|
||||||
|
ptr!124 = col;
|
||||||
|
ptr += 8;
|
||||||
|
}
|
||||||
|
if ptr < end {
|
||||||
|
ptr!120 = col;
|
||||||
|
ptr += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end += x2 - word_end;
|
||||||
|
if ptr + 2 <= end {
|
||||||
|
ptr?120 = col;
|
||||||
|
ptr?121 = col;
|
||||||
|
ptr += 2;
|
||||||
|
}
|
||||||
|
if ptr < end {
|
||||||
|
ptr?120 = col;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
let ptr = y * 320 + x1;
|
let ptr = y * 320 + x1;
|
||||||
let end = ptr + x2 - x1;
|
let end = ptr + x2 - x1;
|
||||||
loop pixels {
|
if ptr + 4 <= end {
|
||||||
|
ptr?120 = col;
|
||||||
|
ptr?121 = col;
|
||||||
|
ptr?122 = col;
|
||||||
|
ptr?123 = col;
|
||||||
|
ptr += 4;
|
||||||
|
}
|
||||||
|
if ptr + 2 <= end {
|
||||||
|
ptr?120 = col;
|
||||||
|
ptr?121 = col;
|
||||||
|
ptr += 2;
|
||||||
|
}
|
||||||
|
if ptr < end {
|
||||||
ptr?120 = col;
|
ptr?120 = col;
|
||||||
branch_if (ptr := ptr + 1) < end: pixels;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,7 +172,7 @@ export fn rectangle(x: f32, y: f32, w: f32, h: f32, col: i32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn rectangle_outline(x: f32, y: f32, w: f32, h: f32, col: i32) {
|
export fn rectangleOutline(x: f32, y: f32, w: f32, h: f32, col: i32) {
|
||||||
let xl = nearest(x) as i32;
|
let xl = nearest(x) as i32;
|
||||||
let xr = nearest(x + w) as i32;
|
let xr = nearest(x + w) as i32;
|
||||||
let yt = nearest(y) as i32;
|
let yt = nearest(y) as i32;
|
||||||
@@ -156,7 +213,7 @@ export fn circle(cx: f32, cy: f32, radius: f32, col: i32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn circle_outline(cx: f32, cy: f32, radius: f32, col: i32) {
|
export fn circleOutline(cx: f32, cy: f32, radius: f32, col: i32) {
|
||||||
let prev_w: f32;
|
let prev_w: f32;
|
||||||
let y = clamp(nearest(cy - radius) as i32, -1, 241);
|
let y = clamp(nearest(cy - radius) as i32, -1, 241);
|
||||||
let maxY = clamp(nearest(cy + radius) as i32, -1, 241);
|
let maxY = clamp(nearest(cy + radius) as i32, -1, 241);
|
||||||
@@ -256,6 +313,11 @@ export fn line(x1: f32, y1: f32, x2: f32, y2: f32, col: i32) {
|
|||||||
p = y1;
|
p = y1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if max_axis == 0 as f32 {
|
||||||
|
setPixel(x1 as i32, y1 as i32, col);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let steps = floor(p + max_axis) as i32 - floor(p) as i32;
|
let steps = floor(p + max_axis) as i32 - floor(p) as i32;
|
||||||
p = floor(p) + 0.5 - p;
|
p = floor(p) + 0.5 - p;
|
||||||
if max_axis < 0 as f32 {
|
if max_axis < 0 as f32 {
|
||||||
@@ -267,7 +329,7 @@ export fn line(x1: f32, y1: f32, x2: f32, y2: f32, col: i32) {
|
|||||||
dy = dy / max_axis;
|
dy = dy / max_axis;
|
||||||
|
|
||||||
let f = min(max_axis, max(0 as f32, p));
|
let f = min(max_axis, max(0 as f32, p));
|
||||||
setPixel((x1 + f * dx) as i32, (y1 + f * dy) as i32, col);
|
setPixel(i32.trunc_sat_f32_s(x1 + f * dx), i32.trunc_sat_f32_s(y1 + f * dy), col);
|
||||||
|
|
||||||
if !steps {
|
if !steps {
|
||||||
return;
|
return;
|
||||||
@@ -280,7 +342,7 @@ export fn line(x1: f32, y1: f32, x2: f32, y2: f32, col: i32) {
|
|||||||
|
|
||||||
loop pixels {
|
loop pixels {
|
||||||
if steps := steps - 1 {
|
if steps := steps - 1 {
|
||||||
setPixel(x1 as i32, y1 as i32, col);
|
setPixel(i32.trunc_sat_f32_s(x1), i32.trunc_sat_f32_s(y1), col);
|
||||||
x1 = x1 + dx;
|
x1 = x1 + dx;
|
||||||
y1 = y1 + dy;
|
y1 = y1 + dy;
|
||||||
branch pixels;
|
branch pixels;
|
||||||
@@ -288,7 +350,93 @@ export fn line(x1: f32, y1: f32, x2: f32, y2: f32, col: i32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
f = min(max_axis, p) - p;
|
f = min(max_axis, p) - p;
|
||||||
setPixel((x1 + f * dx) as i32, (y1 + f * dy) as i32, col);
|
setPixel(i32.trunc_sat_f32_s(x1 + f * dx), i32.trunc_sat_f32_s(y1 + f * dy), col);
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn blitSprite(sprite: i32, size: i32, x: i32, y: i32, control: i32) {
|
||||||
|
let lazy width = size & 65535;
|
||||||
|
let lazy height = select(size >> 16, size >> 16, width);
|
||||||
|
|
||||||
|
let lazy x0 = select(x < 0, -x, 0);
|
||||||
|
let lazy x1 = select(x + width > 320, 320 - x, width);
|
||||||
|
let lazy y0 = select(y < 0, -y, 0);
|
||||||
|
let lazy y1 = select(y + height > 240, 240 - y, height);
|
||||||
|
|
||||||
|
let lazy numRows = y1 - y0;
|
||||||
|
let lazy numCols = x1 - x0;
|
||||||
|
if numRows <= 0 | numCols <= 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let trans = (control & 511) - 256;
|
||||||
|
let lazy flip_x = 1 - ((control >> 8) & 2);
|
||||||
|
let lazy flip_y = 1 - ((control >> 9) & 2);
|
||||||
|
if flip_x < 0 {
|
||||||
|
sprite += width - 1;
|
||||||
|
}
|
||||||
|
if flip_y < 0 {
|
||||||
|
sprite += (height - 1) * width;
|
||||||
|
}
|
||||||
|
|
||||||
|
let spriteRow = sprite + x0 * flip_x + y0 * flip_y * width;
|
||||||
|
let screenRow = x + x0 + (y + y0) * 320;
|
||||||
|
|
||||||
|
loop yloop {
|
||||||
|
let lx = 0;
|
||||||
|
loop xloop {
|
||||||
|
let lazy col = (spriteRow + lx * flip_x)?0;
|
||||||
|
if col != trans {
|
||||||
|
(screenRow + lx)?120 = col;
|
||||||
|
}
|
||||||
|
branch_if (lx +:= 1) < numCols: xloop;
|
||||||
|
}
|
||||||
|
spriteRow += width * flip_y;
|
||||||
|
screenRow += 320;
|
||||||
|
branch_if numRows -:= 1: yloop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn grabSprite(sprite: i32, size: i32, x: i32, y: i32, control: i32) {
|
||||||
|
let lazy width = size & 65535;
|
||||||
|
let lazy height = select(size >> 16, size >> 16, width);
|
||||||
|
|
||||||
|
let lazy x0 = select(x < 0, -x, 0);
|
||||||
|
let lazy x1 = select(x + width > 320, 320 - x, width);
|
||||||
|
let lazy y0 = select(y < 0, -y, 0);
|
||||||
|
let lazy y1 = select(y + height > 240, 240 - y, height);
|
||||||
|
|
||||||
|
let lazy numRows = y1 - y0;
|
||||||
|
let lazy numCols = x1 - x0;
|
||||||
|
if numRows <= 0 | numCols <= 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let trans = (control & 511) - 256;
|
||||||
|
let lazy flip_x = 1 - ((control >> 8) & 2);
|
||||||
|
let lazy flip_y = 1 - ((control >> 9) & 2);
|
||||||
|
if flip_x < 0 {
|
||||||
|
sprite += width - 1;
|
||||||
|
}
|
||||||
|
if flip_y < 0 {
|
||||||
|
sprite += (height - 1) * width;
|
||||||
|
}
|
||||||
|
|
||||||
|
let spriteRow = sprite + x0 * flip_x + y0 * flip_y * width;
|
||||||
|
let screenRow = x + x0 + (y + y0) * 320;
|
||||||
|
|
||||||
|
loop yloop {
|
||||||
|
let lx = 0;
|
||||||
|
loop xloop {
|
||||||
|
let lazy col = (screenRow + lx)?120;
|
||||||
|
if col != trans {
|
||||||
|
(spriteRow + lx * flip_x)?0 = col;
|
||||||
|
}
|
||||||
|
branch_if (lx +:= 1) < numCols: xloop;
|
||||||
|
}
|
||||||
|
spriteRow += width * flip_y;
|
||||||
|
screenRow += 320;
|
||||||
|
branch_if numRows -:= 1: yloop;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////
|
//////////
|
||||||
@@ -299,7 +447,8 @@ global mut textCursorX = 0;
|
|||||||
global mut textCursorY = 0;
|
global mut textCursorY = 0;
|
||||||
global mut textColor = 15;
|
global mut textColor = 15;
|
||||||
global mut bgColor = 0;
|
global mut bgColor = 0;
|
||||||
global mut graphicsText = 0;
|
global mut outputChannel = 0;
|
||||||
|
global mut textScale = 1;
|
||||||
|
|
||||||
export fn printChar(char: i32) {
|
export fn printChar(char: i32) {
|
||||||
loop chars {
|
loop chars {
|
||||||
@@ -311,6 +460,13 @@ export fn printChar(char: i32) {
|
|||||||
global mut controlCodeLength = 0;
|
global mut controlCodeLength = 0;
|
||||||
|
|
||||||
fn printSingleChar(char: i32) {
|
fn printSingleChar(char: i32) {
|
||||||
|
let charSize = 8 * textScale;
|
||||||
|
|
||||||
|
if outputChannel >= 2 & (char < 4 | char > 6) {
|
||||||
|
logChar(char);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
controlCodeLength?0x12d20 = char;
|
controlCodeLength?0x12d20 = char;
|
||||||
controlCodeLength = controlCodeLength + 1;
|
controlCodeLength = controlCodeLength + 1;
|
||||||
char = 0x12d20?0;
|
char = 0x12d20?0;
|
||||||
@@ -324,54 +480,61 @@ fn printSingleChar(char: i32) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if char == 4 | char == 5 {
|
if char >= 4 & char <= 6 {
|
||||||
graphicsText = char == 5;
|
outputChannel = char - 4;
|
||||||
textCursorX = 0;
|
if !outputChannel {
|
||||||
textCursorY = 0;
|
textCursorX = 0;
|
||||||
|
textCursorY = 0;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if char == 7 {
|
||||||
|
80?0 = 80?0 ^ 2;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if char == 8 {
|
if char == 8 {
|
||||||
textCursorX = textCursorX - 8;
|
textCursorX = textCursorX - charSize;
|
||||||
if !graphicsText & textCursorX < 0 {
|
if !outputChannel & textCursorX < 0 {
|
||||||
textCursorX = 320-8;
|
textCursorX = 320-charSize;
|
||||||
printSingleChar(11);
|
printSingleChar(11);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if char == 9 {
|
if char == 9 {
|
||||||
if !graphicsText & textCursorX >= 320 {
|
if !outputChannel & textCursorX >= 320 {
|
||||||
printChar(0xd0a);
|
printChar(0xd0a);
|
||||||
}
|
}
|
||||||
textCursorX = textCursorX + 8;
|
textCursorX = textCursorX + charSize;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if char == 10 {
|
if char == 10 {
|
||||||
textCursorY = textCursorY + 8;
|
textCursorY = textCursorY + charSize;
|
||||||
if !graphicsText & textCursorY >= 240 {
|
if !outputChannel & textCursorY >= 240 {
|
||||||
textCursorY = 240 - 8;
|
textCursorY = 240 - charSize;
|
||||||
let i: i32;
|
let i: i32;
|
||||||
loop scroll_copy {
|
loop scroll_copy {
|
||||||
i!120 = i!(120 + 320 * 8);
|
i!120 = (i + 320 * charSize)!120;
|
||||||
branch_if (i := i + 4) < 320 * (240 - 8): scroll_copy;
|
branch_if (i := i + 4) < 320 * (240 - charSize): scroll_copy;
|
||||||
}
|
}
|
||||||
rectangle(0 as f32, (240 - 8) as f32, 320 as f32, 8 as f32, bgColor);
|
rectangle(0 as f32, (240 - charSize) as f32, 320 as f32, charSize as f32, bgColor);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if char == 11 {
|
if char == 11 {
|
||||||
textCursorY = textCursorY - 8;
|
textCursorY = textCursorY - charSize;
|
||||||
if !graphicsText & textCursorY < 0 {
|
if !outputChannel & textCursorY < 0 {
|
||||||
textCursorY = 0;
|
textCursorY = 0;
|
||||||
let i = 320 * (240 - 8);
|
let i = 320 * (240 - charSize);
|
||||||
loop scroll_copy {
|
loop scroll_copy {
|
||||||
i!(116 + 320 * 8) = i!116;
|
(i + 320 * charSize)!116 = i!116;
|
||||||
branch_if (i := i - 4): scroll_copy;
|
branch_if (i := i - 4): scroll_copy;
|
||||||
}
|
}
|
||||||
rectangle(0 as f32, 0 as f32, 320 as f32, 8 as f32, bgColor);
|
rectangle(0 as f32, 0 as f32, 320 as f32, charSize as f32, bgColor);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -403,9 +566,15 @@ fn printSingleChar(char: i32) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if char == 30 {
|
||||||
|
let scale = 0x12d20?1;
|
||||||
|
textScale = select(scale > 0 & scale <= 16, scale, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if char == 31 {
|
if char == 31 {
|
||||||
textCursorX = 0x12d20?1 * (8 - graphicsText * 6);
|
textCursorX = 0x12d20?1 * (8 - outputChannel * 6);
|
||||||
textCursorY = 0x12d20?2 * (8 - graphicsText * 7);
|
textCursorY = 0x12d20?2 * (8 - outputChannel * 7);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,35 +594,37 @@ data(0x12d00) {
|
|||||||
1, 1, 1, 1, // 16-19,
|
1, 1, 1, 1, // 16-19,
|
||||||
1, 1, 1, 1, // 20-23,
|
1, 1, 1, 1, // 20-23,
|
||||||
1, 1, 1, 1, // 24-27,
|
1, 1, 1, 1, // 24-27,
|
||||||
1, 1, 1, 3 // 28-31
|
1, 1, 2, 3 // 28-31
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drawChar(char: i32) {
|
fn drawChar(char: i32) {
|
||||||
if !graphicsText & textCursorX >= 320 {
|
if !outputChannel & textCursorX >= 320 {
|
||||||
printChar(0xd0a);
|
printChar(0xd0a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let charSize = 8 * textScale;
|
||||||
|
|
||||||
let y: i32;
|
let y: i32;
|
||||||
loop rows {
|
loop rows {
|
||||||
let bits = (char * 8 + y)?0x13400;
|
let bits = (char * 8 + y / textScale)?0x13400;
|
||||||
let x = 0;
|
let x = 0;
|
||||||
if graphicsText {
|
if outputChannel {
|
||||||
loop pixels {
|
loop pixels {
|
||||||
if (bits := bits << 1) & 256 {
|
if (bits << (x / textScale)) & 128 {
|
||||||
setPixel(textCursorX + x, textCursorY + y, textColor);
|
setPixel(textCursorX + x, textCursorY + y, textColor);
|
||||||
}
|
}
|
||||||
branch_if (x := x + 1) < 8: pixels;
|
branch_if (x := x + 1) < charSize: pixels;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
loop pixels {
|
loop pixels {
|
||||||
setPixel(textCursorX + x, textCursorY + y, select((bits := bits << 1) & 256, textColor, bgColor));
|
setPixel(textCursorX + x, textCursorY + y, select((bits << (x / textScale)) & 128, textColor, bgColor));
|
||||||
branch_if (x := x + 1) < 8: pixels;
|
branch_if (x := x + 1) < charSize: pixels;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
branch_if (y := y + 1) < 8: rows;
|
branch_if (y := y + 1) < charSize: rows;
|
||||||
}
|
}
|
||||||
textCursorX = textCursorX + 8;
|
textCursorX = textCursorX + charSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn printString(ptr: i32) {
|
export fn printString(ptr: i32) {
|
||||||
@@ -490,11 +661,37 @@ export fn setBackgroundColor(col: i32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export fn setCursorPosition(x: i32, y: i32) {
|
export fn setCursorPosition(x: i32, y: i32) {
|
||||||
let lazy scale = select(graphicsText, 1, 8);
|
let lazy scale = select(outputChannel, 1, 8);
|
||||||
textCursorX = x * scale;
|
textCursorX = x * scale;
|
||||||
textCursorY = y * scale;
|
textCursorY = y * scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////
|
||||||
|
// SOUND //
|
||||||
|
///////////
|
||||||
|
|
||||||
|
include "ges.cwa"
|
||||||
|
|
||||||
|
export fn playNote(channel: i32, note: i32) {
|
||||||
|
(channel * 6)?80 = (channel * 6)?80 & 0xfe ^ if note {
|
||||||
|
(channel * 6)?83 = note & 127;
|
||||||
|
2 | !(note >> 7)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
data 80 {
|
||||||
|
i8(
|
||||||
|
0x80, 0xc0, 0, 81, 0xa0, 0x50,
|
||||||
|
0xc4, 0, 0, 69, 0x60, 0x40,
|
||||||
|
0x44, 0xb0, 0, 69, 0x90, 0x43,
|
||||||
|
0x4, 0xf0, 0, 69, 0xa4, 0x44,
|
||||||
|
0xff, 0xff,
|
||||||
|
1, 1, 0, 100, 0, 100
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
///////////
|
///////////
|
||||||
// SETUP //
|
// SETUP //
|
||||||
///////////
|
///////////
|
||||||
@@ -535,10 +732,19 @@ start fn setup() {
|
|||||||
branch_if (i := i - 1) >= 0: expand_sweetie;
|
branch_if (i := i - 1) >= 0: expand_sweetie;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memory.fill(0, 0, 64);
|
||||||
|
memory.fill(112, 0, 8);
|
||||||
|
memory.fill(0x14000, 0, 0x2c000);
|
||||||
|
|
||||||
|
|
||||||
cls(0);
|
cls(0);
|
||||||
randomSeed(random());
|
randomSeed(random());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data 0x12c78 {
|
||||||
|
i32(80)
|
||||||
|
}
|
||||||
|
|
||||||
data 0x13000+192*4 {
|
data 0x13000+192*4 {
|
||||||
i32(
|
i32(
|
||||||
0x2c1c1a,
|
0x2c1c1a,
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ for dir in build/*; do
|
|||||||
cp $example $dir/examples
|
cp $example $dir/examples
|
||||||
done
|
done
|
||||||
|
|
||||||
|
cp -r ../examples/include $dir/include
|
||||||
|
|
||||||
mkdir $dir/carts
|
mkdir $dir/carts
|
||||||
for example in $dir/examples/*; do
|
for example in $dir/examples/*; do
|
||||||
build/microw8-linux/uw8 pack -l 9 $example $dir/carts/$(basename ${example%.*}).uw8
|
build/microw8-linux/uw8 pack -l 9 $example $dir/carts/$(basename ${example%.*}).uw8
|
||||||
|
|||||||
+23
-10
@@ -14,12 +14,14 @@ The initial motivation behind MicroW8 was to explore whether there was a way to
|
|||||||
* Memory: 256KB
|
* Memory: 256KB
|
||||||
* Gamepad input (D-Pad + 4 Buttons)
|
* Gamepad input (D-Pad + 4 Buttons)
|
||||||
|
|
||||||
|
For detailed [documentation see here](docs).
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
* [Fireworks](v0.1.1#AgwvgP+M59snqjl4CMKw5sqm1Zw9yJCbSviMjeLUdHus2a3yl/a99+uiBeqZgP/2jqSjrLjRk73COMM6OSLpsxK8ugT1kuk/q4hQUqqPpGozHoa0laulzGGcahzdfdJsYaK1sIdeIYS9M5PnJx/Wk9H+PvWEPy2Zvv7I6IW7Fg==) (127 bytes): Some fireworks to welcome 2022.
|
* [Skip Ahead](v0.2.0#AgVfq24KI2Ok2o8qVtPYj27fSuGnfeSKgbOkIOsaEQMov8TDYQ6UjdjwkZrYcM1i9alo4/+Bhm1PRFEa0YHJlJAk/PGoc2K41rejv9ZSqJqIHNjr7cappqhOR2jT+jk+0b0+U6hO+geRCTP2aufWs7L+f/Z27NFY8LKlqPSv+C6Rd6+ohoKi6sYl5Kcrlf1cyTinV7jTTnmbcXWVDBA5rRKxAGMUTDS8rHxqSztRITOaQVP1pSdYgi/BDdOJOxSOIkeaId84S+Ycls5na7EgwSfVIpgqF+tcfkUecb8t2mQrXA7pyKrh/wzHn5N6Oe5aOgmzY2YpTIct) (249 bytes): A port of my [TIC-80 256byte game](http://tic80.com/play?cart=1735) from LoveByte'21, now with sound
|
||||||
* [Skip Ahead](v0.1.1#AgyfpZ80wkW28kiUZ9VIK4v+RPnVxqjK1dz2BcDoNyQPsS2g4OgEzkTe6jyoAfFOmqKrS8SM2aRljBal9mjNn8i4fP9eBK+RehQKxxGtJa9FqftvqEnh3ez1YaYxqj7jgTdzJ/WAYVmKMovBT1myrX3FamqKSOgMsNedLhVTLAhQup3sNcYEjGNo8b0HZ5+AgMgCwYRGCe//XQOMAaAAzqDILgmpEZ/43RKHcQpHEQwbURfNQJpadJe2sz3q5FlQnTGXQ9oSMokidhlC+aR/IpNHieuBGLhFZ2GfnwVQ0geBbQpTPA==) (229 bytes): A port of my [TIC-80 256byte game](http://tic80.com/play?cart=1735) from LoveByte'21
|
* [Fireworks](v0.2.0#AgwvgP+M59snqjl4CMKw5sqm1Zw9yJCbSviMjeLUdHus2a3yl/a99+uiBeqZgP/2jqSjrLjRk73COMM6OSLpsxK8ugT1kuk/q4hQUqqPpGozHoa0laulzGGcahzdfdJsYaK1sIdeIYS9M5PnJx/Wk9H+PvWEPy2Zvv7I6IW7Fg==) (127 bytes): Some fireworks to welcome 2022.
|
||||||
* [OhNoAnotherTunnel](v0.1.1#AgPP1oEFvPzY/rBZwTumtYn37zeMFgpir1Bkn91jsNcp26VzoUpkAOOJTtnzVBfW+/dGnnIdbq/irBUJztY5wuua80DORTYZndgdwZHcSk15ajc4nyO0g1A6kGWyW56oZk0iPYJA9WtUmoj0Plvy1CGwIZrMe57X7QZcdqc3u6zjTA41Tpiqi9vnO3xbhi8o594Vx0XPXwVzpYq1ZCTYenfAGaXKkDmAFJqiVIsiCg==) (175 bytes): A port of my [entry](http://tic80.com/play?cart=1871) in the Outline'21 bytebattle final
|
* [OhNoAnotherTunnel](v0.2.0#AgPP1oEFvPzY/rBZwTumtYn37zeMFgpir1Bkn91jsNcp26VzoUpkAOOJTtnzVBfW+/dGnnIdbq/irBUJztY5wuua80DORTYZndgdwZHcSk15ajc4nyO0g1A6kGWyW56oZk0iPYJA9WtUmoj0Plvy1CGwIZrMe57X7QZcdqc3u6zjTA41Tpiqi9vnO3xbhi8o594Vx0XPXwVzpYq1ZCTYenfAGaXKkDmAFJqiVIsiCg==) (175 bytes): A port of my [entry](http://tic80.com/play?cart=1871) in the Outline'21 bytebattle final
|
||||||
* [Technotunnel](v0.1.1#AhPXpq894LaUhp5+HQf39f39/Jc8g5zUrBSc0uyKh36ivskczhY84h55zL8gWpkdvKuRQI+KIt80isKzh8jkM8nILcx0RUvyk8yjE8TgNsgkcORVI0RY5k3qE4ySjaycxa2DVZH61UWZuLsCouuwT7I80TbmmetQSbMywJ/avrrCZIAH0UzQfvOiCJNG48NI0FFY1vjB7a7dcp8Uqg==) (157 bytes): A port of my [entry](https://tic80.com/play?cart=1873) in the Outline'21 bytebattle quater final
|
* [Technotunnel](v0.2.0#AhPXpq894LaUhp5+HQf39f39/Jc8g5zUrBSc0uyKh36ivskczhY84h55zL8gWpkdvKuRQI+KIt80isKzh8jkM8nILcx0RUvyk8yjE8TgNsgkcORVI0RY5k3qE4ySjaycxa2DVZH61UWZuLsCouuwT7I80TbmmetQSbMywJ/avrrCZIAH0UzQfvOiCJNG48NI0FFY1vjB7a7dcp8Uqg==) (157 bytes): A port of my [entry](https://tic80.com/play?cart=1873) in the Outline'21 bytebattle quater final
|
||||||
* [Font & Palette](v0.1.1#At/p39+IBnj6ry1TRe7jzVy2A4tXgBvmoW2itzoyF2aM28pGy5QDiKxqrk8l9sbWZLtnAb+jgOfU+9QhpuyCAkhN6gPOU481IUL/df96vNe3h288Dqwhd3sfFpothIVFsMwRK72kW2hiR7zWsaXyy5pNmjR6BJk4piWx9ApT1ZwoUajhk6/zij6itq/FD1U3jj/J3MOwqZ2ef8Bv6ZPQlJIYVf62icGa69wS6SI1qBpIFiF14F8PcztRVbKIxLpT4ArCS6nz6FPnyUkqATGSBNPJ): Just a simple viewer for the default font and palette.
|
* [Font & Palette](v0.2.0#At/p39+IBnj6ry1TRe7jzVy2A4tXgBvmoW2itzoyF2aM28pGy5QDiKxqrk8l9sbWZLtnAb+jgOfU+9QhpuyCAkhN6gPOU481IUL/df96vNe3h288Dqwhd3sfFpothIVFsMwRK72kW2hiR7zWsaXyy5pNmjR6BJk4piWx9ApT1ZwoUajhk6/zij6itq/FD1U3jj/J3MOwqZ2ef8Bv6ZPQlJIYVf62icGa69wS6SI1qBpIFiF14F8PcztRVbKIxLpT4ArCS6nz6FPnyUkqATGSBNPJ): Just a simple viewer for the default font and palette.
|
||||||
|
|
||||||
Examplers for older versions:
|
Examplers for older versions:
|
||||||
|
|
||||||
@@ -29,9 +31,20 @@ Examplers for older versions:
|
|||||||
|
|
||||||
## Versions
|
## Versions
|
||||||
|
|
||||||
### v0.1.0
|
### v0.4.1
|
||||||
|
|
||||||
* [Web runtime](v0.1.0)
|
* [Web runtime](../v0.4.1)
|
||||||
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.1.0/microw8-0.1.0-linux.tgz)
|
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.4.1/microw8-0.4.1-linux.tgz)
|
||||||
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.1.0/microw8-0.1.0-macos.tgz)
|
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.4.1/microw8-0.4.1-macos.tgz)
|
||||||
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.1.0/microw8-0.1.0-windows.zip)
|
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.4.1/microw8-0.4.1-windows.zip)
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
* Windows: fix bad/inconsistent frame rate
|
||||||
|
* fix choppy sound on devices with sample rates != 44100 kHz
|
||||||
|
* add scale mode 'fill' option
|
||||||
|
|
||||||
|
|
||||||
|
### Older versions
|
||||||
|
|
||||||
|
[Find older versions here.](versions)
|
||||||
|
|||||||
+224
-29
@@ -5,22 +5,28 @@ description = "Docs"
|
|||||||
|
|
||||||
# Overview
|
# Overview
|
||||||
|
|
||||||
MicroW8 loads WebAssembly modules with a maximum size of 256kb. You module needs to export
|
MicroW8 loads WebAssembly modules with a maximum size of 256kb. Your module needs to export
|
||||||
a function `fn upd()` which will be called once per frame.
|
a function `fn upd()` which will be called once per frame.
|
||||||
After calling `upd` MicroW8 will display the 320x240 8bpp framebuffer located
|
After calling `upd` MicroW8 will display the 320x240 8bpp framebuffer located
|
||||||
at offset 120 in memory with the 32bpp palette located at 0x13000.
|
at offset 120 in memory with the 32bpp palette located at 0x13000.
|
||||||
|
|
||||||
The memory has to be imported as `env` `memory` and has a maximum size of 256kb (4 pages).
|
The memory has to be imported as `env` `memory` and has a maximum size of 256kb (4 pages).
|
||||||
|
|
||||||
|
If the module exports a function called `start`, it will be called once after the module is
|
||||||
|
loaded.
|
||||||
|
|
||||||
# Memory map
|
# Memory map
|
||||||
|
|
||||||
```
|
```
|
||||||
00000-00040: user memory
|
00000-00040: user memory
|
||||||
00040-00044: time since module start in ms
|
00040-00044: time since module start in ms
|
||||||
00044-0004c: gamepad state
|
00044-0004c: gamepad state
|
||||||
0004c-00078: reserved
|
0004c-00050: number of frames since module start
|
||||||
|
00050-00070: sound data (synced to sound thread)
|
||||||
|
00070-00078: reserved
|
||||||
00078-12c78: frame buffer
|
00078-12c78: frame buffer
|
||||||
12c78-13000: reserved
|
12c78-12c7c: sound registers/work area base address (for sndGes function)
|
||||||
|
12c7c-13000: reserved
|
||||||
13000-13400: palette
|
13000-13400: palette
|
||||||
13400-13c00: font
|
13400-13c00: font
|
||||||
13c00-14000: reserved
|
13c00-14000: reserved
|
||||||
@@ -47,7 +53,7 @@ Returns the arccosine of `x`.
|
|||||||
|
|
||||||
Returns the arctangent of `x`.
|
Returns the arctangent of `x`.
|
||||||
|
|
||||||
### fn atan2(y: f32, y: f32) -> f32
|
### fn atan2(y: f32, x: f32) -> f32
|
||||||
|
|
||||||
Returns the angle between the point `(x, y)` and the positive x-axis.
|
Returns the angle between the point `(x, y)` and the positive x-axis.
|
||||||
|
|
||||||
@@ -140,13 +146,13 @@ Fills the circle at `cx, cy` and with `radius` with the given color index.
|
|||||||
|
|
||||||
(Sets all pixels where the pixel center lies inside the circle.)
|
(Sets all pixels where the pixel center lies inside the circle.)
|
||||||
|
|
||||||
### fn rectangle_outline(x: f32, y: f32, w: f32, h: f32, color: i32)
|
### fn rectangleOutline(x: f32, y: f32, w: f32, h: f32, color: i32)
|
||||||
|
|
||||||
Draws a one pixel outline on the inside of the given rectangle.
|
Draws a one pixel outline on the inside of the given rectangle.
|
||||||
|
|
||||||
(Draws the outermost pixels that are still inside the rectangle area.)
|
(Draws the outermost pixels that are still inside the rectangle area.)
|
||||||
|
|
||||||
### fn circle_outline(cx: f32, cy: f32, radius: f32, color: i32)
|
### fn circleOutline(cx: f32, cy: f32, radius: f32, color: i32)
|
||||||
|
|
||||||
Draws a one pixel outline on the inside of the given circle.
|
Draws a one pixel outline on the inside of the given circle.
|
||||||
|
|
||||||
@@ -156,6 +162,21 @@ Draws a one pixel outline on the inside of the given circle.
|
|||||||
|
|
||||||
Draws a line from `x1,y1` to `x2,y2` in the given color index.
|
Draws a line from `x1,y1` to `x2,y2` in the given color index.
|
||||||
|
|
||||||
|
### fn blitSprite(spriteData: i32, size: i32, x: i32, y: i32, control: i32)
|
||||||
|
|
||||||
|
Copies the pixel data at `spriteData` onto the screen at `x`, `y`. The size of the sprite is passed as `width | (height << 16)`.
|
||||||
|
If the height is given as 0, the sprite is is treated as square (width x width).
|
||||||
|
|
||||||
|
The control parameter controls masking and flipping of the sprite:
|
||||||
|
* bits 0-7: color mask index
|
||||||
|
* bit 8: switch on masked blit (pixel with color mask index are treated as transparent)
|
||||||
|
* bit 9: flip sprite x
|
||||||
|
* bit 10: flip sprite y
|
||||||
|
|
||||||
|
### fn grabSprite(spriteData: i32, size: i32, x: i32, y: i32, control: i32)
|
||||||
|
|
||||||
|
Copies the pixel data on the screen at `x`, `y` to `spriteData`. Parameters are exactly the same as `blitSprite`.
|
||||||
|
|
||||||
## Input
|
## Input
|
||||||
|
|
||||||
MicroW8 provides input from a gamepad with one D-Pad and 4 buttons, or a keyboard emulation thereof.
|
MicroW8 provides input from a gamepad with one D-Pad and 4 buttons, or a keyboard emulation thereof.
|
||||||
@@ -217,34 +238,58 @@ When printing characters only the foreground pixels are set, the background is "
|
|||||||
|
|
||||||
Moving/printing past any border does not cause any special operation, the cursor just goes off-screen.
|
Moving/printing past any border does not cause any special operation, the cursor just goes off-screen.
|
||||||
|
|
||||||
|
### Text scale
|
||||||
|
|
||||||
|
An integer text scale factor in the range 1x-16x can be set with control char 30. An attempt to
|
||||||
|
set a scale outside that range will reset the scale to 1x.
|
||||||
|
|
||||||
|
After startup and `cls` the scale is initialized to 1x.
|
||||||
|
|
||||||
### Control chars
|
### Control chars
|
||||||
|
|
||||||
Characters 0-31 are control characters and don't print by default. They take the next 0-2 following characters as parameters.
|
Characters 0-31 are control characters and don't print by default. They take the next 0-2 following characters as parameters.
|
||||||
Avoid the reserved control chars, they are currently NOPs but their behavior can change in later MicroW8 versions.
|
Avoid the reserved control chars, they are currently NOPs but their behavior can change in later MicroW8 versions.
|
||||||
|
|
||||||
| Code | Parameters | Operation |
|
| Code | Parameters | Operation |
|
||||||
| ----- | ---------- | ------------------------------------ |
|
| ----- | ---------- | ------------------------------------------ |
|
||||||
| 0 | - | Nop |
|
| 0 | - | Nop |
|
||||||
| 1 | char | Print char (including control chars) |
|
| 1 | char | Print char (including control chars) |
|
||||||
| 2-3 | - | Reserved |
|
| 2-3 | - | Reserved |
|
||||||
| 4 | - | Switch to normal mode |
|
| 4 | - | Switch to normal mode, reset cursor to 0,0 |
|
||||||
| 5 | - | Switch to graphics mode |
|
| 5 | - | Switch to graphics mode |
|
||||||
| 6-7 | - | Reserved |
|
| 6 | - | Switch output to (debug) console |
|
||||||
| 8 | - | Move cursor left |
|
| 7 | - | Bell / trigger sound channel 0 |
|
||||||
| 9 | - | Move cursor right |
|
| 8 | - | Move cursor left |
|
||||||
| 10 | - | Move cursor down |
|
| 9 | - | Move cursor right |
|
||||||
| 11 | - | Move cursor up |
|
| 10 | - | Move cursor down |
|
||||||
| 12 | - | do `cls(background_color)` |
|
| 11 | - | Move cursor up |
|
||||||
| 13 | - | Move cursor to the left border |
|
| 12 | - | do `cls(background_color)` |
|
||||||
| 14 | color | Set the background color |
|
| 13 | - | Move cursor to the left border |
|
||||||
| 15 | color | Set the text color |
|
| 14 | color | Set the background color |
|
||||||
| 16-23 | - | Reserved |
|
| 15 | color | Set the text color |
|
||||||
| 24 | - | Swap text/background colors |
|
| 16-23 | - | Reserved |
|
||||||
| 25-30 | - | Reserved |
|
| 24 | - | Swap text/background colors |
|
||||||
| 31 | x, y | Set cursor position (*) |
|
| 25-29 | - | Reserved |
|
||||||
|
| 30 | scale | Set text scale (1-16) |
|
||||||
|
| 31 | x, y | Set cursor position (*) |
|
||||||
|
|
||||||
(*) In graphics mode, the x coordinate is doubled when using control char 31 to be able to cover the whole screen with one byte.
|
(*) In graphics mode, the x coordinate is doubled when using control char 31 to be able to cover the whole screen with one byte.
|
||||||
|
|
||||||
|
#### Debug output
|
||||||
|
|
||||||
|
Control code 6 switches all text output (except codes 4 and 5 to switch output back to the screen) to the console. Where exactly this ends
|
||||||
|
up (if at all) is an implementation detail of the runtimes. The native dev-runtime writes the debug output to `stdout`, the web runtime to
|
||||||
|
the debug console using `console.log`. Both implementations buffer the output until they encounter a newline character (10) in the output stream.
|
||||||
|
|
||||||
|
There may be future runtimes that ignore the debug output completely.
|
||||||
|
|
||||||
|
In CurlyWas, a simple way to log some value might look like this:
|
||||||
|
```
|
||||||
|
printChar('\06V: '); // switch to console out, print some prefix
|
||||||
|
printInt(some_value);
|
||||||
|
printChar('\n\4'); // newline and switch back to screen
|
||||||
|
```
|
||||||
|
|
||||||
### fn printChar(char: i32)
|
### fn printChar(char: i32)
|
||||||
|
|
||||||
Prints the character in the lower 8 bits of `char`. If the upper 24 bits are non-zero, right-shifts `char` by 8 bits and loops back to the beginning.
|
Prints the character in the lower 8 bits of `char`. If the upper 24 bits are non-zero, right-shifts `char` by 8 bits and loops back to the beginning.
|
||||||
@@ -269,6 +314,130 @@ Sets the background color.
|
|||||||
|
|
||||||
Sets the cursor position. In normal mode `x` and `y` are multiplied by 8 to get the pixel position, in graphics mode they are used as is.
|
Sets the cursor position. In normal mode `x` and `y` are multiplied by 8 to get the pixel position, in graphics mode they are used as is.
|
||||||
|
|
||||||
|
## Sound
|
||||||
|
|
||||||
|
### Low level operation
|
||||||
|
|
||||||
|
MicroW8 actually runs two instances of your module. On the first instance, it calls `upd` and displays the framebuffer found in its memory. On the
|
||||||
|
second instance, it calls `snd` instead with an incrementing sample index and expects that function to return sound samples for the left and right
|
||||||
|
channel at 44100 Hz. If your module does not export a `snd` function, it calls the api function `sndGes` instead.
|
||||||
|
|
||||||
|
As the only means of communication, 32 bytes starting at address 0x00050 are copied from main to sound memory after `upd` returns.
|
||||||
|
|
||||||
|
By default, the `sndGes` function generates sound based on the 32 bytes at 0x00050. This means that in the default configuration those 32 bytes act
|
||||||
|
as sound registers. See the `sndGes` function for the meaning of those registers.
|
||||||
|
|
||||||
|
### export fn snd(sampleIndex: i32) -> f32
|
||||||
|
|
||||||
|
If the module exports a `snd` function, it is called 88200 times per second to provide PCM sample data for playback (44.1kHz stereo).
|
||||||
|
The `sampleIndex` will start at 0 and increments by 1 for each call. On even indices the function is expected to return a sample value for
|
||||||
|
the left channel, on odd indices for the right channel.
|
||||||
|
|
||||||
|
### fn playNote(channel: i32, note: i32)
|
||||||
|
|
||||||
|
Triggers a note (1-127) on the given channel (0-3). Notes are semitones with 69 being A4 (same as MIDI). A note value of 0 stops the
|
||||||
|
sound playing on that channel. A note value 128-255 will trigger note-128 and immediately stop it (playing attack+release parts of envelope).
|
||||||
|
|
||||||
|
This function assumes the default setup, with the `sndGes` registers located at 0x00050.
|
||||||
|
|
||||||
|
### fn sndGes(sampleIndex: i32) -> f32
|
||||||
|
|
||||||
|
This implements a sound chip, generating sound based on 32 bytes of sound registers.
|
||||||
|
|
||||||
|
The spec of this sound chip are:
|
||||||
|
|
||||||
|
- 4 channels with individual volume control (0-15)
|
||||||
|
- rect, saw, tri, noise wave forms selectable per channel
|
||||||
|
- each wave form supports some kind of pulse width modulation
|
||||||
|
- each channel has an optional automatic low pass filter, or can be sent to one of two manually controllable filters
|
||||||
|
- each channel can select between a narrow and a wide stereo positioning. The two stereo positions of each channel are fixed.
|
||||||
|
- optional ring modulation
|
||||||
|
|
||||||
|
This function requires 1024 bytes of working memory, the first 32 bytes of which are interpreted as the sound registers.
|
||||||
|
The base address of its working memory can be configured by writing the address to 0x12c78. It defaults to 0x00050.
|
||||||
|
|
||||||
|
Here is a short description of the 32 sound registers.
|
||||||
|
|
||||||
|
```
|
||||||
|
00 - CTRL0
|
||||||
|
06 - CTRL1
|
||||||
|
0c - CTRL2
|
||||||
|
12 - CTRL3
|
||||||
|
| 7 6 | 5 | 4 | 3 2 | 1 | 0 |
|
||||||
|
| wave | ring | wide | filter | trigger | note on |
|
||||||
|
|
||||||
|
note on: stay in decay/sustain part of envelope
|
||||||
|
trigger: the attack part of the envlope is triggered when either this changes
|
||||||
|
or note on is changed from 0 to 1.
|
||||||
|
filter : 0 - no filter
|
||||||
|
1 - fixed 6db 1-pole filter with cutoff two octaves above note
|
||||||
|
2 - programmable filter 0
|
||||||
|
3 - programmable filter 1
|
||||||
|
wide : use wide stereo panning
|
||||||
|
ring : ring modulate with triangle wave at frequency of previous channel
|
||||||
|
wave : 0 - rectangle
|
||||||
|
1 - saw
|
||||||
|
2 - triangle
|
||||||
|
3 - noise
|
||||||
|
|
||||||
|
01 - PULS0
|
||||||
|
07 - PULS1
|
||||||
|
0d - PULS2
|
||||||
|
13 - PULS3
|
||||||
|
Pulse width 0-255, with 0 being the plain version of each wave form.
|
||||||
|
rectangle - 50%-100% pulse width
|
||||||
|
saw - inverts 0%-100% of the saw wave form around the center
|
||||||
|
triangle - morphs into an octave up triangle wave
|
||||||
|
noise - blends into a decimated saw wave (just try it out)
|
||||||
|
|
||||||
|
02 - FINE0
|
||||||
|
08 - FINE1
|
||||||
|
0e - FINE2
|
||||||
|
14 - FINE3
|
||||||
|
Fractional note
|
||||||
|
|
||||||
|
03 - NOTE0
|
||||||
|
09 - NOTE1
|
||||||
|
0f - NOTE2
|
||||||
|
15 - NOTE3
|
||||||
|
Note, 69 = A4
|
||||||
|
|
||||||
|
04 - ENVA0
|
||||||
|
0a - ENVA1
|
||||||
|
10 - ENVA2
|
||||||
|
16 - ENVA3
|
||||||
|
| 7 6 5 4 | 3 2 1 0 |
|
||||||
|
| decay | attack |
|
||||||
|
|
||||||
|
05 - ENVB0
|
||||||
|
0b - ENVB1
|
||||||
|
11 - ENVB2
|
||||||
|
17 - ENVB3
|
||||||
|
| 7 6 5 4 | 3 2 1 0 |
|
||||||
|
| release | sustain |
|
||||||
|
|
||||||
|
18 - VO01
|
||||||
|
| 7 6 5 4 | 3 2 1 0 |
|
||||||
|
| volume 1 | volume 0 |
|
||||||
|
|
||||||
|
19 - VO23
|
||||||
|
| 7 6 5 4 | 3 2 1 0 |
|
||||||
|
| volume 3 | volume 2 |
|
||||||
|
|
||||||
|
1a - FCTR0
|
||||||
|
1b - FCTR1
|
||||||
|
| 7 6 5 4 | 3 | 2 | 1 | 0 |
|
||||||
|
| resonance | 0 | band | high | low |
|
||||||
|
|
||||||
|
1c - FFIN0
|
||||||
|
1e - FFIN1
|
||||||
|
cutoff frequency - fractional note
|
||||||
|
|
||||||
|
1d - FNOT0
|
||||||
|
1f - FNOT1
|
||||||
|
cutoff frequency - note
|
||||||
|
```
|
||||||
|
|
||||||
# The `uw8` tool
|
# The `uw8` tool
|
||||||
|
|
||||||
The `uw8` tool included in the MicroW8 download includes a number of useful tools for developing MicroW8 carts. For small productions written in
|
The `uw8` tool included in the MicroW8 download includes a number of useful tools for developing MicroW8 carts. For small productions written in
|
||||||
@@ -284,7 +453,8 @@ Runs `<file>` which can be a binary WebAssembly module, an `.uw8` cart, a wat (W
|
|||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
|
||||||
* `-t FRAMES`, `--timeout FRAMES` : Sets the timeout in frames (1/60s). If the start or update function runs longer than this it is forcibly interupted
|
* `-b`, `--browser`: Run in browser instead of using native runtime
|
||||||
|
* `-t FRAMES`, `--timeout FRAMES`: Sets the timeout in frames (1/60s). If the start or update function runs longer than this it is forcibly interupted
|
||||||
and execution of the cart is stopped. Defaults to 30 (0.5s)
|
and execution of the cart is stopped. Defaults to 30 (0.5s)
|
||||||
* `-w`, `--watch`: Reloads the given file every time it changes on disk.
|
* `-w`, `--watch`: Reloads the given file every time it changes on disk.
|
||||||
* `-p`, `--pack`: Pack the file into an `.uw8` cart before running it and print the resulting size.
|
* `-p`, `--pack`: Pack the file into an `.uw8` cart before running it and print the resulting size.
|
||||||
@@ -292,6 +462,31 @@ 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.
|
* `-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.
|
* `-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
|
||||||
|
* `--scale-fill`: Scale to fill whole screen, potentially cropping parts of the frame buffer.
|
||||||
|
|
||||||
|
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. You can toggle between scale modes 'fit' and 'fill' with M.
|
||||||
|
|
||||||
## `uw8 pack`
|
## `uw8 pack`
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
@@ -400,7 +595,7 @@ a base module provided by MicroW8.
|
|||||||
|
|
||||||
You can generate this base module yourself using
|
You can generate this base module yourself using
|
||||||
`uw8-tool`. As a quick summary, it provides all function
|
`uw8-tool`. As a quick summary, it provides all function
|
||||||
types with up to 5 parameters (i32 or f32) where the
|
types with up to 7 parameters (i32 or f32) where the
|
||||||
`f32` parameters always preceed the `i32` parameters.
|
`f32` parameters always preceed the `i32` parameters.
|
||||||
Then it includes all imports that MicroW8 provides,
|
Then it includes all imports that MicroW8 provides,
|
||||||
a function section with a single function of type
|
a function section with a single function of type
|
||||||
@@ -440,4 +635,4 @@ Pressing F9 opens a download dialog with a screenshot.
|
|||||||
## Devkit mode
|
## Devkit mode
|
||||||
|
|
||||||
Append `#devkit` to the web runtime url in order to switch to devkit mode. In devkit mode, standard web assembly modules
|
Append `#devkit` to the web runtime url in order to switch to devkit mode. In devkit mode, standard web assembly modules
|
||||||
are loaded bypassing the loader, removing all size restrictions. At the same time, the memory limit is increased to 1GB.
|
are loaded bypassing the loader, removing all size restrictions. At the same time, the memory limit is increased to 1GB.
|
||||||
|
|||||||
@@ -0,0 +1,173 @@
|
|||||||
|
+++
|
||||||
|
description = "Versions"
|
||||||
|
+++
|
||||||
|
|
||||||
|
### v0.4.1
|
||||||
|
|
||||||
|
* [Web runtime](../v0.4.1)
|
||||||
|
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.4.1/microw8-0.4.1-linux.tgz)
|
||||||
|
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.4.1/microw8-0.4.1-macos.tgz)
|
||||||
|
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.4.1/microw8-0.4.1-windows.zip)
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
* Windows: fix bad/inconsistent frame rate
|
||||||
|
* fix choppy sound on devices with sample rates != 44100 kHz
|
||||||
|
* add scale mode 'fill' option
|
||||||
|
|
||||||
|
### v0.4.0
|
||||||
|
|
||||||
|
* [Web runtime](../v0.4.0)
|
||||||
|
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.4.0/microw8-0.4.0-linux.tgz)
|
||||||
|
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.4.0/microw8-0.4.0-macos.tgz)
|
||||||
|
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.4.0/microw8-0.4.0-windows.zip)
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
* add support for sound on mono- and surround-only devices
|
||||||
|
* update wasmtime dependency to fix performance regression in 0.3.0
|
||||||
|
* add frame counter since module start at location 72
|
||||||
|
* add 6 and 7 parameter function types to base module
|
||||||
|
|
||||||
|
### v0.3.0
|
||||||
|
|
||||||
|
* [Web runtime](../v0.3.0)
|
||||||
|
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.3.0/microw8-0.3.0-linux.tgz)
|
||||||
|
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.3.0/microw8-0.3.0-macos.tgz)
|
||||||
|
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.3.0/microw8-0.3.0-windows.zip)
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
* add blitSprite and grabSprite API calls
|
||||||
|
* add support for integer scaling up to 16x for printing text
|
||||||
|
* fix incompatibility with sound devices only offering 16bit audio formats
|
||||||
|
* add support for br_table instruction in packed carts
|
||||||
|
|
||||||
|
### v0.2.2
|
||||||
|
|
||||||
|
* [Web runtime](../v0.2.2)
|
||||||
|
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.2.2/microw8-0.2.2-linux.tgz)
|
||||||
|
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.2.2/microw8-0.2.2-macos.tgz)
|
||||||
|
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.2.2/microw8-0.2.2-windows.zip)
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
* call `start` function after loading cart if the cart exports one
|
||||||
|
* fix `sndGes` having the wrong name and not being included in the auto imports
|
||||||
|
* fix control codes 4-6 (change text output mode) being invoked when used as parameters in other control sequences
|
||||||
|
* only open browser window once a cart was compiled sucessfully when running with `-b`
|
||||||
|
|
||||||
|
### v0.2.1
|
||||||
|
|
||||||
|
* [Web runtime](../v0.2.1)
|
||||||
|
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.2.1/microw8-0.2.1-linux.tgz)
|
||||||
|
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.2.1/microw8-0.2.1-macos.tgz)
|
||||||
|
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.2.1/microw8-0.2.1-windows.zip)
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
* new gpu accelerated renderer with (optional) crt filter
|
||||||
|
* optimized `hline` function, a big speed-up when drawing large filled circles or rectangles
|
||||||
|
* print fractional size of packed `uw8` cart
|
||||||
|
|
||||||
|
### v0.2.0
|
||||||
|
|
||||||
|
* [Web runtime](../v0.2.0)
|
||||||
|
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.2.0/microw8-0.2.0-linux.tgz)
|
||||||
|
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.2.0/microw8-0.2.0-macos.tgz)
|
||||||
|
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.2.0/microw8-0.2.0-windows.zip)
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
* [add sound support!](docs#sound)
|
||||||
|
* add support to redirect text output to the console for debugging using control code 6
|
||||||
|
* update curlywas:
|
||||||
|
* add support for `else if`
|
||||||
|
* add support for escape sequences in strings
|
||||||
|
* add support for char literals
|
||||||
|
* add support for binop-assignment, eg. `+=`, `^=`, `<<=` etc. (also support for the tee operator: `+:=`)
|
||||||
|
* "integer constant cast to float" literal syntax in CurlyWas (ex. `1_f` is equivalent to `1 as f32`)
|
||||||
|
|
||||||
|
### v0.2.0-rc3
|
||||||
|
|
||||||
|
* [Web runtime](../v0.2.0-rc3)
|
||||||
|
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.2.0-rc3/microw8-0.2.0-rc3-linux.tgz)
|
||||||
|
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.2.0-rc3/microw8-0.2.0-rc3-macos.tgz)
|
||||||
|
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.2.0-rc3/microw8-0.2.0-rc3-windows.zip)
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
* improve timing stability some more. essentially now guaranteeing that "frame = time_ms * 6 / 100" returns
|
||||||
|
consecutive frame numbers, provided the module can be run at 60 fps
|
||||||
|
* add support to redirect text output to the console for debugging using control code 6
|
||||||
|
* update curlywas:
|
||||||
|
* add support for `else if`
|
||||||
|
* add support for escape sequences in strings
|
||||||
|
* add support for char literals
|
||||||
|
* add support for binop-assignment, eg. `+=`, `^=`, `<<=` etc. (also support for the tee operator: `+:=`)
|
||||||
|
|
||||||
|
### v0.2.0-rc2
|
||||||
|
|
||||||
|
* [Web runtime](../v0.2.0-rc2)
|
||||||
|
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.2.0-rc2/microw8-0.2.0-rc2-linux.tgz)
|
||||||
|
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.2.0-rc2/microw8-0.2.0-rc2-macos.tgz)
|
||||||
|
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.2.0-rc2/microw8-0.2.0-rc2-windows.zip)
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
* fix timing issues of sound playback, especially on systems with large sound buffers
|
||||||
|
|
||||||
|
### v0.2.0-rc1
|
||||||
|
|
||||||
|
* [Web runtime](../v0.2.0-rc1)
|
||||||
|
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.2.0-rc1/microw8-0.2.0-rc1-linux.tgz)
|
||||||
|
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.2.0-rc1/microw8-0.2.0-rc1-macos.tgz)
|
||||||
|
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.2.0-rc1/microw8-0.2.0-rc1-windows.zip)
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
* [add sound support](docs#sound)
|
||||||
|
* "integer constant cast to float" literal syntax in CurlyWas (ex. `1_f` is equivalent to `1 as f32`)
|
||||||
|
|
||||||
|
Known issues:
|
||||||
|
|
||||||
|
* timing accuracy/update frequency of sound support currently depends on sound buffer size
|
||||||
|
|
||||||
|
### v0.1.2
|
||||||
|
|
||||||
|
* [Web runtime](../v0.1.2)
|
||||||
|
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.1.2/microw8-0.1.2-linux.tgz)
|
||||||
|
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.1.2/microw8-0.1.2-macos.tgz)
|
||||||
|
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.1.2/microw8-0.1.2-windows.zip)
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
* add option to `uw8 run` to run the cart in the browser using the web runtime
|
||||||
|
*../ CurlyWas: implement `include` support
|
||||||
|
* CurlyWas: implement support for constants
|
||||||
|
* fix crash when trying to draw zero sized line
|
||||||
|
|
||||||
|
### v0.1.1
|
||||||
|
|
||||||
|
* [Web runtime](../v0.1.1)
|
||||||
|
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.1.1/microw8-0.1.1-linux.tgz)
|
||||||
|
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.1.1/microw8-0.1.1-macos.tgz)
|
||||||
|
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.1.1/microw8-0.1.1-windows.zip)
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
|
||||||
|
* implement more robust file watcher
|
||||||
|
* add basic video recording on F10 in web runtime
|
||||||
|
*../ add screenshot on F9
|
||||||
|
* add watchdog to interrupt hanging update in native runtime
|
||||||
|
* add devkit mode to web runtime
|
||||||
|
*../ add unpack and compile commands to uw8
|
||||||
|
* add support for table/element section in pack command
|
||||||
|
* disable wayland support (caused missing window decorations in gnome)
|
||||||
|
|
||||||
|
### v0.1.0
|
||||||
|
|
||||||
|
* [Web runtime](../v0.1.0)
|
||||||
|
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.1.0/microw8-0.1.0-linux.tgz)
|
||||||
|
* [MacOS](https://github.com/exoticorn/microw8/releases/download/v0.1.0/microw8-0.1.0-macos.tgz)
|
||||||
|
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.1.0/microw8-0.1.0-windows.zip)
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@
|
|||||||
<section>
|
<section>
|
||||||
<h1 class="text-center heading-text">A WebAssembly based fantasy console</h1>
|
<h1 class="text-center heading-text">A WebAssembly based fantasy console</h1>
|
||||||
</section>
|
</section>
|
||||||
<a href="v0.1.1">
|
<a href="v0.4.1">
|
||||||
<img class="demonstration-gif" style="width:640px;height:480px;image-rendering:pixelated" src="img/technotunnel.png"></img>
|
<img class="demonstration-gif" style="width:640px;height:480px;image-rendering:pixelated" src="img/technotunnel.png"></img>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -15,4 +15,4 @@
|
|||||||
{% endblock hero %}
|
{% endblock hero %}
|
||||||
|
|
||||||
{% block footer %}
|
{% block footer %}
|
||||||
{% endblock footer %}
|
{% endblock footer %}
|
||||||
|
|||||||
+51
-50
@@ -1,36 +1,67 @@
|
|||||||
use anyhow::{bail, Result};
|
use anyhow::{anyhow, bail, Result};
|
||||||
use notify::{DebouncedEvent, Watcher, RecommendedWatcher};
|
use notify_debouncer_mini::{
|
||||||
use std::{
|
new_debouncer,
|
||||||
collections::BTreeSet,
|
notify::{self, RecommendedWatcher},
|
||||||
path::{Path, PathBuf},
|
DebouncedEvent, DebouncedEventKind, Debouncer,
|
||||||
sync::mpsc,
|
|
||||||
time::Duration,
|
|
||||||
};
|
};
|
||||||
|
use std::{collections::BTreeSet, path::PathBuf, sync::mpsc, time::Duration};
|
||||||
|
|
||||||
pub struct FileWatcher {
|
pub struct FileWatcher {
|
||||||
_watcher: RecommendedWatcher,
|
debouncer: Debouncer<RecommendedWatcher>,
|
||||||
watched_files: BTreeSet<PathBuf>,
|
watched_files: BTreeSet<PathBuf>,
|
||||||
|
directories: BTreeSet<PathBuf>,
|
||||||
rx: mpsc::Receiver<DebouncedEvent>,
|
rx: mpsc::Receiver<DebouncedEvent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FileWatcherBuilder(BTreeSet<PathBuf>);
|
|
||||||
|
|
||||||
impl FileWatcher {
|
impl FileWatcher {
|
||||||
pub fn builder() -> FileWatcherBuilder {
|
pub fn new() -> Result<FileWatcher> {
|
||||||
FileWatcherBuilder(BTreeSet::new())
|
let (tx, rx) = mpsc::channel();
|
||||||
|
let debouncer = new_debouncer(Duration::from_millis(100), move |res| match res {
|
||||||
|
Ok(events) => {
|
||||||
|
for event in events {
|
||||||
|
let _ = tx.send(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Error watching for file changes: {err}");
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
Ok(FileWatcher {
|
||||||
|
debouncer,
|
||||||
|
watched_files: BTreeSet::new(),
|
||||||
|
directories: BTreeSet::new(),
|
||||||
|
rx,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_file<P: Into<PathBuf>>(&mut self, path: P) -> Result<()> {
|
||||||
|
let path = path.into();
|
||||||
|
let parent = path.parent().ok_or_else(|| anyhow!("File has no parent"))?;
|
||||||
|
|
||||||
|
if !self.directories.contains(parent) {
|
||||||
|
self.debouncer
|
||||||
|
.watcher()
|
||||||
|
.watch(parent, notify::RecursiveMode::NonRecursive)?;
|
||||||
|
self.directories.insert(parent.to_path_buf());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.watched_files.insert(path);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn poll_changed_file(&self) -> Result<Option<PathBuf>> {
|
pub fn poll_changed_file(&self) -> Result<Option<PathBuf>> {
|
||||||
let event = self.rx.try_recv();
|
match self.rx.try_recv() {
|
||||||
match event {
|
Ok(event) => match event.kind {
|
||||||
Ok(DebouncedEvent::Create(path) | DebouncedEvent::Write(path)) => {
|
DebouncedEventKind::Any => {
|
||||||
let handle = same_file::Handle::from_path(&path)?;
|
let handle = same_file::Handle::from_path(&event.path)?;
|
||||||
for file in &self.watched_files {
|
for file in &self.watched_files {
|
||||||
if handle == same_file::Handle::from_path(file)? {
|
if handle == same_file::Handle::from_path(file)? {
|
||||||
return Ok(Some(path));
|
return Ok(Some(event.path));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
_ => (),
|
||||||
|
},
|
||||||
Err(mpsc::TryRecvError::Disconnected) => bail!("File watcher disconnected"),
|
Err(mpsc::TryRecvError::Disconnected) => bail!("File watcher disconnected"),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
@@ -38,33 +69,3 @@ impl FileWatcher {
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileWatcherBuilder {
|
|
||||||
pub fn add_file<P: Into<PathBuf>>(&mut self, path: P) -> &mut Self {
|
|
||||||
self.0.insert(path.into());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build(self) -> Result<FileWatcher> {
|
|
||||||
let mut directories: BTreeSet<&Path> = BTreeSet::new();
|
|
||||||
|
|
||||||
for file in &self.0 {
|
|
||||||
if let Some(directory) = file.parent() {
|
|
||||||
directories.insert(directory);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let (tx, rx) = mpsc::channel();
|
|
||||||
let mut watcher = notify::watcher(tx, Duration::from_millis(100))?;
|
|
||||||
|
|
||||||
for directory in directories {
|
|
||||||
watcher.watch(directory, notify::RecursiveMode::NonRecursive)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(FileWatcher {
|
|
||||||
_watcher: watcher,
|
|
||||||
watched_files: self.0,
|
|
||||||
rx,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+13
-264
@@ -1,270 +1,19 @@
|
|||||||
use std::io::prelude::*;
|
|
||||||
use std::path::Path;
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use std::time::Duration;
|
|
||||||
use std::{fs::File, thread, time::Instant};
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use minifb::{Key, Window, WindowOptions};
|
|
||||||
use wasmtime::{
|
|
||||||
Engine, GlobalType, Memory, MemoryType, Module, Mutability, Store, TypedFunc, ValType,
|
|
||||||
};
|
|
||||||
|
|
||||||
mod filewatcher;
|
mod filewatcher;
|
||||||
|
#[cfg(feature = "native")]
|
||||||
|
mod run_native;
|
||||||
|
#[cfg(feature = "browser")]
|
||||||
|
mod run_web;
|
||||||
|
|
||||||
pub use filewatcher::FileWatcher;
|
pub use filewatcher::FileWatcher;
|
||||||
|
#[cfg(feature = "native")]
|
||||||
|
pub use run_native::MicroW8;
|
||||||
|
#[cfg(feature = "browser")]
|
||||||
|
pub use run_web::RunWebServer;
|
||||||
|
|
||||||
static GAMEPAD_KEYS: &'static [Key] = &[
|
use anyhow::Result;
|
||||||
Key::Up,
|
|
||||||
Key::Down,
|
|
||||||
Key::Left,
|
|
||||||
Key::Right,
|
|
||||||
Key::Z,
|
|
||||||
Key::X,
|
|
||||||
Key::A,
|
|
||||||
Key::S,
|
|
||||||
];
|
|
||||||
|
|
||||||
pub struct MicroW8 {
|
pub trait Runtime {
|
||||||
engine: Engine,
|
fn is_open(&self) -> bool;
|
||||||
loader_module: Module,
|
fn load(&mut self, module_data: &[u8]) -> Result<()>;
|
||||||
window: Window,
|
fn run_frame(&mut self) -> Result<()>;
|
||||||
window_buffer: Vec<u32>,
|
|
||||||
instance: Option<UW8Instance>,
|
|
||||||
timeout: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UW8Instance {
|
|
||||||
store: Store<()>,
|
|
||||||
memory: Memory,
|
|
||||||
end_frame: TypedFunc<(), ()>,
|
|
||||||
update: TypedFunc<(), ()>,
|
|
||||||
start_time: Instant,
|
|
||||||
module: Vec<u8>,
|
|
||||||
watchdog: Arc<Mutex<UW8WatchDog>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for UW8Instance {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if let Ok(mut watchdog) = self.watchdog.lock() {
|
|
||||||
watchdog.stop = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UW8WatchDog {
|
|
||||||
interupt: wasmtime::InterruptHandle,
|
|
||||||
timeout: u32,
|
|
||||||
stop: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MicroW8 {
|
|
||||||
pub fn new() -> Result<MicroW8> {
|
|
||||||
let engine = wasmtime::Engine::new(wasmtime::Config::new().interruptable(true))?;
|
|
||||||
|
|
||||||
let loader_module =
|
|
||||||
wasmtime::Module::new(&engine, include_bytes!("../platform/bin/loader.wasm"))?;
|
|
||||||
|
|
||||||
let mut options = WindowOptions::default();
|
|
||||||
options.scale = minifb::Scale::X2;
|
|
||||||
options.scale_mode = minifb::ScaleMode::AspectRatioStretch;
|
|
||||||
options.resize = true;
|
|
||||||
let mut window = Window::new("MicroW8", 320, 240, options)?;
|
|
||||||
window.limit_update_rate(Some(std::time::Duration::from_micros(16666)));
|
|
||||||
|
|
||||||
Ok(MicroW8 {
|
|
||||||
engine,
|
|
||||||
loader_module,
|
|
||||||
window,
|
|
||||||
window_buffer: vec![0u32; 320 * 240],
|
|
||||||
instance: None,
|
|
||||||
timeout: 30,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_open(&self) -> bool {
|
|
||||||
self.window.is_open() && !self.window.is_key_down(Key::Escape)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_timeout(&mut self, timeout: u32) {
|
|
||||||
self.timeout = timeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reset(&mut self) {
|
|
||||||
self.instance = None;
|
|
||||||
for v in &mut self.window_buffer {
|
|
||||||
*v = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_from_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
|
|
||||||
self.reset();
|
|
||||||
|
|
||||||
let mut module = vec![];
|
|
||||||
File::open(path)?.read_to_end(&mut module)?;
|
|
||||||
self.load_from_memory(&module)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_from_memory(&mut self, module_data: &[u8]) -> Result<()> {
|
|
||||||
self.reset();
|
|
||||||
|
|
||||||
let mut store = wasmtime::Store::new(&self.engine, ());
|
|
||||||
|
|
||||||
let memory = wasmtime::Memory::new(&mut store, MemoryType::new(4, Some(4)))?;
|
|
||||||
|
|
||||||
let mut linker = wasmtime::Linker::new(&self.engine);
|
|
||||||
linker.define("env", "memory", memory.clone())?;
|
|
||||||
|
|
||||||
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 platform_data = include_bytes!("../platform/bin/platform.uw8");
|
|
||||||
memory.data_mut(&mut store)[..platform_data.len()].copy_from_slice(platform_data);
|
|
||||||
let platform_length =
|
|
||||||
load_uw8.call(&mut store, platform_data.len() as i32)? as u32 as usize;
|
|
||||||
let platform_module =
|
|
||||||
wasmtime::Module::new(&self.engine, &memory.data(&store)[..platform_length])?;
|
|
||||||
|
|
||||||
memory.data_mut(&mut store)[..module_data.len()].copy_from_slice(module_data);
|
|
||||||
let module_length = load_uw8.call(&mut store, module_data.len() as i32)? as u32 as usize;
|
|
||||||
let module = wasmtime::Module::new(&self.engine, &memory.data(&store)[..module_length])?;
|
|
||||||
|
|
||||||
linker.func_wrap("env", "acos", |v: f32| v.acos())?;
|
|
||||||
linker.func_wrap("env", "asin", |v: f32| v.asin())?;
|
|
||||||
linker.func_wrap("env", "atan", |v: f32| v.atan())?;
|
|
||||||
linker.func_wrap("env", "atan2", |x: f32, y: f32| x.atan2(y))?;
|
|
||||||
linker.func_wrap("env", "cos", |v: f32| v.cos())?;
|
|
||||||
linker.func_wrap("env", "exp", |v: f32| v.exp())?;
|
|
||||||
linker.func_wrap("env", "log", |v: f32| v.ln())?;
|
|
||||||
linker.func_wrap("env", "sin", |v: f32| v.sin())?;
|
|
||||||
linker.func_wrap("env", "tan", |v: f32| v.tan())?;
|
|
||||||
linker.func_wrap("env", "pow", |a: f32, b: f32| a.powf(b))?;
|
|
||||||
for i in 9..64 {
|
|
||||||
linker.func_wrap("env", &format!("reserved{}", i), || {})?;
|
|
||||||
}
|
|
||||||
for i in 0..16 {
|
|
||||||
linker.define(
|
|
||||||
"env",
|
|
||||||
&format!("g_reserved{}", i),
|
|
||||||
wasmtime::Global::new(
|
|
||||||
&mut store,
|
|
||||||
GlobalType::new(ValType::I32, Mutability::Const),
|
|
||||||
0.into(),
|
|
||||||
)?,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let platform_instance = linker.instantiate(&mut store, &platform_module)?;
|
|
||||||
|
|
||||||
for export in platform_instance.exports(&mut store) {
|
|
||||||
linker.define(
|
|
||||||
"env",
|
|
||||||
export.name(),
|
|
||||||
export
|
|
||||||
.into_func()
|
|
||||||
.expect("platform surely only exports functions"),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let watchdog = Arc::new(Mutex::new(UW8WatchDog {
|
|
||||||
interupt: store.interrupt_handle()?,
|
|
||||||
timeout: self.timeout,
|
|
||||||
stop: false,
|
|
||||||
}));
|
|
||||||
|
|
||||||
{
|
|
||||||
let watchdog = watchdog.clone();
|
|
||||||
thread::spawn(move || loop {
|
|
||||||
thread::sleep(Duration::from_millis(17));
|
|
||||||
if let Ok(mut watchdog) = watchdog.lock() {
|
|
||||||
if watchdog.stop {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if watchdog.timeout > 0 {
|
|
||||||
watchdog.timeout -= 1;
|
|
||||||
if watchdog.timeout == 0 {
|
|
||||||
watchdog.interupt.interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let instance = linker.instantiate(&mut store, &module)?;
|
|
||||||
if let Ok(mut watchdog) = watchdog.lock() {
|
|
||||||
watchdog.timeout = 0;
|
|
||||||
}
|
|
||||||
let end_frame = platform_instance.get_typed_func::<(), (), _>(&mut store, "endFrame")?;
|
|
||||||
let update = instance.get_typed_func::<(), (), _>(&mut store, "upd")?;
|
|
||||||
|
|
||||||
self.instance = Some(UW8Instance {
|
|
||||||
store,
|
|
||||||
memory,
|
|
||||||
end_frame,
|
|
||||||
update,
|
|
||||||
start_time: Instant::now(),
|
|
||||||
module: module_data.into(),
|
|
||||||
watchdog,
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run_frame(&mut self) -> Result<()> {
|
|
||||||
let mut result = Ok(());
|
|
||||||
if let Some(mut instance) = self.instance.take() {
|
|
||||||
{
|
|
||||||
let time = instance.start_time.elapsed().as_millis() as i32;
|
|
||||||
let mut gamepad: u32 = 0;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Ok(mut watchdog) = instance.watchdog.lock() {
|
|
||||||
watchdog.timeout = self.timeout;
|
|
||||||
}
|
|
||||||
result = instance.update.call(&mut instance.store, ());
|
|
||||||
if let Ok(mut watchdog) = instance.watchdog.lock() {
|
|
||||||
watchdog.timeout = 0;
|
|
||||||
}
|
|
||||||
instance.end_frame.call(&mut instance.store, ())?;
|
|
||||||
|
|
||||||
let memory = instance.memory.data(&instance.store);
|
|
||||||
let framebuffer = &memory[120..];
|
|
||||||
let palette = &memory[0x13000..];
|
|
||||||
for i in 0..320 * 240 {
|
|
||||||
let offset = framebuffer[i] as usize * 4;
|
|
||||||
self.window_buffer[i] = 0xff000000
|
|
||||||
| ((palette[offset + 0] as u32) << 16)
|
|
||||||
| ((palette[offset + 1] as u32) << 8)
|
|
||||||
| palette[offset + 2] as u32;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.window.is_key_pressed(Key::R, minifb::KeyRepeat::No) {
|
|
||||||
self.load_from_memory(&instance.module)?;
|
|
||||||
} else if result.is_ok() {
|
|
||||||
self.instance = Some(instance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.window
|
|
||||||
.update_with_buffer(&self.window_buffer, 320, 240)?;
|
|
||||||
|
|
||||||
result?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+168
-56
@@ -1,23 +1,31 @@
|
|||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
use std::process;
|
use std::process;
|
||||||
use std::{
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
process::exit,
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use pico_args::Arguments;
|
use pico_args::Arguments;
|
||||||
use uw8::{FileWatcher, MicroW8};
|
#[cfg(feature = "native")]
|
||||||
|
use uw8::MicroW8;
|
||||||
|
#[cfg(feature = "browser")]
|
||||||
|
use uw8::RunWebServer;
|
||||||
|
#[cfg(any(feature = "native", feature = "browser"))]
|
||||||
|
use uw8::Runtime;
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
|
env_logger::Builder::from_env(env_logger::Env::default()).init();
|
||||||
let mut args = Arguments::from_env();
|
let mut args = Arguments::from_env();
|
||||||
|
|
||||||
match args.subcommand()?.as_ref().map(|s| s.as_str()) {
|
// try to enable ansi support in win10 cmd shell
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
let _ = ansi_term::enable_ansi_support();
|
||||||
|
|
||||||
|
match args.subcommand()?.as_deref() {
|
||||||
Some("version") => {
|
Some("version") => {
|
||||||
println!("{}", env!("CARGO_PKG_VERSION"));
|
println!("{}", env!("CARGO_PKG_VERSION"));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
#[cfg(any(feature = "native", feature = "browser"))]
|
||||||
Some("run") => run(args),
|
Some("run") => run(args),
|
||||||
Some("pack") => pack(args),
|
Some("pack") => pack(args),
|
||||||
Some("unpack") => unpack(args),
|
Some("unpack") => unpack(args),
|
||||||
@@ -27,7 +35,8 @@ fn main() -> Result<()> {
|
|||||||
println!("uw8 {}", env!("CARGO_PKG_VERSION"));
|
println!("uw8 {}", env!("CARGO_PKG_VERSION"));
|
||||||
println!();
|
println!();
|
||||||
println!("Usage:");
|
println!("Usage:");
|
||||||
println!(" uw8 run [-t/--timeout <frames>] [-w/--watch] [-p/--pack] [-u/--uncompressed] [-l/--level] [-o/--output <out-file>] <file>");
|
#[cfg(any(feature = "native", feature = "browser"))]
|
||||||
|
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 pack [-u/--uncompressed] [-l/--level] <in-file> <out-file>");
|
||||||
println!(" uw8 unpack <in-file> <out-file>");
|
println!(" uw8 unpack <in-file> <out-file>");
|
||||||
println!(" uw8 compile [-d/--debug] <in-file> <out-file>");
|
println!(" uw8 compile [-d/--debug] <in-file> <out-file>");
|
||||||
@@ -41,8 +50,10 @@ fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(feature = "native", feature = "browser"))]
|
||||||
fn run(mut args: Arguments) -> Result<()> {
|
fn run(mut args: Arguments) -> Result<()> {
|
||||||
let watch_mode = args.contains(["-w", "--watch"]);
|
let watch_mode = args.contains(["-w", "--watch"]);
|
||||||
|
#[allow(unused)]
|
||||||
let timeout: Option<u32> = args.opt_value_from_str(["-t", "--timeout"])?;
|
let timeout: Option<u32> = args.opt_value_from_str(["-t", "--timeout"])?;
|
||||||
|
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
@@ -65,37 +76,67 @@ fn run(mut args: Arguments) -> Result<()> {
|
|||||||
config.output_path = Some(path);
|
config.output_path = Some(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "native")]
|
||||||
|
let run_browser = args.contains(["-b", "--browser"]);
|
||||||
|
#[cfg(not(feature = "native"))]
|
||||||
|
let run_browser = args.contains(["-b", "--browser"]) || true;
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
let disable_audio = args.contains(["-m", "--no-audio"]);
|
||||||
|
|
||||||
|
#[cfg(feature = "native")]
|
||||||
|
let window_config = {
|
||||||
|
let mut config = 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()))?;
|
let filename = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
|
||||||
|
|
||||||
let mut uw8 = MicroW8::new()?;
|
let mut watcher = uw8::FileWatcher::new()?;
|
||||||
|
|
||||||
if let Some(timeout) = timeout {
|
use std::process::exit;
|
||||||
uw8.set_timeout(timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut watcher = FileWatcher::builder();
|
let mut runtime: Box<dyn Runtime> = if !run_browser {
|
||||||
|
#[cfg(not(feature = "native"))]
|
||||||
if watch_mode {
|
unimplemented!();
|
||||||
watcher.add_file(&filename);
|
#[cfg(feature = "native")]
|
||||||
}
|
{
|
||||||
|
let mut microw8 = MicroW8::new(timeout, window_config)?;
|
||||||
let watcher = watcher.build()?;
|
if disable_audio {
|
||||||
|
microw8.disable_audio();
|
||||||
if let Err(err) = start_cart(&filename, &mut uw8, &config) {
|
|
||||||
eprintln!("Load error: {}", err);
|
|
||||||
if !watch_mode {
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while uw8.is_open() {
|
|
||||||
if watcher.poll_changed_file()?.is_some() {
|
|
||||||
if let Err(err) = start_cart(&filename, &mut uw8, &config) {
|
|
||||||
eprintln!("Load error: {}", err);
|
|
||||||
}
|
}
|
||||||
|
Box::new(microw8)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
#[cfg(not(feature = "browser"))]
|
||||||
|
unimplemented!();
|
||||||
|
#[cfg(feature = "browser")]
|
||||||
|
Box::new(RunWebServer::new())
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut first_run = true;
|
||||||
|
|
||||||
|
while runtime.is_open() {
|
||||||
|
if first_run || watcher.poll_changed_file()?.is_some() {
|
||||||
|
let (result, dependencies) = start_cart(&filename, &mut *runtime, &config);
|
||||||
|
if watch_mode {
|
||||||
|
for dep in dependencies {
|
||||||
|
watcher.add_file(dep)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Err(err) = result {
|
||||||
|
eprintln!("Load error: {}", err);
|
||||||
|
if !watch_mode {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
first_run = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(err) = uw8.run_frame() {
|
if let Err(err) = runtime.run_frame() {
|
||||||
eprintln!("Runtime error: {}", err);
|
eprintln!("Runtime error: {}", err);
|
||||||
if !watch_mode {
|
if !watch_mode {
|
||||||
exit(1);
|
exit(1);
|
||||||
@@ -112,39 +153,103 @@ struct Config {
|
|||||||
output_path: Option<PathBuf>,
|
output_path: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_cart(filename: &Path, pack: &Option<uw8_tool::PackConfig>) -> Result<Vec<u8>> {
|
fn load_cart(filename: &Path, config: &Config) -> (Result<Vec<u8>>, Vec<PathBuf>) {
|
||||||
let mut cart = vec![];
|
let mut dependencies = Vec::new();
|
||||||
File::open(filename)?.read_to_end(&mut cart)?;
|
fn inner(filename: &Path, config: &Config, dependencies: &mut Vec<PathBuf>) -> Result<Vec<u8>> {
|
||||||
|
let mut cart = match SourceType::of_file(filename)? {
|
||||||
if cart[0] >= 10 {
|
SourceType::Binary => {
|
||||||
let src = String::from_utf8(cart)?;
|
let mut cart = vec![];
|
||||||
cart = if src.chars().find(|c| !c.is_whitespace()) == Some('(') {
|
File::open(filename)?.read_to_end(&mut cart)?;
|
||||||
wat::parse_str(src)?
|
cart
|
||||||
} else {
|
}
|
||||||
curlywas::compile_str(&src, filename, curlywas::Options::default())?
|
SourceType::Wat => {
|
||||||
|
let cart = wat::parse_file(filename)?;
|
||||||
|
cart
|
||||||
|
}
|
||||||
|
SourceType::CurlyWas => {
|
||||||
|
let (module, deps) = curlywas::compile_file(filename, curlywas::Options::default());
|
||||||
|
*dependencies = deps;
|
||||||
|
module?
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let Some(ref pack_config) = config.pack {
|
||||||
|
cart = uw8_tool::pack(&cart, pack_config)?;
|
||||||
|
println!(
|
||||||
|
"\npacked size: {} bytes ({:.2})",
|
||||||
|
cart.len(),
|
||||||
|
uw8_tool::compressed_size(&cart)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref path) = config.output_path {
|
||||||
|
File::create(path)?.write_all(&cart)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(cart)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(pack_config) = pack {
|
let result = inner(filename, config, &mut dependencies);
|
||||||
cart = uw8_tool::pack(&cart, pack_config)?;
|
|
||||||
println!("packed size: {} bytes", cart.len());
|
if dependencies.is_empty() {
|
||||||
|
dependencies.push(filename.to_path_buf());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(cart)
|
(result, dependencies)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_cart(filename: &Path, uw8: &mut MicroW8, config: &Config) -> Result<()> {
|
enum SourceType {
|
||||||
let cart = load_cart(filename, &config.pack)?;
|
Binary,
|
||||||
|
Wat,
|
||||||
|
CurlyWas,
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(ref path) = config.output_path {
|
impl SourceType {
|
||||||
File::create(path)?.write_all(&cart)?;
|
fn of_file(filename: &Path) -> Result<SourceType> {
|
||||||
|
if let Some(extension) = filename.extension() {
|
||||||
|
if extension == "uw8" || extension == "wasm" {
|
||||||
|
return Ok(SourceType::Binary);
|
||||||
|
} else if extension == "wat" || extension == "wast" {
|
||||||
|
return Ok(SourceType::Wat);
|
||||||
|
} else if extension == "cwa" {
|
||||||
|
return Ok(SourceType::CurlyWas);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cart = vec![];
|
||||||
|
File::open(filename)?.read_to_end(&mut cart)?;
|
||||||
|
|
||||||
|
let ty = if cart[0] < 10 {
|
||||||
|
SourceType::Binary
|
||||||
|
} else {
|
||||||
|
let src = String::from_utf8(cart)?;
|
||||||
|
if src.chars().find(|&c| !c.is_whitespace() && c != ';') == Some('(') {
|
||||||
|
SourceType::Wat
|
||||||
|
} else {
|
||||||
|
SourceType::CurlyWas
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(ty)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Err(err) = uw8.load_from_memory(&cart) {
|
#[cfg(any(feature = "native", feature = "browser"))]
|
||||||
|
fn start_cart(
|
||||||
|
filename: &Path,
|
||||||
|
runtime: &mut dyn Runtime,
|
||||||
|
config: &Config,
|
||||||
|
) -> (Result<()>, Vec<PathBuf>) {
|
||||||
|
let (cart, dependencies) = load_cart(filename, config);
|
||||||
|
let cart = match cart {
|
||||||
|
Ok(cart) => cart,
|
||||||
|
Err(err) => return (Err(err), dependencies),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(err) = runtime.load(&cart) {
|
||||||
eprintln!("Load error: {}", err);
|
eprintln!("Load error: {}", err);
|
||||||
Err(err)
|
(Err(err), dependencies)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
(Ok(()), dependencies)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,7 +268,14 @@ fn pack(mut args: Arguments) -> Result<()> {
|
|||||||
|
|
||||||
let out_file = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
|
let out_file = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
|
||||||
|
|
||||||
let cart = load_cart(&in_file, &Some(pack_config))?;
|
let cart = load_cart(
|
||||||
|
&in_file,
|
||||||
|
&Config {
|
||||||
|
pack: Some(pack_config),
|
||||||
|
output_path: None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.0?;
|
||||||
|
|
||||||
File::create(out_file)?.write_all(&cart)?;
|
File::create(out_file)?.write_all(&cart)?;
|
||||||
|
|
||||||
@@ -173,8 +285,8 @@ fn pack(mut args: Arguments) -> Result<()> {
|
|||||||
fn unpack(mut args: Arguments) -> Result<()> {
|
fn unpack(mut args: Arguments) -> Result<()> {
|
||||||
let in_file = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
|
let in_file = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
|
||||||
let out_file = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
|
let out_file = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
|
||||||
|
|
||||||
uw8_tool::unpack_file(&in_file, &out_file).into()
|
uw8_tool::unpack_file(&in_file, &out_file)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile(mut args: Arguments) -> Result<()> {
|
fn compile(mut args: Arguments) -> Result<()> {
|
||||||
@@ -186,7 +298,7 @@ fn compile(mut args: Arguments) -> Result<()> {
|
|||||||
let in_file = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
|
let in_file = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
|
||||||
let out_file = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
|
let out_file = args.free_from_os_str::<PathBuf, bool>(|s| Ok(s.into()))?;
|
||||||
|
|
||||||
let module = curlywas::compile_file(in_file, options)?;
|
let module = curlywas::compile_file(in_file, options).0?;
|
||||||
File::create(out_file)?.write_all(&module)?;
|
File::create(out_file)?.write_all(&module)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,627 @@
|
|||||||
|
use std::sync::{mpsc, Arc, Mutex};
|
||||||
|
use std::time::Duration;
|
||||||
|
use std::{thread, time::Instant};
|
||||||
|
|
||||||
|
use anyhow::{anyhow, bail, Result};
|
||||||
|
use cpal::traits::*;
|
||||||
|
use rubato::Resampler;
|
||||||
|
use uw8_window::{Window, WindowConfig};
|
||||||
|
use wasmtime::{
|
||||||
|
Engine, Func, GlobalType, Memory, MemoryType, Module, Mutability, Store, TypedFunc, ValType,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct MicroW8 {
|
||||||
|
window: Window,
|
||||||
|
stream: Option<cpal::Stream>,
|
||||||
|
engine: Engine,
|
||||||
|
loader_module: Module,
|
||||||
|
disable_audio: bool,
|
||||||
|
module_data: Option<Vec<u8>>,
|
||||||
|
timeout: u32,
|
||||||
|
instance: Option<UW8Instance>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UW8Instance {
|
||||||
|
store: Store<()>,
|
||||||
|
memory: Memory,
|
||||||
|
end_frame: TypedFunc<(), ()>,
|
||||||
|
update: Option<TypedFunc<(), ()>>,
|
||||||
|
start_time: Instant,
|
||||||
|
frame_counter: u32,
|
||||||
|
watchdog: Arc<Mutex<UW8WatchDog>>,
|
||||||
|
sound_tx: Option<mpsc::SyncSender<RegisterUpdate>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for UW8Instance {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Ok(mut watchdog) = self.watchdog.lock() {
|
||||||
|
watchdog.stop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UW8WatchDog {
|
||||||
|
engine: Engine,
|
||||||
|
stop: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl 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() {
|
||||||
|
config.epoch_interruption(true);
|
||||||
|
}
|
||||||
|
let engine = wasmtime::Engine::new(&config)?;
|
||||||
|
|
||||||
|
let loader_module =
|
||||||
|
wasmtime::Module::new(&engine, include_bytes!("../platform/bin/loader.wasm"))?;
|
||||||
|
|
||||||
|
let window = Window::new(window_config)?;
|
||||||
|
|
||||||
|
Ok(MicroW8 {
|
||||||
|
window,
|
||||||
|
stream: None,
|
||||||
|
engine,
|
||||||
|
loader_module,
|
||||||
|
disable_audio: false,
|
||||||
|
module_data: None,
|
||||||
|
timeout: timeout.unwrap_or(0),
|
||||||
|
instance: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disable_audio(&mut self) {
|
||||||
|
self.disable_audio = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl super::Runtime for MicroW8 {
|
||||||
|
fn is_open(&self) -> bool {
|
||||||
|
self.window.is_open()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load(&mut self, module_data: &[u8]) -> Result<()> {
|
||||||
|
self.stream = None;
|
||||||
|
self.instance = None;
|
||||||
|
|
||||||
|
let mut store = wasmtime::Store::new(&self.engine, ());
|
||||||
|
store.set_epoch_deadline(60);
|
||||||
|
|
||||||
|
let memory = wasmtime::Memory::new(&mut store, MemoryType::new(4, Some(4)))?;
|
||||||
|
|
||||||
|
let mut linker = wasmtime::Linker::new(&self.engine);
|
||||||
|
linker.define(&store, "env", "memory", memory)?;
|
||||||
|
|
||||||
|
let loader_instance = linker.instantiate(&mut store, &self.loader_module)?;
|
||||||
|
let load_uw8 = loader_instance.get_typed_func::<i32, i32>(&mut store, "load_uw8")?;
|
||||||
|
|
||||||
|
let platform_data = include_bytes!("../platform/bin/platform.uw8");
|
||||||
|
memory.data_mut(&mut store)[..platform_data.len()].copy_from_slice(platform_data);
|
||||||
|
let platform_length =
|
||||||
|
load_uw8.call(&mut store, platform_data.len() as i32)? as u32 as usize;
|
||||||
|
let platform_module =
|
||||||
|
wasmtime::Module::new(&self.engine, &memory.data(&store)[..platform_length])?;
|
||||||
|
|
||||||
|
memory.data_mut(&mut store)[..module_data.len()].copy_from_slice(module_data);
|
||||||
|
let module_length = load_uw8.call(&mut store, module_data.len() as i32)? as u32 as usize;
|
||||||
|
let module = wasmtime::Module::new(&self.engine, &memory.data(&store)[..module_length])?;
|
||||||
|
|
||||||
|
add_native_functions(&mut linker, &mut store)?;
|
||||||
|
|
||||||
|
let platform_instance = instantiate_platform(&mut linker, &mut store, &platform_module)?;
|
||||||
|
|
||||||
|
let watchdog = Arc::new(Mutex::new(UW8WatchDog {
|
||||||
|
engine: self.engine.clone(),
|
||||||
|
stop: false,
|
||||||
|
}));
|
||||||
|
|
||||||
|
{
|
||||||
|
let watchdog = watchdog.clone();
|
||||||
|
thread::spawn(move || loop {
|
||||||
|
thread::sleep(Duration::from_millis(17));
|
||||||
|
if let Ok(watchdog) = watchdog.lock() {
|
||||||
|
if watchdog.stop {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
watchdog.engine.increment_epoch();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
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)
|
||||||
|
} else {
|
||||||
|
match init_sound(&self.engine, &platform_module, &module) {
|
||||||
|
Ok(sound) => {
|
||||||
|
sound.stream.play()?;
|
||||||
|
(Some(sound.tx), Some(sound.stream))
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Failed to init sound: {}", err);
|
||||||
|
(None, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.instance = Some(UW8Instance {
|
||||||
|
store,
|
||||||
|
memory,
|
||||||
|
end_frame,
|
||||||
|
update,
|
||||||
|
start_time: Instant::now(),
|
||||||
|
frame_counter: 0,
|
||||||
|
watchdog,
|
||||||
|
sound_tx,
|
||||||
|
});
|
||||||
|
self.stream = stream;
|
||||||
|
self.module_data = Some(module_data.into());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_frame(&mut self) -> Result<()> {
|
||||||
|
let input = self.window.begin_frame();
|
||||||
|
|
||||||
|
if input.reset {
|
||||||
|
if let Some(module_data) = self.module_data.take() {
|
||||||
|
self.load(&module_data)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let now = Instant::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);
|
||||||
|
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(&input.gamepads);
|
||||||
|
mem[72..76].copy_from_slice(&instance.frame_counter.to_le_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.frame_counter = instance.frame_counter.wrapping_add(1);
|
||||||
|
|
||||||
|
instance.store.set_epoch_deadline(self.timeout as u64);
|
||||||
|
if let Some(ref update) = instance.update {
|
||||||
|
if let Err(err) = update.call(&mut instance.store, ()) {
|
||||||
|
result = Err(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
instance.end_frame.call(&mut instance.store, ())?;
|
||||||
|
|
||||||
|
let memory = instance.memory.data(&instance.store);
|
||||||
|
|
||||||
|
let mut sound_regs = [0u8; 32];
|
||||||
|
sound_regs.copy_from_slice(&memory[80..112]);
|
||||||
|
if let Some(ref sound_tx) = instance.sound_tx {
|
||||||
|
let _ = sound_tx.send(RegisterUpdate {
|
||||||
|
time,
|
||||||
|
data: sound_regs,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let framebuffer_mem = &memory[120..(120 + 320 * 240)];
|
||||||
|
let palette_mem = &memory[0x13000..];
|
||||||
|
self.window
|
||||||
|
.end_frame(framebuffer_mem, palette_mem, next_frame);
|
||||||
|
|
||||||
|
if result.is_ok() {
|
||||||
|
self.instance = Some(instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_native_functions(
|
||||||
|
linker: &mut wasmtime::Linker<()>,
|
||||||
|
store: &mut wasmtime::Store<()>,
|
||||||
|
) -> Result<()> {
|
||||||
|
linker.func_wrap("env", "acos", |v: f32| v.acos())?;
|
||||||
|
linker.func_wrap("env", "asin", |v: f32| v.asin())?;
|
||||||
|
linker.func_wrap("env", "atan", |v: f32| v.atan())?;
|
||||||
|
linker.func_wrap("env", "atan2", |x: f32, y: f32| x.atan2(y))?;
|
||||||
|
linker.func_wrap("env", "cos", |v: f32| v.cos())?;
|
||||||
|
linker.func_wrap("env", "exp", |v: f32| v.exp())?;
|
||||||
|
linker.func_wrap("env", "log", |v: f32| v.ln())?;
|
||||||
|
linker.func_wrap("env", "sin", |v: f32| v.sin())?;
|
||||||
|
linker.func_wrap("env", "tan", |v: f32| v.tan())?;
|
||||||
|
linker.func_wrap("env", "pow", |a: f32, b: f32| a.powf(b))?;
|
||||||
|
for i in 10..64 {
|
||||||
|
linker.func_wrap("env", &format!("reserved{}", i), || {})?;
|
||||||
|
}
|
||||||
|
let log_line = std::sync::Mutex::new(String::new());
|
||||||
|
linker.func_wrap("env", "logChar", move |c: i32| {
|
||||||
|
let mut log_line = log_line.lock().unwrap();
|
||||||
|
if c == 10 {
|
||||||
|
println!("{}", log_line);
|
||||||
|
log_line.clear();
|
||||||
|
} else {
|
||||||
|
log_line.push(c as u8 as char);
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
for i in 0..16 {
|
||||||
|
let global = wasmtime::Global::new(
|
||||||
|
&mut *store,
|
||||||
|
GlobalType::new(ValType::I32, Mutability::Const),
|
||||||
|
0.into(),
|
||||||
|
)?;
|
||||||
|
linker.define(&store, "env", &format!("g_reserved{}", i), global)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn instantiate_platform(
|
||||||
|
linker: &mut wasmtime::Linker<()>,
|
||||||
|
store: &mut wasmtime::Store<()>,
|
||||||
|
platform_module: &wasmtime::Module,
|
||||||
|
) -> Result<wasmtime::Instance> {
|
||||||
|
let platform_instance = linker.instantiate(&mut *store, &platform_module)?;
|
||||||
|
|
||||||
|
let exports: Vec<(String, Func)> = platform_instance
|
||||||
|
.exports(&mut *store)
|
||||||
|
.map(|e| {
|
||||||
|
(
|
||||||
|
e.name().to_owned(),
|
||||||
|
e.into_func()
|
||||||
|
.expect("platform surely only exports functions"),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
for (name, func) in exports {
|
||||||
|
linker.define(&store, "env", &name, func)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(platform_instance)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RegisterUpdate {
|
||||||
|
time: i32,
|
||||||
|
data: [u8; 32],
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Uw8Sound {
|
||||||
|
stream: cpal::Stream,
|
||||||
|
tx: mpsc::SyncSender<RegisterUpdate>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_sound(
|
||||||
|
engine: &wasmtime::Engine,
|
||||||
|
platform_module: &wasmtime::Module,
|
||||||
|
module: &wasmtime::Module,
|
||||||
|
) -> Result<Uw8Sound> {
|
||||||
|
let mut store = wasmtime::Store::new(engine, ());
|
||||||
|
store.set_epoch_deadline(60);
|
||||||
|
|
||||||
|
let memory = wasmtime::Memory::new(&mut store, MemoryType::new(4, Some(4)))?;
|
||||||
|
|
||||||
|
let mut linker = wasmtime::Linker::new(engine);
|
||||||
|
linker.define(&store, "env", "memory", memory)?;
|
||||||
|
add_native_functions(&mut linker, &mut store)?;
|
||||||
|
|
||||||
|
let platform_instance = instantiate_platform(&mut linker, &mut store, platform_module)?;
|
||||||
|
let instance = linker.instantiate(&mut store, module)?;
|
||||||
|
|
||||||
|
let snd = instance
|
||||||
|
.get_typed_func::<(i32,), f32>(&mut store, "snd")
|
||||||
|
.or_else(|_| platform_instance.get_typed_func::<(i32,), f32>(&mut store, "sndGes"))?;
|
||||||
|
|
||||||
|
let host = cpal::default_host();
|
||||||
|
let device = host
|
||||||
|
.default_output_device()
|
||||||
|
.ok_or_else(|| anyhow!("No audio output device available"))?;
|
||||||
|
let mut configs: Vec<_> = device
|
||||||
|
.supported_output_configs()?
|
||||||
|
.filter(|config| {
|
||||||
|
config.sample_format() == cpal::SampleFormat::F32
|
||||||
|
|| config.sample_format() == cpal::SampleFormat::I16
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if configs.is_empty() {
|
||||||
|
eprintln!(
|
||||||
|
"No suitable audio output config found on device \"{}\", available configs:",
|
||||||
|
device.name()?
|
||||||
|
);
|
||||||
|
for config in device.supported_output_configs()? {
|
||||||
|
eprintln!(" {}ch {}", config.channels(), config.sample_format());
|
||||||
|
}
|
||||||
|
bail!("Failed to configure audio out");
|
||||||
|
}
|
||||||
|
|
||||||
|
configs.sort_by_key(|config| {
|
||||||
|
let rate = 44100
|
||||||
|
.max(config.min_sample_rate().0)
|
||||||
|
.min(config.max_sample_rate().0);
|
||||||
|
let rate_prio = if rate >= 44100 {
|
||||||
|
rate - 44100
|
||||||
|
} else {
|
||||||
|
(44100 - rate) * 1000
|
||||||
|
};
|
||||||
|
let format_prio = (config.sample_format() == cpal::SampleFormat::I16) as u32;
|
||||||
|
let channels_prio = (config.channels() != 2) as u32 * 16777216;
|
||||||
|
rate_prio + format_prio + channels_prio
|
||||||
|
});
|
||||||
|
let config = configs.into_iter().next().unwrap();
|
||||||
|
|
||||||
|
let sample_rate = cpal::SampleRate(44100)
|
||||||
|
.max(config.min_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,
|
||||||
|
cpal::SupportedBufferSize::Range { min, max } => {
|
||||||
|
cpal::BufferSize::Fixed(256.max(min).min(max))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let sample_format = config.sample_format();
|
||||||
|
let num_channels = config.channels();
|
||||||
|
let config = cpal::StreamConfig {
|
||||||
|
buffer_size,
|
||||||
|
..config.config()
|
||||||
|
};
|
||||||
|
|
||||||
|
let sample_rate = config.sample_rate.0 as usize;
|
||||||
|
|
||||||
|
let (tx, rx) = mpsc::sync_channel::<RegisterUpdate>(30);
|
||||||
|
|
||||||
|
struct Resampler {
|
||||||
|
resampler: rubato::FftFixedIn<f32>,
|
||||||
|
input_buffers: Vec<Vec<f32>>,
|
||||||
|
output_buffers: Vec<Vec<f32>>,
|
||||||
|
output_index: usize,
|
||||||
|
}
|
||||||
|
let mut resampler: Option<Resampler> = if sample_rate == 44100 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let rs = rubato::FftFixedIn::new(44100, sample_rate, 128, 1, 2)?;
|
||||||
|
let input_buffers = rs.input_buffer_allocate();
|
||||||
|
let output_buffers = rs.output_buffer_allocate();
|
||||||
|
Some(Resampler {
|
||||||
|
resampler: rs,
|
||||||
|
input_buffers,
|
||||||
|
output_buffers,
|
||||||
|
output_index: usize::MAX,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut sample_index = 0;
|
||||||
|
let mut pending_updates: Vec<RegisterUpdate> = Vec::with_capacity(30);
|
||||||
|
let mut current_time = 0;
|
||||||
|
|
||||||
|
let mut callback = move |mut outer_buffer: &mut [f32]| {
|
||||||
|
let mut first_update = true;
|
||||||
|
while let Ok(update) = rx.try_recv() {
|
||||||
|
if first_update {
|
||||||
|
current_time += update.time.wrapping_sub(current_time) / 8;
|
||||||
|
first_update = false;
|
||||||
|
}
|
||||||
|
pending_updates.push(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
while !outer_buffer.is_empty() {
|
||||||
|
store.set_epoch_deadline(30);
|
||||||
|
while pending_updates
|
||||||
|
.first()
|
||||||
|
.into_iter()
|
||||||
|
.any(|u| u.time.wrapping_sub(current_time) <= 0)
|
||||||
|
{
|
||||||
|
let update = pending_updates.remove(0);
|
||||||
|
memory.write(&mut store, 80, &update.data).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let duration = if let Some(update) = pending_updates.first() {
|
||||||
|
((update.time.wrapping_sub(current_time) as usize) * sample_rate + 999) / 1000
|
||||||
|
} else {
|
||||||
|
outer_buffer.len()
|
||||||
|
};
|
||||||
|
let step_size = (duration.max(64) * 2).min(outer_buffer.len());
|
||||||
|
|
||||||
|
let mut buffer = &mut outer_buffer[..step_size];
|
||||||
|
|
||||||
|
{
|
||||||
|
let mem = memory.data_mut(&mut store);
|
||||||
|
mem[64..68].copy_from_slice(¤t_time.to_le_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clamp_sample(s: f32) -> f32 {
|
||||||
|
if s.is_nan() {
|
||||||
|
0.0
|
||||||
|
} else {
|
||||||
|
s.max(-1.0).min(1.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref mut resampler) = resampler {
|
||||||
|
while !buffer.is_empty() {
|
||||||
|
let copy_size = resampler.output_buffers[0]
|
||||||
|
.len()
|
||||||
|
.saturating_sub(resampler.output_index)
|
||||||
|
.min(buffer.len() / 2);
|
||||||
|
if copy_size == 0 {
|
||||||
|
resampler.input_buffers[0].clear();
|
||||||
|
resampler.input_buffers[1].clear();
|
||||||
|
for _ in 0..resampler.resampler.input_frames_next() {
|
||||||
|
resampler.input_buffers[0].push(clamp_sample(
|
||||||
|
snd.call(&mut store, (sample_index,)).unwrap_or(0.0),
|
||||||
|
));
|
||||||
|
resampler.input_buffers[1].push(clamp_sample(
|
||||||
|
snd.call(&mut store, (sample_index + 1,)).unwrap_or(0.0),
|
||||||
|
));
|
||||||
|
sample_index = sample_index.wrapping_add(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
resampler
|
||||||
|
.resampler
|
||||||
|
.process_into_buffer(
|
||||||
|
&resampler.input_buffers,
|
||||||
|
&mut resampler.output_buffers,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
resampler.output_index = 0;
|
||||||
|
} else {
|
||||||
|
for i in 0..copy_size {
|
||||||
|
buffer[i * 2] = resampler.output_buffers[0][resampler.output_index + i];
|
||||||
|
buffer[i * 2 + 1] =
|
||||||
|
resampler.output_buffers[1][resampler.output_index + i];
|
||||||
|
}
|
||||||
|
resampler.output_index += copy_size;
|
||||||
|
buffer = &mut buffer[copy_size * 2..];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for v in buffer {
|
||||||
|
*v = clamp_sample(snd.call(&mut store, (sample_index,)).unwrap_or(0.0));
|
||||||
|
sample_index = sample_index.wrapping_add(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outer_buffer = &mut outer_buffer[step_size..];
|
||||||
|
current_time = current_time.wrapping_add((step_size * 500 / sample_rate).max(1) as i32);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fn f32_to_i16<F>(mut buffer: &mut [i16], callback: &mut F)
|
||||||
|
where
|
||||||
|
F: FnMut(&mut [f32]),
|
||||||
|
{
|
||||||
|
let mut float_buffer = [0f32; 256];
|
||||||
|
|
||||||
|
while !buffer.is_empty() {
|
||||||
|
let step_size = buffer.len().min(float_buffer.len());
|
||||||
|
let step_buffer = &mut float_buffer[..step_size];
|
||||||
|
callback(step_buffer);
|
||||||
|
for (dest, src) in buffer.iter_mut().take(step_size).zip(step_buffer.iter()) {
|
||||||
|
*dest = (src.max(-1.0).min(1.0) * 32767.0) as i16;
|
||||||
|
}
|
||||||
|
buffer = &mut buffer[step_size..];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stereo_to_mono<F>(mut buffer: &mut [f32], callback: &mut F)
|
||||||
|
where
|
||||||
|
F: FnMut(&mut [f32]),
|
||||||
|
{
|
||||||
|
let mut in_buffer = [0f32; 256];
|
||||||
|
|
||||||
|
while !buffer.is_empty() {
|
||||||
|
let step_size = buffer.len().min(in_buffer.len() / 2);
|
||||||
|
let step_buffer = &mut in_buffer[..step_size * 2];
|
||||||
|
callback(step_buffer);
|
||||||
|
for (index, dest) in buffer.iter_mut().take(step_size).enumerate() {
|
||||||
|
*dest = (step_buffer[index * 2] + step_buffer[index * 2 + 1]) * 0.5;
|
||||||
|
}
|
||||||
|
buffer = &mut buffer[step_size..];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stereo_to_surround<F>(mut buffer: &mut [f32], num_channels: usize, callback: &mut F)
|
||||||
|
where
|
||||||
|
F: FnMut(&mut [f32]),
|
||||||
|
{
|
||||||
|
let mut in_buffer = [0f32; 256];
|
||||||
|
buffer.fill(0.);
|
||||||
|
|
||||||
|
while !buffer.is_empty() {
|
||||||
|
let step_size = (buffer.len() / num_channels).min(in_buffer.len() / 2);
|
||||||
|
let step_buffer = &mut in_buffer[..step_size * 2];
|
||||||
|
callback(step_buffer);
|
||||||
|
for index in 0..step_size {
|
||||||
|
buffer[index * num_channels + 0] = step_buffer[index * 2 + 0];
|
||||||
|
buffer[index * num_channels + 1] = step_buffer[index * 2 + 1];
|
||||||
|
}
|
||||||
|
buffer = &mut buffer[step_size * num_channels..];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let stream = if sample_format == cpal::SampleFormat::F32 {
|
||||||
|
if num_channels == 2 {
|
||||||
|
device.build_output_stream(
|
||||||
|
&config,
|
||||||
|
move |buffer: &mut [f32], _| callback(buffer),
|
||||||
|
move |err| {
|
||||||
|
dbg!(err);
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)?
|
||||||
|
} else if num_channels == 1 {
|
||||||
|
device.build_output_stream(
|
||||||
|
&config,
|
||||||
|
move |buffer: &mut [f32], _| stereo_to_mono(buffer, &mut callback),
|
||||||
|
move |err| {
|
||||||
|
dbg!(err);
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)?
|
||||||
|
} else {
|
||||||
|
device.build_output_stream(
|
||||||
|
&config,
|
||||||
|
move |buffer: &mut [f32], _| {
|
||||||
|
stereo_to_surround(buffer, num_channels as usize, &mut callback)
|
||||||
|
},
|
||||||
|
move |err| {
|
||||||
|
dbg!(err);
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if num_channels == 2 {
|
||||||
|
device.build_output_stream(
|
||||||
|
&config,
|
||||||
|
move |buffer: &mut [i16], _| f32_to_i16(buffer, &mut callback),
|
||||||
|
move |err| {
|
||||||
|
dbg!(err);
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)?
|
||||||
|
} else if num_channels == 1 {
|
||||||
|
device.build_output_stream(
|
||||||
|
&config,
|
||||||
|
move |buffer: &mut [i16], _| {
|
||||||
|
f32_to_i16(buffer, &mut |b| stereo_to_mono(b, &mut callback))
|
||||||
|
},
|
||||||
|
move |err| {
|
||||||
|
dbg!(err);
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)?
|
||||||
|
} else {
|
||||||
|
device.build_output_stream(
|
||||||
|
&config,
|
||||||
|
move |buffer: &mut [i16], _| {
|
||||||
|
f32_to_i16(buffer, &mut |b| {
|
||||||
|
stereo_to_surround(b, num_channels as usize, &mut callback)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
move |err| {
|
||||||
|
dbg!(err);
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Uw8Sound { stream, tx })
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use std::{
|
||||||
|
net::SocketAddr,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
thread,
|
||||||
|
};
|
||||||
|
use tokio::sync::broadcast;
|
||||||
|
use tokio_stream::{wrappers::BroadcastStream, Stream, StreamExt};
|
||||||
|
use warp::{http::Response, Filter};
|
||||||
|
|
||||||
|
pub struct RunWebServer {
|
||||||
|
cart: Arc<Mutex<Vec<u8>>>,
|
||||||
|
tx: broadcast::Sender<()>,
|
||||||
|
socket_addr: SocketAddr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RunWebServer {
|
||||||
|
pub fn new() -> 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()
|
||||||
|
.enable_time()
|
||||||
|
.build()
|
||||||
|
.expect("Failed to create tokio runtime");
|
||||||
|
rt.block_on(async {
|
||||||
|
let html = warp::path::end().map(|| {
|
||||||
|
Response::builder()
|
||||||
|
.header("Content-Type", "text/html")
|
||||||
|
.body(include_str!("run-web.html"))
|
||||||
|
});
|
||||||
|
|
||||||
|
let cart = warp::path("cart")
|
||||||
|
.map(move || server_cart.lock().map_or(Vec::new(), |c| c.clone()));
|
||||||
|
|
||||||
|
let events = warp::path("events").and(warp::get()).map(move || {
|
||||||
|
fn event_stream(
|
||||||
|
tx: &broadcast::Sender<()>,
|
||||||
|
) -> impl Stream<Item = Result<warp::sse::Event, std::convert::Infallible>>
|
||||||
|
{
|
||||||
|
BroadcastStream::new(tx.subscribe())
|
||||||
|
.map(|_| Ok(warp::sse::Event::default().data("L")))
|
||||||
|
}
|
||||||
|
warp::sse::reply(warp::sse::keep_alive().stream(event_stream(&server_tx)))
|
||||||
|
});
|
||||||
|
|
||||||
|
let server_future = warp::serve(html.or(cart).or(events)).bind(server_addr);
|
||||||
|
server_future.await
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
let _ignore_result = self.tx.send(());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_open(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_frame(&mut self) -> Result<()> {
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RunWebServer {
|
||||||
|
fn default() -> RunWebServer {
|
||||||
|
RunWebServer::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,789 @@
|
|||||||
|
{
|
||||||
|
"scope": "source.curlywasm",
|
||||||
|
"completions": [
|
||||||
|
// Keywords
|
||||||
|
{
|
||||||
|
"trigger": "if",
|
||||||
|
"contents": "if ${1:condition} {\n\t${2:// Your start code here}\n}",
|
||||||
|
"details": "if (condition) { block }",
|
||||||
|
"kind": "keyword"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "else",
|
||||||
|
"contents": "else {\n\t${1:// Your start code here}\n}",
|
||||||
|
"details": "else { block }",
|
||||||
|
"kind": "keyword"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "loop",
|
||||||
|
"contents": "loop ${1:label} {\n\t${2:// Your start code here}\n}",
|
||||||
|
"details": "loop label { block }",
|
||||||
|
"kind": "keyword"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "fn",
|
||||||
|
"contents": "fn ${1:name}(${2:params}) {\n\t${3:// Your start code here}\n}",
|
||||||
|
"details": "fn name(params) [-> return_type] { block }",
|
||||||
|
"kind": "keyword"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "let",
|
||||||
|
"contents": "let ${1:variable} = ${2:value};",
|
||||||
|
"details": "let variable[: type] = value;",
|
||||||
|
"kind": "keyword"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "const",
|
||||||
|
"contents": "const ${1:name} = ${2:value};",
|
||||||
|
"details": "const name[: type] = value;",
|
||||||
|
"kind": "keyword"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "branch_if",
|
||||||
|
"contents": "branch_if ${1:condition}: ${2:label};",
|
||||||
|
"details": "branch_if (condition): label;",
|
||||||
|
"kind": "keyword"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "global",
|
||||||
|
"contents": "global mut ${1:name} = ${2:value};",
|
||||||
|
"details": "global mut name[: type] = value;",
|
||||||
|
"kind": "keyword"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "import",
|
||||||
|
"contents": "import \"${1:module.name}\" ${2:|memory(min_pages)|global var_name: type|fn fun_name(param_types) [-> return_type]};",
|
||||||
|
"details": "import \"module.name\" memory(min_pages); import \"module.name\" global var_name: type; import \"module.name\" fn fun_name(param_types) [-> return_type];",
|
||||||
|
"kind": "keyword"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "include",
|
||||||
|
"contents": "include \"${1:path}\"",
|
||||||
|
"details": "include \"path\"",
|
||||||
|
"kind": "keyword"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "api",
|
||||||
|
"contents": "include \"../include/microw8-api.cwa\"",
|
||||||
|
"details": "include \"path\"",
|
||||||
|
"kind": "keyword"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "common",
|
||||||
|
"contents": "include \"../include/common.cwa\"",
|
||||||
|
"details": "include \"path\"",
|
||||||
|
"kind": "keyword"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "export",
|
||||||
|
"contents": "export fn ${1:name}(${2:params}) ${3:-> ${4:return_type}} { ${5:block} }",
|
||||||
|
"details": "export fn name(params) [-> return_type] { block }",
|
||||||
|
"kind": "keyword"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "block",
|
||||||
|
"contents": "block ${1:label} {\n\t${2://content}\n}",
|
||||||
|
"details": "block label { block }",
|
||||||
|
"kind": "keyword"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "branch",
|
||||||
|
"contents": "branch ${1:label};",
|
||||||
|
"details": "branch label;",
|
||||||
|
"kind": "keyword"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "data",
|
||||||
|
"contents": "data ${1:address} {\n\t${2:type}(\n\t\t${3:values}\n\t)\n}",
|
||||||
|
"details": "data address { content }",
|
||||||
|
"kind": "keyword"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "mut",
|
||||||
|
"contents": "mut",
|
||||||
|
"details": "Mutable variable modifier",
|
||||||
|
"kind": "storage.modifier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "lazy",
|
||||||
|
"contents": "lazy",
|
||||||
|
"details": "Lazy variable modifier",
|
||||||
|
"kind": "storage.modifier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "inline",
|
||||||
|
"contents": "inline",
|
||||||
|
"details": "Inline variable modifier",
|
||||||
|
"kind": "storage.modifier"
|
||||||
|
},
|
||||||
|
|
||||||
|
// Operators
|
||||||
|
{
|
||||||
|
"trigger": "as",
|
||||||
|
"contents": "as",
|
||||||
|
"details": "Type cast",
|
||||||
|
"kind": "keyword.operator.type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "?",
|
||||||
|
"contents": "?",
|
||||||
|
"details": "Load byte",
|
||||||
|
"kind": "keyword.operator.memory"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "!",
|
||||||
|
"contents": "!",
|
||||||
|
"details": "Load word",
|
||||||
|
"kind": "keyword.operator.memory"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "$",
|
||||||
|
"contents": "$",
|
||||||
|
"details": "Load float",
|
||||||
|
"kind": "keyword.operator.memory"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "<|",
|
||||||
|
"contents": "<|",
|
||||||
|
"details": "Take first (sequencing operator)",
|
||||||
|
"kind": "keyword.operator.misc"
|
||||||
|
},
|
||||||
|
|
||||||
|
// Memory access intrinsic functions (i32 and f32 only)
|
||||||
|
{
|
||||||
|
"trigger": "i32.load",
|
||||||
|
"contents": "i32.load(${1:base}, ${2:offset}${3:, ${4:align}})",
|
||||||
|
"details": "i32.load(base: i32, offset: i32 = 0, align: i32 = 2) -> i32: Load 32-bit word",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "i32.load8_u",
|
||||||
|
"contents": "i32.load8_u(${1:base}, ${2:offset}${3:, ${4:align}})",
|
||||||
|
"details": "i32.load8_u(base: i32, offset: i32 = 0, align: i32 = 0) -> i32: Load 8-bit unsigned",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "i32.load8_s",
|
||||||
|
"contents": "i32.load8_s(${1:base}, ${2:offset}${3:, ${4:align}})",
|
||||||
|
"details": "i32.load8_s(base: i32, offset: i32 = 0, align: i32 = 0) -> i32: Load 8-bit signed",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "i32.load16_u",
|
||||||
|
"contents": "i32.load16_u(${1:base}, ${2:offset}${3:, ${4:align}})",
|
||||||
|
"details": "i32.load16_u(base: i32, offset: i32 = 0, align: i32 = 1) -> i32: Load 16-bit unsigned",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "i32.load16_s",
|
||||||
|
"contents": "i32.load16_s(${1:base}, ${2:offset}${3:, ${4:align}})",
|
||||||
|
"details": "i32.load16_s(base: i32, offset: i32 = 0, align: i32 = 1) -> i32: Load 16-bit signed",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "i32.store",
|
||||||
|
"contents": "i32.store(${1:value}, ${2:base}, ${3:offset}${4:, ${5:align}});",
|
||||||
|
"details": "i32.store(value: i32, base: i32, offset: i32 = 0, align: i32 = 2): Store 32-bit word",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "i32.store8",
|
||||||
|
"contents": "i32.store8(${1:value}, ${2:base}, ${3:offset}${4:, ${5:align}});",
|
||||||
|
"details": "i32.store8(value: i32, base: i32, offset: i32 = 0, align: i32 = 0): Store 8-bit",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "i32.store16",
|
||||||
|
"contents": "i32.store16(${1:value}, ${2:base}, ${3:offset}${4:, ${5:align}});",
|
||||||
|
"details": "i32.store16(value: i32, base: i32, offset: i32 = 0, align: i32 = 1): Store 16-bit",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "f32.load",
|
||||||
|
"contents": "f32.load(${1:base}, ${2:offset}${3:, ${4:align}})",
|
||||||
|
"details": "f32.load(base: i32, offset: i32 = 0, align: i32 = 2) -> f32: Load 32-bit float",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "f32.store",
|
||||||
|
"contents": "f32.store(${1:value}, ${2:base}, ${3:offset}${4:, ${5:align}});",
|
||||||
|
"details": "f32.store(value: f32, base: i32, offset: i32 = 0, align: i32 = 2): Store 32-bit float",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
|
||||||
|
// API functions
|
||||||
|
{
|
||||||
|
"trigger": "sin",
|
||||||
|
"contents": "sin(${1:x})",
|
||||||
|
"details": "sin(x: f32) -> f32: Returns the sine of x (radians)",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "cos",
|
||||||
|
"contents": "cos(${1:x})",
|
||||||
|
"details": "cos(x: f32) -> f32: Returns the cosine of x (radians)",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "tan",
|
||||||
|
"contents": "tan(${1:x})",
|
||||||
|
"details": "tan(x: f32) -> f32: Returns the tangent of x (radians)",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "asin",
|
||||||
|
"contents": "asin(${1:x})",
|
||||||
|
"details": "asin(x: f32) -> f32: Returns the arcsine of x (radians)",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "acos",
|
||||||
|
"contents": "acos(${1:x})",
|
||||||
|
"details": "acos(x: f32) -> f32: Returns the arccosine of x (radians)",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "atan",
|
||||||
|
"contents": "atan(${1:x})",
|
||||||
|
"details": "atan(x: f32) -> f32: Returns the arctangent of x (radians)",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "atan2",
|
||||||
|
"contents": "atan2(${1:y}, ${2:x})",
|
||||||
|
"details": "atan2(y: f32, x: f32) -> f32: Returns the angle in radians of the point (x, y)",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "pow",
|
||||||
|
"contents": "pow(${1:x}, ${2:y})",
|
||||||
|
"details": "pow(x: f32, y: f32) -> f32: Returns x raised to the power of y",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "log",
|
||||||
|
"contents": "log(${1:x})",
|
||||||
|
"details": "log(x: f32) -> f32: Returns the natural logarithm of x",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "fmod",
|
||||||
|
"contents": "fmod(${1:x}, ${2:y})",
|
||||||
|
"details": "fmod(x: f32, y: f32) -> f32: Returns the remainder of x/y",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "random",
|
||||||
|
"contents": "random()",
|
||||||
|
"details": "random() -> i32: Returns a random 32-bit integer",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "randomf",
|
||||||
|
"contents": "randomf()",
|
||||||
|
"details": "randomf() -> f32: Returns a random float [0, 1)",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "randomSeed",
|
||||||
|
"contents": "randomSeed(${1:seed});",
|
||||||
|
"details": "randomSeed(seed: i32): Seeds the random number generator",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "cls",
|
||||||
|
"contents": "cls(${1:color});",
|
||||||
|
"details": "cls(color: i32): Clears the screen with the given color, resets cursor to 0,0, disables graphics mode",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "setPixel",
|
||||||
|
"contents": "setPixel(${1:x}, ${2:y}, ${3:color});",
|
||||||
|
"details": "setPixel(x: i32, y: i32, color: i32): Sets the pixel at (x, y) to color",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "getPixel",
|
||||||
|
"contents": "getPixel(${1:x}, ${2:y})",
|
||||||
|
"details": "getPixel(x: i32, y: i32) -> i32: Gets the color of the pixel at (x, y) (returns 0 if out of bounds)",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "hline",
|
||||||
|
"contents": "hline(${1:left}, ${2:right}, ${3:y}, ${4:color});",
|
||||||
|
"details": "hline(left: i32, right: i32, y: i32, color: i32): Fills the horizontal line [left, right), y",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "rectangle",
|
||||||
|
"contents": "rectangle(${1:x}, ${2:y}, ${3:width}, ${4:height}, ${5:color});",
|
||||||
|
"details": "rectangle(x: f32, y: f32, w: f32, h: f32, color: i32): Fills the rectangle x,y - x+w,y+h",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "circle",
|
||||||
|
"contents": "circle(${1:cx}, ${2:cy}, ${3:radius}, ${4:color});",
|
||||||
|
"details": "circle(cx: f32, cy: f32, radius: f32, color: i32): Fills the circle at cx, cy with radius",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "line",
|
||||||
|
"contents": "line(${1:x1}, ${2:y1}, ${3:x2}, ${4:y2}, ${5:color});",
|
||||||
|
"details": "line(x1: f32, y1: f32, x2: f32, y2: f32, color: i32): Draws a line from x1,y1 to x2,y2",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "time",
|
||||||
|
"contents": "time()",
|
||||||
|
"details": "time() -> f32: Returns the current time in seconds",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "isButtonPressed",
|
||||||
|
"contents": "isButtonPressed(${1:button})",
|
||||||
|
"details": "isButtonPressed(button: i32) -> bool: Checks if a button is pressed this frame (returns i32)",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "isButtonTriggered",
|
||||||
|
"contents": "isButtonTriggered(${1:button})",
|
||||||
|
"details": "isButtonTriggered(button: i32) -> bool: Checks if a button is newly pressed this frame (returns i32)",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "printChar",
|
||||||
|
"contents": "printChar(${1:char});",
|
||||||
|
"details": "printChar(char: i32): Prints the character in the lower 8 bits",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "printString",
|
||||||
|
"contents": "printString(${1:ptr});",
|
||||||
|
"details": "printString(ptr: i32): Prints the zero-terminated string at memory address ptr",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "printInt",
|
||||||
|
"contents": "printInt(${1:value});",
|
||||||
|
"details": "printInt(value: i32): Prints an integer as a signed decimal number",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "setTextColor",
|
||||||
|
"contents": "setTextColor(${1:color});",
|
||||||
|
"details": "setTextColor(color: i32): Sets the text color",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "setBackgroundColor",
|
||||||
|
"contents": "setBackgroundColor(${1:color});",
|
||||||
|
"details": "setBackgroundColor(color: i32): Sets the background color",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "setCursorPosition",
|
||||||
|
"contents": "setCursorPosition(${1:x}, ${2:y});",
|
||||||
|
"details": "setCursorPosition(x: i32, y: i32): Sets the cursor position (character or pixel coords depending on mode)",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "rectangleOutline",
|
||||||
|
"contents": "rectangleOutline(${1:x}, ${2:y}, ${3:width}, ${4:height}, ${5:color});",
|
||||||
|
"details": "rectangleOutline(x: f32, y: f32, w: f32, h: f32, color: i32): Draws a rectangle outline",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "circleOutline",
|
||||||
|
"contents": "circleOutline(${1:cx}, ${2:cy}, ${3:radius}, ${4:color});",
|
||||||
|
"details": "circleOutline(cx: f32, cy: f32, radius: f32, color: i32): Draws a circle outline",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "exp",
|
||||||
|
"contents": "exp(${1:x})",
|
||||||
|
"details": "exp(x: f32) -> f32: Returns e raised to the power of x",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "playNote",
|
||||||
|
"contents": "playNote(${1:channel}, ${2:note}${3:, ${4:duration}${5:, ${6:volume}}});",
|
||||||
|
"details": "playNote(channel: i32, note: i32): Triggers a note on the given channel (note 0 stops, 128-255 triggers attack+release)",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "sndGes",
|
||||||
|
"contents": "sndGes(${1:sampleIndex})",
|
||||||
|
"details": "sndGes(sampleIndex: i32) -> f32: Sound generator function (uses 32 bytes at 0x50 by default)",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "blitSprite",
|
||||||
|
"contents": "blitSprite(${1:spriteData}, ${2:size}, ${3:x}, ${4:y}, ${5:control});",
|
||||||
|
"details": "blitSprite(spriteData: i32, size: i32, x: i32, y: i32, control: i32): Copies sprite data to screen",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "grabSprite",
|
||||||
|
"contents": "grabSprite(${1:spriteData}, ${2:size}, ${3:x}, ${4:y}, ${5:control});",
|
||||||
|
"details": "grabSprite(spriteData: i32, size: i32, x: i32, y: i32, control: i32): Copies screen data to sprite",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
|
||||||
|
// Built-in functions (or commonly used intrinsics)
|
||||||
|
{
|
||||||
|
"trigger": "start",
|
||||||
|
"contents": "export fn start() {\n\t${1:// Your start code here}\n}",
|
||||||
|
"details": "export fn start(): Called once after module is loaded",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "upd",
|
||||||
|
"contents": "export fn upd() {\n\t${1:// Your update code here}\n}",
|
||||||
|
"details": "export fn upd(): Called once per frame",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "memory.fill",
|
||||||
|
"contents": "memory.fill(${1:dest}, ${2:value}, ${3:size});",
|
||||||
|
"details": "memory.fill(dest: i32, value: i32, size: i32): Fills a memory area",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "memory.copy",
|
||||||
|
"contents": "memory.copy(${1:dest}, ${2:src}, ${3:size});",
|
||||||
|
"details": "memory.copy(dest: i32, src: i32, size: i32): Copies a memory block",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "sqrt",
|
||||||
|
"contents": "sqrt(${1:x})",
|
||||||
|
"details": "sqrt(x: f32) -> f32: Returns the square root of x",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "min",
|
||||||
|
"contents": "min(${1:x}, ${2:y})",
|
||||||
|
"details": "min(x: f32, y: f32) -> f32: Returns the minimum of x and y",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "max",
|
||||||
|
"contents": "max(${1:x}, ${2:y})",
|
||||||
|
"details": "max(x: f32, y: f32) -> f32: Returns the maximum of x and y",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "ceil",
|
||||||
|
"contents": "ceil(${1:x})",
|
||||||
|
"details": "ceil(x: f32) -> f32: Returns the smallest integer >= x",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "floor",
|
||||||
|
"contents": "floor(${1:x})",
|
||||||
|
"details": "floor(x: f32) -> f32: Returns the largest integer <= x",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "trunc",
|
||||||
|
"contents": "trunc(${1:x})",
|
||||||
|
"details": "trunc(x: f32) -> f32: Returns the nearest integer to x toward zero",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "nearest",
|
||||||
|
"contents": "nearest(${1:x})",
|
||||||
|
"details": "nearest(x: f32) -> f32: Returns the nearest integer to x (round to nearest, ties to even)",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "abs",
|
||||||
|
"contents": "abs(${1:x})",
|
||||||
|
"details": "abs(x: f32) -> f32: Returns the absolute value of x",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "copysign",
|
||||||
|
"contents": "copysign(${1:x}, ${2:y})",
|
||||||
|
"details": "copysign(x: f32, y: f32) -> f32: Returns x with the sign of y",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "select",
|
||||||
|
"contents": "select(${1:a}, ${2:b}, ${3:c})",
|
||||||
|
"details": "select(a: f32, b: f32, c: f32) -> f32: Selects b if a is non-zero, otherwise selects c (equivalent to a != 0 ? b : c)",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "i32.clz",
|
||||||
|
"contents": "i32.clz(${1:value})",
|
||||||
|
"details": "i32.clz(value: i32) -> i32: Count Leading Zeros",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "i32.ctz",
|
||||||
|
"contents": "i32.ctz(${1:value})",
|
||||||
|
"details": "i32.ctz(value: i32) -> i32: Count Trailing Zeros",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "i32.popcnt",
|
||||||
|
"contents": "i32.popcnt(${1:value})",
|
||||||
|
"details": "i32.popcnt(value: i32) -> i32: Population Count (number of set bits)",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "i32.bswap",
|
||||||
|
"contents": "i32.bswap(${1:value})",
|
||||||
|
"details": "i32.bswap(value: i32) -> i32: Byte Swap",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "i32.trunc_sat_f32_u",
|
||||||
|
"contents": "i32.trunc_sat_f32_u(${1:value})",
|
||||||
|
"details": "i32.trunc_sat_f32_u(value: f32) -> i32: Truncate f32 to i32 unsigned, saturating",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "i32.trunc_sat_f32_s",
|
||||||
|
"contents": "i32.trunc_sat_f32_s(${1:value})",
|
||||||
|
"details": "i32.trunc_sat_f32_s(value: f32) -> i32: Truncate f32 to i32 signed, saturating",
|
||||||
|
"kind": "support.function"
|
||||||
|
},
|
||||||
|
// Added Constants
|
||||||
|
{
|
||||||
|
"trigger": "TIME_MS",
|
||||||
|
"contents": "TIME_MS",
|
||||||
|
"details": "0x40 Time since module start in ms",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "GAMEPAD",
|
||||||
|
"contents": "GAMEPAD",
|
||||||
|
"details": "0x44 Gamepad state",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "FRAMEBUFFER",
|
||||||
|
"contents": "FRAMEBUFFER",
|
||||||
|
"details": "0x78 Frame buffer address",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "PALETTE",
|
||||||
|
"contents": "PALETTE",
|
||||||
|
"details": "0x13000 Palette address",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "FONT",
|
||||||
|
"contents": "FONT",
|
||||||
|
"details": "0x13400 Font address",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "USER_MEM",
|
||||||
|
"contents": "USER_MEM",
|
||||||
|
"details": "0x14000 Start of general user memory",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "BUTTON_UP",
|
||||||
|
"contents": "BUTTON_UP",
|
||||||
|
"details": "0x00 Gamepad button Up",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "BUTTON_DOWN",
|
||||||
|
"contents": "BUTTON_DOWN",
|
||||||
|
"details": "0x01 Gamepad button Down",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "BUTTON_LEFT",
|
||||||
|
"contents": "BUTTON_LEFT",
|
||||||
|
"details": "0x02 Gamepad button Left",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "BUTTON_RIGHT",
|
||||||
|
"contents": "BUTTON_RIGHT",
|
||||||
|
"details": "0x03 Gamepad button Right",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "BUTTON_A",
|
||||||
|
"contents": "BUTTON_A",
|
||||||
|
"details": "0x04 Gamepad button A (Z on keyboard)",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "BUTTON_B",
|
||||||
|
"contents": "BUTTON_B",
|
||||||
|
"details": "0x05 Gamepad button B (X on keyboard)",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "BUTTON_X",
|
||||||
|
"contents": "BUTTON_X",
|
||||||
|
"details": "0x06 Gamepad button X (A on keyboard)",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "BUTTON_Y",
|
||||||
|
"contents": "BUTTON_Y",
|
||||||
|
"details": "0x07 Gamepad button Y (S on keyboard)",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "PI",
|
||||||
|
"contents": "PI",
|
||||||
|
"details": "Mathematical constant π",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "RAD",
|
||||||
|
"contents": "RAD",
|
||||||
|
"details": "One radian value",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "SCR_X",
|
||||||
|
"contents": "SCR_X",
|
||||||
|
"details": "Screen width in pixels",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "SCR_Y",
|
||||||
|
"contents": "SCR_Y",
|
||||||
|
"details": "Screen height in pixels",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "SCR_W",
|
||||||
|
"contents": "SCR_W",
|
||||||
|
"details": "Screen width in chars (40)",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "SCR_H",
|
||||||
|
"contents": "SCR_H",
|
||||||
|
"details": "Screen height in chars (30)",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "CENTER_X",
|
||||||
|
"contents": "CENTER_X",
|
||||||
|
"details": "X-coordinate of the screen center (160)",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "CENTER_Y",
|
||||||
|
"contents": "CENTER_Y",
|
||||||
|
"details": "Y-coordinate of the screen center (120)",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "SCR_SIZE",
|
||||||
|
"contents": "SCR_SIZE",
|
||||||
|
"details": "Total screen size in pixels (76800)",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "MEM_END",
|
||||||
|
"contents": "MEM_END",
|
||||||
|
"details": "End of available memory",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "CUSTOM_FONT",
|
||||||
|
"contents": "CUSTOM_FONT",
|
||||||
|
"details": "Address for a custom font",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "COLOR_BLACK",
|
||||||
|
"contents": "COLOR_BLACK",
|
||||||
|
"details": "0x00 Black",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "COLOR_DARK_BLUE",
|
||||||
|
"contents": "COLOR_DARK_BLUE",
|
||||||
|
"details": "0x17 Dark Blue",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "COLOR_DARK_PURPLE",
|
||||||
|
"contents": "COLOR_DARK_PURPLE",
|
||||||
|
"details": "0x27 Dark Purple",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "COLOR_CYAN",
|
||||||
|
"contents": "COLOR_CYAN",
|
||||||
|
"details": "0x37 Cyan",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "COLOR_BRIGHT_RED",
|
||||||
|
"contents": "COLOR_BRIGHT_RED",
|
||||||
|
"details": "0x47 Bright Red",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "COLOR_MAGENTA",
|
||||||
|
"contents": "COLOR_MAGENTA",
|
||||||
|
"details": "0x57 Magenta",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "COLOR_ORANGE",
|
||||||
|
"contents": "COLOR_ORANGE",
|
||||||
|
"details": "0x58 Orange",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "COLOR_BRIGHT_YELLOW",
|
||||||
|
"contents": "COLOR_BRIGHT_YELLOW",
|
||||||
|
"details": "0x67 Bright Yellow",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "COLOR_MEDIUM_GREY",
|
||||||
|
"contents": "COLOR_MEDIUM_GREY",
|
||||||
|
"details": "0x78 Medium Grey",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "COLOR_LIGHT_GREY",
|
||||||
|
"contents": "COLOR_LIGHT_GREY",
|
||||||
|
"details": "0x7C Light Grey",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "COLOR_WHITE",
|
||||||
|
"contents": "COLOR_WHITE",
|
||||||
|
"details": "0xF8 White",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "COLOR_BRIGHT_GREEN",
|
||||||
|
"contents": "COLOR_BRIGHT_GREEN",
|
||||||
|
"details": "0x87 Bright Green",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "COLOR_BROWN",
|
||||||
|
"contents": "COLOR_BROWN",
|
||||||
|
"details": "0xD4 Brown",
|
||||||
|
"kind": "constant"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"trigger": "COLOR_DARK_GREY",
|
||||||
|
"contents": "COLOR_DARK_GREY",
|
||||||
|
"details": "0xE1 Dark Grey",
|
||||||
|
"kind": "constant"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,234 @@
|
|||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
# ==========================================================
|
||||||
|
# http://www.sublimetext.com/docs/syntax.html
|
||||||
|
# created by: zbyti & various AI
|
||||||
|
# ==========================================================
|
||||||
|
# For best results, use with Rust-themed color schemes like:
|
||||||
|
# - "RustEnhanced"
|
||||||
|
# - "Atomized"
|
||||||
|
# - "Solarized Rust"
|
||||||
|
# ==========================================================
|
||||||
|
name: CurlyWASM
|
||||||
|
file_extensions: [cwa]
|
||||||
|
scope: source.curlywasm
|
||||||
|
|
||||||
|
contexts:
|
||||||
|
main:
|
||||||
|
# Comments
|
||||||
|
- match: /\*
|
||||||
|
scope: comment.block.curlywasm
|
||||||
|
push: block_comment
|
||||||
|
- match: //
|
||||||
|
scope: comment.line.curlywasm
|
||||||
|
push: line_comment
|
||||||
|
|
||||||
|
# Module system
|
||||||
|
- match: \b(import|include|export)\b
|
||||||
|
scope: keyword.control.import.curlywasm
|
||||||
|
|
||||||
|
# Declarations and definitions
|
||||||
|
- match: \b(fn)\b
|
||||||
|
scope: keyword.declaration.function.curlywasm
|
||||||
|
|
||||||
|
- match: \b(const)\b
|
||||||
|
scope: storage.modifier.const.curlywasm
|
||||||
|
|
||||||
|
- match: \b(global|mut|let|lazy|inline)\b
|
||||||
|
scope: storage.modifier.curlywasm
|
||||||
|
|
||||||
|
# Control flow
|
||||||
|
- match: \b(if|else|block|loop|branch|branch_if)\b
|
||||||
|
scope: keyword.control.flow.curlywasm
|
||||||
|
|
||||||
|
# Type conversion
|
||||||
|
- match: \b(as)\b
|
||||||
|
scope: keyword.operator.type.curlywasm
|
||||||
|
|
||||||
|
# WASM memory access operators
|
||||||
|
- match: \b(load|store)\b
|
||||||
|
scope: keyword.operator.memory.curlywasm
|
||||||
|
|
||||||
|
# API functions
|
||||||
|
- match: \b(sin|cos|tan|asin|acos|atan|atan2|pow|log|fmod|random|randomf|randomSeed|cls|setPixel|getPixel|hline|rectangle|circle|line|time|isButtonPressed|isButtonTriggered|printChar|printString|printInt|setTextColor|setBackgroundColor|setCursorPosition|rectangleOutline|circleOutline|exp|playNote|sndGes|blitSprite|grabSprite)\b
|
||||||
|
scope: support.function.curlywasm
|
||||||
|
|
||||||
|
# Built-in functions
|
||||||
|
- match: \b(start|upd|sqrt|min|max|ceil|floor|trunc|nearest|abs|copysign|select)\b
|
||||||
|
scope: support.function.curlywasm
|
||||||
|
|
||||||
|
# Data blocks - Match 'data {' together and push context
|
||||||
|
- match: \b(data)\s*(\{)
|
||||||
|
captures:
|
||||||
|
1: storage.type.data.curlywasm
|
||||||
|
2: punctuation.section.block.begin.curlywasm
|
||||||
|
push: data_content
|
||||||
|
|
||||||
|
# Base types
|
||||||
|
- match: \b(i8|i16|i32|i64|f32|f64)\b
|
||||||
|
scope: storage.type.primitive.curlywasm
|
||||||
|
|
||||||
|
# Memory access operators
|
||||||
|
- match: (\?|\$|\!)
|
||||||
|
scope: keyword.operator.memory.curlywasm
|
||||||
|
|
||||||
|
# Operators
|
||||||
|
- match: (->)
|
||||||
|
scope: keyword.operator.arrow.curlywasm
|
||||||
|
|
||||||
|
# Assignment operators
|
||||||
|
- match: (=|:=|\+=|-=|\*=|/=|%=|&=|\|=|\^=|#/=)
|
||||||
|
scope: keyword.operator.assignment.curlywasm
|
||||||
|
|
||||||
|
# Arithmetic operators
|
||||||
|
- match: (\+|-|\*|/|%|#/|#%)
|
||||||
|
scope: keyword.operator.arithmetic.curlywasm
|
||||||
|
|
||||||
|
# Bitwise operators
|
||||||
|
- match: (\&|\||\^|<<|>>|#>>)
|
||||||
|
scope: keyword.operator.bitwise.curlywasm
|
||||||
|
|
||||||
|
# Comparison operators
|
||||||
|
- match: (<|>|<=|>=|#<|#<=|#>|#>=|==|!=)
|
||||||
|
scope: keyword.operator.comparison.curlywasm
|
||||||
|
|
||||||
|
# Other operators
|
||||||
|
- match: (<\|)
|
||||||
|
scope: keyword.operator.misc.curlywasm
|
||||||
|
|
||||||
|
# Numeric literals
|
||||||
|
- match: \b(0x[0-9a-fA-F]+)\b
|
||||||
|
scope: constant.numeric.hex.curlywasm
|
||||||
|
|
||||||
|
- match: '\b\d+(_f)\b'
|
||||||
|
scope: constant.numeric.float.curlywasm
|
||||||
|
|
||||||
|
- match: \b0x[0-9a-fA-F]+_f\b
|
||||||
|
scope: constant.numeric.float.curlywasm
|
||||||
|
|
||||||
|
- match: \b([0-9]+\.[0-9]+)\b
|
||||||
|
scope: constant.numeric.float.curlywasm
|
||||||
|
|
||||||
|
- match: \b([0-9]+)\b
|
||||||
|
scope: constant.numeric.integer.curlywasm
|
||||||
|
|
||||||
|
# String literals
|
||||||
|
- match: \"
|
||||||
|
scope: punctuation.definition.string.begin.curlywasm
|
||||||
|
push: double_quoted_string
|
||||||
|
- match: \'
|
||||||
|
scope: punctuation.definition.string.begin.curlywasm
|
||||||
|
push: single_quoted_string
|
||||||
|
|
||||||
|
# Function calls
|
||||||
|
- match: \b([a-zA-Z_][a-zA-Z0-9_]*)\s*\(
|
||||||
|
captures:
|
||||||
|
1: entity.name.function.call.curlywasm
|
||||||
|
|
||||||
|
# Function declarations
|
||||||
|
- match: \bfn\s+([a-zA-Z_][a-zA-Z0-9_]*)\b
|
||||||
|
captures:
|
||||||
|
1: entity.name.function.declaration.curlywasm
|
||||||
|
|
||||||
|
# Constants (Upper case convention)
|
||||||
|
- match: \b([A-Z_][A-Z0-9_]*)\b
|
||||||
|
scope: constant.other.curlywasm
|
||||||
|
|
||||||
|
# Variables (Lower case convention)
|
||||||
|
- match: \b([a-z_][a-zA-Z0-9_]*)\b
|
||||||
|
scope: variable.other.curlywasm
|
||||||
|
|
||||||
|
# Punctuation
|
||||||
|
- match: \{
|
||||||
|
scope: punctuation.section.block.begin.curlywasm
|
||||||
|
- match: \}
|
||||||
|
scope: punctuation.section.block.end.curlywasm
|
||||||
|
- match: \(
|
||||||
|
scope: punctuation.section.group.begin.curlywasm
|
||||||
|
- match: \)
|
||||||
|
scope: punctuation.section.group.end.curlywasm
|
||||||
|
- match: \[
|
||||||
|
scope: punctuation.section.brackets.begin.curlywasm
|
||||||
|
- match: \]
|
||||||
|
scope: punctuation.section.brackets.end.curlywasm
|
||||||
|
- match: ;
|
||||||
|
scope: punctuation.terminator.curlywasm
|
||||||
|
- match: \,
|
||||||
|
scope: punctuation.separator.curlywasm
|
||||||
|
- match: ':'
|
||||||
|
scope: punctuation.separator.type.curlywasm
|
||||||
|
|
||||||
|
# Context for /* ... */ block comments
|
||||||
|
block_comment:
|
||||||
|
- meta_scope: comment.block.curlywasm
|
||||||
|
- match: \*/
|
||||||
|
scope: punctuation.definition.comment.end.curlywasm
|
||||||
|
pop: true
|
||||||
|
|
||||||
|
# Context for // ... line comments
|
||||||
|
line_comment:
|
||||||
|
- meta_scope: comment.line.double-slash.curlywasm
|
||||||
|
- match: $ # Pop at the end of the line
|
||||||
|
pop: true
|
||||||
|
|
||||||
|
# Context for "..." strings
|
||||||
|
double_quoted_string:
|
||||||
|
- meta_scope: string.quoted.double.curlywasm
|
||||||
|
- match: \"
|
||||||
|
scope: punctuation.definition.string.end.curlywasm
|
||||||
|
pop: true
|
||||||
|
- match: \\. # Escape sequences
|
||||||
|
scope: constant.character.escape.curlywasm
|
||||||
|
|
||||||
|
# Context for '...' strings
|
||||||
|
single_quoted_string:
|
||||||
|
- meta_scope: string.quoted.single.curlywasm
|
||||||
|
- match: \'
|
||||||
|
scope: punctuation.definition.string.end.curlywasm
|
||||||
|
pop: true
|
||||||
|
- match: \\. # Escape sequences
|
||||||
|
scope: constant.character.escape.curlywasm
|
||||||
|
|
||||||
|
# Context for the content inside data { ... }
|
||||||
|
data_content:
|
||||||
|
- meta_scope: meta.data.content.curlywasm
|
||||||
|
# Match the closing brace to pop the context
|
||||||
|
- match: \}
|
||||||
|
scope: punctuation.section.block.end.curlywasm
|
||||||
|
pop: true
|
||||||
|
# Include rules for literals within the data block
|
||||||
|
- include: literals
|
||||||
|
# Specific types/keywords allowed inside data blocks
|
||||||
|
- match: \b(i8|i16|i32|i64|f32|f64)\b
|
||||||
|
scope: storage.type.primitive.curlywasm
|
||||||
|
- match: \b(file)\b
|
||||||
|
scope: keyword.control.curlywasm
|
||||||
|
# Punctuation inside data blocks
|
||||||
|
- match: \(
|
||||||
|
scope: punctuation.section.group.begin.curlywasm
|
||||||
|
- match: \)
|
||||||
|
scope: punctuation.section.group.end.curlywasm
|
||||||
|
- match: \,
|
||||||
|
scope: punctuation.separator.curlywasm
|
||||||
|
# Potentially allow comments inside data blocks
|
||||||
|
- include: block_comment
|
||||||
|
- include: line_comment
|
||||||
|
|
||||||
|
# Reusable patterns for literals (used via include)
|
||||||
|
literals:
|
||||||
|
# Numeric literals
|
||||||
|
- match: \b(0x[0-9a-fA-F]+)\b
|
||||||
|
scope: constant.numeric.hex.curlywasm
|
||||||
|
- match: '\b\d+(_f)\b'
|
||||||
|
scope: constant.numeric.float.curlywasm
|
||||||
|
- match: \b([0-9]+\.[0-9]+)\b
|
||||||
|
scope: constant.numeric.float.curlywasm
|
||||||
|
- match: \b([0-9]+)\b
|
||||||
|
scope: constant.numeric.integer.curlywasm
|
||||||
|
# String literals
|
||||||
|
- match: \"
|
||||||
|
scope: punctuation.definition.string.begin.curlywasm
|
||||||
|
push: double_quoted_string
|
||||||
|
- match: \'
|
||||||
|
scope: punctuation.definition.string.begin.curlywasm
|
||||||
|
push: single_quoted_string
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<snippet>
|
||||||
|
<content><![CDATA[
|
||||||
|
data ${1:address} {
|
||||||
|
${2:type}(
|
||||||
|
${3:values}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]]></content>
|
||||||
|
<tabTrigger>datatype</tabTrigger>
|
||||||
|
<description>Data block definition</description>
|
||||||
|
</snippet>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<snippet>
|
||||||
|
<content><![CDATA[
|
||||||
|
export fn ${1:name}(${2:params}) -> ${3:return_type} {
|
||||||
|
${4:// Function body}
|
||||||
|
}
|
||||||
|
]]></content>
|
||||||
|
<tabTrigger>fnexport</tabTrigger>
|
||||||
|
<description>Exported function definition</description>
|
||||||
|
</snippet>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<snippet>
|
||||||
|
<content><![CDATA[
|
||||||
|
if ${1:condition} {
|
||||||
|
${2:// Your if code here}
|
||||||
|
} else {
|
||||||
|
${3:// Your else code here}
|
||||||
|
}
|
||||||
|
]]></content>
|
||||||
|
<tabTrigger>ifelse</tabTrigger>
|
||||||
|
<description>If-else statement</description>
|
||||||
|
</snippet>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<snippet>
|
||||||
|
<content><![CDATA[
|
||||||
|
let inline ${1:variable} = ${2:value};
|
||||||
|
]]></content>
|
||||||
|
<tabTrigger>letinline</tabTrigger>
|
||||||
|
<description>Lazy variable declaration</description>
|
||||||
|
</snippet>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<snippet>
|
||||||
|
<content><![CDATA[
|
||||||
|
let lazy ${1:variable} = ${2:value};
|
||||||
|
]]></content>
|
||||||
|
<tabTrigger>letlazy</tabTrigger>
|
||||||
|
<description>Lazy variable declaration</description>
|
||||||
|
</snippet>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<snippet>
|
||||||
|
<content><![CDATA[
|
||||||
|
${1:label} = 0;
|
||||||
|
loop ${1:label} {
|
||||||
|
${2:// Your loop code here}
|
||||||
|
branch_if ${3:condition}: ${1:label};
|
||||||
|
}
|
||||||
|
]]></content>
|
||||||
|
<tabTrigger>loopbr</tabTrigger>
|
||||||
|
<description>Loop with branch_if</description>
|
||||||
|
</snippet>
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
include "../examples/include/microw8-api.cwa"
|
||||||
|
|
||||||
|
global mut counter = 0;
|
||||||
|
|
||||||
|
export fn upd() {
|
||||||
|
cls(0);
|
||||||
|
|
||||||
|
let col: i32 = 1;
|
||||||
|
|
||||||
|
loop colors {
|
||||||
|
if !testCircle(counter, col) {
|
||||||
|
printInt(counter);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
counter += 1;
|
||||||
|
branch_if (col +:= 1) < 256: colors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn testCircle(seed: i32, col: i32) -> i32 {
|
||||||
|
randomSeed(seed);
|
||||||
|
let cx = randomf() * 640_f - 160_f;
|
||||||
|
let cy = randomf() * 480_f - 120_f;
|
||||||
|
let radius = randomf() * 4_f;
|
||||||
|
radius *= radius;
|
||||||
|
radius *= radius;
|
||||||
|
|
||||||
|
circle(cx, cy, radius, col);
|
||||||
|
|
||||||
|
let min_x = max(0_f, floor(cx - radius - 1_f)) as i32;
|
||||||
|
let min_y = max(0_f, floor(cy - radius - 1_f)) as i32;
|
||||||
|
let max_x = min(320_f, ceil(cx + radius + 1_f)) as i32;
|
||||||
|
let max_y = min(240_f, ceil(cy + radius + 1_f)) as i32;
|
||||||
|
|
||||||
|
let x = min_x;
|
||||||
|
loop xloop {
|
||||||
|
if x < max_x {
|
||||||
|
let y = min_y;
|
||||||
|
loop yloop {
|
||||||
|
if y < max_y {
|
||||||
|
let rx = x as f32 + 0.5 - cx;
|
||||||
|
let ry = y as f32 + 0.5 - cy;
|
||||||
|
let d = sqrt(rx*rx + ry*ry) - radius;
|
||||||
|
if abs(d) > 0.001 {
|
||||||
|
let is_inside = d < 0_f;
|
||||||
|
let is_plotted = getPixel(x, y) == col;
|
||||||
|
if is_inside != is_plotted {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
y += 1;
|
||||||
|
branch yloop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x += 1;
|
||||||
|
branch xloop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
1
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
include "../examples/include/microw8-api.cwa"
|
||||||
|
|
||||||
|
global mut pos = 0;
|
||||||
|
global mut next = 0;
|
||||||
|
|
||||||
|
export fn upd() {
|
||||||
|
let lazy t = 32!32;
|
||||||
|
let lazy tick = t * 6 / 100;
|
||||||
|
let lazy rel = t - tick * 100 / 6;
|
||||||
|
|
||||||
|
setBackgroundColor(select(tick == next, 0, select(tick < next, 0x35, 0x55)));
|
||||||
|
setCursorPosition(pos % 13 * 3, pos / 13 % 30);
|
||||||
|
if rel < 10 {
|
||||||
|
printChar(32);
|
||||||
|
}
|
||||||
|
printInt(rel);
|
||||||
|
|
||||||
|
pos = pos + 1;
|
||||||
|
next = tick + 1;
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import "env.memory" memory(4);
|
||||||
|
import "env.pow" fn pow(f32, f32) -> f32;
|
||||||
|
import "env.sin" fn sin(f32) -> f32;
|
||||||
|
import "env.cls" fn cls(i32);
|
||||||
|
import "env.exp" fn exp(f32) -> f32;
|
||||||
|
import "env.rectangle" fn rectangle(f32, f32, f32, f32, i32);
|
||||||
|
|
||||||
|
include "../platform/src/ges.cwa"
|
||||||
|
|
||||||
|
export fn snd(t: i32) -> f32 {
|
||||||
|
sndGes(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn upd() {
|
||||||
|
80?0 = 32!32 / 200 & 2 | 0x41;
|
||||||
|
80?3 = (32!32 / 400)%8*12/7 + 40;
|
||||||
|
let pulse = (32!32 * 256 / 2000) & 511;
|
||||||
|
if pulse >= 256 {
|
||||||
|
pulse = 511 - pulse;
|
||||||
|
}
|
||||||
|
80?1 = pulse;
|
||||||
|
|
||||||
|
cls(0);
|
||||||
|
rectangle(0.0, 100.0, (pulse * 320 / 256) as f32, 16.0, 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
data 80 {
|
||||||
|
i8(
|
||||||
|
0x41, 0, 0, 80, 0x70, 0
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
include "../examples/include/microw8-api.cwa"
|
||||||
|
|
||||||
|
export fn upd() {
|
||||||
|
printChar('\06f: ');
|
||||||
|
printInt(32!32 * 6 / 100);
|
||||||
|
printChar('\n\4');
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
include "../examples/include/microw8-api.cwa"
|
||||||
|
|
||||||
|
export fn upd() {
|
||||||
|
80?0 = (32!32 >> 11 << 6) | 5;
|
||||||
|
80?1 = (sin(time() * 6 as f32) * 95 as f32) as i32 + 128;
|
||||||
|
plotGes();
|
||||||
|
}
|
||||||
|
|
||||||
|
data 80 { i8 (
|
||||||
|
1, 128, 0, 69, 0, 15,
|
||||||
|
0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0,
|
||||||
|
0xff, 0xff,
|
||||||
|
0xc1, 0, 0, 110, 0, 0
|
||||||
|
) }
|
||||||
|
|
||||||
|
//import "env.gesSnd" fn gesSnd(i32) -> f32;
|
||||||
|
|
||||||
|
include "../platform/src/ges.cwa"
|
||||||
|
|
||||||
|
export fn snd(t: i32) -> f32 {
|
||||||
|
gesSnd(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
global mut samplePos: i32 = 0;
|
||||||
|
|
||||||
|
const SoundBuffer = 0x30000;
|
||||||
|
|
||||||
|
fn plotGes() {
|
||||||
|
rectangle(0 as f32, 10 as f32, 320 as f32, 320 as f32, 0);
|
||||||
|
let count = (time() * 44100 as f32) as i32 * 2 - samplePos;
|
||||||
|
let i: i32;
|
||||||
|
loop genLoop {
|
||||||
|
(i*4)$SoundBuffer = gesSnd(samplePos + i);
|
||||||
|
branch_if (i := i + 1) < count: genLoop;
|
||||||
|
}
|
||||||
|
samplePos = samplePos + count;
|
||||||
|
|
||||||
|
let ch: i32;
|
||||||
|
loop channelLoop {
|
||||||
|
let offset = 159;
|
||||||
|
i = 0;
|
||||||
|
|
||||||
|
loop searchLoop {
|
||||||
|
offset = offset + 1;
|
||||||
|
branch_if (offset * 8 + ch - 8)$SoundBuffer < 0 as f32 | (offset * 8 + ch)$SoundBuffer >= 0 as f32 & offset + 160 < count: searchLoop;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = ch + (offset - 160) * 8;
|
||||||
|
i = 0;
|
||||||
|
loop plotLoop {
|
||||||
|
setPixel(i, floor((i * 8 + offset)$SoundBuffer * 127 as f32) as i32 + 60 + ch * (120/8), 15);
|
||||||
|
branch_if (i := i + 1) < 320: plotLoop;
|
||||||
|
}
|
||||||
|
|
||||||
|
branch_if (ch := ch + 8) < 16: channelLoop;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
include "../examples/include/microw8-api.cwa"
|
||||||
|
|
||||||
|
export fn start() {
|
||||||
|
printChar('Test');
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
Generated
+167
-79
@@ -4,21 +4,27 @@ version = 3
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.52"
|
version = "1.0.81"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3"
|
checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.0.1"
|
version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.72"
|
version = "1.0.92"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
|
checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cdivsufsort"
|
name = "cdivsufsort"
|
||||||
@@ -30,32 +36,56 @@ dependencies = [
|
|||||||
"sacabase",
|
"sacabase",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg-if"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-channel"
|
name = "crossbeam-channel"
|
||||||
version = "0.5.1"
|
version = "0.5.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
|
checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.5"
|
version = "0.8.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
|
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fallible-iterator"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gimli"
|
||||||
|
version = "0.26.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"fallible-iterator",
|
||||||
"lazy_static",
|
"indexmap 1.9.3",
|
||||||
|
"stable_deref_trait",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
@@ -72,10 +102,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
|
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "indexmap"
|
||||||
version = "1.4.0"
|
version = "1.9.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"hashbrown 0.12.3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown 0.14.3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "leb128"
|
name = "leb128"
|
||||||
@@ -84,61 +128,63 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
|
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "lexopt"
|
||||||
version = "0.2.112"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
|
checksum = "478ee9e62aaeaf5b140bd4138753d1f109765488581444218d3ddda43234f3e8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.153"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.14"
|
version = "0.4.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.14"
|
version = "0.2.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pbr"
|
name = "pbr"
|
||||||
version = "1.0.4"
|
version = "1.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ff5751d87f7c00ae6403eb1fcbba229b9c76c9a30de8c1cf87182177b168cea2"
|
checksum = "ed5827dfa0d69b6c92493d6c38e633bbaa5937c153d0d7c28bf12313f8c6d514"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"libc",
|
"libc",
|
||||||
"time",
|
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pico-args"
|
name = "pico-args"
|
||||||
version = "0.4.2"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468"
|
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.36"
|
version = "1.0.79"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
|
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-xid",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.14"
|
version = "1.0.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
|
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
@@ -153,48 +199,80 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "semver"
|
||||||
version = "1.0.84"
|
version = "1.0.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ecb2e6da8ee5eb9a61068762a32fa9619cc591ceb055b3687f4cd4051ec2e06b"
|
checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stable_deref_trait"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.109"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"unicode-xid",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "syn"
|
||||||
version = "0.1.44"
|
version = "2.0.58"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"proc-macro2",
|
||||||
"wasi",
|
"quote",
|
||||||
"winapi",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.58"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.58"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.58",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-segmentation"
|
name = "unicode-segmentation"
|
||||||
version = "1.8.0"
|
version = "1.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
|
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-xid"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "upkr"
|
name = "upkr"
|
||||||
version = "0.1.0"
|
version = "0.2.1"
|
||||||
source = "git+https://github.com/exoticorn/upkr.git?rev=2e7983fc#2e7983fc650788d98da2eecef2d16f63e849e4a0"
|
source = "git+https://github.com/exoticorn/upkr.git?rev=080db40d0088bbee2bdf3c5c75288ac7853d6b7a#080db40d0088bbee2bdf3c5c75288ac7853d6b7a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cdivsufsort",
|
"cdivsufsort",
|
||||||
"pbr",
|
"lexopt",
|
||||||
"pico-args",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -206,22 +284,24 @@ dependencies = [
|
|||||||
"pico-args",
|
"pico-args",
|
||||||
"upkr",
|
"upkr",
|
||||||
"walrus",
|
"walrus",
|
||||||
"wasm-encoder",
|
"wasm-encoder 0.201.0",
|
||||||
"wasmparser 0.81.0",
|
"wasmparser 0.201.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "walrus"
|
name = "walrus"
|
||||||
version = "0.19.0"
|
version = "0.20.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4eb08e48cde54c05f363d984bb54ce374f49e242def9468d2e1b6c2372d291f8"
|
checksum = "2c03529cd0c4400a2449f640d2f27cd1b48c3065226d15e26d98e4429ab0adb7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"gimli",
|
||||||
"id-arena",
|
"id-arena",
|
||||||
"leb128",
|
"leb128",
|
||||||
"log",
|
"log",
|
||||||
"walrus-macro",
|
"walrus-macro",
|
||||||
"wasmparser 0.77.0",
|
"wasm-encoder 0.29.0",
|
||||||
|
"wasmparser 0.80.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -233,35 +313,43 @@ dependencies = [
|
|||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasm-encoder"
|
||||||
version = "0.10.0+wasi-snapshot-preview1"
|
version = "0.29.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
checksum = "18c41dbd92eaebf3612a39be316540b8377c871cb9bde6b064af962984912881"
|
||||||
|
dependencies = [
|
||||||
|
"leb128",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-encoder"
|
name = "wasm-encoder"
|
||||||
version = "0.8.0"
|
version = "0.201.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "db0c351632e46cc06a58a696a6c11e4cf90cad4b9f8f07a0b59128d616c29bb0"
|
checksum = "b9c7d2731df60006819b013f64ccc2019691deccf6e11a1804bc850cd6748f1a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"leb128",
|
"leb128",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasmparser"
|
name = "wasmparser"
|
||||||
version = "0.77.0"
|
version = "0.80.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b35c86d22e720a07d954ebbed772d01180501afe7d03d464f413bb5f8914a8d6"
|
checksum = "449167e2832691a1bff24cde28d2804e90e09586a448c8e76984792c44334a6b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasmparser"
|
name = "wasmparser"
|
||||||
version = "0.81.0"
|
version = "0.201.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "98930446519f63d00a836efdc22f67766ceae8dbcc1571379f2bcabc6b2b9abc"
|
checksum = "84e5df6dba6c0d7fafc63a450f1738451ed7a0b52295d83e868218fa286bf708"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"indexmap 2.2.6",
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
|
|||||||
+6
-6
@@ -6,10 +6,10 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
wasmparser = "0.81"
|
wasmparser = "0.201"
|
||||||
wasm-encoder = "0.8"
|
wasm-encoder = "0.201"
|
||||||
walrus = "0.19"
|
walrus = { version = "0.20.3", default-features = false }
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
pico-args = "0.4"
|
pico-args = "0.5"
|
||||||
upkr = { git = "https://github.com/exoticorn/upkr.git", rev = "2e7983fc" }
|
upkr = { git = "https://github.com/exoticorn/upkr.git", rev = "080db40d0088bbee2bdf3c5c75288ac7853d6b7a" }
|
||||||
pbr = "1"
|
pbr = "1"
|
||||||
|
|||||||
+204
-19
@@ -3,7 +3,7 @@ use std::{collections::HashMap, fs::File, path::Path};
|
|||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use wasm_encoder::{
|
use wasm_encoder::{
|
||||||
CodeSection, EntityType, Export, ExportSection, Function, FunctionSection, ImportSection,
|
CodeSection, EntityType, ExportKind, ExportSection, Function, FunctionSection, ImportSection,
|
||||||
Instruction, MemoryType, Module, TypeSection, ValType,
|
Instruction, MemoryType, Module, TypeSection, ValType,
|
||||||
};
|
};
|
||||||
use ValType::*;
|
use ValType::*;
|
||||||
@@ -37,7 +37,7 @@ impl BaseModule {
|
|||||||
|
|
||||||
let mut types = vec![];
|
let mut types = vec![];
|
||||||
let mut type_map = HashMap::new();
|
let mut type_map = HashMap::new();
|
||||||
for num_params in 0..6 {
|
for num_params in 0..8 {
|
||||||
for num_f32 in 0..=num_params {
|
for num_f32 in 0..=num_params {
|
||||||
for &result in &[None, Some(ValType::I32), Some(ValType::F32)] {
|
for &result in &[None, Some(ValType::I32), Some(ValType::F32)] {
|
||||||
let mut params = vec![];
|
let mut params = vec![];
|
||||||
@@ -71,26 +71,118 @@ impl BaseModule {
|
|||||||
add_function(&mut functions, &type_map, "randomSeed", &[I32], None);
|
add_function(&mut functions, &type_map, "randomSeed", &[I32], None);
|
||||||
|
|
||||||
add_function(&mut functions, &type_map, "cls", &[I32], None);
|
add_function(&mut functions, &type_map, "cls", &[I32], None);
|
||||||
add_function(&mut functions, &type_map, "setPixel", &[I32, I32, I32], None);
|
add_function(
|
||||||
add_function(&mut functions, &type_map, "getPixel", &[I32, I32], Some(I32));
|
&mut functions,
|
||||||
add_function(&mut functions, &type_map, "hline", &[I32, I32, I32, I32], None);
|
&type_map,
|
||||||
add_function(&mut functions, &type_map, "rectangle", &[F32, F32, F32, F32, I32], None);
|
"setPixel",
|
||||||
add_function(&mut functions, &type_map, "circle", &[F32, F32, F32, I32], None);
|
&[I32, I32, I32],
|
||||||
add_function(&mut functions, &type_map, "line", &[F32, F32, F32, F32, I32], None);
|
None,
|
||||||
|
);
|
||||||
|
add_function(
|
||||||
|
&mut functions,
|
||||||
|
&type_map,
|
||||||
|
"getPixel",
|
||||||
|
&[I32, I32],
|
||||||
|
Some(I32),
|
||||||
|
);
|
||||||
|
add_function(
|
||||||
|
&mut functions,
|
||||||
|
&type_map,
|
||||||
|
"hline",
|
||||||
|
&[I32, I32, I32, I32],
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
add_function(
|
||||||
|
&mut functions,
|
||||||
|
&type_map,
|
||||||
|
"rectangle",
|
||||||
|
&[F32, F32, F32, F32, I32],
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
add_function(
|
||||||
|
&mut functions,
|
||||||
|
&type_map,
|
||||||
|
"circle",
|
||||||
|
&[F32, F32, F32, I32],
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
add_function(
|
||||||
|
&mut functions,
|
||||||
|
&type_map,
|
||||||
|
"line",
|
||||||
|
&[F32, F32, F32, F32, I32],
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
add_function(&mut functions, &type_map, "time", &[], Some(F32));
|
add_function(&mut functions, &type_map, "time", &[], Some(F32));
|
||||||
add_function(&mut functions, &type_map, "isButtonPressed", &[I32], Some(I32));
|
add_function(
|
||||||
add_function(&mut functions, &type_map, "isButtonTriggered", &[I32], Some(I32));
|
&mut functions,
|
||||||
|
&type_map,
|
||||||
|
"isButtonPressed",
|
||||||
|
&[I32],
|
||||||
|
Some(I32),
|
||||||
|
);
|
||||||
|
add_function(
|
||||||
|
&mut functions,
|
||||||
|
&type_map,
|
||||||
|
"isButtonTriggered",
|
||||||
|
&[I32],
|
||||||
|
Some(I32),
|
||||||
|
);
|
||||||
|
|
||||||
add_function(&mut functions, &type_map, "printChar", &[I32], None);
|
add_function(&mut functions, &type_map, "printChar", &[I32], None);
|
||||||
add_function(&mut functions, &type_map, "printString", &[I32], None);
|
add_function(&mut functions, &type_map, "printString", &[I32], None);
|
||||||
add_function(&mut functions, &type_map, "printInt", &[I32], None);
|
add_function(&mut functions, &type_map, "printInt", &[I32], None);
|
||||||
add_function(&mut functions, &type_map, "setTextColor", &[I32], None);
|
add_function(&mut functions, &type_map, "setTextColor", &[I32], None);
|
||||||
add_function(&mut functions, &type_map, "setBackgroundColor", &[I32], None);
|
add_function(
|
||||||
add_function(&mut functions, &type_map, "setCursorPosition", &[I32, I32], None);
|
&mut functions,
|
||||||
|
&type_map,
|
||||||
|
"setBackgroundColor",
|
||||||
|
&[I32],
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
add_function(
|
||||||
|
&mut functions,
|
||||||
|
&type_map,
|
||||||
|
"setCursorPosition",
|
||||||
|
&[I32, I32],
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
add_function(&mut functions, &type_map, "rectangle_outline", &[F32, F32, F32, F32, I32], None);
|
add_function(
|
||||||
add_function(&mut functions, &type_map, "circle_outline", &[F32, F32, F32, I32], None);
|
&mut functions,
|
||||||
|
&type_map,
|
||||||
|
"rectangleOutline",
|
||||||
|
&[F32, F32, F32, F32, I32],
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
add_function(
|
||||||
|
&mut functions,
|
||||||
|
&type_map,
|
||||||
|
"circleOutline",
|
||||||
|
&[F32, F32, F32, I32],
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
add_function(&mut functions, &type_map, "exp", &[F32], Some(F32));
|
||||||
|
|
||||||
|
add_function(&mut functions, &type_map, "playNote", &[I32, I32], None);
|
||||||
|
add_function(&mut functions, &type_map, "sndGes", &[I32], Some(F32));
|
||||||
|
|
||||||
|
add_function(
|
||||||
|
&mut functions,
|
||||||
|
&type_map,
|
||||||
|
"blitSprite",
|
||||||
|
&[I32, I32, I32, I32, I32],
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
add_function(
|
||||||
|
&mut functions,
|
||||||
|
&type_map,
|
||||||
|
"grabSprite",
|
||||||
|
&[I32, I32, I32, I32, I32],
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
for i in functions.len()..64 {
|
for i in functions.len()..64 {
|
||||||
add_function(
|
add_function(
|
||||||
@@ -141,13 +233,13 @@ impl BaseModule {
|
|||||||
let mut imports = ImportSection::new();
|
let mut imports = ImportSection::new();
|
||||||
|
|
||||||
for (module, name, type_) in &self.function_imports {
|
for (module, name, type_) in &self.function_imports {
|
||||||
imports.import(*module, Some(name.as_str()), EntityType::Function(*type_));
|
imports.import(*module, name.as_str(), EntityType::Function(*type_));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (module, name, import) in &self.global_imports {
|
for (module, name, import) in &self.global_imports {
|
||||||
imports.import(
|
imports.import(
|
||||||
*module,
|
*module,
|
||||||
Some(name.as_str()),
|
name.as_str(),
|
||||||
EntityType::Global(wasm_encoder::GlobalType {
|
EntityType::Global(wasm_encoder::GlobalType {
|
||||||
val_type: import.type_,
|
val_type: import.type_,
|
||||||
mutable: import.mutable,
|
mutable: import.mutable,
|
||||||
@@ -157,11 +249,12 @@ impl BaseModule {
|
|||||||
|
|
||||||
imports.import(
|
imports.import(
|
||||||
"env",
|
"env",
|
||||||
Some("memory"),
|
"memory",
|
||||||
MemoryType {
|
MemoryType {
|
||||||
minimum: self.memory as u64,
|
minimum: self.memory as u64,
|
||||||
maximum: None,
|
maximum: None,
|
||||||
memory64: false,
|
memory64: false,
|
||||||
|
shared: false,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -182,7 +275,7 @@ impl BaseModule {
|
|||||||
let mut exports = ExportSection::new();
|
let mut exports = ExportSection::new();
|
||||||
|
|
||||||
for (name, fnc) in &self.exports {
|
for (name, fnc) in &self.exports {
|
||||||
exports.export(*name, Export::Function(*fnc));
|
exports.export(*name, ExportKind::Func, *fnc);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.section(&exports);
|
module.section(&exports);
|
||||||
@@ -210,10 +303,75 @@ impl BaseModule {
|
|||||||
|
|
||||||
pub fn create_binary(path: &Path) -> Result<()> {
|
pub fn create_binary(path: &Path) -> Result<()> {
|
||||||
let base1 = BaseModule::for_format_version(1)?.to_wasm();
|
let base1 = BaseModule::for_format_version(1)?.to_wasm();
|
||||||
let data = upkr::pack(&base1, 4, None);
|
let data = upkr::pack(&base1, 4, &upkr::Config::default(), None);
|
||||||
File::create(path)?.write_all(&data)?;
|
File::create(path)?.write_all(&data)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write_as_cwa<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
||||||
|
fn inner(mut file: File, base: &BaseModule) -> Result<()> {
|
||||||
|
writeln!(
|
||||||
|
file,
|
||||||
|
"// MicroW8 APIs, to be `include`d in CurlyWas sources"
|
||||||
|
)?;
|
||||||
|
writeln!(file, "import \"env.memory\" memory({});", base.memory)?;
|
||||||
|
writeln!(file)?;
|
||||||
|
for &(module, ref name, type_id) in &base.function_imports {
|
||||||
|
if !name.contains("reserved") {
|
||||||
|
let ty = &base.types[type_id as usize];
|
||||||
|
let params: Vec<&str> = ty.params.iter().copied().map(type_to_str).collect();
|
||||||
|
write!(
|
||||||
|
file,
|
||||||
|
"import \"{}.{}\" fn {}({})",
|
||||||
|
module,
|
||||||
|
name,
|
||||||
|
name,
|
||||||
|
params.join(", ")
|
||||||
|
)?;
|
||||||
|
if let Some(result) = ty.result {
|
||||||
|
write!(file, " -> {}", type_to_str(result))?;
|
||||||
|
}
|
||||||
|
writeln!(file, ";")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(file)?;
|
||||||
|
for &(name, value) in CONSTANTS {
|
||||||
|
writeln!(file, "const {} = 0x{:x};", name, value)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
inner(File::create(path)?, self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_as_wat<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
||||||
|
fn inner(mut file: File, base: &BaseModule) -> Result<()> {
|
||||||
|
writeln!(file, ";; MicroW8 APIs, in WAT (Wasm Text) format")?;
|
||||||
|
writeln!(file, "(import \"env\" \"memory\" (memory {}))", base.memory)?;
|
||||||
|
writeln!(file)?;
|
||||||
|
for &(module, ref name, type_id) in &base.function_imports {
|
||||||
|
if !name.contains("reserved") {
|
||||||
|
let ty = &base.types[type_id as usize];
|
||||||
|
write!(file, "(import \"{}\" \"{}\" (func ${}", module, name, name)?;
|
||||||
|
for ¶m in &ty.params {
|
||||||
|
write!(file, " (param {})", type_to_str(param))?;
|
||||||
|
}
|
||||||
|
if let Some(result) = ty.result {
|
||||||
|
write!(file, " (result {})", type_to_str(result))?;
|
||||||
|
}
|
||||||
|
writeln!(file, "))")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(file)?;
|
||||||
|
writeln!(file, ";; to use defines, include this file with a preprocessor\n;; like gpp (https://logological.org/gpp).")?;
|
||||||
|
for &(name, value) in CONSTANTS {
|
||||||
|
writeln!(file, "#define {} 0x{:x};", name, value)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
inner(File::create(path)?, self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_function(
|
fn add_function(
|
||||||
@@ -241,3 +399,30 @@ fn lookup_type(
|
|||||||
};
|
};
|
||||||
*type_map.get(&key).unwrap()
|
*type_map.get(&key).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn type_to_str(ty: ValType) -> &'static str {
|
||||||
|
match ty {
|
||||||
|
ValType::I32 => "i32",
|
||||||
|
ValType::I64 => "i64",
|
||||||
|
ValType::F32 => "f32",
|
||||||
|
ValType::F64 => "f64",
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const CONSTANTS: &[(&str, u32)] = &[
|
||||||
|
("TIME_MS", 0x40),
|
||||||
|
("GAMEPAD", 0x44),
|
||||||
|
("FRAMEBUFFER", 0x78),
|
||||||
|
("PALETTE", 0x13000),
|
||||||
|
("FONT", 0x13400),
|
||||||
|
("USER_MEM", 0x14000),
|
||||||
|
("BUTTON_UP", 0),
|
||||||
|
("BUTTON_DOWN", 1),
|
||||||
|
("BUTTON_LEFT", 2),
|
||||||
|
("BUTTON_RIGHT", 3),
|
||||||
|
("BUTTON_A", 4),
|
||||||
|
("BUTTON_B", 5),
|
||||||
|
("BUTTON_X", 6),
|
||||||
|
("BUTTON_Y", 7),
|
||||||
|
];
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
use std::path::Path;
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
pub fn filter_exports(in_path: &Path, out_path: &Path) -> Result<()> {
|
pub fn filter_exports(in_path: &Path, out_path: &Path) -> Result<()> {
|
||||||
let mut module = walrus::Module::from_file(in_path)?;
|
let mut module = walrus::Module::from_file(in_path)?;
|
||||||
|
|
||||||
let exports_to_delete: Vec<_> = module.exports.iter().filter_map(|export| match export.name.as_str() {
|
let exports_to_delete: Vec<_> = module
|
||||||
"upd" => None,
|
.exports
|
||||||
_ => Some(export.id())
|
.iter()
|
||||||
}).collect();
|
.filter_map(|export| match export.name.as_str() {
|
||||||
|
"start" | "upd" | "snd" => None,
|
||||||
|
_ => Some(export.id()),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
for id in exports_to_delete {
|
for id in exports_to_delete {
|
||||||
module.exports.delete(id);
|
module.exports.delete(id);
|
||||||
@@ -18,4 +22,4 @@ pub fn filter_exports(in_path: &Path, out_path: &Path) -> Result<()> {
|
|||||||
module.emit_wasm_file(out_path)?;
|
module.emit_wasm_file(out_path)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-2
@@ -1,7 +1,15 @@
|
|||||||
mod base_module;
|
mod base_module;
|
||||||
mod pack;
|
|
||||||
mod filter_exports;
|
mod filter_exports;
|
||||||
|
mod pack;
|
||||||
|
|
||||||
pub use base_module::BaseModule;
|
pub use base_module::BaseModule;
|
||||||
pub use pack::{pack, pack_file, unpack, unpack_file, PackConfig};
|
|
||||||
pub use filter_exports::filter_exports;
|
pub use filter_exports::filter_exports;
|
||||||
|
pub use pack::{pack, pack_file, unpack, unpack_file, PackConfig};
|
||||||
|
|
||||||
|
pub fn compressed_size(cart: &[u8]) -> f32 {
|
||||||
|
if cart[0] != 2 {
|
||||||
|
cart.len() as f32
|
||||||
|
} else {
|
||||||
|
upkr::compressed_size(&cart[1..]) + 1.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use uw8_tool::BaseModule;
|
|
||||||
use pico_args::Arguments;
|
use pico_args::Arguments;
|
||||||
|
use uw8_tool::BaseModule;
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let mut args = Arguments::from_env();
|
let mut args = Arguments::from_env();
|
||||||
@@ -32,6 +32,14 @@ fn main() -> Result<()> {
|
|||||||
let dest: PathBuf = args.free_from_str()?;
|
let dest: PathBuf = args.free_from_str()?;
|
||||||
uw8_tool::filter_exports(&source, &dest)?;
|
uw8_tool::filter_exports(&source, &dest)?;
|
||||||
}
|
}
|
||||||
|
"base-cwa" => {
|
||||||
|
let path: PathBuf = args.free_from_str()?;
|
||||||
|
BaseModule::for_format_version(1)?.write_as_cwa(path)?;
|
||||||
|
}
|
||||||
|
"base-wat" => {
|
||||||
|
let path: PathBuf = args.free_from_str()?;
|
||||||
|
BaseModule::for_format_version(1)?.write_as_wat(path)?;
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
eprintln!("Unknown subcommand '{}'", cmd);
|
eprintln!("Unknown subcommand '{}'", cmd);
|
||||||
print_help();
|
print_help();
|
||||||
|
|||||||
+135
-125
@@ -10,7 +10,7 @@ use std::{
|
|||||||
use wasm_encoder as enc;
|
use wasm_encoder as enc;
|
||||||
use wasmparser::{
|
use wasmparser::{
|
||||||
BinaryReader, ExportSectionReader, ExternalKind, FunctionBody, FunctionSectionReader,
|
BinaryReader, ExportSectionReader, ExternalKind, FunctionBody, FunctionSectionReader,
|
||||||
ImportSectionEntryType, ImportSectionReader, TableSectionReader, TypeSectionReader,
|
ImportSectionReader, TableSectionReader, TypeRef, TypeSectionReader,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct PackConfig {
|
pub struct PackConfig {
|
||||||
@@ -63,6 +63,7 @@ pub fn pack(data: &[u8], config: &PackConfig) -> Result<Vec<u8>> {
|
|||||||
uw8.extend_from_slice(&upkr::pack(
|
uw8.extend_from_slice(&upkr::pack(
|
||||||
&result[8..],
|
&result[8..],
|
||||||
level,
|
level,
|
||||||
|
&upkr::Config::default(),
|
||||||
Some(&mut |pos| {
|
Some(&mut |pos| {
|
||||||
pb.set(pos as u64);
|
pb.set(pos as u64);
|
||||||
}),
|
}),
|
||||||
@@ -89,7 +90,10 @@ pub fn unpack(data: Vec<u8>) -> Result<Vec<u8>> {
|
|||||||
let (version, data) = match data[0] {
|
let (version, data) = match data[0] {
|
||||||
0 => return Ok(data),
|
0 => return Ok(data),
|
||||||
1 => (1, data[1..].to_vec()),
|
1 => (1, data[1..].to_vec()),
|
||||||
2 => (1, upkr::unpack(&data[1..])),
|
2 => (
|
||||||
|
1,
|
||||||
|
upkr::unpack(&data[1..], &upkr::Config::default(), 4 * 1024 * 1024)?,
|
||||||
|
),
|
||||||
other => bail!("Uknown format version {}", other),
|
other => bail!("Uknown format version {}", other),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -132,8 +136,8 @@ pub fn unpack(data: Vec<u8>) -> Result<Vec<u8>> {
|
|||||||
Ok(dest)
|
Ok(dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_val_type(type_: &wasmparser::Type) -> Result<ValType> {
|
fn to_val_type(type_: &wasmparser::ValType) -> Result<ValType> {
|
||||||
use wasmparser::Type::*;
|
use wasmparser::ValType::*;
|
||||||
Ok(match *type_ {
|
Ok(match *type_ {
|
||||||
I32 => ValType::I32,
|
I32 => ValType::I32,
|
||||||
I64 => ValType::I64,
|
I64 => ValType::I64,
|
||||||
@@ -143,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()
|
types.into_iter().map(to_val_type).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,7 +205,7 @@ impl<'a> ParsedModule<'a> {
|
|||||||
import_section = Some(Section::new(range, ImportSection::parse(reader)?));
|
import_section = Some(Section::new(range, ImportSection::parse(reader)?));
|
||||||
}
|
}
|
||||||
Payload::GlobalSection(reader) => {
|
Payload::GlobalSection(reader) => {
|
||||||
global_section = Some(Section::new(range, reader.get_count()));
|
global_section = Some(Section::new(range, reader.count()));
|
||||||
}
|
}
|
||||||
Payload::FunctionSection(reader) => {
|
Payload::FunctionSection(reader) => {
|
||||||
function_section = Some(Section::new(range, read_function_section(reader)?));
|
function_section = Some(Section::new(range, read_function_section(reader)?));
|
||||||
@@ -219,17 +223,22 @@ impl<'a> ParsedModule<'a> {
|
|||||||
validate_table_section(reader)?;
|
validate_table_section(reader)?;
|
||||||
table_section = Some(Section::new(range, ()));
|
table_section = Some(Section::new(range, ()));
|
||||||
}
|
}
|
||||||
Payload::ElementSection(mut reader) => {
|
Payload::MemorySection(reader) => {
|
||||||
let mut elements = Vec::with_capacity(reader.get_count() as usize);
|
if reader.count() != 0 {
|
||||||
for _ in 0..reader.get_count() {
|
bail!("Found non-empty MemorySection. Memory has to be imported!");
|
||||||
elements.push(Element::parse(reader.read()?)?);
|
}
|
||||||
|
}
|
||||||
|
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);
|
element_section = Some(elements);
|
||||||
}
|
}
|
||||||
Payload::CodeSectionStart { .. } => (),
|
Payload::CodeSectionStart { .. } => (),
|
||||||
Payload::CodeSectionEntry(body) => function_bodies.push(body),
|
Payload::CodeSectionEntry(body) => function_bodies.push(body),
|
||||||
Payload::CustomSection { .. } => (),
|
Payload::CustomSection { .. } => (),
|
||||||
Payload::End => break,
|
Payload::End(..) => break,
|
||||||
other => bail!("Unsupported section: {:?}", other),
|
other => bail!("Unsupported section: {:?}", other),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,7 +466,7 @@ impl<'a> ParsedModule<'a> {
|
|||||||
{
|
{
|
||||||
let mut export_section = enc::ExportSection::new();
|
let mut export_section = enc::ExportSection::new();
|
||||||
for (name, fnc) in my_exports {
|
for (name, fnc) in my_exports {
|
||||||
export_section.export(&name, enc::Export::Function(fnc));
|
export_section.export(&name, enc::ExportKind::Func, fnc);
|
||||||
}
|
}
|
||||||
module.section(&export_section);
|
module.section(&export_section);
|
||||||
}
|
}
|
||||||
@@ -480,8 +489,7 @@ impl<'a> ParsedModule<'a> {
|
|||||||
}
|
}
|
||||||
element_section.active(
|
element_section.active(
|
||||||
None,
|
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),
|
wasm_encoder::Elements::Functions(&functions),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -527,30 +535,32 @@ fn copy_section(module: &mut wasm_encoder::Module, data: &[u8]) -> Result<()> {
|
|||||||
fn read_type_section(reader: TypeSectionReader) -> Result<Vec<base_module::FunctionType>> {
|
fn read_type_section(reader: TypeSectionReader) -> Result<Vec<base_module::FunctionType>> {
|
||||||
let mut function_types = vec![];
|
let mut function_types = vec![];
|
||||||
|
|
||||||
for type_def in reader {
|
for rec_group in reader {
|
||||||
match type_def? {
|
for sub_type in rec_group?.into_types() {
|
||||||
wasmparser::TypeDef::Func(fnc) => {
|
match sub_type.composite_type {
|
||||||
if fnc.returns.len() > 1 {
|
wasmparser::CompositeType::Func(fnc) => {
|
||||||
bail!("Multi-value not supported");
|
if fnc.results().len() > 1 {
|
||||||
|
bail!("Multi-value not supported");
|
||||||
|
}
|
||||||
|
let params = to_val_type_vec(fnc.params())?;
|
||||||
|
let result = to_val_type_vec(fnc.results())?.into_iter().next();
|
||||||
|
function_types.push(FunctionType { params, result });
|
||||||
}
|
}
|
||||||
let params = to_val_type_vec(&fnc.params)?;
|
_ => bail!("Only function types are supported"),
|
||||||
let result = to_val_type_vec(&fnc.returns)?.into_iter().next();
|
|
||||||
function_types.push(FunctionType { params, result });
|
|
||||||
}
|
}
|
||||||
t => bail!("Unsupported type def {:?}", t),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(function_types)
|
Ok(function_types)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_table_section(mut reader: TableSectionReader) -> Result<()> {
|
fn validate_table_section(reader: TableSectionReader) -> Result<()> {
|
||||||
if reader.get_count() != 1 {
|
if reader.count() != 1 {
|
||||||
bail!("Only up to one table supported");
|
bail!("Only up to one table supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
let type_ = reader.read()?;
|
let table = reader.into_iter().next().unwrap()?;
|
||||||
if type_.element_type != wasmparser::Type::FuncRef {
|
if !table.ty.element_type.is_func_ref() {
|
||||||
bail!("Only one funcref table is supported");
|
bail!("Only one funcref table is supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -584,45 +594,38 @@ impl ImportSection {
|
|||||||
|
|
||||||
for import in reader {
|
for import in reader {
|
||||||
let import = import?;
|
let import = import?;
|
||||||
if let Some(field) = import.field {
|
match import.ty {
|
||||||
match import.ty {
|
TypeRef::Func(type_) => {
|
||||||
ImportSectionEntryType::Function(type_) => {
|
functions.push(FunctionImport {
|
||||||
functions.push(FunctionImport {
|
module: import.module.to_string(),
|
||||||
module: import.module.to_string(),
|
field: import.name.to_string(),
|
||||||
field: field.to_string(),
|
type_,
|
||||||
type_,
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
ImportSectionEntryType::Memory(mem) => {
|
|
||||||
if import.module != "env" || field != "memory" {
|
|
||||||
bail!(
|
|
||||||
"Wrong name of memory import {}.{}, should be env.memory",
|
|
||||||
import.module,
|
|
||||||
field
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if mem.memory64 || mem.shared {
|
|
||||||
bail!("Wrong memory import options: {:?}", import.ty);
|
|
||||||
}
|
|
||||||
memory = mem.maximum.unwrap_or(mem.initial) as u32;
|
|
||||||
}
|
|
||||||
ImportSectionEntryType::Global(glbl) => {
|
|
||||||
globals.push(GlobalImport {
|
|
||||||
module: import.module.to_string(),
|
|
||||||
field: field.to_string(),
|
|
||||||
type_: GlobalType {
|
|
||||||
type_: to_val_type(&glbl.content_type)?,
|
|
||||||
mutable: glbl.mutable,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_ => bail!("Unsupported import item {:?}", import.ty),
|
|
||||||
}
|
}
|
||||||
} else {
|
TypeRef::Memory(mem) => {
|
||||||
bail!(
|
if import.module != "env" || import.name != "memory" {
|
||||||
"Found import without field, only module '{}'",
|
bail!(
|
||||||
import.module
|
"Wrong name of memory import {}.{}, should be env.memory",
|
||||||
);
|
import.module,
|
||||||
|
import.name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if mem.memory64 || mem.shared {
|
||||||
|
bail!("Wrong memory import options: {:?}", import.ty);
|
||||||
|
}
|
||||||
|
memory = mem.maximum.unwrap_or(mem.initial) as u32;
|
||||||
|
}
|
||||||
|
TypeRef::Global(glbl) => {
|
||||||
|
globals.push(GlobalImport {
|
||||||
|
module: import.module.to_string(),
|
||||||
|
field: import.name.to_string(),
|
||||||
|
type_: GlobalType {
|
||||||
|
type_: to_val_type(&glbl.content_type)?,
|
||||||
|
mutable: glbl.mutable,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => bail!("Unsupported import item {:?}", import.ty),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -642,40 +645,40 @@ struct Element {
|
|||||||
|
|
||||||
impl Element {
|
impl Element {
|
||||||
fn parse(element: wasmparser::Element) -> Result<Element> {
|
fn parse(element: wasmparser::Element) -> Result<Element> {
|
||||||
if element.ty != wasmparser::Type::FuncRef {
|
match element.items {
|
||||||
bail!("Table element type is not FuncRef");
|
wasmparser::ElementItems::Functions(funcs_reader) => {
|
||||||
}
|
let start_index = if let wasmparser::ElementKind::Active {
|
||||||
|
offset_expr,
|
||||||
|
table_index,
|
||||||
|
} = element.kind
|
||||||
|
{
|
||||||
|
if table_index.map(|i| i > 0).unwrap_or(false) {
|
||||||
|
bail!("Only function table with index 0 is supported");
|
||||||
|
}
|
||||||
|
let mut init_reader = offset_expr.get_operators_reader();
|
||||||
|
if let wasmparser::Operator::I32Const { value: start_index } =
|
||||||
|
init_reader.read()?
|
||||||
|
{
|
||||||
|
start_index as u32
|
||||||
|
} else {
|
||||||
|
bail!("Table element start index is not a integer constant");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bail!("Unsupported table element kind");
|
||||||
|
};
|
||||||
|
|
||||||
let start_index = if let wasmparser::ElementKind::Active {
|
let mut functions = Vec::with_capacity(funcs_reader.count() as usize);
|
||||||
init_expr,
|
for index in funcs_reader {
|
||||||
table_index: 0,
|
functions.push(index?);
|
||||||
} = element.kind
|
}
|
||||||
{
|
|
||||||
let mut init_reader = init_expr.get_operators_reader();
|
Ok(Element {
|
||||||
if let wasmparser::Operator::I32Const { value: start_index } = init_reader.read()? {
|
start_index,
|
||||||
start_index as u32
|
functions,
|
||||||
} else {
|
})
|
||||||
bail!("Table element start index is not a integer constant");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bail!("Unsupported table element kind");
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut items_reader = element.items.get_items_reader()?;
|
|
||||||
|
|
||||||
let mut functions = Vec::with_capacity(items_reader.get_count() as usize);
|
|
||||||
for _ in 0..items_reader.get_count() {
|
|
||||||
if let wasmparser::ElementItem::Func(index) = items_reader.read()? {
|
|
||||||
functions.push(index);
|
|
||||||
} else {
|
|
||||||
bail!("Table element item is not a function");
|
|
||||||
}
|
}
|
||||||
|
_ => bail!("Table element type is not FuncRef"),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Element {
|
|
||||||
start_index,
|
|
||||||
functions,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -706,8 +709,8 @@ fn read_export_section(reader: ExportSectionReader) -> Result<Vec<(String, u32)>
|
|||||||
for export in reader {
|
for export in reader {
|
||||||
let export = export?;
|
let export = export?;
|
||||||
match export.kind {
|
match export.kind {
|
||||||
ExternalKind::Function => {
|
ExternalKind::Func => {
|
||||||
function_exports.push((export.field.to_string(), export.index));
|
function_exports.push((export.name.to_string(), export.index));
|
||||||
}
|
}
|
||||||
_ => (), // just ignore all other kinds since MicroW8 doesn't expect any exports other than functions
|
_ => (), // just ignore all other kinds since MicroW8 doesn't expect any exports other than functions
|
||||||
}
|
}
|
||||||
@@ -728,13 +731,11 @@ fn remap_function(
|
|||||||
}
|
}
|
||||||
let mut function = enc::Function::new(locals);
|
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 {
|
Ok(match ty {
|
||||||
wasmparser::TypeOrFuncType::Type(wasmparser::Type::EmptyBlockType) => {
|
wasmparser::BlockType::Empty => enc::BlockType::Empty,
|
||||||
enc::BlockType::Empty
|
wasmparser::BlockType::Type(ty) => enc::BlockType::Result(to_val_type(&ty)?),
|
||||||
}
|
wasmparser::BlockType::FuncType(ty) => enc::BlockType::FunctionType(
|
||||||
wasmparser::TypeOrFuncType::Type(ty) => enc::BlockType::Result(to_val_type(&ty)?),
|
|
||||||
wasmparser::TypeOrFuncType::FuncType(ty) => enc::BlockType::FunctionType(
|
|
||||||
*type_map
|
*type_map
|
||||||
.get(&ty)
|
.get(&ty)
|
||||||
.ok_or_else(|| anyhow!("Function type index out of range: {}", ty))?,
|
.ok_or_else(|| anyhow!("Function type index out of range: {}", ty))?,
|
||||||
@@ -748,7 +749,7 @@ fn remap_function(
|
|||||||
.ok_or_else(|| anyhow!("Global index out of range: {}", idx))?)
|
.ok_or_else(|| anyhow!("Global index out of range: {}", idx))?)
|
||||||
};
|
};
|
||||||
|
|
||||||
fn mem(m: wasmparser::MemoryImmediate) -> enc::MemArg {
|
fn mem(m: wasmparser::MemArg) -> enc::MemArg {
|
||||||
enc::MemArg {
|
enc::MemArg {
|
||||||
offset: m.offset,
|
offset: m.offset,
|
||||||
align: m.align as u32,
|
align: m.align as u32,
|
||||||
@@ -763,24 +764,31 @@ fn remap_function(
|
|||||||
function.instruction(&match op? {
|
function.instruction(&match op? {
|
||||||
De::Unreachable => En::Unreachable,
|
De::Unreachable => En::Unreachable,
|
||||||
De::Nop => En::Nop,
|
De::Nop => En::Nop,
|
||||||
De::Block { ty } => En::Block(block_type(ty)?),
|
De::Block { blockty } => En::Block(block_type(blockty)?),
|
||||||
De::Loop { ty } => En::Loop(block_type(ty)?),
|
De::Loop { blockty } => En::Loop(block_type(blockty)?),
|
||||||
De::If { ty } => En::If(block_type(ty)?),
|
De::If { blockty } => En::If(block_type(blockty)?),
|
||||||
De::Else => En::Else,
|
De::Else => En::Else,
|
||||||
De::Try { .. } | De::Catch { .. } | De::Throw { .. } | De::Rethrow { .. } => todo!(),
|
De::Try { .. } | De::Catch { .. } | De::Throw { .. } | De::Rethrow { .. } => todo!(),
|
||||||
De::End => En::End,
|
De::End => En::End,
|
||||||
De::Br { relative_depth } => En::Br(relative_depth),
|
De::Br { relative_depth } => En::Br(relative_depth),
|
||||||
De::BrIf { relative_depth } => En::BrIf(relative_depth),
|
De::BrIf { relative_depth } => En::BrIf(relative_depth),
|
||||||
De::BrTable { .. } => todo!(),
|
De::BrTable { targets } => En::BrTable(
|
||||||
|
targets.targets().collect::<Result<Vec<u32>, _>>()?.into(),
|
||||||
|
targets.default(),
|
||||||
|
),
|
||||||
De::Return => En::Return,
|
De::Return => En::Return,
|
||||||
De::Call { function_index } => En::Call(
|
De::Call { function_index } => En::Call(
|
||||||
*function_map
|
*function_map
|
||||||
.get(&function_index)
|
.get(&function_index)
|
||||||
.ok_or_else(|| anyhow!("Function index out of range: {}", function_index))?,
|
.ok_or_else(|| anyhow!("Function index out of range: {}", function_index))?,
|
||||||
),
|
),
|
||||||
De::CallIndirect { index, table_index } => En::CallIndirect {
|
De::CallIndirect {
|
||||||
|
type_index,
|
||||||
|
table_index,
|
||||||
|
table_byte: _, // what is this supposed to be?
|
||||||
|
} => En::CallIndirect {
|
||||||
ty: *type_map
|
ty: *type_map
|
||||||
.get(&index)
|
.get(&type_index)
|
||||||
.ok_or_else(|| anyhow!("Unknown function type in call indirect"))?,
|
.ok_or_else(|| anyhow!("Unknown function type in call indirect"))?,
|
||||||
table: table_index,
|
table: table_index,
|
||||||
},
|
},
|
||||||
@@ -800,16 +808,16 @@ fn remap_function(
|
|||||||
De::I64Load { memarg } => En::I64Load(mem(memarg)),
|
De::I64Load { memarg } => En::I64Load(mem(memarg)),
|
||||||
De::F32Load { memarg } => En::F32Load(mem(memarg)),
|
De::F32Load { memarg } => En::F32Load(mem(memarg)),
|
||||||
De::F64Load { memarg } => En::F64Load(mem(memarg)),
|
De::F64Load { memarg } => En::F64Load(mem(memarg)),
|
||||||
De::I32Load8S { memarg } => En::I32Load8_S(mem(memarg)),
|
De::I32Load8S { memarg } => En::I32Load8S(mem(memarg)),
|
||||||
De::I32Load8U { memarg } => En::I32Load8_U(mem(memarg)),
|
De::I32Load8U { memarg } => En::I32Load8U(mem(memarg)),
|
||||||
De::I32Load16S { memarg } => En::I32Load16_S(mem(memarg)),
|
De::I32Load16S { memarg } => En::I32Load16S(mem(memarg)),
|
||||||
De::I32Load16U { memarg } => En::I32Load16_U(mem(memarg)),
|
De::I32Load16U { memarg } => En::I32Load16U(mem(memarg)),
|
||||||
De::I64Load8S { memarg } => En::I64Load8_S(mem(memarg)),
|
De::I64Load8S { memarg } => En::I64Load8S(mem(memarg)),
|
||||||
De::I64Load8U { memarg } => En::I64Load8_U(mem(memarg)),
|
De::I64Load8U { memarg } => En::I64Load8U(mem(memarg)),
|
||||||
De::I64Load16S { memarg } => En::I64Load16_S(mem(memarg)),
|
De::I64Load16S { memarg } => En::I64Load16S(mem(memarg)),
|
||||||
De::I64Load16U { memarg } => En::I64Load16_U(mem(memarg)),
|
De::I64Load16U { memarg } => En::I64Load16U(mem(memarg)),
|
||||||
De::I64Load32S { memarg } => En::I64Load32_S(mem(memarg)),
|
De::I64Load32S { memarg } => En::I64Load32S(mem(memarg)),
|
||||||
De::I64Load32U { memarg } => En::I64Load32_U(mem(memarg)),
|
De::I64Load32U { memarg } => En::I64Load32U(mem(memarg)),
|
||||||
De::I32Store { memarg } => En::I32Store(mem(memarg)),
|
De::I32Store { memarg } => En::I32Store(mem(memarg)),
|
||||||
De::I64Store { memarg } => En::I64Store(mem(memarg)),
|
De::I64Store { memarg } => En::I64Store(mem(memarg)),
|
||||||
De::F32Store { memarg } => En::F32Store(mem(memarg)),
|
De::F32Store { memarg } => En::F32Store(mem(memarg)),
|
||||||
@@ -828,7 +836,7 @@ fn remap_function(
|
|||||||
De::RefNull { .. } | De::RefIsNull { .. } | De::RefFunc { .. } => todo!(),
|
De::RefNull { .. } | De::RefIsNull { .. } | De::RefFunc { .. } => todo!(),
|
||||||
De::I32Eqz => En::I32Eqz,
|
De::I32Eqz => En::I32Eqz,
|
||||||
De::I32Eq => En::I32Eq,
|
De::I32Eq => En::I32Eq,
|
||||||
De::I32Ne => En::I32Neq,
|
De::I32Ne => En::I32Ne,
|
||||||
De::I32LtS => En::I32LtS,
|
De::I32LtS => En::I32LtS,
|
||||||
De::I32LtU => En::I32LtU,
|
De::I32LtU => En::I32LtU,
|
||||||
De::I32GtS => En::I32GtS,
|
De::I32GtS => En::I32GtS,
|
||||||
@@ -839,7 +847,7 @@ fn remap_function(
|
|||||||
De::I32GeU => En::I32GeU,
|
De::I32GeU => En::I32GeU,
|
||||||
De::I64Eqz => En::I64Eqz,
|
De::I64Eqz => En::I64Eqz,
|
||||||
De::I64Eq => En::I64Eq,
|
De::I64Eq => En::I64Eq,
|
||||||
De::I64Ne => En::I64Neq,
|
De::I64Ne => En::I64Ne,
|
||||||
De::I64LtS => En::I64LtS,
|
De::I64LtS => En::I64LtS,
|
||||||
De::I64LtU => En::I64LtU,
|
De::I64LtU => En::I64LtU,
|
||||||
De::I64GtS => En::I64GtS,
|
De::I64GtS => En::I64GtS,
|
||||||
@@ -849,13 +857,13 @@ fn remap_function(
|
|||||||
De::I64GeS => En::I64GeS,
|
De::I64GeS => En::I64GeS,
|
||||||
De::I64GeU => En::I64GeU,
|
De::I64GeU => En::I64GeU,
|
||||||
De::F32Eq => En::F32Eq,
|
De::F32Eq => En::F32Eq,
|
||||||
De::F32Ne => En::F32Neq,
|
De::F32Ne => En::F32Ne,
|
||||||
De::F32Lt => En::F32Lt,
|
De::F32Lt => En::F32Lt,
|
||||||
De::F32Gt => En::F32Gt,
|
De::F32Gt => En::F32Gt,
|
||||||
De::F32Le => En::F32Le,
|
De::F32Le => En::F32Le,
|
||||||
De::F32Ge => En::F32Ge,
|
De::F32Ge => En::F32Ge,
|
||||||
De::F64Eq => En::F64Eq,
|
De::F64Eq => En::F64Eq,
|
||||||
De::F64Ne => En::F64Neq,
|
De::F64Ne => En::F64Ne,
|
||||||
De::F64Lt => En::F64Lt,
|
De::F64Lt => En::F64Lt,
|
||||||
De::F64Gt => En::F64Gt,
|
De::F64Gt => En::F64Gt,
|
||||||
De::F64Le => En::F64Le,
|
De::F64Le => En::F64Le,
|
||||||
@@ -962,6 +970,8 @@ fn remap_function(
|
|||||||
De::I64TruncSatF32U => En::I64TruncSatF32U,
|
De::I64TruncSatF32U => En::I64TruncSatF32U,
|
||||||
De::I64TruncSatF64S => En::I64TruncSatF64S,
|
De::I64TruncSatF64S => En::I64TruncSatF64S,
|
||||||
De::I64TruncSatF64U => En::I64TruncSatF64U,
|
De::I64TruncSatF64U => En::I64TruncSatF64U,
|
||||||
|
De::MemoryCopy { src_mem, dst_mem } => En::MemoryCopy { src_mem, dst_mem },
|
||||||
|
De::MemoryFill { mem } => En::MemoryFill(mem),
|
||||||
other => bail!("Unsupported instruction {:?}", other),
|
other => bail!("Unsupported instruction {:?}", other),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
Generated
+2228
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
|||||||
|
[package]
|
||||||
|
name = "uw8-window"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
env_logger = "0.11.3"
|
||||||
|
winit = "0.28.6"
|
||||||
|
log = "0.4"
|
||||||
|
pico-args = "0.5"
|
||||||
|
wgpu = "0.17"
|
||||||
|
pollster = "0.3.0"
|
||||||
|
bytemuck = { version = "1.15", features = [ "derive" ] }
|
||||||
|
anyhow = "1"
|
||||||
|
minifb = { version = "0.25.0", default-features = false, features = ["x11"] }
|
||||||
|
winapi = { version = "0.3.9", features = [ "timeapi" ] }
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use crate::{Input, WindowImpl};
|
||||||
|
use anyhow::Result;
|
||||||
|
use minifb::{Key, WindowOptions};
|
||||||
|
|
||||||
|
static GAMEPAD_KEYS: &[Key] = &[
|
||||||
|
Key::Up,
|
||||||
|
Key::Down,
|
||||||
|
Key::Left,
|
||||||
|
Key::Right,
|
||||||
|
Key::Z,
|
||||||
|
Key::X,
|
||||||
|
Key::A,
|
||||||
|
Key::S,
|
||||||
|
];
|
||||||
|
|
||||||
|
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 buffer: Vec<u32> = vec![0; 320 * 240];
|
||||||
|
|
||||||
|
let options = WindowOptions {
|
||||||
|
scale: minifb::Scale::X2,
|
||||||
|
scale_mode: minifb::ScaleMode::AspectRatioStretch,
|
||||||
|
resize: true,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let window = minifb::Window::new("MicroW8", 320, 240, options).unwrap();
|
||||||
|
|
||||||
|
Ok(Window { window, buffer })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
gamepads[0] |= 1 << index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Input {
|
||||||
|
gamepads,
|
||||||
|
reset: self.window.is_key_pressed(Key::R, minifb::KeyRepeat::No),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
| ((palette[offset] as u32) << 16)
|
||||||
|
| ((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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
use wgpu::util::DeviceExt;
|
||||||
|
use winit::dpi::PhysicalSize;
|
||||||
|
|
||||||
|
use super::{scale_mode::ScaleMode, Filter};
|
||||||
|
|
||||||
|
pub struct CrtFilter {
|
||||||
|
uniform_buffer: wgpu::Buffer,
|
||||||
|
bind_group: wgpu::BindGroup,
|
||||||
|
pipeline: wgpu::RenderPipeline,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CrtFilter {
|
||||||
|
pub fn new(
|
||||||
|
device: &wgpu::Device,
|
||||||
|
screen: &wgpu::TextureView,
|
||||||
|
resolution: PhysicalSize<u32>,
|
||||||
|
surface_format: wgpu::TextureFormat,
|
||||||
|
scale_mode: ScaleMode,
|
||||||
|
) -> CrtFilter {
|
||||||
|
let uniforms = Uniforms {
|
||||||
|
texture_scale: scale_mode.texture_scale_from_resolution(resolution),
|
||||||
|
};
|
||||||
|
|
||||||
|
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: None,
|
||||||
|
contents: bytemuck::cast_slice(&[uniforms]),
|
||||||
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
|
});
|
||||||
|
|
||||||
|
let crt_bind_group_layout =
|
||||||
|
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Texture {
|
||||||
|
multisampled: false,
|
||||||
|
view_dimension: wgpu::TextureViewDimension::D2,
|
||||||
|
sample_type: wgpu::TextureSampleType::Float { filterable: false },
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 1,
|
||||||
|
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Buffer {
|
||||||
|
ty: wgpu::BufferBindingType::Uniform,
|
||||||
|
has_dynamic_offset: false,
|
||||||
|
min_binding_size: None,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
label: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
let crt_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
layout: &crt_bind_group_layout,
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: wgpu::BindingResource::TextureView(&screen),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 1,
|
||||||
|
resource: uniform_buffer.as_entire_binding(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
label: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
let crt_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
|
label: None,
|
||||||
|
source: wgpu::ShaderSource::Wgsl(include_str!("crt.wgsl").into()),
|
||||||
|
});
|
||||||
|
|
||||||
|
let render_pipeline_layout =
|
||||||
|
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
|
label: None,
|
||||||
|
bind_group_layouts: &[&crt_bind_group_layout],
|
||||||
|
push_constant_ranges: &[],
|
||||||
|
});
|
||||||
|
|
||||||
|
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||||
|
label: None,
|
||||||
|
layout: Some(&render_pipeline_layout),
|
||||||
|
vertex: wgpu::VertexState {
|
||||||
|
module: &crt_shader,
|
||||||
|
entry_point: "vs_main",
|
||||||
|
buffers: &[],
|
||||||
|
},
|
||||||
|
fragment: Some(wgpu::FragmentState {
|
||||||
|
module: &crt_shader,
|
||||||
|
entry_point: "fs_main",
|
||||||
|
targets: &[Some(wgpu::ColorTargetState {
|
||||||
|
format: surface_format,
|
||||||
|
blend: None,
|
||||||
|
write_mask: wgpu::ColorWrites::ALL,
|
||||||
|
})],
|
||||||
|
}),
|
||||||
|
primitive: Default::default(),
|
||||||
|
depth_stencil: None,
|
||||||
|
multisample: Default::default(),
|
||||||
|
multiview: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
CrtFilter {
|
||||||
|
uniform_buffer,
|
||||||
|
bind_group: crt_bind_group,
|
||||||
|
pipeline: render_pipeline,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Filter for CrtFilter {
|
||||||
|
fn resize(&mut self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>, scale_mode: ScaleMode) {
|
||||||
|
let uniforms = Uniforms {
|
||||||
|
texture_scale: scale_mode.texture_scale_from_resolution(new_size),
|
||||||
|
};
|
||||||
|
queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[uniforms]));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) {
|
||||||
|
render_pass.set_pipeline(&self.pipeline);
|
||||||
|
render_pass.set_bind_group(0, &self.bind_group, &[]);
|
||||||
|
render_pass.draw(0..6, 0..1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
|
struct Uniforms {
|
||||||
|
texture_scale: [f32; 4],
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
struct VertexOutput {
|
||||||
|
@builtin(position) clip_position: vec4<f32>,
|
||||||
|
@location(0) tex_coords: vec2<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Uniforms {
|
||||||
|
texture_scale: vec4<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
@group(0) @binding(1) var<uniform> uniforms: Uniforms;
|
||||||
|
|
||||||
|
@vertex
|
||||||
|
fn vs_main(
|
||||||
|
@builtin(vertex_index) in_vertex_index: u32,
|
||||||
|
) -> VertexOutput {
|
||||||
|
var out: VertexOutput;
|
||||||
|
let i = in_vertex_index / 3u + in_vertex_index % 3u;
|
||||||
|
let x = -1.0 + f32(i % 2u) * 322.0;
|
||||||
|
let y = -1.0 + f32(i / 2u) * 242.0;
|
||||||
|
out.clip_position = vec4<f32>((vec2<f32>(x, y) - vec2<f32>(160.0, 120.0)) * uniforms.texture_scale.xy, 0.0, 1.0);
|
||||||
|
out.tex_coords = vec2<f32>(x, y);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@group(0) @binding(0) var screen_texture: texture_2d<f32>;
|
||||||
|
|
||||||
|
fn sample_pixel(coords: vec2<i32>, offset: vec4<f32>) -> vec3<f32> {
|
||||||
|
let is_outside = any(vec2<u32>(coords) >= vec2<u32>(320u, 240u));
|
||||||
|
if(is_outside) {
|
||||||
|
return vec3<f32>(0.0);
|
||||||
|
} else {
|
||||||
|
let f = max(vec4<f32>(0.008) / offset - vec4<f32>(0.0024), vec4<f32>(0.0));
|
||||||
|
return textureLoad(screen_texture, coords, 0).rgb * (f.x + f.y + f.z + f.w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
|
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;
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
offset_x0 = offset_x0 * offset_x0;
|
||||||
|
offset_x1 = offset_x1 * offset_x1;
|
||||||
|
offset_x2 = offset_x2 * offset_x2;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
acc = acc + sample_pixel(pixel + vec2<i32>(1, 1), offset_x2 + offset_yr);
|
||||||
|
|
||||||
|
return vec4<f32>(acc, 1.0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
use wgpu::util::DeviceExt;
|
||||||
|
use winit::dpi::PhysicalSize;
|
||||||
|
|
||||||
|
use super::{scale_mode::ScaleMode, Filter};
|
||||||
|
|
||||||
|
pub struct FastCrtFilter {
|
||||||
|
uniform_buffer: wgpu::Buffer,
|
||||||
|
bind_group: wgpu::BindGroup,
|
||||||
|
pipeline: wgpu::RenderPipeline,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FastCrtFilter {
|
||||||
|
pub fn new(
|
||||||
|
device: &wgpu::Device,
|
||||||
|
screen: &wgpu::TextureView,
|
||||||
|
resolution: PhysicalSize<u32>,
|
||||||
|
surface_format: wgpu::TextureFormat,
|
||||||
|
chromatic: bool,
|
||||||
|
scale_mode: ScaleMode,
|
||||||
|
) -> FastCrtFilter {
|
||||||
|
let uniforms = Uniforms {
|
||||||
|
texture_scale: scale_mode.texture_scale_from_resolution(resolution),
|
||||||
|
};
|
||||||
|
|
||||||
|
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: None,
|
||||||
|
contents: bytemuck::cast_slice(&[uniforms]),
|
||||||
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
|
});
|
||||||
|
|
||||||
|
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Texture {
|
||||||
|
multisampled: false,
|
||||||
|
view_dimension: wgpu::TextureViewDimension::D2,
|
||||||
|
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 1,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 2,
|
||||||
|
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Buffer {
|
||||||
|
ty: wgpu::BufferBindingType::Uniform,
|
||||||
|
has_dynamic_offset: false,
|
||||||
|
min_binding_size: None,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
label: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||||
|
mag_filter: wgpu::FilterMode::Linear,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
layout: &bind_group_layout,
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: wgpu::BindingResource::TextureView(&screen),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 1,
|
||||||
|
resource: wgpu::BindingResource::Sampler(&sampler),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 2,
|
||||||
|
resource: uniform_buffer.as_entire_binding(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
label: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
|
label: None,
|
||||||
|
source: wgpu::ShaderSource::Wgsl(include_str!("fast_crt.wgsl").into()),
|
||||||
|
});
|
||||||
|
|
||||||
|
let render_pipeline_layout =
|
||||||
|
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
|
label: None,
|
||||||
|
bind_group_layouts: &[&bind_group_layout],
|
||||||
|
push_constant_ranges: &[],
|
||||||
|
});
|
||||||
|
|
||||||
|
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||||
|
label: None,
|
||||||
|
layout: Some(&render_pipeline_layout),
|
||||||
|
vertex: wgpu::VertexState {
|
||||||
|
module: &shader,
|
||||||
|
entry_point: "vs_main",
|
||||||
|
buffers: &[],
|
||||||
|
},
|
||||||
|
fragment: Some(wgpu::FragmentState {
|
||||||
|
module: &shader,
|
||||||
|
entry_point: if chromatic {
|
||||||
|
"fs_main_chromatic"
|
||||||
|
} else {
|
||||||
|
"fs_main"
|
||||||
|
},
|
||||||
|
targets: &[Some(wgpu::ColorTargetState {
|
||||||
|
format: surface_format,
|
||||||
|
blend: None,
|
||||||
|
write_mask: wgpu::ColorWrites::ALL,
|
||||||
|
})],
|
||||||
|
}),
|
||||||
|
primitive: Default::default(),
|
||||||
|
depth_stencil: None,
|
||||||
|
multisample: Default::default(),
|
||||||
|
multiview: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
FastCrtFilter {
|
||||||
|
uniform_buffer,
|
||||||
|
bind_group,
|
||||||
|
pipeline: render_pipeline,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Filter for FastCrtFilter {
|
||||||
|
fn resize(&mut self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>, scale_mode: ScaleMode) {
|
||||||
|
let uniforms = Uniforms {
|
||||||
|
texture_scale: scale_mode.texture_scale_from_resolution(new_size),
|
||||||
|
};
|
||||||
|
queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[uniforms]));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) {
|
||||||
|
render_pass.set_pipeline(&self.pipeline);
|
||||||
|
render_pass.set_bind_group(0, &self.bind_group, &[]);
|
||||||
|
render_pass.draw(0..6, 0..1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
|
struct Uniforms {
|
||||||
|
texture_scale: [f32; 4],
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
struct VertexOutput {
|
||||||
|
@builtin(position) clip_position: vec4<f32>,
|
||||||
|
@location(0) tex_coords: vec2<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Uniforms {
|
||||||
|
texture_scale: vec4<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
@group(0) @binding(2) var<uniform> uniforms: Uniforms;
|
||||||
|
|
||||||
|
@vertex
|
||||||
|
fn vs_main(
|
||||||
|
@builtin(vertex_index) in_vertex_index: u32,
|
||||||
|
) -> VertexOutput {
|
||||||
|
var out: VertexOutput;
|
||||||
|
let i = in_vertex_index / 3u + in_vertex_index % 3u;
|
||||||
|
let x = 0.0 + f32(i % 2u) * 320.0;
|
||||||
|
let y = 0.0 + f32(i / 2u) * 240.0;
|
||||||
|
out.clip_position = vec4<f32>((vec2<f32>(x, y) - vec2<f32>(160.0, 120.0)) * uniforms.texture_scale.xy, 0.0, 1.0);
|
||||||
|
out.tex_coords = vec2<f32>(x, y);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@group(0) @binding(0) var screen_texture: texture_2d<f32>;
|
||||||
|
@group(0) @binding(1) var linear_sampler: sampler;
|
||||||
|
|
||||||
|
fn row_factor(offset: f32) -> f32 {
|
||||||
|
return 1.0 / (1.0 + offset * offset * 16.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn col_factor(offset: f32) -> f32 {
|
||||||
|
let o = max(0.0, abs(offset) - 0.4);
|
||||||
|
return 1.0 / (1.0 + o * o * 16.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sample_screen(tex_coords: vec2<f32>) -> vec4<f32> {
|
||||||
|
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);
|
||||||
|
|
||||||
|
let v = base.y + bottom_factor / (bottom_factor + top_factor);
|
||||||
|
|
||||||
|
let left_factor = col_factor(frac.x);
|
||||||
|
let right_factor = col_factor(frac.x - 1.0);
|
||||||
|
|
||||||
|
let u = base.x + right_factor / (right_factor + left_factor);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,582 @@
|
|||||||
|
use crate::{Input, WindowConfig, WindowImpl};
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use scale_mode::ScaleMode;
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use winit::{
|
||||||
|
dpi::PhysicalSize,
|
||||||
|
event::{Event, VirtualKeyCode, WindowEvent},
|
||||||
|
event_loop::{ControlFlow, EventLoop},
|
||||||
|
window::{Fullscreen, WindowBuilder},
|
||||||
|
};
|
||||||
|
|
||||||
|
use winit::platform::run_return::EventLoopExtRunReturn;
|
||||||
|
|
||||||
|
mod crt;
|
||||||
|
mod fast_crt;
|
||||||
|
pub mod scale_mode;
|
||||||
|
mod square;
|
||||||
|
|
||||||
|
use crt::CrtFilter;
|
||||||
|
use fast_crt::FastCrtFilter;
|
||||||
|
use square::SquareFilter;
|
||||||
|
|
||||||
|
pub struct Window {
|
||||||
|
_instance: wgpu::Instance,
|
||||||
|
surface: wgpu::Surface,
|
||||||
|
_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,
|
||||||
|
scale_mode: ScaleMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Window {
|
||||||
|
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(
|
||||||
|
(320. * window_config.scale).round() as u32,
|
||||||
|
(240. * window_config.scale).round() as u32,
|
||||||
|
))
|
||||||
|
.with_min_inner_size(PhysicalSize::new(320u32, 240))
|
||||||
|
.with_title("MicroW8")
|
||||||
|
.with_fullscreen(if window_config.fullscreen {
|
||||||
|
Some(Fullscreen::Borderless(None))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.build(&event_loop)?;
|
||||||
|
|
||||||
|
window.set_cursor_visible(false);
|
||||||
|
|
||||||
|
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,
|
||||||
|
compatible_surface: Some(&surface),
|
||||||
|
force_fallback_adapter: false,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.ok_or_else(|| anyhow!("Request adapter failed"))?;
|
||||||
|
|
||||||
|
let (device, queue) = adapter
|
||||||
|
.request_device(&wgpu::DeviceDescriptor::default(), None)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let palette_screen_mode = PaletteScreenMode::new(&device);
|
||||||
|
|
||||||
|
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 filter: Box<dyn Filter> = create_filter(
|
||||||
|
&device,
|
||||||
|
&palette_screen_mode.screen_view,
|
||||||
|
window.inner_size(),
|
||||||
|
surface_config.format,
|
||||||
|
window_config.filter,
|
||||||
|
window_config.scale_mode,
|
||||||
|
);
|
||||||
|
|
||||||
|
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,
|
||||||
|
scale_mode: window_config.scale_mode,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pollster::block_on(create(window_config))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowImpl for Window {
|
||||||
|
fn begin_frame(&mut self) -> Input {
|
||||||
|
let mut reset = false;
|
||||||
|
self.event_loop.run_return(|event, _, control_flow| {
|
||||||
|
*control_flow = ControlFlow::WaitUntil(self.next_frame);
|
||||||
|
let mut new_filter = None;
|
||||||
|
match event {
|
||||||
|
Event::WindowEvent { event, .. } => match event {
|
||||||
|
WindowEvent::Resized(new_size) => {
|
||||||
|
self.surface_config.width = new_size.width;
|
||||||
|
self.surface_config.height = new_size.height;
|
||||||
|
self.surface.configure(&self.device, &self.surface_config);
|
||||||
|
self.filter.resize(&self.queue, new_size, self.scale_mode);
|
||||||
|
}
|
||||||
|
WindowEvent::CloseRequested => {
|
||||||
|
self.is_open = false;
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
}
|
||||||
|
WindowEvent::KeyboardInput { input, .. } => {
|
||||||
|
fn gamepad_button(input: &winit::event::KeyboardInput) -> u8 {
|
||||||
|
match input.scancode {
|
||||||
|
44 => 16,
|
||||||
|
45 => 32,
|
||||||
|
30 => 64,
|
||||||
|
31 => 128,
|
||||||
|
_ => match input.virtual_keycode {
|
||||||
|
Some(VirtualKeyCode::Up) => 1,
|
||||||
|
Some(VirtualKeyCode::Down) => 2,
|
||||||
|
Some(VirtualKeyCode::Left) => 4,
|
||||||
|
Some(VirtualKeyCode::Right) => 8,
|
||||||
|
_ => 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if input.state == winit::event::ElementState::Pressed {
|
||||||
|
match input.virtual_keycode {
|
||||||
|
Some(VirtualKeyCode::Escape) => {
|
||||||
|
self.is_open = false;
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
}
|
||||||
|
Some(VirtualKeyCode::F) => {
|
||||||
|
let fullscreen = if self.window.fullscreen().is_some() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(Fullscreen::Borderless(None))
|
||||||
|
};
|
||||||
|
self.is_fullscreen = fullscreen.is_some();
|
||||||
|
self.window.set_fullscreen(fullscreen);
|
||||||
|
}
|
||||||
|
Some(VirtualKeyCode::M) => {
|
||||||
|
self.scale_mode = match self.scale_mode {
|
||||||
|
ScaleMode::Fit => ScaleMode::Fill,
|
||||||
|
ScaleMode::Fill => ScaleMode::Fit,
|
||||||
|
};
|
||||||
|
self.filter.resize(
|
||||||
|
&self.queue,
|
||||||
|
PhysicalSize {
|
||||||
|
width: self.surface_config.width,
|
||||||
|
height: self.surface_config.height,
|
||||||
|
},
|
||||||
|
self.scale_mode,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Some(VirtualKeyCode::R) => reset = true,
|
||||||
|
Some(VirtualKeyCode::Key1) => new_filter = Some(1),
|
||||||
|
Some(VirtualKeyCode::Key2) => new_filter = Some(2),
|
||||||
|
Some(VirtualKeyCode::Key3) => new_filter = Some(3),
|
||||||
|
Some(VirtualKeyCode::Key4) => new_filter = Some(4),
|
||||||
|
Some(VirtualKeyCode::Key5) => new_filter = Some(5),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
self.gamepads[0] |= gamepad_button(&input);
|
||||||
|
} else {
|
||||||
|
self.gamepads[0] &= !gamepad_button(&input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
},
|
||||||
|
Event::RedrawEventsCleared => {
|
||||||
|
if Instant::now() >= self.next_frame
|
||||||
|
// workaround needed on Wayland until the next winit release
|
||||||
|
&& self.window.fullscreen().is_some() == self.is_fullscreen
|
||||||
|
{
|
||||||
|
*control_flow = ControlFlow::Exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
if let Some(new_filter) = new_filter {
|
||||||
|
self.filter = create_filter(
|
||||||
|
&self.device,
|
||||||
|
&self.palette_screen_mode.screen_view,
|
||||||
|
self.window.inner_size(),
|
||||||
|
self.surface_config.format,
|
||||||
|
new_filter,
|
||||||
|
self.scale_mode,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Input {
|
||||||
|
gamepads: self.gamepads,
|
||||||
|
reset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = self
|
||||||
|
.device
|
||||||
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
||||||
|
|
||||||
|
self.palette_screen_mode.resolve_screen(&mut encoder);
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
|
label: None,
|
||||||
|
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||||
|
view: &view,
|
||||||
|
resolve_target: None,
|
||||||
|
ops: wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Clear(wgpu::Color {
|
||||||
|
r: 0.0,
|
||||||
|
g: 0.0,
|
||||||
|
b: 0.0,
|
||||||
|
a: 1.0,
|
||||||
|
}),
|
||||||
|
store: true,
|
||||||
|
},
|
||||||
|
})],
|
||||||
|
depth_stencil_attachment: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
self.filter.render(&mut render_pass);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.queue.submit(std::iter::once(encoder.finish()));
|
||||||
|
output.present();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_open(&self) -> bool {
|
||||||
|
self.is_open
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_filter(
|
||||||
|
device: &wgpu::Device,
|
||||||
|
screen_texture: &wgpu::TextureView,
|
||||||
|
window_size: PhysicalSize<u32>,
|
||||||
|
surface_format: wgpu::TextureFormat,
|
||||||
|
filter: u32,
|
||||||
|
scale_mode: ScaleMode,
|
||||||
|
) -> Box<dyn Filter> {
|
||||||
|
match filter {
|
||||||
|
1 => Box::new(SquareFilter::new(
|
||||||
|
device,
|
||||||
|
screen_texture,
|
||||||
|
window_size,
|
||||||
|
surface_format,
|
||||||
|
scale_mode,
|
||||||
|
)),
|
||||||
|
2 => Box::new(FastCrtFilter::new(
|
||||||
|
device,
|
||||||
|
screen_texture,
|
||||||
|
window_size,
|
||||||
|
surface_format,
|
||||||
|
false,
|
||||||
|
scale_mode,
|
||||||
|
)),
|
||||||
|
3 => Box::new(CrtFilter::new(
|
||||||
|
device,
|
||||||
|
screen_texture,
|
||||||
|
window_size,
|
||||||
|
surface_format,
|
||||||
|
scale_mode,
|
||||||
|
)),
|
||||||
|
4 => Box::new(FastCrtFilter::new(
|
||||||
|
device,
|
||||||
|
screen_texture,
|
||||||
|
window_size,
|
||||||
|
surface_format,
|
||||||
|
true,
|
||||||
|
scale_mode,
|
||||||
|
)),
|
||||||
|
_ => Box::new(AutoCrtFilter::new(
|
||||||
|
device,
|
||||||
|
screen_texture,
|
||||||
|
window_size,
|
||||||
|
surface_format,
|
||||||
|
scale_mode,
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Filter {
|
||||||
|
fn resize(&mut self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>, scale_mode: ScaleMode);
|
||||||
|
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,
|
||||||
|
scale_mode: ScaleMode,
|
||||||
|
) -> AutoCrtFilter {
|
||||||
|
let small = CrtFilter::new(device, screen, resolution, surface_format, scale_mode);
|
||||||
|
let large =
|
||||||
|
FastCrtFilter::new(device, screen, resolution, surface_format, true, scale_mode);
|
||||||
|
AutoCrtFilter {
|
||||||
|
small,
|
||||||
|
large,
|
||||||
|
resolution,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Filter for AutoCrtFilter {
|
||||||
|
fn resize(&mut self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>, scale_mode: ScaleMode) {
|
||||||
|
self.small.resize(queue, new_size, scale_mode);
|
||||||
|
self.large.resize(queue, new_size, scale_mode);
|
||||||
|
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,
|
||||||
|
screen_view: wgpu::TextureView,
|
||||||
|
bind_group: wgpu::BindGroup,
|
||||||
|
pipeline: wgpu::RenderPipeline,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaletteScreenMode {
|
||||||
|
fn new(device: &wgpu::Device) -> PaletteScreenMode {
|
||||||
|
let framebuffer_texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||||
|
size: wgpu::Extent3d {
|
||||||
|
width: 320,
|
||||||
|
height: 240,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
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 {
|
||||||
|
size: wgpu::Extent3d {
|
||||||
|
width: 256,
|
||||||
|
height: 1,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D1,
|
||||||
|
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 {
|
||||||
|
size: wgpu::Extent3d {
|
||||||
|
width: 320,
|
||||||
|
height: 240,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||||
|
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||||
|
label: None,
|
||||||
|
view_formats: &[],
|
||||||
|
});
|
||||||
|
|
||||||
|
let framebuffer_texture_view =
|
||||||
|
framebuffer_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
let palette_texture_view =
|
||||||
|
palette_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
let screen_texture_view =
|
||||||
|
screen_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
|
||||||
|
let palette_bind_group_layout =
|
||||||
|
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Texture {
|
||||||
|
multisampled: false,
|
||||||
|
view_dimension: wgpu::TextureViewDimension::D2,
|
||||||
|
sample_type: wgpu::TextureSampleType::Uint,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 1,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Texture {
|
||||||
|
multisampled: false,
|
||||||
|
view_dimension: wgpu::TextureViewDimension::D1,
|
||||||
|
sample_type: wgpu::TextureSampleType::Float { filterable: false },
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
label: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
let palette_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
layout: &palette_bind_group_layout,
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: wgpu::BindingResource::TextureView(&framebuffer_texture_view),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 1,
|
||||||
|
resource: wgpu::BindingResource::TextureView(&palette_texture_view),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
label: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
let palette_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
|
label: None,
|
||||||
|
source: wgpu::ShaderSource::Wgsl(include_str!("palette.wgsl").into()),
|
||||||
|
});
|
||||||
|
|
||||||
|
let palette_pipeline_layout =
|
||||||
|
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
|
label: None,
|
||||||
|
bind_group_layouts: &[&palette_bind_group_layout],
|
||||||
|
push_constant_ranges: &[],
|
||||||
|
});
|
||||||
|
|
||||||
|
let palette_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||||
|
label: None,
|
||||||
|
layout: Some(&palette_pipeline_layout),
|
||||||
|
vertex: wgpu::VertexState {
|
||||||
|
module: &palette_shader,
|
||||||
|
entry_point: "vs_main",
|
||||||
|
buffers: &[],
|
||||||
|
},
|
||||||
|
fragment: Some(wgpu::FragmentState {
|
||||||
|
module: &palette_shader,
|
||||||
|
entry_point: "fs_main",
|
||||||
|
targets: &[Some(wgpu::ColorTargetState {
|
||||||
|
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||||
|
blend: None,
|
||||||
|
write_mask: wgpu::ColorWrites::ALL,
|
||||||
|
})],
|
||||||
|
}),
|
||||||
|
primitive: Default::default(),
|
||||||
|
depth_stencil: None,
|
||||||
|
multisample: Default::default(),
|
||||||
|
multiview: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
PaletteScreenMode {
|
||||||
|
framebuffer: framebuffer_texture,
|
||||||
|
palette: palette_texture,
|
||||||
|
screen_view: screen_texture_view,
|
||||||
|
bind_group: palette_bind_group,
|
||||||
|
pipeline: palette_pipeline,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_framebuffer(&self, queue: &wgpu::Queue, pixels: &[u8]) {
|
||||||
|
queue.write_texture(
|
||||||
|
wgpu::ImageCopyTexture {
|
||||||
|
texture: &self.framebuffer,
|
||||||
|
mip_level: 0,
|
||||||
|
origin: wgpu::Origin3d::ZERO,
|
||||||
|
aspect: wgpu::TextureAspect::All,
|
||||||
|
},
|
||||||
|
&bytemuck::cast_slice(pixels),
|
||||||
|
wgpu::ImageDataLayout {
|
||||||
|
offset: 0,
|
||||||
|
bytes_per_row: Some(320),
|
||||||
|
rows_per_image: None,
|
||||||
|
},
|
||||||
|
wgpu::Extent3d {
|
||||||
|
width: 320,
|
||||||
|
height: 240,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_palette(&self, queue: &wgpu::Queue, palette: &[u8]) {
|
||||||
|
queue.write_texture(
|
||||||
|
wgpu::ImageCopyTexture {
|
||||||
|
texture: &self.palette,
|
||||||
|
mip_level: 0,
|
||||||
|
origin: wgpu::Origin3d::ZERO,
|
||||||
|
aspect: wgpu::TextureAspect::All,
|
||||||
|
},
|
||||||
|
&bytemuck::cast_slice(palette),
|
||||||
|
wgpu::ImageDataLayout {
|
||||||
|
offset: 0,
|
||||||
|
bytes_per_row: Some(256 * 4),
|
||||||
|
rows_per_image: None,
|
||||||
|
},
|
||||||
|
wgpu::Extent3d {
|
||||||
|
width: 256,
|
||||||
|
height: 1,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_screen(&self, encoder: &mut wgpu::CommandEncoder) {
|
||||||
|
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
|
label: None,
|
||||||
|
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||||
|
view: &self.screen_view,
|
||||||
|
resolve_target: None,
|
||||||
|
ops: wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Load,
|
||||||
|
store: true,
|
||||||
|
},
|
||||||
|
})],
|
||||||
|
depth_stencil_attachment: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
render_pass.set_pipeline(&self.pipeline);
|
||||||
|
render_pass.set_bind_group(0, &self.bind_group, &[]);
|
||||||
|
render_pass.draw(0..3, 0..1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
struct VertexOutput {
|
||||||
|
@builtin(position) clip_position: vec4<f32>,
|
||||||
|
@location(0) tex_coords: vec2<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
@vertex
|
||||||
|
fn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput {
|
||||||
|
var out: VertexOutput;
|
||||||
|
let x = (1.0 - f32(vertex_index)) * 3.0;
|
||||||
|
let y = f32(vertex_index & 1u) * 3.0 - 1.0;
|
||||||
|
out.clip_position = vec4<f32>(x, y, 0.0, 1.0);
|
||||||
|
out.tex_coords = vec2<f32>((x + 1.0) * 160.0, (y + 1.0) * 120.0);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@group(0) @binding(0) var framebuffer_texture: texture_2d<u32>;
|
||||||
|
@group(0) @binding(1) var palette_texture: texture_1d<f32>;
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
|
let texel = vec2<i32>(floor(in.tex_coords));
|
||||||
|
let index = textureLoad(framebuffer_texture, texel, 0).r;
|
||||||
|
return textureLoad(palette_texture, i32(index), 0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
use winit::dpi::PhysicalSize;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub enum ScaleMode {
|
||||||
|
Fit,
|
||||||
|
Fill,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ScaleMode {
|
||||||
|
fn default() -> ScaleMode {
|
||||||
|
ScaleMode::Fit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScaleMode {
|
||||||
|
pub fn texture_scale_from_resolution(&self, res: PhysicalSize<u32>) -> [f32; 4] {
|
||||||
|
let scale = match self {
|
||||||
|
ScaleMode::Fit => ((res.width as f32) / 160.0).min((res.height as f32) / 120.0),
|
||||||
|
ScaleMode::Fill => ((res.width as f32) / 160.0).max((res.height as f32) / 120.0),
|
||||||
|
};
|
||||||
|
[
|
||||||
|
scale / res.width as f32,
|
||||||
|
scale / res.height as f32,
|
||||||
|
2.0 / scale,
|
||||||
|
0.0,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
use wgpu::util::DeviceExt;
|
||||||
|
use winit::dpi::PhysicalSize;
|
||||||
|
|
||||||
|
use super::{scale_mode::ScaleMode, Filter};
|
||||||
|
|
||||||
|
pub struct SquareFilter {
|
||||||
|
uniform_buffer: wgpu::Buffer,
|
||||||
|
bind_group: wgpu::BindGroup,
|
||||||
|
pipeline: wgpu::RenderPipeline,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SquareFilter {
|
||||||
|
pub fn new(
|
||||||
|
device: &wgpu::Device,
|
||||||
|
screen: &wgpu::TextureView,
|
||||||
|
resolution: PhysicalSize<u32>,
|
||||||
|
surface_format: wgpu::TextureFormat,
|
||||||
|
scale_mode: ScaleMode,
|
||||||
|
) -> SquareFilter {
|
||||||
|
let uniforms = Uniforms {
|
||||||
|
texture_scale: scale_mode.texture_scale_from_resolution(resolution),
|
||||||
|
};
|
||||||
|
|
||||||
|
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: None,
|
||||||
|
contents: bytemuck::cast_slice(&[uniforms]),
|
||||||
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
|
});
|
||||||
|
|
||||||
|
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Texture {
|
||||||
|
multisampled: false,
|
||||||
|
view_dimension: wgpu::TextureViewDimension::D2,
|
||||||
|
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 1,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 2,
|
||||||
|
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Buffer {
|
||||||
|
ty: wgpu::BufferBindingType::Uniform,
|
||||||
|
has_dynamic_offset: false,
|
||||||
|
min_binding_size: None,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
label: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||||
|
mag_filter: wgpu::FilterMode::Linear,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
layout: &bind_group_layout,
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: wgpu::BindingResource::TextureView(&screen),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 1,
|
||||||
|
resource: wgpu::BindingResource::Sampler(&sampler),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 2,
|
||||||
|
resource: uniform_buffer.as_entire_binding(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
label: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
|
label: None,
|
||||||
|
source: wgpu::ShaderSource::Wgsl(include_str!("square.wgsl").into()),
|
||||||
|
});
|
||||||
|
|
||||||
|
let render_pipeline_layout =
|
||||||
|
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
|
label: None,
|
||||||
|
bind_group_layouts: &[&bind_group_layout],
|
||||||
|
push_constant_ranges: &[],
|
||||||
|
});
|
||||||
|
|
||||||
|
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||||
|
label: None,
|
||||||
|
layout: Some(&render_pipeline_layout),
|
||||||
|
vertex: wgpu::VertexState {
|
||||||
|
module: &shader,
|
||||||
|
entry_point: "vs_main",
|
||||||
|
buffers: &[],
|
||||||
|
},
|
||||||
|
fragment: Some(wgpu::FragmentState {
|
||||||
|
module: &shader,
|
||||||
|
entry_point: "fs_main",
|
||||||
|
targets: &[Some(wgpu::ColorTargetState {
|
||||||
|
format: surface_format,
|
||||||
|
blend: None,
|
||||||
|
write_mask: wgpu::ColorWrites::ALL,
|
||||||
|
})],
|
||||||
|
}),
|
||||||
|
primitive: Default::default(),
|
||||||
|
depth_stencil: None,
|
||||||
|
multisample: Default::default(),
|
||||||
|
multiview: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
SquareFilter {
|
||||||
|
uniform_buffer,
|
||||||
|
bind_group,
|
||||||
|
pipeline: render_pipeline,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Filter for SquareFilter {
|
||||||
|
fn resize(&mut self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>, scale_mode: ScaleMode) {
|
||||||
|
let uniforms = Uniforms {
|
||||||
|
texture_scale: scale_mode.texture_scale_from_resolution(new_size),
|
||||||
|
};
|
||||||
|
queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[uniforms]));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) {
|
||||||
|
render_pass.set_pipeline(&self.pipeline);
|
||||||
|
render_pass.set_bind_group(0, &self.bind_group, &[]);
|
||||||
|
render_pass.draw(0..6, 0..1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
|
struct Uniforms {
|
||||||
|
texture_scale: [f32; 4],
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
struct VertexOutput {
|
||||||
|
@builtin(position) clip_position: vec4<f32>,
|
||||||
|
@location(0) tex_coords: vec2<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Uniforms {
|
||||||
|
texture_scale: vec4<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
@group(0) @binding(2) var<uniform> uniforms: Uniforms;
|
||||||
|
|
||||||
|
@vertex
|
||||||
|
fn vs_main(
|
||||||
|
@builtin(vertex_index) in_vertex_index: u32,
|
||||||
|
) -> VertexOutput {
|
||||||
|
var out: VertexOutput;
|
||||||
|
let i = in_vertex_index / 3u + in_vertex_index % 3u;
|
||||||
|
let x = 0.0 + f32(i % 2u) * 320.0;
|
||||||
|
let y = 0.0 + f32(i / 2u) * 240.0;
|
||||||
|
out.clip_position = vec4<f32>((vec2<f32>(x, y) - vec2<f32>(160.0, 120.0)) * uniforms.texture_scale.xy, 0.0, 1.0);
|
||||||
|
out.tex_coords = vec2<f32>(x, y);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@group(0) @binding(0) var screen_texture: texture_2d<f32>;
|
||||||
|
@group(0) @binding(1) var linear_sampler: sampler;
|
||||||
|
|
||||||
|
fn aa_tex_coord(c: f32) -> f32 {
|
||||||
|
let low = c - uniforms.texture_scale.z * 0.5;
|
||||||
|
let high = c + uniforms.texture_scale.z * 0.5;
|
||||||
|
let base = floor(low);
|
||||||
|
let center = base + 0.5;
|
||||||
|
let next = base + 1.0;
|
||||||
|
if high > next {
|
||||||
|
return center + (high - next) / (high - low);
|
||||||
|
} else {
|
||||||
|
return center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
|
return textureSample(screen_texture, linear_sampler, vec2<f32>(aa_tex_coord(in.tex_coords.x), aa_tex_coord(in.tex_coords.y)) / vec2<f32>(320.0, 240.0));
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use gpu::scale_mode::ScaleMode;
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
mod cpu;
|
||||||
|
mod gpu;
|
||||||
|
|
||||||
|
pub struct Window {
|
||||||
|
inner: Box<dyn WindowImpl>,
|
||||||
|
fps_counter: Option<FpsCounter>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FpsCounter {
|
||||||
|
start: Instant,
|
||||||
|
num_frames: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Window {
|
||||||
|
pub fn new(mut config: WindowConfig) -> Result<Window> {
|
||||||
|
let fps_counter = if config.fps_counter {
|
||||||
|
Some(FpsCounter {
|
||||||
|
start: Instant::now(),
|
||||||
|
num_frames: 0,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
config.scale = config.scale.max(1.).min(20.);
|
||||||
|
if config.enable_gpu {
|
||||||
|
match gpu::Window::new(config) {
|
||||||
|
Ok(window) => {
|
||||||
|
return Ok(Window {
|
||||||
|
inner: Box::new(window),
|
||||||
|
fps_counter,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Err(err) => eprintln!(
|
||||||
|
"Failed to create gpu window: {}\nFalling back tp cpu window",
|
||||||
|
err
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cpu::Window::new().map(|window| Window {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct WindowConfig {
|
||||||
|
enable_gpu: bool,
|
||||||
|
filter: u32,
|
||||||
|
fullscreen: bool,
|
||||||
|
fps_counter: bool,
|
||||||
|
scale: f32,
|
||||||
|
scale_mode: ScaleMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for WindowConfig {
|
||||||
|
fn default() -> WindowConfig {
|
||||||
|
WindowConfig {
|
||||||
|
enable_gpu: true,
|
||||||
|
filter: 5,
|
||||||
|
fullscreen: false,
|
||||||
|
fps_counter: false,
|
||||||
|
scale: 2.,
|
||||||
|
scale_mode: ScaleMode::Fit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
self.scale = args
|
||||||
|
.opt_value_from_str("--scale")
|
||||||
|
.unwrap()
|
||||||
|
.unwrap_or(self.scale);
|
||||||
|
if args.contains("--scale-fill") {
|
||||||
|
self.scale_mode = ScaleMode::Fill;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
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 mut start_time = Instant::now();
|
||||||
|
|
||||||
|
let mut palette = vec![0u32; 256];
|
||||||
|
for i in 0..256 {
|
||||||
|
let v = i & 15;
|
||||||
|
let r = ((i >> 2) & 12) * v;
|
||||||
|
let g = ((i >> 3) & 12) * v;
|
||||||
|
let b = ((i >> 4) & 12) * v;
|
||||||
|
palette[i as usize] = r + (g << 8) + (b << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut fps_start = Instant::now();
|
||||||
|
let mut fps_counter = 0;
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
println!("{:.1} fps", fps_counter as f32 / elapsed);
|
||||||
|
fps_start = Instant::now();
|
||||||
|
fps_counter = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_frame(framebuffer: &mut [u8], time: f32) {
|
||||||
|
for x in 0..320 {
|
||||||
|
let xr = x as f32 - 160.0;
|
||||||
|
for y in 0..240 {
|
||||||
|
let yr = y as f32 - 120.0;
|
||||||
|
let f = 8192.0 / (xr * xr + yr * yr);
|
||||||
|
let u = xr * f + 512.0 + time * 32.0;
|
||||||
|
let v = yr * f + time * 29.0;
|
||||||
|
let c = (u.floor() as i32 ^ v.floor() as i32) as u32;
|
||||||
|
framebuffer[x + y * 320] = c as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+2
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
rm -rf .parcel-cache && yarn parcel build src/run-web.html && cp dist/run-web.html ../src/
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<html>
|
||||||
|
<button onclick="go()">Go!</button>
|
||||||
|
<canvas id="screen" width="320" height="240"></canvas>
|
||||||
|
<video id="video"></video>
|
||||||
|
<script>
|
||||||
|
function go() {
|
||||||
|
let audioContext = new AudioContext({sampleRate: 44100});
|
||||||
|
|
||||||
|
let oscillator = new OscillatorNode(audioContext);
|
||||||
|
let gain = new GainNode(audioContext, {gain: 1});
|
||||||
|
oscillator.connect(gain);
|
||||||
|
gain.connect(audioContext.destination);
|
||||||
|
for(let i = 0; i < 8; ++i ) {
|
||||||
|
gain.gain.setValueAtTime(1, i / 2);
|
||||||
|
gain.gain.setValueAtTime(0, i / 2 + 0.3);
|
||||||
|
}
|
||||||
|
oscillator.start();
|
||||||
|
oscillator.stop(4);
|
||||||
|
|
||||||
|
let screen = document.getElementById('screen');
|
||||||
|
let context = screen.getContext('2d');
|
||||||
|
let startTime = Date.now();
|
||||||
|
let drawFrame = () => {
|
||||||
|
let time = Date.now() - startTime;
|
||||||
|
context.fillStyle = 'white';
|
||||||
|
context.fillRect(0, 0, 320, 240);
|
||||||
|
if(time < 4000) {
|
||||||
|
if(time % 500 < 300) {
|
||||||
|
context.fillStyle = 'black';
|
||||||
|
context.fillRect(time / 15, 50, 50, 50);
|
||||||
|
}
|
||||||
|
window.requestAnimationFrame(drawFrame);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
drawFrame();
|
||||||
|
|
||||||
|
let stream = screen.captureStream();
|
||||||
|
let audioStreamNode = audioContext.createMediaStreamDestination();
|
||||||
|
gain.connect(audioStreamNode);
|
||||||
|
stream.addTrack(audioStreamNode.stream.getAudioTracks()[0]);
|
||||||
|
let recorder = new MediaRecorder(stream, { mimeType: 'video/webm' });
|
||||||
|
|
||||||
|
let chunks = [];
|
||||||
|
recorder.ondataavailable = e => chunks.push(e.data);
|
||||||
|
recorder.onstop = () => {
|
||||||
|
let blob = new Blob(chunks, {type: 'video/webm'});
|
||||||
|
let url = URL.createObjectURL(blob);
|
||||||
|
let video = document.getElementById('video');
|
||||||
|
video.src = url;
|
||||||
|
video.play();
|
||||||
|
};
|
||||||
|
|
||||||
|
recorder.start();
|
||||||
|
setTimeout(() => recorder.stop(), 4000);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
+2
-1
@@ -4,5 +4,6 @@
|
|||||||
"@parcel/optimizer-data-url": "^2.0.0",
|
"@parcel/optimizer-data-url": "^2.0.0",
|
||||||
"@parcel/transformer-inline-string": "^2.0.0",
|
"@parcel/transformer-inline-string": "^2.0.0",
|
||||||
"parcel": "^2.0.0"
|
"parcel": "^2.0.0"
|
||||||
}
|
},
|
||||||
|
"dependencies": {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
rm -rf .parcel-cache && yarn parcel src/index.html
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
let U8 = (...a) => new Uint8Array(...a);
|
||||||
|
class APU extends AudioWorkletProcessor {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.sampleIndex = 0;
|
||||||
|
this.currentTime = 0;
|
||||||
|
this.isFirstMessage = true;
|
||||||
|
this.pendingUpdates = [];
|
||||||
|
this.port.onmessage = (ev) => {
|
||||||
|
if(this.memory) {
|
||||||
|
if(this.isFirstMessage)
|
||||||
|
{
|
||||||
|
this.currentTime += (ev.data.t - this.currentTime) / 8;
|
||||||
|
this.isFirstMessage = false;
|
||||||
|
}
|
||||||
|
this.pendingUpdates.push(ev.data);
|
||||||
|
} else {
|
||||||
|
this.load(ev.data[0], ev.data[1]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async load(platform_data, data) {
|
||||||
|
let memory = new WebAssembly.Memory({ initial: 4, maximum: 4 });
|
||||||
|
|
||||||
|
let importObject = {
|
||||||
|
env: {
|
||||||
|
memory
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let n of ['acos', 'asin', 'atan', 'atan2', 'cos', 'exp', 'log', 'sin', 'tan', 'pow']) {
|
||||||
|
importObject.env[n] = Math[n];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 9; i < 64; ++i) {
|
||||||
|
importObject.env['reserved' + i] = () => { };
|
||||||
|
}
|
||||||
|
|
||||||
|
let logLine = '';
|
||||||
|
importObject.env['logChar'] = (c) => {
|
||||||
|
if(c == 10) {
|
||||||
|
console.log(logLine);
|
||||||
|
logLine = '';
|
||||||
|
} else {
|
||||||
|
logLine += String.fromCharCode(c);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let i = 0; i < 16; ++i) {
|
||||||
|
importObject.env['g_reserved' + i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let instantiate = async (data) => (await WebAssembly.instantiate(data, importObject)).instance;
|
||||||
|
|
||||||
|
let platform_instance = await instantiate(platform_data);
|
||||||
|
|
||||||
|
for (let name in platform_instance.exports) {
|
||||||
|
importObject.env[name] = platform_instance.exports[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
let instance = await instantiate(data);
|
||||||
|
|
||||||
|
this.memory = memory;
|
||||||
|
|
||||||
|
this.snd = instance.exports.snd || platform_instance.exports.sndGes;
|
||||||
|
|
||||||
|
this.port.postMessage(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
process(inputs, outputs, parameters) {
|
||||||
|
this.isFirstMessage = true;
|
||||||
|
if(this.snd) {
|
||||||
|
while(this.pendingUpdates.length > 0 && this.pendingUpdates[0].t <= this.currentTime) {
|
||||||
|
U8(this.memory.buffer, 80, 32).set(U8(this.pendingUpdates.shift().r));
|
||||||
|
}
|
||||||
|
let u32Mem = new Uint32Array(this.memory.buffer);
|
||||||
|
u32Mem[16] = this.currentTime;
|
||||||
|
let channels = outputs[0];
|
||||||
|
let index = this.sampleIndex;
|
||||||
|
let numSamples = channels[0].length;
|
||||||
|
for(let i = 0; i < numSamples; ++i) {
|
||||||
|
channels[0][i] = this.snd(index++);
|
||||||
|
channels[1][i] = this.snd(index++);
|
||||||
|
}
|
||||||
|
this.sampleIndex = index & 0xffffffff;
|
||||||
|
this.currentTime += numSamples / 44.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registerProcessor('apu', APU);
|
||||||
+6
-5
@@ -10,13 +10,14 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="uw8">
|
<div id="uw8">
|
||||||
<a href="https://exoticorn.github.io/microw8">MicroW8</a> 0.1.1
|
<a href="https://exoticorn.github.io/microw8">MicroW8</a> 0.4.1
|
||||||
</div>
|
</div>
|
||||||
<div id="centered">
|
<div id="centered">
|
||||||
<canvas id="screen" width="320" height="240">
|
<canvas class="screen" id="screen" width="320" height="240">
|
||||||
</canvas>
|
</canvas>
|
||||||
<div id="timer" hidden="true"></div>
|
<button class="screen" id="start" style="display:none">Click to start</button>
|
||||||
<div id="message"></div>
|
<div id="timer" hidden="true"></div>
|
||||||
|
<div id="message"></div>
|
||||||
<button id="cartButton" style="visibility:hidden">Load cart...</button>
|
<button id="cartButton" style="visibility:hidden">Load cart...</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="footer">
|
<div id="footer">
|
||||||
@@ -26,4 +27,4 @@
|
|||||||
<script type="module">
|
<script type="module">
|
||||||
import "./main.js";
|
import "./main.js";
|
||||||
</script>
|
</script>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user