mirror of
https://github.com/italicsjenga/agb.git
synced 2025-02-02 12:36:35 +11:00
192 lines
5 KiB
Rust
192 lines
5 KiB
Rust
use crate::{
|
|
memory_mapped::{MemoryMapped, MemoryMapped1DArray, MemoryMapped2DArray},
|
|
single::{Single, SingleToken},
|
|
};
|
|
use bitflags::bitflags;
|
|
use core::convert::TryInto;
|
|
|
|
const DISPLAY_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0000) };
|
|
const DISPLAY_STATUS: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0004) };
|
|
const VCOUNT: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0006) };
|
|
|
|
const PALETTE_BACKGROUND: MemoryMapped1DArray<u16, 256> =
|
|
unsafe { MemoryMapped1DArray::new(0x0500_0000) };
|
|
const PALETTE_SPRITE: MemoryMapped1DArray<u16, 256> =
|
|
unsafe { MemoryMapped1DArray::new(0x0500_0200) };
|
|
|
|
const BITMAP_MODE_3: MemoryMapped2DArray<u16, { WIDTH as usize }, { HEIGHT as usize }> =
|
|
unsafe { MemoryMapped2DArray::new(0x600_0000) };
|
|
|
|
const BITMAP_PAGE_FRONT_MODE_4: MemoryMapped2DArray<
|
|
u16,
|
|
{ (WIDTH / 2) as usize },
|
|
{ HEIGHT as usize },
|
|
> = unsafe { MemoryMapped2DArray::new(0x600_0000) };
|
|
const BITMAP_PAGE_BACK_MODE_4: MemoryMapped2DArray<
|
|
u16,
|
|
{ (WIDTH / 2) as usize },
|
|
{ HEIGHT as usize },
|
|
> = unsafe { MemoryMapped2DArray::new(0x600_A000) };
|
|
|
|
pub const WIDTH: i32 = 240;
|
|
pub const HEIGHT: i32 = 160;
|
|
|
|
pub enum DisplayMode {
|
|
Tiled0 = 0,
|
|
Tiled1 = 1,
|
|
Tiled2 = 2,
|
|
Bitmap3 = 3,
|
|
Bitmap4 = 4,
|
|
Bitmap5 = 5,
|
|
}
|
|
|
|
pub enum Page {
|
|
Front = 0,
|
|
Back = 1,
|
|
}
|
|
|
|
bitflags! {
|
|
pub 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;
|
|
}
|
|
}
|
|
|
|
pub struct Display {
|
|
in_mode: Single,
|
|
}
|
|
|
|
impl Display {
|
|
pub(crate) const unsafe fn new() -> Self {
|
|
Display {
|
|
in_mode: Single::new(),
|
|
}
|
|
}
|
|
|
|
pub fn bitmap3(&self) -> Bitmap3 {
|
|
Bitmap3::new(
|
|
self.in_mode
|
|
.take()
|
|
.expect("Cannot create new mode as mode already taken"),
|
|
)
|
|
}
|
|
pub fn bitmap4(&self) -> Bitmap4 {
|
|
Bitmap4::new(
|
|
self.in_mode
|
|
.take()
|
|
.expect("Cannot create new mode as mode already taken"),
|
|
)
|
|
}
|
|
}
|
|
|
|
pub struct Bitmap3<'a> {
|
|
_in_mode: SingleToken<'a>,
|
|
}
|
|
|
|
impl<'a> Bitmap3<'a> {
|
|
fn new(in_mode: SingleToken<'a>) -> Self {
|
|
set_graphics_mode(DisplayMode::Bitmap3);
|
|
set_graphics_settings(GraphicsSettings::LAYER_BG2);
|
|
Bitmap3 { _in_mode: in_mode }
|
|
}
|
|
pub fn draw_point(&self, x: i32, y: i32, colour: u16) {
|
|
let x = x.try_into().unwrap();
|
|
let y = y.try_into().unwrap();
|
|
BITMAP_MODE_3.set(x, y, colour)
|
|
}
|
|
}
|
|
|
|
pub struct Bitmap4<'a> {
|
|
_in_mode: SingleToken<'a>,
|
|
}
|
|
|
|
impl<'a> Bitmap4<'a> {
|
|
fn new(in_mode: SingleToken<'a>) -> Self {
|
|
set_graphics_mode(DisplayMode::Bitmap4);
|
|
set_graphics_settings(GraphicsSettings::LAYER_BG2);
|
|
Bitmap4 { _in_mode: in_mode }
|
|
}
|
|
|
|
pub fn draw_point_page(&self, x: i32, y: i32, colour: u8, page: Page) {
|
|
let addr = match page {
|
|
Page::Front => BITMAP_PAGE_FRONT_MODE_4,
|
|
Page::Back => BITMAP_PAGE_BACK_MODE_4,
|
|
};
|
|
|
|
let x_in_screen = (x / 2) as usize;
|
|
let y_in_screen = y as usize;
|
|
|
|
let c = addr.get(x_in_screen, y_in_screen);
|
|
if x & 0b1 != 0 {
|
|
addr.set(x_in_screen, y_in_screen, c | (colour as u16) << 8);
|
|
} else {
|
|
addr.set(x_in_screen, y_in_screen, c | colour as u16);
|
|
}
|
|
}
|
|
|
|
pub fn draw_point(&self, x: i32, y: i32, colour: u8) {
|
|
let disp = DISPLAY_CONTROL.get();
|
|
|
|
let page = if disp & GraphicsSettings::PAGE_SELECT.bits() != 0 {
|
|
Page::Back
|
|
} else {
|
|
Page::Front
|
|
};
|
|
|
|
self.draw_point_page(x, y, colour, page)
|
|
}
|
|
|
|
pub fn set_palette_entry(&self, entry: u32, colour: u16) {
|
|
PALETTE_BACKGROUND.set(entry as usize, colour);
|
|
}
|
|
|
|
pub fn flip_page(&self) {
|
|
let disp = DISPLAY_CONTROL.get();
|
|
let swapped = disp ^ GraphicsSettings::PAGE_SELECT.bits();
|
|
DISPLAY_CONTROL.set(swapped);
|
|
}
|
|
}
|
|
|
|
fn set_graphics_mode(mode: DisplayMode) {
|
|
let current = DISPLAY_CONTROL.get();
|
|
let current = current & (!0b111);
|
|
let s = current | (mode as u16 & 0b111);
|
|
|
|
DISPLAY_CONTROL.set(s);
|
|
}
|
|
|
|
pub 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)]
|
|
pub fn busy_wait_for_VBlank() {
|
|
while VCOUNT.get() >= 160 {}
|
|
while VCOUNT.get() < 160 {}
|
|
}
|
|
|
|
#[allow(non_snake_case)]
|
|
pub fn enable_VBlank_interrupt() {
|
|
let status = DISPLAY_STATUS.get() | (1 << 3);
|
|
DISPLAY_STATUS.set(status);
|
|
}
|
|
|
|
#[allow(non_snake_case)]
|
|
pub fn wait_for_VBlank() {
|
|
crate::syscall::wait_for_VBlank();
|
|
}
|