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) puts it back down.
31pub struct Port(());
32
33/// Open the port, if this machine has one.
34///
35/// A Game Boy Advance runs Color cartridges and has no infrared port, so
36/// [`is_cgb`](crate::is_cgb) on its own is not enough to go on.
37#[inline]
38pub fn open() -> Option<Port> {
39    (crate::is_cgb() && !crate::is_gba()).then(|| {
40        RP.write(Infrared::new().with_read_enable(0b11));
41        Port(())
42    })
43}
44
45impl Port {
46    /// Turn the lamp on or off.
47    #[inline]
48    pub fn led(&self, on: bool) {
49        RP.write(Infrared::new().with_read_enable(0b11).with_led_on(on));
50    }
51
52    /// Whether infrared is reaching the sensor.
53    ///
54    /// This Game Boy's own lamp reaches its own sensor, so [`led`](Self::led)
55    /// registers here as readily as anything across the room.
56    #[inline]
57    pub fn signal(&self) -> bool {
58        // The register reads 0 where light is seen, which is the one place
59        // this module turns something over.
60        !RP.read().receiving()
61    }
62
63    /// Close the port.
64    ///
65    /// The reading half draws current for as long as it is open, which this is
66    /// for.
67    #[inline]
68    pub fn close(self) {
69        RP.write(Infrared::new());
70    }
71}