mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-23 16:21:33 +11:00
disallow multiple video contexts at compile time
This commit is contained in:
parent
195004e8b2
commit
0dc5d620c7
|
@ -12,9 +12,9 @@ struct Vector2D {
|
||||||
|
|
||||||
#[start]
|
#[start]
|
||||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
let gba = gba::Gba::new();
|
let mut gba = gba::Gba::new();
|
||||||
let bitmap = gba.display.bitmap3();
|
let mut bitmap = gba.display.video.bitmap3();
|
||||||
let vblank = gba.display.get_vblank();
|
let vblank = gba.display.vblank.get();
|
||||||
|
|
||||||
let mut input = gba::input::ButtonController::new();
|
let mut input = gba::input::ButtonController::new();
|
||||||
let mut pos = Vector2D {
|
let mut pos = Vector2D {
|
||||||
|
|
|
@ -7,9 +7,9 @@ use gba::display;
|
||||||
|
|
||||||
#[start]
|
#[start]
|
||||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
let gba = gba::Gba::new();
|
let mut gba = gba::Gba::new();
|
||||||
let bitmap = gba.display.bitmap4();
|
let mut bitmap = gba.display.video.bitmap4();
|
||||||
let vblank = gba.display.get_vblank();
|
let vblank = gba.display.vblank.get();
|
||||||
|
|
||||||
bitmap.set_palette_entry(1, 0x001F);
|
bitmap.set_palette_entry(1, 0x001F);
|
||||||
bitmap.set_palette_entry(2, 0x03E0);
|
bitmap.set_palette_entry(2, 0x03E0);
|
||||||
|
|
87
examples/multiple_video.rs
Normal file
87
examples/multiple_video.rs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,9 +7,9 @@ use gba::display;
|
||||||
|
|
||||||
#[start]
|
#[start]
|
||||||
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
let gba = gba::Gba::new();
|
let mut gba = gba::Gba::new();
|
||||||
let bitmap = gba.display.bitmap3();
|
|
||||||
|
|
||||||
|
let mut bitmap = gba.display.bitmap3();
|
||||||
let mut input = gba::input::ButtonController::new();
|
let mut input = gba::input::ButtonController::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
@ -18,9 +18,5 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
if input.is_just_pressed(gba::input::Button::A) {
|
if input.is_just_pressed(gba::input::Button::A) {
|
||||||
bitmap.draw_point(display::WIDTH, 0, 0x05);
|
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{memory_mapped::MemoryMapped2DArray, single::SingleToken};
|
use crate::memory_mapped::MemoryMapped2DArray;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
set_graphics_mode, set_graphics_settings, DisplayMode, GraphicsSettings, HEIGHT, WIDTH,
|
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 }> =
|
const BITMAP_MODE_3: MemoryMapped2DArray<u16, { WIDTH as usize }, { HEIGHT as usize }> =
|
||||||
unsafe { MemoryMapped2DArray::new(0x600_0000) };
|
unsafe { MemoryMapped2DArray::new(0x600_0000) };
|
||||||
|
|
||||||
pub struct Bitmap3<'a> {
|
pub struct Bitmap3 {}
|
||||||
_in_mode: SingleToken<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Bitmap3<'a> {
|
impl Bitmap3 {
|
||||||
pub(crate) unsafe fn new(in_mode: SingleToken<'a>) -> Self {
|
pub(crate) unsafe fn new() -> Self {
|
||||||
set_graphics_mode(DisplayMode::Bitmap3);
|
set_graphics_mode(DisplayMode::Bitmap3);
|
||||||
set_graphics_settings(GraphicsSettings::LAYER_BG2);
|
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
|
/// Draws point to screen at (x, y) coordinates with colour and panics if
|
||||||
/// (x, y) is out of the bounds of the screen.
|
/// (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 x = x.try_into().unwrap();
|
||||||
let y = y.try_into().unwrap();
|
let y = y.try_into().unwrap();
|
||||||
BITMAP_MODE_3.set(x, y, colour)
|
BITMAP_MODE_3.set(x, y, colour)
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
use crate::{
|
use crate::memory_mapped::{MemoryMapped1DArray, MemoryMapped2DArray};
|
||||||
memory_mapped::{MemoryMapped1DArray, MemoryMapped2DArray},
|
|
||||||
single::SingleToken,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
set_graphics_mode, set_graphics_settings, DisplayMode, GraphicsSettings, DISPLAY_CONTROL,
|
set_graphics_mode, set_graphics_settings, DisplayMode, GraphicsSettings, DISPLAY_CONTROL,
|
||||||
|
@ -26,21 +23,19 @@ pub enum Page {
|
||||||
Back = 1,
|
Back = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Bitmap4<'a> {
|
pub struct Bitmap4 {}
|
||||||
_in_mode: SingleToken<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Bitmap4<'a> {
|
impl Bitmap4 {
|
||||||
pub(crate) unsafe fn new(in_mode: SingleToken<'a>) -> Self {
|
pub(crate) unsafe fn new() -> Self {
|
||||||
set_graphics_mode(DisplayMode::Bitmap4);
|
set_graphics_mode(DisplayMode::Bitmap4);
|
||||||
set_graphics_settings(GraphicsSettings::LAYER_BG2);
|
set_graphics_settings(GraphicsSettings::LAYER_BG2);
|
||||||
Bitmap4 { _in_mode: in_mode }
|
Bitmap4 {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draws point on specified page at (x, y) coordinates with colour index
|
/// 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
|
/// whose colour is specified in the background palette. Panics if (x, y) is
|
||||||
/// out of the bounds of the screen.
|
/// 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 {
|
let addr = match page {
|
||||||
Page::Front => BITMAP_PAGE_FRONT_MODE_4,
|
Page::Front => BITMAP_PAGE_FRONT_MODE_4,
|
||||||
Page::Back => BITMAP_PAGE_BACK_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
|
/// 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,
|
/// index whose colour is specified in the background palette. Panics if (x,
|
||||||
/// y) is out of the bounds of the screen.
|
/// 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();
|
let disp = DISPLAY_CONTROL.get();
|
||||||
|
|
||||||
// get other page
|
// get other page
|
||||||
|
@ -74,13 +69,13 @@ impl<'a> Bitmap4<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the colour of colour index in the background palette.
|
/// 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);
|
PALETTE_BACKGROUND.set(entry as usize, colour);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Flips page, changing the Gameboy advance to draw the contents of the
|
/// Flips page, changing the Gameboy advance to draw the contents of the
|
||||||
/// other page
|
/// other page
|
||||||
pub fn flip_page(&self) {
|
pub fn flip_page(&mut self) {
|
||||||
let disp = DISPLAY_CONTROL.get();
|
let disp = DISPLAY_CONTROL.get();
|
||||||
let swapped = disp ^ GraphicsSettings::PAGE_SELECT.bits();
|
let swapped = disp ^ GraphicsSettings::PAGE_SELECT.bits();
|
||||||
DISPLAY_CONTROL.set(swapped);
|
DISPLAY_CONTROL.set(swapped);
|
||||||
|
|
|
@ -9,6 +9,7 @@ use bitmap4::Bitmap4;
|
||||||
|
|
||||||
pub mod bitmap3;
|
pub mod bitmap3;
|
||||||
pub mod bitmap4;
|
pub mod bitmap4;
|
||||||
|
pub mod tiled0;
|
||||||
|
|
||||||
const DISPLAY_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0000) };
|
const DISPLAY_CONTROL: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0000) };
|
||||||
const DISPLAY_STATUS: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0004) };
|
const DISPLAY_STATUS: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0400_0004) };
|
||||||
|
@ -45,50 +46,42 @@ enum DisplayMode {
|
||||||
Bitmap5 = 5,
|
Bitmap5 = 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
/// Manages distribution of display modes, obtained from the gba struct
|
/// Manages distribution of display modes, obtained from the gba struct
|
||||||
pub struct Display {
|
pub struct Display {
|
||||||
in_mode: Single,
|
pub video: Video,
|
||||||
vblank: Single,
|
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 {
|
impl Display {
|
||||||
pub(crate) const unsafe fn new() -> Self {
|
pub(crate) const unsafe fn new() -> Self {
|
||||||
Display {
|
Display {
|
||||||
in_mode: Single::new(),
|
video: Video {},
|
||||||
vblank: Single::new(),
|
vblank: VBlankGiver {},
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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"),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,16 +114,14 @@ pub fn busy_wait_for_VBlank() {
|
||||||
|
|
||||||
/// Once obtained, this guarentees that interrupts are enabled and set up to
|
/// Once obtained, this guarentees that interrupts are enabled and set up to
|
||||||
/// allow for waiting for vblank
|
/// allow for waiting for vblank
|
||||||
pub struct VBlank<'a> {
|
pub struct VBlank {}
|
||||||
_got: SingleToken<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> VBlank<'a> {
|
impl VBlank {
|
||||||
unsafe fn new(a: SingleToken<'a>) -> Self {
|
unsafe fn new() -> Self {
|
||||||
crate::interrupt::enable_interrupts();
|
crate::interrupt::enable_interrupts();
|
||||||
crate::interrupt::enable(crate::interrupt::Interrupt::VBlank);
|
crate::interrupt::enable(crate::interrupt::Interrupt::VBlank);
|
||||||
enable_VBlank_interrupt();
|
enable_VBlank_interrupt();
|
||||||
VBlank { _got: a }
|
VBlank {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[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) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
disable_VBlank_interrupt();
|
disable_VBlank_interrupt();
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use core::cell::Cell;
|
|
||||||
|
|
||||||
pub struct Singleton<T> {
|
pub struct Singleton<T> {
|
||||||
single: Option<T>,
|
single: Option<T>,
|
||||||
}
|
}
|
||||||
|
@ -15,27 +13,25 @@ impl<T> Singleton<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Single {
|
pub struct Single {
|
||||||
is_taken: Cell<bool>,
|
is_taken: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SingleToken<'a> {
|
pub struct SingleToken<'a> {
|
||||||
cell: &'a Cell<bool>,
|
cell: &'a mut bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Single {
|
impl Single {
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Single {
|
Single { is_taken: false }
|
||||||
is_taken: Cell::new(false),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn take(&self) -> Result<SingleToken, &'static str> {
|
pub fn take(&mut self) -> Result<SingleToken, &'static str> {
|
||||||
if self.is_taken.get() {
|
if self.is_taken {
|
||||||
Err("Already taken")
|
Err("Already taken")
|
||||||
} else {
|
} else {
|
||||||
self.is_taken.set(true);
|
self.is_taken = true;
|
||||||
Ok(SingleToken {
|
Ok(SingleToken {
|
||||||
cell: &self.is_taken,
|
cell: &mut self.is_taken,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,6 +39,6 @@ impl Single {
|
||||||
|
|
||||||
impl Drop for SingleToken<'_> {
|
impl Drop for SingleToken<'_> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.cell.set(false);
|
(*self.cell) = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue