mirror of
https://github.com/italicsjenga/gba.git
synced 2024-12-23 10:51:30 +11:00
we can make a little cyan guy show up on the screen.
This commit is contained in:
parent
d412ee7779
commit
6ba28c5347
|
@ -1 +1 @@
|
|||
foo_
|
||||
foo
|
47
examples/game.rs
Normal file
47
examples/game.rs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ fn panic_handler(info: &core::panic::PanicInfo) -> ! {
|
|||
}
|
||||
|
||||
#[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"]
|
||||
static FRAME_KEYS: GbaCell<KeyInput> = GbaCell::new(KeyInput::new());
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(isa_attribute)]
|
||||
|
||||
use gba::prelude::*;
|
||||
|
||||
|
|
|
@ -610,6 +610,11 @@ unsafe extern "C" fn __aeabi_uwrite8(value: u64, address: *mut c_void) {
|
|||
/// function when possible.
|
||||
///
|
||||
/// * **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]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn memcpy(
|
||||
|
@ -626,6 +631,10 @@ pub unsafe extern "C" fn memcpy(
|
|||
/// function when possible.
|
||||
///
|
||||
/// * **Returns:** the original `dest` pointer.
|
||||
///
|
||||
/// ## Safety
|
||||
/// * `src` must be readable for `byte_count` bytes.
|
||||
/// * `dest` must be writable for `byte_count` bytes.
|
||||
#[inline]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn memmove(
|
||||
|
@ -644,6 +653,9 @@ pub unsafe extern "C" fn memmove(
|
|||
/// it up like might happen in C.
|
||||
///
|
||||
/// * **Returns:** the original `dest` pointer.
|
||||
///
|
||||
/// ## Safety
|
||||
/// * `dest` must be writable for `byte_count` bytes.
|
||||
#[inline]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn memset(
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#![allow(non_snake_case)]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
|
||||
//! The GBA's BIOS provides limited built-in utility functions.
|
||||
//!
|
||||
|
|
|
@ -262,6 +262,7 @@ macro_rules! impl_signed_fixed_ops {
|
|||
}
|
||||
impl_trait_op_unit!($t, Neg, neg);
|
||||
impl<const B: u32> core::fmt::Debug for Fixed<$t, B> {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
let whole: $t = self.trunc().into_raw() >> B;
|
||||
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> {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
let whole: $t = self.trunc().into_raw() >> B;
|
||||
let fract: $t = self.fract().into_raw();
|
||||
|
|
|
@ -64,6 +64,7 @@ impl<T> Debug for GbaCell<T>
|
|||
where
|
||||
T: GbaCellSafe + Debug,
|
||||
{
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
<T as Debug>::fmt(&self.read(), f)
|
||||
}
|
||||
|
|
17
src/lib.rs
17
src/lib.rs
|
@ -1,8 +1,9 @@
|
|||
#![no_std]
|
||||
#![feature(asm_sym)]
|
||||
#![feature(asm_const)]
|
||||
#![feature(isa_attribute)]
|
||||
#![feature(naked_functions)]
|
||||
#![warn(clippy::missing_inline_in_public_items)]
|
||||
#![allow(clippy::let_and_return)]
|
||||
#![allow(clippy::result_unit_err)]
|
||||
//#![warn(missing_docs)]
|
||||
|
||||
//! A crate for GBA development.
|
||||
|
@ -72,9 +73,10 @@
|
|||
//!
|
||||
//! ## Other GBA-related Crates
|
||||
//!
|
||||
//! This crate provides a largely "unmanaged" interaction with the GBA's
|
||||
//! hardware. If you would like an API that use the borrow checker to guide you
|
||||
//! more, the [agb](https://docs.rs/agb) crate might be what you want.
|
||||
//! This crate provides an API to interact with the GBA that is safe, but with
|
||||
//! minimal restrictions on what components can be changed when. If you'd like
|
||||
//! 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
|
||||
//!
|
||||
|
@ -114,6 +116,7 @@ impl<const N: usize> Align4<[u8; N]> {
|
|||
/// Views these bytes as a slice of `u32`
|
||||
/// ## Panics
|
||||
/// * If the number of bytes isn't a multiple of 4
|
||||
#[inline]
|
||||
pub fn as_u32_slice(&self) -> &[u32] {
|
||||
assert!(self.0.len() % 4 == 0);
|
||||
// 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`
|
||||
/// ## Panics
|
||||
/// * If the number of bytes isn't a multiple of 2
|
||||
#[inline]
|
||||
pub fn as_u16_slice(&self) -> &[u16] {
|
||||
assert!(self.0.len() % 2 == 0);
|
||||
// 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_rules! include_aligned_bytes {
|
||||
($file:expr $(,)?) => {{
|
||||
let LONG_NAME_THAT_DOES_NOT_CLASH = *include_bytes!($file);
|
||||
Align4(LONG_NAME_THAT_DOES_NOT_CLASH)
|
||||
Align4(*include_bytes!($file))
|
||||
}};
|
||||
}
|
||||
|
|
|
@ -71,6 +71,7 @@ pub struct MgbaBufferedLogger {
|
|||
pub message_level: MgbaMessageLevel,
|
||||
}
|
||||
impl MgbaBufferedLogger {
|
||||
#[inline]
|
||||
pub fn try_new(message_level: MgbaMessageLevel) -> Result<Self, ()> {
|
||||
if mgba_logging_available() {
|
||||
Ok(Self { byte_count: 0, message_level })
|
||||
|
@ -84,6 +85,7 @@ impl MgbaBufferedLogger {
|
|||
}
|
||||
}
|
||||
impl Drop for MgbaBufferedLogger {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
if self.byte_count != 0 {
|
||||
self.flush();
|
||||
|
@ -91,6 +93,7 @@ impl Drop for MgbaBufferedLogger {
|
|||
}
|
||||
}
|
||||
impl core::fmt::Write for MgbaBufferedLogger {
|
||||
#[inline]
|
||||
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||
for b in s.as_bytes().iter().copied() {
|
||||
if b == b'\n' {
|
||||
|
|
11
src/mmio.rs
11
src/mmio.rs
|
@ -35,9 +35,10 @@ use crate::{
|
|||
},
|
||||
dma::DmaControl,
|
||||
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
|
||||
|
@ -214,14 +215,14 @@ def_mmio!(0x0500_0200 = OBJ_PALETTE: VolBlock<Color, Safe, Safe, 256>; "Object t
|
|||
|
||||
// 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.
|
||||
pub const SCREENBLOCK_INDEX_OFFSET: usize = 2 * 1_024;
|
||||
|
||||
/// 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.
|
||||
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_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_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.");
|
||||
|
|
|
@ -107,7 +107,17 @@ use crate::prelude::*;
|
|||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[repr(transparent)]
|
||||
pub struct Color(pub u16);
|
||||
#[allow(clippy::unusual_byte_groupings)]
|
||||
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!();
|
||||
u16_int_field!(0 - 4, red, with_red);
|
||||
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!(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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue