Merge pull request #251 from gwilymk/let-doctests-run

Let doctests run
This commit is contained in:
Gwilym Kuiper 2022-07-12 15:18:49 +01:00 committed by GitHub
commit 241e8b369e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 289 additions and 43 deletions

View file

@ -4,7 +4,7 @@
use agb::fixnum::Num;
use agb::input::{Button, ButtonController, Tri};
use agb::sound::mixer::SoundChannel;
use agb::{include_wav, Gba, fixnum::num};
use agb::{fixnum::num, include_wav, Gba};
// Music - "Dead Code" by Josh Woodward, free download at http://joshwoodward.com
const DEAD_CODE: &[u8] = include_wav!("examples/JoshWoodward-DeadCode.wav");

BIN
agb/examples/sfx/jump.wav Normal file

Binary file not shown.

BIN
agb/examples/sfx/my_bgm.wav Normal file

Binary file not shown.

View file

@ -20,9 +20,38 @@ use crate::{
///
/// # Example
///
/// ```
/// let backdrop = InfiniteScrolledMap::new(
/// background.background(Priority::P2, RegularBackgroundSize::Background32x32),
/// ```rust,no_run
/// # #![no_std]
/// # #![no_main]
/// extern crate alloc;
///
/// use alloc::boxed::Box;
///
/// use agb::display::tiled::{
/// InfiniteScrolledMap,
/// TileSetting,
/// RegularBackgroundSize,
/// TileSet,
/// TileFormat,
/// };
/// use agb::display::Priority;
///
/// mod tilemap {
/// pub const BACKGROUND_MAP: &[u16] = &[ // Probably load this from a file
/// # 0, 1, 2];
/// pub const WIDTH: i32 = // set it to some width
/// # 12;
/// pub const MAP_TILES: &[u8] = &[ // probably load this from a file
/// # 0];
/// }
///
/// # fn foo(mut gba: agb::Gba) {
/// let (gfx, mut vram) = gba.display.video.tiled0();
///
/// let tileset = TileSet::new(&tilemap::MAP_TILES, TileFormat::FourBpp);
///
/// let mut backdrop = InfiniteScrolledMap::new(
/// gfx.background(Priority::P2, RegularBackgroundSize::Background32x32),
/// Box::new(|pos| {
/// (
/// &tileset,
@ -40,6 +69,7 @@ use crate::{
/// backdrop.set_pos(&mut vram, (3, 5).into());
/// backdrop.commit(&mut vram);
/// backdrop.show();
/// # }
/// ```
pub struct InfiniteScrolledMap<'a> {
map: MapLoan<'a, RegularMap>,
@ -83,11 +113,57 @@ impl<'a> InfiniteScrolledMap<'a> {
///
/// # Example
///
/// ```
/// background.init(&mut vram, start_position, || {
/// ```rust,no_run
/// # #![no_std]
/// # #![no_main]
/// # #![no_std]
/// # #![no_main]
/// # extern crate alloc;
/// #
/// # use alloc::boxed::Box;
/// #
/// # use agb::display::tiled::{
/// # InfiniteScrolledMap,
/// # TileSetting,
/// # RegularBackgroundSize,
/// # TileSet,
/// # TileFormat,
/// # };
/// # use agb::display::Priority;
/// #
/// # mod tilemap {
/// # pub const BACKGROUND_MAP: &[u16] = &[0, 1, 2];
/// # pub const WIDTH: i32 = 12;
/// # pub const MAP_TILES: &[u8] = &[0];
/// # }
/// #
/// # fn foo(mut gba: agb::Gba) {
/// # let (gfx, mut vram) = gba.display.video.tiled0();
/// #
/// # let tileset = TileSet::new(&tilemap::MAP_TILES, TileFormat::FourBpp);
/// #
/// # let mut backdrop = InfiniteScrolledMap::new(
/// # gfx.background(Priority::P2, RegularBackgroundSize::Background32x32),
/// # Box::new(|pos| {
/// # (
/// # &tileset,
/// # TileSetting::from_raw(
/// # *tilemap::BACKGROUND_MAP
/// # .get((pos.x + tilemap::WIDTH * pos.y) as usize)
/// # .unwrap_or(&0),
/// # ),
/// # )
/// # }),
/// # );
/// #
/// # let vblank = agb::interrupt::VBlank::get();
/// # let mut mixer = gba.mixer.mixer();
/// let start_position = agb::fixnum::Vector2D::new(10, 10);
/// backdrop.init(&mut vram, start_position, &mut || {
/// vblank.wait_for_vblank();
/// mixer.frame();
/// });
/// # }
/// ```
pub fn init(
&mut self,
@ -111,11 +187,58 @@ impl<'a> InfiniteScrolledMap<'a> {
///
/// # Example
///
/// ```
/// while background.init_partial(&mut vram, start_position) == PartialUpdateStatus::Continue {
/// ```rust,no_run
/// # #![no_std]
/// # #![no_main]
/// # #![no_std]
/// # #![no_main]
/// # extern crate alloc;
/// #
/// # use alloc::boxed::Box;
/// #
/// # use agb::display::tiled::{
/// # InfiniteScrolledMap,
/// # TileSetting,
/// # RegularBackgroundSize,
/// # TileSet,
/// # TileFormat,
/// # PartialUpdateStatus,
/// # };
/// # use agb::display::Priority;
/// #
/// # mod tilemap {
/// # pub const BACKGROUND_MAP: &[u16] = &[0, 1, 2];
/// # pub const WIDTH: i32 = 12;
/// # pub const MAP_TILES: &[u8] = &[0];
/// # }
/// #
/// # fn foo(mut gba: agb::Gba) {
/// # let (gfx, mut vram) = gba.display.video.tiled0();
/// #
/// # let tileset = TileSet::new(&tilemap::MAP_TILES, TileFormat::FourBpp);
/// #
/// # let mut backdrop = InfiniteScrolledMap::new(
/// # gfx.background(Priority::P2, RegularBackgroundSize::Background32x32),
/// # Box::new(|pos| {
/// # (
/// # &tileset,
/// # TileSetting::from_raw(
/// # *tilemap::BACKGROUND_MAP
/// # .get((pos.x + tilemap::WIDTH * pos.y) as usize)
/// # .unwrap_or(&0),
/// # ),
/// # )
/// # }),
/// # );
/// #
/// # let vblank = agb::interrupt::VBlank::get();
/// # let mut mixer = gba.mixer.mixer();
/// let start_position = agb::fixnum::Vector2D::new(10, 10);
/// while backdrop.init_partial(&mut vram, start_position) == PartialUpdateStatus::Continue {
/// vblank.wait_for_vblank();
/// mixer.frame();
/// }
/// # }
/// ```
pub fn init_partial(
&mut self,

View file

@ -235,10 +235,17 @@ fn interrupt_to_root(interrupt: Interrupt) -> &'static InterruptRoot {
///
/// # Examples
///
/// ```
/// let _a = add_interrupt_handler(Interrupt::VBlank, |_: &CriticalSection| {
/// println!("Woah there! There's been a vblank!");
/// ```rust,no_run
/// # #![no_std]
/// # #![no_main]
/// use bare_metal::CriticalSection;
///
/// # fn foo() {
/// # use agb::interrupt::{add_interrupt_handler, Interrupt};
/// let _a = add_interrupt_handler(Interrupt::VBlank, |_: CriticalSection| {
/// agb::println!("Woah there! There's been a vblank!");
/// });
/// # }
/// ```
pub fn add_interrupt_handler<'a>(
interrupt: Interrupt,

View file

@ -50,7 +50,7 @@
///
/// This will generate something along the lines of the following:
///
/// ```
/// ```rust,ignore
/// // module name comes from the name of the toml file, so `sprites` in this case because it is
/// // called `sprites.toml`
/// mod sprites {
@ -75,7 +75,7 @@
/// ```
///
/// In `src/main.rs`
/// ```
/// ```rust,ignore
/// mod gfx {
/// use agb::display::object::ObjectControl;
///
@ -105,7 +105,7 @@
/// ```
///
/// In `src/main.rs`:
/// ```
/// ```rust,ignore
/// mod gfx {
/// use agb::display::background::BackgroundDistributor;
///
@ -139,7 +139,7 @@ macro_rules! include_font {
/// Doing this will ensure that `agb` can correctly set up the environment to call your rust function on start up.
///
/// # Examples
/// ```
/// ```no_run,rust
/// #![no_std]
/// #![no_main]
///
@ -206,7 +206,7 @@ static mut GBASINGLE: single::Singleton<Gba> = single::Singleton::new(unsafe { G
///
/// # Examples
///
/// ```
/// ```no_run,rust
/// #![no_std]
/// #![no_main]
///

View file

@ -35,9 +35,13 @@
//! To create a sound mixer, you will need to get it out of the [`Gba`](crate::Gba) struct
//! as follows:
//!
//! ```
//! ```rust,no_run
//! # #![no_std]
//! # #![no_main]
//! # fn foo(gba: &mut agb::Gba) {
//! let mut mixer = gba.mixer.mixer();
//! mixer.enable();
//! # }
//! ```
//!
//! ## Doing the per-frame work
@ -48,22 +52,34 @@
//!
//! Without interrupts:
//!
//! ```
//! ```rust,no_run
//! # #![no_std]
//! # #![no_main]
//! # fn foo(gba: &mut agb::Gba) {
//! # let mut mixer = gba.mixer.mixer();
//! # let vblank = agb::interrupt::VBlank::get();
//! // Somewhere in your main loop:
//! mixer.frame();
//! vblank.wait_for_vblank();
//! mixer.after_vblank();
//! # }
//! ```
//!
//! Or with interrupts:
//!
//! ```
//! ```rust,no_run
//! # #![no_std]
//! # #![no_main]
//! # fn foo(gba: &mut agb::Gba) {
//! # let mut mixer = gba.mixer.mixer();
//! # let vblank = agb::interrupt::VBlank::get();
//! // outside your main loop, close to initialisation
//! let _mixer_interrupt = mixer.setup_interrupt_handler();
//!
//! // inside your main loop
//! mixer.frame();
//! vblank.wait_for_vblank();
//! # }
//! ```
//!
//! Despite being high performance, the mixer still takes a sizable portion of CPU time (6-10%
@ -79,14 +95,21 @@
//! Use the [`include_wav!`](crate::include_wav) macro in order to load the sound. This will produce
//! an error if your wav file is of the wrong frequency.
//!
//! ```
//! ```rust,no_run
//! # #![no_std]
//! # #![no_main]
//! # fn foo(gba: &mut agb::Gba) {
//! # let mut mixer = gba.mixer.mixer();
//! # let vblank = agb::interrupt::VBlank::get();
//! # use agb::{*, sound::mixer::*};
//! // Outside your main function in global scope:
//! const MY_CRAZY_SOUND: &[u8] = include_wav!("sfx/my_crazy_sound.wav");
//! const MY_CRAZY_SOUND: &[u8] = include_wav!("examples/sfx/jump.wav");
//!
//! // Then to play the sound:
//! let mut channel = SoundChannel::new(MY_CRAZY_SOUND);
//! channel.stereo();
//! let _ = mixer.play_sound(channel); // we don't mind if this sound doesn't actually play
//! # }
//! ```
//!
//! See the [`SoundChannel`] struct for more details on how you can configure the sounds to play.
@ -152,25 +175,39 @@ enum SoundPriority {
/// play regardless of whether you have lots of sound effects playing. You create a high
/// priority sound channel using [`new_high_priority`](SoundChannel::new_high_priority).
///
/// ```
/// ```rust,no_run
/// # #![no_std]
/// # #![no_main]
/// # use agb::sound::mixer::*;
/// # use agb::*;
/// // in global scope:
/// const MY_BGM: [u8] = include_wav!("sfx/my_bgm.wav");
/// const MY_BGM: &[u8] = include_wav!("examples/sfx/my_bgm.wav");
///
/// // somewhere in code
/// # fn foo(gba: &mut Gba) {
/// # let mut mixer = gba.mixer.mixer();
/// let mut bgm = SoundChannel::new_high_priority(MY_BGM);
/// bgm.stereo().should_loop();
/// let _ = mixer.play_sound(bgm);
/// # }
/// ```
///
/// ## Playing a sound effect
///
/// ```
/// ```rust,no_run
/// # #![no_std]
/// # #![no_main]
/// # use agb::sound::mixer::*;
/// # use agb::*;
/// // in global scope:
/// const JUMP_SOUND: [u8] = include_wav!("sfx/jump_sound.wav");
/// const JUMP_SOUND: &[u8] = include_wav!("examples/sfx/jump.wav");
///
/// // somewhere in code
/// let jump_sound = SoundChannel::new(MY_JUMP_SOUND);
/// # fn foo(gba: &mut Gba) {
/// # let mut mixer = gba.mixer.mixer();
/// let jump_sound = SoundChannel::new(JUMP_SOUND);
/// let _ = mixer.play_sound(jump_sound);
/// # }
/// ```
pub struct SoundChannel {
data: &'static [u8],
@ -198,13 +235,20 @@ impl SoundChannel {
///
/// # Example
///
/// ```
/// ```rust,no_run
/// # #![no_std]
/// # #![no_main]
/// # use agb::sound::mixer::*;
/// # use agb::*;
/// # fn foo(gba: &mut Gba) {
/// # let mut mixer = gba.mixer.mixer();
/// // in global scope:
/// const JUMP_SOUND: [u8] = include_wav!("sfx/jump_sound.wav");
/// const JUMP_SOUND: &[u8] = include_wav!("examples/sfx/jump.wav");
///
/// // somewhere in code
/// let jump_sound = SoundChannel::new(MY_JUMP_SOUND);
/// let jump_sound = SoundChannel::new(JUMP_SOUND);
/// let _ = mixer.play_sound(jump_sound);
/// # }
/// ```
#[inline(always)]
#[must_use]
@ -233,14 +277,21 @@ impl SoundChannel {
///
/// # Example
///
/// ```
/// ```rust,no_run
/// # #![no_std]
/// # #![no_main]
/// # use agb::sound::mixer::*;
/// # use agb::*;
/// # fn foo(gba: &mut Gba) {
/// # let mut mixer = gba.mixer.mixer();
/// // in global scope:
/// const MY_BGM: [u8] = include_wav!("sfx/my_bgm.wav");
/// const MY_BGM: &[u8] = include_wav!("examples/sfx/my_bgm.wav");
///
/// // somewhere in code
/// let mut bgm = SoundChannel::new_high_priority(MY_BGM);
/// bgm.stereo().should_loop();
/// let _ = mixer.play_sound(bgm);
/// # }
/// ```
#[inline(always)]
#[must_use]

View file

@ -36,15 +36,28 @@ extern "C" {
/// You should not create this struct directly, instead creating it through the [`Gba`](crate::Gba)
/// struct as follows:
///
/// ```
/// ```rust,no_run
/// # #![no_std]
/// # #![no_main]
/// # use agb::sound::mixer::*;
/// # use agb::*;
/// # fn foo(gba: &mut Gba) {
/// let mut mixer = gba.mixer.mixer();
/// # }
/// ```
///
/// # Example
///
/// ```
/// ```rust,no_run
/// # #![no_std]
/// # #![no_main]
/// # use agb::sound::mixer::*;
/// # use agb::*;
/// # fn foo(gba: &mut Gba) {
/// # let mut mixer = gba.mixer.mixer();
/// # let vblank = agb::interrupt::VBlank::get();
/// // Outside your main function in global scope:
/// const MY_CRAZY_SOUND: &[u8] = include_wav!("sfx/my_crazy_sound.wav");
/// const MY_CRAZY_SOUND: &[u8] = include_wav!("examples/sfx/jump.wav");
///
/// // in your main function:
/// let mut mixer = gba.mixer.mixer();
@ -57,6 +70,7 @@ extern "C" {
/// vblank.wait_for_vblank();
/// mixer.after_vblank();
/// }
/// # }
/// ```
pub struct Mixer {
buffer: MixerBuffer,
@ -72,12 +86,20 @@ pub struct Mixer {
///
/// # Example
///
/// ```
/// ```rust,no_run
/// # #![no_std]
/// # #![no_main]
/// # use agb::sound::mixer::*;
/// # use agb::*;
/// # fn foo(gba: &mut Gba) {
/// # let mut mixer = gba.mixer.mixer();
/// # const MY_BGM: &[u8] = include_wav!("examples/sfx/my_bgm.wav");
/// let mut channel = SoundChannel::new_high_priority(MY_BGM);
/// let bgm_channel_id = mixer.play_sound(channel).unwrap(); // will always be Some if high priority
///
/// // Later, stop that particular channel
/// mixer.channel(bgm_channel_id).stop();
/// mixer.channel(&bgm_channel_id).expect("Expected to still be playing").stop();
/// # }
/// ```
pub struct ChannelId(usize, i32);
@ -106,12 +128,20 @@ impl Mixer {
///
/// # Example
///
/// ```
/// ```rust,no_run
/// # #![no_std]
/// # #![no_main]
/// # use agb::sound::mixer::*;
/// # use agb::*;
/// # fn foo(gba: &mut Gba) {
/// # let mut mixer = gba.mixer.mixer();
/// # let vblank = agb::interrupt::VBlank::get();
/// loop {
/// mixer.frame();
/// vblank.wait_for_vblank();
/// mixer.after_vblank();
/// }
/// # }
/// ```
#[cfg(not(feature = "freq32768"))]
pub fn after_vblank(&mut self) {
@ -127,7 +157,14 @@ impl Mixer {
///
/// # Example
///
/// ```
/// ```rust,no_run
/// # #![no_std]
/// # #![no_main]
/// # use agb::sound::mixer::*;
/// # use agb::*;
/// # fn foo(gba: &mut Gba) {
/// # let mut mixer = gba.mixer.mixer();
/// # let vblank = agb::interrupt::VBlank::get();
/// // you must set this to a named variable to ensure that the scope is long enough
/// let _mixer_interrupt = mixer.setup_interrupt_handler();
///
@ -135,6 +172,7 @@ impl Mixer {
/// mixer.frame();
/// vblank.wait_for_vblank();
/// }
/// # }
/// ```
pub fn setup_interrupt_handler(&self) -> InterruptHandler<'_> {
let mut timer1 = unsafe { Timer::new(1) };
@ -157,12 +195,20 @@ impl Mixer {
///
/// # Example
///
/// ```
/// ```rust,no_run
/// # #![no_std]
/// # #![no_main]
/// # use agb::sound::mixer::*;
/// # use agb::*;
/// # fn foo(gba: &mut Gba) {
/// # let mut mixer = gba.mixer.mixer();
/// # let vblank = agb::interrupt::VBlank::get();
/// loop {
/// mixer.frame();
/// vblank.wait_for_vblank();
/// mixer.after_vblank(); // optional, only if not using interrupts
/// }
/// # }
/// ```
pub fn frame(&mut self) {
if !self.buffer.should_calculate() {
@ -188,9 +234,17 @@ impl Mixer {
///
/// # Example
///
/// ```
/// ```rust,no_run
/// # #![no_std]
/// # #![no_main]
/// # use agb::sound::mixer::*;
/// # use agb::*;
/// # fn foo(gba: &mut Gba) {
/// # let mut mixer = gba.mixer.mixer();
/// # const MY_BGM: &[u8] = include_wav!("examples/sfx/my_bgm.wav");
/// let mut channel = SoundChannel::new_high_priority(MY_BGM);
/// let bgm_channel_id = mixer.play_sound(channel).unwrap(); // will always be Some if high priority
/// # }
/// ```
pub fn play_sound(&mut self, new_channel: SoundChannel) -> Option<ChannelId> {
for (i, channel) in self.channels.iter_mut().enumerate() {
@ -229,12 +283,20 @@ impl Mixer {
///
/// # Example
///
/// ```
/// ```rust,no_run
/// # #![no_std]
/// # #![no_main]
/// # use agb::sound::mixer::*;
/// # use agb::*;
/// # fn foo(gba: &mut Gba) {
/// # let mut mixer = gba.mixer.mixer();
/// # const MY_BGM: &[u8] = include_wav!("examples/sfx/my_bgm.wav");
/// let mut channel = SoundChannel::new_high_priority(MY_BGM);
/// let bgm_channel_id = mixer.play_sound(channel).unwrap(); // will always be Some if high priority
///
/// // Later, stop that particular channel
/// mixer.channel(bgm_channel_id).stop();
/// mixer.channel(&bgm_channel_id).expect("Expected still to be playing").stop();
/// # }
/// ```
pub fn channel(&mut self, id: &ChannelId) -> Option<&'_ mut SoundChannel> {
if let Some(channel) = &mut self.channels[id.0] {

View file

@ -17,6 +17,9 @@ test:
test-release:
just _test-release agb
doctest-agb:
(cd agb && cargo test --doc -Z doctest-xcompile)
clean:
just _all-crates _clean
@ -34,7 +37,7 @@ run-game game:
run-game-debug game:
(cd "examples/{{game}}" && cargo run)
ci: build-debug clippy test build-release test-release build-roms build-book
ci: build-debug clippy test build-release test-release doctest-agb build-roms build-book
build-roms:
just _build-rom "examples/the-purple-night" "PURPLENIGHT"