Skip to main content

gb/mmio/
cgb.rs

1//! Game Boy Color hardware registers.
2//!
3//! The accessors are safe but take effect only on CGB hardware. The undocumented
4//! registers `0xFF72..=0xFF75` are omitted; they have no defined function.
5
6use bitfield_struct::bitfield;
7use voladdress::{Safe, VolAddress};
8
9/// CPU speed switch (`KEY1`).
10///
11/// | Bit | Field | Access | Meaning |
12/// |-----|-------|--------|---------|
13/// | 7   | `double_speed` | RO  | Currently in CGB double speed mode. |
14/// | 6-1 | —              |     | Unused. |
15/// | 0   | `armed`        | R/W | Arm a speed switch; it takes effect on the next `STOP`. |
16#[bitfield(u8)]
17#[derive(PartialEq, Eq)]
18pub struct SpeedSwitch {
19    /// Arm a speed switch; it takes effect on the next `STOP`.
20    pub armed: bool,
21    #[bits(6)]
22    __: u8,
23    /// Currently in CGB double speed mode (read-only).
24    #[bits(1, access = RO)]
25    pub double_speed: bool,
26}
27
28/// VRAM DMA length, mode, and start (`HDMA5`).
29///
30/// | Bit | Field | Access | Meaning |
31/// |-----|-------|--------|---------|
32/// | 7   | `hblank` | R/W | Write: set = HBlank DMA, clear = general-purpose. Read: set = no transfer active. |
33/// | 6-0 | `blocks` | R/W | Transfer length in 16-byte blocks, minus one (0 = one block). |
34#[bitfield(u8)]
35#[derive(PartialEq, Eq)]
36pub struct HdmaCtrl {
37    /// Transfer length in 16-byte blocks, minus one (`0` means one block).
38    #[bits(7)]
39    pub blocks: u8,
40    /// On write: `true` selects HBlank DMA, `false` general-purpose DMA. On
41    /// read: `true` means no transfer is currently active.
42    pub hblank: bool,
43}
44
45/// Infrared communications port (`RP`).
46///
47/// | Bit | Field | Access | Meaning |
48/// |-----|-------|--------|---------|
49/// | 7-6 | `read_enable` | R/W | Set both bits to read the receiver. |
50/// | 5-2 | —             |     | Unused. |
51/// | 1   | `receiving`   | RO  | Receiving an IR signal (reads 0 while a signal is seen). |
52/// | 0   | `led_on`      | R/W | Turn the IR LED on. |
53#[bitfield(u8)]
54#[derive(PartialEq, Eq)]
55pub struct Infrared {
56    /// Turn the IR LED on.
57    pub led_on: bool,
58    /// Receiving an IR signal (read-only; reads `0` while a signal is seen).
59    #[bits(1, access = RO)]
60    pub receiving: bool,
61    #[bits(4)]
62    __: u8,
63    /// Read enable: set both bits to read the receiver.
64    #[bits(2)]
65    pub read_enable: u8,
66}
67
68/// Per-cell tilemap attributes, in VRAM bank 1 at the tilemap addresses.
69///
70/// | Bit | Field | Access | Meaning |
71/// |-----|-------|--------|---------|
72/// | 7   | `priority` | R/W | Set draws this cell's color indices 1-3 over objects. |
73/// | 6   | `y_flip`   | R/W | Mirror vertically. |
74/// | 5   | `x_flip`   | R/W | Mirror horizontally. |
75/// | 4   | —          | R/W | Ignored by the hardware. |
76/// | 3   | `bank`     | R/W | Fetch this cell's tile from VRAM bank 1. |
77/// | 2-0 | `palette`  | R/W | Which of the eight background palettes. |
78#[bitfield(u8)]
79#[derive(PartialEq, Eq)]
80pub struct BgAttr {
81    /// Which of the eight background palettes.
82    #[bits(3)]
83    pub palette: u8,
84    /// Fetch this cell's tile from VRAM bank 1 rather than bank 0.
85    pub bank: bool,
86    #[bits(1)]
87    __: u8,
88    /// Mirror horizontally.
89    pub x_flip: bool,
90    /// Mirror vertically.
91    pub y_flip: bool,
92    /// Draw this cell's color indices 1-3 over objects.
93    pub priority: bool,
94}
95
96/// Color palette index (`BCPS` / `OCPS`).
97///
98/// | Bit | Field | Access | Meaning |
99/// |-----|-------|--------|---------|
100/// | 7   | `auto_increment` | R/W | Auto-increment the address after each data-port write. |
101/// | 6   | —                |     | Unused. |
102/// | 5-0 | `address`        | R/W | Byte offset into palette memory reached through the data port. |
103#[bitfield(u8)]
104#[derive(PartialEq, Eq)]
105pub struct PaletteIndex {
106    /// Byte offset into palette memory reached through the data port.
107    #[bits(6)]
108    pub address: u8,
109    #[bits(1)]
110    __: u8,
111    /// Auto-increment the address after each write to the data port.
112    pub auto_increment: bool,
113}
114
115/// Packed PCM amplitudes (`PCM12` / `PCM34`). Read-only.
116///
117/// | Bit | Field | Access | Meaning |
118/// |-----|-------|--------|---------|
119/// | 7-4 | `high` | RO | Upper channel's amplitude (CH2 for `PCM12`, CH4 for `PCM34`). |
120/// | 3-0 | `low`  | RO | Lower channel's amplitude (CH1 for `PCM12`, CH3 for `PCM34`). |
121#[bitfield(u8)]
122#[derive(PartialEq, Eq)]
123pub struct PcmAmplitudes {
124    /// Lower channel's amplitude (CH1 for `PCM12`, CH3 for `PCM34`).
125    #[bits(4)]
126    pub low: u8,
127    /// Upper channel's amplitude (CH2 for `PCM12`, CH4 for `PCM34`).
128    #[bits(4)]
129    pub high: u8,
130}
131
132/// CPU mode select. Mostly a boot-ROM / DMG-compatibility register, locked
133/// once the boot ROM hands off.
134pub const KEY0: VolAddress<u8, Safe, Safe> = unsafe { VolAddress::new(0xFF4C) };
135/// Prepare speed switch (CGB double speed mode).
136pub const KEY1: VolAddress<SpeedSwitch, Safe, Safe> = unsafe { VolAddress::new(0xFF4D) };
137/// VRAM bank select (bit 0).
138pub const VBK: VolAddress<u8, Safe, Safe> = unsafe { VolAddress::new(0xFF4F) };
139
140/// VRAM DMA source, high byte. Write-only.
141pub const HDMA1: VolAddress<u8, (), Safe> = unsafe { VolAddress::new(0xFF51) };
142/// VRAM DMA source, low byte. Write-only.
143pub const HDMA2: VolAddress<u8, (), Safe> = unsafe { VolAddress::new(0xFF52) };
144/// VRAM DMA destination, high byte. Write-only.
145pub const HDMA3: VolAddress<u8, (), Safe> = unsafe { VolAddress::new(0xFF53) };
146/// VRAM DMA destination, low byte. Write-only.
147pub const HDMA4: VolAddress<u8, (), Safe> = unsafe { VolAddress::new(0xFF54) };
148/// VRAM DMA length/mode/start.
149pub const HDMA5: VolAddress<HdmaCtrl, Safe, Safe> = unsafe { VolAddress::new(0xFF55) };
150
151/// Infrared communications port.
152pub const RP: VolAddress<Infrared, Safe, Safe> = unsafe { VolAddress::new(0xFF56) };
153
154/// Background color palette index.
155pub const BCPS: VolAddress<PaletteIndex, Safe, Safe> = unsafe { VolAddress::new(0xFF68) };
156/// Background color palette data at the current `BCPS` index.
157pub const BCPD: VolAddress<u8, Safe, Safe> = unsafe { VolAddress::new(0xFF69) };
158/// Object color palette index.
159pub const OCPS: VolAddress<PaletteIndex, Safe, Safe> = unsafe { VolAddress::new(0xFF6A) };
160/// Object color palette data at the current `OCPS` index.
161pub const OCPD: VolAddress<u8, Safe, Safe> = unsafe { VolAddress::new(0xFF6B) };
162
163/// Object priority mode: 0 = CGB-style (by OAM index), 1 = DMG-style (by X).
164pub const OPRI: VolAddress<u8, Safe, Safe> = unsafe { VolAddress::new(0xFF6C) };
165
166/// WRAM bank select (bits 0-2) for the `0xD000..=0xDFFF` window.
167pub const SVBK: VolAddress<u8, Safe, Safe> = unsafe { VolAddress::new(0xFF70) };
168
169/// PCM amplitudes for sound channels 1 and 2. Read-only.
170pub const PCM12: VolAddress<PcmAmplitudes, Safe, ()> = unsafe { VolAddress::new(0xFF76) };
171/// PCM amplitudes for sound channels 3 and 4. Read-only.
172pub const PCM34: VolAddress<PcmAmplitudes, Safe, ()> = unsafe { VolAddress::new(0xFF77) };
173
174const _: () = {
175    assert!(SpeedSwitch::new().with_armed(true).into_bits() == 0b0000_0001);
176    assert!(SpeedSwitch::from_bits(0b1000_0000).double_speed());
177    assert!(HdmaCtrl::new().with_hblank(true).into_bits() == 0b1000_0000);
178    assert!(PaletteIndex::new().with_auto_increment(true).into_bits() == 0b1000_0000);
179    assert!(Infrared::new().with_read_enable(0b11).into_bits() == 0b1100_0000);
180    assert!(PcmAmplitudes::new().with_high(0xF).into_bits() == 0b1111_0000);
181};