mirror of
https://github.com/exoticorn/microw8.git
synced 2026-01-20 19:26:43 +01:00
Compare commits
2 Commits
master
...
35aeebd0b6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35aeebd0b6 | ||
|
|
937ccf60c9 |
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -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@v4
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: uw8-${{ matrix.build }}
|
name: uw8-${{ matrix.build }}
|
||||||
path: target/release/${{ matrix.exe }}
|
path: target/release/${{ matrix.exe }}
|
||||||
|
|||||||
764
Cargo.lock
generated
764
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "uw8"
|
name = "uw8"
|
||||||
version = "0.4.1"
|
version = "0.3.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
|
||||||
@@ -28,4 +28,4 @@ tokio-stream = { version = "0.1.15", features = ["sync"], optional = true }
|
|||||||
webbrowser = { version = "0.8.13", optional = true }
|
webbrowser = { version = "0.8.13", optional = true }
|
||||||
ansi_term = "0.12.1"
|
ansi_term = "0.12.1"
|
||||||
cpal = { version = "0.15.3", optional = true }
|
cpal = { version = "0.15.3", optional = true }
|
||||||
rubato = { version = "0.12.0", optional = true }
|
rubato = { version = "0.15.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.4.1/microw8-0.4.1-linux.tgz)
|
* [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.4.1/microw8-0.4.1-macos.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.4.1/microw8-0.4.1-windows.zip)
|
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.3.0/microw8-0.3.0-windows.zip)
|
||||||
|
|
||||||
The download includes
|
The download includes
|
||||||
|
|
||||||
|
|||||||
@@ -1,254 +0,0 @@
|
|||||||
/*
|
|
||||||
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,51 +0,0 @@
|
|||||||
/*
|
|
||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
/*
|
|
||||||
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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
6
examples/tinygo/build.sh
Executable file
6
examples/tinygo/build.sh
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
tinygo build -o cart.wasm -target target.json ./main.go
|
||||||
|
uw8 filter-exports cart.wasm cart.wasm && \
|
||||||
|
#wasm-opt -Oz --fast-math --strip-producers -o cart.wasm cart.wasm && \
|
||||||
|
uw8 pack -l 9 cart.wasm cart.uw8
|
||||||
38
examples/tinygo/main.go
Normal file
38
examples/tinygo/main.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:wasm-module env
|
||||||
|
//export atan2
|
||||||
|
func atan2(x, y float32) float32
|
||||||
|
|
||||||
|
//go:wasm-module env
|
||||||
|
//export time
|
||||||
|
func time() float32
|
||||||
|
|
||||||
|
func sqrt(v float32) float32 {
|
||||||
|
return float32(math.Sqrt(float64(v)))
|
||||||
|
}
|
||||||
|
|
||||||
|
var FRAMEBUFFER = (*[320 * 240]byte)(unsafe.Pointer(uintptr(120)))
|
||||||
|
|
||||||
|
//export upd
|
||||||
|
func upd() {
|
||||||
|
var i int
|
||||||
|
for i < 320*240 {
|
||||||
|
t := time() * 63.0
|
||||||
|
x := float32(i%320 - 160)
|
||||||
|
y := float32(i/320 - 120)
|
||||||
|
d := float32(40000.0) / sqrt(x*x+y*y)
|
||||||
|
u := atan2(x, y) * 512.0 / 3.141
|
||||||
|
c := uint8((int(d+t*2.0) ^ int(u+t)) >> 4)
|
||||||
|
FRAMEBUFFER[i] = c
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
}
|
||||||
23
examples/tinygo/target.json
Normal file
23
examples/tinygo/target.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"llvm-target": "wasm32--wasi",
|
||||||
|
"build-tags": [ "tinygo.wasm" ],
|
||||||
|
"goos": "js",
|
||||||
|
"goarch": "wasm",
|
||||||
|
"linker": "wasm-ld",
|
||||||
|
"libc": "wasi-libc",
|
||||||
|
"cflags": [
|
||||||
|
"--target=wasm32--wasi",
|
||||||
|
"--sysroot={root}/lib/wasi-libc/sysroot",
|
||||||
|
"-Oz"
|
||||||
|
],
|
||||||
|
"ldflags": [
|
||||||
|
"--no-entry",
|
||||||
|
"--export-all",
|
||||||
|
"--import-memory",
|
||||||
|
"--initial-memory=262144",
|
||||||
|
"--global-base=81920",
|
||||||
|
"-zstack-size=4096",
|
||||||
|
"--strip-all"
|
||||||
|
],
|
||||||
|
"wasm-abi": "js"
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -31,19 +31,19 @@ Examplers for older versions:
|
|||||||
|
|
||||||
## Versions
|
## Versions
|
||||||
|
|
||||||
### v0.4.1
|
### v0.3.0
|
||||||
|
|
||||||
* [Web runtime](../v0.4.1)
|
* [Web runtime](v0.3.0)
|
||||||
* [Linux](https://github.com/exoticorn/microw8/releases/download/v0.4.1/microw8-0.4.1-linux.tgz)
|
* [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.4.1/microw8-0.4.1-macos.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.4.1/microw8-0.4.1-windows.zip)
|
* [Windows](https://github.com/exoticorn/microw8/releases/download/v0.3.0/microw8-0.3.0-windows.zip)
|
||||||
|
|
||||||
Changes:
|
Changes:
|
||||||
|
|
||||||
* Windows: fix bad/inconsistent frame rate
|
* add blitSprite and grabSprite API calls
|
||||||
* fix choppy sound on devices with sample rates != 44100 kHz
|
* add support for integer scaling up to 16x for printing text
|
||||||
* add scale mode 'fill' option
|
* fix incompatibility with sound devices only offering 16bit audio formats
|
||||||
|
* add support for br_table instruction in packed carts
|
||||||
|
|
||||||
### Older versions
|
### Older versions
|
||||||
|
|
||||||
|
|||||||
@@ -468,7 +468,6 @@ when using the native runtime:
|
|||||||
* `--no-gpu`: Force old cpu-only window code
|
* `--no-gpu`: Force old cpu-only window code
|
||||||
* `--filter FILTER`: Select an upscale filter at startup
|
* `--filter FILTER`: Select an upscale filter at startup
|
||||||
* `--fullscreen`: Start in fullscreen mode
|
* `--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.
|
Note that the cpu-only window does not support fullscreen nor upscale filters.
|
||||||
|
|
||||||
@@ -485,7 +484,7 @@ The upscale filter options are:
|
|||||||
5, auto_crt (default) : ss_crt below 960x720, chromatic_crt otherwise
|
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.
|
You can switch the upscale filter at any time using the keys 1-5. You can toggle fullscreen with F.
|
||||||
|
|
||||||
## `uw8 pack`
|
## `uw8 pack`
|
||||||
|
|
||||||
@@ -595,7 +594,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 7 parameters (i32 or f32) where the
|
types with up to 5 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
|
||||||
|
|||||||
@@ -2,33 +2,6 @@
|
|||||||
description = "Versions"
|
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
|
### v0.3.0
|
||||||
|
|
||||||
* [Web runtime](../v0.3.0)
|
* [Web runtime](../v0.3.0)
|
||||||
|
|||||||
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.4.1">
|
<a href="v0.3.0">
|
||||||
<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>
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -395,8 +395,8 @@ fn init_sound(
|
|||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let rs = rubato::FftFixedIn::new(44100, sample_rate, 128, 1, 2)?;
|
let rs = rubato::FftFixedIn::new(44100, sample_rate, 128, 1, 2)?;
|
||||||
let input_buffers = rs.input_buffer_allocate();
|
let input_buffers = rs.input_buffer_allocate(true);
|
||||||
let output_buffers = rs.output_buffer_allocate();
|
let output_buffers = rs.output_buffer_allocate(true);
|
||||||
Some(Resampler {
|
Some(Resampler {
|
||||||
resampler: rs,
|
resampler: rs,
|
||||||
input_buffers,
|
input_buffers,
|
||||||
|
|||||||
@@ -1,789 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,234 +0,0 @@
|
|||||||
%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
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<snippet>
|
|
||||||
<content><![CDATA[
|
|
||||||
data ${1:address} {
|
|
||||||
${2:type}(
|
|
||||||
${3:values}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
]]></content>
|
|
||||||
<tabTrigger>datatype</tabTrigger>
|
|
||||||
<description>Data block definition</description>
|
|
||||||
</snippet>
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<snippet>
|
|
||||||
<content><![CDATA[
|
|
||||||
let inline ${1:variable} = ${2:value};
|
|
||||||
]]></content>
|
|
||||||
<tabTrigger>letinline</tabTrigger>
|
|
||||||
<description>Lazy variable declaration</description>
|
|
||||||
</snippet>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<snippet>
|
|
||||||
<content><![CDATA[
|
|
||||||
let lazy ${1:variable} = ${2:value};
|
|
||||||
]]></content>
|
|
||||||
<tabTrigger>letlazy</tabTrigger>
|
|
||||||
<description>Lazy variable declaration</description>
|
|
||||||
</snippet>
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<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>
|
|
||||||
@@ -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..8 {
|
for num_params in 0..6 {
|
||||||
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![];
|
||||||
|
|||||||
917
uw8-window/Cargo.lock
generated
917
uw8-window/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -6,11 +6,11 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
winit = "0.29.15"
|
||||||
env_logger = "0.11.3"
|
env_logger = "0.11.3"
|
||||||
winit = "0.28.6"
|
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
pico-args = "0.5"
|
pico-args = "0.5"
|
||||||
wgpu = "0.17"
|
wgpu = "0.19.3"
|
||||||
pollster = "0.3.0"
|
pollster = "0.3.0"
|
||||||
bytemuck = { version = "1.15", features = [ "derive" ] }
|
bytemuck = { version = "1.15", features = [ "derive" ] }
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
use winit::dpi::PhysicalSize;
|
use winit::dpi::PhysicalSize;
|
||||||
|
|
||||||
use super::{scale_mode::ScaleMode, Filter};
|
use super::Filter;
|
||||||
|
|
||||||
pub struct CrtFilter {
|
pub struct CrtFilter {
|
||||||
uniform_buffer: wgpu::Buffer,
|
uniform_buffer: wgpu::Buffer,
|
||||||
@@ -15,10 +15,9 @@ impl CrtFilter {
|
|||||||
screen: &wgpu::TextureView,
|
screen: &wgpu::TextureView,
|
||||||
resolution: PhysicalSize<u32>,
|
resolution: PhysicalSize<u32>,
|
||||||
surface_format: wgpu::TextureFormat,
|
surface_format: wgpu::TextureFormat,
|
||||||
scale_mode: ScaleMode,
|
|
||||||
) -> CrtFilter {
|
) -> CrtFilter {
|
||||||
let uniforms = Uniforms {
|
let uniforms = Uniforms {
|
||||||
texture_scale: scale_mode.texture_scale_from_resolution(resolution),
|
texture_scale: texture_scale_from_resolution(resolution),
|
||||||
};
|
};
|
||||||
|
|
||||||
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
@@ -113,9 +112,9 @@ impl CrtFilter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Filter for CrtFilter {
|
impl Filter for CrtFilter {
|
||||||
fn resize(&mut self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>, scale_mode: ScaleMode) {
|
fn resize(&mut self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>) {
|
||||||
let uniforms = Uniforms {
|
let uniforms = Uniforms {
|
||||||
texture_scale: scale_mode.texture_scale_from_resolution(new_size),
|
texture_scale: texture_scale_from_resolution(new_size),
|
||||||
};
|
};
|
||||||
queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[uniforms]));
|
queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[uniforms]));
|
||||||
}
|
}
|
||||||
@@ -127,6 +126,16 @@ impl Filter for CrtFilter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn texture_scale_from_resolution(res: PhysicalSize<u32>) -> [f32; 4] {
|
||||||
|
let scale = ((res.width as f32) / 160.0).min((res.height as f32) / 120.0);
|
||||||
|
[
|
||||||
|
res.width as f32 / scale,
|
||||||
|
res.height as f32 / scale,
|
||||||
|
2.0 / scale,
|
||||||
|
0.0,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
struct Uniforms {
|
struct Uniforms {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ fn vs_main(
|
|||||||
let i = in_vertex_index / 3u + in_vertex_index % 3u;
|
let i = in_vertex_index / 3u + in_vertex_index % 3u;
|
||||||
let x = -1.0 + f32(i % 2u) * 322.0;
|
let x = -1.0 + f32(i % 2u) * 322.0;
|
||||||
let y = -1.0 + f32(i / 2u) * 242.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.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);
|
out.tex_coords = vec2<f32>(x, y);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
use winit::dpi::PhysicalSize;
|
use winit::dpi::PhysicalSize;
|
||||||
|
|
||||||
use super::{scale_mode::ScaleMode, Filter};
|
use super::Filter;
|
||||||
|
|
||||||
pub struct FastCrtFilter {
|
pub struct FastCrtFilter {
|
||||||
uniform_buffer: wgpu::Buffer,
|
uniform_buffer: wgpu::Buffer,
|
||||||
@@ -16,10 +16,9 @@ impl FastCrtFilter {
|
|||||||
resolution: PhysicalSize<u32>,
|
resolution: PhysicalSize<u32>,
|
||||||
surface_format: wgpu::TextureFormat,
|
surface_format: wgpu::TextureFormat,
|
||||||
chromatic: bool,
|
chromatic: bool,
|
||||||
scale_mode: ScaleMode,
|
|
||||||
) -> FastCrtFilter {
|
) -> FastCrtFilter {
|
||||||
let uniforms = Uniforms {
|
let uniforms = Uniforms {
|
||||||
texture_scale: scale_mode.texture_scale_from_resolution(resolution),
|
texture_scale: texture_scale_from_resolution(resolution),
|
||||||
};
|
};
|
||||||
|
|
||||||
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
@@ -132,9 +131,9 @@ impl FastCrtFilter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Filter for FastCrtFilter {
|
impl Filter for FastCrtFilter {
|
||||||
fn resize(&mut self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>, scale_mode: ScaleMode) {
|
fn resize(&mut self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>) {
|
||||||
let uniforms = Uniforms {
|
let uniforms = Uniforms {
|
||||||
texture_scale: scale_mode.texture_scale_from_resolution(new_size),
|
texture_scale: texture_scale_from_resolution(new_size),
|
||||||
};
|
};
|
||||||
queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[uniforms]));
|
queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[uniforms]));
|
||||||
}
|
}
|
||||||
@@ -146,6 +145,16 @@ impl Filter for FastCrtFilter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn texture_scale_from_resolution(res: PhysicalSize<u32>) -> [f32; 4] {
|
||||||
|
let scale = ((res.width as f32) / 160.0).min((res.height as f32) / 120.0);
|
||||||
|
[
|
||||||
|
scale / res.width as f32,
|
||||||
|
scale / res.height as f32,
|
||||||
|
2.0 / scale,
|
||||||
|
0.0,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
struct Uniforms {
|
struct Uniforms {
|
||||||
|
|||||||
@@ -1,20 +1,18 @@
|
|||||||
use crate::{Input, WindowConfig, WindowImpl};
|
use crate::{Input, WindowConfig, WindowImpl};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use scale_mode::ScaleMode;
|
use std::{sync::Arc, time::Instant};
|
||||||
use std::time::Instant;
|
|
||||||
|
|
||||||
use winit::{
|
use winit::{
|
||||||
dpi::PhysicalSize,
|
dpi::PhysicalSize,
|
||||||
event::{Event, VirtualKeyCode, WindowEvent},
|
event::{Event, WindowEvent},
|
||||||
event_loop::{ControlFlow, EventLoop},
|
event_loop::{ControlFlow, EventLoop},
|
||||||
|
keyboard::{Key, KeyCode, NamedKey, PhysicalKey},
|
||||||
|
platform::pump_events::{EventLoopExtPumpEvents, PumpStatus},
|
||||||
window::{Fullscreen, WindowBuilder},
|
window::{Fullscreen, WindowBuilder},
|
||||||
};
|
};
|
||||||
|
|
||||||
use winit::platform::run_return::EventLoopExtRunReturn;
|
|
||||||
|
|
||||||
mod crt;
|
mod crt;
|
||||||
mod fast_crt;
|
mod fast_crt;
|
||||||
pub mod scale_mode;
|
|
||||||
mod square;
|
mod square;
|
||||||
|
|
||||||
use crt::CrtFilter;
|
use crt::CrtFilter;
|
||||||
@@ -23,7 +21,7 @@ use square::SquareFilter;
|
|||||||
|
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
_instance: wgpu::Instance,
|
_instance: wgpu::Instance,
|
||||||
surface: wgpu::Surface,
|
surface: wgpu::Surface<'static>,
|
||||||
_adapter: wgpu::Adapter,
|
_adapter: wgpu::Adapter,
|
||||||
device: wgpu::Device,
|
device: wgpu::Device,
|
||||||
queue: wgpu::Queue,
|
queue: wgpu::Queue,
|
||||||
@@ -31,18 +29,17 @@ pub struct Window {
|
|||||||
surface_config: wgpu::SurfaceConfiguration,
|
surface_config: wgpu::SurfaceConfiguration,
|
||||||
filter: Box<dyn Filter>,
|
filter: Box<dyn Filter>,
|
||||||
event_loop: EventLoop<()>,
|
event_loop: EventLoop<()>,
|
||||||
window: winit::window::Window,
|
window: Arc<winit::window::Window>,
|
||||||
gamepads: [u8; 4],
|
gamepads: [u8; 4],
|
||||||
next_frame: Instant,
|
next_frame: Instant,
|
||||||
is_fullscreen: bool,
|
is_fullscreen: bool,
|
||||||
is_open: bool,
|
is_open: bool,
|
||||||
scale_mode: ScaleMode,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
pub fn new(window_config: WindowConfig) -> Result<Window> {
|
pub fn new(window_config: WindowConfig) -> Result<Window> {
|
||||||
async fn create(window_config: WindowConfig) -> Result<Window> {
|
async fn create(window_config: WindowConfig) -> Result<Window> {
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new()?;
|
||||||
let window = WindowBuilder::new()
|
let window = WindowBuilder::new()
|
||||||
.with_inner_size(PhysicalSize::new(
|
.with_inner_size(PhysicalSize::new(
|
||||||
(320. * window_config.scale).round() as u32,
|
(320. * window_config.scale).round() as u32,
|
||||||
@@ -59,8 +56,10 @@ impl Window {
|
|||||||
|
|
||||||
window.set_cursor_visible(false);
|
window.set_cursor_visible(false);
|
||||||
|
|
||||||
|
let window = Arc::new(window);
|
||||||
|
|
||||||
let instance = wgpu::Instance::new(Default::default());
|
let instance = wgpu::Instance::new(Default::default());
|
||||||
let surface = unsafe { instance.create_surface(&window) }?;
|
let surface = instance.create_surface(window.clone())?;
|
||||||
let adapter = instance
|
let adapter = instance
|
||||||
.request_adapter(&wgpu::RequestAdapterOptions {
|
.request_adapter(&wgpu::RequestAdapterOptions {
|
||||||
power_preference: wgpu::PowerPreference::LowPower,
|
power_preference: wgpu::PowerPreference::LowPower,
|
||||||
@@ -93,7 +92,6 @@ impl Window {
|
|||||||
window.inner_size(),
|
window.inner_size(),
|
||||||
surface_config.format,
|
surface_config.format,
|
||||||
window_config.filter,
|
window_config.filter,
|
||||||
window_config.scale_mode,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
surface.configure(&device, &surface_config);
|
surface.configure(&device, &surface_config);
|
||||||
@@ -113,7 +111,6 @@ impl Window {
|
|||||||
next_frame: Instant::now(),
|
next_frame: Instant::now(),
|
||||||
is_fullscreen: window_config.fullscreen,
|
is_fullscreen: window_config.fullscreen,
|
||||||
is_open: true,
|
is_open: true,
|
||||||
scale_mode: window_config.scale_mode,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,8 +121,11 @@ impl Window {
|
|||||||
impl WindowImpl for Window {
|
impl WindowImpl for Window {
|
||||||
fn begin_frame(&mut self) -> Input {
|
fn begin_frame(&mut self) -> Input {
|
||||||
let mut reset = false;
|
let mut reset = false;
|
||||||
self.event_loop.run_return(|event, _, control_flow| {
|
self.event_loop
|
||||||
*control_flow = ControlFlow::WaitUntil(self.next_frame);
|
.set_control_flow(ControlFlow::WaitUntil(self.next_frame));
|
||||||
|
while self.is_open {
|
||||||
|
let timeout = self.next_frame.saturating_duration_since(Instant::now());
|
||||||
|
let status = self.event_loop.pump_events(Some(timeout), |event, elwt| {
|
||||||
let mut new_filter = None;
|
let mut new_filter = None;
|
||||||
match event {
|
match event {
|
||||||
Event::WindowEvent { event, .. } => match event {
|
Event::WindowEvent { event, .. } => match event {
|
||||||
@@ -133,35 +133,34 @@ impl WindowImpl for Window {
|
|||||||
self.surface_config.width = new_size.width;
|
self.surface_config.width = new_size.width;
|
||||||
self.surface_config.height = new_size.height;
|
self.surface_config.height = new_size.height;
|
||||||
self.surface.configure(&self.device, &self.surface_config);
|
self.surface.configure(&self.device, &self.surface_config);
|
||||||
self.filter.resize(&self.queue, new_size, self.scale_mode);
|
self.filter.resize(&self.queue, new_size);
|
||||||
}
|
}
|
||||||
WindowEvent::CloseRequested => {
|
WindowEvent::CloseRequested => {
|
||||||
self.is_open = false;
|
elwt.exit();
|
||||||
*control_flow = ControlFlow::Exit;
|
|
||||||
}
|
}
|
||||||
WindowEvent::KeyboardInput { input, .. } => {
|
WindowEvent::KeyboardInput { event, .. } => {
|
||||||
fn gamepad_button(input: &winit::event::KeyboardInput) -> u8 {
|
fn gamepad_button(input: &winit::event::KeyEvent) -> u8 {
|
||||||
match input.scancode {
|
match input.physical_key {
|
||||||
44 => 16,
|
PhysicalKey::Code(KeyCode::KeyZ) => 16,
|
||||||
45 => 32,
|
PhysicalKey::Code(KeyCode::KeyX) => 32,
|
||||||
30 => 64,
|
PhysicalKey::Code(KeyCode::KeyA) => 64,
|
||||||
31 => 128,
|
PhysicalKey::Code(KeyCode::KeyS) => 128,
|
||||||
_ => match input.virtual_keycode {
|
_ => match input.logical_key {
|
||||||
Some(VirtualKeyCode::Up) => 1,
|
Key::Named(NamedKey::ArrowUp) => 1,
|
||||||
Some(VirtualKeyCode::Down) => 2,
|
Key::Named(NamedKey::ArrowDown) => 2,
|
||||||
Some(VirtualKeyCode::Left) => 4,
|
Key::Named(NamedKey::ArrowLeft) => 4,
|
||||||
Some(VirtualKeyCode::Right) => 8,
|
Key::Named(NamedKey::ArrowRight) => 8,
|
||||||
_ => 0,
|
_ => 0,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if input.state == winit::event::ElementState::Pressed {
|
if event.state == winit::event::ElementState::Pressed {
|
||||||
match input.virtual_keycode {
|
match event.logical_key {
|
||||||
Some(VirtualKeyCode::Escape) => {
|
Key::Named(NamedKey::Escape) => {
|
||||||
self.is_open = false;
|
elwt.exit();
|
||||||
*control_flow = ControlFlow::Exit;
|
|
||||||
}
|
}
|
||||||
Some(VirtualKeyCode::F) => {
|
Key::Character(ref c) => match c.as_str() {
|
||||||
|
"f" => {
|
||||||
let fullscreen = if self.window.fullscreen().is_some() {
|
let fullscreen = if self.window.fullscreen().is_some() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@@ -170,44 +169,24 @@ impl WindowImpl for Window {
|
|||||||
self.is_fullscreen = fullscreen.is_some();
|
self.is_fullscreen = fullscreen.is_some();
|
||||||
self.window.set_fullscreen(fullscreen);
|
self.window.set_fullscreen(fullscreen);
|
||||||
}
|
}
|
||||||
Some(VirtualKeyCode::M) => {
|
"r" => reset = true,
|
||||||
self.scale_mode = match self.scale_mode {
|
"1" => new_filter = Some(1),
|
||||||
ScaleMode::Fit => ScaleMode::Fill,
|
"2" => new_filter = Some(2),
|
||||||
ScaleMode::Fill => ScaleMode::Fit,
|
"3" => new_filter = Some(3),
|
||||||
};
|
"4" => new_filter = Some(4),
|
||||||
self.filter.resize(
|
"5" => new_filter = Some(5),
|
||||||
&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);
|
self.gamepads[0] |= gamepad_button(&event);
|
||||||
} else {
|
} else {
|
||||||
self.gamepads[0] &= !gamepad_button(&input);
|
self.gamepads[0] &= !gamepad_button(&event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
},
|
},
|
||||||
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 {
|
if let Some(new_filter) = new_filter {
|
||||||
@@ -217,10 +196,18 @@ impl WindowImpl for Window {
|
|||||||
self.window.inner_size(),
|
self.window.inner_size(),
|
||||||
self.surface_config.format,
|
self.surface_config.format,
|
||||||
new_filter,
|
new_filter,
|
||||||
self.scale_mode,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
match status {
|
||||||
|
PumpStatus::Exit(_) => self.is_open = false,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
if Instant::now() >= self.next_frame {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
Input {
|
Input {
|
||||||
gamepads: self.gamepads,
|
gamepads: self.gamepads,
|
||||||
reset,
|
reset,
|
||||||
@@ -256,10 +243,12 @@ impl WindowImpl for Window {
|
|||||||
b: 0.0,
|
b: 0.0,
|
||||||
a: 1.0,
|
a: 1.0,
|
||||||
}),
|
}),
|
||||||
store: true,
|
store: wgpu::StoreOp::Store,
|
||||||
},
|
},
|
||||||
})],
|
})],
|
||||||
depth_stencil_attachment: None,
|
depth_stencil_attachment: None,
|
||||||
|
timestamp_writes: None,
|
||||||
|
occlusion_query_set: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
self.filter.render(&mut render_pass);
|
self.filter.render(&mut render_pass);
|
||||||
@@ -280,7 +269,6 @@ fn create_filter(
|
|||||||
window_size: PhysicalSize<u32>,
|
window_size: PhysicalSize<u32>,
|
||||||
surface_format: wgpu::TextureFormat,
|
surface_format: wgpu::TextureFormat,
|
||||||
filter: u32,
|
filter: u32,
|
||||||
scale_mode: ScaleMode,
|
|
||||||
) -> Box<dyn Filter> {
|
) -> Box<dyn Filter> {
|
||||||
match filter {
|
match filter {
|
||||||
1 => Box::new(SquareFilter::new(
|
1 => Box::new(SquareFilter::new(
|
||||||
@@ -288,7 +276,6 @@ fn create_filter(
|
|||||||
screen_texture,
|
screen_texture,
|
||||||
window_size,
|
window_size,
|
||||||
surface_format,
|
surface_format,
|
||||||
scale_mode,
|
|
||||||
)),
|
)),
|
||||||
2 => Box::new(FastCrtFilter::new(
|
2 => Box::new(FastCrtFilter::new(
|
||||||
device,
|
device,
|
||||||
@@ -296,14 +283,12 @@ fn create_filter(
|
|||||||
window_size,
|
window_size,
|
||||||
surface_format,
|
surface_format,
|
||||||
false,
|
false,
|
||||||
scale_mode,
|
|
||||||
)),
|
)),
|
||||||
3 => Box::new(CrtFilter::new(
|
3 => Box::new(CrtFilter::new(
|
||||||
device,
|
device,
|
||||||
screen_texture,
|
screen_texture,
|
||||||
window_size,
|
window_size,
|
||||||
surface_format,
|
surface_format,
|
||||||
scale_mode,
|
|
||||||
)),
|
)),
|
||||||
4 => Box::new(FastCrtFilter::new(
|
4 => Box::new(FastCrtFilter::new(
|
||||||
device,
|
device,
|
||||||
@@ -311,20 +296,18 @@ fn create_filter(
|
|||||||
window_size,
|
window_size,
|
||||||
surface_format,
|
surface_format,
|
||||||
true,
|
true,
|
||||||
scale_mode,
|
|
||||||
)),
|
)),
|
||||||
_ => Box::new(AutoCrtFilter::new(
|
_ => Box::new(AutoCrtFilter::new(
|
||||||
device,
|
device,
|
||||||
screen_texture,
|
screen_texture,
|
||||||
window_size,
|
window_size,
|
||||||
surface_format,
|
surface_format,
|
||||||
scale_mode,
|
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Filter {
|
trait Filter {
|
||||||
fn resize(&mut self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>, scale_mode: ScaleMode);
|
fn resize(&mut self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>);
|
||||||
fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>);
|
fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,11 +323,9 @@ impl AutoCrtFilter {
|
|||||||
screen: &wgpu::TextureView,
|
screen: &wgpu::TextureView,
|
||||||
resolution: PhysicalSize<u32>,
|
resolution: PhysicalSize<u32>,
|
||||||
surface_format: wgpu::TextureFormat,
|
surface_format: wgpu::TextureFormat,
|
||||||
scale_mode: ScaleMode,
|
|
||||||
) -> AutoCrtFilter {
|
) -> AutoCrtFilter {
|
||||||
let small = CrtFilter::new(device, screen, resolution, surface_format, scale_mode);
|
let small = CrtFilter::new(device, screen, resolution, surface_format);
|
||||||
let large =
|
let large = FastCrtFilter::new(device, screen, resolution, surface_format, true);
|
||||||
FastCrtFilter::new(device, screen, resolution, surface_format, true, scale_mode);
|
|
||||||
AutoCrtFilter {
|
AutoCrtFilter {
|
||||||
small,
|
small,
|
||||||
large,
|
large,
|
||||||
@@ -354,9 +335,9 @@ impl AutoCrtFilter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Filter for AutoCrtFilter {
|
impl Filter for AutoCrtFilter {
|
||||||
fn resize(&mut self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>, scale_mode: ScaleMode) {
|
fn resize(&mut self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>) {
|
||||||
self.small.resize(queue, new_size, scale_mode);
|
self.small.resize(queue, new_size);
|
||||||
self.large.resize(queue, new_size, scale_mode);
|
self.large.resize(queue, new_size);
|
||||||
self.resolution = new_size;
|
self.resolution = new_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -569,10 +550,12 @@ impl PaletteScreenMode {
|
|||||||
resolve_target: None,
|
resolve_target: None,
|
||||||
ops: wgpu::Operations {
|
ops: wgpu::Operations {
|
||||||
load: wgpu::LoadOp::Load,
|
load: wgpu::LoadOp::Load,
|
||||||
store: true,
|
store: wgpu::StoreOp::Store,
|
||||||
},
|
},
|
||||||
})],
|
})],
|
||||||
depth_stencil_attachment: None,
|
depth_stencil_attachment: None,
|
||||||
|
timestamp_writes: None,
|
||||||
|
occlusion_query_set: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
render_pass.set_pipeline(&self.pipeline);
|
render_pass.set_pipeline(&self.pipeline);
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
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,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
use winit::dpi::PhysicalSize;
|
use winit::dpi::PhysicalSize;
|
||||||
|
|
||||||
use super::{scale_mode::ScaleMode, Filter};
|
use super::Filter;
|
||||||
|
|
||||||
pub struct SquareFilter {
|
pub struct SquareFilter {
|
||||||
uniform_buffer: wgpu::Buffer,
|
uniform_buffer: wgpu::Buffer,
|
||||||
@@ -15,10 +15,9 @@ impl SquareFilter {
|
|||||||
screen: &wgpu::TextureView,
|
screen: &wgpu::TextureView,
|
||||||
resolution: PhysicalSize<u32>,
|
resolution: PhysicalSize<u32>,
|
||||||
surface_format: wgpu::TextureFormat,
|
surface_format: wgpu::TextureFormat,
|
||||||
scale_mode: ScaleMode,
|
|
||||||
) -> SquareFilter {
|
) -> SquareFilter {
|
||||||
let uniforms = Uniforms {
|
let uniforms = Uniforms {
|
||||||
texture_scale: scale_mode.texture_scale_from_resolution(resolution),
|
texture_scale: texture_scale_from_resolution(resolution),
|
||||||
};
|
};
|
||||||
|
|
||||||
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
@@ -127,9 +126,9 @@ impl SquareFilter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Filter for SquareFilter {
|
impl Filter for SquareFilter {
|
||||||
fn resize(&mut self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>, scale_mode: ScaleMode) {
|
fn resize(&mut self, queue: &wgpu::Queue, new_size: PhysicalSize<u32>) {
|
||||||
let uniforms = Uniforms {
|
let uniforms = Uniforms {
|
||||||
texture_scale: scale_mode.texture_scale_from_resolution(new_size),
|
texture_scale: texture_scale_from_resolution(new_size),
|
||||||
};
|
};
|
||||||
queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[uniforms]));
|
queue.write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[uniforms]));
|
||||||
}
|
}
|
||||||
@@ -141,6 +140,16 @@ impl Filter for SquareFilter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn texture_scale_from_resolution(res: PhysicalSize<u32>) -> [f32; 4] {
|
||||||
|
let scale = ((res.width as f32) / 160.0).min((res.height as f32) / 120.0);
|
||||||
|
[
|
||||||
|
scale / res.width as f32,
|
||||||
|
scale / res.height as f32,
|
||||||
|
2.0 / scale,
|
||||||
|
0.0,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
struct Uniforms {
|
struct Uniforms {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use gpu::scale_mode::ScaleMode;
|
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
mod cpu;
|
mod cpu;
|
||||||
@@ -74,7 +73,6 @@ pub struct WindowConfig {
|
|||||||
fullscreen: bool,
|
fullscreen: bool,
|
||||||
fps_counter: bool,
|
fps_counter: bool,
|
||||||
scale: f32,
|
scale: f32,
|
||||||
scale_mode: ScaleMode,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for WindowConfig {
|
impl Default for WindowConfig {
|
||||||
@@ -85,7 +83,6 @@ impl Default for WindowConfig {
|
|||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
fps_counter: false,
|
fps_counter: false,
|
||||||
scale: 2.,
|
scale: 2.,
|
||||||
scale_mode: ScaleMode::Fit,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,9 +109,6 @@ impl WindowConfig {
|
|||||||
.opt_value_from_str("--scale")
|
.opt_value_from_str("--scale")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap_or(self.scale);
|
.unwrap_or(self.scale);
|
||||||
if args.contains("--scale-fill") {
|
|
||||||
self.scale_mode = ScaleMode::Fill;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="uw8">
|
<div id="uw8">
|
||||||
<a href="https://exoticorn.github.io/microw8">MicroW8</a> 0.4.1
|
<a href="https://exoticorn.github.io/microw8">MicroW8</a> 0.3.1
|
||||||
</div>
|
</div>
|
||||||
<div id="centered">
|
<div id="centered">
|
||||||
<canvas class="screen" id="screen" width="320" height="240">
|
<canvas class="screen" id="screen" width="320" height="240">
|
||||||
|
|||||||
Reference in New Issue
Block a user