Merge pull request #160 from gwilymk/make-agb-entry-create-gba-struct

Make agb entry create gba struct
This commit is contained in:
Gwilym Kuiper 2022-01-17 19:47:44 +00:00 committed by GitHub
commit a9e728a037
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 82 additions and 81 deletions

View file

@ -2,9 +2,9 @@
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use quote::{quote, ToTokens};
use rand::Rng;
use syn::{Ident, ItemFn, ReturnType, Type, Visibility};
use syn::{FnArg, Ident, ItemFn, Pat, ReturnType, Token, Type, Visibility};
#[proc_macro_attribute]
pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
@ -15,16 +15,43 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
f.sig.constness.is_none()
&& f.vis == Visibility::Inherited
&& f.sig.abi.is_none()
&& f.sig.inputs.is_empty()
&& f.sig.generics.params.is_empty()
&& f.sig.generics.where_clause.is_none()
&& match f.sig.output {
ReturnType::Type(_, ref ty) => matches!(**ty, Type::Never(_)),
_ => false,
},
"#[agb::entry] must have signature [unsafe] fn () -> !"
"#[agb::entry] must have signature [unsafe] fn (mut agb::Gba) -> !"
);
// Check that the function signature takes 1 argument, agb::Gba
let arguments: Vec<_> = f.sig.inputs.iter().collect();
assert_eq!(
arguments.len(),
1,
"#[agb::entry] must have signature [unsafe] fn (mut agb::Gba) -> !, but got {} arguments",
arguments.len(),
);
let (argument_type, (argument_name, is_mutable)) = match arguments[0] {
FnArg::Typed(pat_type) => (
pat_type.ty.to_token_stream(),
match &*pat_type.pat {
Pat::Ident(ident) => {
assert!(
ident.attrs.is_empty() && ident.by_ref.is_none() && ident.subpat.is_none(),
"#[agb::entry] must have signature [unsafe] fn (mut agb::Gba) -> !"
);
(ident.ident.clone(), ident.mutability.is_some())
}
_ => panic!("Expected first argument to #[agb::entry] to be a basic identifier"),
},
),
_ => panic!("Expected first argument to #[agb::entry] to not be self"),
};
assert!(
args.to_string() == "",
"Must pass no args to #[agb::entry] macro"
@ -35,10 +62,23 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
let attrs = f.attrs;
let stmts = f.block.stmts;
let mutable = if is_mutable {
Some(Token![mut](Span::call_site()))
} else {
None
};
assert!(
argument_type.to_string().ends_with("Gba"),
"Expected first argument to have type 'Gba'"
);
quote!(
#[export_name = "main"]
#(#attrs)*
pub fn #fn_name() -> ! {
let #mutable #argument_name = unsafe { #argument_type ::new_in_entry() };
#(#stmts)*
}
)

View file

@ -6,7 +6,7 @@ extern crate alloc;
use alloc::boxed::Box;
#[agb::entry]
fn main() -> ! {
fn main(_gba: agb::Gba) -> ! {
loop {
let b = Box::new(1);
agb::println!("dynamic allocation made to {:?}", &*b as *const _);

View file

@ -4,9 +4,7 @@
use agb::sound;
#[agb::entry]
fn main() -> ! {
let gba = agb::Gba::new();
fn main(gba: agb::Gba) -> ! {
gba.sound.enable();
let sweep_settings = sound::dmg::SweepSettings::default();

View file

@ -9,8 +9,7 @@ struct Vector2D {
}
#[agb::entry]
fn main() -> ! {
let mut gba = agb::Gba::new();
fn main(mut gba: agb::Gba) -> ! {
let mut bitmap = gba.display.video.bitmap3();
let vblank = agb::interrupt::VBlank::get();

View file

@ -4,8 +4,7 @@
use agb::display;
#[agb::entry]
fn main() -> ! {
let mut gba = agb::Gba::new();
fn main(mut gba: agb::Gba) -> ! {
let mut bitmap = gba.display.video.bitmap4();
let vblank = agb::interrupt::VBlank::get();

View file

@ -35,14 +35,13 @@ fn frame_ranger(count: u32, start: u32, end: u32, delay: u32) -> u16 {
}
#[agb::entry]
fn main() -> ! {
fn main(mut gba: agb::Gba) -> ! {
let map_as_grid: &[[u16; 32]; 32] = unsafe {
(&MAP_MAP as *const [u16; 1024] as *const [[u16; 32]; 32])
.as_ref()
.unwrap()
};
let mut gba = agb::Gba::new();
let mut gfx = gba.display.video.tiled0();
let vblank = agb::interrupt::VBlank::get();
let mut input = agb::input::ButtonController::new();

View file

@ -10,8 +10,7 @@ use agb::{include_wav, Gba};
const DEAD_CODE: &[u8] = include_wav!("examples/JoshWoodward-DeadCode.wav");
#[agb::entry]
fn main() -> ! {
let mut gba = Gba::new();
fn main(mut gba: Gba) -> ! {
let mut input = ButtonController::new();
let vblank_provider = agb::interrupt::VBlank::get();

View file

@ -9,8 +9,7 @@ struct Vector2D {
}
#[agb::entry]
fn main() -> ! {
let mut gba = agb::Gba::new();
fn main(mut gba: agb::Gba) -> ! {
let vblank = agb::interrupt::VBlank::get();
let mut input = agb::input::ButtonController::new();

View file

@ -2,7 +2,7 @@
#![no_main]
#[agb::entry]
fn main() -> ! {
fn main(_gba: agb::Gba) -> ! {
let count = agb::interrupt::Mutex::new(0);
agb::add_interrupt_handler!(agb::interrupt::Interrupt::VBlank, |key| {
let mut count = count.lock_with_key(&key);

View file

@ -4,9 +4,7 @@
use agb::display;
#[agb::entry]
fn main() -> ! {
let mut gba = agb::Gba::new();
fn main(mut gba: agb::Gba) -> ! {
let mut bitmap = gba.display.video.bitmap3();
let mut input = agb::input::ButtonController::new();

View file

@ -8,8 +8,7 @@ use agb::{include_wav, Gba};
const LET_IT_IN: &[u8] = include_wav!("examples/JoshWoodward-LetItIn.wav");
#[agb::entry]
fn main() -> ! {
let mut gba = Gba::new();
fn main(mut gba: Gba) -> ! {
let vblank_provider = agb::interrupt::VBlank::get();
let mut timer_controller = gba.timers.timers();

View file

@ -4,8 +4,7 @@
use agb::{display, syscall};
#[agb::entry]
fn main() -> ! {
let mut gba = agb::Gba::new();
fn main(mut gba: agb::Gba) -> ! {
let mut bitmap = gba.display.video.bitmap3();
for x in 0..display::WIDTH {

View file

@ -4,8 +4,7 @@
use agb::display::example_logo;
#[agb::entry]
fn main() -> ! {
let mut gba = agb::Gba::new();
fn main(mut gba: agb::Gba) -> ! {
let mut gfx = gba.display.video.tiled0();
example_logo::display_logo(&mut gfx);

View file

@ -13,8 +13,7 @@ struct BackCosines {
}
#[agb::entry]
fn main() -> ! {
let mut gba = agb::Gba::new();
fn main(mut gba: agb::Gba) -> ! {
let mut gfx = gba.display.video.tiled0();
example_logo::display_logo(&mut gfx);

View file

@ -114,7 +114,7 @@ pub use agb_image_converter::include_gfx;
/// This macro declares the entry point to your game written using `agb`.
///
/// It is already included in the template, but your `main` function must be annotated with `#[agb::entry]`, take no arguments and never return.
/// It is already included in the template, but your `main` function must be annotated with `#[agb::entry]`, takes 1 argument and never returns.
/// Doing this will ensure that `agb` can correctly set up the environment to call your rust function on start up.
///
/// # Examples
@ -125,9 +125,7 @@ pub use agb_image_converter::include_gfx;
/// use agb::Gba;
///
/// #[agb::entry]
/// fn main() -> ! {
/// let mut gba = Gba::new();
///
/// fn main(mut gba: Gba) -> ! {
/// loop {}
/// }
/// ```
@ -180,13 +178,7 @@ static mut GBASINGLE: single::Singleton<Gba> = single::Singleton::new(unsafe { G
/// The Gba struct is used to control access to the Game Boy Advance's hardware in a way which makes it the
/// borrow checker's responsibility to ensure no clashes of global resources.
///
/// This is typically created once at the start of the main function and then the various fields are used
/// to ensure mutually exclusive use of the various hardware registers. It provides a gateway into the main
/// usage of `agb` library.
///
/// # Panics
///
/// Calling this twice will panic.
/// This is will be created for you via the #[agb::entry] attribute.
///
/// # Examples
///
@ -197,9 +189,7 @@ static mut GBASINGLE: single::Singleton<Gba> = single::Singleton::new(unsafe { G
/// use agb::Gba;
///
/// #[agb::entry]
/// fn main() -> ! {
/// let mut gba = Gba::new();
///
/// fn main(mut gba: Gba) -> !
/// // Do whatever you need to do with gba
///
/// loop {}
@ -219,15 +209,9 @@ pub struct Gba {
}
impl Gba {
/// Creates a new instance of the Gba struct.
///
/// Note that you can only create 1 instance, and trying to create a second will panic.
///
/// # Panics
///
/// Panics if you try to create the second instance.
pub fn new() -> Self {
unsafe { GBASINGLE.take() }
#[doc(hidden)]
pub unsafe fn new_in_entry() -> Self {
GBASINGLE.take()
}
const unsafe fn single_new() -> Self {
@ -240,12 +224,6 @@ impl Gba {
}
}
impl Default for Gba {
fn default() -> Self {
Self::new()
}
}
#[doc(hidden)]
pub trait Testable {
fn run(&self, gba: &mut Gba);
@ -283,7 +261,11 @@ fn panic_implementation(info: &core::panic::PanicInfo) -> ! {
loop {}
}
#[cfg(test)]
static mut TEST_GBA: Option<Gba> = None;
#[doc(hidden)]
#[cfg(test)]
pub fn test_runner(tests: &[&dyn Testable]) {
let mut mgba = mgba::Mgba::new().unwrap();
mgba.print(
@ -292,7 +274,7 @@ pub fn test_runner(tests: &[&dyn Testable]) {
)
.unwrap();
let mut gba = Gba::new();
let mut gba = unsafe { TEST_GBA.as_mut() }.unwrap();
for test in tests {
test.run(&mut gba);
@ -307,7 +289,8 @@ pub fn test_runner(tests: &[&dyn Testable]) {
#[cfg(test)]
#[entry]
fn agb_test_main() -> ! {
fn agb_test_main(gba: Gba) -> ! {
unsafe { TEST_GBA = Some(gba) };
test_main();
#[allow(clippy::empty_loop)]
loop {}

View file

@ -33,9 +33,7 @@ mod gfx {
// ensures that everything is in order. `agb` will call this after setting up the stack
// and interrupt handlers correctly.
#[agb::entry]
fn main() -> ! {
let mut gba = Gba::new();
fn main(mut gba: Gba) -> ! {
let _tiled = gba.display.video.tiled0();
let mut object = gba.display.object.get();
gfx::load_sprite_data(&mut object);

View file

@ -1,15 +1,15 @@
# The Gba struct
In this section, we'll cover the importance of the Gba struct and how to create it.
In this section, we'll cover the importance of the Gba struct and how it gets created for you.
# The importance of the Gba struct
Almost all interaction with the Game Boy Advance's hardware goes through the [Gba singleton struct](https://docs.rs/agb/latest/agb/struct.Gba.html).
Your games using `agb` will typically create this in the `main` function and then handle the abstractions in there.
You should not create the Gba struct yourself, instead having it be passed into your main function.
The Gba struct is used to take advantage of rust's borrow checker, and lean on it to ensure that access to the Game Boy Advance hardware is done 'sensibly'.
You won't have to worry about 2 bits of your code modifying data in the wrong way!
This struct is a 'singleton', so you cannot create 2 instances of it at once.
This struct is a 'singleton', so you cannot create another instance of it.
Attempting to do so will result in a panic which by default crashes the game.
# How all agb games start
@ -21,8 +21,6 @@ Replace the content of the `main` function with the following:
# #![no_main]
# #[agb::entry]
# fn main() -> ! {
let mut gba = agb::Gba::new();
loop {} // infinite loop for now
# }
```
@ -36,6 +34,6 @@ Although there isn't much to see at the moment (just a black screen), you can st
# What we did
This was a very simple but incredibly important part of any game using `agb`.
All interactions with the hardware are gated via the Gba struct, so it must be created at the start of your `main` function and never again.
All interactions with the hardware are gated via the Gba struct, which you never create yourself.
You are now ready to learn about display modes and how to start getting things onto the screen!

View file

@ -772,10 +772,8 @@ impl<'a, 'b, 'c> PlayingLevel<'a, 'b> {
}
}
#[no_mangle]
pub fn main() -> ! {
let mut agb = agb::Gba::new();
#[agb::entry]
fn main(mut agb: agb::Gba) -> ! {
splash_screen::show_splash_screen(&mut agb, splash_screen::SplashScreen::Start, None, None);
loop {

View file

@ -2207,9 +2207,7 @@ mod tilemap {
}
#[agb::entry]
fn main() -> ! {
let mut gba = agb::Gba::new();
fn main(mut gba: agb::Gba) -> ! {
loop {
game_with_level(&mut gba);
}

View file

@ -12,12 +12,11 @@
use agb::{display, syscall};
// The main function must take 0 arguments and never return. The agb::entry decorator
// The main function must take 1 arguments and never return. The agb::entry decorator
// ensures that everything is in order. `agb` will call this after setting up the stack
// and interrupt handlers correctly.
// and interrupt handlers correctly. It will also handle creating the `Gba` struct for you.
#[agb::entry]
fn main() -> ! {
let mut gba = agb::Gba::new();
fn main(mut gba: agb::Gba) -> ! {
let mut bitmap = gba.display.video.bitmap3();
for x in 0..display::WIDTH {