1#![deny(unreachable_pub)]
2#![deny(unsafe_code)]
3#![deny(unused_crate_dependencies)]
4
5mod alarm;
6mod context;
7mod io;
8mod lw;
9mod samples;
10mod utils;
11
12#[cfg(test)]
19use wasm_bindgen_test as _;
20
21use cpu::*;
22
23use float_next_after::NextAfter;
24use lw::display_lw_unit_output_event;
25use tracing::{Level, event};
26use wasm_bindgen::prelude::*;
27use web_sys::{Document, Window};
28
29pub use alarm::{
30 drain_alarm_changes, get_alarm_statuses, set_alarm_masked, tx2_unmasked_alarm_active,
31};
32use context::make_context;
33pub use io::HtmlCanvas2DPainter;
34pub use io::*;
35
36#[wasm_bindgen(start)]
37pub fn start() -> Result<(), JsValue> {
38 utils::set_panic_hook();
39 Ok(())
40}
41
42fn try_log_level_from_str(log_level: &str) -> Result<Level, String> {
43 match log_level {
44 "trace" => Ok(Level::TRACE),
45 "debug" => Ok(Level::DEBUG),
46 "info" => Ok(Level::INFO),
47 "warn" => Ok(Level::WARN),
48 "error" => Ok(Level::ERROR),
49 invalid => Err(format!("invalid log level '{invalid}'")),
50 }
51}
52
53#[wasm_bindgen]
54pub fn init(log_level: &str) -> Result<(), JsValue> {
55 tracing_wasm::set_as_global_default_with_config(
56 tracing_wasm::WASMLayerConfigBuilder::new()
57 .set_max_level(try_log_level_from_str(log_level)?)
58 .build(),
59 );
60 event!(
61 Level::INFO,
62 "init: tracing iniialised (max level is {log_level})"
63 );
64 Ok(())
65}
66
67#[wasm_bindgen]
68pub fn create_tx2(simulated_system_time_secs: f64, elapsed_time_secs: f64) -> Tx2 {
69 let mem_config = MemoryConfiguration {
70 with_u_memory: false,
71 };
72 let context = make_context(simulated_system_time_secs, elapsed_time_secs);
73 let panic_on_unmasked_alarm = cpu::PanicOnUnmaskedAlarm::No;
74 Tx2::new(&context, panic_on_unmasked_alarm, &mem_config)
75}
76
77#[wasm_bindgen]
78pub fn tx2_next_simulated_tick(tx2: &Tx2) -> f64 {
79 let next = tx2.next_tick();
80 let f = next.as_secs_f64().next_after(f64::INFINITY);
84 event!(
85 Level::TRACE,
86 "tx2_next_simulated_tick: next={next:?}, f={f:?}"
87 );
88 f
89}
90
91fn window() -> Window {
92 web_sys::window().expect("no global window exists")
93}
94
95fn document() -> Document {
96 window()
97 .document()
98 .expect("should have a document on the window")
99}
100
101fn display_output_event(output_event: OutputEvent) {
102 match output_event {
103 OutputEvent::LincolnWriterPrint { unit, ch } => {
104 let doc: Document = document();
105 display_lw_unit_output_event(unit, ch, doc)
106 }
107 }
108}
109
110#[wasm_bindgen]
111pub fn tx2_do_tick(tx2: &mut Tx2, simulated_time: f64, real_elapsed_time: f64) {
112 let context = make_context(simulated_time, real_elapsed_time);
113 event!(
114 Level::TRACE,
115 "tx2_do_tick: simulated_time={simulated_time:?}, real_elapsed_time={real_elapsed_time:?}, context={context:?}"
116 );
117 match tx2.tick(&context) {
118 Ok(Some(output)) => {
119 event!(
120 Level::INFO,
121 "tx2_do_tick: handling output event for {output:?}"
122 );
123 display_output_event(output);
124 }
125 Ok(None) => (),
126 Err(e) => {
127 event!(Level::ERROR, "New unmasked TX-2 alarm: {:?}", e);
128 }
129 }
130}
131
132#[wasm_bindgen]
133pub fn tx2_codabo(tx2: &mut Tx2, simulated_time: f64, elapsed_time_secs: f64) {
134 event!(Level::INFO, "codabo");
135 let context = make_context(simulated_time, elapsed_time_secs);
136 if let Err(e) = tx2.codabo(&context, &ResetMode::ResetTSP) {
137 panic!("codabo failed: {e}");
138 }
139 tx2.set_next_execution_due(context.simulated_time, Some(context.simulated_time));
140 tx2.set_run_mode(RunMode::Running);
141}
142
143#[wasm_bindgen]
144pub fn create_html_canvas_2d_painter(
145 context: web_sys::CanvasRenderingContext2d,
146 hits_only: bool,
147) -> Result<HtmlCanvas2DPainter, JsValue> {
148 HtmlCanvas2DPainter::new(context, hits_only).map_err(|e| e.to_string().into())
149}