disallow multiple video contexts at compile time

This commit is contained in:
Corwin Kuiper 2021-03-08 01:59:05 +00:00
parent 195004e8b2
commit 0dc5d620c7
8 changed files with 153 additions and 90 deletions

View file

@ -12,9 +12,9 @@ struct Vector2D {
#[start]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
let gba = gba::Gba::new();
let bitmap = gba.display.bitmap3();
let vblank = gba.display.get_vblank();
let mut gba = gba::Gba::new();
let mut bitmap = gba.display.video.bitmap3();
let vblank = gba.display.vblank.get();
let mut input = gba::input::ButtonController::new();
let mut pos = Vector2D {

View file

@ -7,9 +7,9 @@ use gba::display;
#[start]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
let gba = gba::Gba::new();
let bitmap = gba.display.bitmap4();
let vblank = gba.display.get_vblank();
let mut gba = gba::Gba::new();
let mut bitmap = gba.display.video.bitmap4();
let vblank = gba.display.vblank.get();
bitmap.set_palette_entry(1, 0x001F);
bitmap.set_palette_entry(2, 0x03E0);

View file

@ -0,0 +1,87 @@
#![no_std]
#![feature(start)]
extern crate gba;
use gba::display;
struct Vector2D {
x: i32,
y: i32,
}
#[start]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
let mut gba = gba::Gba::new();
let mut vblank = gba.display.vblank.get();
let mut input = gba::input::ButtonController::new();
loop {
bitmap3_mode(&mut gba.display.video.bitmap3(), &mut vblank, &mut input);
bitmap4_mode(&mut gba.display.video.bitmap4(), &mut vblank, &mut input);
}
}
fn bitmap3_mode(
bitmap: &mut display::bitmap3::Bitmap3,
vblank: &mut display::VBlank,
input: &mut gba::input::ButtonController,
) {
let mut pos = Vector2D {
x: display::WIDTH / 2,
y: display::HEIGHT / 2,
};
loop {
vblank.wait_for_VBlank();
input.update();
if input.is_just_pressed(gba::input::Button::B) {
break;
}
pos.x += input.x_tri() as i32;
pos.y += input.y_tri() as i32;
pos.x = pos.x.clamp(0, display::WIDTH - 1);
pos.y = pos.y.clamp(0, display::HEIGHT - 1);
bitmap.draw_point(pos.x, pos.y, 0x001F);
}
}
fn bitmap4_mode(
bitmap: &mut display::bitmap4::Bitmap4,
vblank: &mut display::VBlank,
input: &mut gba::input::ButtonController,
) {
bitmap.set_palette_entry(1, 0x001F);
bitmap.set_palette_entry(2, 0x03E0);
bitmap.draw_point_page(
display::WIDTH / 2,
display::HEIGHT / 2,
1,
display::bitmap4::Page::Front,
);
bitmap.draw_point_page(
display::WIDTH / 2 + 5,
display::HEIGHT / 2,
2,
display::bitmap4::Page::Back,
);
let mut count = 0;
loop {
vblank.wait_for_VBlank();
input.update();
if input.is_just_pressed(gba::input::Button::B) {
break;
}
count += 1;
if count % 6 == 0 {
bitmap.flip_page();
}
}
}

View file

@ -7,9 +7,9 @@ use gba::display;
#[start]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
let gba = gba::Gba::new();
let bitmap = gba.display.bitmap3();
let mut gba = gba::Gba::new();
let mut bitmap = gba.display.bitmap3();
let mut input = gba::input::ButtonController::new();
loop {
@ -18,9 +18,5 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
if input.is_just_pressed(gba::input::Button::A) {
bitmap.draw_point(display::WIDTH, 0, 0x05);
}
// if B is pressed, try take another bitmap
if input.is_just_pressed(gba::input::Button::B) {
gba.display.bitmap4();
}
}
}

View file

@ -1,4 +1,4 @@
use crate::{memory_mapped::MemoryMapped2DArray, single::SingleToken};
use crate::memory_mapped::MemoryMapped2DArray;
use super::{
set_graphics_mode, set_graphics_settings, DisplayMode, GraphicsSettings, HEIGHT, WIDTH,
@ -9,20 +9,18 @@ use core::convert::TryInto;
const BITMAP_MODE_3: MemoryMapped2DArray<u16, { WIDTH as usize }, { HEIGHT as usize }> =
unsafe { MemoryMapped2DArray::new(0x600_0000) };
pub struct Bitmap3<'a> {
_in_mode: SingleToken<'a>,
}
pub struct Bitmap3 {}
impl<'a> Bitmap3<'a> {
pub(crate) unsafe fn new(in_mode: SingleToken<'a>) -> Self {
impl Bitmap3 {
pub(crate) unsafe fn new() -> Self {
set_graphics_mode(DisplayMode::Bitmap3);
set_graphics_settings(GraphicsSettings::LAYER_BG2);
Bitmap3 { _in_mode: in_mode }
Bitmap3 {}
}
/// Draws point to screen at (x, y) coordinates with colour and panics if
/// (x, y) is out of the bounds of the screen.
pub fn draw_point(&self, x: i32, y: i32, colour: u16) {
pub fn draw_point(&mut 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)

View file

@ -1,7 +1,4 @@
use crate::{
memory_mapped::{MemoryMapped1DArray, MemoryMapped2DArray},
single::SingleToken,
};
use crate::memory_mapped::{MemoryMapped1DArray, MemoryMapped2DArray};
use super::{
set_graphics_mode, set_graphics_settings, DisplayMode, GraphicsSettings, DISPLAY_CONTROL,
@ -26,21 +23,19 @@ pub enum Page {
Back = 1,
}
pub struct Bitmap4<'a> {
_in_mode: SingleToken<'a>,
}
pub struct Bitmap4 {}
impl<'a> Bitmap4<'a> {
pub(crate) unsafe fn new(in_mode: SingleToken<'a>) -> Self {
impl Bitmap4 {
pub(crate) unsafe fn new() -> Self {
set_graphics_mode(DisplayMode::Bitmap4);
set_graphics_settings(GraphicsSettings::LAYER_BG2);
Bitmap4 { _in_mode: in_mode }
Bitmap4 {}
}
/// Draws point on specified page at (x, y) coordinates with colour index
/// whose colour is specified in the background palette. Panics if (x, y) is
/// out of the bounds of the screen.
pub fn draw_point_page(&self, x: i32, y: i32, colour: u8, page: Page) {
pub fn draw_point_page(&mut 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,
@ -60,7 +55,7 @@ impl<'a> Bitmap4<'a> {
/// Draws point on the non-current page at (x, y) coordinates with colour
/// index whose colour is specified in the background palette. Panics if (x,
/// y) is out of the bounds of the screen.
pub fn draw_point(&self, x: i32, y: i32, colour: u8) {
pub fn draw_point(&mut self, x: i32, y: i32, colour: u8) {
let disp = DISPLAY_CONTROL.get();
// get other page
@ -74,13 +69,13 @@ impl<'a> Bitmap4<'a> {
}
/// Sets the colour of colour index in the background palette.
pub fn set_palette_entry(&self, entry: u32, colour: u16) {
pub fn set_palette_entry(&mut self, entry: u32, colour: u16) {
PALETTE_BACKGROUND.set(entry as usize, colour);
}
/// Flips page, changing the Gameboy advance to draw the contents of the
/// other page
pub fn flip_page(&self) {
pub fn flip_page(&mut self) {
let disp = DISPLAY_CONTROL.get();
let swapped = disp ^ GraphicsSettings::PAGE_SELECT.bits();
DISPLAY_CONTROL.set(swapped);

View file

@ -9,6 +9,7 @@ use bitmap4::Bitmap4;
pub mod bitmap3;
pub mod bitmap4;
pub mod tiled0;
const DISPLAY_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0000) };
const DISPLAY_STATUS: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0004) };
@ -45,50 +46,42 @@ enum DisplayMode {
Bitmap5 = 5,
}
#[non_exhaustive]
/// Manages distribution of display modes, obtained from the gba struct
pub struct Display {
in_mode: Single,
vblank: Single,
pub video: Video,
pub vblank: VBlankGiver,
}
#[non_exhaustive]
pub struct Video {}
#[non_exhaustive]
pub struct VBlankGiver {}
impl Video {
/// Bitmap mode that provides a 16-bit colour framebuffer
pub fn bitmap3(&mut self) -> Bitmap3 {
unsafe { Bitmap3::new() }
}
/// Bitmap 4 provides two 8-bit paletted framebuffers with page switching
pub fn bitmap4(&mut self) -> Bitmap4 {
unsafe { bitmap4::Bitmap4::new() }
}
}
impl VBlankGiver {
/// Gets a vblank handle where only one can be obtained at a time
pub fn get(&mut self) -> VBlank {
unsafe { VBlank::new() }
}
}
impl Display {
pub(crate) const unsafe fn new() -> Self {
Display {
in_mode: Single::new(),
vblank: Single::new(),
}
}
/// Bitmap mode that provides a 16-bit colour framebuffer
pub fn bitmap3(&self) -> Bitmap3 {
unsafe {
Bitmap3::new(
self.in_mode
.take()
.expect("Cannot create new mode as mode already taken"),
)
}
}
/// Bitmap 4 provides two 8-bit paletted framebuffers with page switching
pub fn bitmap4(&self) -> Bitmap4 {
unsafe {
bitmap4::Bitmap4::new(
self.in_mode
.take()
.expect("Cannot create new mode as mode already taken"),
)
}
}
/// Gets a vblank handle where only one can be obtained at a time
pub fn get_vblank(&self) -> VBlank {
unsafe {
VBlank::new(
self.vblank
.take()
.expect("Cannot create another vblank handler"),
)
video: Video {},
vblank: VBlankGiver {},
}
}
}
@ -121,16 +114,14 @@ pub fn busy_wait_for_VBlank() {
/// Once obtained, this guarentees that interrupts are enabled and set up to
/// allow for waiting for vblank
pub struct VBlank<'a> {
_got: SingleToken<'a>,
}
pub struct VBlank {}
impl<'a> VBlank<'a> {
unsafe fn new(a: SingleToken<'a>) -> Self {
impl VBlank {
unsafe fn new() -> Self {
crate::interrupt::enable_interrupts();
crate::interrupt::enable(crate::interrupt::Interrupt::VBlank);
enable_VBlank_interrupt();
VBlank { _got: a }
VBlank {}
}
#[allow(non_snake_case)]
@ -141,7 +132,7 @@ impl<'a> VBlank<'a> {
}
}
impl<'a> Drop for VBlank<'a> {
impl Drop for VBlank {
fn drop(&mut self) {
unsafe {
disable_VBlank_interrupt();

View file

@ -1,5 +1,3 @@
use core::cell::Cell;
pub struct Singleton<T> {
single: Option<T>,
}
@ -15,27 +13,25 @@ impl<T> Singleton<T> {
}
pub struct Single {
is_taken: Cell<bool>,
is_taken: bool,
}
pub struct SingleToken<'a> {
cell: &'a Cell<bool>,
cell: &'a mut bool,
}
impl Single {
pub const fn new() -> Self {
Single {
is_taken: Cell::new(false),
}
Single { is_taken: false }
}
pub fn take(&self) -> Result<SingleToken, &'static str> {
if self.is_taken.get() {
pub fn take(&mut self) -> Result<SingleToken, &'static str> {
if self.is_taken {
Err("Already taken")
} else {
self.is_taken.set(true);
self.is_taken = true;
Ok(SingleToken {
cell: &self.is_taken,
cell: &mut self.is_taken,
})
}
}
@ -43,6 +39,6 @@ impl Single {
impl Drop for SingleToken<'_> {
fn drop(&mut self) {
self.cell.set(false);
(*self.cell) = false;
}
}