we can make a little cyan guy show up on the screen.

This commit is contained in:
lokathor 2022-11-28 16:33:47 -07:00
parent d412ee7779
commit 6ba28c5347
12 changed files with 114 additions and 14 deletions

View file

@ -1 +1 @@
foo_ foo

47
examples/game.rs Normal file
View file

@ -0,0 +1,47 @@
#![no_std]
#![no_main]
use core::fmt::Write;
use gba::prelude::*;
#[panic_handler]
fn panic_handler(info: &core::panic::PanicInfo) -> ! {
if let Ok(mut logger) = MgbaBufferedLogger::try_new(MgbaMessageLevel::Fatal) {
writeln!(logger, "{info}").ok();
}
loop {}
}
#[no_mangle]
extern "C" fn main() -> ! {
DISPSTAT.write(DisplayStatus::new().with_irq_vblank(true));
IE.write(IrqBits::VBLANK);
IME.write(true);
TIMER0_CONTROL.write(TimerControl::new().with_enabled(true));
Cga8x8Thick.bitunpack_4bpp(CHARBLOCK0_4BPP.as_region(), 0);
Cga8x8Thick.bitunpack_4bpp(OBJ_TILES.as_region(), 0);
BG_PALETTE.index(1).write(Color::MAGENTA);
OBJ_PALETTE.index(1).write(Color::CYAN);
let no_display = ObjAttr0::new().with_style(ObjDisplayStyle::NotDisplayed);
OBJ_ATTR0.iter().for_each(|va| va.write(no_display));
let mut obj = ObjAttr::new();
obj.set_x(13);
obj.set_y(37);
obj.set_tile_id(1);
OBJ_ATTR_ALL.index(0).write(obj);
DISPCNT.write(DisplayControl::new().with_show_obj(true));
loop {
VBlankIntrWait();
let keys = KEYINPUT.read();
if keys.a() {
let t = TIMER0_COUNT.read();
BACKDROP_COLOR.write(Color(t));
}
}
}

View file

@ -13,7 +13,7 @@ fn panic_handler(info: &core::panic::PanicInfo) -> ! {
} }
#[allow(dead_code)] #[allow(dead_code)]
const FOO_: Align4<[u8; 4]> = include_aligned_bytes!("foo.txt"); const FOO: Align4<[u8; 3]> = include_aligned_bytes!("foo.txt");
#[link_section = ".ewram"] #[link_section = ".ewram"]
static FRAME_KEYS: GbaCell<KeyInput> = GbaCell::new(KeyInput::new()); static FRAME_KEYS: GbaCell<KeyInput> = GbaCell::new(KeyInput::new());

View file

@ -1,6 +1,5 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(isa_attribute)]
use gba::prelude::*; use gba::prelude::*;

View file

@ -610,6 +610,11 @@ unsafe extern "C" fn __aeabi_uwrite8(value: u64, address: *mut c_void) {
/// function when possible. /// function when possible.
/// ///
/// * **Returns:** the original `dest` pointer. /// * **Returns:** the original `dest` pointer.
///
/// ## Safety
/// * `src` must be readable for `byte_count` bytes.
/// * `dest` must be writable for `byte_count` bytes.
/// * The `src` and `dest` regions must not overlap.
#[inline] #[inline]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn memcpy( pub unsafe extern "C" fn memcpy(
@ -626,6 +631,10 @@ pub unsafe extern "C" fn memcpy(
/// function when possible. /// function when possible.
/// ///
/// * **Returns:** the original `dest` pointer. /// * **Returns:** the original `dest` pointer.
///
/// ## Safety
/// * `src` must be readable for `byte_count` bytes.
/// * `dest` must be writable for `byte_count` bytes.
#[inline] #[inline]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn memmove( pub unsafe extern "C" fn memmove(
@ -644,6 +653,9 @@ pub unsafe extern "C" fn memmove(
/// it up like might happen in C. /// it up like might happen in C.
/// ///
/// * **Returns:** the original `dest` pointer. /// * **Returns:** the original `dest` pointer.
///
/// ## Safety
/// * `dest` must be writable for `byte_count` bytes.
#[inline] #[inline]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn memset( pub unsafe extern "C" fn memset(

View file

@ -1,4 +1,5 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
#![allow(clippy::missing_safety_doc)]
//! The GBA's BIOS provides limited built-in utility functions. //! The GBA's BIOS provides limited built-in utility functions.
//! //!

View file

@ -262,6 +262,7 @@ macro_rules! impl_signed_fixed_ops {
} }
impl_trait_op_unit!($t, Neg, neg); impl_trait_op_unit!($t, Neg, neg);
impl<const B: u32> core::fmt::Debug for Fixed<$t, B> { impl<const B: u32> core::fmt::Debug for Fixed<$t, B> {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let whole: $t = self.trunc().into_raw() >> B; let whole: $t = self.trunc().into_raw() >> B;
let fract: $t = self.fract().into_raw(); let fract: $t = self.fract().into_raw();
@ -315,6 +316,7 @@ macro_rules! impl_unsigned_fixed_ops {
} }
} }
impl<const B: u32> core::fmt::Debug for Fixed<$t, B> { impl<const B: u32> core::fmt::Debug for Fixed<$t, B> {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let whole: $t = self.trunc().into_raw() >> B; let whole: $t = self.trunc().into_raw() >> B;
let fract: $t = self.fract().into_raw(); let fract: $t = self.fract().into_raw();

View file

@ -64,6 +64,7 @@ impl<T> Debug for GbaCell<T>
where where
T: GbaCellSafe + Debug, T: GbaCellSafe + Debug,
{ {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
<T as Debug>::fmt(&self.read(), f) <T as Debug>::fmt(&self.read(), f)
} }

View file

@ -1,8 +1,9 @@
#![no_std] #![no_std]
#![feature(asm_sym)]
#![feature(asm_const)] #![feature(asm_const)]
#![feature(isa_attribute)]
#![feature(naked_functions)] #![feature(naked_functions)]
#![warn(clippy::missing_inline_in_public_items)]
#![allow(clippy::let_and_return)]
#![allow(clippy::result_unit_err)]
//#![warn(missing_docs)] //#![warn(missing_docs)]
//! A crate for GBA development. //! A crate for GBA development.
@ -72,9 +73,10 @@
//! //!
//! ## Other GBA-related Crates //! ## Other GBA-related Crates
//! //!
//! This crate provides a largely "unmanaged" interaction with the GBA's //! This crate provides an API to interact with the GBA that is safe, but with
//! hardware. If you would like an API that use the borrow checker to guide you //! minimal restrictions on what components can be changed when. If you'd like
//! more, the [agb](https://docs.rs/agb) crate might be what you want. //! an API where the borrow checker provides stronger control over component
//! access then the [agb](https://docs.rs/agb) crate might be what you want.
//! //!
//! ## Safety //! ## Safety
//! //!
@ -114,6 +116,7 @@ impl<const N: usize> Align4<[u8; N]> {
/// Views these bytes as a slice of `u32` /// Views these bytes as a slice of `u32`
/// ## Panics /// ## Panics
/// * If the number of bytes isn't a multiple of 4 /// * If the number of bytes isn't a multiple of 4
#[inline]
pub fn as_u32_slice(&self) -> &[u32] { pub fn as_u32_slice(&self) -> &[u32] {
assert!(self.0.len() % 4 == 0); assert!(self.0.len() % 4 == 0);
// Safety: our struct is aligned to 4, so the pointer will already be // Safety: our struct is aligned to 4, so the pointer will already be
@ -128,6 +131,7 @@ impl<const N: usize> Align4<[u8; N]> {
/// Views these bytes as a slice of `u16` /// Views these bytes as a slice of `u16`
/// ## Panics /// ## Panics
/// * If the number of bytes isn't a multiple of 2 /// * If the number of bytes isn't a multiple of 2
#[inline]
pub fn as_u16_slice(&self) -> &[u16] { pub fn as_u16_slice(&self) -> &[u16] {
assert!(self.0.len() % 2 == 0); assert!(self.0.len() % 2 == 0);
// Safety: our struct is aligned to 4, so the pointer will already be // Safety: our struct is aligned to 4, so the pointer will already be
@ -144,7 +148,6 @@ impl<const N: usize> Align4<[u8; N]> {
#[macro_export] #[macro_export]
macro_rules! include_aligned_bytes { macro_rules! include_aligned_bytes {
($file:expr $(,)?) => {{ ($file:expr $(,)?) => {{
let LONG_NAME_THAT_DOES_NOT_CLASH = *include_bytes!($file); Align4(*include_bytes!($file))
Align4(LONG_NAME_THAT_DOES_NOT_CLASH)
}}; }};
} }

View file

@ -71,6 +71,7 @@ pub struct MgbaBufferedLogger {
pub message_level: MgbaMessageLevel, pub message_level: MgbaMessageLevel,
} }
impl MgbaBufferedLogger { impl MgbaBufferedLogger {
#[inline]
pub fn try_new(message_level: MgbaMessageLevel) -> Result<Self, ()> { pub fn try_new(message_level: MgbaMessageLevel) -> Result<Self, ()> {
if mgba_logging_available() { if mgba_logging_available() {
Ok(Self { byte_count: 0, message_level }) Ok(Self { byte_count: 0, message_level })
@ -84,6 +85,7 @@ impl MgbaBufferedLogger {
} }
} }
impl Drop for MgbaBufferedLogger { impl Drop for MgbaBufferedLogger {
#[inline]
fn drop(&mut self) { fn drop(&mut self) {
if self.byte_count != 0 { if self.byte_count != 0 {
self.flush(); self.flush();
@ -91,6 +93,7 @@ impl Drop for MgbaBufferedLogger {
} }
} }
impl core::fmt::Write for MgbaBufferedLogger { impl core::fmt::Write for MgbaBufferedLogger {
#[inline]
fn write_str(&mut self, s: &str) -> core::fmt::Result { fn write_str(&mut self, s: &str) -> core::fmt::Result {
for b in s.as_bytes().iter().copied() { for b in s.as_bytes().iter().copied() {
if b == b'\n' { if b == b'\n' {

View file

@ -35,9 +35,10 @@ use crate::{
}, },
dma::DmaControl, dma::DmaControl,
sound::{ sound::{
SweepControl, TonePattern, ToneFrequency, WaveBank, WaveLenVolume, WaveFrequency, NoiseLenEnvelope, NoiseFrequency, LeftRightVolume, SoundMix, SoundEnable, SoundBias SweepControl, TonePattern, ToneFrequency, WaveBank, WaveLenVolume, WaveFrequency,
NoiseLenEnvelope, NoiseFrequency, LeftRightVolume, SoundMix, SoundEnable, SoundBias
}, },
timers::TimerControl, keys::{KeyInput, KeyControl}, mgba::MgbaMessageLevel, fixed::{i16fx8, i32fx8}, timers::TimerControl, keys::{KeyInput, KeyControl}, mgba::MgbaMessageLevel, fixed::{i16fx8, i32fx8}, prelude::ObjAttr,
}; };
// Note(Lokathor): This macro lets us stick each address at the start of the // Note(Lokathor): This macro lets us stick each address at the start of the
@ -214,14 +215,14 @@ def_mmio!(0x0500_0200 = OBJ_PALETTE: VolBlock<Color, Safe, Safe, 256>; "Object t
// Video RAM (VRAM) // Video RAM (VRAM)
/// The VRAM offset per screenblock index. /// The VRAM byte offset per screenblock index.
/// ///
/// This is the same for all background types and sizes. /// This is the same for all background types and sizes.
pub const SCREENBLOCK_INDEX_OFFSET: usize = 2 * 1_024; pub const SCREENBLOCK_INDEX_OFFSET: usize = 2 * 1_024;
/// The size of the background tile region of VRAM. /// The size of the background tile region of VRAM.
/// ///
/// Background tile index use can cross between charblocks, but not past the end /// Background tile index use will work between charblocks, but not past the end
/// of BG tile memory into OBJ tile memory. /// of BG tile memory into OBJ tile memory.
pub const BG_TILE_REGION_SIZE: usize = 64 * 1_024; pub const BG_TILE_REGION_SIZE: usize = 64 * 1_024;
@ -475,6 +476,8 @@ def_mmio!(0x0700_0000 = OBJ_ATTR0: VolSeries<ObjAttr0, Safe, Safe, 128, {size_of
def_mmio!(0x0700_0002 = OBJ_ATTR1: VolSeries<ObjAttr1, Safe, Safe, 128, {size_of::<[u16;4]>()}>; "Object attributes 1."); def_mmio!(0x0700_0002 = OBJ_ATTR1: VolSeries<ObjAttr1, Safe, Safe, 128, {size_of::<[u16;4]>()}>; "Object attributes 1.");
def_mmio!(0x0700_0004 = OBJ_ATTR2: VolSeries<ObjAttr2, Safe, Safe, 128, {size_of::<[u16;4]>()}>; "Object attributes 2."); def_mmio!(0x0700_0004 = OBJ_ATTR2: VolSeries<ObjAttr2, Safe, Safe, 128, {size_of::<[u16;4]>()}>; "Object attributes 2.");
def_mmio!(0x0700_0000 = OBJ_ATTR_ALL: VolSeries<ObjAttr, Safe, Safe, 128, {size_of::<[u16;4]>()}>; "Object attributes (all in one).");
def_mmio!(0x0700_0006 = AFFINE_PARAM_A: VolSeries<i16fx8, Safe, Safe, 32, {size_of::<[u16;16]>()}>; "Affine parameters A."); def_mmio!(0x0700_0006 = AFFINE_PARAM_A: VolSeries<i16fx8, Safe, Safe, 32, {size_of::<[u16;16]>()}>; "Affine parameters A.");
def_mmio!(0x0700_000E = AFFINE_PARAM_B: VolSeries<i16fx8, Safe, Safe, 32, {size_of::<[u16;16]>()}>; "Affine parameters B."); def_mmio!(0x0700_000E = AFFINE_PARAM_B: VolSeries<i16fx8, Safe, Safe, 32, {size_of::<[u16;16]>()}>; "Affine parameters B.");
def_mmio!(0x0700_0016 = AFFINE_PARAM_C: VolSeries<i16fx8, Safe, Safe, 32, {size_of::<[u16;16]>()}>; "Affine parameters C."); def_mmio!(0x0700_0016 = AFFINE_PARAM_C: VolSeries<i16fx8, Safe, Safe, 32, {size_of::<[u16;16]>()}>; "Affine parameters C.");

View file

@ -107,7 +107,17 @@ use crate::prelude::*;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)] #[repr(transparent)]
pub struct Color(pub u16); pub struct Color(pub u16);
#[allow(clippy::unusual_byte_groupings)]
impl Color { impl Color {
pub const BLACK: Color = Color(0b0_00000_00000_00000);
pub const RED: Color = Color(0b0_00000_00000_11111);
pub const GREEN: Color = Color(0b0_00000_11111_00000);
pub const YELLOW: Color = Color(0b0_00000_11111_11111);
pub const BLUE: Color = Color(0b0_11111_00000_00000);
pub const MAGENTA: Color = Color(0b0_11111_00000_11111);
pub const CYAN: Color = Color(0b0_11111_11111_00000);
pub const WHITE: Color = Color(0b0_11111_11111_11111);
pub_const_fn_new_zeroed!(); pub_const_fn_new_zeroed!();
u16_int_field!(0 - 4, red, with_red); u16_int_field!(0 - 4, red, with_red);
u16_int_field!(5 - 9, green, with_green); u16_int_field!(5 - 9, green, with_green);
@ -358,3 +368,22 @@ impl ObjAttr2 {
u16_int_field!(10 - 11, priority, with_priority); u16_int_field!(10 - 11, priority, with_priority);
u16_int_field!(12 - 15, palbank, with_palbank); u16_int_field!(12 - 15, palbank, with_palbank);
} }
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct ObjAttr(pub ObjAttr0, pub ObjAttr1, pub ObjAttr2);
impl ObjAttr {
#[inline]
pub const fn new() -> Self {
Self(ObjAttr0(0), ObjAttr1(0), ObjAttr2(0))
}
pub fn set_x(&mut self, x: u16) {
self.1 = self.1.with_x(x);
}
pub fn set_y(&mut self, y: u16) {
self.0 = self.0.with_y(y);
}
pub fn set_tile_id(&mut self, id: u16) {
self.2 = self.2.with_tile_id(id);
}
}