dev/prometeuc-base (#4)

Co-authored-by: Nilton Constantino <nilton.constantino@visma.com>
Reviewed-on: #4
This commit is contained in:
bquarkz 2026-01-20 11:46:23 +00:00 committed by Nilton Constantino
parent d9f6c30c38
commit 4cc0170c74
No known key found for this signature in database
19 changed files with 1235 additions and 31 deletions

399
Cargo.lock generated
View File

@ -154,6 +154,12 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "anyhow"
version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
[[package]]
name = "arrayref"
version = "0.3.9"
@ -181,6 +187,12 @@ dependencies = [
"libloading 0.7.4",
]
[[package]]
name = "assert-unchecked"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7330592adf847ee2e3513587b4db2db410a0d751378654e7e993d9adcbe5c795"
[[package]]
name = "atomic-waker"
version = "1.1.2"
@ -258,6 +270,9 @@ name = "bumpalo"
version = "3.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
dependencies = [
"allocator-api2",
]
[[package]]
name = "bytemuck"
@ -297,6 +312,15 @@ dependencies = [
"wayland-client",
]
[[package]]
name = "castaway"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a"
dependencies = [
"rustversion",
]
[[package]]
name = "cc"
version = "1.2.52"
@ -400,7 +424,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
dependencies = [
"termcolor",
"unicode-width",
"unicode-width 0.1.14",
]
[[package]]
@ -450,6 +474,20 @@ dependencies = [
"memchr",
]
[[package]]
name = "compact_str"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32"
dependencies = [
"castaway",
"cfg-if",
"itoa",
"rustversion",
"ryu",
"static_assertions",
]
[[package]]
name = "concurrent-queue"
version = "2.5.0"
@ -519,6 +557,12 @@ dependencies = [
"bindgen",
]
[[package]]
name = "cow-utils"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "417bef24afe1460300965a25ff4a24b8b45ad011948302ec221e8a0a81eb2c79"
[[package]]
name = "cpal"
version = "0.15.3"
@ -775,6 +819,15 @@ dependencies = [
"allocator-api2",
]
[[package]]
name = "hashbrown"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"allocator-api2",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
@ -1105,6 +1158,22 @@ dependencies = [
"minimal-lexical",
]
[[package]]
name = "nonmax"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "610a5acd306ec67f907abe5567859a3c693fb9886eb1f012ab8f2a47bef3db51"
[[package]]
name = "num-bigint"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-derive"
version = "0.4.2"
@ -1116,6 +1185,15 @@ dependencies = [
"syn 2.0.114",
]
[[package]]
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.19"
@ -1423,6 +1501,187 @@ dependencies = [
"ttf-parser",
]
[[package]]
name = "owo-colors"
version = "4.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52"
[[package]]
name = "oxc-miette"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e03e63fd113c068b82d07c9c614b0b146c08a3ac0a4dface3ea1d1a9d14d549e"
dependencies = [
"cfg-if",
"owo-colors",
"oxc-miette-derive",
"textwrap",
"thiserror",
"unicode-width 0.2.2",
]
[[package]]
name = "oxc-miette-derive"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e21f680e8c5f1900297d394627d495351b9e37761f7bbf90116bd5eeb6e80967"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
name = "oxc_allocator"
version = "0.48.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17374a91329f2e362bc8be31cb3e171ef226777d0ea668e17a39620443693027"
dependencies = [
"allocator-api2",
"bumpalo",
"hashbrown 0.15.5",
"rustc-hash 2.1.1",
"simdutf8",
]
[[package]]
name = "oxc_ast"
version = "0.48.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21b38cfe954ee987354af015020f5cb2723df6fed6cd688eff24388437c7eb7e"
dependencies = [
"bitflags 2.10.0",
"cow-utils",
"num-bigint",
"num-traits",
"oxc_allocator",
"oxc_ast_macros",
"oxc_estree",
"oxc_regular_expression",
"oxc_span",
"oxc_syntax",
]
[[package]]
name = "oxc_ast_macros"
version = "0.48.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85f9e03eff90b041078edcc8aff09457eb4126fa5d62a383fdb082d3ae286274"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
name = "oxc_diagnostics"
version = "0.48.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a24c41929c1910d83fb6f4c508190a8b2d1bad02e81c8cb3a9d3f474ca0ad2c1"
dependencies = [
"cow-utils",
"oxc-miette",
]
[[package]]
name = "oxc_ecmascript"
version = "0.48.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30fba86c97b571671f941b818e6831ba821aac398b9f6f3b4e3367ca264b8324"
dependencies = [
"num-bigint",
"num-traits",
"oxc_ast",
"oxc_span",
"oxc_syntax",
]
[[package]]
name = "oxc_estree"
version = "0.48.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e587034bb5f108dd987e5419a4ad35a173550bd9439fc2b610d34e4ce41e8b23"
[[package]]
name = "oxc_index"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5eca5d9726cd0a6e433debe003b7bc88b2ecad0bb6109f0cef7c55e692139a34"
[[package]]
name = "oxc_parser"
version = "0.48.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dc385aca3586877f8c918aed1141b885dedcfc1019d6ea5be4105f8e2a7965e"
dependencies = [
"assert-unchecked",
"bitflags 2.10.0",
"cow-utils",
"memchr",
"num-bigint",
"num-traits",
"oxc_allocator",
"oxc_ast",
"oxc_diagnostics",
"oxc_ecmascript",
"oxc_regular_expression",
"oxc_span",
"oxc_syntax",
"rustc-hash 2.1.1",
"seq-macro",
]
[[package]]
name = "oxc_regular_expression"
version = "0.48.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8d172402f6d542ab2837d35126cc42ad6989b9c2289b06bc5c8c89dceb60c48"
dependencies = [
"oxc_allocator",
"oxc_ast_macros",
"oxc_diagnostics",
"oxc_estree",
"oxc_span",
"phf",
"rustc-hash 2.1.1",
"unicode-id-start",
]
[[package]]
name = "oxc_span"
version = "0.48.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75881376dfdbb6a23f12a66794904f0d215629f208edcf7e156c0770e89e2101"
dependencies = [
"compact_str",
"oxc-miette",
"oxc_allocator",
"oxc_ast_macros",
"oxc_estree",
]
[[package]]
name = "oxc_syntax"
version = "0.48.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6d88037a5de07f66299ab523a65545cc48d550658cea8cadcea09b60596dd49"
dependencies = [
"assert-unchecked",
"bitflags 2.10.0",
"cow-utils",
"nonmax",
"oxc_allocator",
"oxc_ast_macros",
"oxc_estree",
"oxc_index",
"oxc_span",
"phf",
"rustc-hash 2.1.1",
"ryu-js",
"unicode-id-start",
]
[[package]]
name = "parking_lot"
version = "0.12.5"
@ -1458,6 +1717,48 @@ version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "phf"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
dependencies = [
"phf_macros",
"phf_shared",
]
[[package]]
name = "phf_generator"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
dependencies = [
"phf_shared",
"rand",
]
[[package]]
name = "phf_macros"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216"
dependencies = [
"phf_generator",
"phf_shared",
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
name = "phf_shared"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
dependencies = [
"siphasher",
]
[[package]]
name = "pin-project"
version = "1.1.10"
@ -1607,6 +1908,22 @@ dependencies = [
"winit",
]
[[package]]
name = "prometeuc"
version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"oxc_allocator",
"oxc_ast",
"oxc_parser",
"oxc_span",
"prometeu-bytecode",
"prometeu-core",
"serde",
"serde_json",
]
[[package]]
name = "quick-xml"
version = "0.38.4"
@ -1631,6 +1948,21 @@ version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
[[package]]
name = "range-alloc"
version = "0.1.4"
@ -1760,6 +2092,18 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984"
[[package]]
name = "ryu-js"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd29631678d6fb0903b69223673e122c32e9ae559d0960a38d574695ebc0ea15"
[[package]]
name = "safe_arch"
version = "0.7.4"
@ -1803,6 +2147,12 @@ dependencies = [
"tiny-skia",
]
[[package]]
name = "seq-macro"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc"
[[package]]
name = "serde"
version = "1.0.228"
@ -1852,6 +2202,18 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "simdutf8"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
[[package]]
name = "siphasher"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
[[package]]
name = "slab"
version = "0.4.11"
@ -1873,6 +2235,12 @@ version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "smawk"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
[[package]]
name = "smithay-client-toolkit"
version = "0.19.2"
@ -1965,6 +2333,17 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "textwrap"
version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057"
dependencies = [
"smawk",
"unicode-linebreak",
"unicode-width 0.2.2",
]
[[package]]
name = "thiserror"
version = "1.0.69"
@ -2071,12 +2450,24 @@ dependencies = [
"wide",
]
[[package]]
name = "unicode-id-start"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81b79ad29b5e19de4260020f8919b443b2ef0277d242ce532ec7b7a2cc8b6007"
[[package]]
name = "unicode-ident"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "unicode-linebreak"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
[[package]]
name = "unicode-segmentation"
version = "1.12.0"
@ -2089,6 +2480,12 @@ version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "unicode-width"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
[[package]]
name = "unicode-xid"
version = "0.2.6"

View File

@ -3,6 +3,8 @@ members = [
"crates/prometeu-core",
"crates/prometeu-runtime-desktop",
"crates/prometeu",
"crates/prometeu-bytecode",
"crates/prometeuc",
]
resolver = "2"

View File

@ -6,7 +6,7 @@ pub mod firmware;
pub mod fs;
pub mod telemetry;
pub mod debugger_protocol;
mod prometeu_os;
pub mod prometeu_os;
mod prometeu_hub;
pub use hardware::hardware::Hardware;

View File

@ -1,3 +1,20 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum ButtonId {
Up = 0,
Down = 1,
Left = 2,
Right = 3,
A = 4,
B = 5,
X = 6,
Y = 7,
L = 8,
R = 9,
Start = 10,
Select = 11,
}
#[derive(Default, Clone, Copy, Debug)]
pub struct Button {
pub pressed: bool,

View File

@ -9,7 +9,7 @@ mod cartridge;
mod cartridge_loader;
mod window;
pub use button::Button;
pub use button::{Button, ButtonId};
pub use cartridge::{AppMode, Cartridge, CartridgeDTO, CartridgeError};
pub use cartridge_loader::{CartridgeLoader, DirectoryCartridgeLoader, PackedCartridgeLoader};
pub use color::Color;

View File

@ -1,4 +1,6 @@
mod prometeu_os;
pub mod syscalls;
pub use prometeu_os::PrometeuOS;
pub use syscalls::Syscall;
pub use crate::virtual_machine::native_interface::NativeInterface;

View File

@ -2,7 +2,7 @@ use crate::fs::{FsBackend, FsState, VirtualFS};
use crate::hardware::{HardwareBridge, InputSignals};
use crate::log::{LogLevel, LogService, LogSource};
use crate::model::{Cartridge, Color, Sample};
use crate::prometeu_os::NativeInterface;
use crate::prometeu_os::{NativeInterface, Syscall};
use crate::telemetry::{CertificationConfig, Certifier, TelemetryFrame};
use crate::virtual_machine::{Value, VirtualMachine};
use std::collections::HashMap;
@ -554,17 +554,18 @@ impl NativeInterface for PrometeuOS {
/// Each syscall returns the number of virtual cycles it consumed.
fn syscall(&mut self, id: u32, vm: &mut VirtualMachine, hw: &mut dyn HardwareBridge) -> Result<u64, String> {
self.telemetry_current.syscalls += 1;
match id {
let syscall = Syscall::from_u32(id).ok_or_else(|| format!("Unknown syscall: 0x{:08X}", id))?;
match syscall {
// --- System Syscalls ---
// system.has_cart() -> bool
0x0001 => {
Syscall::SystemHasCart => {
// Returns true if a cartridge is available.
vm.push(Value::Boolean(true)); // For now, assume true or check state
Ok(10)
}
// system.run_cart() -> null
0x0002 => {
Syscall::SystemRunCart => {
// Triggers loading and execution of the current cartridge.
vm.push(Value::Null);
Ok(100)
@ -573,7 +574,7 @@ impl NativeInterface for PrometeuOS {
// --- GFX Syscalls ---
// gfx.clear(color_index) -> null
0x1001 => {
Syscall::GfxClear => {
let color_idx = vm.pop_integer()? as usize;
let color = self.get_color(color_idx, hw);
hw.gfx_mut().clear(color);
@ -581,7 +582,7 @@ impl NativeInterface for PrometeuOS {
Ok(100)
}
// gfx.draw_rect(x, y, w, h, color_index) -> null
0x1002 => {
Syscall::GfxFillRect => {
let color_idx = vm.pop_integer()? as usize;
let h = vm.pop_integer()? as i32;
let w = vm.pop_integer()? as i32;
@ -593,7 +594,7 @@ impl NativeInterface for PrometeuOS {
Ok(200)
}
// gfx.draw_line(x1, y1, x2, y2, color_index) -> null
0x1003 => {
Syscall::GfxDrawLine => {
let color_idx = vm.pop_integer()? as usize;
let y2 = vm.pop_integer()? as i32;
let x2 = vm.pop_integer()? as i32;
@ -605,7 +606,7 @@ impl NativeInterface for PrometeuOS {
Ok(200)
}
// gfx.draw_circle(x, y, r, color_index) -> null
0x1004 => {
Syscall::GfxDrawCircle => {
let color_idx = vm.pop_integer()? as usize;
let r = vm.pop_integer()? as i32;
let y = vm.pop_integer()? as i32;
@ -616,7 +617,7 @@ impl NativeInterface for PrometeuOS {
Ok(200)
}
// gfx.draw_disc(x, y, r, border_color_idx, fill_color_idx) -> null
0x1005 => {
Syscall::GfxDrawDisc => {
let fill_color_idx = vm.pop_integer()? as usize;
let border_color_idx = vm.pop_integer()? as usize;
let r = vm.pop_integer()? as i32;
@ -629,7 +630,7 @@ impl NativeInterface for PrometeuOS {
Ok(300)
}
// gfx.draw_square(x, y, w, h, border_color_idx, fill_color_idx) -> null
0x1006 => {
Syscall::GfxDrawSquare => {
let fill_color_idx = vm.pop_integer()? as usize;
let border_color_idx = vm.pop_integer()? as usize;
let h = vm.pop_integer()? as i32;
@ -646,7 +647,7 @@ impl NativeInterface for PrometeuOS {
// --- Input Syscalls ---
// input.get_pad(button_id) -> bool
0x2001 => {
Syscall::InputGetPad => {
let button_id = vm.pop_integer()? as u32;
let is_down = self.is_button_down(button_id, hw);
vm.push(Value::Boolean(is_down));
@ -656,7 +657,7 @@ impl NativeInterface for PrometeuOS {
// --- Audio Syscalls ---
// audio.play_sample(sample_id, voice_id, volume, pan, pitch)
0x3001 => {
Syscall::AudioPlaySample => {
let pitch = vm.pop_number()?;
let pan = vm.pop_integer()? as u8;
let volume = vm.pop_integer()? as u8;
@ -681,7 +682,7 @@ impl NativeInterface for PrometeuOS {
// FS_OPEN(path) -> handle
// Opens a file in the virtual sandbox and returns a numeric handle.
0x4001 => {
Syscall::FsOpen => {
let path = match vm.pop()? {
Value::String(s) => s,
_ => return Err("Expected string path".into()),
@ -697,7 +698,7 @@ impl NativeInterface for PrometeuOS {
Ok(200)
}
// FS_READ(handle) -> content
0x4002 => {
Syscall::FsRead => {
let handle = vm.pop_integer()? as u32;
let path = self.open_files.get(&handle).ok_or("Invalid handle")?;
match self.fs.read_file(path) {
@ -713,7 +714,7 @@ impl NativeInterface for PrometeuOS {
}
}
// FS_WRITE(handle, content)
0x4003 => {
Syscall::FsWrite => {
let content = match vm.pop()? {
Value::String(s) => s,
_ => return Err("Expected string content".into()),
@ -732,14 +733,14 @@ impl NativeInterface for PrometeuOS {
}
}
// FS_CLOSE(handle)
0x4004 => {
Syscall::FsClose => {
let handle = vm.pop_integer()? as u32;
self.open_files.remove(&handle);
vm.push(Value::Null);
Ok(100)
}
// FS_LISTDIR(path)
0x4005 => {
Syscall::FsListDir => {
let path = match vm.pop()? {
Value::String(s) => s,
_ => return Err("Expected string path".into()),
@ -758,7 +759,7 @@ impl NativeInterface for PrometeuOS {
}
}
// FS_EXISTS(path) -> bool
0x4006 => {
Syscall::FsExists => {
let path = match vm.pop()? {
Value::String(s) => s,
_ => return Err("Expected string path".into()),
@ -767,7 +768,7 @@ impl NativeInterface for PrometeuOS {
Ok(100)
}
// FS_DELETE(path)
0x4007 => {
Syscall::FsDelete => {
let path = match vm.pop()? {
Value::String(s) => s,
_ => return Err("Expected string path".into()),
@ -782,7 +783,7 @@ impl NativeInterface for PrometeuOS {
// --- Log Syscalls (0x5000) ---
// LOG_WRITE(level, msg)
0x5001 => {
Syscall::LogWrite => {
let msg = match vm.pop()? {
Value::String(s) => s,
_ => return Err("Expected string message".into()),
@ -791,7 +792,7 @@ impl NativeInterface for PrometeuOS {
self.syscall_log_write(vm, level, 0, msg)
}
// LOG_WRITE_TAG(level, tag, msg)
0x5002 => {
Syscall::LogWriteTag => {
let msg = match vm.pop()? {
Value::String(s) => s,
_ => return Err("Expected string message".into()),
@ -800,8 +801,6 @@ impl NativeInterface for PrometeuOS {
let level = vm.pop_integer()?;
self.syscall_log_write(vm, level, tag, msg)
}
_ => Err(format!("Unknown syscall: 0x{:08X}", id)),
}
}
}

View File

@ -0,0 +1,110 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum Syscall {
// System
SystemHasCart = 0x0001,
SystemRunCart = 0x0002,
// GFX
GfxClear = 0x1001,
GfxFillRect = 0x1002,
GfxDrawLine = 0x1003,
GfxDrawCircle = 0x1004,
GfxDrawDisc = 0x1005,
GfxDrawSquare = 0x1006,
// Input
InputGetPad = 0x2001,
// Audio
AudioPlaySample = 0x3001,
// FS
FsOpen = 0x4001,
FsRead = 0x4002,
FsWrite = 0x4003,
FsClose = 0x4004,
FsListDir = 0x4005,
FsExists = 0x4006,
FsDelete = 0x4007,
// Log
LogWrite = 0x5001,
LogWriteTag = 0x5002,
}
impl Syscall {
pub fn from_u32(id: u32) -> Option<Self> {
match id {
0x0001 => Some(Self::SystemHasCart),
0x0002 => Some(Self::SystemRunCart),
0x1001 => Some(Self::GfxClear),
0x1002 => Some(Self::GfxFillRect),
0x1003 => Some(Self::GfxDrawLine),
0x1004 => Some(Self::GfxDrawCircle),
0x1005 => Some(Self::GfxDrawDisc),
0x1006 => Some(Self::GfxDrawSquare),
0x2001 => Some(Self::InputGetPad),
0x3001 => Some(Self::AudioPlaySample),
0x4001 => Some(Self::FsOpen),
0x4002 => Some(Self::FsRead),
0x4003 => Some(Self::FsWrite),
0x4004 => Some(Self::FsClose),
0x4005 => Some(Self::FsListDir),
0x4006 => Some(Self::FsExists),
0x4007 => Some(Self::FsDelete),
0x5001 => Some(Self::LogWrite),
0x5002 => Some(Self::LogWriteTag),
_ => None,
}
}
pub fn as_str(&self) -> &'static str {
match self {
Self::SystemHasCart => "system.has_cart",
Self::SystemRunCart => "system.run_cart",
Self::GfxClear => "gfx.clear",
Self::GfxFillRect => "gfx.fillRect",
Self::GfxDrawLine => "gfx.drawLine",
Self::GfxDrawCircle => "gfx.drawCircle",
Self::GfxDrawDisc => "gfx.drawDisc",
Self::GfxDrawSquare => "gfx.drawSquare",
Self::InputGetPad => "input.get_pad",
Self::AudioPlaySample => "audio.playSample",
Self::FsOpen => "fs.open",
Self::FsRead => "fs.read",
Self::FsWrite => "fs.write",
Self::FsClose => "fs.close",
Self::FsListDir => "fs.listDir",
Self::FsExists => "fs.exists",
Self::FsDelete => "fs.delete",
Self::LogWrite => "log.write",
Self::LogWriteTag => "log.writeTag",
}
}
pub fn from_name(name: &str) -> Option<Self> {
match name {
"system.has_cart" => Some(Self::SystemHasCart),
"system.run_cart" => Some(Self::SystemRunCart),
"gfx.clear" => Some(Self::GfxClear),
"gfx.fillRect" | "gfx.draw_rect" => Some(Self::GfxFillRect),
"gfx.drawLine" | "gfx.draw_line" => Some(Self::GfxDrawLine),
"gfx.drawCircle" | "gfx.draw_circle" => Some(Self::GfxDrawCircle),
"gfx.drawDisc" | "gfx.draw_disc" => Some(Self::GfxDrawDisc),
"gfx.drawSquare" | "gfx.draw_square" => Some(Self::GfxDrawSquare),
"input.get_pad" => Some(Self::InputGetPad),
"audio.playSample" | "audio.play_sample" => Some(Self::AudioPlaySample),
"fs.open" => Some(Self::FsOpen),
"fs.read" => Some(Self::FsRead),
"fs.write" => Some(Self::FsWrite),
"fs.close" => Some(Self::FsClose),
"fs.listDir" => Some(Self::FsListDir),
"fs.exists" => Some(Self::FsExists),
"fs.delete" => Some(Self::FsDelete),
"log.write" => Some(Self::LogWrite),
"log.writeTag" => Some(Self::LogWriteTag),
_ => None,
}
}
}

View File

@ -5,3 +5,12 @@ edition = "2021"
[dependencies]
prometeu-bytecode = { path = "../prometeu-bytecode" }
prometeu-core = { path = "../prometeu-core" }
oxc_parser = "0.48.0"
oxc_allocator = "0.48.0"
oxc_ast = "0.48.0"
oxc_span = "0.48.0"
clap = { version = "4.4", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
anyhow = "1.0"

View File

@ -0,0 +1,35 @@
use clap::{Parser, Subcommand};
use std::path::PathBuf;
#[derive(Parser)]
#[command(name = "prometeuc")]
#[command(about = "Prometeu Compiler", long_about = None)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
}
#[derive(Subcommand)]
pub enum Commands {
/// Builds a Prometeu project
Build {
/// Project directory
project_dir: PathBuf,
/// Entry file
#[arg(short, long)]
entry: Option<PathBuf>,
/// Output PBC file
#[arg(short, long)]
out: Option<PathBuf>,
/// Emit disassembly file
#[arg(long, default_value_t = true)]
emit_disasm: bool,
/// Emit symbols file
#[arg(long, default_value_t = true)]
emit_symbols: bool,
},
}

View File

@ -0,0 +1,324 @@
use anyhow::{Result, anyhow};
use oxc_ast::ast::*;
use oxc_span::{Span, GetSpan};
use prometeu_bytecode::opcode::OpCode;
use prometeu_bytecode::asm::{Asm, Operand, assemble};
use crate::compiler::Symbol;
use crate::syscall_map;
use std::collections::HashMap;
pub struct Codegen {
file_name: String,
source_text: String,
pub symbols: Vec<Symbol>,
instructions: Vec<(Asm, bool)>, // (Asm, has_symbol)
locals: HashMap<String, u32>,
next_local: u32,
label_count: u32,
}
impl Codegen {
pub fn new(file_name: String, source_text: String) -> Self {
Self {
file_name,
source_text,
symbols: Vec::new(),
instructions: Vec::new(),
locals: HashMap::new(),
next_local: 0,
label_count: 0,
}
}
pub fn compile_program(&mut self, program: &Program) -> Result<Vec<u8>> {
// Find tick function
let mut tick_fn = None;
for item in &program.body {
if let Statement::ExportNamedDeclaration(decl) = item {
if let Some(Declaration::FunctionDeclaration(f)) = &decl.declaration {
if let Some(ident) = &f.id {
if ident.name == "tick" {
tick_fn = Some(f);
break;
}
}
}
}
}
let tick_fn = tick_fn.ok_or_else(|| anyhow!("export function tick() not found"))?;
// Entry point: loop calling tick
self.emit_label("entry".to_string());
self.emit_op(OpCode::Call, vec![Operand::Label("tick".to_string()), Operand::U32(0)], Span::default());
self.emit_op(OpCode::FrameSync, vec![], Span::default());
self.emit_op(OpCode::Jmp, vec![Operand::Label("entry".to_string())], Span::default());
// Function tick
self.emit_label("tick".to_string());
self.compile_function(tick_fn)?;
self.emit_op(OpCode::Ret, vec![], Span::default());
let asm_vec: Vec<Asm> = self.instructions.iter().map(|(a, _)| a.clone()).collect();
let rom = assemble(&asm_vec).map_err(|e| anyhow!("Assemble error: {}", e))?;
// Finalize symbols (associate PC)
self.finalize_symbols();
Ok(rom)
}
fn compile_function(&mut self, f: &Function) -> Result<()> {
self.locals.clear();
self.next_local = 0;
if let Some(body) = &f.body {
for stmt in &body.statements {
self.compile_stmt(stmt)?;
}
}
Ok(())
}
fn compile_stmt(&mut self, stmt: &Statement) -> Result<()> {
match stmt {
Statement::VariableDeclaration(var) => {
for decl in &var.declarations {
if let BindingPatternKind::BindingIdentifier(ident) = &decl.id.kind {
let name = ident.name.to_string();
if let Some(init) = &decl.init {
self.compile_expr(init)?;
} else {
self.emit_op(OpCode::PushI32, vec![Operand::I32(0)], decl.span);
}
let id = self.next_local;
self.locals.insert(name, id);
self.emit_op(OpCode::SetLocal, vec![Operand::U32(id)], decl.span);
self.next_local += 1;
}
}
}
Statement::ExpressionStatement(expr_stmt) => {
self.compile_expr(&expr_stmt.expression)?;
self.emit_op(OpCode::Pop, vec![], expr_stmt.span);
}
Statement::IfStatement(if_stmt) => {
let else_label = self.new_label("else");
let end_label = self.new_label("end_if");
self.compile_expr(&if_stmt.test)?;
self.emit_op(OpCode::JmpIfFalse, vec![Operand::Label(else_label.clone())], if_stmt.span);
self.compile_stmt(&if_stmt.consequent)?;
self.emit_op(OpCode::Jmp, vec![Operand::Label(end_label.clone())], Span::default());
self.emit_label(else_label);
if let Some(alt) = &if_stmt.alternate {
self.compile_stmt(alt)?;
}
self.emit_label(end_label);
}
Statement::BlockStatement(block) => {
for stmt in &block.body {
self.compile_stmt(stmt)?;
}
}
_ => return Err(anyhow!("Unsupported statement type at {:?}", stmt.span())),
}
Ok(())
}
fn compile_expr(&mut self, expr: &Expression) -> Result<()> {
match expr {
Expression::NumericLiteral(n) => {
let val = n.value;
if val.fract() == 0.0 && val >= i32::MIN as f64 && val <= i32::MAX as f64 {
self.emit_op(OpCode::PushI32, vec![Operand::I32(val as i32)], n.span);
} else {
self.emit_op(OpCode::PushI64, vec![Operand::I64(val as i64)], n.span);
}
}
Expression::BooleanLiteral(b) => {
self.emit_op(OpCode::PushBool, vec![Operand::Bool(b.value)], b.span);
}
Expression::Identifier(ident) => {
let name = ident.name.to_string();
if let Some(&id) = self.locals.get(&name) {
self.emit_op(OpCode::GetLocal, vec![Operand::U32(id)], ident.span);
} else {
return Err(anyhow!("Undefined variable: {} at {:?}", name, ident.span));
}
}
Expression::AssignmentExpression(assign) => {
if let AssignmentTarget::AssignmentTargetIdentifier(ident) = &assign.left {
let name = ident.name.to_string();
self.compile_expr(&assign.right)?;
if let Some(&id) = self.locals.get(&name) {
self.emit_op(OpCode::Dup, vec![], assign.span);
self.emit_op(OpCode::SetLocal, vec![Operand::U32(id)], assign.span);
} else {
return Err(anyhow!("Undefined variable: {} at {:?}", name, ident.span));
}
} else {
return Err(anyhow!("Unsupported assignment target at {:?}", assign.span));
}
}
Expression::BinaryExpression(bin) => {
self.compile_expr(&bin.left)?;
self.compile_expr(&bin.right)?;
let op = match bin.operator {
BinaryOperator::Addition => OpCode::Add,
BinaryOperator::Subtraction => OpCode::Sub,
BinaryOperator::Multiplication => OpCode::Mul,
BinaryOperator::Division => OpCode::Div,
BinaryOperator::Equality => OpCode::Eq,
BinaryOperator::Inequality => OpCode::Neq,
BinaryOperator::LessThan => OpCode::Lt,
BinaryOperator::GreaterThan => OpCode::Gt,
BinaryOperator::LessEqualThan => OpCode::Lte,
BinaryOperator::GreaterEqualThan => OpCode::Gte,
_ => return Err(anyhow!("Unsupported binary operator {:?} at {:?}", bin.operator, bin.span)),
};
self.emit_op(op, vec![], bin.span);
}
Expression::LogicalExpression(log) => {
self.compile_expr(&log.left)?;
self.compile_expr(&log.right)?;
let op = match log.operator {
LogicalOperator::And => OpCode::And,
LogicalOperator::Or => OpCode::Or,
_ => return Err(anyhow!("Unsupported logical operator {:?} at {:?}", log.operator, log.span)),
};
self.emit_op(op, vec![], log.span);
}
Expression::UnaryExpression(unary) => {
self.compile_expr(&unary.argument)?;
let op = match unary.operator {
UnaryOperator::UnaryNegation => OpCode::Neg,
UnaryOperator::UnaryPlus => return Ok(()),
UnaryOperator::LogicalNot => OpCode::Not,
_ => return Err(anyhow!("Unsupported unary operator {:?} at {:?}", unary.operator, unary.span)),
};
self.emit_op(op, vec![], unary.span);
}
Expression::CallExpression(call) => {
let name = self.get_callee_name(&call.callee)?;
if let Some(syscall_id) = syscall_map::map_syscall(&name) {
if name == "input.btnA" {
self.emit_op(OpCode::PushI32, vec![Operand::I32(syscall_map::BTN_A as i32)], call.span);
} else if name == "input.btnB" {
self.emit_op(OpCode::PushI32, vec![Operand::I32(syscall_map::BTN_B as i32)], call.span);
} else {
for arg in &call.arguments {
if let Some(expr) = arg.as_expression() {
self.compile_expr(expr)?;
}
}
}
self.emit_op(OpCode::Syscall, vec![Operand::U32(syscall_id)], call.span);
} else {
return Err(anyhow!("Unsupported function call: {} at {:?}", name, call.span));
}
}
Expression::StaticMemberExpression(member) => {
let obj = self.get_callee_name_from_member_obj(&member.object)?;
let prop = member.property.name.to_string();
let full_name = format!("{}.{}", obj, prop);
// If it's used as a value (GetGlobal/GetLocal?), but for now we only support it in calls
return Err(anyhow!("Member expression outside call not supported: {} at {:?}", full_name, member.span));
}
_ => return Err(anyhow!("Unsupported expression type at {:?}", expr.span())),
}
Ok(())
}
fn get_callee_name(&self, expr: &Expression) -> Result<String> {
match expr {
Expression::Identifier(ident) => Ok(ident.name.to_string()),
Expression::StaticMemberExpression(member) => {
let obj = self.get_callee_name_from_member_obj(&member.object)?;
let prop = member.property.name.to_string();
Ok(format!("{}.{}", obj, prop))
}
_ => Err(anyhow!("Unsupported callee expression at {:?}", expr.span())),
}
}
fn get_callee_name_from_member_obj(&self, expr: &Expression) -> Result<String> {
if let Expression::Identifier(ident) = expr {
Ok(ident.name.to_string())
} else {
Err(anyhow!("Unsupported member object expression"))
}
}
fn new_label(&mut self, prefix: &str) -> String {
let label = format!("{}_{}", prefix, self.label_count);
self.label_count += 1;
label
}
fn emit_label(&mut self, name: String) {
self.instructions.push((Asm::Label(name), false));
}
fn emit_op(&mut self, opcode: OpCode, operands: Vec<Operand>, span: Span) {
let has_symbol = if !span.is_unspanned() {
let (line, col) = self.lookup_pos(span.start);
self.symbols.push(Symbol {
pc: 0,
file: self.file_name.clone(),
line,
col,
});
true
} else {
false
};
self.instructions.push((Asm::Op(opcode, operands), has_symbol));
}
fn lookup_pos(&self, pos: u32) -> (usize, usize) {
let mut line = 1;
let mut col = 1;
for (i, c) in self.source_text.char_indices() {
if i as u32 == pos {
break;
}
if c == '\n' {
line += 1;
col = 1;
} else {
col += 1;
}
}
(line, col)
}
fn finalize_symbols(&mut self) {
let mut current_pc = 0u32;
let mut symbol_ptr = 0;
for (instr, has_symbol) in &self.instructions {
match instr {
Asm::Label(_) => {}
Asm::Op(_opcode, operands) => {
if *has_symbol && symbol_ptr < self.symbols.len() {
self.symbols[symbol_ptr].pc = current_pc;
symbol_ptr += 1;
}
current_pc += 2;
for operand in operands {
match operand {
Operand::U32(_) | Operand::I32(_) | Operand::Label(_) => current_pc += 4,
Operand::I64(_) | Operand::F64(_) => current_pc += 8,
Operand::Bool(_) => current_pc += 1,
}
}
}
}
}
}
}

View File

@ -1,2 +1,3 @@
pub use prometeu_bytecode::opcode::OpCode;
pub use prometeu_bytecode::asm::{Asm, Operand, assemble};
pub mod codegen;
pub use codegen::Codegen;

View File

@ -0,0 +1,72 @@
use std::path::Path;
use anyhow::{Result, Context};
use oxc_allocator::Allocator;
use oxc_parser::Parser;
use oxc_span::SourceType;
use crate::codegen::Codegen;
use std::fs;
use prometeu_bytecode::disasm::disasm;
use serde::Serialize;
#[derive(Serialize)]
pub struct Symbol {
pub pc: u32,
pub file: String,
pub line: usize,
pub col: usize,
}
pub fn compile(entry: &Path, out: &Path, emit_disasm: bool, emit_symbols: bool) -> Result<()> {
let source_text = fs::read_to_string(entry)
.with_context(|| format!("Failed to read entry file: {:?}", entry))?;
let allocator = Allocator::default();
let source_type = SourceType::from_path(entry).unwrap_or_default();
let mut codegen = Codegen::new(entry.to_string_lossy().to_string(), source_text.clone());
let rom = {
let ret = Parser::new(&allocator, &source_text, source_type).parse();
if !ret.errors.is_empty() {
for error in ret.errors {
eprintln!("{:?}", error);
}
return Err(anyhow::anyhow!("Failed to parse module"));
}
codegen.compile_program(&ret.program)?
};
fs::write(out, &rom).with_context(|| format!("Failed to write PBC to {:?}", out))?;
if emit_symbols {
let symbols_path = out.with_file_name("symbols.json");
let symbols_json = serde_json::to_string_pretty(&codegen.symbols)?;
fs::write(&symbols_path, symbols_json)?;
}
if emit_disasm {
let disasm_path = out.with_extension("disasm.txt");
let instructions = disasm(&rom).map_err(|e| anyhow::anyhow!("Disassembly failed: {}", e))?;
let mut disasm_text = String::new();
for instr in instructions {
let symbol = codegen.symbols.iter().find(|s| s.pc == instr.pc);
let comment = if let Some(s) = symbol {
format!(" ; {}:{}", s.file, s.line)
} else {
"".to_string()
};
let operands_str = instr.operands.iter()
.map(|o| format!("{:?}", o))
.collect::<Vec<_>>()
.join(" ");
disasm_text.push_str(&format!("{:08X} {:?} {}{}\n", instr.pc, instr.opcode, operands_str, comment));
}
fs::write(disasm_path, disasm_text)?;
}
Ok(())
}

View File

@ -1,5 +1,37 @@
pub mod codegen;
use clap::Parser;
use anyhow::Result;
fn main() {
println!("Prometeu Compiler (stub)");
pub mod cli;
pub mod codegen;
pub mod compiler;
pub mod syscall_map;
fn main() -> Result<()> {
let cli = cli::Cli::parse();
match cli.command {
cli::Commands::Build {
project_dir,
entry,
out,
emit_disasm,
emit_symbols,
} => {
let entry = entry.unwrap_or_else(|| project_dir.join("src/main.ts"));
let build_dir = project_dir.join("build");
let out = out.unwrap_or_else(|| build_dir.join("program.pbc"));
if !build_dir.exists() {
std::fs::create_dir_all(&build_dir)?;
}
println!("Building project at {:?}", project_dir);
println!("Entry: {:?}", entry);
println!("Output: {:?}", out);
compiler::compile(&entry, &out, emit_disasm, emit_symbols)?;
}
}
Ok(())
}

View File

@ -0,0 +1,27 @@
use prometeu_core::prometeu_os::Syscall;
use prometeu_core::model::ButtonId;
pub const BTN_UP: u32 = ButtonId::Up as u32;
pub const BTN_DOWN: u32 = ButtonId::Down as u32;
pub const BTN_LEFT: u32 = ButtonId::Left as u32;
pub const BTN_RIGHT: u32 = ButtonId::Right as u32;
pub const BTN_A: u32 = ButtonId::A as u32;
pub const BTN_B: u32 = ButtonId::B as u32;
pub const BTN_X: u32 = ButtonId::X as u32;
pub const BTN_Y: u32 = ButtonId::Y as u32;
pub const BTN_L: u32 = ButtonId::L as u32;
pub const BTN_R: u32 = ButtonId::R as u32;
pub const BTN_START: u32 = ButtonId::Start as u32;
pub const BTN_SELECT: u32 = ButtonId::Select as u32;
pub fn map_syscall(name: &str) -> Option<u32> {
if let Some(syscall) = Syscall::from_name(name) {
return Some(syscall as u32);
}
// Fallback para nomes especiais do compilador
match name {
"input.btnA" | "input.btnB" => Some(Syscall::InputGetPad as u32),
_ => None,
}
}

View File

@ -0,0 +1,29 @@
00000000 Call U32(18) U32(0)
0000000A FrameSync
0000000C Jmp U32(0)
00000012 PushI32 U32(2016) ; examples/colorsquare/src/main.ts:2
00000018 SetLocal U32(0) ; examples/colorsquare/src/main.ts:2
0000001E PushI32 U32(4) ; examples/colorsquare/src/main.ts:4
00000024 Syscall U32(8193) ; examples/colorsquare/src/main.ts:4
0000002A JmpIfFalse U32(70) ; examples/colorsquare/src/main.ts:4
00000030 PushI32 U32(63488) ; examples/colorsquare/src/main.ts:4
00000036 Dup ; examples/colorsquare/src/main.ts:4
00000038 SetLocal U32(0) ; examples/colorsquare/src/main.ts:4
0000003E Pop ; examples/colorsquare/src/main.ts:4
00000040 Jmp U32(70)
00000046 PushI32 U32(5) ; examples/colorsquare/src/main.ts:5
0000004C Syscall U32(8193) ; examples/colorsquare/src/main.ts:5
00000052 JmpIfFalse U32(110) ; examples/colorsquare/src/main.ts:5
00000058 PushI32 U32(31) ; examples/colorsquare/src/main.ts:5
0000005E Dup ; examples/colorsquare/src/main.ts:5
00000060 SetLocal U32(0) ; examples/colorsquare/src/main.ts:5
00000066 Pop ; examples/colorsquare/src/main.ts:5
00000068 Jmp U32(110)
0000006E PushI32 U32(60) ; examples/colorsquare/src/main.ts:7
00000074 PushI32 U32(60) ; examples/colorsquare/src/main.ts:7
0000007A PushI32 U32(40) ; examples/colorsquare/src/main.ts:7
00000080 PushI32 U32(40) ; examples/colorsquare/src/main.ts:7
00000086 GetLocal U32(0) ; examples/colorsquare/src/main.ts:7
0000008C Syscall U32(4098) ; examples/colorsquare/src/main.ts:7
00000092 Pop ; examples/colorsquare/src/main.ts:7
00000094 Ret

Binary file not shown.

View File

@ -0,0 +1,140 @@
[
{
"pc": 18,
"file": "examples/colorsquare/src/main.ts",
"line": 2,
"col": 15
},
{
"pc": 24,
"file": "examples/colorsquare/src/main.ts",
"line": 2,
"col": 7
},
{
"pc": 30,
"file": "examples/colorsquare/src/main.ts",
"line": 4,
"col": 7
},
{
"pc": 36,
"file": "examples/colorsquare/src/main.ts",
"line": 4,
"col": 7
},
{
"pc": 42,
"file": "examples/colorsquare/src/main.ts",
"line": 4,
"col": 3
},
{
"pc": 48,
"file": "examples/colorsquare/src/main.ts",
"line": 4,
"col": 29
},
{
"pc": 54,
"file": "examples/colorsquare/src/main.ts",
"line": 4,
"col": 21
},
{
"pc": 56,
"file": "examples/colorsquare/src/main.ts",
"line": 4,
"col": 21
},
{
"pc": 62,
"file": "examples/colorsquare/src/main.ts",
"line": 4,
"col": 21
},
{
"pc": 70,
"file": "examples/colorsquare/src/main.ts",
"line": 5,
"col": 7
},
{
"pc": 76,
"file": "examples/colorsquare/src/main.ts",
"line": 5,
"col": 7
},
{
"pc": 82,
"file": "examples/colorsquare/src/main.ts",
"line": 5,
"col": 3
},
{
"pc": 88,
"file": "examples/colorsquare/src/main.ts",
"line": 5,
"col": 29
},
{
"pc": 94,
"file": "examples/colorsquare/src/main.ts",
"line": 5,
"col": 21
},
{
"pc": 96,
"file": "examples/colorsquare/src/main.ts",
"line": 5,
"col": 21
},
{
"pc": 102,
"file": "examples/colorsquare/src/main.ts",
"line": 5,
"col": 21
},
{
"pc": 110,
"file": "examples/colorsquare/src/main.ts",
"line": 7,
"col": 16
},
{
"pc": 116,
"file": "examples/colorsquare/src/main.ts",
"line": 7,
"col": 20
},
{
"pc": 122,
"file": "examples/colorsquare/src/main.ts",
"line": 7,
"col": 24
},
{
"pc": 128,
"file": "examples/colorsquare/src/main.ts",
"line": 7,
"col": 28
},
{
"pc": 134,
"file": "examples/colorsquare/src/main.ts",
"line": 7,
"col": 32
},
{
"pc": 140,
"file": "examples/colorsquare/src/main.ts",
"line": 7,
"col": 3
},
{
"pc": 146,
"file": "examples/colorsquare/src/main.ts",
"line": 7,
"col": 3
}
]

View File

@ -0,0 +1,8 @@
export function tick(): void {
let color = 0x07E0; // green
if (input.btnA()) color = 0xF800; // red
if (input.btnB()) color = 0x001F; // blue
gfx.fillRect(60, 60, 40, 40, color);
}