gb/ppu/bg.rs
1//! The background: a tilemap seen through a window the size of the screen.
2//!
3//! The grid is 256 by 256 pixels and the screen shows 160 by 144 of it, placed by
4//! [`set_scroll`] and wrapping at either edge. What fills the grid is
5//! [`map`](super::map)'s business; this is where it sits.
6//!
7//! The scroll registers are re-read as the PPU fetches each tile, so writing them
8//! part way down a frame moves the rest of it, which gives a background its
9//! wobble or its parallax. See <https://gbdev.io/pandocs/Scrolling.html>.
10
11use crate::mmio::{LCDC, SCX, SCY};
12
13use super::map::Map;
14
15/// Where the screen sits on the grid, as `(x, y)`.
16#[inline]
17pub fn scroll() -> (u8, u8) {
18 (SCX.read(), SCY.read())
19}
20
21/// Move the screen to `(x, y)` on the grid.
22#[inline]
23pub fn set_scroll(x: u8, y: u8) {
24 SCX.write(x);
25 SCY.write(y);
26}
27
28/// Move the screen horizontally.
29///
30/// Separate from [`set_scroll`] because a per-scanline effect has room for one
31/// register write and not two.
32#[inline]
33pub fn set_scroll_x(x: u8) {
34 SCX.write(x);
35}
36
37/// Move the screen vertically.
38#[inline]
39pub fn set_scroll_y(y: u8) {
40 SCY.write(y);
41}
42
43/// Which grid the background reads, from `LCDC` bit 3.
44#[inline]
45pub fn map() -> Map {
46 if LCDC.read().bg_tilemap_high() { Map::One } else { Map::Zero }
47}
48
49/// Point the background at a grid.
50#[inline]
51pub fn set_map(map: Map) {
52 // Read-modify-write: the enable bit is the only reason this is unsafe.
53 unsafe { LCDC.write(LCDC.read().with_bg_tilemap_high(map == Map::One)) };
54}
55
56/// `LCDC` bit 0, which means different things on the two machines.
57///
58/// On the original Game Boy, clearing it blanks the background and the window, leaving
59/// only objects. On the Game Boy Color they keep drawing, and clearing it instead
60/// gives objects priority over them.
61#[inline]
62pub fn enabled() -> bool {
63 LCDC.read().bg_window_enable()
64}
65
66/// Set `LCDC` bit 0. See [`enabled`] for what it does on each machine.
67#[inline]
68pub fn set_enabled(on: bool) {
69 unsafe { LCDC.write(LCDC.read().with_bg_window_enable(on)) };
70}