1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
use crate::memory_mapped::MemoryMapped;

use bilge::prelude::*;
use bitflags::bitflags;

use video::Video;

use self::{
    blend::Blend,
    object::{initilise_oam, OamManaged, OamUnmanaged, SpriteLoader},
    window::Windows,
};

/// Graphics mode 3. Bitmap mode that provides a 16-bit colour framebuffer.
pub mod bitmap3;
/// Graphics mode 4. Bitmap 4 provides two 8-bit paletted framebuffers with page switching.
pub mod bitmap4;
/// Test logo of agb.
pub mod example_logo;
pub mod object;
/// Palette type.
pub mod palette16;
/// Data produced by agb-image-converter
pub mod tile_data;
/// Graphics mode 0. Four regular backgrounds.
pub mod tiled;
/// Giving out graphics mode.
pub mod video;

pub mod affine;
pub mod blend;
pub mod window;

pub mod font;
pub use font::{Font, FontLetter};

const DISPLAY_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0000) };
pub(crate) const DISPLAY_STATUS: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0004) };
const VCOUNT: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0006) };

bitflags! {
    #[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]
    struct GraphicsSettings: u16 {
        const PAGE_SELECT = 1 << 0x4;
        const OAM_HBLANK = 1 << 0x5;
        const SPRITE1_D = 1 << 0x6;
        const SCREEN_BLANK = 1 << 0x7;
        const LAYER_BG0 = 1 << 0x8;
        const LAYER_BG1 = 1 << 0x9;
        const LAYER_BG2 = 1 << 0xA;
        const LAYER_BG3 = 1  << 0xB;
        const LAYER_OBJ = 1 << 0xC;
        const WINDOW0 = 1 << 0xD;
        const WINDOW1 = 1 << 0xE;
        const WINDOW_OBJECT = 1 << 0xF;
    }
}

/// Width of the Gameboy advance screen in pixels
pub const WIDTH: i32 = 240;
/// Height of the Gameboy advance screen in pixels
pub const HEIGHT: i32 = 160;

#[allow(dead_code)]
enum DisplayMode {
    Tiled0 = 0,
    Tiled1 = 1,
    Tiled2 = 2,
    Bitmap3 = 3,
    Bitmap4 = 4,
    Bitmap5 = 5,
}

#[non_exhaustive]
/// Manages distribution of display modes, obtained from the gba struct
pub struct Display {
    pub video: Video,
    pub object: ObjectDistribution,
    pub window: WindowDist,
    pub blend: BlendDist,
}

#[non_exhaustive]
pub struct ObjectDistribution;

impl ObjectDistribution {
    pub fn get_unmanaged(&mut self) -> (OamUnmanaged<'_>, SpriteLoader) {
        unsafe { initilise_oam() };
        (OamUnmanaged::new(), SpriteLoader::new())
    }

    pub fn get_managed(&mut self) -> OamManaged<'_> {
        unsafe { initilise_oam() };
        OamManaged::new()
    }

    /// The old name for [`get_managed`][ObjectDistribution::get_managed] kept around for easier migration.
    /// This will be removed in a future release.
    #[deprecated = "use get_managed to get the managed oam instead"]
    pub fn get(&mut self) -> OamManaged<'_> {
        self.get_managed()
    }
}

#[non_exhaustive]
pub struct WindowDist;

impl WindowDist {
    pub fn get(&mut self) -> Windows<'_> {
        Windows::new()
    }
}

#[non_exhaustive]
pub struct BlendDist;

impl BlendDist {
    pub fn get(&mut self) -> Blend<'_> {
        Blend::new()
    }
}

impl Display {
    pub(crate) const unsafe fn new() -> Self {
        Display {
            video: Video,
            object: ObjectDistribution,
            window: WindowDist,
            blend: BlendDist,
        }
    }
}

unsafe fn set_graphics_mode(mode: DisplayMode) {
    let current = DISPLAY_CONTROL.get();
    let current = current & (!0b111);
    let s = current | (mode as u16 & 0b111);

    // disable blank screen
    let s = s & !(1 << 7);

    DISPLAY_CONTROL.set(s);
}

unsafe fn set_graphics_settings(settings: GraphicsSettings) {
    let current = DISPLAY_CONTROL.get();
    // preserve display mode
    let current = current & 0b111;
    let s = settings.bits() | current;

    DISPLAY_CONTROL.set(s);
}

#[allow(non_snake_case)]
/// Waits until vblank using a busy wait loop, this should almost never be used.
/// I only say almost because whilst I don't believe there to be a reason to use
/// this I can't rule it out.
pub fn busy_wait_for_vblank() {
    while VCOUNT.get() >= 160 {}
    while VCOUNT.get() < 160 {}
}

#[bitsize(2)]
#[derive(FromBits, PartialEq, Eq, Clone, Copy, Debug)]
pub enum Priority {
    P0 = 0,
    P1 = 1,
    P2 = 2,
    P3 = 3,
}