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)]
|
#[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());
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![feature(isa_attribute)]
|
|
||||||
|
|
||||||
use gba::prelude::*;
|
use gba::prelude::*;
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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.
|
||||||
//!
|
//!
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
17
src/lib.rs
17
src/lib.rs
|
@ -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)
|
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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' {
|
||||||
|
|
11
src/mmio.rs
11
src/mmio.rs
|
@ -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.");
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue