mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-23 16:21:33 +11:00
Merge pull request #329 from corwinkuiper/fix-docs-rs
Fix docs.rs build of agbrs
This commit is contained in:
commit
336925fe45
|
@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
This is a minor release to fix the build of the docs on [docs.rs/agb](https://docs.rs/agb).
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fixed the agb crate's docs.rs build
|
||||||
|
|
||||||
## [0.12.0] - 2022/10/11
|
## [0.12.0] - 2022/10/11
|
||||||
|
|
||||||
This version of `agb` has some exciting new features we'd like to highlight and some brand new contributors!
|
This version of `agb` has some exciting new features we'd like to highlight and some brand new contributors!
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
#![deny(clippy::needless_pass_by_value)]
|
#![deny(clippy::needless_pass_by_value)]
|
||||||
#![deny(clippy::redundant_closure_for_method_calls)]
|
#![deny(clippy::redundant_closure_for_method_calls)]
|
||||||
#![deny(clippy::cloned_instead_of_copied)]
|
#![deny(clippy::cloned_instead_of_copied)]
|
||||||
|
#![deny(rustdoc::broken_intra_doc_links)]
|
||||||
|
#![deny(rustdoc::private_intra_doc_links)]
|
||||||
|
|
||||||
//! # agb
|
//! # agb
|
||||||
//! `agb` is a library for making games on the Game Boy Advance using the Rust
|
//! `agb` is a library for making games on the Game Boy Advance using the Rust
|
||||||
|
|
|
@ -5,17 +5,18 @@
|
||||||
//! There are, broadly speaking, three different kinds of save media that can be
|
//! There are, broadly speaking, three different kinds of save media that can be
|
||||||
//! found in official Game Carts:
|
//! found in official Game Carts:
|
||||||
//!
|
//!
|
||||||
//! * Battery-Backed SRAM: The simplest kind of save media, which can be accessed
|
//! * Battery-Backed SRAM: The simplest kind of save media, which can be
|
||||||
//! like normal memory. You can have SRAM up to 32KiB, and while there exist a
|
//! accessed like normal memory. You can have SRAM up to 32KiB, and while
|
||||||
//! few variants this does not matter much for a game developer.
|
//! there exist a few variants this does not matter much for a game developer.
|
||||||
//! * EEPROM: A kind of save media based on very cheap chips and slow chips.
|
//! * EEPROM: A kind of save media based on very cheap chips and slow chips.
|
||||||
//! These are accessed using a serial interface based on reading/writing bit
|
//! These are accessed using a serial interface based on reading/writing bit
|
||||||
//! streams into IO registers. This memory comes in 8KiB and 512 byte versions,
|
//! streams into IO registers. This memory comes in 8KiB and 512 byte
|
||||||
//! which unfortunately cannot be distinguished at runtime.
|
//! versions, which unfortunately cannot be distinguished at runtime.
|
||||||
//! * Flash: A kind of save media based on flash memory. Flash memory can be read
|
//! * Flash: A kind of save media based on flash memory. Flash memory can be
|
||||||
//! like ordinary memory, but writing requires sending commands using multiple
|
//! read like ordinary memory, but writing requires sending commands using
|
||||||
//! IO register spread across the address space. This memory comes in 64KiB
|
//! multiple IO register spread across the address space. This memory comes in
|
||||||
//! and 128KiB variants, which can thankfully be distinguished using a chip ID.
|
//! 64KiB and 128KiB variants, which can thankfully be distinguished using a
|
||||||
|
//! chip ID.
|
||||||
//!
|
//!
|
||||||
//! As these various types of save media cannot be easily distinguished at
|
//! As these various types of save media cannot be easily distinguished at
|
||||||
//! runtime, the kind of media in use should be set manually.
|
//! runtime, the kind of media in use should be set manually.
|
||||||
|
@ -39,30 +40,32 @@
|
||||||
//!
|
//!
|
||||||
//! ## Using save media
|
//! ## Using save media
|
||||||
//!
|
//!
|
||||||
//! To access save media, use the [`SaveData::new`] method to create a new
|
//! To access save media, use the [`SaveManager::access`] or
|
||||||
//! [`SaveData`] object. Its methods are used to read or write save media.
|
//! [`SaveManager::access_with_timer`] methods to create a new [`SaveData`]
|
||||||
|
//! object. Its methods are used to read or write save media.
|
||||||
//!
|
//!
|
||||||
//! Reading data from the savegame is simple. Use [`read`] to copy data from an
|
//! Reading data from the savegame is simple. Use [`read`] to copy data from an
|
||||||
//! offset in the savegame into a buffer in memory.
|
//! offset in the savegame into a buffer in memory.
|
||||||
//!
|
//!
|
||||||
//! Writing to save media requires you to prepare the area for writing by calling
|
//! Writing to save media requires you to prepare the area for writing by
|
||||||
//! the [`prepare_write`] method to return a [`SavePreparedBlock`], which contains
|
//! calling the [`prepare_write`] method to return a [`SavePreparedBlock`],
|
||||||
//! the actual [`write`] method.
|
//! which contains the actual [`write`] method.
|
||||||
//!
|
//!
|
||||||
//! The `prepare_write` method leaves everything in a sector that overlaps the
|
//! The `prepare_write` method leaves everything in a sector that overlaps the
|
||||||
//! range passed to it in an implementation defined state. On some devices it may
|
//! range passed to it in an implementation defined state. On some devices it
|
||||||
//! do nothing, and on others, it may clear the entire range to `0xFF`.
|
//! may do nothing, and on others, it may clear the entire range to `0xFF`.
|
||||||
//!
|
//!
|
||||||
//! Because writes can only be prepared on a per-sector basis, a clear on a range
|
//! Because writes can only be prepared on a per-sector basis, a clear on a
|
||||||
//! of `4000..5000` on a device with 4096 byte sectors will actually clear a range
|
//! range of `4000..5000` on a device with 4096 byte sectors will actually clear
|
||||||
//! of `0..8192`. Use [`sector_size`] to find the sector size, or [`align_range`]
|
//! a range of `0..8192`. Use [`sector_size`] to find the sector size, or
|
||||||
//! to directly calculate the range of memory that will be affected by the clear.
|
//! [`align_range`] to directly calculate the range of memory that will be
|
||||||
|
//! affected by the clear.
|
||||||
//!
|
//!
|
||||||
//! [`read`]: SaveData::read
|
//! [`read`]: SaveData::read
|
||||||
//! [`prepare_write`]: SaveData::prepare_write
|
//! [`prepare_write`]: SaveData::prepare_write
|
||||||
//! [`write`]: SavePreparedBlock::write
|
//! [`write`]: SavePreparedBlock::write
|
||||||
//! [`sector_size`]: SaveAccess::sector_size
|
//! [`sector_size`]: SaveData::sector_size
|
||||||
//! [`align_range`]: SaveAccess::align_range
|
//! [`align_range`]: SaveData::align_range
|
||||||
//!
|
//!
|
||||||
//! ## Performance and Other Details
|
//! ## Performance and Other Details
|
||||||
//!
|
//!
|
||||||
|
@ -78,14 +81,14 @@
|
||||||
//! * Atmel flash chips have a sector size of 128 bytes. Reads to any alignment
|
//! * Atmel flash chips have a sector size of 128 bytes. Reads to any alignment
|
||||||
//! are efficient, however, unaligned writes are extremely slow.
|
//! are efficient, however, unaligned writes are extremely slow.
|
||||||
//! `prepare_write` does not immediately erase any data.
|
//! `prepare_write` does not immediately erase any data.
|
||||||
//! * EEPROM has a sector size of 8 bytes. Unaligned reads and writes are
|
//! * EEPROM has a sector size of 8 bytes. Unaligned reads and writes are slower
|
||||||
//! slower than aligned writes, however, this is easily mitigated by the
|
//! than aligned writes, however, this is easily mitigated by the small sector
|
||||||
//! small sector size.
|
//! size.
|
||||||
|
|
||||||
use core::ops::Range;
|
|
||||||
use crate::save::utils::Timeout;
|
use crate::save::utils::Timeout;
|
||||||
use crate::sync::{Mutex, RawMutexGuard};
|
use crate::sync::{Mutex, RawMutexGuard};
|
||||||
use crate::timer::Timer;
|
use crate::timer::Timer;
|
||||||
|
use core::ops::Range;
|
||||||
|
|
||||||
mod asm_utils;
|
mod asm_utils;
|
||||||
mod eeprom;
|
mod eeprom;
|
||||||
|
@ -167,7 +170,12 @@ trait RawSaveAccess: Sync {
|
||||||
fn info(&self) -> Result<&'static MediaInfo, Error>;
|
fn info(&self) -> Result<&'static MediaInfo, Error>;
|
||||||
fn read(&self, offset: usize, buffer: &mut [u8], timeout: &mut Timeout) -> Result<(), Error>;
|
fn read(&self, offset: usize, buffer: &mut [u8], timeout: &mut Timeout) -> Result<(), Error>;
|
||||||
fn verify(&self, offset: usize, buffer: &[u8], timeout: &mut Timeout) -> Result<bool, Error>;
|
fn verify(&self, offset: usize, buffer: &[u8], timeout: &mut Timeout) -> Result<bool, Error>;
|
||||||
fn prepare_write(&self, sector: usize, count: usize, timeout: &mut Timeout) -> Result<(), Error>;
|
fn prepare_write(
|
||||||
|
&self,
|
||||||
|
sector: usize,
|
||||||
|
count: usize,
|
||||||
|
timeout: &mut Timeout,
|
||||||
|
) -> Result<(), Error>;
|
||||||
fn write(&self, offset: usize, buffer: &[u8], timeout: &mut Timeout) -> Result<(), Error>;
|
fn write(&self, offset: usize, buffer: &[u8], timeout: &mut Timeout) -> Result<(), Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +183,10 @@ static CURRENT_SAVE_ACCESS: Mutex<Option<&'static dyn RawSaveAccess>> = Mutex::n
|
||||||
|
|
||||||
fn set_save_implementation(access_impl: &'static dyn RawSaveAccess) {
|
fn set_save_implementation(access_impl: &'static dyn RawSaveAccess) {
|
||||||
let mut access = CURRENT_SAVE_ACCESS.lock();
|
let mut access = CURRENT_SAVE_ACCESS.lock();
|
||||||
assert!(access.is_none(), "Cannot initialize the savegame engine more than once.");
|
assert!(
|
||||||
|
access.is_none(),
|
||||||
|
"Cannot initialize the savegame engine more than once."
|
||||||
|
);
|
||||||
*access = Some(access_impl);
|
*access = Some(access_impl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,7 +269,7 @@ impl SaveData {
|
||||||
/// Returns a range that contains all sectors the input range overlaps.
|
/// Returns a range that contains all sectors the input range overlaps.
|
||||||
///
|
///
|
||||||
/// This can be used to calculate which blocks would be erased by a call
|
/// This can be used to calculate which blocks would be erased by a call
|
||||||
/// to [`prepare_write`](`SaveAccess::prepare_write`)
|
/// to [`prepare_write`](`SaveData::prepare_write`)
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn align_range(&self, range: Range<usize>) -> Range<usize> {
|
pub fn align_range(&self, range: Range<usize>) -> Range<usize> {
|
||||||
let shift = self.info.sector_shift;
|
let shift = self.info.sector_shift;
|
||||||
|
@ -270,19 +281,21 @@ impl SaveData {
|
||||||
///
|
///
|
||||||
/// This will erase any data in any sector overlapping the input range. To
|
/// This will erase any data in any sector overlapping the input range. To
|
||||||
/// calculate which offset ranges would be affected, use the
|
/// calculate which offset ranges would be affected, use the
|
||||||
/// [`align_range`](`SaveAccess::align_range`) function.
|
/// [`align_range`](`SaveData::align_range`) function.
|
||||||
pub fn prepare_write(&mut self, range: Range<usize>) -> Result<SavePreparedBlock, Error> {
|
pub fn prepare_write(&mut self, range: Range<usize>) -> Result<SavePreparedBlock, Error> {
|
||||||
self.check_bounds(range.clone())?;
|
self.check_bounds(range.clone())?;
|
||||||
if self.info.uses_prepare_write {
|
if self.info.uses_prepare_write {
|
||||||
let range = self.align_range(range.clone());
|
let range = self.align_range(range.clone());
|
||||||
let shift = self.info.sector_shift;
|
let shift = self.info.sector_shift;
|
||||||
self.access.prepare_write(
|
self.access.prepare_write(
|
||||||
range.start >> shift, range.len() >> shift, &mut self.timeout,
|
range.start >> shift,
|
||||||
|
range.len() >> shift,
|
||||||
|
&mut self.timeout,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
Ok(SavePreparedBlock {
|
Ok(SavePreparedBlock {
|
||||||
parent: self,
|
parent: self,
|
||||||
range
|
range,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -302,11 +315,14 @@ impl<'a> SavePreparedBlock<'a> {
|
||||||
pub fn write(&mut self, offset: usize, buffer: &[u8]) -> Result<(), Error> {
|
pub fn write(&mut self, offset: usize, buffer: &[u8]) -> Result<(), Error> {
|
||||||
if buffer.is_empty() {
|
if buffer.is_empty() {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else if !self.range.contains(&offset) ||
|
} else if !self.range.contains(&offset)
|
||||||
!self.range.contains(&(offset + buffer.len() - 1)) {
|
|| !self.range.contains(&(offset + buffer.len() - 1))
|
||||||
|
{
|
||||||
Err(Error::OutOfBounds)
|
Err(Error::OutOfBounds)
|
||||||
} else {
|
} else {
|
||||||
self.parent.access.write(offset, buffer, &mut self.parent.timeout)
|
self.parent
|
||||||
|
.access
|
||||||
|
.write(offset, buffer, &mut self.parent.timeout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -454,4 +470,4 @@ impl SaveManager {
|
||||||
pub fn access_with_timer(&mut self, timer: Timer) -> Result<SaveData, Error> {
|
pub fn access_with_timer(&mut self, timer: Timer) -> Result<SaveData, Error> {
|
||||||
SaveData::new(Some(timer))
|
SaveData::new(Some(timer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@ unsafe fn transfer_align4_thumb<T: Copy>(mut dst: *mut T, mut src: *const T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instruction_set(arm::a32)]
|
#[cfg_attr(not(doc), instruction_set(arm::a32))]
|
||||||
#[allow(unused_assignments)]
|
#[allow(unused_assignments)]
|
||||||
unsafe fn transfer_align4_arm<T: Copy>(mut dst: *mut T, mut src: *const T) {
|
unsafe fn transfer_align4_arm<T: Copy>(mut dst: *mut T, mut src: *const T) {
|
||||||
let size = mem::size_of::<T>();
|
let size = mem::size_of::<T>();
|
||||||
|
@ -168,14 +168,14 @@ unsafe fn exchange<T>(dst: *mut T, src: *const T) -> T {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instruction_set(arm::a32)]
|
#[cfg_attr(not(doc), instruction_set(arm::a32))]
|
||||||
unsafe fn exchange_align4_arm<T>(dst: *mut T, i: u32) -> u32 {
|
unsafe fn exchange_align4_arm<T>(dst: *mut T, i: u32) -> u32 {
|
||||||
let out;
|
let out;
|
||||||
asm!("swp {2}, {1}, [{0}]", in(reg) dst, in(reg) i, lateout(reg) out);
|
asm!("swp {2}, {1}, [{0}]", in(reg) dst, in(reg) i, lateout(reg) out);
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instruction_set(arm::a32)]
|
#[cfg_attr(not(doc), instruction_set(arm::a32))]
|
||||||
unsafe fn exchange_align1_arm<T>(dst: *mut T, i: u8) -> u8 {
|
unsafe fn exchange_align1_arm<T>(dst: *mut T, i: u8) -> u8 {
|
||||||
let out;
|
let out;
|
||||||
asm!("swpb {2}, {1}, [{0}]", in(reg) dst, in(reg) i, lateout(reg) out);
|
asm!("swpb {2}, {1}, [{0}]", in(reg) dst, in(reg) i, lateout(reg) out);
|
||||||
|
@ -220,7 +220,9 @@ pub struct Static<T> {
|
||||||
impl<T> Static<T> {
|
impl<T> Static<T> {
|
||||||
/// Creates a new static variable.
|
/// Creates a new static variable.
|
||||||
pub const fn new(val: T) -> Self {
|
pub const fn new(val: T) -> Self {
|
||||||
Static { data: UnsafeCell::new(val) }
|
Static {
|
||||||
|
data: UnsafeCell::new(val),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replaces the current value of the static variable with another, and
|
/// Replaces the current value of the static variable with another, and
|
||||||
|
@ -260,10 +262,10 @@ unsafe impl<T> Sync for Static<T> {}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::Gba;
|
|
||||||
use crate::interrupt::Interrupt;
|
use crate::interrupt::Interrupt;
|
||||||
use crate::sync::Static;
|
use crate::sync::Static;
|
||||||
use crate::timer::Divider;
|
use crate::timer::Divider;
|
||||||
|
use crate::Gba;
|
||||||
|
|
||||||
fn write_read_concurrency_test_impl<const COUNT: usize>(gba: &mut Gba) {
|
fn write_read_concurrency_test_impl<const COUNT: usize>(gba: &mut Gba) {
|
||||||
let sentinel = [0x12345678; COUNT];
|
let sentinel = [0x12345678; COUNT];
|
||||||
|
@ -303,7 +305,7 @@ mod test {
|
||||||
// and interrupt
|
// and interrupt
|
||||||
if interrupt_seen && no_interrupt_seen {
|
if interrupt_seen && no_interrupt_seen {
|
||||||
timer.set_enabled(false);
|
timer.set_enabled(false);
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if i % 8192 == 0 && i != 0 {
|
if i % 8192 == 0 && i != 0 {
|
||||||
|
@ -326,4 +328,4 @@ mod test {
|
||||||
write_read_concurrency_test_impl::<9>(gba);
|
write_read_concurrency_test_impl::<9>(gba);
|
||||||
write_read_concurrency_test_impl::<10>(gba);
|
write_read_concurrency_test_impl::<10>(gba);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
9
justfile
9
justfile
|
@ -24,6 +24,13 @@ test-release:
|
||||||
doctest-agb:
|
doctest-agb:
|
||||||
(cd agb && cargo test --doc -Z doctest-xcompile)
|
(cd agb && cargo test --doc -Z doctest-xcompile)
|
||||||
|
|
||||||
|
check-docs:
|
||||||
|
(cd agb && cargo doc --target=thumbv6m-none-eabi)
|
||||||
|
just _build_docs agb-fixnum
|
||||||
|
|
||||||
|
_build_docs crate:
|
||||||
|
(cd "{{crate}}" && cargo doc)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
just _all-crates _clean
|
just _all-crates _clean
|
||||||
|
|
||||||
|
@ -45,7 +52,7 @@ check-linker-script-consistency:
|
||||||
find -type f -name gba.ld -print0 | xargs -0 -n1 cmp -- agb/gba.ld
|
find -type f -name gba.ld -print0 | xargs -0 -n1 cmp -- agb/gba.ld
|
||||||
find -type f -name gba_mb.ld -print0 | xargs -0 -n1 cmp -- agb/gba_mb.ld
|
find -type f -name gba_mb.ld -print0 | xargs -0 -n1 cmp -- agb/gba_mb.ld
|
||||||
|
|
||||||
ci: check-linker-script-consistency build-debug clippy test build-release test-release doctest-agb build-roms build-book
|
ci: check-linker-script-consistency build-debug clippy test build-release test-release doctest-agb build-roms build-book check-docs
|
||||||
|
|
||||||
build-roms:
|
build-roms:
|
||||||
just _build-rom "examples/the-purple-night" "PURPLENIGHT"
|
just _build-rom "examples/the-purple-night" "PURPLENIGHT"
|
||||||
|
|
Loading…
Reference in a new issue