chipset/
pit.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use bitfield_struct::bitfield;
5use chipset_device::ChipsetDevice;
6use chipset_device::io::IoError;
7use chipset_device::io::IoResult;
8use chipset_device::pio::PortIoIntercept;
9use chipset_device::poll_device::PollDevice;
10use inspect::Inspect;
11use inspect::InspectMut;
12use open_enum::open_enum;
13use std::ops::RangeInclusive;
14use std::task::Context;
15use std::task::Poll;
16use std::time::Duration;
17use thiserror::Error;
18use vmcore::device_state::ChangeDeviceState;
19use vmcore::line_interrupt::LineInterrupt;
20use vmcore::vmtime::VmTime;
21use vmcore::vmtime::VmTimeAccess;
22
23#[rustfmt::skip]
24#[derive(Inspect)]
25#[bitfield(u8)]
26struct ControlWord {
27    #[bits(1)] bcd: bool,
28    #[inspect(with = "|x| Mode::from(*x)")]
29    #[bits(3)] mode: u8,
30    #[inspect(with = "|x| RwMode(*x)")]
31    #[bits(2)] rw: u8,
32    #[inspect(skip)] // Ignore `select` since it's not part of the persistent state.
33    #[bits(2)] select: u8,
34}
35
36#[rustfmt::skip]
37#[bitfield(u8)]
38struct StatusWord {
39    #[bits(1)] bcd: bool,
40    #[bits(3)] mode: u8,
41    #[bits(2)] rw: u8,
42    #[bits(1)] null: bool,
43    #[bits(1)] out: bool,
44}
45
46#[bitfield(u8)]
47struct ReadBackCommand {
48    reserved: bool,
49    counter0: bool,
50    counter1: bool,
51    counter2: bool,
52    status: bool,
53    count: bool,
54    #[bits(2)]
55    one: u8,
56}
57
58const PIT_TIMER_RANGE_START: u16 = 0x40;
59const PIT_TIMER_RANGE_END: u16 = 0x42;
60const PIT_CONTROL_REGISTER: u16 = 0x43;
61const PIT_PORT61_REGISTER: u16 = 0x61;
62
63#[derive(Debug, Inspect)]
64struct Timer {
65    // Static configuration
66    enabled_at_reset: bool,
67
68    // Runtime glue
69    interrupt: Option<LineInterrupt>,
70
71    // Volatile state
72    #[inspect(flatten)]
73    state: TimerState,
74}
75
76#[derive(Copy, Clone, Debug, Inspect)]
77struct TimerState {
78    ce: u16,         // "counting element", i.e. the counter
79    cr: u16,         // count register, the new value
80    ol: Option<u16>, // the output latch
81    sl: Option<u8>,  // the status latch
82    state: CountState,
83    control: ControlWord,
84    out: bool,       // timer output
85    gate: bool,      // timer input
86    null: bool,      // cr has been set but not copied to ce yet
87    read_high: bool, // read the high counter byte next
88    cr_low: Option<u8>,
89}
90
91#[derive(Copy, Clone, Debug, Inspect, PartialEq, Eq)]
92enum CountState {
93    Inactive,
94    WaitingForGate,
95    Reloading,
96    Active,
97    Counting,
98}
99
100#[derive(Debug, Copy, Clone, Inspect, PartialEq, Eq)]
101enum Mode {
102    TerminalCount = 0,
103    HardwareOneShot = 1,
104    RateGenerator = 2,
105    SquareWave = 3,
106    SoftwareStrobe = 4,
107    HardwareStrobe = 5,
108}
109
110impl From<u8> for Mode {
111    fn from(v: u8) -> Self {
112        match v {
113            0 => Mode::TerminalCount,
114            1 => Mode::HardwareOneShot,
115            2 | 6 => Mode::RateGenerator,
116            3 | 7 => Mode::SquareWave,
117            4 => Mode::SoftwareStrobe,
118            5 => Mode::HardwareStrobe,
119            _ => unreachable!(),
120        }
121    }
122}
123
124impl Mode {
125    /// Returns true for modes where counting stops when gate is low.
126    fn gate_stops_count(&self) -> bool {
127        match self {
128            Mode::TerminalCount | Mode::RateGenerator | Mode::SquareWave | Mode::SoftwareStrobe => {
129                true
130            }
131            Mode::HardwareOneShot | Mode::HardwareStrobe => false,
132        }
133    }
134}
135
136open_enum! {
137    #[derive(Inspect)]
138    #[inspect(debug)]
139    enum RwMode: u8 {
140        LOW = 1,
141        HIGH = 2,
142        LOW_HIGH = 3,
143    }
144}
145
146const fn from_bcd(n: u16) -> u16 {
147    (n & 0xf) + ((n & 0xf0) >> 4) * 10 + ((n & 0xf00) >> 8) * 100 + ((n & 0xf000) >> 12) * 1000
148}
149
150const fn to_bcd(n: u16) -> u16 {
151    (n % 10) + (((n / 10) % 10) << 4) + (((n / 100) % 10) << 8) + (((n / 1000) % 10) << 12)
152}
153
154/// Subtracts `n` from `ce` with wrap.
155///
156/// If `bcd`, then `ce` is in BCD format. The return value is always in binary
157/// format.
158fn counter_sub(ce: u16, n: u64, bcd: bool) -> u64 {
159    if bcd {
160        let ce = from_bcd(ce);
161        let n = (n % 10000) as u16;
162        (if ce >= n { ce - n } else { 10000 - (n - ce) }) as u64
163    } else {
164        ce.wrapping_sub(n as u16) as u64
165    }
166}
167
168/// Nanoseconds per PIT tick.
169const NANOS_PER_TICK: u64 = 838;
170
171impl Timer {
172    fn new(enabled_at_reset: bool, interrupt: Option<LineInterrupt>) -> Self {
173        Self {
174            enabled_at_reset,
175            interrupt,
176            state: TimerState::new(enabled_at_reset),
177        }
178    }
179
180    fn reset(&mut self) {
181        self.state = TimerState::new(self.enabled_at_reset);
182        self.sync_interrupt();
183    }
184
185    fn sync_interrupt(&mut self) {
186        if let Some(interrupt) = &self.interrupt {
187            interrupt.set_level(self.state.out);
188        }
189    }
190
191    fn set_out(&mut self, state: bool) {
192        if self.state.out != state {
193            self.state.out = state;
194            self.sync_interrupt();
195        }
196    }
197
198    fn load_ce(&mut self) {
199        self.state.ce = self.state.cr;
200        self.state.null = false;
201    }
202
203    /// Sets CE to the given value, wrapped. Stores CE in BCD
204    /// format if the PIT is in BCD mode.
205    fn set_ce(&mut self, ce: u64) {
206        if self.state.control.bcd() {
207            self.state.ce = to_bcd((ce % 10000) as u16);
208        } else {
209            self.state.ce = ce as u16;
210        }
211    }
212
213    fn evaluate(&mut self, mut ticks: u64) {
214        let mode = self.state.op_mode();
215        let bcd = self.state.control.bcd();
216        while ticks > 0 {
217            match self.state.state {
218                CountState::Inactive | CountState::WaitingForGate => break,
219                CountState::Reloading => {
220                    ticks -= 1;
221                    self.load_ce();
222                    self.state.state = CountState::Active;
223                }
224                CountState::Active => {
225                    if !self.state.gate && mode.gate_stops_count() {
226                        break;
227                    }
228                    // Counts per tick.
229                    let per = match mode {
230                        Mode::TerminalCount
231                        | Mode::HardwareOneShot
232                        | Mode::RateGenerator
233                        | Mode::SoftwareStrobe
234                        | Mode::HardwareStrobe => 1,
235                        Mode::SquareWave => {
236                            // Strip the low bit. This takes an extra tick when
237                            // out is high.
238                            if self.state.ce & 1 != 0 {
239                                self.state.ce &= !1;
240                                if self.state.out {
241                                    ticks -= 1;
242                                    continue;
243                                }
244                            }
245                            2
246                        }
247                    };
248                    if self.state.ce as u64 == per {
249                        // Terminal state.
250                        self.state.ce = 0;
251                        ticks -= 1;
252                        match mode {
253                            Mode::TerminalCount | Mode::HardwareOneShot => {
254                                self.set_out(true);
255                                self.state.state = CountState::Counting;
256                            }
257                            Mode::RateGenerator => {
258                                self.set_out(true);
259                                self.load_ce();
260                            }
261                            Mode::SquareWave => {
262                                self.set_out(!self.state.out);
263                                self.load_ce();
264                            }
265                            Mode::SoftwareStrobe | Mode::HardwareStrobe => {
266                                self.set_out(false);
267                                self.state.state = CountState::Counting;
268                            }
269                        }
270                    } else {
271                        if ticks >= counter_sub(self.state.ce, per, bcd) / per {
272                            // Decrement down to one tick before the terminal state.
273                            ticks -= counter_sub(self.state.ce, per, bcd) / per;
274                            self.state.ce = per as u16;
275                            if mode == Mode::RateGenerator {
276                                self.set_out(false);
277                            }
278                        } else {
279                            self.set_ce(counter_sub(self.state.ce, ticks * per, bcd));
280                            ticks = 0;
281                        }
282                    }
283                }
284                CountState::Counting => {
285                    if !self.state.gate && mode.gate_stops_count() {
286                        break;
287                    }
288                    self.set_ce(counter_sub(self.state.ce, ticks, bcd));
289                    ticks = 0;
290                    self.set_out(true);
291                }
292            }
293        }
294    }
295
296    fn set_control(&mut self, control: ControlWord) {
297        if control.rw() == 0 {
298            self.state.latch_counter();
299            return;
300        }
301
302        self.state.control = control.with_select(0);
303        self.state.ce = 0;
304        self.state.cr = 0;
305        self.state.cr_low = None;
306        self.state.read_high = false;
307        self.state.state = CountState::Inactive;
308        self.state.null = true;
309        self.set_out(match self.state.op_mode() {
310            Mode::TerminalCount => false,
311            Mode::HardwareOneShot => true,
312            Mode::RateGenerator | Mode::SquareWave => true,
313            Mode::SoftwareStrobe => true,
314            Mode::HardwareStrobe => true,
315        });
316    }
317
318    fn write(&mut self, n: u8) {
319        let n = n as u16;
320        match RwMode(self.state.control.rw()) {
321            RwMode::LOW => self.state.cr = n,
322            RwMode::HIGH => self.state.cr = n << 8,
323            RwMode::LOW_HIGH => {
324                if let Some(low) = self.state.cr_low {
325                    self.state.cr = (n << 8) | (low as u16);
326                } else {
327                    self.state.cr_low = Some(n as u8);
328                    // Wait for high to be set before taking any actions.
329                    return;
330                }
331            }
332            _ => unreachable!(),
333        }
334        self.state.null = true;
335        match self.state.op_mode() {
336            Mode::TerminalCount => {
337                self.state.state = CountState::Reloading;
338                self.set_out(false);
339            }
340            Mode::HardwareOneShot => {
341                self.state.state = CountState::WaitingForGate;
342            }
343            Mode::RateGenerator | Mode::SquareWave => {
344                if self.state.state != CountState::Active {
345                    self.state.state = CountState::Reloading;
346                }
347            }
348            Mode::SoftwareStrobe => {
349                self.state.state = CountState::Reloading;
350            }
351            Mode::HardwareStrobe => {
352                self.state.state = CountState::WaitingForGate;
353            }
354        }
355    }
356
357    fn read(&mut self) -> u8 {
358        if let Some(sl) = self.state.sl.take() {
359            return sl;
360        }
361        let value = self.state.ol.unwrap_or(self.state.ce);
362        let value = match RwMode(self.state.control.rw()) {
363            RwMode::LOW => value as u8,
364            RwMode::HIGH => (value >> 8) as u8,
365            RwMode::LOW_HIGH => {
366                self.state.read_high = !self.state.read_high;
367                if self.state.read_high {
368                    value as u8
369                } else {
370                    (value >> 8) as u8
371                }
372            }
373            _ => unreachable!(),
374        };
375        if !self.state.read_high {
376            self.state.ol = None;
377        }
378        value
379    }
380
381    fn set_gate(&mut self, gate: bool) {
382        match self.state.op_mode() {
383            Mode::TerminalCount => {}
384            Mode::HardwareOneShot => {
385                if !self.state.gate && gate && self.state.state == CountState::WaitingForGate {
386                    self.state.state = CountState::Reloading;
387                    self.set_out(false);
388                }
389            }
390            Mode::RateGenerator | Mode::SquareWave => {
391                if gate && !self.state.gate {
392                    if self.state.state == CountState::Active {
393                        self.state.state = CountState::Reloading;
394                    }
395                } else if !gate {
396                    self.set_out(true);
397                }
398            }
399            Mode::SoftwareStrobe => {}
400            Mode::HardwareStrobe => {
401                if !self.state.gate && gate && self.state.state == CountState::WaitingForGate {
402                    self.state.state = CountState::Reloading;
403                }
404            }
405        }
406        self.state.gate = gate;
407    }
408}
409
410impl TimerState {
411    fn new(enabled: bool) -> Self {
412        Self {
413            ce: 0,
414            cr: 0,
415            ol: None,
416            sl: None,
417            control: ControlWord::new().with_rw(1),
418            state: CountState::Inactive,
419            out: false,
420            null: true,
421            gate: enabled,
422            read_high: false,
423            cr_low: None,
424        }
425    }
426
427    fn op_mode(&self) -> Mode {
428        self.control.mode().into()
429    }
430
431    // Returns the number of ticks until the next interrupt will occur.
432    fn next_wakeup(&self) -> Option<u64> {
433        let mode = self.op_mode();
434        let bcd = self.control.bcd();
435        match self.state {
436            CountState::Inactive => None,
437            CountState::WaitingForGate => None,
438            CountState::Reloading | CountState::Active => {
439                if !self.gate && mode.gate_stops_count() {
440                    return None;
441                }
442                // Add an extra count for the reload cycle.
443                let (ce, extra) = if self.state == CountState::Reloading {
444                    (self.cr, 1)
445                } else {
446                    (self.ce, 0)
447                };
448                let v = {
449                    match mode {
450                        Mode::TerminalCount
451                        | Mode::HardwareOneShot
452                        | Mode::SoftwareStrobe
453                        | Mode::HardwareStrobe => {
454                            // Changing output in ce ticks.
455                            counter_sub(ce, 1, bcd) + 1
456                        }
457                        Mode::RateGenerator => {
458                            if ce == 1 {
459                                // Going high in 1 tick.
460                                1
461                            } else {
462                                // Going low in ce - 1 ticks.
463                                counter_sub(ce, 1, bcd)
464                            }
465                        }
466                        Mode::SquareWave => {
467                            // Inverts in ce / 2 ticks, rounding up if out is high.
468                            (counter_sub(ce, 2, bcd) + 2) / 2 + (self.out && ce & 1 != 0) as u64
469                        }
470                    }
471                };
472                Some(v + extra)
473            }
474            CountState::Counting => {
475                if self.out || (!self.gate && mode.gate_stops_count()) {
476                    None
477                } else {
478                    Some(1)
479                }
480            }
481        }
482    }
483
484    fn latch_status(&mut self) {
485        if self.sl.is_none() {
486            self.sl = Some(
487                StatusWord(self.control.0)
488                    .with_null(self.null)
489                    .with_out(self.out)
490                    .into(),
491            );
492        }
493    }
494
495    fn latch_counter(&mut self) {
496        if self.ol.is_none() {
497            self.ol = Some(self.ce);
498        }
499    }
500}
501
502#[derive(InspectMut)]
503pub struct PitDevice {
504    // Runtime glue
505    vmtime: VmTimeAccess,
506
507    // Sub-emulators
508    #[inspect(iter_by_index)]
509    timers: [Timer; { PIT_TIMER_RANGE_END - PIT_TIMER_RANGE_START + 1 } as usize],
510
511    // Runtime book-keeping
512    dram_refresh: bool, // just jitters back and forth
513
514    // Volatile state
515    last: VmTime,
516}
517
518impl PitDevice {
519    pub fn new(interrupt: LineInterrupt, vmtime: VmTimeAccess) -> Self {
520        PitDevice {
521            // Timers 1 and 2 are enabled by default. Timer 1's output is hooked
522            // up to the interrupt line.
523            timers: [
524                Timer::new(true, Some(interrupt)),
525                Timer::new(true, None),
526                Timer::new(false, None),
527            ],
528            last: vmtime.now(),
529            vmtime,
530            dram_refresh: false,
531        }
532    }
533
534    fn evaluate(&mut self, now: VmTime) {
535        // Accumulate an integer number of ticks.
536        //
537        // N.B. if self.last were set to now, then each call to evaluate
538        // would leak a portion of a tick, causing timers to expire late.
539        let delta = now.checked_sub(self.last).unwrap_or(Duration::ZERO);
540        let ticks = delta.as_nanos() as u64 / NANOS_PER_TICK;
541        self.last = self
542            .last
543            .wrapping_add(Duration::from_nanos(ticks * NANOS_PER_TICK));
544        self.timers[0].evaluate(ticks);
545        self.timers[1].evaluate(ticks);
546        self.timers[2].evaluate(ticks);
547    }
548
549    fn arm_wakeup(&mut self) {
550        // Request another tick if needed. This is only needed for timer 0 since
551        // that's the only one wired up to an interrupt.
552        if let Some(next) = self.timers[0].state.next_wakeup() {
553            // Delay waking up if the next wakeup is too soon to avoid spinning.
554            let next = next.max(20);
555            self.vmtime.set_timeout_if_before(
556                self.last
557                    .wrapping_add(Duration::from_nanos(next * NANOS_PER_TICK)),
558            );
559        }
560    }
561}
562
563impl ChangeDeviceState for PitDevice {
564    fn start(&mut self) {}
565
566    async fn stop(&mut self) {}
567
568    async fn reset(&mut self) {
569        for timer in &mut self.timers {
570            timer.reset();
571        }
572        self.last = self.vmtime.now();
573    }
574}
575
576impl ChipsetDevice for PitDevice {
577    fn supports_poll_device(&mut self) -> Option<&mut dyn PollDevice> {
578        Some(self)
579    }
580
581    fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
582        Some(self)
583    }
584}
585
586impl PollDevice for PitDevice {
587    fn poll_device(&mut self, cx: &mut Context<'_>) {
588        if let Poll::Ready(now) = self.vmtime.poll_timeout(cx) {
589            self.evaluate(now);
590            // Re-register the poll before arming the next wakeup rather than
591            // after so that a very short wakeup will still allow this function
592            // to return, hopefully avoiding livelock.
593            assert!(self.vmtime.poll_timeout(cx).is_pending());
594            self.arm_wakeup();
595        }
596    }
597}
598
599impl PortIoIntercept for PitDevice {
600    fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
601        if data.len() != 1 {
602            return IoResult::Err(IoError::InvalidAccessSize);
603        }
604
605        self.evaluate(self.vmtime.now());
606        match io_port {
607            PIT_TIMER_RANGE_START..=PIT_TIMER_RANGE_END => {
608                let offset = io_port - PIT_TIMER_RANGE_START;
609                data[0] = self.timers[offset as usize].read();
610            }
611            PIT_CONTROL_REGISTER => {
612                tracelimit::warn_ratelimited!("reading from write-only command register!");
613                data[0] = !0;
614            }
615            PIT_PORT61_REGISTER => {
616                data[0] = ((self.timers[2].state.out as u8) << 5)
617                    | ((self.dram_refresh as u8) << 4)
618                    | self.timers[2].state.gate as u8;
619                // Cycle the DRAM refresh bit every read. PCAT uses this to
620                // validate that DRAM is working, but it's not practical or
621                // useful to make the timing accurate.
622                self.dram_refresh = !self.dram_refresh;
623            }
624            _ => return IoResult::Err(IoError::InvalidRegister),
625        }
626
627        self.arm_wakeup();
628        IoResult::Ok
629    }
630
631    fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
632        let &[b] = data else {
633            return IoResult::Err(IoError::InvalidAccessSize);
634        };
635
636        self.evaluate(self.vmtime.now());
637
638        match io_port {
639            PIT_TIMER_RANGE_START..=PIT_TIMER_RANGE_END => {
640                let offset = io_port - PIT_TIMER_RANGE_START;
641                self.timers[offset as usize].write(b);
642            }
643            PIT_CONTROL_REGISTER => {
644                let control = ControlWord(b);
645                match control.select() {
646                    i @ 0..=2 => {
647                        tracing::trace!(timer = i, ?control, "control write");
648                        self.timers[i as usize].set_control(control);
649                    }
650                    3 => {
651                        let command = ReadBackCommand(b);
652                        tracing::trace!(?command, "read back");
653                        for (i, select) in
654                            [command.counter0(), command.counter1(), command.counter2()]
655                                .into_iter()
656                                .enumerate()
657                        {
658                            if select {
659                                if command.status() {
660                                    self.timers[i].state.latch_status();
661                                }
662                                if command.count() {
663                                    self.timers[i].state.latch_counter();
664                                }
665                            }
666                        }
667                    }
668                    _ => unreachable!(),
669                }
670            }
671            PIT_PORT61_REGISTER => {
672                self.timers[2].set_gate((b & 1) != 0);
673            }
674            _ => return IoResult::Err(IoError::InvalidRegister),
675        }
676
677        self.arm_wakeup();
678        IoResult::Ok
679    }
680
681    fn get_static_regions(&mut self) -> &[(&str, RangeInclusive<u16>)] {
682        &[
683            ("main", PIT_TIMER_RANGE_START..=PIT_CONTROL_REGISTER),
684            ("port61", PIT_PORT61_REGISTER..=PIT_PORT61_REGISTER),
685        ]
686    }
687}
688
689mod save_restore {
690    use super::*;
691    use vmcore::save_restore::RestoreError;
692    use vmcore::save_restore::SaveError;
693    use vmcore::save_restore::SaveRestore;
694
695    mod state {
696        use mesh::payload::Protobuf;
697        use vmcore::save_restore::SavedStateRoot;
698        use vmcore::vmtime::VmTime;
699
700        #[derive(Protobuf)]
701        #[mesh(package = "chipset.pit")]
702        pub enum SavedCountState {
703            #[mesh(1)]
704            Inactive,
705            #[mesh(2)]
706            WaitingForGate,
707            #[mesh(3)]
708            Reloading,
709            #[mesh(4)]
710            Active,
711            #[mesh(5)]
712            Counting,
713        }
714
715        #[derive(Protobuf)]
716        #[mesh(package = "chipset.pit")]
717        pub struct SavedTimerState {
718            #[mesh(1)]
719            pub ce: u16,
720            #[mesh(2)]
721            pub cr: u16,
722            #[mesh(3)]
723            pub ol: Option<u16>,
724            #[mesh(4)]
725            pub sl: Option<u8>,
726            #[mesh(5)]
727            pub state: SavedCountState,
728            #[mesh(6)]
729            pub control: u8,
730            #[mesh(7)]
731            pub out: bool,
732            #[mesh(8)]
733            pub gate: bool,
734            #[mesh(9)]
735            pub null: bool,
736            #[mesh(10)]
737            pub read_high: bool,
738            #[mesh(11)]
739            pub cr_low: Option<u8>,
740        }
741
742        #[derive(Protobuf, SavedStateRoot)]
743        #[mesh(package = "chipset.pit")]
744        pub struct SavedState {
745            #[mesh(1)]
746            pub timers: [SavedTimerState; 3],
747            #[mesh(2)]
748            pub last: VmTime,
749        }
750    }
751
752    #[derive(Debug, Error)]
753    enum PitDeviceRestoreError {
754        #[error("last tick time is after current time")]
755        InvalidLastTick,
756    }
757
758    impl SaveRestore for PitDevice {
759        type SavedState = state::SavedState;
760
761        fn save(&mut self) -> Result<Self::SavedState, SaveError> {
762            let Self {
763                vmtime: _,
764                timers,
765                dram_refresh: _,
766                last,
767            } = self;
768
769            Ok(state::SavedState {
770                timers: [&timers[0].state, &timers[1].state, &timers[2].state].map(|timer| {
771                    let &TimerState {
772                        ce,
773                        cr,
774                        ol,
775                        sl,
776                        state,
777                        control,
778                        out,
779                        gate,
780                        null,
781                        read_high,
782                        cr_low,
783                    } = timer;
784
785                    state::SavedTimerState {
786                        ce,
787                        cr,
788                        ol,
789                        sl,
790                        state: match state {
791                            CountState::Inactive => state::SavedCountState::Inactive,
792                            CountState::WaitingForGate => state::SavedCountState::WaitingForGate,
793                            CountState::Reloading => state::SavedCountState::Reloading,
794                            CountState::Active => state::SavedCountState::Active,
795                            CountState::Counting => state::SavedCountState::Counting,
796                        },
797                        control: control.into(),
798                        out,
799                        gate,
800                        null,
801                        read_high,
802                        cr_low,
803                    }
804                }),
805
806                last: *last,
807            })
808        }
809
810        fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
811            let state::SavedState { timers, last } = state;
812
813            for (timer, state) in self.timers.iter_mut().zip(timers) {
814                let state::SavedTimerState {
815                    ce,
816                    cr,
817                    ol,
818                    sl,
819                    state,
820                    control,
821                    out,
822                    gate,
823                    null,
824                    read_high,
825                    cr_low,
826                } = state;
827
828                timer.state = TimerState {
829                    ce,
830                    cr,
831                    ol,
832                    sl,
833                    state: match state {
834                        state::SavedCountState::Inactive => CountState::Inactive,
835                        state::SavedCountState::WaitingForGate => CountState::WaitingForGate,
836                        state::SavedCountState::Reloading => CountState::Reloading,
837                        state::SavedCountState::Active => CountState::Active,
838                        state::SavedCountState::Counting => CountState::Counting,
839                    },
840                    out,
841                    control: ControlWord::from(control), // no unused bits
842                    gate,
843                    null,
844                    read_high,
845                    cr_low,
846                };
847
848                timer.sync_interrupt();
849            }
850
851            self.last = last;
852            if last.is_after(self.vmtime.now()) {
853                return Err(RestoreError::InvalidSavedState(
854                    PitDeviceRestoreError::InvalidLastTick.into(),
855                ));
856            }
857
858            Ok(())
859        }
860    }
861}
862
863#[cfg(test)]
864mod tests {
865    use super::ControlWord;
866    use super::Mode;
867    use super::RwMode;
868    use super::Timer;
869    use super::to_bcd;
870    use crate::pit::from_bcd;
871
872    #[test]
873    fn test_bcd_comp() {
874        for i in 0..=9999 {
875            assert_eq!(from_bcd(to_bcd(i)), i, "{i} {}", to_bcd(i));
876        }
877    }
878
879    fn set_timer(timer: &mut Timer, mode: Mode, mut cr: u16, bcd: bool) {
880        timer.set_control(
881            ControlWord::new()
882                .with_mode(mode as u8)
883                .with_rw(RwMode::LOW_HIGH.0)
884                .with_bcd(bcd),
885        );
886        if bcd {
887            cr = to_bcd(cr);
888        }
889        timer.write(cr as u8);
890        timer.write((cr >> 8) as u8);
891    }
892
893    fn check_invert(timer: &mut Timer, initial_out: bool, expected_next: u64) {
894        let mode = Mode::from(timer.state.control.mode());
895        assert_eq!(timer.state.out, initial_out, "{mode:?}");
896        let n = timer.state.next_wakeup().unwrap();
897        assert_eq!(n, expected_next, "{mode:?}");
898        for i in 0..n - 1 {
899            timer.evaluate(1);
900            assert_eq!(
901                i + timer.state.next_wakeup().unwrap() + 1,
902                n,
903                "{mode:?}, {i}"
904            );
905            assert_eq!(timer.state.out, initial_out, "{mode:?}, {i}");
906        }
907        timer.evaluate(1);
908        assert_eq!(timer.state.out, !initial_out, "{mode:?}, {n}");
909    }
910
911    fn check_done(timer: &mut Timer) {
912        assert!(timer.state.next_wakeup().is_none());
913        let out = timer.state.out;
914        for _ in 0..65536 {
915            timer.evaluate(1);
916            assert_eq!(timer.state.out, out);
917        }
918    }
919
920    fn test_output(bcd: bool) {
921        let mut timer = Timer::new(true, None);
922        let max = if bcd { 10000 } else { 0x10000 };
923
924        set_timer(&mut timer, Mode::TerminalCount, 0, bcd);
925        check_invert(&mut timer, false, max + 1);
926        check_done(&mut timer);
927
928        set_timer(&mut timer, Mode::HardwareOneShot, 0, bcd);
929        check_done(&mut timer);
930        timer.set_gate(false);
931        timer.set_gate(true);
932        check_invert(&mut timer, false, max + 1);
933        check_done(&mut timer);
934
935        set_timer(&mut timer, Mode::RateGenerator, 0, bcd);
936        check_invert(&mut timer, true, max);
937        check_invert(&mut timer, false, 1);
938        check_invert(&mut timer, true, max - 1);
939
940        set_timer(&mut timer, Mode::SquareWave, 0, bcd);
941        check_invert(&mut timer, true, max / 2 + 1);
942        check_invert(&mut timer, false, max / 2);
943        check_invert(&mut timer, true, max / 2);
944
945        set_timer(&mut timer, Mode::SquareWave, 1001, bcd);
946        check_invert(&mut timer, true, 502);
947        check_invert(&mut timer, false, 500);
948        check_invert(&mut timer, true, 501);
949
950        set_timer(&mut timer, Mode::SoftwareStrobe, 0, bcd);
951        check_invert(&mut timer, true, max + 1);
952        check_invert(&mut timer, false, 1);
953        check_done(&mut timer);
954
955        set_timer(&mut timer, Mode::HardwareStrobe, 0, bcd);
956        check_done(&mut timer);
957        timer.set_gate(false);
958        timer.set_gate(true);
959        check_invert(&mut timer, true, max + 1);
960        check_invert(&mut timer, false, 1);
961        check_done(&mut timer);
962    }
963
964    #[test]
965    fn test_binary() {
966        test_output(false);
967    }
968
969    #[test]
970    fn test_bcd() {
971        test_output(true);
972    }
973}