Skip to main content

gb/
ir.rs

1//! The Game Boy Color's infrared port.
2//!
3//! What the hardware offers is a lamp and a light sensor. There is no clock, no
4//! framing and nothing to say a message has arrived. Talking to anything means
5//! building all of that out of [`Port::led`] and [`Port::signal`].
6//!
7//! ```ignore
8//! let Some(port) = ir::open() else { return };
9//!
10//! port.led(true);
11//! ```
12//!
13//! # Sending
14//!
15//! A receiver settles to whatever infrared is already in the room, so a lamp
16//! held on reads as nothing after a moment and a message has to be pulses. A
17//! Philips remote control, for one, sends a run of 32 flashes of 10 and 17.5
18//! microseconds where a single one of 880 would say the same thing.
19//!
20//! Nothing here counts those out. The lengths belong to whatever is on the
21//! other end, and [double speed](crate::rt::DOUBLE_SPEED) halves what a loop of
22//! a given length takes.
23//!
24//! See <https://gbdev.io/pandocs/CGB_Registers.html>.
25
26use crate::mmio::cgb::{Infrared, RP};
27
28/// The port, open and drawing current.
29///
30/// Dropping this leaves it that way. [`close`](Self::close) is what puts it
31/// back down.
32pub struct Port(());
33
34/// Open the port, if this machine has one.
35///
36/// A Game Boy Advance runs Color cartridges and has no infrared port, so
37/// [`is_cgb`](crate::is_cgb) on its own is not enough to go on.
38#[inline]
39pub fn open() -> Option<Port> {
40    (crate::is_cgb() && !crate::is_gba()).then(|| {
41        RP.write(Infrared::new().with_read_enable(0b11));
42        Port(())
43    })
44}
45
46impl Port {
47    /// Turn the lamp on or off.
48    #[inline]
49    pub fn led(&self, on: bool) {
50        RP.write(Infrared::new().with_read_enable(0b11).with_led_on(on));
51    }
52
53    /// Whether infrared is reaching the sensor.
54    ///
55    /// This Game Boy's own lamp reaches its own sensor, so [`led`](Self::led)
56    /// registers here as readily as anything across the room.
57    #[inline]
58    pub fn signal(&self) -> bool {
59        // The register reads 0 where light is seen, which is the one place
60        // this module turns something over.
61        !RP.read().receiving()
62    }
63
64    /// Close the port.
65    ///
66    /// The reading half draws current for as long as it is open, which is what
67    /// this is for.
68    #[inline]
69    pub fn close(self) {
70        RP.write(Infrared::new());
71    }
72}