1+ #![ allow( clippy:: boxed_local) ]
2+
3+ use std:: time:: Duration ;
4+
5+ use embedded_graphics:: pixelcolor:: Rgb888 ;
6+ use embedded_graphics_web_simulator:: {
7+ display:: WebSimulatorDisplay , output_settings:: OutputSettings ,
8+ } ;
9+ use mindy:: {
10+ parser:: LogicParser ,
11+ types:: { PackedPoint2 , ProcessorConfig , ProcessorLinkConfig , content} ,
12+ vm:: {
13+ Building , BuildingData , EmbeddedDisplayData , InstructionResult , LVar , LogicVM ,
14+ LogicVMBuilder ,
15+ buildings:: { HYPER_PROCESSOR , LOGIC_PROCESSOR , MICRO_PROCESSOR , WORLD_PROCESSOR } ,
16+ variables:: Constants ,
17+ } ,
18+ } ;
119use wasm_bindgen:: prelude:: * ;
20+ use web_sys:: Element ;
221
322#[ global_allocator]
423static ALLOC : wee_alloc:: WeeAlloc = wee_alloc:: WeeAlloc :: INIT ;
524
6- #[ wasm_bindgen]
7- extern "C" {
8- fn alert ( s : & str ) ;
25+ const MAX_DELTA : f64 = 6. ;
26+
27+ #[ allow( unused_macros) ]
28+ macro_rules! log {
29+ ( $( $t: tt) * ) => {
30+ web_sys:: console:: log_1( & format!( $( $t) * ) . into( ) ) ;
31+ } ;
932}
1033
1134#[ wasm_bindgen]
@@ -14,6 +37,172 @@ pub fn init() {
1437}
1538
1639#[ wasm_bindgen]
17- pub fn greet ( ) {
18- alert ( "Hello world!" ) ;
40+ pub struct WebLogicVM {
41+ vm : LogicVM ,
42+ globals : Constants ,
43+ logic_parser : LogicParser ,
44+ prev_timestamp : Option < f64 > ,
45+ }
46+
47+ impl WebLogicVM {
48+ fn new ( vm : LogicVM , globals : Constants ) -> Self {
49+ Self {
50+ vm,
51+ globals,
52+ logic_parser : LogicParser :: new ( ) ,
53+ prev_timestamp : None ,
54+ }
55+ }
56+ }
57+
58+ #[ wasm_bindgen]
59+ impl WebLogicVM {
60+ pub fn do_tick ( & mut self , timestamp : f64 ) {
61+ // convert to seconds
62+ let timestamp = timestamp / 1000. ;
63+
64+ // nominal 60 ticks per second
65+ let delta = match self . prev_timestamp {
66+ Some ( prev_timestamp) => ( timestamp - prev_timestamp) * 60. ,
67+ None => 1. ,
68+ }
69+ . min ( MAX_DELTA ) ;
70+
71+ self . prev_timestamp = Some ( timestamp) ;
72+ self . vm
73+ . do_tick_with_delta ( Duration :: from_secs_f64 ( timestamp) , delta) ;
74+ }
75+
76+ #[ wasm_bindgen( getter) ]
77+ pub fn time ( & self ) -> f64 {
78+ self . vm . time ( ) . as_secs_f64 ( )
79+ }
80+
81+ pub fn set_processor_config (
82+ & mut self ,
83+ position : PackedPoint2 ,
84+ code : & str ,
85+ links : Option < Box < [ PackedPoint2 ] > > ,
86+ ) -> Result < ( ) , String > {
87+ let ast = self . logic_parser . parse ( code) . map_err ( |e| e. to_string ( ) ) ?;
88+
89+ let building = self
90+ . vm
91+ . building ( position)
92+ . ok_or_else ( || format ! ( "building does not exist: {position}" ) ) ?;
93+
94+ let BuildingData :: Processor ( processor) = & mut * building. data . borrow_mut ( ) else {
95+ return Err ( format ! (
96+ "expected processor at {position} but got {}" ,
97+ building. block. name
98+ ) ) ;
99+ } ;
100+
101+ processor
102+ . update_config (
103+ ast,
104+ links
105+ . map ( |v| {
106+ v. iter ( )
107+ . map ( |p| {
108+ ProcessorLinkConfig :: unnamed ( p. x - position. x , p. y - position. y )
109+ } )
110+ . collect :: < Vec < _ > > ( )
111+ } )
112+ . as_deref ( ) ,
113+ & self . vm ,
114+ building,
115+ & self . globals ,
116+ )
117+ . map_err ( |e| e. to_string ( ) )
118+ }
119+ }
120+
121+ #[ wasm_bindgen]
122+ pub struct WebLogicVMBuilder {
123+ builder : LogicVMBuilder ,
124+ }
125+
126+ #[ wasm_bindgen]
127+ impl WebLogicVMBuilder {
128+ #[ wasm_bindgen( constructor) ]
129+ pub fn new ( ) -> Self {
130+ Self {
131+ builder : LogicVMBuilder :: new ( ) ,
132+ }
133+ }
134+
135+ pub fn add_processor ( & mut self , position : PackedPoint2 , kind : ProcessorKind ) {
136+ self . builder . add_building (
137+ Building :: from_processor_config (
138+ kind. name ( ) ,
139+ position,
140+ & ProcessorConfig :: default ( ) ,
141+ & self . builder ,
142+ )
143+ . expect ( "failed to create processor" ) ,
144+ ) ;
145+ }
146+
147+ pub fn add_display (
148+ & mut self ,
149+ position : PackedPoint2 ,
150+ width : u32 ,
151+ height : u32 ,
152+ parent : & Element ,
153+ ) {
154+ let display = WebSimulatorDisplay :: < Rgb888 > :: new (
155+ ( width, height) ,
156+ & OutputSettings :: default ( ) ,
157+ Some ( parent) ,
158+ ) ;
159+
160+ let display_data = EmbeddedDisplayData :: new (
161+ display,
162+ Some ( Box :: new ( |display| {
163+ display. flush ( ) . expect ( "failed to flush display" ) ;
164+ InstructionResult :: Yield
165+ } ) ) ,
166+ )
167+ . expect ( "failed to initialize display" ) ;
168+
169+ self . builder . add_building ( Building :: new (
170+ content:: blocks:: FROM_NAME [ "tile-logic-display" ] ,
171+ position,
172+ display_data. into ( ) ,
173+ ) ) ;
174+ }
175+
176+ pub fn build ( self ) -> Result < WebLogicVM , String > {
177+ let globals = LVar :: create_global_constants ( ) ;
178+ match self . builder . build_with_globals ( & globals) {
179+ Ok ( vm) => Ok ( WebLogicVM :: new ( vm, globals) ) ,
180+ Err ( e) => Err ( e. to_string ( ) ) ,
181+ }
182+ }
183+ }
184+
185+ impl Default for WebLogicVMBuilder {
186+ fn default ( ) -> Self {
187+ Self :: new ( )
188+ }
189+ }
190+
191+ #[ wasm_bindgen]
192+ pub enum ProcessorKind {
193+ Micro ,
194+ Logic ,
195+ Hyper ,
196+ World ,
197+ }
198+
199+ impl ProcessorKind {
200+ fn name ( & self ) -> & str {
201+ match self {
202+ Self :: Micro => MICRO_PROCESSOR ,
203+ Self :: Logic => LOGIC_PROCESSOR ,
204+ Self :: Hyper => HYPER_PROCESSOR ,
205+ Self :: World => WORLD_PROCESSOR ,
206+ }
207+ }
19208}
0 commit comments