agb/display/
mod.rs

1use crate::memory_mapped::MemoryMapped;
2
3use bilge::prelude::*;
4use bitflags::bitflags;
5
6use video::Video;
7
8use self::{
9    blend::Blend,
10    object::{initilise_oam, OamManaged, OamUnmanaged, SpriteLoader},
11    window::Windows,
12};
13
14/// Graphics mode 3. Bitmap mode that provides a 16-bit colour framebuffer.
15pub mod bitmap3;
16/// Graphics mode 4. Bitmap 4 provides two 8-bit paletted framebuffers with page switching.
17pub mod bitmap4;
18/// Test logo of agb.
19pub mod example_logo;
20pub mod object;
21/// Palette type.
22pub mod palette16;
23/// Data produced by agb-image-converter
24pub mod tile_data;
25/// Graphics mode 0. Four regular backgrounds.
26pub mod tiled;
27/// Giving out graphics mode.
28pub mod video;
29
30pub mod affine;
31pub mod blend;
32pub mod window;
33
34pub mod font;
35pub use font::{Font, FontLetter};
36
37const DISPLAY_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0000) };
38pub(crate) const DISPLAY_STATUS: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0004) };
39const VCOUNT: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0006) };
40
41bitflags! {
42    #[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]
43    struct GraphicsSettings: u16 {
44        const PAGE_SELECT = 1 << 0x4;
45        const OAM_HBLANK = 1 << 0x5;
46        const SPRITE1_D = 1 << 0x6;
47        const SCREEN_BLANK = 1 << 0x7;
48        const LAYER_BG0 = 1 << 0x8;
49        const LAYER_BG1 = 1 << 0x9;
50        const LAYER_BG2 = 1 << 0xA;
51        const LAYER_BG3 = 1  << 0xB;
52        const LAYER_OBJ = 1 << 0xC;
53        const WINDOW0 = 1 << 0xD;
54        const WINDOW1 = 1 << 0xE;
55        const WINDOW_OBJECT = 1 << 0xF;
56    }
57}
58
59/// Width of the Gameboy advance screen in pixels
60pub const WIDTH: i32 = 240;
61/// Height of the Gameboy advance screen in pixels
62pub const HEIGHT: i32 = 160;
63
64#[allow(dead_code)]
65enum DisplayMode {
66    Tiled0 = 0,
67    Tiled1 = 1,
68    Tiled2 = 2,
69    Bitmap3 = 3,
70    Bitmap4 = 4,
71    Bitmap5 = 5,
72}
73
74#[non_exhaustive]
75/// Manages distribution of display modes, obtained from the gba struct
76pub struct Display {
77    pub video: Video,
78    pub object: ObjectDistribution,
79    pub window: WindowDist,
80    pub blend: BlendDist,
81}
82
83#[non_exhaustive]
84pub struct ObjectDistribution;
85
86impl ObjectDistribution {
87    pub fn get_unmanaged(&mut self) -> (OamUnmanaged<'_>, SpriteLoader) {
88        unsafe { initilise_oam() };
89        (OamUnmanaged::new(), SpriteLoader::new())
90    }
91
92    pub fn get_managed(&mut self) -> OamManaged<'_> {
93        unsafe { initilise_oam() };
94        OamManaged::new()
95    }
96
97    /// The old name for [`get_managed`][ObjectDistribution::get_managed] kept around for easier migration.
98    /// This will be removed in a future release.
99    #[deprecated = "use get_managed to get the managed oam instead"]
100    pub fn get(&mut self) -> OamManaged<'_> {
101        self.get_managed()
102    }
103}
104
105#[non_exhaustive]
106pub struct WindowDist;
107
108impl WindowDist {
109    pub fn get(&mut self) -> Windows<'_> {
110        Windows::new()
111    }
112}
113
114#[non_exhaustive]
115pub struct BlendDist;
116
117impl BlendDist {
118    pub fn get(&mut self) -> Blend<'_> {
119        Blend::new()
120    }
121}
122
123impl Display {
124    pub(crate) const unsafe fn new() -> Self {
125        Display {
126            video: Video,
127            object: ObjectDistribution,
128            window: WindowDist,
129            blend: BlendDist,
130        }
131    }
132}
133
134unsafe fn set_graphics_mode(mode: DisplayMode) {
135    let current = DISPLAY_CONTROL.get();
136    let current = current & (!0b111);
137    let s = current | (mode as u16 & 0b111);
138
139    // disable blank screen
140    let s = s & !(1 << 7);
141
142    DISPLAY_CONTROL.set(s);
143}
144
145unsafe fn set_graphics_settings(settings: GraphicsSettings) {
146    let current = DISPLAY_CONTROL.get();
147    // preserve display mode
148    let current = current & 0b111;
149    let s = settings.bits() | current;
150
151    DISPLAY_CONTROL.set(s);
152}
153
154/// Waits until vblank using a busy wait loop, this should almost never be used.
155/// I only say almost because whilst I don't believe there to be a reason to use
156/// this I can't rule it out.
157pub fn busy_wait_for_vblank() {
158    while VCOUNT.get() >= 160 {}
159    while VCOUNT.get() < 160 {}
160}
161
162/// The priority of a background layer or object. A higher priority should be
163/// thought of as rendering first, and so is behind that of a lower priority.
164/// For an equal priority background layer and object, the background has a
165/// higher priority and therefore is behind the object.
166#[bitsize(2)]
167#[derive(FromBits, PartialEq, Eq, Clone, Copy, Debug, Default)]
168pub enum Priority {
169    #[default]
170    P0 = 0,
171    P1 = 1,
172    P2 = 2,
173    P3 = 3,
174}