From 313480fec41a2656584f2c17b35c983e758399be Mon Sep 17 00:00:00 2001 From: Nilton Constantino Date: Tue, 20 Jan 2026 11:11:31 +0000 Subject: [PATCH 1/2] add prometeuc --- Cargo.lock | 398 +++++++++++++++++- Cargo.toml | 2 + crates/prometeuc/Cargo.toml | 8 + crates/prometeuc/src/cli.rs | 35 ++ crates/prometeuc/src/codegen/codegen.rs | 324 ++++++++++++++ crates/prometeuc/src/codegen/mod.rs | 5 +- crates/prometeuc/src/compiler.rs | 72 ++++ crates/prometeuc/src/main.rs | 38 +- crates/prometeuc/src/syscall_map.rs | 23 + examples/colorsquare/build/program.disasm.txt | 29 ++ examples/colorsquare/build/program.pbc | Bin 0 -> 150 bytes examples/colorsquare/build/symbols.json | 140 ++++++ examples/colorsquare/src/main.ts | 8 + 13 files changed, 1076 insertions(+), 6 deletions(-) create mode 100644 crates/prometeuc/src/cli.rs create mode 100644 crates/prometeuc/src/codegen/codegen.rs create mode 100644 crates/prometeuc/src/compiler.rs create mode 100644 crates/prometeuc/src/syscall_map.rs create mode 100644 examples/colorsquare/build/program.disasm.txt create mode 100644 examples/colorsquare/build/program.pbc create mode 100644 examples/colorsquare/build/symbols.json create mode 100644 examples/colorsquare/src/main.ts diff --git a/Cargo.lock b/Cargo.lock index 552dd83b..5876ca92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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,21 @@ dependencies = [ "winit", ] +[[package]] +name = "prometeuc" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "oxc_allocator", + "oxc_ast", + "oxc_parser", + "oxc_span", + "prometeu-bytecode", + "serde", + "serde_json", +] + [[package]] name = "quick-xml" version = "0.38.4" @@ -1631,6 +1947,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 +2091,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 +2146,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 +2201,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 +2234,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 +2332,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 +2449,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 +2479,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" diff --git a/Cargo.toml b/Cargo.toml index 98f1879e..a726ea0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,8 @@ members = [ "crates/prometeu-core", "crates/prometeu-runtime-desktop", "crates/prometeu", + "crates/prometeu-bytecode", + "crates/prometeuc", ] resolver = "2" diff --git a/crates/prometeuc/Cargo.toml b/crates/prometeuc/Cargo.toml index 7e87b37e..50109ad2 100644 --- a/crates/prometeuc/Cargo.toml +++ b/crates/prometeuc/Cargo.toml @@ -5,3 +5,11 @@ edition = "2021" [dependencies] prometeu-bytecode = { path = "../prometeu-bytecode" } +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" diff --git a/crates/prometeuc/src/cli.rs b/crates/prometeuc/src/cli.rs new file mode 100644 index 00000000..1f57b91c --- /dev/null +++ b/crates/prometeuc/src/cli.rs @@ -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, + + /// Output PBC file + #[arg(short, long)] + out: Option, + + /// Emit disassembly file + #[arg(long, default_value_t = true)] + emit_disasm: bool, + + /// Emit symbols file + #[arg(long, default_value_t = true)] + emit_symbols: bool, + }, +} diff --git a/crates/prometeuc/src/codegen/codegen.rs b/crates/prometeuc/src/codegen/codegen.rs new file mode 100644 index 00000000..1dddc283 --- /dev/null +++ b/crates/prometeuc/src/codegen/codegen.rs @@ -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, + instructions: Vec<(Asm, bool)>, // (Asm, has_symbol) + locals: HashMap, + 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> { + // 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 = 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 { + 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 { + 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, 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, + } + } + } + } + } + } +} diff --git a/crates/prometeuc/src/codegen/mod.rs b/crates/prometeuc/src/codegen/mod.rs index 141c9923..484803d5 100644 --- a/crates/prometeuc/src/codegen/mod.rs +++ b/crates/prometeuc/src/codegen/mod.rs @@ -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; \ No newline at end of file diff --git a/crates/prometeuc/src/compiler.rs b/crates/prometeuc/src/compiler.rs new file mode 100644 index 00000000..cda88df4 --- /dev/null +++ b/crates/prometeuc/src/compiler.rs @@ -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::>() + .join(" "); + + disasm_text.push_str(&format!("{:08X} {:?} {}{}\n", instr.pc, instr.opcode, operands_str, comment)); + } + fs::write(disasm_path, disasm_text)?; + } + + Ok(()) +} diff --git a/crates/prometeuc/src/main.rs b/crates/prometeuc/src/main.rs index 2e184d62..b91734e5 100644 --- a/crates/prometeuc/src/main.rs +++ b/crates/prometeuc/src/main.rs @@ -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(()) } diff --git a/crates/prometeuc/src/syscall_map.rs b/crates/prometeuc/src/syscall_map.rs new file mode 100644 index 00000000..768164a8 --- /dev/null +++ b/crates/prometeuc/src/syscall_map.rs @@ -0,0 +1,23 @@ +pub const SYS_GFX_FILL_RECT: u32 = 0x1002; +pub const SYS_INPUT_GET_PAD: u32 = 0x2001; + +pub const BTN_UP: u32 = 0; +pub const BTN_DOWN: u32 = 1; +pub const BTN_LEFT: u32 = 2; +pub const BTN_RIGHT: u32 = 3; +pub const BTN_A: u32 = 4; +pub const BTN_B: u32 = 5; +pub const BTN_X: u32 = 6; +pub const BTN_Y: u32 = 7; +pub const BTN_L: u32 = 8; +pub const BTN_R: u32 = 9; +pub const BTN_START: u32 = 10; +pub const BTN_SELECT: u32 = 11; + +pub fn map_syscall(name: &str) -> Option { + match name { + "gfx.fillRect" => Some(SYS_GFX_FILL_RECT), + "input.btnA" | "input.btnB" | "input.get_pad" => Some(SYS_INPUT_GET_PAD), + _ => None, + } +} diff --git a/examples/colorsquare/build/program.disasm.txt b/examples/colorsquare/build/program.disasm.txt new file mode 100644 index 00000000..b5f3343d --- /dev/null +++ b/examples/colorsquare/build/program.disasm.txt @@ -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 diff --git a/examples/colorsquare/build/program.pbc b/examples/colorsquare/build/program.pbc new file mode 100644 index 0000000000000000000000000000000000000000..9e4d093b2d0f4db4e416061d916c18d07f993ca2 GIT binary patch literal 150 zcmWGw5MqFU1_mZDL!99OI|G9=l+6O93m6y`7#Nrt+< Date: Tue, 20 Jan 2026 11:45:46 +0000 Subject: [PATCH 2/2] organized syscall maps --- Cargo.lock | 1 + crates/prometeu-core/src/lib.rs | 2 +- crates/prometeu-core/src/model/button.rs | 17 +++ crates/prometeu-core/src/model/mod.rs | 2 +- crates/prometeu-core/src/prometeu_os/mod.rs | 2 + .../src/prometeu_os/prometeu_os.rs | 45 ++++--- .../prometeu-core/src/prometeu_os/syscalls.rs | 110 ++++++++++++++++++ crates/prometeuc/Cargo.toml | 1 + crates/prometeuc/src/syscall_map.rs | 36 +++--- 9 files changed, 175 insertions(+), 41 deletions(-) create mode 100644 crates/prometeu-core/src/prometeu_os/syscalls.rs diff --git a/Cargo.lock b/Cargo.lock index 5876ca92..e61a0629 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1919,6 +1919,7 @@ dependencies = [ "oxc_parser", "oxc_span", "prometeu-bytecode", + "prometeu-core", "serde", "serde_json", ] diff --git a/crates/prometeu-core/src/lib.rs b/crates/prometeu-core/src/lib.rs index fe373146..87c706c6 100644 --- a/crates/prometeu-core/src/lib.rs +++ b/crates/prometeu-core/src/lib.rs @@ -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; diff --git a/crates/prometeu-core/src/model/button.rs b/crates/prometeu-core/src/model/button.rs index 72ab2bdb..75812ff0 100644 --- a/crates/prometeu-core/src/model/button.rs +++ b/crates/prometeu-core/src/model/button.rs @@ -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, diff --git a/crates/prometeu-core/src/model/mod.rs b/crates/prometeu-core/src/model/mod.rs index fc42885a..8eb4733b 100644 --- a/crates/prometeu-core/src/model/mod.rs +++ b/crates/prometeu-core/src/model/mod.rs @@ -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; diff --git a/crates/prometeu-core/src/prometeu_os/mod.rs b/crates/prometeu-core/src/prometeu_os/mod.rs index 64c645ec..87fcf170 100644 --- a/crates/prometeu-core/src/prometeu_os/mod.rs +++ b/crates/prometeu-core/src/prometeu_os/mod.rs @@ -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; \ No newline at end of file diff --git a/crates/prometeu-core/src/prometeu_os/prometeu_os.rs b/crates/prometeu-core/src/prometeu_os/prometeu_os.rs index 04748f08..831b7342 100644 --- a/crates/prometeu-core/src/prometeu_os/prometeu_os.rs +++ b/crates/prometeu-core/src/prometeu_os/prometeu_os.rs @@ -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 { 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)), } } } \ No newline at end of file diff --git a/crates/prometeu-core/src/prometeu_os/syscalls.rs b/crates/prometeu-core/src/prometeu_os/syscalls.rs new file mode 100644 index 00000000..f2aef4be --- /dev/null +++ b/crates/prometeu-core/src/prometeu_os/syscalls.rs @@ -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 { + 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 { + 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, + } + } +} diff --git a/crates/prometeuc/Cargo.toml b/crates/prometeuc/Cargo.toml index 50109ad2..539e6e3f 100644 --- a/crates/prometeuc/Cargo.toml +++ b/crates/prometeuc/Cargo.toml @@ -5,6 +5,7 @@ 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" diff --git a/crates/prometeuc/src/syscall_map.rs b/crates/prometeuc/src/syscall_map.rs index 768164a8..50ffae92 100644 --- a/crates/prometeuc/src/syscall_map.rs +++ b/crates/prometeuc/src/syscall_map.rs @@ -1,23 +1,27 @@ -pub const SYS_GFX_FILL_RECT: u32 = 0x1002; -pub const SYS_INPUT_GET_PAD: u32 = 0x2001; +use prometeu_core::prometeu_os::Syscall; +use prometeu_core::model::ButtonId; -pub const BTN_UP: u32 = 0; -pub const BTN_DOWN: u32 = 1; -pub const BTN_LEFT: u32 = 2; -pub const BTN_RIGHT: u32 = 3; -pub const BTN_A: u32 = 4; -pub const BTN_B: u32 = 5; -pub const BTN_X: u32 = 6; -pub const BTN_Y: u32 = 7; -pub const BTN_L: u32 = 8; -pub const BTN_R: u32 = 9; -pub const BTN_START: u32 = 10; -pub const BTN_SELECT: u32 = 11; +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 { + if let Some(syscall) = Syscall::from_name(name) { + return Some(syscall as u32); + } + + // Fallback para nomes especiais do compilador match name { - "gfx.fillRect" => Some(SYS_GFX_FILL_RECT), - "input.btnA" | "input.btnB" | "input.get_pad" => Some(SYS_INPUT_GET_PAD), + "input.btnA" | "input.btnB" => Some(Syscall::InputGetPad as u32), _ => None, } } -- 2.47.2