2018-11-14 06:47:52 +11:00
|
|
|
//! Module for all things relating to the Video RAM.
|
|
|
|
//!
|
|
|
|
//! Note that the GBA has six different display modes available, and the
|
|
|
|
//! _meaning_ of Video RAM depends on which display mode is active. In all
|
|
|
|
//! cases, Video RAM is **96kb** from `0x0600_0000` to `0x0601_7FFF`.
|
|
|
|
//!
|
|
|
|
//! # Safety
|
|
|
|
//!
|
|
|
|
//! Note that all possible bit patterns are technically allowed within Video
|
|
|
|
//! Memory. If you write the "wrong" thing into video memory you don't crash the
|
|
|
|
//! GBA, instead you just get graphical glitches (or perhaps nothing at all).
|
|
|
|
//! Accordingly, the "safe" functions here will check that you're in bounds, but
|
|
|
|
//! they won't bother to check that you've set the video mode they're designed
|
|
|
|
//! for.
|
|
|
|
|
2018-11-15 13:50:42 +11:00
|
|
|
pub use super::*;
|
|
|
|
|
2018-11-14 06:47:52 +11:00
|
|
|
/// The physical width in pixels of the GBA screen.
|
|
|
|
pub const SCREEN_WIDTH: isize = 240;
|
|
|
|
|
|
|
|
/// The physical height in pixels of the GBA screen.
|
|
|
|
pub const SCREEN_HEIGHT: isize = 160;
|
|
|
|
|
|
|
|
/// The start of VRAM.
|
|
|
|
///
|
|
|
|
/// Depending on what display mode is currently set there's different ways that
|
|
|
|
/// your program should interpret the VRAM space. Accordingly, we give the raw
|
|
|
|
/// value as just being a `usize`.
|
|
|
|
pub const VRAM_BASE_ADDRESS: usize = 0x0600_0000;
|
|
|
|
|
|
|
|
/// Draws a pixel to the screen while in Display Mode 3, with bounds checks.
|
2018-11-15 13:50:42 +11:00
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// If `col` or `row` are out of bounds this will panic.
|
|
|
|
pub fn mode3_draw_pixel(col: isize, row: isize, color: u16) {
|
2018-11-14 06:47:52 +11:00
|
|
|
assert!(col >= 0 && col < SCREEN_WIDTH);
|
|
|
|
assert!(row >= 0 && row < SCREEN_HEIGHT);
|
2018-11-15 13:50:42 +11:00
|
|
|
unsafe { mode3_draw_pixel_unchecked(col, row, color) }
|
2018-11-14 06:47:52 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Draws a pixel to the screen while in Display Mode 3.
|
|
|
|
///
|
|
|
|
/// Coordinates are relative to the top left corner.
|
|
|
|
///
|
|
|
|
/// If you're in another mode you'll get something weird drawn, but it's memory
|
|
|
|
/// safe in the rust sense as long as you stay in bounds.
|
|
|
|
///
|
|
|
|
/// # Safety
|
|
|
|
///
|
|
|
|
/// * `col` must be in `0..SCREEN_WIDTH`
|
|
|
|
/// * `row` must be in `0..SCREEN_HEIGHT`
|
2018-11-15 13:50:42 +11:00
|
|
|
pub unsafe fn mode3_draw_pixel_unchecked(col: isize, row: isize, color: u16) {
|
|
|
|
VolatilePtr(VRAM_BASE_ADDRESS as *mut u16).offset(col + row * SCREEN_WIDTH).write(color);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Reads the given pixel of video memory according to Mode 3 placement.
|
|
|
|
///
|
|
|
|
/// # Failure
|
|
|
|
///
|
|
|
|
/// If the location is out of bounds you get `None`.
|
|
|
|
pub fn mode3_read_pixel(col: isize, row: isize) -> Option<u16> {
|
|
|
|
if col >= 0 && col < SCREEN_WIDTH && row >= 0 && row < SCREEN_HEIGHT {
|
|
|
|
unsafe { Some(VolatilePtr(VRAM_BASE_ADDRESS as *mut u16).offset(col + row * SCREEN_WIDTH).read()) }
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Clears the entire screen to the color specified.
|
|
|
|
pub unsafe fn mode3_clear_screen(color: u16) {
|
2018-11-20 21:41:42 +11:00
|
|
|
// TODO: use DMA?
|
2018-11-15 13:50:42 +11:00
|
|
|
let color = color as u32;
|
|
|
|
let bulk_color = color << 16 | color;
|
|
|
|
let mut ptr = VolatilePtr(VRAM_BASE_ADDRESS as *mut u32);
|
2018-11-20 21:41:42 +11:00
|
|
|
for _ in 0..(SCREEN_HEIGHT * SCREEN_WIDTH / 2) {
|
|
|
|
ptr.write(bulk_color);
|
|
|
|
ptr = ptr.offset(1);
|
2018-11-15 13:50:42 +11:00
|
|
|
}
|
2018-11-14 06:47:52 +11:00
|
|
|
}
|