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}