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}