1use std::cmp::min;
15use std::collections::BTreeMap;
16use std::time::Duration;
17
18use tracing::{Level, event, span};
19
20use wasm_bindgen::prelude::*;
21
22use base::prelude::*;
23
24use crate::diagnostics::CurrentInstructionDiagnostics;
25
26use super::PETR;
27use super::alarm::{Alarm, AlarmKind, Alarmer, UnmaskedAlarm};
28use super::alarmunit::AlarmStatus;
29use super::context::Context;
30use super::control::{ConfigurationMemorySetup, ControlUnit, ResetMode, RunMode};
31use super::event::{InputEvent, OutputEvent};
32use super::io::{DeviceManager, ExtendedUnitState, InputFlagRaised, set_up_peripherals};
33use super::memory::{MemoryConfiguration, MemoryUnit};
34use super::{InputEventError, PanicOnUnmaskedAlarm};
35
36#[wasm_bindgen]
38pub struct Tx2 {
39 control: ControlUnit,
40 mem: MemoryUnit,
41 devices: DeviceManager,
42 next_execution_due: Option<Duration>,
43 next_hw_poll_due: Duration,
44 run_mode: RunMode,
45}
46
47impl Tx2 {
48 pub fn new(
50 ctx: &Context,
51 panic_on_unmasked_alarm: PanicOnUnmaskedAlarm,
52 mem_config: &MemoryConfiguration,
53 ) -> Tx2 {
54 let control = ControlUnit::new(
55 panic_on_unmasked_alarm,
56 ConfigurationMemorySetup::Uninitialised,
57 );
58 event!(
59 Level::DEBUG,
60 "Initial control unit state iis {:?}",
61 &control
62 );
63
64 let mem = MemoryUnit::new(ctx, mem_config);
65 let mut devices = DeviceManager::new();
66 set_up_peripherals(ctx, &mut devices);
67 Tx2 {
68 control,
69 mem,
70 devices,
71 next_execution_due: None,
72 next_hw_poll_due: ctx.simulated_time,
73 run_mode: RunMode::InLimbo,
74 }
75 }
76
77 #[must_use]
78 pub fn get_status_of_alarm(&self, name: &str) -> Option<AlarmStatus> {
79 self.control.get_status_of_alarm(name)
80 }
81
82 #[must_use]
83 pub fn get_alarm_statuses(&self) -> Vec<AlarmStatus> {
84 self.control.get_alarm_statuses()
85 }
86
87 pub fn set_alarm_masked(&mut self, kind: AlarmKind, masked: bool) -> Result<(), Alarm> {
88 self.control.set_alarm_masked(kind, masked)
89 }
90
91 pub fn set_run_mode(&mut self, run_mode: RunMode) {
92 self.run_mode = run_mode;
93 }
94
95 pub fn set_next_execution_due(&mut self, now: Duration, newval: Option<Duration>) {
98 if let Some(t) = newval {
99 assert!(now <= t);
100 }
101 event!(
102 Level::TRACE,
103 "Changing next_execution_due from {:?} to {:?}",
104 self.next_execution_due,
105 newval,
106 );
107 self.next_execution_due = newval;
108 }
109
110 fn set_next_hw_poll_due(&mut self, now: Duration, newval: Duration) {
113 assert!(now <= newval);
114 event!(
115 Level::TRACE,
116 "Changing next_hw_poll_due from {:?} to {:?}",
117 self.next_hw_poll_due,
118 newval,
119 );
120 self.next_hw_poll_due = newval;
121 }
122
123 pub fn codabo(&mut self, ctx: &Context, reset_mode: &ResetMode) -> Result<(), Alarm> {
125 self.control
126 .codabo(ctx, reset_mode, &mut self.devices, &mut self.mem)
127 }
128
129 fn on_input_event(
130 &mut self,
131 ctx: &Context,
132 unit: Unsigned6Bit,
133 event: InputEvent,
134 ) -> Result<InputFlagRaised, InputEventError> {
135 match self.devices.on_input_event(ctx, unit, event) {
136 Ok(InputFlagRaised::Yes) => {
137 self.devices.update_poll_time(ctx, unit);
140 Ok(InputFlagRaised::Yes)
141 }
142 Ok(InputFlagRaised::No) => Ok(InputFlagRaised::No),
143 Err(e) => Err(e),
144 }
145 }
146
147 pub fn mount_paper_tape(
149 &mut self,
150 ctx: &Context,
151 data: Vec<u8>,
152 ) -> Result<InputFlagRaised, InputEventError> {
153 self.on_input_event(ctx, PETR, InputEvent::PetrMountPaperTape { data })
154 }
155
156 pub fn lw_input(
159 &mut self,
160 ctx: &Context,
161 unit: Unsigned6Bit,
162 codes: &[Unsigned6Bit],
163 ) -> Result<(bool, InputFlagRaised), String> {
164 let event = InputEvent::LwKeyboardInput {
165 data: codes.to_vec(),
166 };
167 match self.on_input_event(ctx, unit, event) {
168 Ok(flag_raise) => {
169 if flag_raise == InputFlagRaised::Yes {
170 self.next_hw_poll_due = ctx.simulated_time;
173 }
174 Ok((true, flag_raise))
175 }
176 Err(InputEventError::BufferUnavailable) => Ok((false, InputFlagRaised::No)),
177 Err(e) => Err(e.to_string()),
178 }
179 }
180
181 #[must_use]
184 pub fn next_tick(&self) -> Duration {
185 match (
186 self.run_mode,
187 self.next_hw_poll_due,
188 self.next_execution_due,
189 ) {
190 (RunMode::InLimbo, hw, _) | (RunMode::Running, hw, None) => hw,
191 (RunMode::Running, hw, Some(insn)) => min(hw, insn),
192 }
193 }
194
195 fn poll_hw(&mut self, ctx: &Context) -> Result<(), Alarm> {
196 let now = &ctx.simulated_time;
198 event!(Level::TRACE, "polling hardware for updates (now={:?})", now);
199 match self
200 .control
201 .poll_hardware(ctx, &mut self.devices, self.run_mode)
202 {
203 Ok((mode, next)) => {
204 if self.run_mode != mode {
205 event!(
206 Level::DEBUG,
207 "poll_hardware updating run_mode to ({mode:?})"
208 );
209 }
210 self.run_mode = mode;
211
212 self.set_next_hw_poll_due(
213 *now,
214 match next {
215 Some(when) => when,
216 None => {
217 *now + Duration::from_micros(5)
220 }
221 },
222 );
223 Ok(())
224 }
225 Err(alarm) => {
226 event!(
227 Level::INFO,
228 "Alarm raised during hardware polling at system time {:?}",
229 now
230 );
231 let diags: CurrentInstructionDiagnostics = self.control.diagnostics().clone();
232 self.control.fire_if_not_masked(alarm, diags)
233 }
234 }
235 }
236
237 fn execute_one_instruction(
238 &mut self,
239 ctx: &Context,
240 ) -> Result<(u64, Option<OutputEvent>), UnmaskedAlarm> {
241 let now = &ctx.simulated_time;
242 if self.run_mode == RunMode::InLimbo {
243 event!(
244 Level::WARN,
245 "execute_one_instruction was called while machine is in LIMBO"
246 );
247 self.set_next_execution_due(*now, None);
248 return Ok((0, None));
249 }
250
251 let mut hardware_state_changed: Option<SequenceNumber> = None;
252 match self.control.execute_instruction(
253 ctx,
254 &mut self.devices,
255 &mut self.mem,
256 &mut hardware_state_changed,
257 ) {
258 Err((alarm, address)) => {
259 event!(
260 Level::INFO,
261 "Alarm raised during instruction execution at {:o} at system time {:?}",
262 address,
263 &ctx.simulated_time
264 );
265 self.set_next_execution_due(*now, None);
266 assert!(self.unmasked_alarm_active());
267 Err(UnmaskedAlarm {
268 alarm,
269 address: Some(address),
270 when: ctx.simulated_time,
271 })
272 }
273 Ok((ns, new_run_mode, maybe_output)) => {
274 match (self.run_mode, new_run_mode) {
275 (RunMode::Running, RunMode::InLimbo) => {
276 event!(Level::DEBUG, "Entering LIMBO");
277 self.set_next_execution_due(*now, None);
278 }
279 (RunMode::InLimbo, RunMode::Running) => {
280 event!(Level::DEBUG, "Leaving LIMBO");
281 self.set_next_execution_due(*now, Some(*now + Duration::from_nanos(1)));
282 }
283 (old_run_mode, new_run_mode) => {
284 assert_eq!(old_run_mode, new_run_mode);
285 }
286 }
287 self.run_mode = new_run_mode;
288
289 if let Some(seq) = hardware_state_changed {
290 event!(
293 Level::DEBUG,
294 "hardware state change for unit {seq}; bringing forward next hardware poll"
295 );
296 self.set_next_hw_poll_due(*now, *now + Duration::from_nanos(1));
297 } else {
298 event!(
299 Level::TRACE,
300 "current instruction did not affect the hardware"
301 );
302 }
303 Ok((ns, maybe_output))
305 }
306 }
307 }
308
309 pub fn tick(&mut self, ctx: &Context) -> Result<Option<OutputEvent>, UnmaskedAlarm> {
312 let system_time = ctx.simulated_time;
313 let tick_span = span!(Level::INFO, "tick", t=?system_time);
314 let _enter = tick_span.enter();
315 event!(
316 Level::TRACE,
317 "tick: system_time={:?}, next_execution_due={:?}, next_hw_poll_due={:?}",
318 system_time,
319 self.next_execution_due,
320 self.next_hw_poll_due
321 );
322 let due: Duration = if let Some(inst_due) = self.next_execution_due {
323 min(self.next_hw_poll_due, inst_due)
324 } else {
325 self.next_hw_poll_due
326 };
327 if due > system_time {
328 let premature_by = due - system_time;
329 event!(
330 Level::WARN,
331 "tick() was called {premature_by:?} prematurely (run mode is {:?})",
332 &self.run_mode
333 );
334 }
335
336 if ctx.simulated_time >= self.next_hw_poll_due {
337 event!(
338 Level::DEBUG,
339 "tick(): polling the hardware (because this is due now)"
340 );
341 let prev_poll_due = self.next_hw_poll_due;
342 match self.poll_hw(ctx) {
343 Ok(()) => {
344 if self.next_hw_poll_due == prev_poll_due {
345 event!(
346 Level::WARN,
347 "polled hardware successfully at system time {:?}, but poll_hw returned with next_hw_poll_due={:?}",
348 system_time,
349 self.next_hw_poll_due
350 );
351 }
352 }
353 Err(alarm) => {
354 return Err(UnmaskedAlarm {
355 alarm,
356 address: None, when: ctx.simulated_time,
358 });
359 }
360 }
361 } else {
362 event!(
363 Level::TRACE,
364 "not polling hardware for updates (remaining wait: {:?})",
365 self.next_hw_poll_due - system_time,
366 );
367 }
368
369 if self.run_mode == RunMode::InLimbo {
370 let interval: Duration = self.next_hw_poll_due - system_time;
375 event!(
376 Level::TRACE,
377 "machine is in limbo, waiting {:?} for a flag to be raised",
378 &interval,
379 );
380 Ok(None)
383 } else if self.unmasked_alarm_active() {
384 event!(
385 Level::DEBUG,
386 "will not execute the next instruction because there is an an unmasked alarm."
387 );
388 Ok(None) } else {
390 match self.next_execution_due {
392 Some(next) if next <= system_time => {
393 let (ns, maybe_output) = self.execute_one_instruction(ctx)?;
394 let mut due = next + Duration::from_nanos(ns);
395 if due <= system_time {
396 due = system_time + Duration::from_nanos(1);
397 }
398 self.set_next_execution_due(system_time, Some(due));
399 Ok(maybe_output)
400 }
401 None => {
402 event!(
403 Level::TRACE,
404 "instruction execution clock is not running, no instruction to execute"
405 );
406 Ok(None)
407 }
408 Some(next) => {
409 let wait_for = next - system_time;
410 event!(
411 Level::TRACE,
412 "next instruction execution not due for {wait_for:?}"
413 );
414 Ok(None)
415 }
416 }
417 }
418 }
419
420 #[must_use]
421 pub fn unmasked_alarm_active(&self) -> bool {
422 self.control.unmasked_alarm_active()
423 }
424
425 pub fn drain_alarm_changes(&mut self) -> BTreeMap<AlarmKind, AlarmStatus> {
426 self.control.drain_alarm_changes()
427 }
428
429 pub fn disconnect_all_devices(&mut self, ctx: &Context) -> Result<(), Alarm> {
430 self.devices.disconnect_all(ctx, &mut self.control)
431 }
432
433 fn extended_state_of_software_sequence(
434 &self,
435 seq: Unsigned6Bit,
436 index_value: Signed18Bit,
437 ) -> ExtendedUnitState {
438 ExtendedUnitState {
439 flag: self.control.current_flag_state(&seq),
440 connected: false,
441 in_maintenance: false,
442 name: format!("Sequence {seq:>02o}"),
443 status: None,
444 text_info: "(software only)".to_string(),
445 index_value,
446 }
447 }
448
449 fn software_sequence_statuses(&self) -> BTreeMap<Unsigned6Bit, ExtendedUnitState> {
450 let regvalues = self.control.inspect_registers();
451 [u6!(0), u6!(0o76), u6!(0o77)]
452 .into_iter()
453 .map(|seq| {
454 let index_value: &Signed18Bit = regvalues
455 .index_regs
456 .get(usize::from(seq))
457 .expect("software sequences should all have valid index register values");
458 (
459 seq,
460 self.extended_state_of_software_sequence(seq, *index_value),
461 )
462 })
463 .collect()
464 }
465
466 pub fn sequence_statuses(
467 &mut self,
468 ctx: &Context,
469 ) -> Result<BTreeMap<Unsigned6Bit, ExtendedUnitState>, Alarm> {
470 let mut result: BTreeMap<Unsigned6Bit, ExtendedUnitState> =
472 self.devices.device_statuses(ctx, &mut self.control)?;
473 result.append(&mut self.software_sequence_statuses());
475 Ok(result)
476 }
477
478 pub fn drain_device_changes(
479 &mut self,
480 ctx: &Context,
481 ) -> Result<BTreeMap<Unsigned6Bit, ExtendedUnitState>, Alarm> {
482 let mut mapping = self.devices.drain_changes(ctx, &mut self.control)?;
483 for (seq, index_value) in self.control.drain_flag_changes().into_iter() {
484 mapping
485 .entry(seq)
486 .or_insert_with(|| self.extended_state_of_software_sequence(seq, index_value));
487 }
488 Ok(mapping)
489 }
490}