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};