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